Working with Pages Programmatically

Improvements?

Let us know by posting here.

Programmatically Modifying Pages with Concrete

With Concrete, you can programmatically modify pages. Here's how you can set attributes, update properties, or manage pages.

Adjusting Page Attributes

To set the "Exclude from Nav" attribute to true:

$page = \Page::getCurrentPage();
$page->setAttribute('exclude_nav', true);

For complex attributes, such as an address:

$page = \Page::getCurrentPage();
$addressValues = array(
    'address1' => '123 SW Test',
    'address2' => 'Suite 100',
    'city' => 'Portland',
    'state_province' => 'OR',
    'postal_code' => '99999'
);
$page->setAttribute('address', $addressValues);

Editing Page Properties

Utilize the update method to change page properties:

$page = \Page::getCurrentPage();
$page->update(array(
    'cName' => 'My new page name' // changes the name
));

There are multiple properties you can adjust, such as Name (cName), Description (cDescription), Posting Date (cDatePublic), and more.

Creating a New Page

Choose a parent page, page type, and template to add a new page:

$parentPage = \Page::getByPath('/my/articles');
$pageType = \PageType::getByHandle('blog_post');
$template = \PageTemplate::getByHandle('three_column');
$data = array('cName' => 'Hello World!', 'cDescription' => 'Just a quick blog post.');
$newPage = $parentPage->add($pageType, $data, $template);

Move or Copy a Page

To move:

$entry = \Page::getByPath('/my/articles/hello-all');
$archives = \Page::getByPath('/archives');
$entry->move($archives);

To copy:

$starter = \Page::getByPath("/starters/profile-page");
$parent = \Page::getByPath('/profiles');
$copiedPage = $starter->duplicate($parent);
$copiedPage->update(array('cName' => 'Jane Doe'));

Deleting a Page

Directly deleting:

$pageToDelete = \Page::getByPath('/profiles/jane-doe');
$pageToDelete->delete();

Moving to trash:

$pageToTrash = \Page::getByID(200);
$pageToTrash->moveToTrash();

Note: Direct delete doesn't send the page to trash.

PageList: Searching, Sorting & Filtering Pages

With Concrete CMS, the Concrete\Core\Page\PageList object lets developers obtain lists of pages based on set criteria. This object is preferred for sorting, searching, or filtering pages over direct database querying. It considers permissions, page versions, aliases, and more.

Get all Pages

$list = new \Concrete\Core\Page\PageList();
$pages = $list->getResults();

This fetches all pages, excluding: - Aliases - Inactive pages - Pages inaccessible due to permissions - System/dashboard pages

Basic Filters:

Specific Page Type

$list = new \Concrete\Core\Page\PageList();
$list->filterByPageTypeHandle('blog_entry');
$pages = $list->getResults();

Multiple Page Types

$list->filterByPageTypeHandle(['blog_entry', 'press_release']);

Keywords

$list->filterByKeywords('foobar');

This searches name, description, text content, and searchable page attributes.

For advanced keyword searching:

$list->filterByFulltextKeywords('foobar');

Attribute Filtering

Use 'magic methods' for easy attribute-based filtering. For instance:

$list->filterByExcludeNav(true);

For topic attributes

$topicNode = \Concrete\Core\Tree\Node::getByID(10);
$list->filterByBlogEntryTopic($topicNode);

Sorting

By Public Date

$list->sortByPublicDate();

Reverse Order

$list->sortByPublicDateDescending();

Alphabetically by Name

$list->sortByName();

Sitemap Order

$list->sortByDisplayOrder();

By Page Attribute

$list->sortByAttributeName();

Inclusions and Permissions

Ignore Permissions

$list->ignorePermissions();

Include Special Pages

$list->includeInactivePages();
$list->includeAliases();
$list->includeSystemPages();

For Multilingual Sites

$list->setSiteTreeToAll();

Advanced Usage

You can directly use the underlying QueryBuilder from Doctrine DBAL:

$list = new \Concrete\Core\Page\PageList();
$qb = $list->getQueryObject();
$qb->where('p.clsActive', 1)
   ->andWhere('p.clsTemplate', 0)
   ->orderBy('p.cDisplayOrder', 'ASC');
$pages = $list->getResults();

For repetitive custom queries, extend the PageList class to create a custom class.

Pagination

For paginating results:

$list = new \Concrete\Core\Page\PageList();
$pagination = $list->getPagination();
$pagination->setMaxPerPage(10)->setCurrentPage(2);
$results = $pagination->getCurrentPageResults();

Rendering Pagination

echo $pagination->renderDefaultView();

For more, refer to the Pagerfanta documentation.

Custom Page Type Validator

Page types in Concrete define attributes and blocks for specific type of page you expect editors to create frequently. For instance, a Job Posting type might have attributes like "Department" and "Location". To add custom validation, like ensuring a job isn't posted for a date more than 30 days ahead, we use a custom page type validator.

Set Up Custom Validator for Job Posting

  1. Inform Concrete about the custom validator for the Job Posting page type:
$manager = \Core::make('manager/page_type/validator');
$manager->extend('job_posting', function($app) {
    return new \Application\Src\Page\Type\Validator\JobPostingValidator();
});

This code registers our custom validator for the job_posting page type.

  1. Create the custom validator:
    • Make a directory: application/src/Page/Type/Validator/
    • Add a file: JobPostingValidator.php with the following code:
<?php

namespace Application\Src\Page\Type\Validator;
use Concrete\Core\Page\Page;
use Concrete\Core\Page\Type\Composer\Control\Control;
use Concrete\Core\Page\Type\Composer\Control\CorePageProperty\DateTimeCorePageProperty;
use \Concrete\Core\Page\Type\Validator\StandardValidator;

class JobPostingValidator extends StandardValidator {
    public function validatePublishDraftRequest(Page $page = null) {
        $e = parent::validatePublishDraftRequest($page);
        foreach(Control::getList($this->type) as $control) {
            if ($control instanceof DateTimeCorePageProperty) {
                $property = $control;
                break;
            }
        }

        $property->setPageObject($page);
        $date = $property->getRequestValue() ?: $property->getPageTypeComposerControlDraftValue();
        $datetime = is_array($date) ? strtotime(\Core::make("helper/form/date_time")->translate('date_time', $date)) : strtotime($date);

        if ($datetime - time() > 2592000) {
            $e->add(t('A job cannot be posted more than 30 days in the future.'));
        }
        return $e;
    }
}

This validator ensures a job's date isn't more than 30 days in the future. It keeps standard validation and adds custom logic.

Programmatically Adding Page Types

Example: News Item Page

We'll add a "News Item" page type that can only use the "left side template" and can only be added beneath a "News" page type.

  1. Import Required Namespaces
use PageType;
use PageTemplate;
use \Concrete\Core\Page\Type\PublishTarget\Type\Type as PublishTargetType;
  1. Define the Page Template

Only the "left sidebar" template is used:

$left_side = PageTemplate::getByHandle('left_sidebar');
  1. Add the Page Type

Ensure it doesn't exist, then add:

$newsItem = PageType::getByHandle('news_item');
if (!is_object($newsItem)) {
    $newsItem = PageType::add(
        array(
            'handle' => 'news_item',
            'name' => 'News Item',
            'defaultTemplate' => $left_side,
            'allowedTemplates' => 'C',
            'templates' => array($left_side),
            'ptLaunchInComposer' => false,
            'ptIsFrequentlyAdded' => true
        ),
        $pkg
    );
}
  1. Set Publish Restrictions

Ensure news items are only published under a "News" page type:

$newsTarget = PublishTargetType::getByHandle('page_type');
$newsId = PageType::getByHandle('news')->getPageTypeID();

$configuredNews = $newsTarget->configurePageTypePublishTarget($newsItem, array('ptID' => $newsId, 'selectorFormFactorPageType' => null));

$newsItem->setConfiguredPageTypePublishTargetObject($configuredNews);

Appendix: Publish Target Configuration

All Type

$allTarget = PublishTargetType::getByHandle('all');
$configuredAllTarget = $allTarget->configurePageTypePublishTarget($pageTypeObject, array('selectorFormFactorAll' => null, 'startingPointPageIDall' => (cID)));
$pageType->setConfiguredPageTypePublishTargetObject($configuredAllTarget);

Page Type Type

$typeTarget = PublishTargetType::getByHandle('page_type');
$configuredTypeTarget = $typeTarget->configurePageTypePublishTarget($pageTypeObject, array('ptID' => (ptID), 'startingPointPageIDPageType' => (cID), 'selectorFormFactorPageType' => null));
$pageType->setConfiguredPageTypePublishTargetObject($configuredTypeTarget);

Parent Page Type

$parentTarget = PublishTargetType::getByHandle('parent_page');
$configuredPageTarget = $parentTarget->configurePageTypePublishTarget($pageTypeObject, array('cParentID' => (cID)));
$pageType->setConfiguredPageTypePublishTargetObject($configuredPageTarget);