/**
  * 
  * "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;
 }