An error occurred while loading the file. Please try again.
-
dockx thibault authored
Allow snaps removal when user are removed
Verifieda06b6b8f
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2017-2020 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*!
* \brief Management base class
*/
class management implements FusionDirectoryDialog
{
/* Object types we are currently managing */
public $objectTypes;
/* managementListing instance which manages the entries */
public $listing;
/* managementFilter instance which manages the filters */
public $filter;
/* Copy&Paste */
protected $cpHandler = NULL;
protected $cpPastingStarted = FALSE;
protected $skipCpHandler = FALSE;
/* Snapshots */
protected $snapHandler = NULL;
public static $skipSnapshots = FALSE;
// The currently used object(s) (e.g. in edit, removal)
protected $currentDn = '';
protected $currentDns = [];
// The last used object(s).
protected $previousDn = '';
protected $previousDns = [];
// The opened object.
/**
* @var ?simpleTabs
*/
protected $tabObject = NULL;
protected $dialogObject = NULL;
// The last opened object.
protected $last_tabObject = NULL;
protected $last_dialogObject = NULL;
protected $renderCache;
public $headline;
public $title;
public $icon;
protected $actions = [];
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
protected $actionHandlers = [];
protected $exporters = [];
public $neededAttrs = [];
public static $skipTemplates = TRUE;
/* Disable and hide configuration system */
protected $skipConfiguration = FALSE;
protected $columnConfiguration;
/* Default columns */
public static $columns = [
['ObjectTypeColumn', []],
['LinkColumn', ['attributes' => 'nameAttr', 'label' => 'Name']],
['LinkColumn', ['attributes' => 'description', 'label' => 'Description']],
['ActionsColumn', ['label' => 'Actions']],
];
function __construct (
$objectTypes = FALSE,
array $filterElementDefinitions = [
['TabFilterElement', []],
]
)
{
global $config, $class_mapping;
if ($objectTypes === FALSE) {
$plInfos = pluglist::pluginInfos(get_class($this));
$objectTypes = $plInfos['plManages'];
}
if (!preg_match('/^geticon/', $this->icon)) {
$this->icon = get_template_path($this->icon);
}
/* Ignore non existing objectTypes. This happens when an optional plugin is missing. */
foreach ($objectTypes as $key => $type) {
try {
objects::infos($type);
$objectTypes[$key] = strtoupper($type);
} catch (NonExistingObjectTypeException $e) {
unset($objectTypes[$key]);
}
}
$this->objectTypes = array_values($objectTypes);
$this->setUpHeadline();
$this->setUpListing();
$this->setUpFilter($filterElementDefinitions);
// Add copy&paste and snapshot handler.
if (!$this->skipCpHandler) {
$this->cpHandler = new CopyPasteHandler();
}
if (!static::$skipSnapshots && ($config->get_cfg_value('enableSnapshots') == 'TRUE')) {
$this->snapHandler = new SnapshotHandler();
}
// Load exporters
foreach (array_keys($class_mapping) as $class) {
if (preg_match('/Exporter$/', $class)) {
$info = call_user_func([$class, 'getInfo']);
if ($info != NULL) {
$this->exporters = array_merge($this->exporters, $info);
}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
}
}
$this->configureActions();
}
protected function setUpListing ()
{
$this->listing = new managementListing($this);
}
protected function setUpFilter (array $filterElementDefinitions)
{
$this->filter = new managementFilter($this, NULL, $filterElementDefinitions);
}
protected function setUpHeadline ()
{
$plInfos = pluglist::pluginInfos(get_class($this));
$this->headline = $plInfos['plShortName'];
$this->title = $plInfos['plTitle'];
$this->icon = $plInfos['plIcon'];
}
protected function configureActions ()
{
global $config;
// Register default actions
$createMenu = [];
if (!static::$skipTemplates) {
$templateMenu = [];
$fromTemplateMenu = [];
}
foreach ($this->objectTypes as $type) {
$infos = objects::infos($type);
$img = 'geticon.php?context=actions&icon=document-new&size=16';
if (isset($infos['icon'])) {
$img = $infos['icon'];
}
$createMenu[] = new Action(
'new_' . $type, $infos['name'], $img,
'0', 'newEntry',
[$infos['aclCategory'] . '/' . $infos['mainTab'] . '/c']
);
if (!static::$skipTemplates) {
$templateMenu[] = new Action(
'new_template_' . $type, $infos['name'], $img,
'0', 'newEntryTemplate',
[$infos['aclCategory'] . '/template/c']
);
$fromTemplateMenu[] = new Action(
'template_apply_' . $type, $infos['name'], $img,
'0', 'newEntryFromTemplate',
[$infos['aclCategory'] . '/template/r', $infos['aclCategory'] . '/' . $infos['mainTab'] . '/c']
);
}
}
if (!static::$skipTemplates) {
$createMenu =
array_merge(
[
new SubMenuAction(
'template', _('Template'), 'geticon.php?context=devices&icon=template&size=16',
$templateMenu
),
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
new SubMenuAction(
'fromtemplate', _('From template'), 'geticon.php?context=actions&icon=document-new&size=16',
$fromTemplateMenu
),
],
$createMenu
);
}
$this->registerAction(
new SubMenuAction(
'new', _('Create'), 'geticon.php?context=actions&icon=document-new&size=16',
$createMenu
)
);
// Add export actions
$exportMenu = [];
foreach ($this->exporters as $action => $exporter) {
$exportMenu[] = new Action(
$action, $exporter['label'], $exporter['image'],
'0', 'export'
);
}
$this->registerAction(
new SubMenuAction(
'export', _('Export list'), 'geticon.php?context=actions&icon=document-export&size=16',
$exportMenu
)
);
$this->registerAction(
new Action(
'edit', _('Edit'), 'geticon.php?context=actions&icon=document-edit&size=16',
'+', 'editEntry'
)
);
$this->actions['edit']->setSeparator(TRUE);
if (!$this->skipCpHandler) {
$this->registerAction(
new Action(
'cut', _('Cut'), 'geticon.php?context=actions&icon=edit-cut&size=16',
'+', 'copyPasteHandler',
['dr']
)
);
$this->registerAction(
new Action(
'copy', _('Copy'), 'geticon.php?context=actions&icon=edit-copy&size=16',
'+', 'copyPasteHandler',
['r']
)
);
$this->registerAction(
new Action(
'paste', _('Paste'), 'geticon.php?context=actions&icon=edit-paste&size=16',
'0', 'copyPasteHandler',
['w']
)
);
$this->actions['paste']->setEnableFunction([$this, 'enablePaste']);
}
if (!static::$skipTemplates) {
$this->registerAction(
new Action(
'template_apply_to', _('Apply template'), 'geticon.php?context=actions&icon=tools-wizard&size=16',
'+', 'applyTemplateToEntry',
['/template/r', 'c'],
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
TRUE,
FALSE
)
);
}
if (class_available('archivedObject')) {
$action = archivedObject::getManagementAction($this->objectTypes, 'archiveRequested');
if ($action !== NULL) {
$this->registerAction($action);
$this->registerAction(new HiddenAction('archiveConfirmed', 'archiveConfirmed'));
$this->registerAction(new HiddenAction('archiveCancel', 'cancelEdit'));
}
}
$this->registerAction(
new Action(
'remove', _('Remove'), 'geticon.php?context=actions&icon=edit-delete&size=16',
'+', 'removeRequested',
['d']
)
);
if (!static::$skipSnapshots && ($config->get_cfg_value('enableSnapshots') == 'TRUE')) {
$this->registerAction(
new Action(
'snapshot', _('Create snapshot'), 'geticon.php?context=actions&icon=snapshot&size=16',
'1', 'createSnapshotDialog',
['/SnapshotHandler/c']
)
);
$this->registerAction(
new Action(
'restore', _('Restore snapshot'), 'geticon.php?context=actions&icon=document-restore&size=16',
'*', 'restoreSnapshotDialog',
['w', '/SnapshotHandler/r']
)
);
$this->actions['snapshot']->setSeparator(TRUE);
$this->actions['restore']->setEnableFunction([$this, 'enableSnapshotRestore']);
}
if (!static::$skipTemplates) {
$this->registerAction(
new Action(
'template_apply', _('Create an object from this template'), 'geticon.php?context=actions&icon=document-new&size=16',
'1', 'newEntryFromTemplate',
['/template/r', 'c'],
FALSE,
TRUE,
['template']
)
);
}
/* Actions from footer are not in any menus and do not need a label */
$this->registerAction(new HiddenAction('apply', 'applyChanges'));
$this->registerAction(new HiddenAction('save', 'saveChanges'));
$this->registerAction(new HiddenAction('cancel', 'cancelEdit'));
$this->registerAction(new HiddenAction('cancelDelete', 'cancelEdit'));
$this->registerAction(new HiddenAction('removeConfirmed', 'removeConfirmed'));
if (!$this->skipConfiguration) {
$this->registerAction(new HiddenAction('configure', 'configureDialog'));
}
}
/*!
* \brief Register an action to show in the action menu and/or the action column
*/
function registerAction (Action $action)
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
{
$action->setParent($this);
$this->actions[$action->getName()] = $action;
foreach ($action->listActions() as $actionName) {
$this->actionHandlers[$actionName] = $action;
}
}
public function getColumnConfiguration (): array
{
global $config;
if (!isset($this->columnConfiguration)) {
// LDAP configuration
$this->columnConfiguration = $config->getManagementConfig(get_class($this));
}
if (!isset($this->columnConfiguration)) {
// Default configuration
$this->columnConfiguration = static::$columns;
}
// Session configuration
return $this->columnConfiguration;
}
public function setColumnConfiguration ($columns)
{
$this->columnConfiguration = $columns;
$this->listing->reloadColumns();
}
/*!
* \brief Detects actions/events send by the ui
* and the corresponding targets.
*/
function detectPostActions (): array
{
if (!is_object($this->listing)) {
throw new FusionDirectoryException('No valid listing object');
}
$action = ['targets' => [], 'action' => '', 'subaction' => NULL];
if ($this->showTabFooter()) {
if (isset($_POST['edit_cancel'])) {
$action['action'] = 'cancel';
} elseif (isset($_POST['edit_finish'])) {
$action['action'] = 'save';
} elseif (isset($_POST['edit_apply'])) {
$action['action'] = 'apply';
}
} elseif (!$this->dialogOpened()) {
if (isset($_POST['delete_confirmed'])) {
$action['action'] = 'removeConfirmed';
} elseif (isset($_POST['delete_cancel'])) {
$action['action'] = 'cancelDelete';
} elseif (isset($_POST['archive_confirmed'])) {
$action['action'] = 'archiveConfirmed';
} elseif (isset($_POST['archive_cancel'])) {
$action['action'] = 'archiveCancel';
} else {
$action = $this->listing->getAction();
}
}
return $action;
}
/*!
* \brief Calls the registered method for a given action/event.
*/
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
function handleAction (array $action)
{
// Start action
if (isset($action['subaction']) && isset($this->actionHandlers[$action['action'] . '_' . $action['subaction']])) {
return $this->actionHandlers[$action['action'] . '_' . $action['subaction']]->execute($this, $action);
} elseif (isset($this->actionHandlers[$action['action']])) {
return $this->actionHandlers[$action['action']]->execute($this, $action);
}
}
protected function handleSubAction (array $action): bool
{
if (preg_match('/^tab_/', $action['subaction'])) {
$tab = preg_replace('/^tab_/', '', $action['subaction']);
if (isset($this->tabObject->by_object[$tab])) {
$this->tabObject->current = $tab;
} else {
trigger_error('Unknown tab: ' . $tab);
}
return TRUE;
}
return FALSE;
}
/* For management we have to render directly in readPost in some cases */
public function readPost ()
{
$this->renderCache = $this->execute();
}
public function update (): bool
{
if ($this->renderCache === NULL) {
if (!$this->dialogOpened()) {
// Update list
$this->listing->update();
// Init snapshot list for renderSnapshotActions
if (is_object($this->snapHandler)) {
$this->snapHandler->initSnapshotCache($this->listing->getBase());
}
}
}
return TRUE;
}
public function render (): string
{
if ($this->renderCache === NULL) {
if ($this->tabObject instanceof simpleTabs) {
/* Display tab object */
$display = $this->tabObject->render();
$display .= $this->getTabFooter();
$this->renderCache = $this->getHeader() . $display;
} elseif (is_object($this->dialogObject)) {
/* Display dialog object */
$display = $this->dialogObject->render();
$display .= $this->getTabFooter();
$this->renderCache = $this->getHeader() . $display;
} else {
/* Display list */
$this->renderCache = $this->renderList();
}
}
return $this->renderCache;
}
/*!
* \brief Execute this plugin
* Handle actions/events, locking, snapshots, dialogs, tabs,...
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
*/
protected function execute ()
{
// Ensure that html posts and gets are kept even if we see a 'Entry islocked' dialog.
session::set('LOCK_VARS_TO_USE', ['/^act$/', '/^listing/', '/^PID$/']);
/* Display the copy & paste dialog, if it is currently open */
$ret = $this->copyPasteHandler();
if ($ret) {
return $this->getHeader() . $ret;
}
// Handle actions (POSTs and GETs)
$action = $this->detectPostActions();
if (!empty($action['action'])) {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action, 'Action');
try {
$str = $this->handleAction($action);
if (!empty($str)) {
return $this->getHeader() . $str;
}
} catch (FusionDirectoryException $e) {
$error = new FusionDirectoryError(htmlescape($e->getMessage()), 0, $e);
$error->display();
}
}
/* Save tab or dialog object */
if ($this->tabObject instanceof simpleTabs) {
$this->tabObject->readPost();
$this->tabObject->update();
} elseif (is_object($this->dialogObject)) {
try {
$this->dialogObject->readPost();
if (is_object($this->dialogObject)) {
/* Check again as readPost might close it */
if (!$this->dialogObject->update()) {
$this->closeDialogs();
}
}
} catch (FusionDirectoryException $e) {
$error = new FusionDirectoryError(htmlescape($e->getMessage()), 0, $e);
$error->display();
$this->closeDialogs();
}
}
return NULL;
}
function renderList (): string
{
global $config, $ui;
// Rendering things using smarty themselves first
$listRender = $this->listing->render();
$filterRender = $this->renderFilter();
$actionMenu = $this->renderActionMenu();
$smarty = get_smarty();
$smarty->assign('usePrototype', 'true');
$smarty->assign('LIST', $listRender);
$smarty->assign('FILTER', $filterRender);
$smarty->assign('ACTIONS', $actionMenu);
$smarty->assign('SIZELIMIT', $ui->getSizeLimitHandler()->renderWarning());
$smarty->assign('NAVIGATION', $this->listing->renderNavigation($this->skipConfiguration));
$smarty->assign('BASE', $this->listing->renderBase());
$smarty->assign('HEADLINE', $this->headline);
return $this->getHeader() . $smarty->fetch(get_template_path('management/management.tpl'));
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
}
protected function renderFilter (): string
{
return $this->filter->render();
}
protected function renderActionMenu (): string
{
$menuActions = [];
foreach ($this->actions as $action) {
// Build ul/li list
$action->fillMenuItems($menuActions);
}
if (empty($menuActions)) {
return '';
}
$smarty = get_smarty();
$smarty->assign('actions', $menuActions);
return $smarty->fetch(get_template_path('management/actionmenu.tpl'));
}
function renderActionColumn (ListingEntry $entry): string
{
// Go thru all actions
$result = '';
foreach ($this->actions as $action) {
$result .= $action->renderColumnIcons($entry);
}
return $result;
}
function fillActionRowClasses (&$classes, ListingEntry $entry)
{
foreach ($this->actions as $action) {
$action->fillRowClasses($classes, $entry);
}
}
/*!
* \brief Removes ldap object locks created by this class.
* Whenever an object is edited, we create locks to avoid
* concurrent modifications.
* This locks will automatically removed here.
*/
public function removeLocks ()
{
if (!empty($this->currentDn) && ($this->currentDn != 'new')) {
Lock::deleteByObject($this->currentDn);
}
if (count($this->currentDns)) {
Lock::deleteByObject($this->currentDns);
}
}
function dialogOpened (): bool
{
return (is_object($this->tabObject) || is_object($this->dialogObject));
}
/*!
* \brief Sets smarty headline and returns the plugin header which is displayed whenever a tab object is opened.
*/
protected function getHeader (): string
{
global $smarty;
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
$smarty->assign('headline', $this->title);
$smarty->assign('headline_image', $this->icon);
if (is_object($this->tabObject) && ($this->currentDn != '')) {
return '<div class="pluginfo">' . $this->currentDn . "</div>\n";
}
return '';
}
function openTabObject ($object)
{
$this->tabObject = $object;
$this->tabObject->parent = &$this;
}
/*!
* \brief This method closes dialogs
* and cleans up the cached object info and the ui.
*/
public function closeDialogs ()
{
$this->previousDn = $this->currentDn;
$this->currentDn = '';
$this->previousDns = $this->currentDns;
$this->currentDns = [];
$this->last_tabObject = $this->tabObject;
$this->tabObject = NULL;
$this->last_dialogObject = $this->dialogObject;
$this->dialogObject = NULL;
}
protected function listAclCategories (): array
{
$cat = [];
foreach ($this->objectTypes as $type) {
$infos = objects::infos($type);
$cat[] = $infos['aclCategory'];
}
return array_unique($cat);
}
/*!
* \brief Whether footer buttons should appear
*/
protected function showTabFooter (): bool
{
// Do not display tab footer for non tab objects
if (!($this->tabObject instanceof simpleTabs)) {
return FALSE;
}
// Check if there is a dialog opened - We don't need any buttons in this case.
if ($this->tabObject->dialogOpened()) {
return FALSE;
}
return TRUE;
}
/*!
* \brief Generates the footer which is used whenever a tab object is displayed.
*/
protected function getTabFooter (): string
{
// Do not display tab footer for non tab objects
if (!$this->showTabFooter()) {
return '';
}
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
$smarty = get_smarty();
$smarty->assign('readOnly', $this->tabObject->readOnly());
$smarty->assign('showApply', ($this->currentDn != 'new'));
return $smarty->fetch(get_template_path('management/tabfooter.tpl'));
}
function handleTemplateApply ($cancel = FALSE)
{
if (static::$skipTemplates) {
return;
}
if ($cancel) {
$msgs = [];
} else {
$msgs = $this->tabObject->save();
}
if (count($msgs)) {
msg_dialog::displayChecks($msgs);
return;
} else {
if (!$cancel) {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Template applied!');
}
Lock::deleteByObject($this->currentDn);
if (empty($this->currentDns)) {
$this->closeDialogs();
} else {
$this->last_tabObject = $this->tabObject;
$this->tabObject = NULL;
$this->currentDn = array_shift($this->currentDns);
$this->dialogObject->setNextTarget($this->currentDn);
$this->dialogObject->readPost();
}
}
}
function enablePaste ($action, ListingEntry $entry = NULL): bool
{
if ($entry === NULL) {
return $this->cpHandler->entries_queued();
} else {
return FALSE;
}
}
/* Action handlers */
/*!
* \brief This method intiates the object creation.
*
* \param array $action A combination of both 'action' and 'target':
* action: The name of the action which was the used as trigger.
* target: A list of object dns, which should be affected by this method.
*/
function newEntry (array $action)
{
$type = $action['subaction'];
$this->currentDn = 'new';
// Open object
$this->openTabObject(objects::create($type));
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Create entry initiated');
}
function newEntryTemplate (array $action)
{
if (static::$skipTemplates) {
return;
}
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
$type = preg_replace('/^template_/', '', $action['subaction']);
$this->currentDn = 'new';
// Open object
$this->openTabObject(objects::createTemplate($type));
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Create template entry initiated');
}
function newEntryFromTemplate (array $action)
{
if (static::$skipTemplates) {
return;
}
if (isset($action['targets'][0])) {
$dn = $action['targets'][0];
} else {
$dn = NULL;
}
if ($action['subaction'] == 'apply') {
if ($dn === NULL) {
return;
}
$type = $this->listing->getEntry($dn)->getTemplatedType();
} else {
$type = preg_replace('/^apply_/', '', $action['subaction']);
}
$this->dialogObject = new templateDialog($this, $type, $dn);
}
function applyTemplateToEntry (array $action)
{
global $ui;
if (static::$skipTemplates) {
return;
}
if (empty($action['targets'])) {
return;
}
$this->currentDns = $action['targets'];
// check locks
if ($locks = Lock::get($this->currentDns)) {
return Lock::genLockedMessage($locks, FALSE, _('Apply anyway'));
}
// Add locks
Lock::add($this->currentDns);
// Detect type and check that all targets share the same type
$type = NULL;
foreach ($this->currentDns as $dn) {
$entry = $this->listing->getEntry($dn);
if ($entry === NULL) {
trigger_error('Could not find ' . $dn . ', action canceled');
$this->currentDns = [];
return;
}
if ($entry->isTemplate()) {
$error = new FusionDirectoryError(htmlescape(_('Applying a template to a template is not possible')));
$error->display();
$this->currentDns = [];
return;
}
if (!isset($type)) {
$type = $entry->type;
} elseif ($entry->type != $type) {
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
$error = new FusionDirectoryError(htmlescape(_('All selected entries need to share the same type to be able to apply a template to them')));
$error->display();
$this->currentDns = [];
return;
}
}
$this->currentDn = array_shift($this->currentDns);
$this->dialogObject = new templateDialog($this, $type, NULL, $this->currentDn);
}
/*! \brief Queue selected objects to be archived.
* Checks Locks and ask for confirmation.
*/
public function archiveRequested (array $action)
{
global $ui;
if (empty($action['targets'])) {
return;
}
$this->currentDns = $action['targets'];
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action['targets'], 'Entry archive requested');
// Check locks
if ($locks = Lock::get($this->currentDns)) {
return Lock::genLockedMessage($locks, FALSE, _('Archive anyway'));
}
// Add locks
Lock::add($this->currentDns);
$objects = [];
foreach ($this->currentDns as $dn) {
$entry = $this->listing->getEntry($dn);
if ($entry->isTemplate()) {
$error = new FusionDirectoryError(htmlescape(_('Archiving a template is not possible')));
$error->display();
$this->removeLocks();
$this->currentDns = [];
return;
}
$infos = objects::infos($entry->getTemplatedType());
$objects[] = [
'name' => $entry[$infos['nameAttr']][0],
'dn' => $dn,
'icon' => $infos['icon'],
'type' => $infos['name']
];
}
$smarty = get_smarty();
$smarty->assign('objects', $objects);
return $smarty->fetch(get_template_path('simple-archive.tpl'));
}
public function archiveConfirmed (array $action)
{
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Archiving');
$success = 0;
foreach ($this->currentDns as $dn) {
$entry = $this->listing->getEntry($dn);
$errors = archivedObject::archiveObject($entry->type, $dn);
if (empty($errors)) {
$success++;
} else {
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
msg_dialog::displayChecks($errors);
}
Lock::deleteByObject($dn);
}
if ($success > 0) {
msg_dialog::display(
_('Archive success'),
htmlescape(sprintf(_('%d entries were successfully archived'), $success)),
INFO_DIALOG
);
}
$this->currentDns = [];
}
/*!
* \brief This method opens an existing object to be edited.
*
* \param array $action A combination of both 'action' and 'targets':
* action: The name of the action which was the used as trigger.
* targets: A list of object dns, which should be affected by this method.
*/
function editEntry (array $action)
{
global $ui;
// Do not create a new tabObject while there is already one opened,
// the user may have just pressed F5 to reload the page.
if (is_object($this->tabObject)) {
return;
}
$target = array_pop($action['targets']);
$entry = $this->listing->getEntry($target);
if ($entry === NULL) {
trigger_error('Could not find ' . $target . ', open canceled');
return;
}
// Get the dn of the object and create lock
$this->currentDn = $target;
if ($locks = Lock::get($this->currentDn, TRUE)) {
return Lock::genLockedMessage($locks, TRUE);
}
Lock::add($this->currentDn);
// Open object
$this->openTabObject(objects::open($this->currentDn, $entry->getTemplatedType()));
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Edit entry initiated');
if (isset($action['subaction'])
&& ($this->handleSubAction($action) === FALSE)) {
trigger_error('Was not able to handle subaction: ' . $action['subaction']);
}
}
/*!
* \brief Editing an object was canceled.
* Close dialogs/tabs and remove locks.
*/
function cancelEdit ()
{
if (($this->tabObject instanceof simpleTabs) && ($this->dialogObject instanceof templateDialog)) {
$this->handleTemplateApply(TRUE);
return;
}
$this->removeLocks();
$this->closeDialogs();
}
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
/*!
* \brief Save object modifications and closes dialogs (returns to object listing).
* - Calls 'simpleTabs::save' to save back object modifications (e.g. to ldap).
* - Calls 'management::closeDialogs' to return to the object listing.
*/
function saveChanges ()
{
if ($this->tabObject instanceof simpleTabs) {
$this->tabObject->readPost();
$this->tabObject->update();
if ($this->dialogObject instanceof templateDialog) {
$this->handleTemplateApply();
} else {
$msgs = $this->tabObject->save();
if (count($msgs)) {
msg_dialog::displayChecks($msgs);
} else {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Entry saved');
$this->removeLocks();
$this->closeDialogs();
}
}
}
}
/*!
* \brief Save object modifications and keep dialogs opened
*/
function applyChanges ()
{
if ($this->tabObject instanceof simpleTabs) {
$this->tabObject->readPost();
$this->tabObject->update();
$msgs = $this->tabObject->save();
if (count($msgs)) {
msg_dialog::displayChecks($msgs);
} else {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Modifications applied');
$this->tabObject->re_init();
/* Avoid applying the POST a second time */
$_POST = [];
}
}
}
/*! \brief Queue selected objects to be removed.
* Checks ACLs, Locks and ask for confirmation.
*/
function removeRequested (array $action)
{
global $ui;
$disallowed = [];
$this->currentDns = [];
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action['targets'], 'Entry deletion requested');
// Check permissons for each target
foreach ($action['targets'] as $dn) {
$entry = $this->listing->getEntry($dn);
try {
if ($entry->checkAcl('d')) {
$this->currentDns[] = $dn;
} else {
$disallowed[] = $dn;
}
} catch (NonExistingObjectTypeException $e) {
trigger_error('Unknown object type received :' . $e->getMessage());
}
}
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
if (count($disallowed)) {
$error = new FusionDirectoryPermissionError(msgPool::permDelete($disallowed));
$error->display();
}
// We've at least one entry to delete.
if (count($this->currentDns)) {
// Check locks
if ($locks = Lock::get($this->currentDns)) {
return Lock::genLockedMessage($locks, FALSE, _('Delete anyway'));
}
// Add locks
Lock::add($this->currentDns);
$objects = [];
foreach ($this->currentDns as $dn) {
$entry = $this->listing->getEntry($dn);
$infos = objects::infos($entry->getTemplatedType());
if ($entry->isTemplate()) {
$infos['nameAttr'] = 'cn';
}
$objects[] = [
'name' => $entry[$infos['nameAttr']][0],
'dn' => $dn,
'icon' => $infos['icon'],
'type' => $infos['name']
];
}
return $this->removeConfirmationDialog($objects);
}
}
/*! \brief Display confirmation dialog
*/
protected function removeConfirmationDialog (array $objects)
{
$smarty = get_smarty();
$smarty->assign('objects', $objects);
$smarty->assign('multiple', TRUE);
return $smarty->fetch(get_template_path('simple-remove.tpl'));
}
/*! \brief Deletion was confirmed, delete the objects queued.
* Checks ACLs just in case.
*/
function removeConfirmed (array $action)
{
global $ui;
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDns, 'Entry deletion confirmed');
$snapshotHandler = new SnapshotHandler();
foreach ($this->currentDns as $dn) {
$entry = $this->listing->getEntry($dn);
if (empty($entry)) {
continue;
}
if ($entry->checkAcl('d')) {
// Delete the object
$this->currentDn = $dn;
$this->openTabObject(objects::open($this->currentDn, $entry->getTemplatedType()));
$errors = $this->tabObject->delete();
msg_dialog::displayChecks($errors);
// Remove the lock for the current object.
Lock::deleteByObject($this->currentDn);
// Remove related snapshots
$dnSnapshotsList = $snapshotHandler->getSnapshots($this->currentDn, TRUE);
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
foreach ($dnSnapshotsList as $snap) {
$snapshotHandler->removeSnapshot($snap['dn']);
}
} else {
$error = new FusionDirectoryPermissionError(msgPool::permDelete($dn));
$error->display();
logging::log('security', 'management/' . get_class($this), $dn, [], 'Tried to trick deletion.');
}
}
// Cleanup
$this->removeLocks();
$this->closeDialogs();
}
function configureDialog (array $action)
{
if (!$this->skipConfiguration) {
$this->dialogObject = new ManagementConfigurationDialog($this);
}
}
/*! \brief This method is used to queue and process copy&paste actions.
* Allows to copy, cut and paste mutliple entries at once.
*/
function copyPasteHandler (array $action = ['action' => ''])
{
global $ui;
// Exit if copy&paste handler is disabled.
if (!is_object($this->cpHandler)) {
return FALSE;
}
// Save user input
$this->cpHandler->readPost();
// Add entries to queue
if (($action['action'] == 'copy') || ($action['action'] == 'cut')) {
$this->cpHandler->cleanup_queue();
foreach ($action['targets'] as $dn) {
$entry = $this->listing->getEntry($dn);
if (($action['action'] == 'copy') && $entry->checkAcl('r')) {
$this->cpHandler->add_to_queue($dn, 'copy', $entry->getTemplatedType());
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Entry copied!');
}
if (($action['action'] == 'cut') && $entry->checkAcl('rd')) {
$this->cpHandler->add_to_queue($dn, 'cut', $entry->getTemplatedType());
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Entry cut!');
}
}
}
// Initiate pasting
if ($action['action'] == 'paste') {
$this->cpPastingStarted = TRUE;
}
// Display any c&p dialogs, eg. object modifications required before pasting.
if ($this->cpPastingStarted && $this->cpHandler->entries_queued()) {
$this->cpHandler->update();
$data = $this->cpHandler->render();
if (!empty($data)) {
return $data;
}
}
// Automatically disable pasting process since there is no entry left to paste.
if (!$this->cpHandler->entries_queued()) {
$this->cpPastingStarted = FALSE;
1191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
$this->cpHandler->resetPaste();
}
return '';
}
/*!
* \brief Opens the snapshot creation dialog for the given target.
*/
function createSnapshotDialog (array $action)
{
global $config, $ui;
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action['targets'], 'Snapshot creation initiated!');
$this->currentDn = array_pop($action['targets']);
if (empty($this->currentDn)) {
return;
}
$entry = $this->listing->getEntry($this->currentDn);
if ($entry->snapshotCreationAllowed()) {
$this->dialogObject = new SnapshotCreateDialog($this->currentDn, $this, '');
} else {
$error = new FusionDirectoryError(
htmlescape(sprintf(
_('You are not allowed to create a snapshot for %s.'),
$this->currentDn
))
);
$error->display();
}
}
/*!
* \brief Displays the "Restore snapshot dialog" for a given target.
* If no target is specified, open the restore removed object dialog.
*/
function restoreSnapshotDialog (array $action)
{
global $config, $ui;
if (empty($action['targets'])) {
// No target, open the restore removed object dialog.
$this->currentDn = $this->listing->getBase();
$aclCategories = $this->listAclCategories();
} else {
// Display the restore points for a given object.
$this->currentDn = $action['targets'][0];
if (empty($this->currentDn)) {
return;
}
$aclCategories = [objects::infos($this->listing->getEntry($this->currentDn)->getTemplatedType())['aclCategory']];
}
if ($ui->allow_snapshot_restore($this->currentDn, $aclCategories, empty($action['targets']))) {
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Snapshot restoring initiated!');
$this->dialogObject = new SnapshotRestoreDialog($this->currentDn, $this, empty($action['targets']), $aclCategories);
} else {
$error = new FusionDirectoryError(
htmlescape(sprintf(
_('You are not allowed to restore a snapshot for %s.'),
$this->currentDn
))
);
$error->display();
}
}
function export (array $action)
{
1261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
if (!isset($this->exporters[$action['action']])) {
trigger_error('Unknown exporter ' . $action['action']);
return;
}
$exporter = $this->exporters[$action['action']];
$file = $exporter['class']::export($this->listing);
send_binary_content($file, $exporter['filename'], $exporter['mime']);
}
/* End of action handlers */
/* Methods related to Snapshots */
function getSnapshotBases (): array
{
$bases = [];
foreach ($this->objectTypes as $type) {
$infos = objects::infos($type);
$bases[] = $infos['ou'] . $this->listing->getBase();
}
// No bases specified? Try base
if (!count($bases)) {
$bases[] = $this->listing->getBase();
}
return array_unique($bases);
}
/*!
* \brief Get all deleted snapshots
*/
function getAllDeletedSnapshots (): array
{
$bases = $this->getSnapshotBases();
$tmp = [];
foreach ($bases as $base) {
$tmp = array_merge($tmp, $this->snapHandler->getAllDeletedSnapshots($base));
}
return $tmp;
}
/*
* \brief Return available snapshots for the given base
*
* \param string $dn The DN
*/
function getAvailableSnapsShots (string $dn): array
{
return $this->snapHandler->getAvailableSnapsShots($dn);
}
/*
* \brief Whether snapshot restore action should be enabled for an entry
*/
function enableSnapshotRestore ($action, ListingEntry $entry = NULL): bool
{
if ($entry !== NULL) {
/* For entries */
return $this->snapHandler->hasSnapshots($entry->dn);
} else {
/* For action menu */
return $this->snapHandler->hasDeletedSnapshots($this->getSnapshotBases());
}
}
/*!
* \brief Creates a new snapshot entry
* If source arg is not set, default to 'FD'.
*/
1331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
function createSnapshot (string $dn, string $description, string $snapshotSource = 'FD')
{
global $ui;
if (empty($dn) || ($this->currentDn !== $dn)) {
trigger_error('There was a problem with the snapshot workflow');
return;
}
$entry = $this->listing->getEntry($dn);
if ($entry->snapshotCreationAllowed()) {
$this->snapHandler->createSnapshot($dn, $description, $entry->type, $snapshotSource);
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot created!');
} else {
$error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn)));
$error->display();
}
}
/*!
* \brief Restores a snapshot object.
*
* \param String $dn The DN of the snapshot
*/
function restoreSnapshot (string $dn)
{
global $ui;
if (!empty($dn) && $ui->allow_snapshot_restore($dn, $this->dialogObject->aclCategory, $this->dialogObject->global)) {
$dn = $this->snapHandler->restoreSnapshot($dn);
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot restored');
$this->closeDialogs();
if ($dn !== FALSE) {
$this->listing->focusDn($dn);
$entry = $this->listing->getEntry($dn);
$this->currentDn = $dn;
Lock::add($this->currentDn);
// Open object
$this->openTabObject(objects::open($this->currentDn, $entry->getTemplatedType()));
$this->saveChanges();
}
} else {
$error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to restore a snapshot for %s.'), $dn)));
$error->display();
}
}
/*!
* \brief Delete a snapshot
*
* \param string $dn DN of the snapshot
*/
function removeSnapshot (string $dn)
{
global $ui;
if (!empty($dn) && $ui->allow_snapshot_delete($dn, $this->dialogObject->aclCategory)) {
$this->snapHandler->removeSnapshot($dn);
logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, 'Snapshot deleted');
} else {
$error = new FusionDirectoryPermissionError(htmlescape(sprintf(_('You are not allowed to delete a snapshot for %s.'), $dn)));
$error->display();
}
}
static function mainInc ($classname = NULL, $objectTypes = FALSE)
{
global $remove_lock, $cleanup, $display;
if ($classname === NULL) {
$classname = get_called_class();
}
14011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429
/* Remove locks */
if ($remove_lock && session::is_set($classname)) {
$macl = session::get($classname);
$macl->removeLocks();
}
if ($cleanup) {
/* Clean up */
session::un_set($classname);
} else {
if (!session::is_set($classname) || (isset($_GET['reset']) && $_GET['reset'] == 1)) {
/* Create the object if missing or reset requested */
$managementObject = new $classname($objectTypes);
} else {
/* Retrieve the object from session */
$managementObject = session::get($classname);
}
/* Execute and display */
$managementObject->readPost();
$managementObject->update();
$display = $managementObject->render();
/* Store the object in the session */
session::set($classname, $managementObject);
}
}
}