Example #1
0
 /**
  * @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"');
 }
Example #3
0
 /**
  * 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;
     }
 }
Example #7
0
 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;
 }
Example #13
0
    /**
     * 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;
    }
Example #14
0
 /**
  * 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;
 }
Example #15
0
 /**
  * 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;
    }
Example #19
0
 /**
  * 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}");
     }
 }
Example #20
0
    /**
     * 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());
 }
Example #23
0
 /**
  * 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' => 'damian@thefans.com'), array('Name' => 'Richard', 'Email' => 'richie@richers.com'), 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' => 'damian@thefans.com'), array('Name' => 'Richard', 'Email' => 'richie@richers.com'), array('Name' => 'Stephen'), array('Name' => 'Mitch')), $nonEmptyOnly);
     // Filter by many including null, empty string, and non-empty
     $items1 = $list->filter('Email', array(null, '', 'damian@thefans.com'));
     $this->assertDOSEquals(array(array('Name' => 'Damian', 'Email' => 'damian@thefans.com'), array('Name' => 'Stephen'), array('Name' => 'Mitch'), array('Name' => 'Hamish')), $items1);
     // Filter exclusion of above list
     $items2 = $list->filter('Email:not', array(null, '', 'damian@thefans.com'));
     $this->assertDOSEquals(array(array('Name' => 'Richard', 'Email' => 'richie@richers.com')), $items2);
     // Filter by many including empty string and non-empty
     $items3 = $list->filter('Email', array('', 'damian@thefans.com'));
     $this->assertDOSEquals(array(array('Name' => 'Damian', 'Email' => 'damian@thefans.com'), 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('', 'damian@thefans.com'));
     $this->assertDOSEquals(array(array('Name' => 'Richard', 'Email' => 'richie@richers.com'), 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('', 'damian@thefans.com'), 'Email' => null));
     $this->assertDOSEquals(array(array('Name' => 'Richard', 'Email' => 'richie@richers.com'), 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;
         }
     }
 }
Example #26
0
 /**
  * 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;
             }
         }
     }
 }