/** * @todo Test the results of a publication better */ public function testPublish() { $page1 = $this->objFromFixture('Page', "page1"); $page2 = $this->objFromFixture('Page', "page2"); $this->session()->inst_set('loggedInAs', $this->idFromFixture('Member', 'admin')); $response = $this->get('admin/pages/publishall?confirm=1'); $this->assertContains('Done: Published 30 pages', $response->getBody()); $actions = CMSBatchActionHandler::config()->batch_actions; // Some modules (e.g., cmsworkflow) will remove this action $actions = CMSBatchActionHandler::config()->batch_actions; if (isset($actions['publish'])) { $response = $this->get('admin/pages/batchactions/publish?ajax=1&csvIDs=' . implode(',', array($page1->ID, $page2->ID))); $responseData = Convert::json2array($response->getBody()); $this->assertArrayHasKey($page1->ID, $responseData['modified']); $this->assertArrayHasKey($page2->ID, $responseData['modified']); } // Get the latest version of the redirector page $pageID = $this->idFromFixture('RedirectorPage', 'page5'); $latestID = DB::prepared_query('select max("Version") from "RedirectorPage_versions" where "RecordID" = ?', array($pageID))->value(); $dsCount = DB::prepared_query('select count("Version") from "RedirectorPage_versions" where "RecordID" = ? and "Version"= ?', array($pageID, $latestID))->value(); $this->assertEquals(1, $dsCount, "Published page has no duplicate version records: it has " . $dsCount . " for version " . $latestID); $this->session()->clear('loggedInAs'); //$this->assertRegexp('/Done: Published 4 pages/', $response->getBody()) /* $response = Director::test("admin/pages/publishitems", array( 'ID' => '' 'Title' => '' 'action_publish' => 'Save and publish', ), $session); $this->assertRegexp('/Done: Published 4 pages/', $response->getBody()) */ }
public function testSaveWithArrayValueSet() { $article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags'); $articleWithTags = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithtags'); $tag1 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag1'); $tag2 = $this->objFromFixture('CheckboxSetFieldTest_Tag', 'tag2'); /* Create a CheckboxSetField with 2 items selected. Note that the array is in the format (key) => (selected) */ $field = new CheckboxSetField("Tags", "Test field", DataObject::get("CheckboxSetFieldTest_Tag")->map()); $field->setValue(array($tag1->ID => true, $tag2->ID => true)); /* Saving should work */ $field->saveInto($article); $this->assertEquals(array($tag1->ID, $tag2->ID), DB::prepared_query("SELECT \"CheckboxSetFieldTest_TagID\"\n\t\t\t\tFROM \"CheckboxSetFieldTest_Article_Tags\"\n\t\t\t\tWHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_ArticleID\" = ?", array($article->ID))->column(), 'Data shold be saved into CheckboxSetField manymany relation table on the "right end"'); $this->assertEquals(array($articleWithTags->ID, $article->ID), DB::query("SELECT \"CheckboxSetFieldTest_ArticleID\"\n\t\t\t\tFROM \"CheckboxSetFieldTest_Article_Tags\"\n\t\t\t\tWHERE \"CheckboxSetFieldTest_Article_Tags\".\"CheckboxSetFieldTest_TagID\" = {$tag1->ID}\n\t\t\t")->column(), 'Data shold be saved into CheckboxSetField manymany relation table on the "left end"'); }
/** * Initialisation function that is run before any action on the controller is called. * * @uses BasicAuth::requireLogin() */ public function init() { if ($this->basicAuthEnabled) { BasicAuth::protect_site_if_necessary(); } // Directly access the session variable just in case the Group or Member tables don't yet exist if (Member::config()->log_last_visited) { Deprecation::notice('4.0', 'Member::$LastVisited is deprecated. From 4.0 onwards you should implement this as a custom extension'); if (Session::get('loggedInAs') && Security::database_is_ready() && ($member = Member::currentUser())) { DB::prepared_query(sprintf('UPDATE "Member" SET "LastVisited" = %s WHERE "ID" = ?', DB::get_conn()->now()), array($member->ID)); } } // This is used to test that subordinate controllers are actually calling parent::init() - a common bug $this->baseInitCalled = true; }
public function run($request) { $pages = 0; $links = 0; $linkedPages = new DataList('SiteTree'); $linkedPages = $linkedPages->innerJoin('SiteTree_LinkTracking', '"SiteTree_LinkTracking"."SiteTreeID" = "SiteTree"."ID"'); if ($linkedPages) { foreach ($linkedPages as $page) { $tracking = DB::prepared_query('SELECT "ChildID", "FieldName" FROM "SiteTree_LinkTracking" WHERE "SiteTreeID" = ?', array($page->ID))->map(); foreach ($tracking as $childID => $fieldName) { $linked = DataObject::get_by_id('SiteTree', $childID); // TOOD: Replace in all HTMLText fields $page->Content = preg_replace("/href *= *([\"']?){$linked->URLSegment}\\/?/i", "href=\$1[sitetree_link,id={$linked->ID}]", $page->Content, -1, $replaced); if ($replaced) { $links += $replaced; } } $page->write(); $pages++; } } echo "Rewrote {$links} link(s) on {$pages} page(s) to use shortcodes.\n"; }
/** * @uses LeftAndMainExtension->augmentNewSiteTreeItem() */ public function getNewItem($id, $setID = true) { $parentClass = $this->stat('tree_class'); list($dummy, $className, $parentID, $suffix) = array_pad(explode('-', $id), 4, null); if (!is_subclass_of($className, $parentClass) && strcasecmp($className, $parentClass) != 0) { $response = Security::permissionFailure($this); if (!$response) { $response = $this->response; } throw new SS_HTTPResponse_Exception($response); } $newItem = new $className(); if (!$suffix) { $sessionTag = "NewItems." . $parentID . "." . $className; if (Session::get($sessionTag)) { $suffix = '-' . Session::get($sessionTag); Session::set($sessionTag, Session::get($sessionTag) + 1); } else { Session::set($sessionTag, 1); } $id = $id . $suffix; } $newItem->Title = _t('CMSMain.NEWPAGE', "New {pagetype}", 'followed by a page type title', array('pagetype' => singleton($className)->i18n_singular_name())); $newItem->ClassName = $className; $newItem->ParentID = $parentID; // DataObject::fieldExists only checks the current class, not the hierarchy // This allows the CMS to set the correct sort value if ($newItem->castingHelper('Sort')) { $newItem->Sort = DB::prepared_query('SELECT MAX("Sort") FROM "SiteTree" WHERE "ParentID" = ?', array($parentID))->value() + 1; } if ($setID) { $newItem->ID = $id; } # Some modules like subsites add extra fields that need to be set when the new item is created $this->extend('augmentNewSiteTreeItem', $newItem); return $newItem; }
/** * Pre-populate the cache for Versioned::get_versionnumber_by_stage() for * a list of record IDs, for more efficient database querying. If $idList * is null, then every page will be pre-cached. * * @param string $class * @param string $stage * @param array $idList */ public static function prepopulate_versionnumber_cache($class, $stage, $idList = null) { if (!Config::inst()->get('Versioned', 'prepopulate_versionnumber_cache')) { return; } $filter = ""; $parameters = array(); if ($idList) { // Validate the ID list foreach ($idList as $id) { if (!is_numeric($id)) { user_error("Bad ID passed to Versioned::prepopulate_versionnumber_cache() in \$idList: " . $id, E_USER_ERROR); } } $filter = 'WHERE "ID" IN (' . DB::placeholders($idList) . ')'; $parameters = $idList; } $baseClass = ClassInfo::baseDataClass($class); $stageTable = $stage == 'Stage' ? $baseClass : "{$baseClass}_{$stage}"; $versions = DB::prepared_query("SELECT \"ID\", \"Version\" FROM \"{$stageTable}\" {$filter}", $parameters)->map(); foreach ($versions as $id => $version) { self::$cache_versionnumber[$baseClass][$stage][$id] = $version; } }
public function onAfterWrite() { parent::onAfterWrite(); // Don't do this stuff when we're publishing if (!$this->extension_instances['Versioned']->migratingVersion) { if ($this->isChanged('CopyContentFromID') && $this->CopyContentFromID != 0 && $this instanceof VirtualPage) { $this->updateImageTracking(); } } // Check if page type has changed to a non-virtual page. // Caution: Relies on the fact that the current instance is still of the old page type. if ($this->isChanged('ClassName', 2)) { $changed = $this->getChangedFields(); $classBefore = $changed['ClassName']['before']; $classAfter = $changed['ClassName']['after']; if ($classBefore != $classAfter) { // Remove all database rows for the old page type to avoid inconsistent data retrieval. // TODO This should apply to all page type changes, not only on VirtualPage - but needs // more comprehensive testing as its a destructive operation $removedTables = array_diff(ClassInfo::dataClassesFor($classBefore), ClassInfo::dataClassesFor($classAfter)); if ($removedTables) { foreach ($removedTables as $removedTable) { // Note: *_versions records are left intact foreach (array('', 'Live') as $stage) { if ($stage) { $removedTable = "{$removedTable}_{$stage}"; } DB::prepared_query("DELETE FROM \"{$removedTable}\" WHERE \"ID\" = ?", array($this->ID)); } } } // Also publish the change immediately to avoid inconsistent behaviour between // a non-virtual draft and a virtual live record (e.g. republishing the original record // shouldn't republish the - now unrelated - changes on the ex-VirtualPage draft). // Copies all stage fields to live as well. // @todo Update get_one to support parameterised queries $source = DataObject::get_by_id("SiteTree", $this->CopyContentFromID); $this->copyFrom($source); $this->publish('Stage', 'Live'); // Change reference on instance (as well as removing the underlying database tables) $this->CopyContentFromID = 0; } } }
protected function addRecord($record) { if ($record['RecordID'] != $record['ParentID']) { // This is only used retain the latest version of the record. DB::prepared_query("REPLACE INTO {$this->replayTable}({$this->replaceColumnString}) VALUES(?, ?, ?, ?, ?);", array($record['RecordID'], $record['ID'], $record['ParentID'], $record['URLSegment'], $record['Version'])); } }
/** * Post a message to the forum. This method is called whenever you want to make a * new post or edit an existing post on the forum * * @param Array - Data * @param Form - Submitted Form */ function doPostMessageForm($data, $form) { $member = Member::currentUser(); $content = isset($data['Content']) ? $this->filterLanguage($data["Content"]) : ""; $title = isset($data['Title']) ? $this->filterLanguage($data["Title"]) : false; // If a thread id is passed append the post to the thread. Otherwise create // a new thread $thread = false; if (isset($data['ThreadID'])) { $thread = DataObject::get_by_id('ForumThread', $data['ThreadID']); } // If this is a simple edit the post then handle it here. Look up the correct post, // make sure we have edit rights to it then update the post $post = false; if (isset($data['ID'])) { $post = DataObject::get_by_id('Post', $data['ID']); if ($post && $post->isFirstPost()) { if ($title) { $thread->Title = $title; } } } // Check permissions $messageSet = array('default' => _t('Forum.LOGINTOPOST', 'You\'ll need to login before you can post to that forum. Please do so below.'), 'alreadyLoggedIn' => _t('Forum.NOPOSTPERMISSION', 'I\'m sorry, but you do not have permission post to this forum.'), 'logInAgain' => _t('Forum.LOGINTOPOSTAGAIN', 'You have been logged out of the forums. If you would like to log in again to post, enter a username and password below.')); // Creating new thread if (!$thread && !$this->canPost()) { Security::permissionFailure($this, $messageSet); return false; } // Replying to existing thread if ($thread && !$post && !$thread->canPost()) { Security::permissionFailure($this, $messageSet); return false; } // Editing existing post if ($thread && $post && !$post->canEdit()) { Security::permissionFailure($this, $messageSet); return false; } if (!$thread) { $thread = new ForumThread(); $thread->ForumID = $this->ID; if ($title) { $thread->Title = $title; } $starting_thread = true; } // Upload and Save all files attached to the field // Attachment will always be blank, If they had an image it will be at least in Attachment-0 //$attachments = new DataObjectSet(); $attachments = new ArrayList(); if (!empty($data['Attachment-0']) && !empty($data['Attachment-0']['tmp_name'])) { $id = 0; // // @todo this only supports ajax uploads. Needs to change the key (to simply Attachment). // while (isset($data['Attachment-' . $id])) { $image = $data['Attachment-' . $id]; if ($image && !empty($image['tmp_name'])) { $file = Post_Attachment::create(); $file->OwnerID = Member::currentUserID(); $folder = Config::inst()->get('ForumHolder', 'attachments_folder'); try { $upload = Upload::create()->loadIntoFile($image, $file, $folder); $file->write(); $attachments->push($file); } catch (ValidationException $e) { $message = _t('Forum.UPLOADVALIDATIONFAIL', 'Unallowed file uploaded. Please only upload files of the following: '); $message .= implode(', ', Config::inst()->get('File', 'allowed_extensions')); $form->addErrorMessage('Attachment', $message, 'bad'); Session::set("FormInfo.Form_PostMessageForm.data", $data); return $this->redirectBack(); } } $id++; } } // from now on the user has the correct permissions. save the current thread settings $thread->write(); if (!$post || !$post->canEdit()) { $post = new Post(); $post->AuthorID = $member ? $member->ID : 0; $post->ThreadID = $thread->ID; } $post->ForumID = $thread->ForumID; $post->Content = $content; $post->write(); if ($attachments) { foreach ($attachments as $attachment) { $attachment->PostID = $post->ID; $attachment->write(); } } // Add a topic subscription entry if required $isSubscribed = ForumThread_Subscription::already_subscribed($thread->ID); if (isset($data['TopicSubscription'])) { if (!$isSubscribed) { // Create a new topic subscription for this member $obj = new ForumThread_Subscription(); $obj->ThreadID = $thread->ID; $obj->MemberID = Member::currentUserID(); $obj->write(); } } elseif ($isSubscribed) { // See if the member wanted to remove themselves DB::prepared_query('DELETE FROM "ForumThread_Subscription" WHERE "ThreadID" = ? AND "MemberID" = ?', array($post->ThreadID, $member->ID)); } // Send any notifications that need to be sent ForumThread_Subscription::notify($post); // Send any notifications to moderators of the forum if (Forum::$notify_moderators) { if (isset($starting_thread) && $starting_thread) { $this->notifyModerators($post, $thread, true); } else { $this->notifyModerators($post, $thread); } } return $this->redirect($post->Link()); }
/** * Execute this query. * * @return SS_Query */ public function execute() { $sql = $this->sql($parameters); return DB::prepared_query($sql, $parameters); }
/** * Return the number of rows in this query if the limit were removed. Useful in paged data sets. * * @param string $column * @return int */ public function unlimitedRowCount($column = null) { // we can't clear the select if we're relying on its output by a HAVING clause if (count($this->having)) { $records = $this->execute(); return $records->numRecords(); } $clone = clone $this; $clone->limit = null; $clone->orderby = null; // Choose a default column if ($column == null) { if ($this->groupby) { // @todo Test case required here $countQuery = new SQLSelect(); $countQuery->setSelect("count(*)"); $countQuery->setFrom(array('(' . $clone->sql($innerParameters) . ') all_distinct')); $sql = $countQuery->sql($parameters); // $parameters should be empty $result = DB::prepared_query($sql, $innerParameters); return $result->value(); } else { $clone->setSelect(array("count(*)")); } } else { $clone->setSelect(array("count({$column})")); } $clone->setGroupBy(array()); return $clone->execute()->value(); }
/** * Check that the given member has the given permission. * * @param int|Member memberID The ID of the member to check. Leave blank for the current member. * Alternatively you can use a member object. * @param string|array $code Code of the permission to check (case-sensitive) * @param string $arg Optional argument (e.g. a permissions for a specific page) * @param bool $strict Use "strict" checking (which means a permission * will be granted if the key does not exist at all)? * @return int|bool The ID of the permission record if the permission * exists; FALSE otherwise. If "strict" checking is * disabled, TRUE will be returned if the permission does not exist at all. */ public static function checkMember($member, $code, $arg = "any", $strict = true) { if (!$member) { $memberID = $member = Member::currentUserID(); } else { $memberID = is_object($member) ? $member->ID : $member; } if ($arg == 'any') { // Cache the permissions in memory if (!isset(self::$cache_permissions[$memberID])) { self::$cache_permissions[$memberID] = self::permissions_for_member($memberID); } // If $admin_implies_all was false then this would be inefficient, but that's an edge // case and this keeps the code simpler if (!is_array($code)) { $code = array($code); } if (Config::inst()->get('Permission', 'admin_implies_all')) { $code[] = "ADMIN"; } // Multiple $code values - return true if at least one matches, ie, intersection exists return (bool) array_intersect($code, self::$cache_permissions[$memberID]); } // Code filters $codeParams = is_array($code) ? $code : array($code); $codeClause = DB::placeholders($codes); $adminParams = self::$admin_implies_all ? array('ADMIN') : array(); $adminClause = self::$admin_implies_all ? ", ?" : ''; // The following code should only be used if you're not using the "any" arg. This is kind // of obselete functionality and could possibly be deprecated. $groupParams = self::groupList($memberID); if (empty($groupParams)) { return false; } $groupClause = DB::placeholders($groupParams); // Arg component $argClause = ""; $argParams = array(); switch ($arg) { case "any": break; case "all": $argClause = " AND \"Arg\" = ?"; $argParams = array(-1); break; default: if (is_numeric($arg)) { $argClause = "AND \"Arg\" IN (?, ?) "; $argParams = array(-1, $arg); } else { user_error("Permission::checkMember: bad arg '{$arg}'", E_USER_ERROR); } } $adminFilter = Config::inst()->get('Permission', 'admin_implies_all') ? ",'ADMIN'" : ''; // Raw SQL for efficiency $permission = DB::prepared_query("SELECT \"ID\"\n\t\t\tFROM \"Permission\"\n\t\t\tWHERE (\n\t\t\t\t\"Code\" IN ({$codeClause} {$adminClause})\n\t\t\t\tAND \"Type\" = ?\n\t\t\t\tAND \"GroupID\" IN ({$groupClause})\n\t\t\t\t{$argClause}\n\t\t\t)", array_merge($codeParams, $adminParams, array(self::GRANT_PERMISSION), $groupParams, $argParams))->value(); if ($permission) { return $permission; } // Strict checking disabled? if (!Config::inst()->get('Permission', 'strict_checking') || !$strict) { $hasPermission = DB::prepared_query("SELECT COUNT(*)\n\t\t\t\tFROM \"Permission\"\n\t\t\t\tWHERE (\n\t\t\t\t\t\"Code\" IN ({$codeClause}) AND\n\t\t\t\t\t\"Type\" = ?\n\t\t\t\t)", array_merge($codeParams, array(self::GRANT_PERMISSION)))->value(); if (!$hasPermission) { return; } } return false; }
/** * Return a link to show this post * * @return String */ function Link($action = "show") { // only include the forum thread ID in the URL if we're showing the thread either // by showing the posts or replying therwise we only need to pass a single ID. $includeThreadID = $action == "show" || $action == "reply" ? true : false; $link = $this->Thread()->Link($action, $includeThreadID); // calculate what page results the post is on // the count is the position of the post in the thread $count = DB::prepared_query(' SELECT COUNT("ID") FROM "Post" WHERE "ThreadID" = ? AND "Status" = \'Moderated\' AND "ID" < ?', array($this->ThreadID, $this->ID))->value(); $start = $count >= Forum::$posts_per_page ? floor($count / Forum::$posts_per_page) * Forum::$posts_per_page : 0; $pos = ($start == 0 ? '' : "?start={$start}") . ($count == 0 ? '' : "#post{$this->ID}"); return $action == "show" ? $link . $pos : $link; }
/** * Check that the given member has the given permission. * * @param int|Member memberID The ID of the member to check. Leave blank for the current member. * Alternatively you can use a member object. * @param string|array $code Code of the permission to check (case-sensitive) * @param string $arg Optional argument (e.g. a permissions for a specific page) * @param bool $strict Use "strict" checking (which means a permission * will be granted if the key does not exist at all)? * @return int|bool The ID of the permission record if the permission * exists; FALSE otherwise. If "strict" checking is * disabled, TRUE will be returned if the permission does not exist at all. */ public static function checkMember($member, $code, $arg = "any", $strict = true) { if (!$member) { $memberID = $member = Member::currentUserID(); } else { $memberID = is_object($member) ? $member->ID : $member; } if (!$memberID) { return false; } // Turn the code into an array as we may need to add other permsissions to the set we check if (!is_array($code)) { $code = array($code); } if ($arg == 'any') { $adminImpliesAll = (bool) Config::inst()->get('Permission', 'admin_implies_all'); // Cache the permissions in memory if (!isset(self::$cache_permissions[$memberID])) { self::$cache_permissions[$memberID] = self::permissions_for_member($memberID); } foreach ($code as $permCode) { if ($permCode === 'CMS_ACCESS') { foreach (self::$cache_permissions[$memberID] as $perm) { //if they have admin rights OR they have an explicit access to the CMS then give permission if ($adminImpliesAll && $perm == 'ADMIN' || substr($perm, 0, 11) === 'CMS_ACCESS_') { return true; } } } elseif (substr($permCode, 0, 11) === 'CMS_ACCESS_' && !in_array('CMS_ACCESS_LeftAndMain', $code)) { //cms_access_leftandmain means access to all CMS areas $code[] = 'CMS_ACCESS_LeftAndMain'; } } // if ADMIN has all privileges, then we need to push that code in if ($adminImpliesAll) { $code[] = "ADMIN"; } // Multiple $code values - return true if at least one matches, ie, intersection exists return (bool) array_intersect($code, self::$cache_permissions[$memberID]); } // Code filters $codeParams = is_array($code) ? $code : array($code); $codeClause = DB::placeholders($codeParams); $adminParams = self::$admin_implies_all ? array('ADMIN') : array(); $adminClause = self::$admin_implies_all ? ", ?" : ''; // The following code should only be used if you're not using the "any" arg. This is kind // of obselete functionality and could possibly be deprecated. $groupParams = self::groupList($memberID); if (empty($groupParams)) { return false; } $groupClause = DB::placeholders($groupParams); // Arg component $argClause = ""; $argParams = array(); switch ($arg) { case "any": break; case "all": $argClause = " AND \"Arg\" = ?"; $argParams = array(-1); break; default: if (is_numeric($arg)) { $argClause = "AND \"Arg\" IN (?, ?) "; $argParams = array(-1, $arg); } else { user_error("Permission::checkMember: bad arg '{$arg}'", E_USER_ERROR); } } $adminFilter = Config::inst()->get('Permission', 'admin_implies_all') ? ",'ADMIN'" : ''; // Raw SQL for efficiency $permission = DB::prepared_query("SELECT \"ID\"\n\t\t\tFROM \"Permission\"\n\t\t\tWHERE (\n\t\t\t\t\"Code\" IN ({$codeClause} {$adminClause})\n\t\t\t\tAND \"Type\" = ?\n\t\t\t\tAND \"GroupID\" IN ({$groupClause})\n\t\t\t\t{$argClause}\n\t\t\t)", array_merge($codeParams, $adminParams, array(self::GRANT_PERMISSION), $groupParams, $argParams))->value(); if ($permission) { return $permission; } // Strict checking disabled? if (!Config::inst()->get('Permission', 'strict_checking') || !$strict) { $hasPermission = DB::prepared_query("SELECT COUNT(*)\n\t\t\t\tFROM \"Permission\"\n\t\t\t\tWHERE (\n\t\t\t\t\t\"Code\" IN ({$codeClause}) AND\n\t\t\t\t\t\"Type\" = ?\n\t\t\t\t)", array_merge($codeParams, array(self::GRANT_PERMISSION)))->value(); if (!$hasPermission) { return; } } return false; }
/** * Check if this page has been published. * * @return bool */ public function isPublished() { if ($this->isNew()) { return false; } return DB::prepared_query("SELECT \"ID\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($this->ID))->value() ? true : false; }
/** * Number of posts for this person * * @return int */ public function NumPosts() { if (!$this->owner->exists()) { return 0; } return (int) DB::prepared_query("SELECT count(*) FROM \"Post\" WHERE \"AuthorID\" = ?", array($this->owner->ID))->value(); }
/** * Check if this record exists on the draft stage * * @return bool */ public function isOnDraft() { if (!$this->owner->isInDB()) { return false; } $table = ClassInfo::baseDataClass($this->owner->class); $result = DB::prepared_query("SELECT COUNT(*) FROM \"{$table}\" WHERE \"{$table}\".\"ID\" = ?", array($this->owner->ID)); return (bool) $result->value(); }
/** * Get the latest members from the forum group. * * @param int $limit Number of members to return * @return ArrayList */ function getLatestMembers($limit = null) { if (!is_null($limit)) { Deprecation::notice('1.0', '$limit parameter is deprecated, please chain the limit clause'); } $groupID = DB::query('SELECT "ID" FROM "Group" WHERE "Code" = \'forum-members\'')->value(); // if we're just looking for a single MemberID, do a quicker query on the join table. if ($limit == 1) { $latestMemberId = DB::prepared_query('SELECT MAX("MemberID") FROM "Group_Members" WHERE "Group_Members"."GroupID" = ?', array($groupID))->value(); $latestMembers = Member::get()->byId($latestMemberId); } else { $latestMembers = Member::get()->leftJoin('Group_Members', '"Member"."ID" = "Group_Members"."MemberID"')->filter('GroupID', $groupID)->sort('"Member"."ID" DESC'); if ($limit) { $latestMembers = $latestMembers->limit($limit); } } return $latestMembers; }
/** * Ensures that the latest version of a record is the expected value * * @param type $record * @param type $version */ protected function assertRecordHasLatestVersion($record, $version) { foreach (ClassInfo::ancestry(get_class($record), true) as $table) { $versionForClass = DB::prepared_query($sql = "SELECT MAX(\"Version\") FROM \"{$table}_versions\" WHERE \"RecordID\" = ?", array($record->ID))->value(); $this->assertEquals($version, $versionForClass, "That the table {$table} has the latest version {$version}"); } }
/** * Check if the user has permissions to run URL debug tools, * else redirect them to log in. */ public static function require_developer_login() { if (Director::isDev()) { return; } if (isset($_SESSION['loggedInAs'])) { // We have to do some raw SQL here, because this method is called in Object::defineMethods(). // This means we have to be careful about what objects we create, as we don't want Object::defineMethods() // being called again. // This basically calls Permission::checkMember($_SESSION['loggedInAs'], 'ADMIN'); // @TODO - Rewrite safely using DataList::filter $memberID = $_SESSION['loggedInAs']; $permission = DB::prepared_query(' SELECT "ID" FROM "Permission" INNER JOIN "Group_Members" ON "Permission"."GroupID" = "Group_Members"."GroupID" WHERE "Permission"."Code" = ? AND "Permission"."Type" = ? AND "Group_Members"."MemberID" = ?', array('ADMIN', Permission::GRANT_PERMISSION, $memberID))->value(); if ($permission) { return; } } // This basically does the same as // Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.") // We have to do this because of how early this method is called in execution. $_SESSION['Security']['Message']['message'] = "You need to login with developer access to make use of debugging tools."; $_SESSION['Security']['Message']['type'] = 'warning'; $_SESSION['BackURL'] = $_SERVER['REQUEST_URI']; header($_SERVER['SERVER_PROTOCOL'] . " 302 Found"); header("Location: " . Director::baseURL() . Security::login_url()); die; }
/** * Checks to see if a Member is already subscribed to this thread * * @param int $threadID The ID of the thread to check * @param int $memberID The ID of the currently logged in member (Defaults to Member::currentUserID()) * * @return bool true if they are subscribed, false if they're not */ static function already_subscribed($threadID, $memberID = null) { if ($threadID && !$memberID) { $memberID = Member::currentUserID(); } if (!$threadID || !$memberID) { return false; } return DB::prepared_query(' SELECT COUNT("ID") FROM "ForumThread_Subscription" WHERE "ThreadID" = ? AND "MemberID" = ?', array($threadID, $memberID))->value() > 0; }
public function testTwoFileRenamesInARowWork() { $page = $this->objFromFixture('Page', 'page1'); $this->assertTrue($page->doPublish()); $this->assertContains('<img src="/assets/FileLinkTrackingTest/55b443b601/testscript-test-file.jpg"', DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value()); // Rename the file twice $file = $this->objFromFixture('Image', 'file1'); $file->Name = 'renamed-test-file.jpg'; $file->write(); // TODO Workaround for bug in DataObject->getChangedFields(), which returns stale data, // and influences File->updateFilesystem() $file = DataObject::get_by_id('File', $file->ID); $file->Name = 'renamed-test-file-second-time.jpg'; $file->write(); // Confirm that the correct image is shown in both the draft and live site $this->assertContains('<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file-second-time.jpg"', DB::prepared_query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($page->ID))->value()); $this->assertContains('<img src="/assets/FileLinkTrackingTest/55b443b601/renamed-test-file-second-time.jpg"', DB::prepared_query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = ?", array($page->ID))->value()); }
/** * Construct a child of this Folder with the given name. * It does this without actually using the object model, as this starts messing * with all the data. Rather, it does a direct database insert. * * @param string $name Name of the file or folder * @return integer the ID of the newly saved File record */ public function constructChild($name) { // Determine the class name - File, Folder or Image $baseDir = $this->FullPath; if (is_dir($baseDir . $name)) { $className = "Folder"; } else { $className = File::get_class_for_file_extension(pathinfo($name, PATHINFO_EXTENSION)); } $ownerID = Member::currentUserID(); $filename = $this->Filename . $name; if ($className == 'Folder') { $filename .= '/'; } $nowExpression = DB::get_conn()->now(); DB::prepared_query("INSERT INTO \"File\"\n\t\t\t(\"ClassName\", \"ParentID\", \"OwnerID\", \"Name\", \"Filename\", \"Created\", \"LastEdited\", \"Title\")\n\t\t\tVALUES (?, ?, ?, ?, ?, {$nowExpression}, {$nowExpression}, ?)", array($className, $this->ID, $ownerID, $name, $filename, $name)); return DB::get_generated_id("File"); }
public function testFilterByNull() { $list = DataObjectTest_Fan::get(); // Force DataObjectTest_Fan/fan5::Email to empty string $fan5id = $this->idFromFixture('DataObjectTest_Fan', 'fan5'); DB::prepared_query("UPDATE \"DataObjectTest_Fan\" SET \"Email\" = '' WHERE \"ID\" = ?", array($fan5id)); // Filter by null email $nullEmails = $list->filter('Email', null); $this->assertDOSEquals(array(array('Name' => 'Stephen'), array('Name' => 'Mitch')), $nullEmails); // Filter by non-null $nonNullEmails = $list->filter('Email:not', null); $this->assertDOSEquals(array(array('Name' => 'Damian', 'Email' => '*****@*****.**'), array('Name' => 'Richard', 'Email' => '*****@*****.**'), array('Name' => 'Hamish')), $nonNullEmails); // Filter by empty only $emptyOnly = $list->filter('Email', ''); $this->assertDOSEquals(array(array('Name' => 'Hamish')), $emptyOnly); // Non-empty only. This should include null values, since ExactMatchFilter works around // the caveat that != '' also excludes null values in ANSI SQL-92 behaviour. $nonEmptyOnly = $list->filter('Email:not', ''); $this->assertDOSEquals(array(array('Name' => 'Damian', 'Email' => '*****@*****.**'), array('Name' => 'Richard', 'Email' => '*****@*****.**'), array('Name' => 'Stephen'), array('Name' => 'Mitch')), $nonEmptyOnly); // Filter by many including null, empty string, and non-empty $items1 = $list->filter('Email', array(null, '', '*****@*****.**')); $this->assertDOSEquals(array(array('Name' => 'Damian', 'Email' => '*****@*****.**'), array('Name' => 'Stephen'), array('Name' => 'Mitch'), array('Name' => 'Hamish')), $items1); // Filter exclusion of above list $items2 = $list->filter('Email:not', array(null, '', '*****@*****.**')); $this->assertDOSEquals(array(array('Name' => 'Richard', 'Email' => '*****@*****.**')), $items2); // Filter by many including empty string and non-empty $items3 = $list->filter('Email', array('', '*****@*****.**')); $this->assertDOSEquals(array(array('Name' => 'Damian', 'Email' => '*****@*****.**'), array('Name' => 'Hamish')), $items3); // Filter by many including empty string and non-empty // This also relies no the workaround for null comparison as in the $nonEmptyOnly test $items4 = $list->filter('Email:not', array('', '*****@*****.**')); $this->assertDOSEquals(array(array('Name' => 'Richard', 'Email' => '*****@*****.**'), array('Name' => 'Stephen'), array('Name' => 'Mitch')), $items4); // Filter by many including empty string and non-empty // The extra null check isn't necessary, but check that this doesn't fail $items5 = $list->filterAny(array('Email:not' => array('', '*****@*****.**'), 'Email' => null)); $this->assertDOSEquals(array(array('Name' => 'Richard', 'Email' => '*****@*****.**'), array('Name' => 'Stephen'), array('Name' => 'Mitch')), $items5); // Filter by null or empty values $items6 = $list->filter('Email', array(null, '')); $this->assertDOSEquals(array(array('Name' => 'Stephen'), array('Name' => 'Mitch'), array('Name' => 'Hamish')), $items6); }
/** * @param array $params Request params (unsanitized) */ public function __construct($params = null) { $this->ids = array(); $this->expanded = array(); $parents = array(); $q = $this->getQuery($params); $res = $q->execute(); if (!$res) { return; } // And keep a record of parents we don't need to get parents // of themselves, as well as IDs to mark foreach ($res as $row) { if ($row['ParentID']) { $parents[$row['ParentID']] = true; } $this->ids[$row['ID']] = true; } // We need to recurse up the tree, // finding ParentIDs for each ID until we run out of parents while (!empty($parents)) { $parentsClause = DB::placeholders($parents); $res = DB::prepared_query("SELECT \"ParentID\", \"ID\" FROM \"SiteTree\" WHERE \"ID\" in ({$parentsClause})", array_keys($parents)); $parents = array(); foreach ($res as $row) { if ($row['ParentID']) { $parents[$row['ParentID']] = true; } $this->ids[$row['ID']] = true; $this->expanded[$row['ID']] = true; } } }
/** * Delete the database record (recursively for folders) without touching the filesystem */ public function deleteDatabaseOnly() { if (is_numeric($this->ID)) { DB::prepared_query('DELETE FROM "File" WHERE "ID" = ?', array($this->ID)); } }
/** * Populate $this->searchIds with the IDs of the pages matching the searched parameter and their parents. * Reverse-constructs the tree starting from the leaves. Initially taken from CMSSiteTreeFilter, but modified * with pluggable search function. */ protected function populateIDs() { // get all the leaves to be displayed if ($this->searchCallback) { $res = call_user_func($this->searchCallback, $this->sourceObject, $this->labelField, $this->search); } else { $sourceObject = $this->sourceObject; $filters = array(); if (singleton($sourceObject)->hasDatabaseField($this->labelField)) { $filters["{$this->labelField}:PartialMatch"] = $this->search; } else { if (singleton($sourceObject)->hasDatabaseField('Title')) { $filters["Title:PartialMatch"] = $this->search; } if (singleton($sourceObject)->hasDatabaseField('Name')) { $filters["Name:PartialMatch"] = $this->search; } } if (empty($filters)) { throw new InvalidArgumentException(sprintf('Cannot query by %s.%s, not a valid database column', $sourceObject, $this->labelField)); } $res = DataObject::get($this->sourceObject)->filterAny($filters); } if ($res) { // iteratively fetch the parents in bulk, until all the leaves can be accessed using the tree control foreach ($res as $row) { if ($row->ParentID) { $parents[$row->ParentID] = true; } $this->searchIds[$row->ID] = true; } while (!empty($parents)) { $idsClause = DB::placeholders($parents); $res = DB::prepared_query("SELECT \"ParentID\", \"ID\" FROM \"{$this->sourceObject}\" WHERE \"ID\" in ({$idsClause})", array_keys($parents)); $parents = array(); foreach ($res as $row) { if ($row['ParentID']) { $parents[$row['ParentID']] = true; } $this->searchIds[$row['ID']] = true; $this->searchExpanded[$row['ID']] = true; } } } }