/** * * "Secure" version of {@link Folder::SyncChildren}. * * @return array */ public function securedSyncChildren() { $parentID = (int) $this->owner->ID; // parentID = 0 on the singleton, used as the 'root node'; $added = 0; $deleted = 0; $skipped = 0; // First, merge any children that are duplicates //customised if ($parentID === 0) { //make sure there are no merges between a secured folder and non-secured folder. //there is only one case that there are both secured child folder and non-secured child folder exists, //that is when $this->owner is on assets root. $duplicateChildrenNames = DB::query("SELECT \"Name\" FROM \"File\"" . " WHERE \"ParentID\"={$parentID} AND \"Secured\"='0' GROUP BY \"Name\" HAVING count(*) > 1")->column(); } else { $duplicateChildrenNames = DB::query("SELECT \"Name\" FROM \"File\"" . " WHERE \"ParentID\" = {$parentID} GROUP BY \"Name\" HAVING count(*) > 1")->column(); } if ($duplicateChildrenNames) { foreach ($duplicateChildrenNames as $childName) { $childName = Convert::raw2sql($childName); // Note, we do this in the database rather than object-model; otherwise we get all sorts of problems // about deleting files $children = DB::query("SELECT \"ID\" FROM \"File\"" . " WHERE \"Name\" = '{$childName}' AND \"ParentID\" = {$parentID}")->column(); if ($children) { $keptChild = array_shift($children); foreach ($children as $removedChild) { DB::query("UPDATE \"File\" SET \"ParentID\" = {$keptChild} WHERE \"ParentID\" = {$removedChild}"); DB::query("DELETE FROM \"File\" WHERE \"ID\" = {$removedChild}"); } } else { user_error("Inconsistent database issue: SELECT ID FROM \"File\" WHERE Name = '{$childName}'" . " AND ParentID = {$parentID} should have returned data", E_USER_WARNING); } } } // Get index of database content // We don't use DataObject so that things like subsites doesn't muck with this. $dbChildren = DB::query("SELECT * FROM \"File\" WHERE \"ParentID\" = {$parentID}"); $hasDbChild = array(); if ($dbChildren) { foreach ($dbChildren as $dbChild) { $className = $dbChild['ClassName']; if (!$className) { $className = "File"; } $hasDbChild[$dbChild['Name']] = new $className($dbChild); } } $unwantedDbChildren = $hasDbChild; // if we're syncing a folder with no ID, we assume we're syncing the root assets folder // however the Filename field is populated with "NewFolder", so we need to set this to empty // to satisfy the baseDir variable below, which is the root folder to scan for new files in if (!$parentID) { $this->owner->Filename = ''; } // Iterate through the actual children, correcting the database as necessary $baseDir = $this->owner->FullPath; // @todo this shouldn't call die() but log instead if ($parentID && !$this->owner->Filename) { die($this->owner->ID . " - " . $this->owner->FullPath); } if (file_exists($baseDir)) { $actualChildren = scandir($baseDir); $ignoreRules = Config::inst()->get('Filesystem', 'sync_blacklisted_patterns'); foreach ($actualChildren as $actualChild) { if ($ignoreRules) { $skip = false; foreach ($ignoreRules as $rule) { if (preg_match($rule, $actualChild)) { $skip = true; break; } } if ($skip) { $skipped++; continue; } } // A record with a bad class type doesn't deserve to exist. It must be purged! if (isset($hasDbChild[$actualChild])) { $child = $hasDbChild[$actualChild]; if (!$child instanceof Folder && is_dir($baseDir . $actualChild) || $child instanceof Folder && !is_dir($baseDir . $actualChild)) { DB::query("DELETE FROM \"File\" WHERE \"ID\" = {$child->ID}"); unset($hasDbChild[$actualChild]); } } if (isset($hasDbChild[$actualChild])) { $child = $hasDbChild[$actualChild]; unset($unwantedDbChildren[$actualChild]); } else { $added++; $childID = $this->constructChildSecuredWithSecuredFlag($actualChild); $child = DataObject::get_by_id("File", $childID); } if ($child && is_dir($baseDir . $actualChild)) { //customised //when we synch on assets root //we don't want to sync the secured root folder SECURED_FILES_ASSET_SUBDIR //This is only the case where both secured child folder and non-secured child folder exist if ($parentID === 0 && $child->ID === FileSecured::getSecuredRoot()->ID) { $skipped++; } else { //customised //Of cause, we need to call this customised version recursively $childResult = $child->securedSyncChildren(); $added += $childResult['added']; $deleted += $childResult['deleted']; $skipped += $childResult['skipped']; } } // Clean up the child record from memory after use. Important! $child->destroy(); $child = null; } // Iterate through the unwanted children, removing them all if (isset($unwantedDbChildren)) { foreach ($unwantedDbChildren as $unwantedDbChild) { DB::query("DELETE FROM \"File\" WHERE \"ID\" = {$unwantedDbChild->ID}"); $deleted++; } } } else { DB::query("DELETE FROM \"File\" WHERE \"ID\" = {$this->owner}->ID"); } return array('added' => $added, 'deleted' => $deleted, 'skipped' => $skipped); }
/** * * Return fake-ID "root" if no ID is found (needed to upload files into the root-folder) * * @return mixed (string | number) */ public function currentPageID() { if (is_numeric($this->request->requestVar('ID'))) { return $this->request->requestVar('ID'); } elseif (is_numeric($this->urlParams['ID'])) { return $this->urlParams['ID']; } elseif (Session::get("{$this->class}.currentPage")) { return Session::get("{$this->class}.currentPage"); } else { $securedRoot = FileSecured::getSecuredRoot(); if ($securedRoot && $securedRoot->exists()) { return $securedRoot->ID; } else { SecuredAssetAdmin::instantiate(); $securedRoot = FileSecured::getSecuredRoot(); return $securedRoot->ID; } } }
/** * * @param FieldList $fields * @return void */ public function updateCMSFields(FieldList $fields) { $controller = Controller::curr(); if ($controller instanceof SecuredAssetAdmin || $controller instanceof CMSSecuredFileAddController) { Requirements::combine_files('securedassetsadmincmsfields.js', array(SECURED_FILES_MODULE_DIR . '/thirdparty/javascript/jquery-ui/timepicker/jquery-ui-sliderAccess.js', SECURED_FILES_MODULE_DIR . '/thirdparty/javascript/jquery-ui/timepicker/jquery-ui-timepicker-addon.min.js', SECURED_FILES_MODULE_DIR . "/javascript/SecuredFilesLeftAndMain.js")); Requirements::css(SECURED_FILES_MODULE_DIR . '/thirdparty/javascript/jquery-ui/timepicker/jquery-ui-timepicker-addon.min.css'); Requirements::css(SECURED_FILES_MODULE_DIR . "/css/SecuredFilesLeftAndMain.css"); Requirements::javascript(SECURED_FILES_MODULE_DIR . "/javascript/SecuredFilesLeftAndMain.js"); if ($this->isFile()) { $buttonsSecurity = $this->showButtonsSecurity(); $buttonsEmbargoExpiry = $this->showButtonsEmbargoExpiry(); // Embargo field $embargoTypeField = new OptionSetField("EmbargoType", "", array("None" => _t('AdvancedSecuredFiles.NONENICE', "None"), "Indefinitely" => _t('AdvancedSecuredFiles.INDEFINITELYNICE', "Hide document indefinitely"), "UntilAFixedDate" => _t('AdvancedSecuredFiles.UNTILAFIXEDDATENICE', 'Hide until set date'))); $embargoUntilDateField = DatetimeField::create('EmbargoedUntilDate', ''); $embargoUntilDateField->getDateField()->setConfig('showcalendar', true)->setConfig('dateformat', 'dd-MM-yyyy')->setConfig('datavalueformat', 'dd-MM-yyyy')->setAttribute('readonly', true); $embargoUntilDateField->getTimeField()->setAttribute('readonly', true); // Expiry field $expireTypeField = new OptionSetField("ExpiryType", "", array("None" => _t('AdvancedSecuredFiles.NONENICE', "None"), "AtAFixedDate" => _t('AdvancedSecuredFiles.ATAFIXEDDATENICE', 'Set file to expire on'))); $expiryDatetime = DatetimeField::create('ExpireAtDate', ''); $expiryDatetime->getDateField()->setConfig('showcalendar', true)->setConfig('dateformat', 'dd-MM-yyyy')->setConfig('datavalueformat', 'dd-MM-yyyy')->setAttribute('readonly', true); $expiryDatetime->getTimeField()->setAttribute('readonly', true); $securitySettingsGroup = FieldGroup::create(FieldGroup::create($embargoTypeField, $embargoUntilDateField)->addExtraClass('embargo option-change-datetime')->setName("EmbargoGroupField"), FieldGroup::create($expireTypeField, $expiryDatetime)->addExtraClass('expiry option-change-datetime')->setName("ExpiryGroupField")); } else { $buttonsSecurity = $this->showButtonsSecurity(); $buttonsEmbargoExpiry = ''; $securitySettingsGroup = FieldGroup::create(); } $canViewTypeField = new OptionSetField("CanViewType", "", array("Inherit" => _t('AdvancedSecuredFiles.INHERIT', "Inherit from parent folder"), "Anyone" => _t('SiteTree.ACCESSANYONE', 'Anyone'), "LoggedInUsers" => _t('SiteTree.ACCESSLOGGEDIN', 'Logged-in users'), "OnlyTheseUsers" => _t('SiteTree.ACCESSONLYTHESE', 'Only these people (choose from list)'))); $canEditTypeField = new OptionSetField("CanEditType", "", array("Inherit" => _t('AdvancedSecuredFiles.INHERIT', "Inherit from parent folder"), "LoggedInUsers" => _t('SiteTree.ACCESSLOGGEDIN', 'Logged-in users'), "OnlyTheseUsers" => _t('SiteTree.ACCESSONLYTHESE', 'Only these people (choose from list)'))); $groupsMap = array(); foreach (Group::get() as $group) { // Listboxfield values are escaped, use ASCII char instead of » $groupsMap[$group->ID] = $group->getBreadcrumbs(' > '); } asort($groupsMap); $viewerGroupsField = ListboxField::create("ViewerGroups", _t('AdvancedSecuredFiles.VIEWERGROUPS', "Viewer Groups"))->setMultiple(true)->setSource($groupsMap)->setAttribute('data-placeholder', _t('AdvancedSecuredFiles.GroupPlaceholder', 'Click to select group')); $editorGroupsField = ListBoxField::create("EditorGroups", _t('AdvancedSecuredFiles.EDITORGROUPS', "Editor Groups"))->setMultiple(true)->setSource($groupsMap)->setAttribute('data-placeholder', _t('AdvancedSecuredFiles.GroupPlaceholder', 'Click to select group')); $securitySettingsGroup->push(FieldGroup::create($canViewTypeField, $viewerGroupsField)->addExtraClass('whocanview option-change-listbox')->setName("CanViewGroupField")); $securitySettingsGroup->push(FieldGroup::create($canEditTypeField, $editorGroupsField)->addExtraClass('whocanedit option-change-listbox')->setName("CanEditGroupField")); $securitySettingsGroup->setName("SecuritySettingsGroupField")->addExtraClass('security-settings'); $showAdvanced = AdvancedAssetsFilesSiteConfig::is_security_enabled() || $this->isFile() && AdvancedAssetsFilesSiteConfig::is_embargoexpiry_enabled(); if ($showAdvanced) { $fields->insertAfter(LiteralField::create('BottomTaskSelection', $this->owner->renderWith('componentField', ArrayData::create(array('ComponentSecurity' => AdvancedAssetsFilesSiteConfig::component_cms_icon('security'), 'ComponentEmbargoExpiry' => AdvancedAssetsFilesSiteConfig::component_cms_icon('embargoexpiry'), 'ButtonsSecurity' => $buttonsSecurity, 'ButtonsEmbargoExpiry' => $buttonsEmbargoExpiry)))), "ParentID"); $fields->insertAfter($securitySettingsGroup, "BottomTaskSelection"); } } if (!is_a($this->owner, "Folder") && is_a($this->owner, "File")) { $parentIDField = $fields->dataFieldByName("ParentID"); if ($controller instanceof SecuredAssetAdmin) { $securedRoot = FileSecured::getSecuredRoot(); $parentIDField->setTreeBaseID($securedRoot->ID); $parentIDField->setFilterFunction(create_function('$node', "return \$node->Secured == 1;")); } else { $parentIDField->setFilterFunction(create_function('$node', "return \$node->Secured == 0;")); } // SilverStripe core has a bug for search function now, so disable it for now. $parentIDField->setShowSearch(false); } }
/** * * Can be queried with an ajax request to trigger the filesystem sync. It returns a FormResponse status message * to display in the CMS * * @return null */ public function doSync() { $securedRoot = FileSecured::getSecuredRoot(); $message = SecuredFilesystem::sync_secured($securedRoot->ID); $this->response->addHeader('X-Status', rawurlencode($message)); return; }