public function processRecord($record, $columnMap, &$results, $preview = false) { $objID = parent::processRecord($record, $columnMap, $results, $preview); $_cache_groupByCode = array(); // Add to predefined groups /** @var Member $member */ $member = DataObject::get_by_id($this->objectClass, $objID); foreach ($this->groups as $group) { // TODO This isnt the most memory effective way to add members to a group $member->Groups()->add($group); } // Add to groups defined in CSV if (isset($record['Groups']) && $record['Groups']) { $groupCodes = explode(',', $record['Groups']); foreach ($groupCodes as $groupCode) { $groupCode = Convert::raw2url($groupCode); if (!isset($_cache_groupByCode[$groupCode])) { $group = Group::get()->filter('Code', $groupCode)->first(); if (!$group) { $group = new Group(); $group->Code = $groupCode; $group->Title = $groupCode; $group->write(); } $member->Groups()->add($group); $_cache_groupByCode[$groupCode] = $group; } } } $member->destroy(); unset($member); return $objID; }
public function processRecord($record, $columnMap, &$results, $preview = false) { // We match by 'Code', the ID property is confusing the importer if (isset($record['ID'])) { unset($record['ID']); } $objID = parent::processRecord($record, $columnMap, $results, $preview); $group = DataObject::get_by_id($this->objectClass, $objID); // set group hierarchies - we need to do this after all records // are imported to avoid missing "early" references to parents // which are imported later on in the CSV file. if (isset($record['ParentCode']) && $record['ParentCode']) { $parentGroup = DataObject::get_one('SilverStripe\\Security\\Group', array('"Group"."Code"' => $record['ParentCode'])); if ($parentGroup) { $group->ParentID = $parentGroup->ID; $group->write(); } } // set permission codes - these are all additive, meaning // existing permissions arent cleared. if (isset($record['PermissionCodes']) && $record['PermissionCodes']) { foreach (explode(',', $record['PermissionCodes']) as $code) { $p = DataObject::get_one('SilverStripe\\Security\\Permission', array('"Permission"."Code"' => $code, '"Permission"."GroupID"' => $group->ID)); if (!$p) { $p = new Permission(array('Code' => $code)); $p->write(); } $group->Permissions()->add($p); } } return $objID; }
/** * Adds the item to this relation. * * It does so by setting the relationFilters. * * @param DataObject|int $item The DataObject to be added, or its ID */ public function add($item) { if (is_numeric($item)) { $item = DataObject::get_by_id($this->dataClass, $item); } else { if (!$item instanceof $this->dataClass) { user_error("PolymorphicHasManyList::add() expecting a {$this->dataClass} object, or ID value", E_USER_ERROR); } } $foreignID = $this->getForeignID(); // Validate foreignID if (!$foreignID) { user_error("PolymorphicHasManyList::add() can't be called until a foreign ID is set", E_USER_WARNING); return; } if (is_array($foreignID)) { user_error("PolymorphicHasManyList::add() can't be called on a list linked to mulitple foreign IDs", E_USER_WARNING); return; } $foreignKey = $this->foreignKey; $classForeignKey = $this->classForeignKey; $item->{$foreignKey} = $foreignID; $item->{$classForeignKey} = $this->getForeignClass(); $item->write(); }
public function getValue() { $id = $this->getIDValue(); $class = $this->getClassValue(); if ($id && $class && is_subclass_of($class, 'SilverStripe\\ORM\\DataObject')) { return DataObject::get_by_id($class, $id); } return null; }
public function testSQLInsert() { $factory = new FixtureFactory(); $relPath = ltrim(FRAMEWORK_DIR . '/tests/testing/YamlFixtureTest.yml', '/'); $fixture = Injector::inst()->create('SilverStripe\\Dev\\YamlFixture', $relPath); $fixture->writeInto($factory); $this->assertGreaterThan(0, $factory->getId("YamlFixtureTest_DataObject", "testobject1")); $object1 = DataObject::get_by_id("YamlFixtureTest_DataObject", $factory->getId("YamlFixtureTest_DataObject", "testobject1")); $this->assertTrue($object1->ManyManyRelation()->Count() == 2, "Should be two items in this relationship"); $this->assertGreaterThan(0, $factory->getId("YamlFixtureTest_DataObject", "testobject2")); $object2 = DataObject::get_by_id("YamlFixtureTest_DataObject", $factory->getId("YamlFixtureTest_DataObject", "testobject2")); $this->assertTrue($object2->ManyManyRelation()->Count() == 1, "Should be one item in this relationship"); }
public function testCleartextPasswordsAreHashedWithDefaultAlgo() { $loader = new MemberCsvBulkLoader(); $results = $loader->load($this->getCurrentRelativePath() . '/MemberCsvBulkLoaderTest_cleartextpws.csv'); $member = $results->Created()->First(); $memberID = $member->ID; DataObject::flush_and_destroy_cache(); $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $memberID); // TODO Direct getter doesn't work, wtf! $this->assertEquals(Security::config()->password_encryption_algorithm, $member->getField('PasswordEncryption')); $result = $member->checkPassword('mypassword'); $this->assertTrue($result->valid()); }
public function testRun() { $m = new Member(); $m->Password = '******'; $m->PasswordEncryption = 'none'; $m->write(); $t = new EncryptAllPasswordsTask(); $t->run(null); $m = DataObject::get_by_id('SilverStripe\\Security\\Member', $m->ID); $this->assertEquals($m->PasswordEncryption, 'blowfish'); $this->assertNotEquals($m->Password, 'plain'); $result = $m->checkPassword('plain'); $this->assertTrue($result->valid()); }
public function testNoLegacyPasswordHashMigrationOnIncompatibleAlgorithm() { Config::inst()->update('SilverStripe\\Security\\PasswordEncryptor', 'encryptors', array('crc32' => array('SilverStripe\\Security\\PasswordEncryptor_PHPHash' => 'crc32'))); $field = Member::config()->unique_identifier_field; $member = new Member(); $member->{$field} = '*****@*****.**'; $member->PasswordEncryption = "crc32"; $member->Password = "******"; $member->write(); $data = array('Email' => $member->{$field}, 'Password' => 'mypassword'); MemberAuthenticator::authenticate($data); $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member->ID); $this->assertEquals($member->PasswordEncryption, "crc32"); $result = $member->checkPassword('mypassword'); $this->assertTrue($result->valid()); }
public function testDuplicateManyManyClasses() { //create new test classes below $one = new DataObjectDuplicateTestClass1(); $two = new DataObjectDuplicateTestClass2(); $three = new DataObjectDuplicateTestClass3(); //set some simple fields $text1 = "Test Text 1"; $text2 = "Test Text 2"; $text3 = "Test Text 3"; $one->text = $text1; $two->text = $text2; $three->text = $text3; //write the to DB $one->write(); $two->write(); $three->write(); //create relations $one->twos()->add($two); $one->threes()->add($three); $one = DataObject::get_by_id("DataObjectDuplicateTestClass1", $one->ID); $two = DataObject::get_by_id("DataObjectDuplicateTestClass2", $two->ID); $three = DataObject::get_by_id("DataObjectDuplicateTestClass3", $three->ID); //test duplication $oneCopy = $one->duplicate(); $twoCopy = $two->duplicate(); $threeCopy = $three->duplicate(); $oneCopy = DataObject::get_by_id("DataObjectDuplicateTestClass1", $oneCopy->ID); $twoCopy = DataObject::get_by_id("DataObjectDuplicateTestClass2", $twoCopy->ID); $threeCopy = DataObject::get_by_id("DataObjectDuplicateTestClass3", $threeCopy->ID); $this->assertNotNull($oneCopy, "Copy of 1 exists"); $this->assertNotNull($twoCopy, "Copy of 2 exists"); $this->assertNotNull($threeCopy, "Copy of 3 exists"); $this->assertEquals($text1, $oneCopy->text); $this->assertEquals($text2, $twoCopy->text); $this->assertEquals($text3, $threeCopy->text); $this->assertNotEquals($one->twos()->Count(), $oneCopy->twos()->Count(), "Many-to-one relation not copied (has_many)"); $this->assertEquals($one->threes()->Count(), $oneCopy->threes()->Count(), "Object has the correct number of relations"); $this->assertEquals($three->ones()->Count(), $threeCopy->ones()->Count(), "Object has the correct number of relations"); $this->assertEquals($one->ID, $twoCopy->one()->ID, "Match between relation of copy and the original"); $this->assertEquals(0, $oneCopy->twos()->Count(), "Many-to-one relation not copied (has_many)"); $this->assertEquals($three->ID, $oneCopy->threes()->First()->ID, "Match between relation of copy and the original"); $this->assertEquals($one->ID, $threeCopy->ones()->First()->ID, "Match between relation of copy and the original"); }
/** * Return this field's linked items */ public function getItems() { // If the value has been set, use that if ($this->value != 'unchanged' && is_array($this->sourceObject)) { $items = array(); $values = is_array($this->value) ? $this->value : preg_split('/ *, */', trim($this->value)); foreach ($values as $value) { $item = new stdClass(); $item->ID = $value; $item->Title = $this->sourceObject[$value]; $items[] = $item; } return $items; // Otherwise, look data up from the linked relation } if ($this->value != 'unchanged' && is_string($this->value)) { $items = new ArrayList(); $ids = explode(',', $this->value); foreach ($ids as $id) { if (!is_numeric($id)) { continue; } $item = DataObject::get_by_id($this->sourceObject, $id); if ($item) { $items->push($item); } } return $items; } else { if ($this->form) { $fieldName = $this->name; $record = $this->form->getRecord(); if (is_object($record) && $record->hasMethod($fieldName)) { return $record->{$fieldName}(); } } } }
public function getHTMLFragments($gridField) { $modelClass = $gridField->getModelClass(); $parentID = 0; if ($this->currentID) { $modelObj = DataObject::get_by_id($modelClass, $this->currentID); if ($modelObj->hasMethod('getParent')) { $parent = $modelObj->getParent(); } elseif ($modelObj->ParentID) { $parent = $modelObj->Parent(); } if ($parent) { $parentID = $parent->ID; } // Attributes $attrs = array_merge($this->attributes, array('href' => sprintf($this->linkSpec, $parentID), 'class' => 'cms-panel-link ss-ui-button font-icon-level-up no-text grid-levelup')); $attrsStr = ''; foreach ($attrs as $k => $v) { $attrsStr .= " {$k}=\"" . Convert::raw2att($v) . "\""; } $forTemplate = new ArrayData(array('UpLink' => DBField::create_field('HTMLFragment', sprintf('<a%s></a>', $attrsStr)))); return array('before' => $forTemplate->renderWith('Includes/GridFieldLevelup')); } }
public function getHTMLFragments($gridField) { $modelClass = $gridField->getModelClass(); $parentID = 0; if (!$this->currentID) { return null; } $modelObj = DataObject::get_by_id($modelClass, $this->currentID); $parent = null; if ($modelObj->hasMethod('getParent')) { $parent = $modelObj->getParent(); } elseif ($modelObj->ParentID) { $parent = $modelObj->Parent(); } if ($parent) { $parentID = $parent->ID; } // Attributes $attrs = array_merge($this->attributes, array('href' => sprintf($this->linkSpec, $parentID), 'class' => 'cms-panel-link ss-ui-button font-icon-level-up no-text grid-levelup')); $linkTag = FormField::create_tag('a', $attrs); $forTemplate = new ArrayData(array('UpLink' => DBField::create_field('HTMLFragment', $linkTag))); $template = SSViewer::get_templates_by_class($this, '', __CLASS__); return array('before' => $forTemplate->renderWith($template)); }
/** * Delete the record with the given ID. * * @param string $className The class name of the record to be deleted * @param int $id ID of record to be deleted */ public static function delete_by_id($className, $id) { $obj = DataObject::get_by_id($className, $id); if ($obj) { $obj->delete(); } else { user_error("{$className} object #{$id} wasn't found when calling DataObject::delete_by_id", E_USER_WARNING); } }
/** * Check if the member is in the given group or any parent groups. * * @param int|Group|string $group Group instance, Group Code or ID * @param boolean $strict Only determine direct group membership if set to TRUE (Default: FALSE) * @return bool Returns TRUE if the member is in the given group, otherwise FALSE. */ public function inGroup($group, $strict = false) { if (is_numeric($group)) { $groupCheckObj = DataObject::get_by_id('SilverStripe\\Security\\Group', $group); } elseif (is_string($group)) { $groupCheckObj = DataObject::get_one('SilverStripe\\Security\\Group', array('"Group"."Code"' => $group)); } elseif ($group instanceof Group) { $groupCheckObj = $group; } else { user_error('Member::inGroup(): Wrong format for $group parameter', E_USER_ERROR); } if (!$groupCheckObj) { return false; } $groupCandidateObjs = $strict ? $this->getManyManyComponents("Groups") : $this->Groups(); if ($groupCandidateObjs) { foreach ($groupCandidateObjs as $groupCandidateObj) { if ($groupCandidateObj->ID == $groupCheckObj->ID) { return true; } } } return false; }
public function testFormatFromSettings() { $memberID = $this->logInWithPermission(); $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $memberID); $member->DateFormat = 'dd/MM/YYYY'; $member->write(); $fixtures = array('2000-12-31' => '31/12/2000', '31-12-2000' => '31/12/2000', '31/12/2000' => '31/12/2000', '2014-04-01' => '01/04/2014'); foreach ($fixtures as $from => $to) { $date = DBField::create_field('Date', $from); // With member $this->assertEquals($to, $date->FormatFromSettings($member)); // Without member $this->assertEquals($to, $date->FormatFromSettings()); } }
protected function extractValue($item, $key) { if (is_numeric($item)) { $item = DataObject::get_by_id($this->dataClass, $item); } return parent::extractValue($item, $key); }
public function testSaveTreeNodeParentID() { $this->loginWithPermission('ADMIN'); $page1 = $this->objFromFixture('LeftAndMainTest_Object', 'page1'); $page2 = $this->objFromFixture('LeftAndMainTest_Object', 'page2'); $page3 = $this->objFromFixture('LeftAndMainTest_Object', 'page3'); $page31 = $this->objFromFixture('LeftAndMainTest_Object', 'page31'); $page32 = $this->objFromFixture('LeftAndMainTest_Object', 'page32'); // Move page2 into page3, between page3.1 and page 3.2 $siblingIDs = array($page31->ID, $page2->ID, $page32->ID); $data = array('SiblingIDs' => $siblingIDs, 'ID' => $page2->ID, 'ParentID' => $page3->ID); $response = $this->post('LeftAndMainTest_Controller/savetreenode', $data); $this->assertEquals(200, $response->getStatusCode()); $page2 = DataObject::get_by_id('LeftAndMainTest_Object', $page2->ID, false); $page31 = DataObject::get_by_id('LeftAndMainTest_Object', $page31->ID, false); $page32 = DataObject::get_by_id('LeftAndMainTest_Object', $page32->ID, false); $this->assertEquals($page3->ID, $page2->ParentID, 'Moved page gets new parent'); $this->assertEquals(1, $page31->Sort, 'Children pages before insertaion are unaffected'); $this->assertEquals(2, $page2->Sort, 'Moved page is correctly sorted'); $this->assertEquals(3, $page32->Sort, 'Children pages after insertion are resorted'); }
/** * Get an object from the fixture. * * @param $class The data class, as specified in your fixture file. Parent classes won't work * @param $identifier The identifier string, as provided in your fixture file */ public function get($class, $identifier) { $id = $this->getId($class, $identifier); if ($id) { return DataObject::get_by_id($class, $id); } }
/** * Find local File dataobject given ID * * @param int $id * @return array */ protected function viewfile_getLocalFileByID($id) { /** @var File $file */ $file = DataObject::get_by_id('File', $id); if ($file && $file->canView()) { return array($file, $file->getURL()); } return [null, null]; }
/** * Get foreign member record for this relation * * @return Member */ protected function getMember() { $id = $this->getForeignID(); if ($id) { return DataObject::get_by_id('SilverStripe\\Security\\Member', $id); } }
/** * Helper method for processing batch actions. * Returns a set of status-updating JavaScript to return to the CMS. * * @param SS_List $objs The SS_List of objects to perform this batch action * on. * @param string $helperMethod The method to call on each of those objects. * @param string $successMessage * @param array $arguments * @return string JSON encoded map in the following format: * { * 'modified': { * 3: {'TreeTitle': 'Page3'}, * 5: {'TreeTitle': 'Page5'} * }, * 'deleted': { * // all deleted pages * } * } */ public function batchaction(SS_List $objs, $helperMethod, $successMessage, $arguments = array()) { $status = array('modified' => array(), 'error' => array(), 'deleted' => array(), 'success' => array()); foreach ($objs as $obj) { // Perform the action $id = $obj->ID; if (!call_user_func_array(array($obj, $helperMethod), $arguments)) { $status['error'][$id] = $id; } else { $status['success'][$id] = $id; } // Now make sure the tree title is appropriately updated $publishedRecord = DataObject::get_by_id($this->managedClass, $id); if ($publishedRecord) { $status['modified'][$id] = array('TreeTitle' => $publishedRecord->TreeTitle); } else { $status['deleted'][$id] = $id; } $obj->destroy(); unset($obj); } return $this->response($successMessage, $status); }
/** * Test that UploadField:overwriteWarning cannot overwrite Upload:replaceFile */ public function testConfigOverwriteWarningCannotRelaceFiles() { Upload::config()->replaceFile = false; UploadField::config()->defaultConfig = array_merge(UploadField::config()->defaultConfig, array('overwriteWarning' => true)); $tmpFileName = 'testUploadBasic.txt'; $response = $this->mockFileUpload('NoRelationField', $tmpFileName); $this->assertFalse($response->isError()); $responseData = Convert::json2array($response->getBody()); $uploadedFile = DataObject::get_by_id('SilverStripe\\Assets\\File', (int) $responseData[0]['id']); $this->assertTrue(is_object($uploadedFile), 'The file object is created'); $this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile)); $tmpFileName = 'testUploadBasic.txt'; $response = $this->mockFileUpload('NoRelationField', $tmpFileName); $this->assertFalse($response->isError()); $responseData = Convert::json2array($response->getBody()); $uploadedFile2 = DataObject::get_by_id('SilverStripe\\Assets\\File', (int) $responseData[0]['id']); $this->assertTrue(is_object($uploadedFile2), 'The file object is created'); $this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile2)); $this->assertTrue($uploadedFile->Filename !== $uploadedFile2->Filename, 'Filename is not the same'); $this->assertTrue($uploadedFile->ID !== $uploadedFile2->ID, 'File database record is not the same'); }
public function testAlternatingRepeatedLoginAttempts() { Member::config()->lock_out_after_incorrect_logins = 3; // ATTEMPTING LOG-IN TWICE WITH ONE ACCOUNT AND TWICE WITH ANOTHER SHOULDN'T LOCK ANYBODY OUT $this->doTestLoginForm('*****@*****.**', 'incorrectpassword'); $this->doTestLoginForm('*****@*****.**', 'incorrectpassword'); $this->doTestLoginForm('*****@*****.**', 'incorrectpassword'); $this->doTestLoginForm('*****@*****.**', 'incorrectpassword'); $member1 = DataObject::get_by_id("SilverStripe\\Security\\Member", $this->idFromFixture('SilverStripe\\Security\\Member', 'test')); $member2 = DataObject::get_by_id("SilverStripe\\Security\\Member", $this->idFromFixture('SilverStripe\\Security\\Member', 'noexpiry')); $this->assertNull($member1->LockedOutUntil); $this->assertNull($member2->LockedOutUntil); // BUT, DOING AN ADDITIONAL LOG-IN WITH EITHER OF THEM WILL LOCK OUT, SINCE THAT IS THE 3RD FAILURE IN // THIS SESSION $this->doTestLoginForm('*****@*****.**', 'incorrectpassword'); $member1 = DataObject::get_by_id("SilverStripe\\Security\\Member", $this->idFromFixture('SilverStripe\\Security\\Member', 'test')); $this->assertNotNull($member1->LockedOutUntil); $this->doTestLoginForm('*****@*****.**', 'incorrectpassword'); $member2 = DataObject::get_by_id("SilverStripe\\Security\\Member", $this->idFromFixture('SilverStripe\\Security\\Member', 'noexpiry')); $this->assertNotNull($member2->LockedOutUntil); }
/** * If an object ID is set, add the object to the list * * @param GridField $gridField * @param SS_List $dataList * @return SS_List */ public function getManipulatedData(GridField $gridField, SS_List $dataList) { $objectID = $gridField->State->GridFieldAddRelation(null); if (empty($objectID)) { return $dataList; } $object = DataObject::get_by_id($gridField->getModelClass(), $objectID); if ($object) { $dataList->add($object); } $gridField->State->GridFieldAddRelation = null; return $dataList; }
/** * Get the list of groups that the given member belongs to. * * Call without an argument to get the groups that the current member * belongs to. In this case, the results will be session-cached. * * @param int $memberID The ID of the member. Leave blank for the current * member. * @return array Returns a list of group IDs to which the member belongs * to or NULL. */ public static function groupList($memberID = null) { // Default to current member, with session-caching if (!$memberID) { $member = Member::currentUser(); if ($member && isset($_SESSION['Permission_groupList'][$member->ID])) { return $_SESSION['Permission_groupList'][$member->ID]; } } else { $member = DataObject::get_by_id("SilverStripe\\Security\\Member", $memberID); } if ($member) { // Build a list of the IDs of the groups. Most of the heavy lifting // is done by Member::Groups // NOTE: This isn't effecient; but it's called once per session so // it's a low priority to fix. $groups = $member->Groups(); $groupList = array(); if ($groups) { foreach ($groups as $group) { $groupList[] = $group->ID; } } // Session caching if (!$memberID) { $_SESSION['Permission_groupList'][$member->ID] = $groupList; } return isset($groupList) ? $groupList : null; } }
/** * Write a Money object to the database, then re-read it to ensure it * is re-read properly. */ public function testGettingWrittenDataObject() { $local = i18n::get_locale(); //make sure that the $ amount is not prefixed by US$, as it would be in non-US locale i18n::set_locale('en_US'); $obj = new MoneyTest_DataObject(); $m = new DBMoney(); $m->setAmount(987.65); $m->setCurrency('USD'); $obj->MyMoney = $m; $this->assertEquals("\$987.65", $obj->MyMoney->Nice(), "Money field not added to data object properly when read prior to first writing the record."); $objID = $obj->write(); $moneyTest = DataObject::get_by_id('MoneyTest_DataObject', $objID); $this->assertTrue($moneyTest instanceof MoneyTest_DataObject); $this->assertEquals('USD', $moneyTest->MyMoneyCurrency); $this->assertEquals(987.65, $moneyTest->MyMoneyAmount); $this->assertEquals("\$987.65", $moneyTest->MyMoney->Nice(), "Money field not added to data object properly when read."); i18n::set_locale($local); }
/** * Test import with custom identifiers by importing the data. * * @todo Test duplicateCheck callbacks */ public function testLoadWithIdentifiers() { // first load $loader = new CsvBulkLoader('CsvBulkLoaderTest_Player'); $filepath = $this->getCurrentAbsolutePath() . '/CsvBulkLoaderTest_PlayersWithId.csv'; $loader->duplicateChecks = array('ExternalIdentifier' => 'ExternalIdentifier', 'NonExistantIdentifier' => 'ExternalIdentifier', 'ExternalIdentifier' => 'ExternalIdentifier', 'AdditionalIdentifier' => 'ExternalIdentifier'); $results = $loader->load($filepath); $createdPlayers = $results->Created(); $player = $createdPlayers->First(); $this->assertEquals($player->FirstName, 'John'); $this->assertEquals($player->Biography, 'He\'s a good guy', 'test updating of duplicate imports within the same import works'); // load with updated data $filepath = FRAMEWORK_PATH . '/tests/dev/CsvBulkLoaderTest_PlayersWithIdUpdated.csv'; $results = $loader->load($filepath); // HACK need to update the loaded record from the database $player = DataObject::get_by_id('CsvBulkLoaderTest_Player', $player->ID); $this->assertEquals($player->FirstName, 'JohnUpdated', 'Test updating of existing records works'); // null values are valid imported // $this->assertEquals($player->Biography, 'He\'s a good guy', // 'Test retaining of previous information on duplicate when overwriting with blank field'); }
/** * @return File */ public function getItem() { return DataObject::get_by_id('SilverStripe\\Assets\\File', $this->itemID); }
/** * Update the position and parent of a tree node. * Only saves the node if changes were made. * * Required data: * - 'ID': The moved node * - 'ParentID': New parent relation of the moved node (0 for root) * - 'SiblingIDs': Array of all sibling nodes to the moved node (incl. the node itself). * In case of a 'ParentID' change, relates to the new siblings under the new parent. * * @param HTTPRequest $request * @return HTTPResponse JSON string with a * @throws HTTPResponse_Exception */ public function savetreenode($request) { if (!SecurityToken::inst()->checkRequest($request)) { return $this->httpError(400); } if (!Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN')) { $this->getResponse()->setStatusCode(403, _t('LeftAndMain.CANT_REORGANISE', "You do not have permission to rearange the site tree. Your change was not saved.")); return; } $className = $this->stat('tree_class'); $statusUpdates = array('modified' => array()); $id = $request->requestVar('ID'); $parentID = $request->requestVar('ParentID'); if ($className == 'SilverStripe\\CMS\\Model\\SiteTree' && ($page = DataObject::get_by_id('Page', $id))) { $root = $page->getParentType(); if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) { $this->getResponse()->setStatusCode(403, _t('LeftAndMain.CANT_REORGANISE', "You do not have permission to alter Top level pages. Your change was not saved.")); return; } } $siblingIDs = $request->requestVar('SiblingIDs'); $statusUpdates = array('modified' => array()); if (!is_numeric($id) || !is_numeric($parentID)) { throw new InvalidArgumentException(); } $node = DataObject::get_by_id($className, $id); if ($node && !$node->canEdit()) { return Security::permissionFailure($this); } if (!$node) { $this->getResponse()->setStatusCode(500, _t('LeftAndMain.PLEASESAVE', "Please Save Page: This page could not be updated because it hasn't been saved yet.")); return; } // Update hierarchy (only if ParentID changed) if ($node->ParentID != $parentID) { $node->ParentID = (int) $parentID; $node->write(); $statusUpdates['modified'][$node->ID] = array('TreeTitle' => $node->TreeTitle); // Update all dependent pages if (class_exists('SilverStripe\\CMS\\Model\\VirtualPage')) { $virtualPages = VirtualPage::get()->filter("CopyContentFromID", $node->ID); foreach ($virtualPages as $virtualPage) { $statusUpdates['modified'][$virtualPage->ID] = array('TreeTitle' => $virtualPage->TreeTitle()); } } $this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.'))); } // Update sorting if (is_array($siblingIDs)) { $counter = 0; foreach ($siblingIDs as $id) { if ($id == $node->ID) { $node->Sort = ++$counter; $node->write(); $statusUpdates['modified'][$node->ID] = array('TreeTitle' => $node->TreeTitle); } else { if (is_numeric($id)) { // Nodes that weren't "actually moved" shouldn't be registered as // having been edited; do a direct SQL update instead ++$counter; DB::prepared_query("UPDATE \"{$className}\" SET \"Sort\" = ? WHERE \"ID\" = ?", array($counter, $id)); } } } $this->getResponse()->addHeader('X-Status', rawurlencode(_t('LeftAndMain.REORGANISATIONSUCCESSFUL', 'Reorganised the site tree successfully.'))); } return Convert::raw2json($statusUpdates); }
/** * Get the whole tree of a part of the tree via an AJAX request. * * @param HTTPRequest $request * @return string * @throws Exception */ public function tree(HTTPRequest $request) { // Array sourceObject is an explicit list of values - construct a "flat tree" if (is_array($this->sourceObject)) { $output = "<ul class=\"tree\">\n"; foreach ($this->sourceObject as $k => $v) { $output .= '<li id="selector-' . $this->name . '-' . $k . '"><a>' . $v . '</a>'; } $output .= "</ul>"; return $output; } // Regular source specification $isSubTree = false; $this->search = $request->requestVar('search'); $id = is_numeric($request->latestParam('ID')) ? (int) $request->latestParam('ID') : (int) $request->requestVar('ID'); /** @var DataObject|Hierarchy $obj */ $obj = null; if ($id && !$request->requestVar('forceFullTree')) { $obj = DataObject::get_by_id($this->sourceObject, $id); $isSubTree = true; if (!$obj) { throw new Exception("TreeDropdownField->tree(): the object #{$id} of type {$this->sourceObject} could not be found"); } } else { if ($this->baseID) { $obj = DataObject::get_by_id($this->sourceObject, $this->baseID); } if (!$this->baseID || !$obj) { $obj = DataObject::singleton($this->sourceObject); } } // pre-process the tree - search needs to operate globally, not locally as marking filter does if ($this->search) { $this->populateIDs(); } if ($this->filterCallback || $this->search) { $obj->setMarkingFilterFunction(array($this, "filterMarking")); } $obj->markPartialTree($nodeCountThreshold = 30, $context = null, $this->childrenMethod, $this->numChildrenMethod); // allow to pass values to be selected within the ajax request if (isset($_REQUEST['forceValue']) || $this->value) { $forceValue = isset($_REQUEST['forceValue']) ? $_REQUEST['forceValue'] : $this->value; $values = preg_split('/,\\s*/', $forceValue); if ($values) { foreach ($values as $value) { if (!$value || $value == 'unchanged') { continue; } $obj->markToExpose($this->objectForKey($value)); } } } $self = $this; $titleFn = function (&$child) use(&$self) { /** @var DataObject|Hierarchy $child */ $keyField = $self->keyField; $labelField = $self->labelField; return sprintf('<li id="selector-%s-%s" data-id="%s" class="class-%s %s %s"><a rel="%d">%s</a>', Convert::raw2xml($self->getName()), Convert::raw2xml($child->{$keyField}), Convert::raw2xml($child->{$keyField}), Convert::raw2xml($child->class), Convert::raw2xml($child->markingClasses($self->numChildrenMethod)), $self->nodeIsDisabled($child) ? 'disabled' : '', (int) $child->ID, $child->obj($labelField)->forTemplate()); }; // Limit the amount of nodes shown for performance reasons. // Skip the check if we're filtering the tree, since its not clear how many children will // match the filter criteria until they're queried (and matched up with previously marked nodes). $nodeThresholdLeaf = Config::inst()->get('SilverStripe\\ORM\\Hierarchy\\Hierarchy', 'node_threshold_leaf'); if ($nodeThresholdLeaf && !$this->filterCallback && !$this->search) { $className = $this->sourceObject; $nodeCountCallback = function ($parent, $numChildren) use($className, $nodeThresholdLeaf) { if ($className === 'SilverStripe\\CMS\\Model\\SiteTree' && $parent->ID && $numChildren > $nodeThresholdLeaf) { return sprintf('<ul><li><span class="item">%s</span></li></ul>', _t('LeftAndMain.TooManyPages', 'Too many pages')); } return null; }; } else { $nodeCountCallback = null; } if ($isSubTree) { $html = $obj->getChildrenAsUL("", $titleFn, null, true, $this->childrenMethod, $this->numChildrenMethod, true, null, $nodeCountCallback); return substr(trim($html), 4, -5); } else { $html = $obj->getChildrenAsUL('class="tree"', $titleFn, null, true, $this->childrenMethod, $this->numChildrenMethod, true, null, $nodeCountCallback); return $html; } }