From dab2a7ac9de643b7e1159713f9b0167873779b2f Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Tue, 16 Aug 2016 13:22:58 +1200 Subject: [PATCH] Split code into class-per-file Add psr-4 directive in composer.json --- code/Controllers/AssetAdmin.php | 34 -- .../AssetAdmin_DeleteBatchAction.php | 47 +++ code/Controllers/CMSSiteTreeFilter.php | 217 ------------ .../CMSSiteTreeFilter_ChangedPages.php | 29 ++ .../CMSSiteTreeFilter_DeletedPages.php | 39 +++ .../CMSSiteTreeFilter_PublishedPages.php | 54 +++ code/Controllers/CMSSiteTreeFilter_Search.php | 32 ++ .../CMSSiteTreeFilter_StatusDeletedPages.php | 50 +++ .../CMSSiteTreeFilter_StatusDraftPages.php | 39 +++ ...TreeFilter_StatusRemovedFromDraftPages.php | 38 +++ code/Controllers/ContentController.php | 1 - code/Controllers/OldPageRedirector.php | 1 - code/Controllers/RootURLController.php | 1 - code/Controllers/SilverStripeNavigator.php | 314 ------------------ .../Controllers/SilverStripeNavigatorItem.php | 141 ++++++++ .../SilverStripeNavigatorItem_ArchiveLink.php | 60 ++++ .../SilverStripeNavigatorItem_CMSLink.php | 51 +++ .../SilverStripeNavigatorItem_LiveLink.php | 68 ++++ .../SilverStripeNavigatorItem_StageLink.php | 76 +++++ code/Forms/SiteTreeURLSegmentField.php | 15 - .../SiteTreeURLSegmentField_Readonly.php | 18 + code/Model/ErrorPage.php | 24 -- code/Model/ErrorPage_Controller.php | 33 ++ code/Model/RedirectorPage.php | 28 -- code/Model/RedirectorPage_Controller.php | 35 ++ code/Model/SiteTreeLinkTracking.php | 123 ------- code/Model/SiteTreeLinkTracking_Parser.php | 133 ++++++++ code/Model/VirtualPage.php | 103 ------ code/Model/VirtualPage_Controller.php | 115 +++++++ composer.json | 73 ++-- 30 files changed, 1096 insertions(+), 896 deletions(-) create mode 100644 code/Controllers/AssetAdmin_DeleteBatchAction.php create mode 100644 code/Controllers/CMSSiteTreeFilter_ChangedPages.php create mode 100644 code/Controllers/CMSSiteTreeFilter_DeletedPages.php create mode 100644 code/Controllers/CMSSiteTreeFilter_PublishedPages.php create mode 100644 code/Controllers/CMSSiteTreeFilter_Search.php create mode 100644 code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php create mode 100644 code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php create mode 100644 code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php create mode 100644 code/Controllers/SilverStripeNavigatorItem.php create mode 100644 code/Controllers/SilverStripeNavigatorItem_ArchiveLink.php create mode 100644 code/Controllers/SilverStripeNavigatorItem_CMSLink.php create mode 100644 code/Controllers/SilverStripeNavigatorItem_LiveLink.php create mode 100644 code/Controllers/SilverStripeNavigatorItem_StageLink.php create mode 100644 code/Forms/SiteTreeURLSegmentField_Readonly.php create mode 100644 code/Model/ErrorPage_Controller.php create mode 100644 code/Model/RedirectorPage_Controller.php create mode 100644 code/Model/SiteTreeLinkTracking_Parser.php create mode 100644 code/Model/VirtualPage_Controller.php diff --git a/code/Controllers/AssetAdmin.php b/code/Controllers/AssetAdmin.php index 4dbde464..0a4f18ec 100644 --- a/code/Controllers/AssetAdmin.php +++ b/code/Controllers/AssetAdmin.php @@ -697,37 +697,3 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{ } } -/** - * Delete multiple {@link Folder} records (and the associated filesystem nodes). - * Usually used through the {@link AssetAdmin} interface. - * - * @package cms - * @subpackage batchactions - */ -class AssetAdmin_DeleteBatchAction extends CMSBatchAction { - public function getActionTitle() { - // _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below') - return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders'); - } - - public function run(SS_List $records) { - $status = array( - 'modified'=>array(), - 'deleted'=>array() - ); - - foreach($records as $record) { - $id = $record->ID; - - // Perform the action - if($record->canDelete()) $record->delete(); - - $status['deleted'][$id] = array(); - - $record->destroy(); - unset($record); - } - - return Convert::raw2json($status); - } -} diff --git a/code/Controllers/AssetAdmin_DeleteBatchAction.php b/code/Controllers/AssetAdmin_DeleteBatchAction.php new file mode 100644 index 00000000..5386c56b --- /dev/null +++ b/code/Controllers/AssetAdmin_DeleteBatchAction.php @@ -0,0 +1,47 @@ + array(), + 'deleted' => array() + ); + + foreach ($records as $record) { + $id = $record->ID; + + // Perform the action + if ($record->canDelete()) { + $record->delete(); + } + + $status['deleted'][$id] = array(); + + $record->destroy(); + unset($record); + } + + return Convert::raw2json($status); + } +} diff --git a/code/Controllers/CMSSiteTreeFilter.php b/code/Controllers/CMSSiteTreeFilter.php index 38aa3416..a3361be5 100644 --- a/code/Controllers/CMSSiteTreeFilter.php +++ b/code/Controllers/CMSSiteTreeFilter.php @@ -240,220 +240,3 @@ abstract class CMSSiteTreeFilter extends Object implements LeftAndMain_SearchFil return $ids; } } - -/** - * This filter will display the SiteTree as a site visitor might see the site, i.e only the - * pages that is currently published. - * - * Note that this does not check canView permissions that might hide pages from certain visitors - * - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_PublishedPages extends CMSSiteTreeFilter { - - /** - * @return string - */ - static public function title() { - return _t('CMSSIteTreeFilter_PublishedPages.Title', "Published pages"); - } - - /** - * @var string - */ - protected $childrenMethod = "AllHistoricalChildren"; - - /** - * @var string - */ - protected $numChildrenMethod = 'numHistoricalChildren'; - - /** - * Filters out all pages who's status who's status that doesn't exist on live - * - * @see {@link SiteTree::getStatusFlags()} - * @return SS_List - */ - public function getFilteredPages() { - $pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree'); - $pages = $this->applyDefaultFilters($pages); - $pages = $pages->filterByCallback(function(SiteTree $page) { - return $page->getExistsOnLive(); - }); - return $pages; - } -} - -/** - * Works a bit different than the other filters: - * Shows all pages *including* those deleted from stage and live. - * It does not filter out pages still existing in the different stages. - * - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter { - - /** - * @var string - */ - protected $childrenMethod = "AllHistoricalChildren"; - - /** - * @var string - */ - protected $numChildrenMethod = 'numHistoricalChildren'; - - static public function title() { - return _t('CMSSiteTreeFilter_DeletedPages.Title', "All pages, including archived"); - } - - public function getFilteredPages() { - $pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree'); - $pages = $this->applyDefaultFilters($pages); - return $pages; - } -} - -/** - * Gets all pages which have changed on stage. - * - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter { - - static public function title() { - return _t('CMSSiteTreeFilter_ChangedPages.Title', "Modified pages"); - } - - public function getFilteredPages() { - $pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage'); - $pages = $this->applyDefaultFilters($pages) - ->leftJoin('SiteTree_Live', '"SiteTree_Live"."ID" = "SiteTree"."ID"') - ->where('"SiteTree"."Version" <> "SiteTree_Live"."Version"'); - return $pages; - } -} - -/** - * Filters pages which have a status "Removed from Draft". - * - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_StatusRemovedFromDraftPages extends CMSSiteTreeFilter { - - static public function title() { - return _t('CMSSiteTreeFilter_StatusRemovedFromDraftPages.Title', 'Live but removed from draft'); - } - - /** - * Filters out all pages who's status is set to "Removed from draft". - * - * @return SS_List - */ - public function getFilteredPages() { - $pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree'); - $pages = $this->applyDefaultFilters($pages); - $pages = $pages->filterByCallback(function(SiteTree $page) { - // If page is removed from stage but not live - return $page->getIsDeletedFromStage() && $page->getExistsOnLive(); - }); - return $pages; - } -} - -/** - * Filters pages which have a status "Draft". - * - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_StatusDraftPages extends CMSSiteTreeFilter { - - static public function title() { - return _t('CMSSiteTreeFilter_StatusDraftPages.Title', 'Draft pages'); - } - - /** - * Filters out all pages who's status is set to "Draft". - * - * @see {@link SiteTree::getStatusFlags()} - * @return SS_List - */ - public function getFilteredPages() { - $pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage'); - $pages = $this->applyDefaultFilters($pages); - $pages = $pages->filterByCallback(function(SiteTree $page) { - // If page exists on stage but not on live - return (!$page->getIsDeletedFromStage() && $page->getIsAddedToStage()); - }); - return $pages; - } -} - -/** - * Filters pages which have a status "Deleted". - * - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_StatusDeletedPages extends CMSSiteTreeFilter { - - /** - * @var string - */ - protected $childrenMethod = "AllHistoricalChildren"; - - /** - * @var string - */ - protected $numChildrenMethod = 'numHistoricalChildren'; - - static public function title() { - return _t('CMSSiteTreeFilter_StatusDeletedPages.Title', 'Archived pages'); - } - - /** - * Filters out all pages who's status is set to "Deleted". - * - * @see {@link SiteTree::getStatusFlags()} - * @return SS_List - */ - public function getFilteredPages() { - $pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree'); - $pages = $this->applyDefaultFilters($pages); - - $pages = $pages->filterByCallback(function(SiteTree $page) { - // Doesn't exist on either stage or live - return $page->getIsDeletedFromStage() && !$page->getExistsOnLive(); - }); - return $pages; - } -} - -/** - * @package cms - * @subpackage content - */ -class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter { - - static public function title() { - return _t('CMSSiteTreeFilter_Search.Title', "All pages"); - } - - /** - * Retun an array of maps containing the keys, 'ID' and 'ParentID' for each page to be displayed - * in the search. - * - * @return SS_List - */ - public function getFilteredPages() { - // Filter default records - $pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage'); - $pages = $this->applyDefaultFilters($pages); - return $pages; - } -} diff --git a/code/Controllers/CMSSiteTreeFilter_ChangedPages.php b/code/Controllers/CMSSiteTreeFilter_ChangedPages.php new file mode 100644 index 00000000..4459455a --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_ChangedPages.php @@ -0,0 +1,29 @@ +applyDefaultFilters($pages) + ->leftJoin('SiteTree_Live', '"SiteTree_Live"."ID" = "SiteTree"."ID"') + ->where('"SiteTree"."Version" <> "SiteTree_Live"."Version"'); + return $pages; + } +} diff --git a/code/Controllers/CMSSiteTreeFilter_DeletedPages.php b/code/Controllers/CMSSiteTreeFilter_DeletedPages.php new file mode 100644 index 00000000..9f204b4a --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_DeletedPages.php @@ -0,0 +1,39 @@ +applyDefaultFilters($pages); + return $pages; + } +} diff --git a/code/Controllers/CMSSiteTreeFilter_PublishedPages.php b/code/Controllers/CMSSiteTreeFilter_PublishedPages.php new file mode 100644 index 00000000..7917b820 --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_PublishedPages.php @@ -0,0 +1,54 @@ +applyDefaultFilters($pages); + $pages = $pages->filterByCallback(function (SiteTree $page) { + return $page->getExistsOnLive(); + }); + return $pages; + } +} diff --git a/code/Controllers/CMSSiteTreeFilter_Search.php b/code/Controllers/CMSSiteTreeFilter_Search.php new file mode 100644 index 00000000..3b0d1a39 --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_Search.php @@ -0,0 +1,32 @@ +applyDefaultFilters($pages); + return $pages; + } +} diff --git a/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php b/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php new file mode 100644 index 00000000..7cfc0bf3 --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_StatusDeletedPages.php @@ -0,0 +1,50 @@ +applyDefaultFilters($pages); + + $pages = $pages->filterByCallback(function (SiteTree $page) { + // Doesn't exist on either stage or live + return $page->getIsDeletedFromStage() && !$page->getExistsOnLive(); + }); + return $pages; + } +} diff --git a/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php b/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php new file mode 100644 index 00000000..c65a8452 --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_StatusDraftPages.php @@ -0,0 +1,39 @@ +applyDefaultFilters($pages); + $pages = $pages->filterByCallback(function (SiteTree $page) { + // If page exists on stage but not on live + return (!$page->getIsDeletedFromStage() && $page->getIsAddedToStage()); + }); + return $pages; + } +} diff --git a/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php b/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php new file mode 100644 index 00000000..874ab051 --- /dev/null +++ b/code/Controllers/CMSSiteTreeFilter_StatusRemovedFromDraftPages.php @@ -0,0 +1,38 @@ +applyDefaultFilters($pages); + $pages = $pages->filterByCallback(function (SiteTree $page) { + // If page is removed from stage but not live + return $page->getIsDeletedFromStage() && $page->getExistsOnLive(); + }); + return $pages; + } +} diff --git a/code/Controllers/ContentController.php b/code/Controllers/ContentController.php index f5f2b88c..6a0a5ae6 100755 --- a/code/Controllers/ContentController.php +++ b/code/Controllers/ContentController.php @@ -17,7 +17,6 @@ use SilverStripe\Security\Member; use SilverStripe\Security\Permission; use Controller; use Page; - use SiteConfig; use SS_HTTPRequest; use Translatable; diff --git a/code/Controllers/OldPageRedirector.php b/code/Controllers/OldPageRedirector.php index 847927e9..0ebb7060 100644 --- a/code/Controllers/OldPageRedirector.php +++ b/code/Controllers/OldPageRedirector.php @@ -11,7 +11,6 @@ use SS_HTTPResponse; use Controller; use SS_HTTPResponse_Exception; - class OldPageRedirector extends Extension { /** diff --git a/code/Controllers/RootURLController.php b/code/Controllers/RootURLController.php index 05813ced..c1a6d868 100644 --- a/code/Controllers/RootURLController.php +++ b/code/Controllers/RootURLController.php @@ -14,7 +14,6 @@ use SS_HTTPRequest; use ClassInfo; use Director; - /** * @package cms * @subpackage control diff --git a/code/Controllers/SilverStripeNavigator.php b/code/Controllers/SilverStripeNavigator.php index df784ecc..73fdefa7 100644 --- a/code/Controllers/SilverStripeNavigator.php +++ b/code/Controllers/SilverStripeNavigator.php @@ -4,18 +4,11 @@ namespace SilverStripe\CMS\Controllers; use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\DataObject; -use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\ORM\SS_List; -use SilverStripe\ORM\Versioning\Versioned; -use SilverStripe\ORM\FieldType\DBField; -use SilverStripe\Security\Member; use ViewableData; use ClassInfo; -use Controller; use SilverStripe\Admin\CMSPreviewable; -use SilverStripe\Admin\LeftAndMain; use SiteTreeFutureState; -use SilverStripe\CMS\Model\RedirectorPage; /** @@ -108,310 +101,3 @@ class SilverStripeNavigator extends ViewableData { ); } } - -/** - * Navigator items are links that appear in the $SilverStripeNavigator bar. - * To add an item, extend this class - it will be automatically picked up. - * When instanciating items manually, please ensure to call {@link canView()}. - * - * @package cms - * @subpackage content - */ -abstract class SilverStripeNavigatorItem extends ViewableData { - - /** - * @param DataObject|CMSPreviewable - */ - protected $record; - - /** @var string */ - protected $recordLink; - - /** - * @param DataObject|CMSPreviewable $record - */ - public function __construct(CMSPreviewable $record) { - parent::__construct(); - $this->record = $record; - } - - /** - * @return string HTML, mostly a link - but can be more complex as well. - * For example, a "future state" item might show a date selector. - */ - abstract public function getHTML(); - - /** - * @return string - * Get the Title of an item - */ - abstract public function getTitle(); - - /** - * Machine-friendly name. - * - * @return string - */ - public function getName() { - return substr(get_class($this), strpos(get_class($this), '_')+1); - } - - /** - * Optional link to a specific view of this record. - * Not all items are simple links, please use {@link getHTML()} - * to represent an item in markup unless you know what you're doing. - * - * @return string - */ - public function getLink() {} - - /** - * @return string - */ - public function getMessage() {} - - /** - * @return DataObject - */ - public function getRecord() { - return $this->record; - } - - /** - * @return int - */ - public function getPriority() { - return $this->stat('priority'); - } - - /** - * As items might convey different record states like a "stage" or "live" table, - * an item can be active (showing the record in this state). - * - * @return boolean - */ - public function isActive() { - return false; - } - - /** - * Filters items based on member permissions or other criteria, - * such as if a state is generally available for the current record. - * - * @param Member $member - * @return Boolean - */ - public function canView($member = null) { - return true; - } - - /** - * Counts as "archived" if the current record is a different version from both live and draft. - * - * @return boolean - */ - public function isArchived() { - if(!$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')) return false; - - if(!isset($this->record->_cached_isArchived)) { - $baseClass = $this->record->baseClass(); - $currentDraft = Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID); - $currentLive = Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID); - - $this->record->_cached_isArchived = ( - (!$currentDraft || ($currentDraft && $this->record->Version != $currentDraft->Version)) - && (!$currentLive || ($currentLive && $this->record->Version != $currentLive->Version)) - ); -} - - return $this->record->_cached_isArchived; - } -} - -/** - * @package cms - * @subpackage content - */ -class SilverStripeNavigatorItem_CMSLink extends SilverStripeNavigatorItem { - /** @config */ - private static $priority = 10; - - public function getHTML() { - return sprintf( - '%s', - $this->record->CMSEditLink(), - _t('ContentController.CMS', 'CMS') - ); - } - - public function getTitle() { - return _t('ContentController.CMS', 'CMS', 'Used in navigation. Should be a short label'); - } - - public function getLink() { - return $this->record->CMSEditLink(); - } - - public function isActive() { - return (Controller::curr() instanceof LeftAndMain); - } - - public function canView($member = null) { - return ( - // Don't show in CMS - !(Controller::curr() instanceof LeftAndMain) - // Don't follow redirects in preview, they break the CMS editing form - && !($this->record instanceof RedirectorPage) - ); - } - -} - -/** - * @package cms - * @subpackage content - */ -class SilverStripeNavigatorItem_StageLink extends SilverStripeNavigatorItem { - /** @config */ - private static $priority = 20; - - public function getHTML() { - $draftPage = $this->getDraftPage(); - if($draftPage) { - $this->recordLink = Controller::join_links($draftPage->AbsoluteLink(), "?stage=Stage"); - return "isActive() ? 'class="current" ' : '') ."href=\"$this->recordLink\">". _t('ContentController.DRAFTSITE', 'Draft Site') .""; - } - } - - public function getTitle() { - return _t('ContentController.DRAFT', 'Draft', 'Used for the Switch between draft and published view mode. Needs to be a short label'); - } - - public function getMessage() { - return "
". _t('ContentController.DRAFTSITE', 'Draft Site') ."
"; - } - - public function getLink() { - $date = Versioned::current_archived_date(); - return Controller::join_links( - $this->record->PreviewLink(), - '?stage=Stage', - $date ? '?archiveDate=' . $date : null - ); - } - - public function canView($member = null) { - return ( - $this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned') - && $this->getDraftPage() - // Don't follow redirects in preview, they break the CMS editing form - && !($this->record instanceof RedirectorPage) - ); - } - - public function isActive() { - return ( - Versioned::get_stage() == 'Stage' - && !(ClassInfo::exists('SiteTreeFutureState') && SiteTreeFutureState::get_future_datetime()) - && !$this->isArchived() - ); - } - - protected function getDraftPage() { - $baseClass = $this->record->baseClass(); - return Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID); - } -} - -/** - * @package cms - * @subpackage content - */ -class SilverStripeNavigatorItem_LiveLink extends SilverStripeNavigatorItem { - /** @config */ - private static $priority = 30; - - public function getHTML() { - $livePage = $this->getLivePage(); - if($livePage) { - $this->recordLink = Controller::join_links($livePage->AbsoluteLink(), "?stage=Live"); - return "isActive() ? 'class="current" ' : '') ."href=\"$this->recordLink\">". _t('ContentController.PUBLISHEDSITE', 'Published Site') .""; - } - } - - public function getTitle() { - return _t('ContentController.PUBLISHED', 'Published', 'Used for the Switch between draft and published view mode. Needs to be a short label'); - } - - public function getMessage() { - return "
". _t('ContentController.PUBLISHEDSITE', 'Published Site') ."
"; - } - - public function getLink() { - return Controller::join_links($this->record->PreviewLink(), '?stage=Live'); - } - - public function canView($member = null) { - return ( - $this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned') - && $this->getLivePage() - // Don't follow redirects in preview, they break the CMS editing form - && !($this->record instanceof RedirectorPage) - ); - } - - public function isActive() { - return ( - (!Versioned::get_stage() || Versioned::get_stage() == 'Live') - && !$this->isArchived() - ); - } - - protected function getLivePage() { - $baseClass = $this->record->baseClass(); - return Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID); - } -} - -/** - * @package cms - * @subpackage content - */ -class SilverStripeNavigatorItem_ArchiveLink extends SilverStripeNavigatorItem { - /** @config */ - private static $priority = 40; - - public function getHTML() { - $this->recordLink = $this->record->AbsoluteLink(); - return "isActive() ? ' current' : '') ."\" href=\"$this->recordLink?archiveDate={$this->record->LastEdited}\" target=\"_blank\">". _t('ContentController.ARCHIVEDSITE', 'Preview version') .""; - } - - public function getTitle() { - return _t('SilverStripeNavigator.ARCHIVED', 'Archived'); - } - - public function getMessage() { - if($date = Versioned::current_archived_date()) { - /** @var DBDatetime $dateObj */ - $dateObj = DBField::create_field('Datetime', $date); - return "
". _t('ContentController.ARCHIVEDSITEFROM', 'Archived site from') ."
" . $dateObj->Nice() . "
"; - } - } - - public function getLink() { - return $this->record->PreviewLink() . '?archiveDate=' . urlencode($this->record->LastEdited); - } - - public function canView($member = null) { - return ( - $this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned') - && $this->isArchived() - // Don't follow redirects in preview, they break the CMS editing form - && !($this->record instanceof RedirectorPage) - ); - } - - public function isActive() { - return $this->isArchived(); - } -} diff --git a/code/Controllers/SilverStripeNavigatorItem.php b/code/Controllers/SilverStripeNavigatorItem.php new file mode 100644 index 00000000..5834e4ea --- /dev/null +++ b/code/Controllers/SilverStripeNavigatorItem.php @@ -0,0 +1,141 @@ +record = $record; + } + + /** + * @return string HTML, mostly a link - but can be more complex as well. + * For example, a "future state" item might show a date selector. + */ + abstract public function getHTML(); + + /** + * @return string + * Get the Title of an item + */ + abstract public function getTitle(); + + /** + * Machine-friendly name. + * + * @return string + */ + public function getName() + { + return substr(get_class($this), strpos(get_class($this), '_') + 1); + } + + /** + * Optional link to a specific view of this record. + * Not all items are simple links, please use {@link getHTML()} + * to represent an item in markup unless you know what you're doing. + * + * @return string + */ + public function getLink() + { + } + + /** + * @return string + */ + public function getMessage() + { + } + + /** + * @return DataObject + */ + public function getRecord() + { + return $this->record; + } + + /** + * @return int + */ + public function getPriority() + { + return $this->stat('priority'); + } + + /** + * As items might convey different record states like a "stage" or "live" table, + * an item can be active (showing the record in this state). + * + * @return boolean + */ + public function isActive() + { + return false; + } + + /** + * Filters items based on member permissions or other criteria, + * such as if a state is generally available for the current record. + * + * @param Member $member + * @return Boolean + */ + public function canView($member = null) + { + return true; + } + + /** + * Counts as "archived" if the current record is a different version from both live and draft. + * + * @return boolean + */ + public function isArchived() + { + if (!$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')) { + return false; + } + + if (!isset($this->record->_cached_isArchived)) { + $baseClass = $this->record->baseClass(); + $currentDraft = Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID); + $currentLive = Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID); + + $this->record->_cached_isArchived = ( + (!$currentDraft || ($currentDraft && $this->record->Version != $currentDraft->Version)) + && (!$currentLive || ($currentLive && $this->record->Version != $currentLive->Version)) + ); + } + + return $this->record->_cached_isArchived; + } +} diff --git a/code/Controllers/SilverStripeNavigatorItem_ArchiveLink.php b/code/Controllers/SilverStripeNavigatorItem_ArchiveLink.php new file mode 100644 index 00000000..7fb4d49c --- /dev/null +++ b/code/Controllers/SilverStripeNavigatorItem_ArchiveLink.php @@ -0,0 +1,60 @@ +recordLink = $this->record->AbsoluteLink(); + return "isActive() ? ' current' : '') . "\" href=\"$this->recordLink?archiveDate={$this->record->LastEdited}\" target=\"_blank\">" . _t('ContentController.ARCHIVEDSITE', + 'Preview version') . ""; + } + + public function getTitle() + { + return _t('SilverStripeNavigator.ARCHIVED', 'Archived'); + } + + public function getMessage() + { + if ($date = Versioned::current_archived_date()) { + /** @var DBDatetime $dateObj */ + $dateObj = DBField::create_field('Datetime', $date); + return "
" . _t('ContentController.ARCHIVEDSITEFROM', + 'Archived site from') . "
" . $dateObj->Nice() . "
"; + } + } + + public function getLink() + { + return $this->record->PreviewLink() . '?archiveDate=' . urlencode($this->record->LastEdited); + } + + public function canView($member = null) + { + return ( + $this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned') + && $this->isArchived() + // Don't follow redirects in preview, they break the CMS editing form + && !($this->record instanceof RedirectorPage) + ); + } + + public function isActive() + { + return $this->isArchived(); + } +} diff --git a/code/Controllers/SilverStripeNavigatorItem_CMSLink.php b/code/Controllers/SilverStripeNavigatorItem_CMSLink.php new file mode 100644 index 00000000..d04d901e --- /dev/null +++ b/code/Controllers/SilverStripeNavigatorItem_CMSLink.php @@ -0,0 +1,51 @@ +%s', + $this->record->CMSEditLink(), + _t('ContentController.CMS', 'CMS') + ); + } + + public function getTitle() + { + return _t('ContentController.CMS', 'CMS', 'Used in navigation. Should be a short label'); + } + + public function getLink() + { + return $this->record->CMSEditLink(); + } + + public function isActive() + { + return (Controller::curr() instanceof LeftAndMain); + } + + public function canView($member = null) + { + return ( + // Don't show in CMS + !(Controller::curr() instanceof LeftAndMain) + // Don't follow redirects in preview, they break the CMS editing form + && !($this->record instanceof RedirectorPage) + ); + } + +} diff --git a/code/Controllers/SilverStripeNavigatorItem_LiveLink.php b/code/Controllers/SilverStripeNavigatorItem_LiveLink.php new file mode 100644 index 00000000..daaf16f7 --- /dev/null +++ b/code/Controllers/SilverStripeNavigatorItem_LiveLink.php @@ -0,0 +1,68 @@ +getLivePage(); + if ($livePage) { + $this->recordLink = Controller::join_links($livePage->AbsoluteLink(), "?stage=Live"); + return "isActive() ? 'class="current" ' : '') . "href=\"$this->recordLink\">" . _t('ContentController.PUBLISHEDSITE', + 'Published Site') . ""; + } + } + + public function getTitle() + { + return _t('ContentController.PUBLISHED', 'Published', + 'Used for the Switch between draft and published view mode. Needs to be a short label'); + } + + public function getMessage() + { + return "
" . _t('ContentController.PUBLISHEDSITE', + 'Published Site') . "
"; + } + + public function getLink() + { + return Controller::join_links($this->record->PreviewLink(), '?stage=Live'); + } + + public function canView($member = null) + { + return ( + $this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned') + && $this->getLivePage() + // Don't follow redirects in preview, they break the CMS editing form + && !($this->record instanceof RedirectorPage) + ); + } + + public function isActive() + { + return ( + (!Versioned::get_stage() || Versioned::get_stage() == 'Live') + && !$this->isArchived() + ); + } + + protected function getLivePage() + { + $baseClass = $this->record->baseClass(); + return Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID); + } +} diff --git a/code/Controllers/SilverStripeNavigatorItem_StageLink.php b/code/Controllers/SilverStripeNavigatorItem_StageLink.php new file mode 100644 index 00000000..521a9f66 --- /dev/null +++ b/code/Controllers/SilverStripeNavigatorItem_StageLink.php @@ -0,0 +1,76 @@ +getDraftPage(); + if ($draftPage) { + $this->recordLink = Controller::join_links($draftPage->AbsoluteLink(), "?stage=Stage"); + return "isActive() ? 'class="current" ' : '') . "href=\"$this->recordLink\">" . _t('ContentController.DRAFTSITE', + 'Draft Site') . ""; + } + } + + public function getTitle() + { + return _t('ContentController.DRAFT', 'Draft', + 'Used for the Switch between draft and published view mode. Needs to be a short label'); + } + + public function getMessage() + { + return "
" . _t('ContentController.DRAFTSITE', + 'Draft Site') . "
"; + } + + public function getLink() + { + $date = Versioned::current_archived_date(); + return Controller::join_links( + $this->record->PreviewLink(), + '?stage=Stage', + $date ? '?archiveDate=' . $date : null + ); + } + + public function canView($member = null) + { + return ( + $this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned') + && $this->getDraftPage() + // Don't follow redirects in preview, they break the CMS editing form + && !($this->record instanceof RedirectorPage) + ); + } + + public function isActive() + { + return ( + Versioned::get_stage() == 'Stage' + && !(ClassInfo::exists('SiteTreeFutureState') && SiteTreeFutureState::get_future_datetime()) + && !$this->isArchived() + ); + } + + protected function getDraftPage() + { + $baseClass = $this->record->baseClass(); + return Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID); + } +} diff --git a/code/Forms/SiteTreeURLSegmentField.php b/code/Forms/SiteTreeURLSegmentField.php index 29690f88..0349ebf0 100644 --- a/code/Forms/SiteTreeURLSegmentField.php +++ b/code/Forms/SiteTreeURLSegmentField.php @@ -158,18 +158,3 @@ class SiteTreeURLSegmentField extends TextField { return $newInst; } } - - -/** - * Readonly version of a site tree URL segment field - * - * @package forms - * @subpackage fields-basic - */ -class SiteTreeURLSegmentField_Readonly extends SiteTreeURLSegmentField { - protected $readonly = true; - - public function performReadonlyTransformation() { - return clone $this; - } -} diff --git a/code/Forms/SiteTreeURLSegmentField_Readonly.php b/code/Forms/SiteTreeURLSegmentField_Readonly.php new file mode 100644 index 00000000..28501b24 --- /dev/null +++ b/code/Forms/SiteTreeURLSegmentField_Readonly.php @@ -0,0 +1,18 @@ +setStatusCode($this->ErrorCode); - return $response; - } -} - diff --git a/code/Model/ErrorPage_Controller.php b/code/Model/ErrorPage_Controller.php new file mode 100644 index 00000000..b396704f --- /dev/null +++ b/code/Model/ErrorPage_Controller.php @@ -0,0 +1,33 @@ +setStatusCode($this->ErrorCode); + return $response; + } +} diff --git a/code/Model/RedirectorPage.php b/code/Model/RedirectorPage.php index c9c03dc2..a2c4554b 100644 --- a/code/Model/RedirectorPage.php +++ b/code/Model/RedirectorPage.php @@ -180,31 +180,3 @@ class RedirectorPage extends Page { return array(); } } - -/** - * Controller for the {@link RedirectorPage}. - * @package cms - * @subpackage content - */ -class RedirectorPage_Controller extends Page_Controller { - - public function init() { - parent::init(); - - // Check we don't already have a redirect code set - /** @var RedirectorPage $page */ - $page = $this->data(); - if(!$this->getResponse()->isFinished() && $link = $page->redirectionLink()) { - $this->redirect($link, 301); - } - } - - /** - * If we ever get this far, it means that the redirection failed. - */ - public function Content() { - return "

" . - _t('RedirectorPage.HASBEENSETUP', 'A redirector page has been set up without anywhere to redirect to.') . - "

"; - } -} diff --git a/code/Model/RedirectorPage_Controller.php b/code/Model/RedirectorPage_Controller.php new file mode 100644 index 00000000..29a9f131 --- /dev/null +++ b/code/Model/RedirectorPage_Controller.php @@ -0,0 +1,35 @@ +data(); + if (!$this->getResponse()->isFinished() && $link = $page->redirectionLink()) { + $this->redirect($link, 301); + } + } + + /** + * If we ever get this far, it means that the redirection failed. + */ + public function Content() + { + return "

" . + _t('RedirectorPage.HASBEENSETUP', 'A redirector page has been set up without anywhere to redirect to.') . + "

"; + } +} diff --git a/code/Model/SiteTreeLinkTracking.php b/code/Model/SiteTreeLinkTracking.php index 0b15ca51..86fca6b0 100644 --- a/code/Model/SiteTreeLinkTracking.php +++ b/code/Model/SiteTreeLinkTracking.php @@ -230,126 +230,3 @@ class SiteTreeLinkTracking extends DataExtension { } } } - -/** - * A helper object for extracting information about links. - */ -class SiteTreeLinkTracking_Parser { - - /** - * Finds the links that are of interest for the link tracking automation. Checks for brokenness and attaches - * extracted metadata so consumers can decide what to do with the DOM element (provided as DOMReference). - * - * @param SS_HTMLValue $htmlValue Object to parse the links from. - * @return array Associative array containing found links with the following field layout: - * Type: string, name of the link type - * Target: any, a reference to the target object, depends on the Type - * Anchor: string, anchor part of the link - * DOMReference: DOMElement, reference to the link to apply changes. - * Broken: boolean, a flag highlighting whether the link should be treated as broken. - */ - public function process(SS_HTMLValue $htmlValue) { - $results = array(); - - $links = $htmlValue->getElementsByTagName('a'); - if(!$links) { - return $results; - } - - foreach($links as $link) { - if (!$link->hasAttribute('href')) { - continue; - } - - $href = Director::makeRelative($link->getAttribute('href')); - - // Definitely broken links. - if($href == '' || $href[0] == '/') { - $results[] = array( - 'Type' => 'broken', - 'Target' => null, - 'Anchor' => null, - 'DOMReference' => $link, - 'Broken' => true - ); - - continue; - } - - // Link to a page on this site. - $matches = array(); - if(preg_match('/\[sitetree_link(?:\s*|%20|,)?id=(?[0-9]+)\](#(?.*))?/i', $href, $matches)) { - $page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $matches['id']); - $broken = false; - - if (!$page) { - // Page doesn't exist. - $broken = true; - } else if (!empty($matches['anchor'])) { - $anchor = preg_quote($matches['anchor'], '/'); - - if (!preg_match("/(name|id)=\"{$anchor}\"/", $page->Content)) { - // Broken anchor on the target page. - $broken = true; - } - } - - $results[] = array( - 'Type' => 'sitetree', - 'Target' => $matches['id'], - 'Anchor' => empty($matches['anchor']) ? null : $matches['anchor'], - 'DOMReference' => $link, - 'Broken' => $broken - ); - - continue; - } - - // Link to a file on this site. - $matches = array(); - if(preg_match('/\[file_link(?:\s*|%20|,)?id=(?[0-9]+)/i', $href, $matches)) { - $results[] = array( - 'Type' => 'file', - 'Target' => $matches['id'], - 'Anchor' => null, - 'DOMReference' => $link, - 'Broken' => !DataObject::get_by_id('File', $matches['id']) - ); - - continue; - } - - // Local anchor. - $matches = array(); - if(preg_match('/^#(.*)/i', $href, $matches)) { - $anchor = preg_quote($matches[1], '#'); - $results[] = array( - 'Type' => 'localanchor', - 'Target' => null, - 'Anchor' => $matches[1], - 'DOMReference' => $link, - 'Broken' => !preg_match("#(name|id)=\"{$anchor}\"#", $htmlValue->getContent()) - ); - - continue; - } - - } - - // Find all [image ] shortcodes (will be inline, not inside attributes) - $content = $htmlValue->getContent(); - if(preg_match_all('/\[image([^\]]+)\bid=(["])?(?\d+)\D/i', $content, $matches)) { - foreach($matches['id'] as $id) { - $results[] = array( - 'Type' => 'image', - 'Target' => (int)$id, - 'Anchor' => null, - 'DOMReference' => null, - 'Broken' => !DataObject::get_by_id('Image', (int)$id) - ); - } - } - return $results; - } - -} diff --git a/code/Model/SiteTreeLinkTracking_Parser.php b/code/Model/SiteTreeLinkTracking_Parser.php new file mode 100644 index 00000000..51425ad8 --- /dev/null +++ b/code/Model/SiteTreeLinkTracking_Parser.php @@ -0,0 +1,133 @@ +getElementsByTagName('a'); + if (!$links) { + return $results; + } + + foreach ($links as $link) { + if (!$link->hasAttribute('href')) { + continue; + } + + $href = Director::makeRelative($link->getAttribute('href')); + + // Definitely broken links. + if ($href == '' || $href[0] == '/') { + $results[] = array( + 'Type' => 'broken', + 'Target' => null, + 'Anchor' => null, + 'DOMReference' => $link, + 'Broken' => true + ); + + continue; + } + + // Link to a page on this site. + $matches = array(); + if (preg_match('/\[sitetree_link(?:\s*|%20|,)?id=(?[0-9]+)\](#(?.*))?/i', $href, $matches)) { + $page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $matches['id']); + $broken = false; + + if (!$page) { + // Page doesn't exist. + $broken = true; + } else { + if (!empty($matches['anchor'])) { + $anchor = preg_quote($matches['anchor'], '/'); + + if (!preg_match("/(name|id)=\"{$anchor}\"/", $page->Content)) { + // Broken anchor on the target page. + $broken = true; + } + } + } + + $results[] = array( + 'Type' => 'sitetree', + 'Target' => $matches['id'], + 'Anchor' => empty($matches['anchor']) ? null : $matches['anchor'], + 'DOMReference' => $link, + 'Broken' => $broken + ); + + continue; + } + + // Link to a file on this site. + $matches = array(); + if (preg_match('/\[file_link(?:\s*|%20|,)?id=(?[0-9]+)/i', $href, $matches)) { + $results[] = array( + 'Type' => 'file', + 'Target' => $matches['id'], + 'Anchor' => null, + 'DOMReference' => $link, + 'Broken' => !DataObject::get_by_id('File', $matches['id']) + ); + + continue; + } + + // Local anchor. + $matches = array(); + if (preg_match('/^#(.*)/i', $href, $matches)) { + $anchor = preg_quote($matches[1], '#'); + $results[] = array( + 'Type' => 'localanchor', + 'Target' => null, + 'Anchor' => $matches[1], + 'DOMReference' => $link, + 'Broken' => !preg_match("#(name|id)=\"{$anchor}\"#", $htmlValue->getContent()) + ); + + continue; + } + + } + + // Find all [image ] shortcodes (will be inline, not inside attributes) + $content = $htmlValue->getContent(); + if (preg_match_all('/\[image([^\]]+)\bid=(["])?(?\d+)\D/i', $content, $matches)) { + foreach ($matches['id'] as $id) { + $results[] = array( + 'Type' => 'image', + 'Target' => (int)$id, + 'Anchor' => null, + 'DOMReference' => null, + 'Broken' => !DataObject::get_by_id('Image', (int)$id) + ); + } + } + return $results; + } + +} diff --git a/code/Model/VirtualPage.php b/code/Model/VirtualPage.php index e7dc7e37..3a11133a 100644 --- a/code/Model/VirtualPage.php +++ b/code/Model/VirtualPage.php @@ -455,106 +455,3 @@ class VirtualPage extends Page { } } - -/** - * Controller for the virtual page. - * @package cms - */ -class VirtualPage_Controller extends Page_Controller { - - private static $allowed_actions = array( - 'loadcontentall' => 'ADMIN', - ); - - /** - * Backup of virtualised controller - * - * @var ContentController - */ - protected $virtualController = null; - - /** - * Get virtual controller - * - * @return ContentController - */ - protected function getVirtualisedController() { - if($this->virtualController) { - return $this->virtualController; - } - - // Validate virtualised model - /** @var VirtualPage $page */ - $page = $this->data(); - $virtualisedPage = $page->CopyContentFrom(); - if (!$virtualisedPage || !$virtualisedPage->exists()) { - return null; - } - - // Create controller using standard mechanism - $this->virtualController = ModelAsController::controller_for($virtualisedPage); - return $this->virtualController; - } - - public function getViewer($action) { - $controller = $this->getVirtualisedController() ?: $this; - return $controller->getViewer($action); - } - - /** - * When the virtualpage is loaded, check to see if the versions are the same - * if not, reload the content. - * NOTE: Virtual page must have a container object of subclass of sitetree. - * We can't load the content without an ID or record to copy it from. - */ - public function init(){ - parent::init(); - $this->__call('init', array()); - } - - /** - * Also check the original object's original controller for the method - * - * @param string $method - * @return bool - */ - public function hasMethod($method) { - if(parent::hasMethod($method)) { - return true; - }; - - // Fallback - $controller = $this->getVirtualisedController(); - return $controller && $controller->hasMethod($method); - } - - /** - * Pass unrecognized method calls on to the original controller - * - * @param string $method - * @param string $args - * @return mixed - * - * @throws Exception Any error other than a 'no method' error. - */ - public function __call($method, $args) - { - // Check if we can safely call this method before passing it back - // to custom methods. - if($this->getExtraMethodConfig($method)) { - return parent::__call($method, $args); - } - - // Pass back to copied page - $controller = $this->getVirtualisedController(); - if(!$controller) { - return null; - } - - // Ensure request/response data is available on virtual controller - $controller->setRequest($this->getRequest()); - $controller->setResponse($this->getResponse()); - - return call_user_func_array(array($controller, $method), $args); - } -} diff --git a/code/Model/VirtualPage_Controller.php b/code/Model/VirtualPage_Controller.php new file mode 100644 index 00000000..a06768b4 --- /dev/null +++ b/code/Model/VirtualPage_Controller.php @@ -0,0 +1,115 @@ + 'ADMIN', + ); + + /** + * Backup of virtualised controller + * + * @var ContentController + */ + protected $virtualController = null; + + /** + * Get virtual controller + * + * @return ContentController + */ + protected function getVirtualisedController() + { + if ($this->virtualController) { + return $this->virtualController; + } + + // Validate virtualised model + /** @var VirtualPage $page */ + $page = $this->data(); + $virtualisedPage = $page->CopyContentFrom(); + if (!$virtualisedPage || !$virtualisedPage->exists()) { + return null; + } + + // Create controller using standard mechanism + $this->virtualController = ModelAsController::controller_for($virtualisedPage); + return $this->virtualController; + } + + public function getViewer($action) + { + $controller = $this->getVirtualisedController() ?: $this; + return $controller->getViewer($action); + } + + /** + * When the virtualpage is loaded, check to see if the versions are the same + * if not, reload the content. + * NOTE: Virtual page must have a container object of subclass of sitetree. + * We can't load the content without an ID or record to copy it from. + */ + public function init() + { + parent::init(); + $this->__call('init', array()); + } + + /** + * Also check the original object's original controller for the method + * + * @param string $method + * @return bool + */ + public function hasMethod($method) + { + if (parent::hasMethod($method)) { + return true; + }; + + // Fallback + $controller = $this->getVirtualisedController(); + return $controller && $controller->hasMethod($method); + } + + /** + * Pass unrecognized method calls on to the original controller + * + * @param string $method + * @param string $args + * @return mixed + * + * @throws Exception Any error other than a 'no method' error. + */ + public function __call($method, $args) + { + // Check if we can safely call this method before passing it back + // to custom methods. + if ($this->getExtraMethodConfig($method)) { + return parent::__call($method, $args); + } + + // Pass back to copied page + $controller = $this->getVirtualisedController(); + if (!$controller) { + return null; + } + + // Ensure request/response data is available on virtual controller + $controller->setRequest($this->getRequest()); + $controller->setResponse($this->getResponse()); + + return call_user_func_array(array($controller, $method), $args); + } +} diff --git a/composer.json b/composer.json index 6e4098ad..03cd29a2 100644 --- a/composer.json +++ b/composer.json @@ -1,37 +1,40 @@ { - "name": "silverstripe/cms", - "type": "silverstripe-module", - "description": "The SilverStripe Content Management System", - "homepage": "http://silverstripe.org", - "license": "BSD-3-Clause", - "keywords": ["silverstripe", "cms"], - "authors": [ - { - "name": "SilverStripe", - "homepage": "http://silverstripe.com" - }, - { - "name": "The SilverStripe Community", - "homepage": "http://silverstripe.org" - } - ], - "require": { - "php": ">=5.5.0", - "composer/installers": "*", - "silverstripe/framework": "^4.0", - "silverstripe/reports": "^4.0", - "silverstripe/siteconfig": "^4.0" - }, - "require-dev": { - "phpunit/PHPUnit": "~4.8" - }, - "extra": { - "branch-alias": { - "dev-master": "4.0.x-dev" - } - }, - "autoload": { - "classmap": ["tests/behat/"] - }, - "minimum-stability": "dev" + "name": "silverstripe/cms", + "type": "silverstripe-module", + "description": "The SilverStripe Content Management System", + "homepage": "http://silverstripe.org", + "license": "BSD-3-Clause", + "keywords": ["silverstripe", "cms"], + "authors": [ + { + "name": "SilverStripe", + "homepage": "http://silverstripe.com" + }, + { + "name": "The SilverStripe Community", + "homepage": "http://silverstripe.org" + } + ], + "require": { + "php": ">=5.5.0", + "composer/installers": "*", + "silverstripe/framework": "^4.0", + "silverstripe/reports": "^4.0", + "silverstripe/siteconfig": "^4.0" + }, + "require-dev": { + "phpunit/PHPUnit": "~4.8" + }, + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "SilverStripe\\CMS\\": "code/" + }, + "classmap": ["tests/behat/"] + }, + "minimum-stability": "dev" }