Merge remote-tracking branch 'origin/3'

This commit is contained in:
Damian Mooyman 2015-06-09 11:12:30 +12:00
commit dc935628f8
34 changed files with 1219 additions and 573 deletions

View File

@ -22,21 +22,49 @@ class CMSBatchAction_Publish extends CMSBatchAction {
} }
/** /**
* Un-publish items batch action. * Unpublish items batch action.
* *
* @package cms * @package cms
* @subpackage batchaction * @subpackage batchaction
*/ */
class CMSBatchAction_Unpublish extends CMSBatchAction { class CMSBatchAction_Unpublish extends CMSBatchAction {
public function getActionTitle() { public function getActionTitle() {
return _t('CMSBatchActions.UNPUBLISH_PAGES', 'Un-publish'); return _t('CMSBatchActions.UNPUBLISH_PAGES', 'Unpublish');
} }
public function run(SS_List $pages) { public function run(SS_List $pages) {
return $this->batchaction($pages, 'doUnpublish', return $this->batchaction($pages, 'doUnpublish',
_t('CMSBatchActions.UNPUBLISHED_PAGES', 'Un-published %d pages') _t('CMSBatchActions.UNPUBLISHED_PAGES', 'Unpublished %d pages')
); );
} }
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canDeleteFromLive', false, true);
}
}
/**
* Archives a page, removing it from both live and stage
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Archive extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.ARCHIVE', 'Archive');
}
public function run(SS_List $pages) {
return $this->batchaction($pages, 'doArchive',
_t('CMSBatchActions.ARCHIVED_PAGES', 'Archived %d pages')
);
}
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canArchive', true, true);
}
} }
/** /**
@ -44,6 +72,7 @@ class CMSBatchAction_Unpublish extends CMSBatchAction {
* *
* @package cms * @package cms
* @subpackage batchaction * @subpackage batchaction
* @deprecated since version 4.0
*/ */
class CMSBatchAction_Delete extends CMSBatchAction { class CMSBatchAction_Delete extends CMSBatchAction {
public function getActionTitle() { public function getActionTitle() {
@ -51,6 +80,7 @@ class CMSBatchAction_Delete extends CMSBatchAction {
} }
public function run(SS_List $pages) { public function run(SS_List $pages) {
Deprecation::notice('4.0', 'Delete is deprecated. Use Archive instead');
$status = array( $status = array(
'modified'=>array(), 'modified'=>array(),
'deleted'=>array(), 'deleted'=>array(),
@ -70,7 +100,6 @@ class CMSBatchAction_Delete extends CMSBatchAction {
'"SiteTree"."ID"' => $id '"SiteTree"."ID"' => $id
)); ));
if($liveRecord) { if($liveRecord) {
$liveRecord->IsDeletedFromStage = true;
$status['modified'][$liveRecord->ID] = array( $status['modified'][$liveRecord->ID] = array(
'TreeTitle' => $liveRecord->TreeTitle, 'TreeTitle' => $liveRecord->TreeTitle,
); );
@ -93,14 +122,15 @@ class CMSBatchAction_Delete extends CMSBatchAction {
* *
* @package cms * @package cms
* @subpackage batchaction * @subpackage batchaction
* @deprecated since version 4.0
*/ */
class CMSBatchAction_DeleteFromLive extends CMSBatchAction { class CMSBatchAction_DeleteFromLive extends CMSBatchAction {
public function getActionTitle() { public function getActionTitle() {
return _t('CMSBatchActions.DELETE_PAGES', 'Delete from published site'); return _t('CMSBatchActions.DELETE_PAGES', 'Delete from published site');
} }
public function run(SS_List $pages) { public function run(SS_List $pages) {
Deprecation::notice('4.0', 'Delete From Live is deprecated. Use Unpublish instead');
$status = array( $status = array(
'modified'=>array(), 'modified'=>array(),
'deleted'=>array() 'deleted'=>array()
@ -117,7 +147,6 @@ class CMSBatchAction_DeleteFromLive extends CMSBatchAction {
'"SiteTree"."ID"' => $id '"SiteTree"."ID"' => $id
)); ));
if($stageRecord) { if($stageRecord) {
$stageRecord->IsAddedToStage = true;
$status['modified'][$stageRecord->ID] = array( $status['modified'][$stageRecord->ID] = array(
'TreeTitle' => $stageRecord->TreeTitle, 'TreeTitle' => $stageRecord->TreeTitle,
); );

View File

@ -294,9 +294,9 @@ JS
$actions = $form->Actions(); $actions = $form->Actions();
$saveBtn = $actions->fieldByName('action_save'); $saveBtn = $actions->fieldByName('action_save');
$deleteBtn = $actions->fieldByName('action_delete'); $deleteBtn = $actions->fieldByName('action_delete');
$actions->removeByName('action_save');
$actions->removeByName('action_delete');
if(($saveBtn || $deleteBtn) && $fields->fieldByName('Root.DetailsView')) { if(($saveBtn || $deleteBtn) && $fields->fieldByName('Root.DetailsView')) {
$actions->removeByName('action_save');
$actions->removeByName('action_delete');
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.DetailsView', 'Root.DetailsView',
CompositeField::create($saveBtn,$deleteBtn)->addExtraClass('Actions') CompositeField::create($saveBtn,$deleteBtn)->addExtraClass('Actions')
@ -535,6 +535,7 @@ JS
public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) { public function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
if (!$childrenMethod) $childrenMethod = 'ChildFolders'; if (!$childrenMethod) $childrenMethod = 'ChildFolders';
if (!$numChildrenMethod) $numChildrenMethod = 'numChildFolders';
return parent::getSiteTreeFor($className, $rootID, $childrenMethod, $numChildrenMethod, $filterFunction, $minNodeCount); return parent::getSiteTreeFor($className, $rootID, $childrenMethod, $numChildrenMethod, $filterFunction, $minNodeCount);
} }
@ -543,7 +544,7 @@ JS
} }
public function SiteTreeAsUL() { public function SiteTreeAsUL() {
return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders'); return $this->getSiteTreeFor($this->stat('tree_class'), null, 'ChildFolders', 'numChildFolders');
} }
//------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------//

View File

@ -36,6 +36,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
private static $page_length = 15; private static $page_length = 15;
private static $allowed_actions = array( private static $allowed_actions = array(
'archive',
'buildbrokenlinks', 'buildbrokenlinks',
'deleteitems', 'deleteitems',
'DeleteItemsForm', 'DeleteItemsForm',
@ -56,6 +57,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
'ListViewForm', 'ListViewForm',
'childfilter', 'childfilter',
); );
/**
* Enable legacy batch actions.
* @deprecated since version 4.0
* @var array
* @config
*/
private static $enabled_legacy_actions = array();
public function init() { public function init() {
// set reading lang // set reading lang
@ -88,8 +97,24 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
CMSBatchActionHandler::register('publish', 'CMSBatchAction_Publish'); CMSBatchActionHandler::register('publish', 'CMSBatchAction_Publish');
CMSBatchActionHandler::register('unpublish', 'CMSBatchAction_Unpublish'); CMSBatchActionHandler::register('unpublish', 'CMSBatchAction_Unpublish');
CMSBatchActionHandler::register('delete', 'CMSBatchAction_Delete');
CMSBatchActionHandler::register('deletefromlive', 'CMSBatchAction_DeleteFromLive');
// Check legacy actions
$legacy = $this->config()->enabled_legacy_actions;
// Delete from live is unnecessary since we have unpublish which does the same thing
if(in_array('CMSBatchAction_DeleteFromLive', $legacy)) {
Deprecation::notice('4.0', 'Delete From Live is deprecated. Use Un-publish instead');
CMSBatchActionHandler::register('deletefromlive', 'CMSBatchAction_DeleteFromLive');
}
// Delete action
if(in_array('CMSBatchAction_Delete', $legacy)) {
Deprecation::notice('4.0', 'Delete from Stage is deprecated. Use Archive instead.');
CMSBatchActionHandler::register('delete', 'CMSBatchAction_Delete');
} else {
CMSBatchActionHandler::register('archive', 'CMSBatchAction_Archive');
}
} }
public function index($request) { public function index($request) {
@ -576,8 +601,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$actions = $form->Actions(); $actions = $form->Actions();
if($record) { if($record) {
$deletedFromStage = $record->IsDeletedFromStage; $deletedFromStage = $record->getIsDeletedFromStage();
$deleteFromLive = !$record->ExistsOnLive; $deleteFromLive = !$record->getExistsOnLive();
$fields->push($idField = new HiddenField("ID", false, $id)); $fields->push($idField = new HiddenField("ID", false, $id));
// Necessary for different subsites // Necessary for different subsites
@ -1059,13 +1084,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
* @see deletefromlive() * @see deletefromlive()
*/ */
public function delete($data, $form) { public function delete($data, $form) {
Deprecation::notice('4.0', 'Delete from stage is deprecated. Use archive instead');
$id = $data['ID']; $id = $data['ID'];
$record = DataObject::get_by_id("SiteTree", $id); $record = DataObject::get_by_id("SiteTree", $id);
if($record && !$record->canDelete()) return Security::permissionFailure(); if($record && !$record->canDelete()) return Security::permissionFailure();
if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404); if(!$record || !$record->ID) throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
// save ID and delete record // Delete record
$recordID = $record->ID;
$record->delete(); $record->delete();
$this->response->addHeader( $this->response->addHeader(
@ -1077,6 +1102,34 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getResponseNegotiator()->respond($this->getRequest()); return $this->getResponseNegotiator()->respond($this->getRequest());
} }
/**
* Delete this page from both live and stage
*
* @param type $data
* @param type $form
*/
public function archive($data, $form) {
$id = $data['ID'];
$record = DataObject::get_by_id("SiteTree", $id);
if(!$record || !$record->exists()) {
throw new SS_HTTPResponse_Exception("Bad record ID #$id", 404);
}
if(!$record->canArchive()) {
return Security::permissionFailure();
}
// Archive record
$record->doArchive();
$this->response->addHeader(
'X-Status',
rawurlencode(sprintf(_t('CMSMain.ARCHIVEDPAGE',"Archived page '%s'"), $record->Title))
);
// Even if the record has been deleted from stage and live, it can be viewed in "archive mode"
return $this->getResponseNegotiator()->respond($this->getRequest());
}
public function publish($data, $form) { public function publish($data, $form) {
$data['publish'] = '1'; $data['publish'] = '1';

View File

@ -259,7 +259,7 @@ class CMSSIteTreeFilter_PublishedPages extends CMSSiteTreeFilter {
$pages = Versioned::get_including_deleted('SiteTree'); $pages = Versioned::get_including_deleted('SiteTree');
$pages = $this->applyDefaultFilters($pages); $pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function($page) { $pages = $pages->filterByCallback(function($page) {
return $page->ExistsOnLive; return $page->getExistsOnLive();
}); });
return $pages; return $pages;
} }
@ -286,7 +286,7 @@ class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter {
protected $numChildrenMethod = 'numHistoricalChildren'; protected $numChildrenMethod = 'numHistoricalChildren';
static public function title() { static public function title() {
return _t('CMSSiteTreeFilter_DeletedPages.Title', "All pages, including deleted"); return _t('CMSSiteTreeFilter_DeletedPages.Title', "All pages, including archived");
} }
public function getFilteredPages() { public function getFilteredPages() {
@ -305,7 +305,7 @@ class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter {
class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter { class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter {
static public function title() { static public function title() {
return _t('CMSSiteTreeFilter_ChangedPages.Title', "Changed pages"); return _t('CMSSiteTreeFilter_ChangedPages.Title', "Modified pages");
} }
public function getFilteredPages() { public function getFilteredPages() {
@ -339,7 +339,7 @@ class CMSSiteTreeFilter_StatusRemovedFromDraftPages extends CMSSiteTreeFilter {
$pages = $this->applyDefaultFilters($pages); $pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function($page) { $pages = $pages->filterByCallback(function($page) {
// If page is removed from stage but not live // If page is removed from stage but not live
return $page->IsDeletedFromStage && $page->ExistsOnLive; return $page->getIsDeletedFromStage() && $page->getExistsOnLive();
}); });
return $pages; return $pages;
} }
@ -354,7 +354,7 @@ class CMSSiteTreeFilter_StatusRemovedFromDraftPages extends CMSSiteTreeFilter {
class CMSSiteTreeFilter_StatusDraftPages extends CMSSiteTreeFilter { class CMSSiteTreeFilter_StatusDraftPages extends CMSSiteTreeFilter {
static public function title() { static public function title() {
return _t('CMSSiteTreeFilter_StatusDraftPages.Title', 'Draft unpublished pages'); return _t('CMSSiteTreeFilter_StatusDraftPages.Title', 'Draft pages');
} }
/** /**
@ -368,7 +368,7 @@ class CMSSiteTreeFilter_StatusDraftPages extends CMSSiteTreeFilter {
$pages = $this->applyDefaultFilters($pages); $pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function($page) { $pages = $pages->filterByCallback(function($page) {
// If page exists on stage but not on live // If page exists on stage but not on live
return (!$page->IsDeletedFromStage && $page->IsAddedToStage); return (!$page->getIsDeletedFromStage() && $page->getIsAddedToStage());
}); });
return $pages; return $pages;
} }
@ -393,7 +393,7 @@ class CMSSiteTreeFilter_StatusDeletedPages extends CMSSiteTreeFilter {
protected $numChildrenMethod = 'numHistoricalChildren'; protected $numChildrenMethod = 'numHistoricalChildren';
static public function title() { static public function title() {
return _t('CMSSiteTreeFilter_StatusDeletedPages.Title', 'Deleted pages'); return _t('CMSSiteTreeFilter_StatusDeletedPages.Title', 'Archived pages');
} }
/** /**
@ -408,7 +408,7 @@ class CMSSiteTreeFilter_StatusDeletedPages extends CMSSiteTreeFilter {
$pages = $pages->filterByCallback(function($page) { $pages = $pages->filterByCallback(function($page) {
// Doesn't exist on either stage or live // Doesn't exist on either stage or live
return $page->IsDeletedFromStage && !$page->ExistsOnLive; return $page->getIsDeletedFromStage() && !$page->getExistsOnLive();
}); });
return $pages; return $pages;
} }

View File

@ -119,7 +119,7 @@ class ContentController extends Controller {
|| (Versioned::current_stage() && Versioned::current_stage() != 'Live') || (Versioned::current_stage() && Versioned::current_stage() != 'Live')
) )
) { ) {
if(!$this->dataRecord->canViewStage(Versioned::current_archived_date() ? 'Stage' : Versioned::current_stage())) { if(!$this->dataRecord->canView()) {
Session::clear('currentStage'); Session::clear('currentStage');
Session::clear('archiveDate'); Session::clear('archiveDate');

View File

@ -260,6 +260,7 @@ class ErrorPage extends Page {
$this->response->addHeader('X-Status', rawurlencode($fileErrorText)); $this->response->addHeader('X-Status', rawurlencode($fileErrorText));
return $this->httpError(405); return $this->httpError(405);
} }
return true;
} }
/** /**

View File

@ -1,17 +1,16 @@
<?php <?php
/** /**
* Basic data-object representing all pages within the site tree. * Basic data-object representing all pages within the site tree. All page types that live within the hierarchy should
* This data-object takes care of the heirachy. All page types that live within the hierarchy should inherit from this. * inherit from this. In addition, it contains a number of static methods for querying the site tree and working with
* In addition, it contains a number of static methods for querying the site tree. * draft and published states.
* *
* <h2>URLs</h2> * <h2>URLs</h2>
* A page is identified during request handling via its "URLSegment" database column. * A page is identified during request handling via its "URLSegment" database column. As pages can be nested, the full
* As pages can be nested, the full path of a URL might contain multiple segments. * path of a URL might contain multiple segments. Each segment is stored in its filtered representation (through
* Each segment is stored in its filtered representation (through {@link URLSegmentFilter}). * {@link URLSegmentFilter}). The full path is constructed via {@link Link()}, {@link RelativeLink()} and
* The full path is constructed via {@link Link()}, {@link RelativeLink()} and {@link AbsoluteLink()}. * {@link AbsoluteLink()}. You can allow these segments to contain multibyte characters through
* You can allow these segments to contain multibyte characters through {@link URLSegmentFilter::$default_allow_multibyte}. * {@link URLSegmentFilter::$default_allow_multibyte}.
* *
* @property integer ID ID of the SiteTree object.
* @property string URLSegment * @property string URLSegment
* @property string Title * @property string Title
* @property string MenuTitle * @property string MenuTitle
@ -21,17 +20,17 @@
* @property string ShowInMenus * @property string ShowInMenus
* @property string ShowInSearch * @property string ShowInSearch
* @property string Sort Integer value denoting the sort order. * @property string Sort Integer value denoting the sort order.
* @property string HasBrokenFile
* @property string HasBrokenLink
* @property string ReportClass * @property string ReportClass
* @property string CanViewType Type of restriction for viewing this object. * @property string CanViewType Type of restriction for viewing this object.
* @property string CanEditType Type of restriction for editing this object. * @property string CanEditType Type of restriction for editing this object.
* *
* @method ManyManyList LinkTracking() List of site pages linked on this page. * @method ManyManyList ViewerGroups List of groups that can view this object.
* @method ManyManyList ImageTracking() List of Images linked on this page. * @method ManyManyList EditorGroups List of groups that can edit this object.
* @method ManyManyList ViewerGroups() List of groups that can view this object. * @method ManyManyList BackLinkTracking List of site pages that link to this page.
* @method ManyManyList EditorGroups() List of groups that can edit this object. *
* @method ManyManyList BackLinkTracking() List of site pages that link to this page. * @mixin Hierarchy
* @mixin Versioned
* @mixin SiteTreeLinkTracking
* *
* @package cms * @package cms
*/ */
@ -162,8 +161,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* If this is false, the class cannot be created in the CMS by regular content authors, only by ADMINs. * If this is false, the class cannot be created in the CMS by regular content authors, only by ADMINs.
* @var boolean * @var boolean
* @config * @config
*/ */
private static $can_create = true; private static $can_create = true;
/** /**
@ -228,7 +227,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
private static $enforce_strict_hierarchy = true; private static $enforce_strict_hierarchy = true;
/** /**
* The value used for the meta generator tag. Leave blank to omit the tag. * The value used for the meta generator tag. Leave blank to omit the tag.
* *
* @config * @config
* @var string * @var string
@ -317,8 +316,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* Note that if no model can be found, this method will fall over to a extended alternateGetByLink method provided * Note that if no model can be found, this method will fall over to a extended alternateGetByLink method provided
* by a extension attached to {@link SiteTree} * by a extension attached to {@link SiteTree}
* *
* @param string $link * @param string $link The link of the page to search for
* @param bool $cache * @param bool $cache True (default) to use caching, false to force a fresh search from the database
* @return SiteTree * @return SiteTree
*/ */
static public function get_by_link($link, $cache = true) { static public function get_by_link($link, $cache = true) {
@ -389,9 +388,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return a subclass map of SiteTree * Return a subclass map of SiteTree that shouldn't be hidden through {@link SiteTree::$hide_ancestor}
* that shouldn't be hidden through
* {@link SiteTree::$hide_ancestor}
* *
* @return array * @return array
*/ */
@ -414,7 +411,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
} }
// If any of the descendents don't want any of the elders to show up, cruelly render the elders surplus to requirements. // If any of the descendents don't want any of the elders to show up, cruelly render the elders surplus to
// requirements
if($kill_ancestors) { if($kill_ancestors) {
$kill_ancestors = array_unique($kill_ancestors); $kill_ancestors = array_unique($kill_ancestors);
foreach($kill_ancestors as $mark) { foreach($kill_ancestors as $mark) {
@ -430,10 +428,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Replace a "[sitetree_link id=n]" shortcode with a link to the page with the corresponding ID. * Replace a "[sitetree_link id=n]" shortcode with a link to the page with the corresponding ID.
* *
* @param array $arguments * @param array $arguments
* @param mixed $content * @param string $content
* @param object|null $parser * @param TextParser $parser
* @return string|void * @return string
*/ */
static public function link_shortcode_handler($arguments, $content = null, $parser = null) { static public function link_shortcode_handler($arguments, $content = null, $parser = null) {
if(!isset($arguments['id']) || !is_numeric($arguments['id'])) return; if(!isset($arguments['id']) || !is_numeric($arguments['id'])) return;
@ -454,14 +452,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $link; return $link;
} }
} }
/** /**
* Return the link for this {@link SiteTree} object, with the {@link Director::baseURL()} included. * Return the link for this {@link SiteTree} object, with the {@link Director::baseURL()} included.
* *
* @param string $action Optional controller action (method). * @param string $action Optional controller action (method).
* Note: URI encoding of this parameter is applied automatically through template casting, * Note: URI encoding of this parameter is applied automatically through template casting,
* don't encode the passed parameter. * don't encode the passed parameter. Please use {@link Controller::join_links()} instead to
* Please use {@link Controller::join_links()} instead to append GET parameters. * append GET parameters.
* @return string * @return string
*/ */
public function Link($action = null) { public function Link($action = null) {
@ -483,9 +481,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Base link used for previewing. Defaults to absolute URL, * Base link used for previewing. Defaults to absolute URL, in order to account for domain changes, e.g. on multi
* in order to account for domain changes, e.g. on multi site setups. * site setups. Does not contain hints about the stage, see {@link SilverStripeNavigator} for details.
* Does not contain hints about the stage, see {@link SilverStripeNavigator} for details.
* *
* @param string $action See {@link Link()} * @param string $action See {@link Link()}
* @return string * @return string
@ -501,7 +498,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Return the link for this {@link SiteTree} object relative to the SilverStripe root. * Return the link for this {@link SiteTree} object relative to the SilverStripe root.
* *
* By default, it this page is the current home page, and there is no action specified then this will return a link * By default, if this page is the current home page, and there is no action specified then this will return a link
* to the root of the site. However, if you set the $action parameter to TRUE then the link will not be rewritten * to the root of the site. However, if you set the $action parameter to TRUE then the link will not be rewritten
* and returned in its full form. * and returned in its full form.
* *
@ -512,7 +509,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
*/ */
public function RelativeLink($action = null) { public function RelativeLink($action = null) {
if($this->ParentID && self::config()->nested_urls) { if($this->ParentID && self::config()->nested_urls) {
$base = $this->Parent()->RelativeLink($this->URLSegment); $parent = $this->Parent();
// If page is removed select parent from version history (for archive page view)
if((!$parent || !$parent->exists()) && $this->IsDeletedFromStage) {
$parent = Versioned::get_latest_version('SiteTree', $this->ParentID);
}
$base = $parent->RelativeLink($this->URLSegment);
} elseif(!$action && $this->URLSegment == RootURLController::get_homepage_link()) { } elseif(!$action && $this->URLSegment == RootURLController::get_homepage_link()) {
// Unset base for root-level homepages. // Unset base for root-level homepages.
// Note: Homepages with action parameters (or $action === true) // Note: Homepages with action parameters (or $action === true)
@ -530,9 +532,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return Controller::join_links($base, '/', $action); return Controller::join_links($base, '/', $action);
} }
/** /**
* Get the absolute URL for this page on the Live site. * Get the absolute URL for this page on the Live site.
*
* @param bool $includeStageEqualsLive Whether to append the URL with ?stage=Live to force Live mode
* @return string
*/ */
public function getAbsoluteLiveLink($includeStageEqualsLive = true) { public function getAbsoluteLiveLink($includeStageEqualsLive = true) {
$oldStage = Versioned::current_stage(); $oldStage = Versioned::current_stage();
@ -552,7 +557,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* @return String * Generates a link to edit this page in the CMS.
*
* @return string
*/ */
public function CMSEditLink() { public function CMSEditLink() {
return Controller::join_links(singleton('CMSPageEditController')->Link('show'), $this->ID); return Controller::join_links(singleton('CMSPageEditController')->Link('show'), $this->ID);
@ -569,7 +576,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Returns TRUE if this is the currently active page that is being used to handle a request. * Returns true if this is the currently active page being used to handle this request.
* *
* @return bool * @return bool
*/ */
@ -578,8 +585,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Check if this page is in the currently active section (e.g. it is either current or one of it's children is * Check if this page is in the currently active section (e.g. it is either current or one of its children is
* currently being viewed. * currently being viewed).
* *
* @return bool * @return bool
*/ */
@ -590,9 +597,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Check if the parent of this page has been removed (or made otherwise unavailable), and * Check if the parent of this page has been removed (or made otherwise unavailable), and is still referenced by
* is still referenced by this child. Any such orphaned page may still require access via * this child. Any such orphaned page may still require access via the CMS, but should not be shown as accessible
* the cms, but should not be shown as accessible to external users. * to external users.
* *
* @return bool * @return bool
*/ */
@ -624,8 +631,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return "link", "current" or section depending on if this page is the current page, or not on the current page but * Return "link", "current" or "section" depending on if this page is the current page, or not on the current page
* in the current section. * but in the current section.
* *
* @return string * @return string
*/ */
@ -642,8 +649,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Check if this page is in the given current section. * Check if this page is in the given current section.
* *
* @param string $sectionName Name of the section to check. * @param string $sectionName Name of the section to check
* @return boolean True if we are in the given section. * @return bool True if we are in the given section
*/ */
public function InSection($sectionName) { public function InSection($sectionName) {
$page = Director::get_current_page(); $page = Director::get_current_page();
@ -656,11 +663,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Create a duplicate of this node. Doesn't affect joined data - create a * Create a duplicate of this node. Doesn't affect joined data - create a custom overloading of this if you need
* custom overloading of this if you need such behaviour. * such behaviour.
* *
* @param bool $doWrite * @param bool $doWrite Whether to write the new object before returning it
* @return SiteTree The duplicated object. * @return self The duplicated object
*/ */
public function duplicate($doWrite = true) { public function duplicate($doWrite = true) {
@ -678,12 +685,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $page; return $page;
} }
/** /**
* Duplicates each child of this node recursively and returns the * Duplicates each child of this node recursively and returns the top-level duplicate node.
* duplicate node.
* *
* @return SiteTree The duplicated object. * @return self The duplicated object
*/ */
public function duplicateWithChildren() { public function duplicateWithChildren() {
$clone = $this->duplicate(); $clone = $this->duplicate();
@ -702,10 +707,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $clone; return $clone;
} }
/** /**
* Duplicate this node and its children as a child of the node with the * Duplicate this node and its children as a child of the node with the given ID
* given ID
* *
* @param int $id ID of the new node's new parent * @param int $id ID of the new node's new parent
*/ */
@ -717,8 +720,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return a breadcrumb trail to this page. Excludes "hidden" pages * Return a breadcrumb trail to this page. Excludes "hidden" pages (with ShowInMenus=0) by default.
* (with ShowInMenus=0).
* *
* @param int $maxDepth The maximum depth to traverse. * @param int $maxDepth The maximum depth to traverse.
* @param boolean $unlinked Whether to link page titles. * @param boolean $unlinked Whether to link page titles.
@ -768,8 +770,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Make this page a child of another page. * Make this page a child of another page.
* *
* If the parent page does not exist, resolve it to a valid ID * If the parent page does not exist, resolve it to a valid ID before updating this page's reference.
* before updating this page's reference.
* *
* @param SiteTree|int $item Either the parent object, or the parent ID * @param SiteTree|int $item Either the parent object, or the parent ID
*/ */
@ -785,7 +786,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Get the parent of this page. * Get the parent of this page.
* *
* @return SiteTree Parent of this page. * @return SiteTree Parent of this page
*/ */
public function getParent() { public function getParent() {
if ($parentID = $this->getField("ParentID")) { if ($parentID = $this->getField("ParentID")) {
@ -794,8 +795,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return a string of the form "parent - page" or * Return a string of the form "parent - page" or "grandparent - parent - page" using page titles
* "grandparent - parent - page".
* *
* @param int $level The maximum amount of levels to traverse. * @param int $level The maximum amount of levels to traverse.
* @param string $separator Seperating string * @param string $separator Seperating string
@ -812,8 +812,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* This function should return true if the current user can execute this action. * This function should return true if the current user can execute this action. It can be overloaded to customise
* It can be overloaded to customise the security model for an application. * the security model for an application.
* *
* Slightly altered from parent behaviour in {@link DataObject->can()}: * Slightly altered from parent behaviour in {@link DataObject->can()}:
* - Checks for existence of a method named "can<$perm>()" on the object * - Checks for existence of a method named "can<$perm>()" on the object
@ -823,10 +823,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* *
* @uses DataObjectDecorator->can() * @uses DataObjectDecorator->can()
* *
* @param string $perm The permission to be checked, such as 'View'. * @param string $perm The permission to be checked, such as 'View'
* @param Member $member The member whose permissions need checking. * @param Member $member The member whose permissions need checking. Defaults to the currently logged in user.
* Defaults to the currently logged in user. * @return bool True if the the member is allowed to do the given action
* @return boolean True if the the member is allowed to do the given action.
*/ */
public function can($perm, $member = null) { public function can($perm, $member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) { if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
@ -846,14 +845,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return ($member && Permission::checkMember($member, $perm)); return ($member && Permission::checkMember($member, $perm));
} }
/** /**
* This function should return true if the current user can add children * This function should return true if the current user can add children to this page. It can be overloaded to
* to this page. It can be overloaded to customise the security model for an * customise the security model for an application.
* application.
* *
* Denies permission if any of the following conditions is TRUE: * Denies permission if any of the following conditions is true:
* - alternateCanAddChildren() on a extension returns FALSE * - alternateCanAddChildren() on a extension returns false
* - canEdit() is not granted * - canEdit() is not granted
* - There are no classes defined in {@link $allowed_children} * - There are no classes defined in {@link $allowed_children}
* *
@ -861,8 +858,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* @uses canEdit() * @uses canEdit()
* @uses $allowed_children * @uses $allowed_children
* *
* @param Member|int|null $member * @param Member|int $member
* @return boolean True if the current user can add children. * @return bool True if the current user can add children
*/ */
public function canAddChildren($member = null) { public function canAddChildren($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) { if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
@ -878,14 +875,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $this->canEdit($member) && $this->stat('allowed_children') != 'none'; return $this->canEdit($member) && $this->stat('allowed_children') != 'none';
} }
/** /**
* This function should return true if the current user can view this * This function should return true if the current user can view this page. It can be overloaded to customise the
* page. It can be overloaded to customise the security model for an * security model for an application.
* application.
* *
* Denies permission if any of the following conditions is TRUE: * Denies permission if any of the following conditions is true:
* - canView() on any extension returns FALSE * - canView() on any extension returns false
* - "CanViewType" directive is set to "Inherit" and any parent page return false for canView() * - "CanViewType" directive is set to "Inherit" and any parent page return false for canView()
* - "CanViewType" directive is set to "LoggedInUsers" and no user is logged in * - "CanViewType" directive is set to "LoggedInUsers" and no user is logged in
* - "CanViewType" directive is set to "OnlyTheseUsers" and user is not in the given groups * - "CanViewType" directive is set to "OnlyTheseUsers" and user is not in the given groups
@ -893,8 +888,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* @uses DataExtension->canView() * @uses DataExtension->canView()
* @uses ViewerGroups() * @uses ViewerGroups()
* *
* @param Member|int|null $member * @param Member|int $member
* @return boolean True if the current user can view this page. * @return bool True if the current user can view this page
*/ */
public function canView($member = null) { public function canView($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) { if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
@ -959,9 +954,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* *
* @todo Implement in CMS UI. * @todo Implement in CMS UI.
* *
* @param String $stage * @param string $stage
* @param Member $member * @param Member $member
* @return boolean * @return bool
*/ */
public function canViewStage($stage = 'Live', $member = null) { public function canViewStage($stage = 'Live', $member = null) {
$oldMode = Versioned::get_reading_mode(); $oldMode = Versioned::get_reading_mode();
@ -974,21 +969,20 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* This function should return true if the current user can delete this * This function should return true if the current user can delete this page. It can be overloaded to customise the
* page. It can be overloaded to customise the security model for an * security model for an application.
* application.
* *
* Denies permission if any of the following conditions is TRUE: * Denies permission if any of the following conditions is true:
* - canDelete() returns FALSE on any extension * - canDelete() returns false on any extension
* - canEdit() returns FALSE * - canEdit() returns false
* - any descendant page returns FALSE for canDelete() * - any descendant page returns false for canDelete()
* *
* @uses canDelete() * @uses canDelete()
* @uses SiteTreeExtension->canDelete() * @uses SiteTreeExtension->canDelete()
* @uses canEdit() * @uses canEdit()
* *
* @param Member $member * @param Member $member
* @return boolean True if the current user can delete this page. * @return bool True if the current user can delete this page
*/ */
public function canDelete($member = null) { public function canDelete($member = null) {
if($member instanceof Member) $memberID = $member->ID; if($member instanceof Member) $memberID = $member->ID;
@ -1012,13 +1006,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* This function should return true if the current user can create new * This function should return true if the current user can create new pages of this class, regardless of class. It
* pages of this class, regardless of context. It can be overloaded * can be overloaded to customise the security model for an application.
* to customise the security model for an application.
* *
* By default, permission to create at the root level is based on the SiteConfig * By default, permission to create at the root level is based on the SiteConfig configuration, and permission to
* configuration, and permission to create beneath a parent is based on the * create beneath a parent is based on the ability to edit that parent page.
* ability to edit that parent page.
* *
* Use {@link canAddChildren()} to control behaviour of creating children under this page. * Use {@link canAddChildren()} to control behaviour of creating children under this page.
* *
@ -1027,9 +1019,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* *
* @param Member $member * @param Member $member
* @param array $context Optional array which may contain array('Parent' => $parentObj) * @param array $context Optional array which may contain array('Parent' => $parentObj)
* If a parent page is known, it will be checked for validity. * If a parent page is known, it will be checked for validity.
* If omitted, it will be assumed this is to be created as a top level page. * If omitted, it will be assumed this is to be created as a top level page.
* @return boolean True if the current user can create pages on this class. * @return bool True if the current user can create pages on this class.
*/ */
public function canCreate($member = null) { public function canCreate($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) { if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
@ -1062,23 +1054,24 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* This function should return true if the current user can edit this * This function should return true if the current user can edit this page. It can be overloaded to customise the
* page. It can be overloaded to customise the security model for an * security model for an application.
* application.
* *
* Denies permission if any of the following conditions is TRUE: * Denies permission if any of the following conditions is true:
* - canEdit() on any extension returns FALSE * - canEdit() on any extension returns false
* - canView() return false * - canView() return false
* - "CanEditType" directive is set to "Inherit" and any parent page return false for canEdit() * - "CanEditType" directive is set to "Inherit" and any parent page return false for canEdit()
* - "CanEditType" directive is set to "LoggedInUsers" and no user is logged in or doesn't have the CMS_Access_CMSMAIN permission code * - "CanEditType" directive is set to "LoggedInUsers" and no user is logged in or doesn't have the
* CMS_Access_CMSMAIN permission code
* - "CanEditType" directive is set to "OnlyTheseUsers" and user is not in the given groups * - "CanEditType" directive is set to "OnlyTheseUsers" and user is not in the given groups
* *
* @uses canView() * @uses canView()
* @uses EditorGroups() * @uses EditorGroups()
* @uses DataExtension->canEdit() * @uses DataExtension->canEdit()
* *
* @param Member $member Set to FALSE if you want to explicitly test permissions without a valid user (useful for unit tests) * @param Member $member Set to false if you want to explicitly test permissions without a valid user (useful for
* @return boolean True if the current user can edit this page. * unit tests)
* @return bool True if the current user can edit this page
*/ */
public function canEdit($member = null) { public function canEdit($member = null) {
if($member instanceof Member) $memberID = $member->ID; if($member instanceof Member) $memberID = $member->ID;
@ -1106,18 +1099,17 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* This function should return true if the current user can publish this * This function should return true if the current user can publish this page. It can be overloaded to customise
* page. It can be overloaded to customise the security model for an * the security model for an application.
* application.
* *
* Denies permission if any of the following conditions is TRUE: * Denies permission if any of the following conditions is true:
* - canPublish() on any extension returns FALSE * - canPublish() on any extension returns false
* - canEdit() returns FALSE * - canEdit() returns false
* *
* @uses SiteTreeExtension->canPublish() * @uses SiteTreeExtension->canPublish()
* *
* @param Member $member * @param Member $member
* @return boolean True if the current user can publish this page. * @return bool True if the current user can publish this page.
*/ */
public function canPublish($member = null) { public function canPublish($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
@ -1141,7 +1133,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Stub method to get the site config, provided so it's easy to override * Stub method to get the site config, unless the current class can provide an alternate.
*
* @return SiteConfig
*/ */
public function getSiteConfig() { public function getSiteConfig() {
@ -1154,13 +1148,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Pre-populate the cache of canEdit, canView, canDelete, canPublish permissions. * Pre-populate the cache of canEdit, canView, canDelete, canPublish permissions. This method will use the static
* This method will use the static can_(perm)_multiple method for efficiency. * can_(perm)_multiple method for efficiency.
* *
* @param string $permission The permission: edit, view, publish, approve, etc. * @param string $permission The permission: edit, view, publish, approve, etc.
* @param array $ids An array of page IDs * @param array $ids An array of page IDs
* @param callback|null $batchCallback The function/static method to call to calculate permissions. Defaults * @param callable|string $batchCallback The function/static method to call to calculate permissions. Defaults
* to 'SiteTree::can_(permission)_multiple' * to 'SiteTree::can_(permission)_multiple'
*/ */
static public function prepopulate_permission_cache($permission = 'CanEditType', $ids, $batchCallback = null) { static public function prepopulate_permission_cache($permission = 'CanEditType', $ids, $batchCallback = null) {
if(!$batchCallback) $batchCallback = "SiteTree::can_{$permission}_multiple"; if(!$batchCallback) $batchCallback = "SiteTree::can_{$permission}_multiple";
@ -1172,26 +1166,27 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
. "with callback '$batchCallback'", E_USER_WARNING); . "with callback '$batchCallback'", E_USER_WARNING);
} }
} }
/** /**
* This method is NOT a full replacement for the individual can*() methods, e.g. {@link canEdit()}. * This method is NOT a full replacement for the individual can*() methods, e.g. {@link canEdit()}. Rather than
* Rather than checking (potentially slow) PHP logic, it relies on the database group associations, * checking (potentially slow) PHP logic, it relies on the database group associations, e.g. the "CanEditType" field
* e.g. the "CanEditType" field plus the "SiteTree_EditorGroups" many-many table. * plus the "SiteTree_EditorGroups" many-many table. By batch checking multiple records, we can combine the queries
* By batch checking multiple records, we can combine the queries efficiently. * efficiently.
* *
* Caches based on $typeField data. To invalidate the cache, use {@link SiteTree::reset()} * Caches based on $typeField data. To invalidate the cache, use {@link SiteTree::reset()} or set the $useCached
* or set the $useCached property to FALSE. * property to FALSE.
* *
* @param Array $ids Of {@link SiteTree} IDs * @param array $ids Of {@link SiteTree} IDs
* @param Int $memberID Member ID * @param int $memberID Member ID
* @param String $typeField A property on the data record, e.g. "CanEditType". * @param string $typeField A property on the data record, e.g. "CanEditType".
* @param String $groupJoinTable A many-many table name on this record, e.g. "SiteTree_EditorGroups" * @param string $groupJoinTable A many-many table name on this record, e.g. "SiteTree_EditorGroups"
* @param String $siteConfigMethod Method to call on {@link SiteConfig} for toplevel items, e.g. "canEdit" * @param string $siteConfigMethod Method to call on {@link SiteConfig} for toplevel items, e.g. "canEdit"
* @param String $globalPermission If the member doesn't have this permission code, don't bother iterating deeper. * @param string $globalPermission If the member doesn't have this permission code, don't bother iterating deeper
* @param Boolean $useCached * @param bool $useCached
* @return Array An map of {@link SiteTree} ID keys, to boolean values * @return array An map of {@link SiteTree} ID keys to boolean values
*/ */
static public function batch_permission_check($ids, $memberID, $typeField, $groupJoinTable, $siteConfigMethod, $globalPermission = null, $useCached = true) { public static function batch_permission_check($ids, $memberID, $typeField, $groupJoinTable, $siteConfigMethod,
$globalPermission = null, $useCached = true) {
if($globalPermission === NULL) $globalPermission = array('CMS_ACCESS_LeftAndMain', 'CMS_ACCESS_CMSMain'); if($globalPermission === NULL) $globalPermission = array('CMS_ACCESS_LeftAndMain', 'CMS_ACCESS_CMSMain');
// Sanitise the IDs // Sanitise the IDs
@ -1225,8 +1220,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
// Placeholder for parameterised ID list // Placeholder for parameterised ID list
$idPlaceholders = DB::placeholders($ids); $idPlaceholders = DB::placeholders($ids);
// if page can't be viewed, don't grant edit permissions // If page can't be viewed, don't grant edit permissions to do - implement can_view_multiple(), so this can
// to do - implement can_view_multiple(), so this can be enabled // be enabled
//$ids = array_keys(array_filter(self::can_view_multiple($ids, $memberID))); //$ids = array_keys(array_filter(self::can_view_multiple($ids, $memberID)));
// Get the groups that the given member belongs to // Get the groups that the given member belongs to
@ -1271,15 +1266,16 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
); );
if($potentiallyInherited) { if($potentiallyInherited) {
// Group $potentiallyInherited by ParentID; we'll look at the permission of all those // Group $potentiallyInherited by ParentID; we'll look at the permission of all those parents and
// parents and then see which ones the user has permission on // then see which ones the user has permission on
$groupedByParent = array(); $groupedByParent = array();
foreach($potentiallyInherited as $item) { foreach($potentiallyInherited as $item) {
if($item->ParentID) { if($item->ParentID) {
if(!isset($groupedByParent[$item->ParentID])) $groupedByParent[$item->ParentID] = array(); if(!isset($groupedByParent[$item->ParentID])) $groupedByParent[$item->ParentID] = array();
$groupedByParent[$item->ParentID][] = $item->ID; $groupedByParent[$item->ParentID][] = $item->ID;
} else { } else {
// Might return different site config based on record context, e.g. when subsites module is used // Might return different site config based on record context, e.g. when subsites module
// is used
$siteConfig = $item->getSiteConfig(); $siteConfig = $item->getSiteConfig();
$result[$item->ID] = $siteConfig->{$siteConfigMethod}($memberID); $result[$item->ID] = $siteConfig->{$siteConfigMethod}($memberID);
} }
@ -1312,15 +1308,15 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return array(); return array();
} }
} }
/** /**
* Get the 'can edit' information for a number of SiteTree pages. * Get the 'can edit' information for a number of SiteTree pages.
* *
* @param array $ids An array of IDs of the SiteTree pages to look up. * @param array $ids An array of IDs of the SiteTree pages to look up
* @param int $memberID ID of member. * @param int $memberID ID of member
* @param bool $useCached Return values from the permission cache if they exist. * @param bool $useCached Return values from the permission cache if they exist
* @return array A map where the IDs are keys and the values are booleans stating whether the given * @return array A map where the IDs are keys and the values are booleans stating whether the given page can be
* page can be edited. * edited
*/ */
static public function can_edit_multiple($ids, $memberID, $useCached = true) { static public function can_edit_multiple($ids, $memberID, $useCached = true) {
return self::batch_permission_check($ids, $memberID, 'CanEditType', 'SiteTree_EditorGroups', 'canEditPages', null, $useCached); return self::batch_permission_check($ids, $memberID, 'CanEditType', 'SiteTree_EditorGroups', 'canEditPages', null, $useCached);
@ -1328,9 +1324,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Get the 'can edit' information for a number of SiteTree pages. * Get the 'can edit' information for a number of SiteTree pages.
* @param array $ids An array of IDs of the SiteTree pages to look up. *
* @param int $memberID ID of member. * @param array $ids An array of IDs of the SiteTree pages to look up
* @param bool $useCached Return values from the permission cache if they exist. * @param int $memberID ID of member
* @param bool $useCached Return values from the permission cache if they exist
* @return array * @return array
*/ */
static public function can_delete_multiple($ids, $memberID, $useCached = true) { static public function can_delete_multiple($ids, $memberID, $useCached = true) {
@ -1389,23 +1386,19 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$deletable = array(); $deletable = array();
} }
// Convert the array of deletable IDs into a map of the original IDs with true/false as the // Convert the array of deletable IDs into a map of the original IDs with true/false as the value
// value
return array_fill_keys($deletable, true) + array_fill_keys($ids, false); return array_fill_keys($deletable, true) + array_fill_keys($ids, false);
} }
/** /**
* Collate selected descendants of this page. * Collate selected descendants of this page.
* *
* {@link $condition} will be evaluated on each descendant, and if it is * {@link $condition} will be evaluated on each descendant, and if it is succeeds, that item will be added to the
* succeeds, that item will be added to the $collator array. * $collator array.
* *
* @param string $condition The PHP condition to be evaluated. The page * @param string $condition The PHP condition to be evaluated. The page will be called $item
* will be called $item * @param array $collator An array, passed by reference, to collect all of the matching descendants.
* @param array $collator An array, passed by reference, to collect all * @return bool
* of the matching descendants.
* @return true|void
*/ */
public function collateDescendants($condition, &$collator) { public function collateDescendants($condition, &$collator) {
if($children = $this->Children()) { if($children = $this->Children()) {
@ -1417,13 +1410,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
} }
/** /**
* Return the title, description, keywords and language metatags. * Return the title, description, keywords and language metatags.
* *
* @todo Move <title> tag in separate getter for easier customization and more obvious usage * @todo Move <title> tag in separate getter for easier customization and more obvious usage
* *
* @param boolean|string $includeTitle Show default <title>-tag, set to false for custom templating * @param bool $includeTitle Show default <title>-tag, set to false for custom templating
* @return string The XHTML metatags * @return string The XHTML metatags
*/ */
public function MetaTags($includeTitle = true) { public function MetaTags($includeTitle = true) {
@ -1460,29 +1452,23 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $tags; return $tags;
} }
/** /**
* Returns the object that contains the content that a user would * Returns the object that contains the content that a user would associate with this page.
* associate with this page.
* *
* Ordinarily, this is just the page itself, but for example on * Ordinarily, this is just the page itself, but for example on RedirectorPages or VirtualPages ContentSource() will
* RedirectorPages or VirtualPages ContentSource() will return the page * return the page that is linked to.
* that is linked to.
* *
* @return SiteTree The content source. * @return $this
*/ */
public function ContentSource() { public function ContentSource() {
return $this; return $this;
} }
/** /**
* Add default records to database. * Add default records to database.
* *
* This function is called whenever the database is built, after the * This function is called whenever the database is built, after the database tables have all been created. Overload
* database tables have all been created. Overload this to add default * this to add default records when the database is built, but make sure you call parent::requireDefaultRecords().
* records when the database is built, but make sure you call
* parent::requireDefaultRecords().
*/ */
public function requireDefaultRecords() { public function requireDefaultRecords() {
parent::requireDefaultRecords(); parent::requireDefaultRecords();
@ -1534,9 +1520,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
} }
//------------------------------------------------------------------------------------//
protected function onBeforeWrite() { protected function onBeforeWrite() {
parent::onBeforeWrite(); parent::onBeforeWrite();
@ -1574,10 +1557,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$fieldsIgnoredByVersioning = array('HasBrokenLink', 'Status', 'HasBrokenFile', 'ToDo', 'VersionID', 'SaveCount'); $fieldsIgnoredByVersioning = array('HasBrokenLink', 'Status', 'HasBrokenFile', 'ToDo', 'VersionID', 'SaveCount');
$changedFields = array_keys($this->getChangedFields(true, 2)); $changedFields = array_keys($this->getChangedFields(true, 2));
// This more rigorous check is inline with the test that write() // This more rigorous check is inline with the test that write() does to dedcide whether or not to write to the
// does to dedcide whether or not to write to the DB. We use that // DB. We use that to avoid cluttering the system with a migrateVersion() call that doesn't get used
// to avoid cluttering the system with a migrateVersion() call
// that doesn't get used
$oneChangedFields = array_keys($this->getChangedFields(true, 1)); $oneChangedFields = array_keys($this->getChangedFields(true, 1));
if($oneChangedFields && !array_diff($changedFields, $fieldsIgnoredByVersioning)) { if($oneChangedFields && !array_diff($changedFields, $fieldsIgnoredByVersioning)) {
@ -1621,7 +1602,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
} }
public function onAfterDelete() { public function onAfterDelete() {
// Need to flush cache to avoid outdated versionnumber references // Need to flush cache to avoid outdated versionnumber references
$this->flushCache(); $this->flushCache();
@ -1680,11 +1660,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Returns TRUE if this object has a URLSegment value that does not conflict with any other objects. This methods * Returns true if this object has a URLSegment value that does not conflict with any other objects. This method
* checks for: * checks for:
* - A page with the same URLSegment that has a conflict. * - A page with the same URLSegment that has a conflict
* - Conflicts with actions on the parent page. * - Conflicts with actions on the parent page
* - A conflict caused by a root page having the same URLSegment as a class name. * - A conflict caused by a root page having the same URLSegment as a class name
* *
* @return bool * @return bool
*/ */
@ -1725,13 +1705,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Generate a URL segment based on the title provided. * Generate a URL segment based on the title provided.
* *
* If {@link Extension}s wish to alter URL segment generation, they can do so by defining * If {@link Extension}s wish to alter URL segment generation, they can do so by defining
* updateURLSegment(&$url, $title). $url will be passed by reference and should be modified. * updateURLSegment(&$url, $title). $url will be passed by reference and should be modified. $title will contain
* $title will contain the title that was originally used as the source of this generated URL. * the title that was originally used as the source of this generated URL. This lets extensions either start from
* This lets extensions either start from scratch, or incrementally modify the generated URL. * scratch, or incrementally modify the generated URL.
* *
* @param string $title Page title. * @param string $title Page title
* @return string Generated url segment * @return string Generated url segment
*/ */
public function generateURLSegment($title){ public function generateURLSegment($title){
@ -1748,6 +1728,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Gets the URL segment for the latest draft version of this page.
*
* @return string * @return string
*/ */
public function getStageURLSegment() { public function getStageURLSegment() {
@ -1758,6 +1740,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Gets the URL segment for the currently published version of this page.
*
* @return string * @return string
*/ */
public function getLiveURLSegment() { public function getLiveURLSegment() {
@ -1768,8 +1752,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Rewrite a file URL on this page, after its been renamed. * Rewrite a file URL on this page, after its been renamed. Triggers the onRenameLinkedAsset action on extensions.
* Triggers the onRenameLinkedAsset action on extensions.
*/ */
public function rewriteFileURL($old, $new) { public function rewriteFileURL($old, $new) {
$fields = $this->inheritedDatabaseFields(); $fields = $this->inheritedDatabaseFields();
@ -1805,8 +1788,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Returns the pages that depend on this page. * Returns the pages that depend on this page. This includes virtual pages, pages that link to it, etc.
* This includes virtual pages, pages that link to it, etc.
* *
* @param bool $includeVirtuals Set to false to exlcude virtual pages. * @param bool $includeVirtuals Set to false to exlcude virtual pages.
* @return ArrayList * @return ArrayList
@ -1863,7 +1845,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return all virtual pages that link to this page * Return all virtual pages that link to this page.
*
* @return DataList
*/ */
public function VirtualPages() { public function VirtualPages() {
@ -1891,14 +1875,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Returns a FieldList with which to create the main editing form. * Returns a FieldList with which to create the main editing form.
* *
* You can override this in your child classes to add extra fields - first * You can override this in your child classes to add extra fields - first get the parent fields using
* get the parent fields using parent::getCMSFields(), then use * parent::getCMSFields(), then use addFieldToTab() on the FieldList.
* addFieldToTab() on the FieldList.
*
* See {@link getSettingsFields()} for a different set of fields
* concerned with configuration aspects on the record, e.g. access control
* *
* @return FieldList The fields to be displayed in the CMS. * See {@link getSettingsFields()} for a different set of fields concerned with configuration aspects on the record,
* e.g. access control.
*
* @return FieldList The fields to be displayed in the CMS
*/ */
public function getCMSFields() { public function getCMSFields() {
require_once("forms/Form.php"); require_once("forms/Form.php");
@ -2079,8 +2062,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Returns fields related to configuration aspects on this record, e.g. access control. * Returns fields related to configuration aspects on this record, e.g. access control. See {@link getCMSFields()}
* See {@link getCMSFields()} for content-related fields. * for content-related fields.
* *
* @return FieldList * @return FieldList
*/ */
@ -2139,10 +2122,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$visibility->setTitle($this->fieldLabel('Visibility')); $visibility->setTitle($this->fieldLabel('Visibility'));
/*
* This filter ensures that the ParentID dropdown selection does not show this node, // This filter ensures that the ParentID dropdown selection does not show this node,
* or its descendents, as this causes vanishing bugs. // or its descendents, as this causes vanishing bugs
*/
$parentIDField->setFilterFunction(create_function('$node', "return \$node->ID != {$this->ID};")); $parentIDField->setFilterFunction(create_function('$node', "return \$node->ID != {$this->ID};"));
$parentTypeSelector->addExtraClass('parentTypeSelector'); $parentTypeSelector->addExtraClass('parentTypeSelector');
@ -2195,9 +2177,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* * @param bool $includerelations A boolean value to indicate if the labels returned should include relation fields
* @param boolean $includerelations a boolean value to indicate if the labels returned include relation fields * @return array
* @return array|string
*/ */
public function fieldLabels($includerelations = true) { public function fieldLabels($includerelations = true) {
$cacheKey = $this->class . '_' . $includerelations; $cacheKey = $this->class . '_' . $includerelations;
@ -2238,17 +2219,15 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return self::$_cache_field_labels[$cacheKey]; return self::$_cache_field_labels[$cacheKey];
} }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** /**
* Get the actions available in the CMS for this page - eg Save, Publish. * Get the actions available in the CMS for this page - eg Save, Publish.
* *
* Frontend scripts and styles know how to handle the following FormFields: * Frontend scripts and styles know how to handle the following FormFields:
* * top-level FormActions appear as standalone buttons * - top-level FormActions appear as standalone buttons
* * top-level CompositeField with FormActions within appear as grouped buttons * - top-level CompositeField with FormActions within appear as grouped buttons
* * TabSet & Tabs appear as a drop ups * - TabSet & Tabs appear as a drop ups
* * FormActions within the Tab are restyled as links * - FormActions within the Tab are restyled as links
* * major actions can provide alternate states for richer presentation (see ssui.button widget extension). * - major actions can provide alternate states for richer presentation (see ssui.button widget extension)
* *
* @return FieldList The available actions for this page. * @return FieldList The available actions for this page.
*/ */
@ -2296,7 +2275,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $actions; return $actions;
} }
if($this->isPublished() && $this->canPublish() && !$this->IsDeletedFromStage && $this->canDeleteFromLive()) { if($this->isPublished() && $this->canPublish() && !$this->getIsDeletedFromStage() && $this->canDeleteFromLive()) {
// "unpublish" // "unpublish"
$moreOptions->push( $moreOptions->push(
FormAction::create('unpublish', _t('SiteTree.BUTTONUNPUBLISH', 'Unpublish'), 'delete') FormAction::create('unpublish', _t('SiteTree.BUTTONUNPUBLISH', 'Unpublish'), 'delete')
@ -2305,7 +2284,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
); );
} }
if($this->stagesDiffer('Stage', 'Live') && !$this->IsDeletedFromStage) { if($this->stagesDiffer('Stage', 'Live') && !$this->getIsDeletedFromStage()) {
if($this->isPublished() && $this->canEdit()) { if($this->isPublished() && $this->canEdit()) {
// "rollback" // "rollback"
$moreOptions->push( $moreOptions->push(
@ -2316,7 +2295,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
if($this->canEdit()) { if($this->canEdit()) {
if($this->IsDeletedFromStage) { if($this->getIsDeletedFromStage()) {
// The usual major actions are not available, so we provide alternatives here. // The usual major actions are not available, so we provide alternatives here.
if($existsOnLive) { if($existsOnLive) {
// "restore" // "restore"
@ -2324,20 +2303,50 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if($this->canDelete() && $this->canDeleteFromLive()) { if($this->canDelete() && $this->canDeleteFromLive()) {
// "delete from live" // "delete from live"
$majorActions->push( $majorActions->push(
FormAction::create('deletefromlive',_t('CMSMain.DELETEFP','Delete'))->addExtraClass('ss-ui-action-destructive') FormAction::create('deletefromlive',_t('CMSMain.DELETEFP','Delete'))
->addExtraClass('ss-ui-action-destructive')
); );
} }
} else { } else {
// Determine if we should force a restore to root (where once it was a subpage)
$restoreToRoot = $this->isParentArchived();
// "restore" // "restore"
$title = $restoreToRoot
? _t('CMSMain.RESTORE_TO_ROOT','Restore draft at top level')
: _t('CMSMain.RESTORE','Restore draft');
$description = $restoreToRoot
? _t('CMSMain.RESTORE_TO_ROOT_DESC','Restore the archived version to draft as a top level page')
: _t('CMSMain.RESTORE_DESC', 'Restore the archived version to draft');
$majorActions->push( $majorActions->push(
FormAction::create('restore',_t('CMSMain.RESTORE','Restore'))->setAttribute('data-icon', 'decline') FormAction::create('restore', $title)
->setDescription($description)
->setAttribute('data-to-root', $restoreToRoot)
->setAttribute('data-icon', 'decline')
); );
} }
} else { } else {
if($this->canDelete()) { // Detect use of legacy actions
// "delete" // {@see CMSMain::enabled_legacy_actions}
$legacy = CMSMain::config()->enabled_legacy_actions;
if(in_array('CMSBatchAction_Delete', $legacy)) {
Deprecation::notice('4.0', 'Delete from Stage is deprecated. Use Archive instead.');
if($this->canDelete()) {
// delete
$moreOptions->push(
FormAction::create('delete',_t('CMSMain.DELETE','Delete draft'))
->addExtraClass('delete ss-ui-action-destructive')
);
}
} elseif($this->canArchive()) {
// "archive"
$moreOptions->push( $moreOptions->push(
FormAction::create('delete',_t('CMSMain.DELETE','Delete draft'))->addExtraClass('delete ss-ui-action-destructive') FormAction::create('archive',_t('CMSMain.ARCHIVE','Archive'))
->setDescription(_t(
'SiteTree.BUTTONARCHIVEDESC',
'Unpublish and send to archive'
))
->addExtraClass('delete ss-ui-action-destructive')
); );
} }
@ -2351,7 +2360,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
} }
if($this->canPublish() && !$this->IsDeletedFromStage) { if($this->canPublish() && !$this->getIsDeletedFromStage()) {
// "publish", as with "save", it supports an alternate state to show when action is needed. // "publish", as with "save", it supports an alternate state to show when action is needed.
$majorActions->push( $majorActions->push(
$publish = FormAction::create('publish', _t('SiteTree.BUTTONPUBLISHED', 'Published')) $publish = FormAction::create('publish', _t('SiteTree.BUTTONPUBLISHED', 'Published'))
@ -2379,6 +2388,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* *
* @uses SiteTreeExtension->onBeforePublish() * @uses SiteTreeExtension->onBeforePublish()
* @uses SiteTreeExtension->onAfterPublish() * @uses SiteTreeExtension->onAfterPublish()
* @return bool True if published
*/ */
public function doPublish() { public function doPublish() {
if (!$this->canPublish()) return false; if (!$this->canPublish()) return false;
@ -2405,7 +2415,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if($linkedPages) foreach($linkedPages as $page) { if($linkedPages) foreach($linkedPages as $page) {
$page->copyFrom($page->CopyContentFrom()); $page->copyFrom($page->CopyContentFrom());
$page->write(); $page->write();
if($page->ExistsOnLive) $page->doPublish(); if($page->getExistsOnLive()) $page->doPublish();
} }
// Need to update pages linking to this one as no longer broken, on the live site // Need to update pages linking to this one as no longer broken, on the live site
@ -2487,13 +2497,35 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
$this->invokeWithExtensions('onAfterRevertToLive', $this); $this->invokeWithExtensions('onAfterRevertToLive', $this);
return true;
}
/**
* Determine if this page references a parent which is archived, and not available in stage
*
* @return bool True if there is an archived parent
*/
protected function isParentArchived() {
if($parentID = $this->ParentID) {
$parentPage = Versioned::get_latest_version("SiteTree", $parentID);
if(!$parentPage || $parentPage->IsDeletedFromStage) {
return true;
}
}
return false;
} }
/** /**
* Restore the content in the active copy of this SiteTree page to the stage site. * Restore the content in the active copy of this SiteTree page to the stage site.
* @return The SiteTree object. *
* @return self
*/ */
public function doRestoreToStage() { public function doRestoreToStage() {
// Ensure that the parent page is restored, otherwise restore to root
if($this->isParentArchived()) {
$this->ParentID = 0;
}
// if no record can be found on draft stage (meaning it has been "deleted from draft" before), // if no record can be found on draft stage (meaning it has been "deleted from draft" before),
// create an empty record // create an empty record
if(!DB::prepared_query("SELECT \"ID\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($this->ID))->value()) { if(!DB::prepared_query("SELECT \"ID\" FROM \"SiteTree\" WHERE \"ID\" = ?", array($this->ID))->value()) {
@ -2521,6 +2553,50 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $result; return $result;
} }
/**
* Removes the page from both live and stage
*
* @return bool Success
*/
public function doArchive() {
if($this->doUnpublish()) {
$this->delete();
return true;
}
return false;
}
/**
* Check if the current user is allowed to archive this page.
* If extended, ensure that both canDelete and canDeleteFromLive are extended also
*
* @param Member $member
* @return bool
*/
public function canArchive($member = null) {
if(!$member) {
$member = Member::currentUser();
}
// Standard mechanism for accepting permission changes from extensions
$extended = $this->extendedCan('canArchive', $member);
if($extended !== null) {
return $extended;
}
// Check if this page can be deleted
if(!$this->canDelete($member)) {
return false;
}
// If published, check if we can delete from live
if($this->ExistsOnLive && !$this->canDeleteFromLive($member)) {
return false;
}
return true;
}
/** /**
* Synonym of {@link doUnpublish} * Synonym of {@link doUnpublish}
*/ */
@ -2529,17 +2605,14 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Check if this page is new - that is, if it has yet to have been written * Check if this page is new - that is, if it has yet to have been written to the database.
* to the database.
* *
* @return boolean True if this page is new. * @return bool
*/ */
public function isNew() { public function isNew() {
/** /**
* This check was a problem for a self-hosted site, and may indicate a * This check was a problem for a self-hosted site, and may indicate a bug in the interpreter on their server,
* bug in the interpreter on their server, or a bug here * or a bug here. Changing the condition from empty($this->ID) to !$this->ID && !$this->record['ID'] fixed this.
* Changing the condition from empty($this->ID) to
* !$this->ID && !$this->record['ID'] fixed this.
*/ */
if(empty($this->ID)) return true; if(empty($this->ID)) return true;
@ -2552,7 +2625,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/** /**
* Check if this page has been published. * Check if this page has been published.
* *
* @return boolean True if this page has been published. * @return bool
*/ */
public function isPublished() { public function isPublished() {
if($this->isNew()) if($this->isNew())
@ -2564,10 +2637,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Get the class dropdown used in the CMS to change the class of a page. * Get the class dropdown used in the CMS to change the class of a page. This returns the list of options in the
* This returns the list of options in the drop as a Map from class name * dropdown as a Map from class name to singular name. Filters by {@link SiteTree->canCreate()}, as well as
* to text in dropdown. Filters by {@link SiteTree->canCreate()}, * {@link SiteTree::$needs_permission}.
* as well as {@link SiteTree::$needs_permission}.
* *
* @return array * @return array
*/ */
@ -2580,7 +2652,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
foreach($classes as $class) { foreach($classes as $class) {
$instance = singleton($class); $instance = singleton($class);
// if the current page type is this the same as the class type always show the page type in the list see open ticket 5880 for why // if the current page type is this the same as the class type always show the page type in the list
if ($this->ClassName != $instance->ClassName) { if ($this->ClassName != $instance->ClassName) {
if((($instance instanceof HiddenClass) || !$instance->canCreate())) continue; if((($instance instanceof HiddenClass) || !$instance->canCreate())) continue;
} }
@ -2594,10 +2666,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$currentClass = $class; $currentClass = $class;
$result[$class] = $pageTypeName; $result[$class] = $pageTypeName;
// if we're in translation mode, the link between the translated pagetype // If we're in translation mode, the link between the translated pagetype title and the actual classname
// title and the actual classname might not be obvious, so we add it in parantheses // might not be obvious, so we add it in parantheses. Example: class "RedirectorPage" has the title
// Example: class "RedirectorPage" has the title "Weiterleitung" in German, // "Weiterleitung" in German, so it shows up as "Weiterleitung (RedirectorPage)"
// so it shows up as "Weiterleitung (RedirectorPage)"
if(i18n::get_lang_from_locale(i18n::get_locale()) != 'en') { if(i18n::get_lang_from_locale(i18n::get_locale()) != 'en') {
$result[$class] = $result[$class] . " ({$class})"; $result[$class] = $result[$class] . " ({$class})";
} }
@ -2616,20 +2687,18 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $result; return $result;
} }
/** /**
* Returns an array of the class names of classes that are allowed * Returns an array of the class names of classes that are allowed to be children of this class.
* to be children of this class.
* *
* @return array * @return string[]
*/ */
public function allowedChildren() { public function allowedChildren() {
$allowedChildren = array(); $allowedChildren = array();
$candidates = $this->stat('allowed_children'); $candidates = $this->stat('allowed_children');
if($candidates && $candidates != "none" && $candidates != "SiteTree_root") { if($candidates && $candidates != "none" && $candidates != "SiteTree_root") {
foreach($candidates as $candidate) { foreach($candidates as $candidate) {
// If a classname is prefixed by "*", such as "*Page", then only that // If a classname is prefixed by "*", such as "*Page", then only that class is allowed - no subclasses.
// class is allowed - no subclasses. Otherwise, the class and all its subclasses are allowed. // Otherwise, the class and all its subclasses are allowed.
if(substr($candidate,0,1) == '*') { if(substr($candidate,0,1) == '*') {
$allowedChildren[] = substr($candidate,1); $allowedChildren[] = substr($candidate,1);
} else { } else {
@ -2644,7 +2713,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $allowedChildren; return $allowedChildren;
} }
/** /**
* Returns the class name of the default class for children of this page. * Returns the class name of the default class for children of this page.
* *
@ -2660,10 +2728,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
} }
/** /**
* Returns the class name of the default class for the parent of this * Returns the class name of the default class for the parent of this page.
* page.
* *
* @return string * @return string
*/ */
@ -2672,8 +2738,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Get the title for use in menus for this page. If the MenuTitle * Get the title for use in menus for this page. If the MenuTitle field is set it returns that, else it returns the
* field is set it returns that, else it returns the Title field. * Title field.
* *
* @return string * @return string
*/ */
@ -2700,42 +2766,41 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* A flag provides the user with additional data about the current page status, * A flag provides the user with additional data about the current page status, for example a "removed from draft"
* for example a "removed from draft" status. Each page can have more than one status flag. * status. Each page can have more than one status flag. Returns a map of a unique key to a (localized) title for
* Returns a map of a unique key to a (localized) title for the flag. * the flag. The unique key can be reused as a CSS class. Use the 'updateStatusFlags' extension point to customize
* The unique key can be reused as a CSS class. * the flags.
* Use the 'updateStatusFlags' extension point to customize the flags.
* *
* Example (simple): * Example (simple):
* "deletedonlive" => "Deleted" * "deletedonlive" => "Deleted"
* *
* Example (with optional title attribute): * Example (with optional title attribute):
* "deletedonlive" => array('text' => "Deleted", 'title' => 'This page has been deleted') * "deletedonlive" => array('text' => "Deleted", 'title' => 'This page has been deleted')
* *
* @param Boolean $cached * @param bool $cached Whether to serve the fields from cache; false regenerate them
* @return array * @return array
*/ */
public function getStatusFlags($cached = true) { public function getStatusFlags($cached = true) {
if(!$this->_cache_statusFlags || !$cached) { if(!$this->_cache_statusFlags || !$cached) {
$flags = array(); $flags = array();
if($this->IsDeletedFromStage) { if($this->getIsDeletedFromStage()) {
if($this->ExistsOnLive) { if($this->getExistsOnLive()) {
$flags['removedfromdraft'] = array( $flags['removedfromdraft'] = array(
'text' => _t('SiteTree.REMOVEDFROMDRAFTSHORT', 'Removed from draft'), 'text' => _t('SiteTree.REMOVEDFROMDRAFTSHORT', 'Removed from draft'),
'title' => _t('SiteTree.REMOVEDFROMDRAFTHELP', 'Page is published, but has been deleted from draft'), 'title' => _t('SiteTree.REMOVEDFROMDRAFTHELP', 'Page is published, but has been deleted from draft'),
); );
} else { } else {
$flags['deletedonlive'] = array( $flags['archived'] = array(
'text' => _t('SiteTree.DELETEDPAGESHORT', 'Deleted'), 'text' => _t('SiteTree.ARCHIVEDPAGESHORT', 'Archived'),
'title' => _t('SiteTree.DELETEDPAGEHELP', 'Page is no longer published'), 'title' => _t('SiteTree.ARCHIVEDPAGEHELP', 'Page is removed from draft and live'),
); );
} }
} else if($this->IsAddedToStage) { } else if($this->getIsAddedToStage()) {
$flags['addedtodraft'] = array( $flags['addedtodraft'] = array(
'text' => _t('SiteTree.ADDEDTODRAFTSHORT', 'Draft'), 'text' => _t('SiteTree.ADDEDTODRAFTSHORT', 'Draft'),
'title' => _t('SiteTree.ADDEDTODRAFTHELP', "Page has not been published yet") 'title' => _t('SiteTree.ADDEDTODRAFTHELP', "Page has not been published yet")
); );
} else if($this->IsModifiedOnStage) { } else if($this->getIsModifiedOnStage()) {
$flags['modified'] = array( $flags['modified'] = array(
'text' => _t('SiteTree.MODIFIEDONDRAFTSHORT', 'Modified'), 'text' => _t('SiteTree.MODIFIEDONDRAFTSHORT', 'Modified'),
'title' => _t('SiteTree.MODIFIEDONDRAFTHELP', 'Page has unpublished changes'), 'title' => _t('SiteTree.MODIFIEDONDRAFTHELP', 'Page has unpublished changes'),
@ -2751,11 +2816,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* getTreeTitle will return three <span> html DOM elements, an empty <span> with * getTreeTitle will return three <span> html DOM elements, an empty <span> with the class 'jstree-pageicon' in
* the class 'jstree-pageicon' in front, following by a <span> wrapping around its * front, following by a <span> wrapping around its MenutTitle, then following by a <span> indicating its
* MenutTitle, then following by a <span> indicating its publication status. * publication status.
* *
* @return string a html string ready to be directly used in a template * @return string An HTML string ready to be directly used in a template
*/ */
public function getTreeTitle() { public function getTreeTitle() {
// Build the list of candidate children // Build the list of candidate children
@ -2788,8 +2853,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Returns the page in the current page stack of the given level. * Returns the page in the current page stack of the given level. Level(1) will return the main menu item that
* Level(1) will return the main menu item that we're currently inside, etc. * we're currently inside, etc.
*
* @param int $level
* @return SiteTree
*/ */
public function Level($level) { public function Level($level) {
$parent = $this; $parent = $this;
@ -2802,7 +2870,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return the CSS classes to apply to this node in the CMS tree * Return the CSS classes to apply to this node in the CMS tree.
* *
* @param string $numChildrenMethod * @param string $numChildrenMethod
* @return string * @return string
@ -2840,11 +2908,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Compares current draft with live version, * Compares current draft with live version, and returns true if no draft version of this page exists but the page
* and returns TRUE if no draft version of this page exists, * is still published (eg, after triggering "Delete from draft site" in the CMS).
* but the page is still published (after triggering "Delete from draft site" in the CMS).
* *
* @return boolean * @return bool
*/ */
public function getIsDeletedFromStage() { public function getIsDeletedFromStage() {
if(!$this->ID) return true; if(!$this->ID) return true;
@ -2852,26 +2919,27 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID); $stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID);
// Return true for both completely deleted pages and for pages just deleted from stage. // Return true for both completely deleted pages and for pages just deleted from stage
return !($stageVersion); return !($stageVersion);
} }
/** /**
* Return true if this page exists on the live site * Return true if this page exists on the live site
*
* @return bool
*/ */
public function getExistsOnLive() { public function getExistsOnLive() {
return (bool)Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->ID); return (bool)Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->ID);
} }
/** /**
* Compares current draft with live version, * Compares current draft with live version, and returns true if these versions differ, meaning there have been
* and returns TRUE if these versions differ, * unpublished changes to the draft site.
* meaning there have been unpublished changes to the draft site.
* *
* @return boolean * @return bool
*/ */
public function getIsModifiedOnStage() { public function getIsModifiedOnStage() {
// new unsaved pages could be never be published // New unsaved pages could be never be published
if($this->isNew()) return false; if($this->isNew()) return false;
$stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID); $stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID);
@ -2884,14 +2952,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Compares current draft with live version, * Compares current draft with live version, and returns true if no live version exists, meaning the page was never
* and returns true if no live version exists, * published.
* meaning the page was never published.
* *
* @return boolean * @return bool
*/ */
public function getIsAddedToStage() { public function getIsAddedToStage() {
// new unsaved pages could be never be published // New unsaved pages could be never be published
if($this->isNew()) return false; if($this->isNew()) return false;
$stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID); $stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID);
@ -2901,18 +2968,16 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Stops extendCMSFields() being called on getCMSFields(). * Stops extendCMSFields() being called on getCMSFields(). This is useful when you need access to fields added by
* This is useful when you need access to fields added by subclasses * subclasses of SiteTree in a extension. Call before calling parent::getCMSFields(), and reenable afterwards.
* of SiteTree in a extension. Call before calling parent::getCMSFields(),
* and reenable afterwards.
*/ */
static public function disableCMSFieldsExtensions() { static public function disableCMSFieldsExtensions() {
self::$runCMSFieldsExtensions = false; self::$runCMSFieldsExtensions = false;
} }
/** /**
* Reenables extendCMSFields() being called on getCMSFields() after * Reenables extendCMSFields() being called on getCMSFields() after it has been disabled by
* it has been disabled by disableCMSFieldsExtensions(). * disableCMSFieldsExtensions().
*/ */
static public function enableCMSFieldsExtensions() { static public function enableCMSFieldsExtensions() {
self::$runCMSFieldsExtensions = true; self::$runCMSFieldsExtensions = true;
@ -2954,9 +3019,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Return the translated Singular name * Return the translated Singular name.
* *
* @return String * @return string
*/ */
public function i18n_singular_name() { public function i18n_singular_name() {
// Convert 'Page' to 'SiteTree' for correct localization lookups // Convert 'Page' to 'SiteTree' for correct localization lookups
@ -2965,8 +3030,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
} }
/** /**
* Overloaded to also provide entities for 'Page' class which is usually * Overloaded to also provide entities for 'Page' class which is usually located in custom code, hence textcollector
* located in custom code, hence textcollector picks it up for the wrong folder. * picks it up for the wrong folder.
*
* @return array
*/ */
public function provideI18nEntities() { public function provideI18nEntities() {
$entities = parent::provideI18nEntities(); $entities = parent::provideI18nEntities();
@ -2984,12 +3051,20 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return $entities; return $entities;
} }
/**
* Returns 'root' if the current page has no parent, or 'subpage' otherwise
*
* @return string
*/
public function getParentType() { public function getParentType() {
return $this->ParentID == 0 ? 'root' : 'subpage'; return $this->ParentID == 0 ? 'root' : 'subpage';
} }
static public function reset() { /**
* Clear the permissions cache for SiteTree
*/
public static function reset() {
self::$cache_permissions = array(); self::$cache_permissions = array();
} }

View File

@ -1,18 +1,18 @@
<?php <?php
/** /**
* Adds tracking of links in any HTMLText fields which reference SiteTree or File items. Attaching this to any * Adds tracking of links in any HTMLText fields which reference SiteTree or File items.
* DataObject will add four fields which contain all links to SiteTree and File items referenced in any HTMLText fields,
* and two booleans to indicate if there are any broken links.
* *
* SiteTreeLinkTracking provides augmentSyncLinkTracking as an entry point for the tracking updater. * Attaching this to any DataObject will add four fields which contain all links to SiteTree and File items
* referenced in any HTMLText fields, and two booleans to indicate if there are any broken links. Call
* augmentSyncLinkTracking to update those fields with any changes to those fields.
* *
* Additionally, a SiteTreeLinkTracking_Highlighter extension is provided which, when applied to HtmlEditorField, * @property SiteTree owner
* will reuse the link SiteTreeLinkTracking's parser to add "ss-broken" classes to all broken links found this way.
* The resulting class will be saved to the Content on the subsequent write operation. If links are found to be
* no longer broken, the class will be removed on the next write.
* *
* The underlying SiteTreeLinkTracking_Parser can recognise broken internal links, broken internal anchors, and some * @property bool HasBrokenFile
* typical broken links such as empty href, or a link starting with a slash. * @property bool HasBrokenLink
*
* @method ManyManyList LinkTracking List of site pages linked on this page.
* @method ManyManyList ImageTracking List of Images linked on this page.
*/ */
class SiteTreeLinkTracking extends DataExtension { class SiteTreeLinkTracking extends DataExtension {
@ -37,6 +37,11 @@ class SiteTreeLinkTracking extends DataExtension {
"ImageTracking" => array("FieldName" => "Varchar") "ImageTracking" => array("FieldName" => "Varchar")
); );
/**
* Scrape the content of a field to detect anly links to local SiteTree pages or files
*
* @param string $fieldName The name of the field on {@link @owner} to scrape
*/
public function trackLinksInField($fieldName) { public function trackLinksInField($fieldName) {
$record = $this->owner; $record = $this->owner;
@ -137,6 +142,9 @@ class SiteTreeLinkTracking extends DataExtension {
} }
} }
/**
* Find HTMLText fields on {@link owner} to scrape for links that need tracking
*/
public function augmentSyncLinkTracking() { public function augmentSyncLinkTracking() {
// Reset boolean broken flags // Reset boolean broken flags
$this->owner->HasBrokenLink = false; $this->owner->HasBrokenLink = false;

View File

@ -86,14 +86,17 @@ class BrokenLinksReport extends SS_Report {
$dateTitle = _t('BrokenLinksReport.ColumnDateLastPublished', 'Date last published'); $dateTitle = _t('BrokenLinksReport.ColumnDateLastPublished', 'Date last published');
} }
$linkBase = singleton('CMSPageEditController')->Link('show') . '/'; $linkBase = singleton('CMSPageEditController')->Link('show');
$fields = array( $fields = array(
"Title" => array( "Title" => array(
"title" => _t('BrokenLinksReport.PageName', 'Page name'), "title" => _t('BrokenLinksReport.PageName', 'Page name'),
'formatting' => sprintf( 'formatting' => function($value, $item) use ($linkBase) {
'<a href=\"' . $linkBase . '$ID\" title=\"%s\">$value</a>', return sprintf('<a href=\"%s\" title=\"%s\">%s</a>',
_t('BrokenLinksReport.HoverTitleEditPage', 'Edit page') Controller::join_links($linkBase, $item->ID),
) _t('BrokenLinksReport.HoverTitleEditPage', 'Edit page'),
$value
);
}
), ),
"LastEdited" => array( "LastEdited" => array(
"title" => $dateTitle, "title" => $dateTitle,

View File

@ -27,7 +27,7 @@ class ContentControllerSearchExtension extends Extension {
$actions = new FieldList( $actions = new FieldList(
new FormAction('results', _t('SearchForm.GO', 'Go')) new FormAction('results', _t('SearchForm.GO', 'Go'))
); );
$form = new SearchForm($this->owner, 'SearchForm', $fields, $actions); $form = SearchForm::create($this->owner, 'SearchForm', $fields, $actions);
$form->classesToSearch(FulltextSearchable::get_searchable_classes()); $form->classesToSearch(FulltextSearchable::get_searchable_classes());
return $form; return $form;
} }

View File

@ -276,6 +276,116 @@
} }
}); });
/**
* Class: .cms-edit-form .Actions #Form_EditForm_action_archive
*
* Informing the user about the archive action while requiring confirmation
*/
$('.cms-edit-form .Actions #Form_EditForm_action_archive').entwine({
/**
* Function: onclick
*
* Parameters:
* (Event) e
*/
onclick: function(e) {
var form = this.parents('form:first'), version = form.find(':input[name=Version]').val(), message = '';
message = ss.i18n.sprintf(
ss.i18n._t('CMSMain.Archive'),
version
);
if(confirm(message)) {
return this._super(e);
} else {
return false;
}
}
});
/**
* Class: .cms-edit-form .Actions #Form_EditForm_action_restore
*
* Informing the user about the archive action while requiring confirmation
*/
$('.cms-edit-form .Actions #Form_EditForm_action_restore').entwine({
/**
* Function: onclick
*
* Parameters:
* (Event) e
*/
onclick: function(e) {
var form = this.parents('form:first'),
version = form.find(':input[name=Version]').val(),
message = '',
toRoot = this.data('toRoot');
message = ss.i18n.sprintf(
ss.i18n._t(toRoot ? 'CMSMain.RestoreToRoot' : 'CMSMain.Restore'),
version
);
if(confirm(message)) {
return this._super(e);
} else {
return false;
}
}
});
/**
* Class: .cms-edit-form .Actions #Form_EditForm_action_delete
*
* Informing the user about the delete from draft action while requiring confirmation
*/
$('.cms-edit-form .Actions #Form_EditForm_action_delete').entwine({
/**
* Function: onclick
*
* Parameters:
* (Event) e
*/
onclick: function(e) {
var form = this.parents('form:first'), version = form.find(':input[name=Version]').val(), message = '';
message = ss.i18n.sprintf(
ss.i18n._t('CMSMain.DeleteFromDraft'),
version
);
if(confirm(message)) {
return this._super(e);
} else {
return false;
}
}
});
/**
* Class: .cms-edit-form .Actions #Form_EditForm_action_unpublish
* Informing the user about the unpublish action while requiring confirmation
*/
$('.cms-edit-form .Actions #Form_EditForm_action_unpublish').entwine({
/**
* Function: onclick
*
* Parameters:
* (Event) e
*/
onclick: function(e) {
var form = this.parents('form:first'), version = form.find(':input[name=Version]').val(), message = '';
message = ss.i18n.sprintf(
ss.i18n._t('CMSMain.Unpublish'),
version
);
if(confirm(message)) {
return this._super(e);
} else {
return false;
}
}
});
/** /**
* Enable save buttons upon detecting changes to content. * Enable save buttons upon detecting changes to content.
* "changed" class is added by jQuery.changetracker. * "changed" class is added by jQuery.changetracker.

View File

@ -36,8 +36,13 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
"Tree.ThisPageOnly": "This page only", "Tree.ThisPageOnly": "This page only",
"Tree.ThisPageAndSubpages": "This page and subpages", "Tree.ThisPageAndSubpages": "This page and subpages",
"Tree.ShowAsList": "Show children as list", "Tree.ShowAsList": "Show children as list",
"CMSMain.ConfirmRestoreFromLive": "Do you really want to copy the published content to the draft site?", "CMSMain.ConfirmRestoreFromLive": "Are you sure you want to revert draft to when the page was last published?",
"CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?", "CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?",
"CMSMain.Archive": "Are you sure you want to archive this page?\n\nThe page will be unpublished and sent to the archive.",
"CMSMain.Restore": "Are you sure you want to restore this page from archive?",
"CMSMain.RestoreToRoot": "Are you sure you want to restore this page from archive?\n\nBecause the parent page is not available this will be restored to the top level.",
"CMSMain.Unpublish": "Are you sure you want to remove your page from the published site?\n\nThis page will still be available in the sitetree as draft.",
"CMSMain.DeleteFromDraft": "Are you sure you want to remove your page from the draft site?\n\nThis page will remain on the published site.",
"URLSEGMENT.Edit": "Edit", "URLSEGMENT.Edit": "Edit",
"URLSEGMENT.OK": "OK", "URLSEGMENT.OK": "OK",
"URLSEGMENT.Cancel": "Cancel", "URLSEGMENT.Cancel": "Cancel",

View File

@ -4,42 +4,42 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined'); if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else { } else {
ss.i18n.addDictionary('id', { ss.i18n.addDictionary('id', {
"CMSMAIN.WARNINGSAVEPAGESBEFOREADDING": "You have to save a page before adding children underneath it", "CMSMAIN.WARNINGSAVEPAGESBEFOREADDING": "Anda harus menyimpan laman sebelum menambahkan laman turunan.",
"CMSMAIN.CANTADDCHILDREN": "You can't add children to the selected node", "CMSMAIN.CANTADDCHILDREN": "Anda tidak dapat menambahkan turunan pada simpul terpilih",
"CMSMAIN.ERRORADDINGPAGE": "Error adding page", "CMSMAIN.ERRORADDINGPAGE": "Ada kesalahan menambahkan laman",
"CMSMAIN.FILTEREDTREE": "Filtered tree to only show changed pages", "CMSMAIN.FILTEREDTREE": "Hanya menampilkan laman yang berubah",
"CMSMAIN.ERRORFILTERPAGES": "Could not filter tree to only show changed pages<br />%s", "CMSMAIN.ERRORFILTERPAGES": "Tidak dapat menyaring laman<br />%s",
"CMSMAIN.ERRORUNFILTER": "Unfiltered tree", "CMSMAIN.ERRORUNFILTER": "Tak tersaring",
"CMSMAIN.PUBLISHINGPAGES": "Publishing pages...", "CMSMAIN.PUBLISHINGPAGES": "Menerbitkan laman...",
"CMSMAIN.SELECTONEPAGE": "Mohon pilih minimal 1 halaman.", "CMSMAIN.SELECTONEPAGE": "Mohon pilih minimal 1 laman.",
"CMSMAIN.ERRORPUBLISHING": "Error publishing pages", "CMSMAIN.ERRORPUBLISHING": "Ada kesalahan menerbitkan laman",
"CMSMAIN.REALLYDELETEPAGES": "Do you really want to delete the %s marked pages?", "CMSMAIN.REALLYDELETEPAGES": "Anda ingin menghapus laman %s yang ditandai?",
"CMSMAIN.DELETINGPAGES": "Sedang menghapus halaman...", "CMSMAIN.DELETINGPAGES": "Menghapus laman...",
"CMSMAIN.ERRORDELETINGPAGES": "Error deleting pages", "CMSMAIN.ERRORDELETINGPAGES": "Ada kesalahan menghapus laman",
"CMSMAIN.PUBLISHING": "Publishing...", "CMSMAIN.PUBLISHING": "Menerbitkan...",
"CMSMAIN.RESTORING": "Sedang pemulihan", "CMSMAIN.RESTORING": "Memulihkan...",
"CMSMAIN.ERRORREVERTING": "Error reverting to live content", "CMSMAIN.ERRORREVERTING": "Ada kesalahan mengembalikan konten",
"CMSMAIN.SAVING": "Sedang menyimpan...", "CMSMAIN.SAVING": "Menyimpan...",
"CMSMAIN.SELECTMOREPAGES": "You have %s pages selected.\n\nDo you really want to perform this action?", "CMSMAIN.SELECTMOREPAGES": "Anda memilih %s laman.\n\nAnda ingin melanjutkan?",
"CMSMAIN.ALERTCLASSNAME": "The page type will be updated after the page is saved", "CMSMAIN.ALERTCLASSNAME": "Jenis laman akan diperbarui setelah laman disimpan",
"CMSMAIN.URLSEGMENTVALIDATION": "URLs can only be made up of letters, digits and hyphens.", "CMSMAIN.URLSEGMENTVALIDATION": "URL hanya boleh terdiri dari huruf, angka dan tanda sambung.",
"AssetAdmin.BATCHACTIONSDELETECONFIRM": "Apakah kamu yakin akan menghapus %s map?", "AssetAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus folder %s?",
"AssetTableField.REALLYDELETE": "Do you really want to delete the marked files?", "AssetTableField.REALLYDELETE": "Anda ingin menghapus berkas yang ditandai?",
"AssetTableField.MOVING": "Memindahkan %s berkas(s)", "AssetTableField.MOVING": "Memindahkan %s berkas",
"CMSMAIN.AddSearchCriteria": "Tambah kriteria", "CMSMAIN.AddSearchCriteria": "Tambah Kriteria",
"WidgetAreaEditor.TOOMANY": "Sorry, you have reached the maximum number of widgets in this area", "WidgetAreaEditor.TOOMANY": "Maaf, Anda mencapai jumlah maksimal widget di area ini",
"AssetAdmin.ConfirmDelete": "Do you really want to delete this folder and all contained files?", "AssetAdmin.ConfirmDelete": "Anda ingin menghapus folder dan berkas di dalamnya?",
"Folder.Name": "Nama map", "Folder.Name": "Nama folder",
"Tree.AddSubPage": "Tambah halaman baru di sini", "Tree.AddSubPage": "Tambah laman baru di sini",
"Tree.Duplicate": "Duplikasi", "Tree.Duplicate": "Gandakan",
"Tree.EditPage": "Ubah", "Tree.EditPage": "Edit",
"Tree.ThisPageOnly": "Hanya halaman ini", "Tree.ThisPageOnly": "Hanya laman ini",
"Tree.ThisPageAndSubpages": "This page and subpages", "Tree.ThisPageAndSubpages": "Laman dan sublaman ini",
"Tree.ShowAsList": "Show children as list", "Tree.ShowAsList": "Tampilkan turunan sebagai daftar",
"CMSMain.ConfirmRestoreFromLive": "Do you really want to copy the published content to the draft site?", "CMSMain.ConfirmRestoreFromLive": "Apakah Anda ingin menyalin konten yang sudah terbit ke draf?",
"CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?", "CMSMain.RollbackToVersion": "Apakah Anda ingin kembali ke versi #%s dari laman ini?",
"URLSEGMENT.Edit": "Ubah", "URLSEGMENT.Edit": "Edit",
"URLSEGMENT.OK": "Oke", "URLSEGMENT.OK": "OK",
"URLSEGMENT.Cancel": "Batal" "URLSEGMENT.Cancel": "Batal"
}); });
} }

View File

@ -31,8 +31,13 @@
"Tree.ThisPageOnly": "This page only", "Tree.ThisPageOnly": "This page only",
"Tree.ThisPageAndSubpages": "This page and subpages", "Tree.ThisPageAndSubpages": "This page and subpages",
"Tree.ShowAsList": "Show children as list", "Tree.ShowAsList": "Show children as list",
"CMSMain.ConfirmRestoreFromLive": "Do you really want to copy the published content to the draft site?", "CMSMain.ConfirmRestoreFromLive": "Are you sure you want to revert draft to when the page was last published?",
"CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?", "CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?",
"CMSMain.Archive": "Are you sure you want to archive this page?\n\nThe page will be unpublished and sent to the archive.",
"CMSMain.Restore": "Are you sure you want to restore this page from archive?",
"CMSMain.RestoreToRoot": "Are you sure you want to restore this page from archive?\n\nBecause the parent page is not available this will be restored to the top level.",
"CMSMain.Unpublish": "Are you sure you want to remove your page from the published site?\n\nThis page will still be available in the sitetree as draft.",
"CMSMain.DeleteFromDraft": "Are you sure you want to remove your page from the draft site?\n\nThis page will remain on the published site.",
"URLSEGMENT.Edit": "Edit", "URLSEGMENT.Edit": "Edit",
"URLSEGMENT.OK": "OK", "URLSEGMENT.OK": "OK",
"URLSEGMENT.Cancel": "Cancel", "URLSEGMENT.Cancel": "Cancel",

View File

@ -1,39 +1,39 @@
{ {
"CMSMAIN.WARNINGSAVEPAGESBEFOREADDING": "You have to save a page before adding children underneath it", "CMSMAIN.WARNINGSAVEPAGESBEFOREADDING": "Anda harus menyimpan laman sebelum menambahkan laman turunan.",
"CMSMAIN.CANTADDCHILDREN": "You can't add children to the selected node", "CMSMAIN.CANTADDCHILDREN": "Anda tidak dapat menambahkan turunan pada simpul terpilih",
"CMSMAIN.ERRORADDINGPAGE": "Error adding page", "CMSMAIN.ERRORADDINGPAGE": "Ada kesalahan menambahkan laman",
"CMSMAIN.FILTEREDTREE": "Filtered tree to only show changed pages", "CMSMAIN.FILTEREDTREE": "Hanya menampilkan laman yang berubah",
"CMSMAIN.ERRORFILTERPAGES": "Could not filter tree to only show changed pages<br />%s", "CMSMAIN.ERRORFILTERPAGES": "Tidak dapat menyaring laman<br />%s",
"CMSMAIN.ERRORUNFILTER": "Unfiltered tree", "CMSMAIN.ERRORUNFILTER": "Tak tersaring",
"CMSMAIN.PUBLISHINGPAGES": "Publishing pages...", "CMSMAIN.PUBLISHINGPAGES": "Menerbitkan laman...",
"CMSMAIN.SELECTONEPAGE": "Mohon pilih minimal 1 halaman.", "CMSMAIN.SELECTONEPAGE": "Mohon pilih minimal 1 laman.",
"CMSMAIN.ERRORPUBLISHING": "Error publishing pages", "CMSMAIN.ERRORPUBLISHING": "Ada kesalahan menerbitkan laman",
"CMSMAIN.REALLYDELETEPAGES": "Do you really want to delete the %s marked pages?", "CMSMAIN.REALLYDELETEPAGES": "Anda ingin menghapus laman %s yang ditandai?",
"CMSMAIN.DELETINGPAGES": "Sedang menghapus halaman...", "CMSMAIN.DELETINGPAGES": "Menghapus laman...",
"CMSMAIN.ERRORDELETINGPAGES": "Error deleting pages", "CMSMAIN.ERRORDELETINGPAGES": "Ada kesalahan menghapus laman",
"CMSMAIN.PUBLISHING": "Publishing...", "CMSMAIN.PUBLISHING": "Menerbitkan...",
"CMSMAIN.RESTORING": "Sedang pemulihan", "CMSMAIN.RESTORING": "Memulihkan...",
"CMSMAIN.ERRORREVERTING": "Error reverting to live content", "CMSMAIN.ERRORREVERTING": "Ada kesalahan mengembalikan konten",
"CMSMAIN.SAVING": "Sedang menyimpan...", "CMSMAIN.SAVING": "Menyimpan...",
"CMSMAIN.SELECTMOREPAGES": "You have %s pages selected.\n\nDo you really want to perform this action?", "CMSMAIN.SELECTMOREPAGES": "Anda memilih %s laman.\n\nAnda ingin melanjutkan?",
"CMSMAIN.ALERTCLASSNAME": "The page type will be updated after the page is saved", "CMSMAIN.ALERTCLASSNAME": "Jenis laman akan diperbarui setelah laman disimpan",
"CMSMAIN.URLSEGMENTVALIDATION": "URLs can only be made up of letters, digits and hyphens.", "CMSMAIN.URLSEGMENTVALIDATION": "URL hanya boleh terdiri dari huruf, angka dan tanda sambung.",
"AssetAdmin.BATCHACTIONSDELETECONFIRM": "Apakah kamu yakin akan menghapus %s map?", "AssetAdmin.BATCHACTIONSDELETECONFIRM": "Anda ingin menghapus folder %s?",
"AssetTableField.REALLYDELETE": "Do you really want to delete the marked files?", "AssetTableField.REALLYDELETE": "Anda ingin menghapus berkas yang ditandai?",
"AssetTableField.MOVING": "Memindahkan %s berkas(s)", "AssetTableField.MOVING": "Memindahkan %s berkas",
"CMSMAIN.AddSearchCriteria": "Tambah kriteria", "CMSMAIN.AddSearchCriteria": "Tambah Kriteria",
"WidgetAreaEditor.TOOMANY": "Sorry, you have reached the maximum number of widgets in this area", "WidgetAreaEditor.TOOMANY": "Maaf, Anda mencapai jumlah maksimal widget di area ini",
"AssetAdmin.ConfirmDelete": "Do you really want to delete this folder and all contained files?", "AssetAdmin.ConfirmDelete": "Anda ingin menghapus folder dan berkas di dalamnya?",
"Folder.Name": "Nama map", "Folder.Name": "Nama folder",
"Tree.AddSubPage": "Tambah halaman baru di sini", "Tree.AddSubPage": "Tambah laman baru di sini",
"Tree.Duplicate": "Duplikasi", "Tree.Duplicate": "Gandakan",
"Tree.EditPage": "Ubah", "Tree.EditPage": "Edit",
"Tree.ThisPageOnly": "Hanya halaman ini", "Tree.ThisPageOnly": "Hanya laman ini",
"Tree.ThisPageAndSubpages": "This page and subpages", "Tree.ThisPageAndSubpages": "Laman dan sublaman ini",
"Tree.ShowAsList": "Show children as list", "Tree.ShowAsList": "Tampilkan turunan sebagai daftar",
"CMSMain.ConfirmRestoreFromLive": "Do you really want to copy the published content to the draft site?", "CMSMain.ConfirmRestoreFromLive": "Apakah Anda ingin menyalin konten yang sudah terbit ke draf?",
"CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?", "CMSMain.RollbackToVersion": "Apakah Anda ingin kembali ke versi #%s dari laman ini?",
"URLSEGMENT.Edit": "Ubah", "URLSEGMENT.Edit": "Edit",
"URLSEGMENT.OK": "Oke", "URLSEGMENT.OK": "OK",
"URLSEGMENT.Cancel": "Batal" "URLSEGMENT.Cancel": "Batal"
} }

View File

@ -6,7 +6,7 @@
"CMSMAIN.ERRORFILTERPAGES": "Kunde inte filtrera trädet för att visa enbart ändrade sidor<br />%s", "CMSMAIN.ERRORFILTERPAGES": "Kunde inte filtrera trädet för att visa enbart ändrade sidor<br />%s",
"CMSMAIN.ERRORUNFILTER": "Ofiltrerat träd", "CMSMAIN.ERRORUNFILTER": "Ofiltrerat träd",
"CMSMAIN.PUBLISHINGPAGES": "Publicerar sidor...", "CMSMAIN.PUBLISHINGPAGES": "Publicerar sidor...",
"CMSMAIN.SELECTONEPAGE": "Vänligen välj åtminståne 1 sida.", "CMSMAIN.SELECTONEPAGE": "Vänligen välj åtminstone 1 sida.",
"CMSMAIN.ERRORPUBLISHING": "Ett fel uppstod när sidorna skulle publiceras", "CMSMAIN.ERRORPUBLISHING": "Ett fel uppstod när sidorna skulle publiceras",
"CMSMAIN.REALLYDELETEPAGES": "Vill du verkligen radera de %s markerade sidorna?", "CMSMAIN.REALLYDELETEPAGES": "Vill du verkligen radera de %s markerade sidorna?",
"CMSMAIN.DELETINGPAGES": "Raderar sidor...", "CMSMAIN.DELETINGPAGES": "Raderar sidor...",
@ -21,7 +21,7 @@
"AssetAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s mappar?", "AssetAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s mappar?",
"AssetTableField.REALLYDELETE": "Vill du verkligen radera de markerade filerna?", "AssetTableField.REALLYDELETE": "Vill du verkligen radera de markerade filerna?",
"AssetTableField.MOVING": "Flyttar %s fil(er)", "AssetTableField.MOVING": "Flyttar %s fil(er)",
"CMSMAIN.AddSearchCriteria": "Lägg till kriterie", "CMSMAIN.AddSearchCriteria": "Lägg till kriterium",
"WidgetAreaEditor.TOOMANY": "Du har tyvärr nått max antal widgetar i detta område.", "WidgetAreaEditor.TOOMANY": "Du har tyvärr nått max antal widgetar i detta område.",
"AssetAdmin.ConfirmDelete": "Vill du verkligen radera denna mapp och alla filer i den?", "AssetAdmin.ConfirmDelete": "Vill du verkligen radera denna mapp och alla filer i den?",
"Folder.Name": "Mappnamn", "Folder.Name": "Mappnamn",

View File

@ -11,7 +11,7 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
"CMSMAIN.ERRORFILTERPAGES": "Kunde inte filtrera trädet för att visa enbart ändrade sidor<br />%s", "CMSMAIN.ERRORFILTERPAGES": "Kunde inte filtrera trädet för att visa enbart ändrade sidor<br />%s",
"CMSMAIN.ERRORUNFILTER": "Ofiltrerat träd", "CMSMAIN.ERRORUNFILTER": "Ofiltrerat träd",
"CMSMAIN.PUBLISHINGPAGES": "Publicerar sidor...", "CMSMAIN.PUBLISHINGPAGES": "Publicerar sidor...",
"CMSMAIN.SELECTONEPAGE": "Vänligen välj åtminståne 1 sida.", "CMSMAIN.SELECTONEPAGE": "Vänligen välj åtminstone 1 sida.",
"CMSMAIN.ERRORPUBLISHING": "Ett fel uppstod när sidorna skulle publiceras", "CMSMAIN.ERRORPUBLISHING": "Ett fel uppstod när sidorna skulle publiceras",
"CMSMAIN.REALLYDELETEPAGES": "Vill du verkligen radera de %s markerade sidorna?", "CMSMAIN.REALLYDELETEPAGES": "Vill du verkligen radera de %s markerade sidorna?",
"CMSMAIN.DELETINGPAGES": "Raderar sidor...", "CMSMAIN.DELETINGPAGES": "Raderar sidor...",
@ -26,7 +26,7 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
"AssetAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s mappar?", "AssetAdmin.BATCHACTIONSDELETECONFIRM": "Vill du verkligen radera %s mappar?",
"AssetTableField.REALLYDELETE": "Vill du verkligen radera de markerade filerna?", "AssetTableField.REALLYDELETE": "Vill du verkligen radera de markerade filerna?",
"AssetTableField.MOVING": "Flyttar %s fil(er)", "AssetTableField.MOVING": "Flyttar %s fil(er)",
"CMSMAIN.AddSearchCriteria": "Lägg till kriterie", "CMSMAIN.AddSearchCriteria": "Lägg till kriterium",
"WidgetAreaEditor.TOOMANY": "Du har tyvärr nått max antal widgetar i detta område.", "WidgetAreaEditor.TOOMANY": "Du har tyvärr nått max antal widgetar i detta område.",
"AssetAdmin.ConfirmDelete": "Vill du verkligen radera denna mapp och alla filer i den?", "AssetAdmin.ConfirmDelete": "Vill du verkligen radera denna mapp och alla filer i den?",
"Folder.Name": "Mappnamn", "Folder.Name": "Mappnamn",

View File

@ -238,7 +238,7 @@ cs:
HASBEENSETUP: 'Přesměrovací stránka byla nastavena bez cíle.' HASBEENSETUP: 'Přesměrovací stránka byla nastavena bez cíle.'
HEADER: 'Tato stránka přesměruje uživatele na jinou stránku' HEADER: 'Tato stránka přesměruje uživatele na jinou stránku'
OTHERURL: 'Jiná web adresa' OTHERURL: 'Jiná web adresa'
PLURALNAME: 'Přesměrovací stránky' PLURALNAME: 'Přesměrovací stránka'
REDIRECTTO: 'Přesměrovat na' REDIRECTTO: 'Přesměrovat na'
REDIRECTTOEXTERNAL: 'Jiná web stránka' REDIRECTTOEXTERNAL: 'Jiná web stránka'
REDIRECTTOPAGE: 'Stránka na vašem webu' REDIRECTTOPAGE: 'Stránka na vašem webu'

View File

@ -200,6 +200,8 @@ de:
415: '415 - nicht-unterstützer Medientyp' 415: '415 - nicht-unterstützer Medientyp'
416: '416 - Anfragebereich nicht erfüllbar' 416: '416 - Anfragebereich nicht erfüllbar'
417: '417 - Erwartung nicht erfüllt' 417: '417 - Erwartung nicht erfüllt'
422: '422 - Verarbeitung abgelehnt'
429: '429 - Zu viele Anfragen'
500: '500 - Interner Serverfehler' 500: '500 - Interner Serverfehler'
501: '501 - nicht implementiert' 501: '501 - nicht implementiert'
502: '502 - Fehlerhafter Gateway' 502: '502 - Fehlerhafter Gateway'
@ -236,7 +238,7 @@ de:
HASBEENSETUP: 'Eine Weiterleitungsseite wurde erstellt ohne das eine Weiterleitung definiert wurde.' HASBEENSETUP: 'Eine Weiterleitungsseite wurde erstellt ohne das eine Weiterleitung definiert wurde.'
HEADER: 'Diese Seite wird Nutzer auf eine andere Seite weiterleiten' HEADER: 'Diese Seite wird Nutzer auf eine andere Seite weiterleiten'
OTHERURL: 'Andere Webseiten URL' OTHERURL: 'Andere Webseiten URL'
PLURALNAME: 'Umleitungsseite' PLURALNAME: 'Weiterleitungsseiten'
REDIRECTTO: 'Weiterleiten zu' REDIRECTTO: 'Weiterleiten zu'
REDIRECTTOEXTERNAL: 'Andere Website' REDIRECTTOEXTERNAL: 'Andere Website'
REDIRECTTOPAGE: 'Eine Seite auf Ihrer Website' REDIRECTTOPAGE: 'Eine Seite auf Ihrer Website'

View File

@ -70,6 +70,7 @@ el:
DELETEFP: Διαγραφή DELETEFP: Διαγραφή
EMAIL: Email EMAIL: Email
EditTree: 'Επεξεργασία Δένδρου' EditTree: 'Επεξεργασία Δένδρου'
ListFiltered: 'Φιλτραρισμένη λίστα.'
NEWPAGE: 'Νέο {pagetype}' NEWPAGE: 'Νέο {pagetype}'
PAGENOTEXISTS: 'Αυτή η σελίδα δεν υπάρχει' PAGENOTEXISTS: 'Αυτή η σελίδα δεν υπάρχει'
PAGES: Σελίδες PAGES: Σελίδες
@ -83,6 +84,7 @@ el:
TabContent: Περιεχόμενο TabContent: Περιεχόμενο
TabHistory: Ιστορικό TabHistory: Ιστορικό
TabSettings: Ρυθμίσεις TabSettings: Ρυθμίσεις
TreeFiltered: 'Φιλτραρισμένο δένδρο.'
TreeFilteredClear: 'Καθαρισμός φίλτρου' TreeFilteredClear: 'Καθαρισμός φίλτρου'
MENUTITLE: 'Επεξεργασία Σελίδας' MENUTITLE: 'Επεξεργασία Σελίδας'
CMSMain_left_ss: CMSMain_left_ss:
@ -133,7 +135,17 @@ el:
PUBLISHED: Δημοσιευμένο PUBLISHED: Δημοσιευμένο
Password: Κωδικός Password: Κωδικός
VIEWPAGEIN: 'Προβολή Σελίδας σε:' VIEWPAGEIN: 'Προβολή Σελίδας σε:'
ErrorPage:
404: '404 - Δεν βρέθηκε'
CODE: 'Κωδικός σφάλματος'
DEFAULTERRORPAGETITLE: 'Η σελίδα δεν βρέθηκε'
DEFAULTSERVERERRORPAGETITLE: 'Σφάλμα διακομιστή'
PLURALNAME: 'Σελίδες Σφάλματος'
SINGULARNAME: 'Σελίδα Σφάλματος'
Folder: Folder:
AddFolderButton: 'Προσθήκη φακέλου'
DELETEUNUSEDTHUMBNAILS: 'Διαγραφή αχρησιμοποίητων εικονιδίων'
UNUSEDFILESTITLE: 'Αχρησιμοποίητα αρχεία'
UNUSEDTHUMBNAILSTITLE: 'Αχρησιμοποίητα εικονίδια' UNUSEDTHUMBNAILSTITLE: 'Αχρησιμοποίητα εικονίδια'
UploadFilesButton: Μεταφόρτωση UploadFilesButton: Μεταφόρτωση
LeftAndMain: LeftAndMain:
@ -149,8 +161,12 @@ el:
RedirectorPage: RedirectorPage:
DESCRIPTION: 'Κάνει ανακατεύθυνση σε μια διαφορετική εσωτερική σελίδα' DESCRIPTION: 'Κάνει ανακατεύθυνση σε μια διαφορετική εσωτερική σελίδα'
HEADER: 'Αυτή η σελίδα θα ανακατευθύνει τους χρήστες σε μια άλλη σελίδα' HEADER: 'Αυτή η σελίδα θα ανακατευθύνει τους χρήστες σε μια άλλη σελίδα'
REDIRECTTO: 'Ανακατεύθυνση σε'
ReportAdmin: ReportAdmin:
ReportTitle: Τίτλος ReportTitle: Τίτλος
MENUTITLE: Αναφορές
ReportAdminForm:
FILTERBY: 'Φίλτρο κατά'
SearchForm: SearchForm:
GO: Μετάβαση GO: Μετάβαση
SEARCH: Αναζήτηση SEARCH: Αναζήτηση
@ -207,12 +223,14 @@ el:
MENUTITLE: 'Επίπεδο πλοίγησης' MENUTITLE: 'Επίπεδο πλοίγησης'
METADESC: 'Μετα-Περιγραφή' METADESC: 'Μετα-Περιγραφή'
MODIFIEDONDRAFTHELP: 'Η σελίδα έχει μη δημοσιευμένες αλλαγές' MODIFIEDONDRAFTHELP: 'Η σελίδα έχει μη δημοσιευμένες αλλαγές'
MODIFIEDONDRAFTSHORT: Τροποποιημένο
MetadataToggle: Μετα-δεδομένα MetadataToggle: Μετα-δεδομένα
MoreOptions: 'Περισσότερες επιλογές' MoreOptions: 'Περισσότερες επιλογές'
NOTPUBLISHED: 'Δεν έχει δημοσιευθεί' NOTPUBLISHED: 'Δεν έχει δημοσιευθεί'
PAGELOCATION: 'Θέση σελίδας' PAGELOCATION: 'Θέση σελίδας'
PAGETITLE: 'Όνομα σελίδας' PAGETITLE: 'Όνομα σελίδας'
PAGETYPE: 'Τύπος σελίδας' PAGETYPE: 'Τύπος σελίδας'
PARENTID: 'Γονική Σελίδα'
PARENTTYPE: 'Θέση σελίδας' PARENTTYPE: 'Θέση σελίδας'
PLURALNAME: Σελίδες PLURALNAME: Σελίδες
REORGANISE_DESCRIPTION: 'Αλλαγή δομής του ιστοτόπου ' REORGANISE_DESCRIPTION: 'Αλλαγή δομής του ιστοτόπου '
@ -240,6 +258,7 @@ el:
DESCRIPTION: 'Εμφανίζει το περιεχόμενο μιας άλλης σελίδας' DESCRIPTION: 'Εμφανίζει το περιεχόμενο μιας άλλης σελίδας'
EditLink: επεξεργασία EditLink: επεξεργασία
HEADER: 'Αυτή είναι μια εικονική σελίδα' HEADER: 'Αυτή είναι μια εικονική σελίδα'
PLURALNAME: 'Εικονικές Σελίδες'
SINGULARNAME: 'Εικονική Σελίδα' SINGULARNAME: 'Εικονική Σελίδα'
CMSFileAddController: CMSFileAddController:
MENUTITLE: Αρχεία MENUTITLE: Αρχεία

View File

@ -67,8 +67,8 @@ en:
DELETE_PAGES: 'Delete from published site' DELETE_PAGES: 'Delete from published site'
PUBLISHED_PAGES: 'Published %d pages, %d failures' PUBLISHED_PAGES: 'Published %d pages, %d failures'
PUBLISH_PAGES: Publish PUBLISH_PAGES: Publish
UNPUBLISHED_PAGES: 'Un-published %d pages' UNPUBLISHED_PAGES: 'Unpublished %d pages'
UNPUBLISH_PAGES: Un-publish UNPUBLISH_PAGES: Unpublish
CMSMain: CMSMain:
ACCESS: 'Access to ''{title}'' section' ACCESS: 'Access to ''{title}'' section'
ACCESS_HELP: 'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".' ACCESS_HELP: 'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
@ -100,8 +100,11 @@ en:
REMOVED: 'Deleted ''{title}''{description} from live site' REMOVED: 'Deleted ''{title}''{description} from live site'
REMOVEDPAGE: 'Removed ''{title}'' from the published site' REMOVEDPAGE: 'Removed ''{title}'' from the published site'
REMOVEDPAGEFROMDRAFT: 'Removed ''%s'' from the draft site' REMOVEDPAGEFROMDRAFT: 'Removed ''%s'' from the draft site'
RESTORE: Restore RESTORE: 'Restore draft'
RESTORE_TO_ROOT: 'Restore draft at top level'
RESTORE_TO_ROOT_DESC: 'Restore the archived version to draft as a top level page'
RESTORED: 'Restored ''{title}'' successfully' RESTORED: 'Restored ''{title}'' successfully'
RESTORE_DESC: 'Restore the archived version to draft'
ROLLBACK: 'Roll back to this version' ROLLBACK: 'Roll back to this version'
ROLLEDBACKPUBv2: 'Rolled back to published version.' ROLLEDBACKPUBv2: 'Rolled back to published version.'
ROLLEDBACKVERSIONv2: 'Rolled back to version #%d.' ROLLEDBACKVERSIONv2: 'Rolled back to version #%d.'
@ -152,11 +155,19 @@ en:
FILTERDATETO: To FILTERDATETO: To
FILTERLABELTEXT: Content FILTERLABELTEXT: Content
CMSSiteTreeFilter_ChangedPages: CMSSiteTreeFilter_ChangedPages:
Title: 'Changed pages' Title: 'Modified pages'
CMSSiteTreeFilter_DeletedPages: CMSSiteTreeFilter_DeletedPages:
Title: 'All pages, including deleted' Title: 'All pages, including archived'
CMSSIteTreeFilter_PublishedPages:
Title: 'Published pages'
CMSSiteTreeFilter_Search: CMSSiteTreeFilter_Search:
Title: 'All pages' Title: 'All pages'
CMSSiteTreeFilter_StatusDeletedPages:
Title: 'Archived pages'
CMSSiteTreeFilter_StatusDraftPages:
Title: 'Draft pages'
CMSSiteTreeFilter_StatusRemovedFromDraftPages:
Title: 'Live but removed from draft'
ContentControl: ContentControl:
NOTEWONTBESHOWN: 'Note: this message will not be shown to your visitors' NOTEWONTBESHOWN: 'Note: this message will not be shown to your visitors'
ContentController: ContentController:

View File

@ -215,7 +215,7 @@ eo:
DEFAULTSERVERERRORPAGETITLE: 'Servila eraro' DEFAULTSERVERERRORPAGETITLE: 'Servila eraro'
DESCRIPTION: 'Propra enhavo por diversaj kazoj de eraro (ekzemple, "Ne trovis paĝon")' DESCRIPTION: 'Propra enhavo por diversaj kazoj de eraro (ekzemple, "Ne trovis paĝon")'
ERRORFILEPROBLEM: 'Eraro okazis malfermante dosieron "{filename}" por skribi. Bonvolu kontroli permesojn.' ERRORFILEPROBLEM: 'Eraro okazis malfermante dosieron "{filename}" por skribi. Bonvolu kontroli permesojn.'
PLURALNAME: 'Prieraraj paĝoj' PLURALNAME: 'Paĝoj pri eraroj'
SINGULARNAME: 'Prierara paĝo' SINGULARNAME: 'Prierara paĝo'
Folder: Folder:
AddFolderButton: 'Aldoni dosierujon' AddFolderButton: 'Aldoni dosierujon'
@ -238,7 +238,7 @@ eo:
HASBEENSETUP: 'Alidirekta paĝo estis agordita sen ie al kie alidirekti.' HASBEENSETUP: 'Alidirekta paĝo estis agordita sen ie al kie alidirekti.'
HEADER: 'Ĉi tiu paĝo redirektos uzantojn al alia paĝo' HEADER: 'Ĉi tiu paĝo redirektos uzantojn al alia paĝo'
OTHERURL: 'URL de alia retejo' OTHERURL: 'URL de alia retejo'
PLURALNAME: 'Alidirektaj paĝoj' PLURALNAME: 'Paĝoj pri alidirekto'
REDIRECTTO: 'Alidirekti al' REDIRECTTO: 'Alidirekti al'
REDIRECTTOEXTERNAL: 'Alia retejo' REDIRECTTOEXTERNAL: 'Alia retejo'
REDIRECTTOPAGE: 'Paĝo en via retejo' REDIRECTTOPAGE: 'Paĝo en via retejo'

View File

@ -1,14 +1,40 @@
fa_IR: fa_IR:
AssetAdmin: AssetAdmin:
ADDFILES: 'اضافه کردن فایل'
ActionAdd: 'اضافه کردن پوشه'
AppCategoryAudio: صوتی
AppCategoryDocument: اسناد
AppCategoryImage: تصویر
AppCategoryVideo: ویدئو
BackToFolder: 'بازگشت به پوشه'
CREATED: تاریخ
DetailsView: جزییات
FILES: فایل ها
FROMTHEINTERNET: 'از اینترنت'
FROMYOURCOMPUTER: 'از کامپیوتر شما'
Filetype: 'نوع فایل'
NEWFOLDER: پوشه جديد NEWFOLDER: پوشه جديد
SIZE: حجم
TreeView: 'مشاهده درخت'
Upload: آپلود
AssetAdmin_DeleteBatchAction:
TITLE: 'حذف پوشه ها'
AssetAdmin_left_ss:
GO: برو
BrokenLinksReport: BrokenLinksReport:
ColumnURL: آدرس ColumnURL: آدرس
HoverTitleEditPage: 'ویرایش صفحه' HoverTitleEditPage: 'ویرایش صفحه'
PageName: 'نام صفحه' PageName: 'نام صفحه'
ReasonDropdown: 'مشکل در بررسی' ReasonDropdown: 'مشکل در بررسی'
CMSAddPageController:
Title: 'اضافه کردن صفحه'
CMSBatchActions: CMSBatchActions:
DELETE_DRAFT_PAGES: 'حذف از پیش نویس سایت'
PUBLISH_PAGES: انتشار PUBLISH_PAGES: انتشار
CMSMain: CMSMain:
AddNewButton: 'جدید'
Cancel: لغو
Create: ایجاد
DELETE: 'حذف کردن از پیشنویس سایت' DELETE: 'حذف کردن از پیشنویس سایت'
DELETEFP: حذف کردن از منتشر شده های سایت DELETEFP: حذف کردن از منتشر شده های سایت
EMAIL: پست الکترونیک EMAIL: پست الکترونیک
@ -17,6 +43,10 @@ fa_IR:
SAVE: ذخیره SAVE: ذخیره
CMSPageHistoryController: CMSPageHistoryController:
SHOWVERSION: 'نمایش ویرایش' SHOWVERSION: 'نمایش ویرایش'
CMSPageHistoryController_versions_ss:
AUTHOR: ناشر
CMSPagesController:
MENUTITLE: صفحات
ContentController: ContentController:
DRAFTSITE: 'تارگاه چرکنویس' DRAFTSITE: 'تارگاه چرکنویس'
ErrorPage: ErrorPage:

View File

@ -1,32 +1,39 @@
id: id:
AssetAdmin: AssetAdmin:
ADDFILES: 'Tambah Berkas' ADDFILES: 'Tambah Berkas'
ActionAdd: 'Tambah map' ActionAdd: 'Tambah Folder'
AppCategoryArchive: Arsip AppCategoryArchive: Arsip
AppCategoryAudio: Suara AppCategoryAudio: Audio
AppCategoryDocument: Dokumen AppCategoryDocument: Dokumen
AppCategoryFlash: Flash AppCategoryFlash: Flash
AppCategoryImage: Gambar AppCategoryImage: Gambar
AppCategoryVideo: Video AppCategoryVideo: Video
BackToFolder: 'Kembali ke map' BackToFolder: 'Kembali ke Folder'
CREATED: Tanggal CREATED: Tanggal
CurrentFolderOnly: 'Batas pada map sekarang?' CurrentFolderOnly: 'Batasi sesuai folder ini?'
DetailsView: Perincian DetailsView: Perincian
FILES: Berkas-berkas FILES: Berkas
FILESYSTEMSYNC: 'Selaraskan berkas'
FILESYSTEMSYNCTITLE: 'Perbarui entri database CMS untuk berkas-berkas pada sistem berkas (filesystem). Berguna saat berkas-berkas baru diunggah di luar sistem CMS, misalnya melalui FTP.'
FROMTHEINTERNET: 'Dari internet' FROMTHEINTERNET: 'Dari internet'
FROMYOURCOMPUTER: 'Dari komputer kamu' FROMYOURCOMPUTER: 'Dari komputer Anda'
Filetype: 'Jenis berkas' Filetype: 'Jenis berkas'
ListView: 'Tampilan daftar' ListView: 'Tampilan Daftar'
NEWFOLDER: Map baru NEWFOLDER: FolderBaru
SIZE: Ukuran SIZE: Ukuran
TreeView: 'Tampilan menurun' THUMBSDELETED: '{count} thumbnail tak terpakai telah dihapus'
MENUTITLE: Berkas-berkas TreeView: 'Tampilan Struktur'
Upload: Unggah
MENUTITLE: Berkas
AssetAdmin_DeleteBatchAction: AssetAdmin_DeleteBatchAction:
TITLE: 'Hapus map' TITLE: 'Hapus Folder'
AssetAdmin_Tools: AssetAdmin_Tools:
FILTER: Saring FILTER: Saring
AssetAdmin_left_ss: AssetAdmin_left_ss:
GO: Lanjut GO: Lanjut
AssetTableField:
BACKLINKCOUNT: 'Dipakai pada:'
PAGES: laman
BackLink_Button_ss: BackLink_Button_ss:
Back: Kembali Back: Kembali
BrokenLinksReport: BrokenLinksReport:
@ -34,83 +41,147 @@ id:
BROKENLINKS: 'Laporan tautan rusak' BROKENLINKS: 'Laporan tautan rusak'
CheckSite: 'Cek situs' CheckSite: 'Cek situs'
CheckSiteDropdownDraft: 'Draf situs' CheckSiteDropdownDraft: 'Draf situs'
CheckSiteDropdownPublished: 'Publikasi situs' CheckSiteDropdownPublished: 'Situs Terbit'
ColumnDateLastModified: 'Tanggal terakhir modifikasi' ColumnDateLastModified: 'Tanggal terakhir modifikasi'
ColumnDateLastPublished: 'Tanggal terakhir terbit' ColumnDateLastPublished: 'Tanggal terbit terakhir'
ColumnProblemType: 'Jenis masalah' ColumnProblemType: 'Jenis masalah'
ColumnURL: URL ColumnURL: URL
HoverTitleEditPage: 'Edit halaman ' HasBrokenFile: 'terdapat berkas yang rusak'
PageName: 'Nama halaman' HasBrokenLink: 'terdapat tautan yang rusak'
HasBrokenLinkAndFile: 'terdapat berkas dan tautan yang rusak'
HoverTitleEditPage: 'Edit laman '
PageName: 'Nama laman'
ReasonDropdown: 'Masalah yang diperiksa' ReasonDropdown: 'Masalah yang diperiksa'
ReasonDropdownBROKENFILE: 'Berkas rusak' ReasonDropdownBROKENFILE: 'Berkas rusak'
ReasonDropdownBROKENLINK: 'Tautan rusak' ReasonDropdownBROKENLINK: 'Tautan rusak'
ReasonDropdownRPBROKENLINK: 'Laman pengarah mengarah ke laman yang tidak ada'
ReasonDropdownVPBROKENLINK: 'Laman virtual mengarah ke laman yang tidak ada'
RedirectorNonExistent: 'laman pengarah mengarah ke laman yang tidak ada'
VirtualPageNonExistent: 'laman virtual mengarah ke laman yang tidak ada'
CMSAddPageController: CMSAddPageController:
Title: 'Tambah halaman' Title: 'Tambah laman'
CMSBatchActions: CMSBatchActions:
DELETED_DRAFT_PAGES: '%d laman berhasil dihapus dari draft, %d laman gagal dihapus'
DELETED_PAGES: '%d laman berhasil dihapus dari situs terbit, %d gagal'
DELETE_DRAFT_PAGES: 'Hapus dari draf situs' DELETE_DRAFT_PAGES: 'Hapus dari draf situs'
DELETE_PAGES: 'Hapus dari publikasi situs' DELETE_PAGES: 'Hapus dari situs terbit'
PUBLISHED_PAGES: 'Menerbitkan %d halaman, %d gagal' PUBLISHED_PAGES: 'Menerbitkan %d laman, %d gagal'
PUBLISH_PAGES: Terbitkan PUBLISH_PAGES: Terbitkan
UNPUBLISH_PAGES: Tidak terbit UNPUBLISHED_PAGES: '%d laman dibatalterbitkan'
UNPUBLISH_PAGES: Batalterbitkan
CMSMain: CMSMain:
AddNew: 'Tambah halaman baru' ACCESS: 'Akses ke bagian ''{title}'''
ACCESS_HELP: 'Perbolehkan menampilkan bagian dengan struktur laman dan konten. Perijinan menampilkan dan mengedit dapat diatur melalui pilihan terkait laman, sebagaimana "Perijinan Konten".'
AddNew: 'Tambah laman baru'
AddNewButton: 'Tambah baru' AddNewButton: 'Tambah baru'
AddPageRestriction: 'Catatan: Sebagian jenis laman tidak dapat dipilih'
Cancel: Batal Cancel: Batal
ChoosePageType: 'Pilih jenis halaman' ChoosePageParentMode: 'Pilih lokasi untuk laman ini'
ChoosePageType: 'Pilih jenis laman'
Create: Buat Create: Buat
DELETE: 'Hapus dari situs draft' DELETE: 'Hapus dari situs draft'
DELETEFP: Hapus dari situs yang telah diterbitkan DELETEFP: Hapus dari situs yang telah diterbitkan
DESCREMOVED: 'dan {count} turunannya'
DUPLICATED: '''{title}'' berhasil diduplikasi'
DUPLICATEDWITHCHILDREN: '''{title}'' dan turunannya berhasil diduplikasi'
EMAIL: Surel EMAIL: Surel
NEWPAGE: 'Baru {pagetype}' EditTree: 'Edit Struktur'
PAGENOTEXISTS: 'Halaman tidak ada' ListFiltered: 'Daftar tersaring'
PAGES: Halaman NEWPAGE: '{pagetype} baru'
PAGENOTEXISTS: 'Laman ini tidak ada'
PAGES: Laman
PAGETYPEANYOPT: Lain PAGETYPEANYOPT: Lain
PAGETYPEOPT: 'Jenis Halaman' PAGETYPEOPT: 'Jenis Laman'
PUBPAGES: 'Selesai: Diterbitkan {count} halaman' PUBALLCONFIRM: 'Mohon terbitkan semua laman pada situs'
PageAdded: 'Berhasil buat halaman' PUBALLFUN: 'Fungsi "Terbitkan Semua"'
PUBALLFUN2: "Menekan tombol ini sama dengan membuka semua laman dan menerbitkannya. Hal ini \n\t\t\t\tditujukan untuk dilakukan setelah ada banyak pengeditan konten, misalnya saat situs pertama kali \n\t\t\t\tdiinstal."
PUBPAGES: 'Selesai: {count} laman terbit'
PageAdded: 'Laman berhasil dibuat'
REMOVED: 'Dihapus ''{title}''{description} dari situs langsung' REMOVED: 'Dihapus ''{title}''{description} dari situs langsung'
REMOVEDPAGE: 'Menghapus ''{title}'' dari terbitan situs ' REMOVEDPAGE: 'Menghapus ''{title}'' dari situs terbit'
REMOVEDPAGEFROMDRAFT: 'Menghapus ''%s'' dari draf situs' REMOVEDPAGEFROMDRAFT: 'Menghapus ''%s'' dari draf situs'
RESTORE: Pulihkan RESTORE: Pulihkan
RESTORED: 'Pemulihan ''{title}'' sukses' RESTORED: 'Pemulihan ''{title}'' sukses'
ROLLBACK: 'Kembali ke versi ini' ROLLBACK: 'Kembali ke versi ini'
ROLLEDBACKPUBv2: 'Kembali ke versi terbit'
ROLLEDBACKVERSIONv2: 'Kembali ke versi #%d.'
SAVE: Simpan SAVE: Simpan
SAVEDRAFT: 'Simpan draf' SAVEDRAFT: 'Simpan draf'
TabContent: Konten TabContent: Konten
TabHistory: Sejarah TabHistory: Sejarah
TabSettings: Pengaturan TabSettings: Pengaturan
TreeFiltered: 'Struktur tersaring.'
TreeFilteredClear: 'Bersihkan saring' TreeFilteredClear: 'Bersihkan saring'
MENUTITLE: 'Edit Laman'
CMSMain_left_ss:
APPLY_FILTER: 'Terapkan Saring'
RESET: Reset
CMSPageAddController:
ParentMode_child: 'Di bawah laman lain'
ParentMode_top: 'Laman atas'
MENUTITLE: 'Tambah laman'
CMSPageHistoryController:
COMPAREMODE: 'Modus pembanding (pilih dua)'
COMPAREVERSIONS: 'Bandingkan Versi'
COMPARINGVERSION: 'Bandingkan versi {version1} dan {version2}.'
REVERTTOTHISVERSION: 'Kembali ke versi ini'
SHOWUNPUBLISHED: 'Tampilkan versi batalterbit'
SHOWVERSION: 'Tampilkan Versi'
VIEW: tampilkan
VIEWINGLATEST: 'Versi terakhir sedang ditampilkan.'
VIEWINGVERSION: 'Versi {version} sedang ditampilkan.'
MENUTITLE: Sejarah
CMSPageHistoryController_versions_ss: CMSPageHistoryController_versions_ss:
AUTHOR: Penulis
NOTPUBLISHED: 'Batal terbit'
PUBLISHER: Penerbit PUBLISHER: Penerbit
UNKNOWN: Tidak diketahui UNKNOWN: Tidak diketahui
WHEN: Ketika WHEN: Ketika
CMSPagesController: CMSPagesController:
GalleryView: 'Tampilan Galeri' GalleryView: 'Tampilan Galeri'
ListView: 'Tampilan daftar' ListView: 'Tampilan daftar'
MENUTITLE: Halaman MENUTITLE: Laman
TreeView: 'Tampilan Struktur'
CMSPagesController_ContentToolbar_ss:
MULTISELECT: Multi-pilihan
CMSPagesController_Tools_ss: CMSPagesController_Tools_ss:
FILTER: Saring FILTER: Saring
CMSSearch: CMSSearch:
FILTERDATEFROM: Dari FILTERDATEFROM: Dari
FILTERDATEHEADING: Tanggal FILTERDATEHEADING: Tanggal
FILTERDATETO: Ke FILTERDATETO: Ke
FILTERLABELTEXT: Isi FILTERLABELTEXT: Konten
CMSSiteTreeFilter_ChangedPages: CMSSiteTreeFilter_ChangedPages:
Title: 'Perubahan halaman' Title: 'Laman yang berubah'
CMSSiteTreeFilter_DeletedPages:
Title: 'Semua laman, termasuk yang terhapus'
CMSSiteTreeFilter_Search: CMSSiteTreeFilter_Search:
Title: 'Semua halaman' Title: 'Semua laman'
ContentControl:
NOTEWONTBESHOWN: 'Catatan: pesan ini tidak akan ditampilkan kepada pengunjung'
ContentController: ContentController:
ARCHIVEDSITE: 'Versi pratinjau'
ARCHIVEDSITEFROM: 'Arsip dari'
CMS: CMS CMS: CMS
DRAFT: Draf DRAFT: Draf
DRAFTSITE: 'Draf situs' DRAFTSITE: 'Draf situs'
DRAFT_SITE_ACCESS_RESTRICTION: 'Anda harus login untuk menampilkan konten draft atau arsip. <a href="%s">Klik di sini untuk kembali ke situs terbit.</a>'
Email: Surel Email: Surel
INSTALL_SUCCESS: 'Pemasangan sukses' INSTALL_SUCCESS: 'Penginstalan berhasil!'
InstallFilesDeleted: 'Berkas pemasangan telah berhasil dihapus' InstallFilesDeleted: 'Berkas penginstalan telah berhasil dihapus.'
InstallSuccessCongratulations: 'SilverStripe telah sukses dipasang!' InstallSecurityWarning: 'Untuk alasan keamanan, sekarang Anda perlu menghapus berkas-berkas penginstalan, kecuali Anda berencana untuk melakukan penginstalan ulang lagi (<em>memerlukan login admin, lihat di atas</em>). Selanjutnya server juga memerlukan akses tulis ke folder "assets", Anda dapat mencabut akses tulis dari folder lainnya. <a href="{link}" style="text-align: center;">Klik di sini untuk menghapus berkas-berkas penginstalan.</a>'
InstallSuccessCongratulations: 'SilverStripe telah berhasil diinstal!'
LOGGEDINAS: 'Masuk sebagai' LOGGEDINAS: 'Masuk sebagai'
LOGIN: Masuk LOGIN: Masuk
LOGOUT: 'Keluar' LOGOUT: 'Keluar'
NOTLOGGEDIN: 'Tidak masuk' NOTLOGGEDIN: 'Tidak masuk'
PUBLISHED: Terbit
PUBLISHEDSITE: 'Situs Terbit'
Password: Kata kunci
PostInstallTutorialIntro: 'Situs ini adalah versi sederhana dari situs SilverStripe 3. Untuk meningkatkannya, mohon kunjungi {link}.'
StartEditing: 'Anda dapat mulai mengedit konten dengan membuka <a href="{link}">CMS</a>.'
UnableDeleteInstall: 'Gagal menghapus berkas-berkas penginstalan. Mohon lakukan penghapusan berkas-berkas berikut ini secara manual.'
VIEWPAGEIN: 'Tampikan Laman pada:'
ErrorPage: ErrorPage:
400: '400 - Permintaan buruk' 400: '400 - Permintaan buruk'
401: '401 - Tidak ada otorisasi' 401: '401 - Tidak ada otorisasi'
@ -129,103 +200,215 @@ id:
415: '425 - Tipe Media Tidak Disupport' 415: '425 - Tipe Media Tidak Disupport'
416: '416 - Lingkup Permintaan Tidak Dapat Dipuaskan' 416: '416 - Lingkup Permintaan Tidak Dapat Dipuaskan'
417: '417 - Expectation Gagal' 417: '417 - Expectation Gagal'
422: '422 - Entitas Tidak Terproses'
429: '429 - Terlalu Banyak Permintaan'
500: '500 - Server Internal Error' 500: '500 - Server Internal Error'
501: '501 - Tidak Diimplementasi' 501: '501 - Tidak Diimplementasi'
502: '502 - Gateway Buruk' 502: '502 - Gateway Buruk'
503: '503 - Servis Tidak Tersedia' 503: '503 - Servis Tidak Tersedia'
505: '505 - Versi HTTP Tidak Disupport' 504: '504 - Masa Tunggu Terlewat'
505: '505 - Versi HTTP Tidak Didukung'
CODE: 'Kode yang salah' CODE: 'Kode yang salah'
DEFAULTERRORPAGECONTENT: '<p>Maaf, sepertinya anda berusha mengakses halaman yang tidak ada.</p><p>Mohon periksa ejaan URL yang ingin anda akses dan coba lagi.</p>' DEFAULTERRORPAGECONTENT: '<p>Maaf, sepertinya anda berusaha mengakses laman yang tidak ada.</p><p>Mohon periksa ejaan URL yang ingin anda akses dan coba lagi.</p>'
DEFAULTERRORPAGETITLE: 'Halaman tidak ditemukan' DEFAULTERRORPAGETITLE: 'Laman tidak ditemukan'
DEFAULTSERVERERRORPAGECONTENT: '<p>Maaf, ada masalah dalam penanganan permintaan Anda.</p>'
DEFAULTSERVERERRORPAGETITLE: 'Server mengalami kesalahan' DEFAULTSERVERERRORPAGETITLE: 'Server mengalami kesalahan'
DESCRIPTION: 'Laman untuk berbagai kasus kesalahan (misalnya "Laman tidak ditemukan")'
ERRORFILEPROBLEM: 'Gagal membuka berkas "{filename}" untuk penulisan data. Mohon periksa pengaturan akses tulis.'
PLURALNAME: 'Laman Kesalahan'
SINGULARNAME: 'Laman Kesalahan'
Folder: Folder:
AddFolderButton: 'Tambah map' AddFolderButton: 'Tambah map'
DELETEUNUSEDTHUMBNAILS: 'Hapus thumbnail-thumbnail yang tidal dipakai' DELETEUNUSEDTHUMBNAILS: 'Hapus thumbnail-thumbnail yang tidal dipakai'
UNUSEDFILESTITLE: 'File-file yang tidak dipakai' UNUSEDFILESTITLE: 'Berkas tidak terpakai'
UNUSEDTHUMBNAILSTITLE: 'Thumbnail-thumbnail yang tidak dipakai' UNUSEDTHUMBNAILSTITLE: 'Thumbnail-thumbnail yang tidak dipakai'
UploadFilesButton: Unggah
LeftAndMain: LeftAndMain:
DELETED: Hapus DELETED: Hapus
PreviewButton: Pratinjau
SAVEDUP: Disimpan SAVEDUP: Disimpan
SearchResults: 'Hasil pencarian' SearchResults: 'Hasil pencarian'
Permission:
CMS_ACCESS_CATEGORY: 'Akses CMS'
Permissions:
CONTENT_CATEGORY: 'Perijinan konten'
PERMISSIONS_CATEGORY: 'Peran dan akses perijinan'
RedirectorPage: RedirectorPage:
HASBEENSETUP: 'halaman yang mengirim user ke alamat lain dibuat tanpa tujuan dari pengiriman itu sendiri' DESCRIPTION: 'Mengarahkan ke laman internal berbeda'
HEADER: 'Halaman ini akan mengarahkan user ke halaman lain' HASBEENSETUP: 'Sebuah laman pengarah telah dibuat tanpa arah yang dituju.'
HEADER: 'Laman ini akan mengarahkan pengguna ke laman lain'
OTHERURL: 'URL situs web lain' OTHERURL: 'URL situs web lain'
PLURALNAME: 'Laman Pengarah'
REDIRECTTO: 'Arahkan lagi ke' REDIRECTTO: 'Arahkan lagi ke'
REDIRECTTOEXTERNAL: 'Situs web yang lain' REDIRECTTOEXTERNAL: 'Situs web yang lain'
REDIRECTTOPAGE: 'Sebuah halaman pada situs web Anda' REDIRECTTOPAGE: 'Laman pada situs Anda'
YOURPAGE: 'Halaman pada situs web Anda' SINGULARNAME: 'Laman Pengarah'
YOURPAGE: 'Laman pada situs Anda'
ReportAdmin: ReportAdmin:
ReportTitle: Judul ReportTitle: Judul
MENUTITLE: Laporan MENUTITLE: Laporan
ReportAdminForm: ReportAdminForm:
FILTERBY: 'Saring dengan' FILTERBY: 'Saring dengan'
SITETREE:
VIRTUALPAGEDRAFTWARNING: 'Mohon terbitkan laman tertaut untuk menayangkan laman virtual'
VIRTUALPAGEWARNING: 'Mohon pilih laman tertaut dan simpan lebih dulu untuk menayangkan laman ini'
VIRTUALPAGEWARNINGSETTINGS: 'Mohon pilih laman tertaut pada konten utama untuk menayangkan'
SearchForm: SearchForm:
GO: Pergi GO: Pergi
SEARCH: Cari SEARCH: Cari
SearchResults: 'Hasil Pencarian' SearchResults: 'Hasil Pencarian'
SideReport: SideReport:
ContentGroupTitle: 'Isi laporan' BROKENFILES: 'Laman dengan berkas rusak'
BROKENLINKS: 'Laman dengan tautan rusak'
BROKENREDIRECTORPAGES: 'LamanPengarah mengarah pada laman terhapus'
BROKENVIRTUALPAGES: 'LamanVirtual mengarah pada laman terhapus'
BrokenLinksGroupTitle: 'Laporan tautan-tautan rusak'
ContentGroupTitle: 'Laporan konten'
EMPTYPAGES: 'Laman tanpa konten'
LAST2WEEKS: 'Laman diedit dalam 2 minggu terakhir'
OtherGroupTitle: Lainnya
ParameterLiveCheckbox: 'Periksa situs live'
REPEMPTY: 'Laporan {title} kosong.'
SilverStripeNavigator:
ARCHIVED: Terarsip
SilverStripeNavigatorLink:
ShareInstructions: 'Untuk membagi laman ini, salin dan tempel pada tautan berikut.'
ShareLink: 'Bagi tautan'
SilverStripeNavigatorLinkl: SilverStripeNavigatorLinkl:
CloseLink: Tutup CloseLink: Tutup
SiteConfig: SiteConfig:
DEFAULTTHEME: '(Gunakan tema standar)'
EDITHEADER: 'Siapa yang boleh mengedit laman pada situs ini?'
EDIT_PERMISSION: 'Mengelola pengaturan situs'
EDIT_PERMISSION_HELP: 'Bolehkan mengedit pengaturan akses umum atau perijinan tingkat atas.'
PLURALNAME: 'Pengaturan Situs'
SINGULARNAME: 'Pengaturan Situs'
SITENAMEDEFAULT: 'Nama Situs' SITENAMEDEFAULT: 'Nama Situs'
SITETAGLINE: 'Slogan Situs' SITETAGLINE: 'Slogan Situs'
SITETITLE: 'Judul Situs' SITETITLE: 'Judul Situs'
TABACCESS: Akses
TABMAIN: Tab Utama TABMAIN: Tab Utama
TAGLINEDEFAULT: 'slogan situsmu disini' TAGLINEDEFAULT: 'slogan situsmu disini'
THEME: Tema THEME: Tema
TOPLEVELCREATE: 'Siapa yang dapat membuat laman utama yang baru?'
VIEWHEADER: 'Siapa yang dapat menampilkan laman pada situs ini?'
SiteTree: SiteTree:
ACCESSANYONE: Siapa saja ACCESSANYONE: Siapa saja
ACCESSHEADER: 'Siapa yang dapat melihat halaman ini pada situs saya?' ACCESSHEADER: 'Siapa yang dapat melihat laman ini?'
ACCESSLOGGEDIN: 'User yang masuk' ACCESSLOGGEDIN: 'User yang masuk'
ACCESSONLYTHESE: 'Hanya orang ini saja (pilih dari daftar)' ACCESSONLYTHESE: 'Hanya orang ini saja (pilih dari daftar)'
ALLOWCOMMENTS: 'Bolehkan komentar pada halaman ini?' ADDEDTODRAFTHELP: 'Laman belum terbit'
ADDEDTODRAFTSHORT: Darft
ALLOWCOMMENTS: 'Bolehkan komentar pada laman ini?'
APPEARSVIRTUALPAGES: 'Konten ini akan tampil juga pada laman virtual di bagian {title}.'
BUTTONCANCELDRAFT: 'Batalkan perubahan draft' BUTTONCANCELDRAFT: 'Batalkan perubahan draft'
BUTTONCANCELDRAFTDESC: 'Hapus draft dan kembalikan ke halaman yang sedang dipublikasikan' BUTTONCANCELDRAFTDESC: 'Hapus draft dan kembalikan ke laman terbit'
BUTTONUNPUBLISH: Tidak Dipublikasi BUTTONPUBLISHED: Terbit
BUTTONUNPUBLISHDESC: 'Pindahkan halaman ini dari situs yang dipublikasikan' BUTTONSAVED: Tersimpan
BUTTONSAVEPUBLISH: 'Simpan dan terbitkan'
BUTTONUNPUBLISH: Batalterbitkan
BUTTONUNPUBLISHDESC: 'Hapus laman ini dari situs terbit'
Comments: Komen-komen Comments: Komen-komen
Content: Isi Content: Konten
DEFAULTABOUTCONTENT: '<p>Anda dapat mengisi halaman ini dengan isi anda sendiri, atau hapus dan buat halaman-halaman anda sendiri.<br /></p>' DEFAULTABOUTCONTENT: '<p>Anda dapat mengisi laman ini dengan konten Anda sendiri, atau menghapusnya dan membuat laman Anda sendiri.<br /></p>'
DEFAULTABOUTTITLE: 'Tentang Kami' DEFAULTABOUTTITLE: 'Tentang Kami'
DEFAULTCONTACTCONTENT: '<p>Anda dapat mengisi halaman ini dengan isi anda sendiri, atau hapus dan buat halaman-halaman anda sendiri.<br /></p>' DEFAULTCONTACTCONTENT: '<p>Anda dapat mengisi laman ini dengan konten Anda sendiri, atau menghapusnya dan membuat laman Anda sendiri.<br /></p>'
DEFAULTCONTACTTITLE: 'Hubungi Kami' DEFAULTCONTACTTITLE: 'Hubungi Kami'
DEFAULTHOMECONTENT: '<p>Selamat datang di SilverStripe! Ini adalah homepage default anda. Anda dapat mengedit halaman ini dengan membuka <a href="admin/">CMS ini</a>. Sekarang anda dapat mengakses <a href="http://doc.silverstripe.com">dokumentasi developer</a>, atau memulai <a href="http://doc.silverstripe.com/doku.php?id=tutorials">tutorial-tutorial yang tersedia.</a></p>' DEFAULTHOMECONTENT: '<p>Selamat datang di SilverStripe! Ini adalah halaman awal situs Anda. Anda dapat mengedit laman ini dengan membuka <a href="admin/">CMS ini</a>. Selanjutnya Anda dapat mengakses <a href="http://doc.silverstripe.com">dokumentasi pengembang</a>, atau mulai dengan <a href="http://doc.silverstripe.com/doku.php?id=tutorials">tutorial-tutorial yang tersedia.</a></p>'
DEFAULTHOMETITLE: Beranda
DELETEDPAGEHELP: 'Laman tidak lagi terbit'
DELETEDPAGESHORT: Hapus DELETEDPAGESHORT: Hapus
DEPENDENT_NOTE: 'Laman-laman berikut ini bergantung pada laman ini. Termasuk laman virtual, laman pengarah, dan laman-laman dengan tautan konten.'
DESCRIPTION: 'Laman konten umum'
DependtPageColumnLinkType: 'Jenis tautan'
DependtPageColumnURL: URL
EDITANYONE: 'Siapa saja yang dapat masuk ke dalam CMS' EDITANYONE: 'Siapa saja yang dapat masuk ke dalam CMS'
EDITHEADER: 'Siapa yang dapat mengedit ini dari dalam CMS?' EDITHEADER: 'Siapa yang dapat mengedit laman ini?'
EDITONLYTHESE: 'Hanya orang ini saja (pilih dari daftar)' EDITONLYTHESE: 'Hanya orang ini saja (pilih dari daftar)'
EDITORGROUPS: 'Editor Grup' EDITORGROUPS: 'Kelompok Editor'
HASBROKENLINKS: 'Halaman ini mempunyai link yang rusak' EDIT_ALL_DESCRIPTION: 'Edit semua laman'
EDIT_ALL_HELP: 'Bolehkan mengedit semua laman pada situs, terlepas dari pengaturan Akses. Memerlukan perijinan "Akses ke bagian ''Laman''".'
Editors: 'Kelompok Editor'
GroupPlaceholder: 'Klik untuk memilih kelompok'
HASBROKENLINKS: 'Laman ini mempunyai tautan yang rusak.'
HTMLEDITORTITLE: Konten HTMLEDITORTITLE: Konten
INHERIT: 'Warisi dari laman induk'
LASTPUBLISHED: 'Terakhir terbit'
LASTSAVED: 'Terakhir tersimpan'
LASTUPDATED: 'Terakhir diperbarui'
LINKCHANGENOTE: 'Mengganti tautan laman ini akan mempengaruhi semua tautan pada laman terkait.'
MENUTITLE: 'Label navigasi' MENUTITLE: 'Label navigasi'
METADESC: 'Deskripsi' METADESC: 'Deskripsi'
METADESCHELP: 'Mesin pencari menggunakan konten ini untuk menampilkan hasil pencarian (meskipun tidak mempengaruhi pemeringkatan situs).'
METAEXTRA: 'Penanda Meta'
METAEXTRAHELP: 'Penanda HTML untuk informasi meta tambahan. Contohnya &lt;meta name="namaMeta" content="konten Anda di sini" /&gt;'
MODIFIEDONDRAFTHELP: 'Laman memiliki perubahan yang tidak terbit'
MODIFIEDONDRAFTSHORT: Diubah
MetadataToggle: Metadata MetadataToggle: Metadata
MoreOptions: 'Pilihan lain' MoreOptions: 'Pilihan lain'
PAGELOCATION: 'Lokasi halaman' NOTPUBLISHED: 'Tidak diterbitkan'
PAGETITLE: 'Nama Halaman' OBSOLETECLASS: 'Jenis laman {type} ini sudah usang. Menyimpannya akan memperbarui jenisnya dan Anda kemungkinan akan kehilangan data'
PAGETYPE: 'Tipe halaman' PAGELOCATION: 'Lokasi laman'
PARENTTYPE: 'Lokasi halaman' PAGETITLE: 'Nama laman'
PAGETYPE: 'Tipe laman'
PARENTID: 'Laman induk'
PARENTTYPE: 'Lokasi laman'
PARENTTYPE_ROOT: 'Laman atas'
PARENTTYPE_SUBPAGE: 'Sub-laman di bawah laman induk'
PERMISSION_GRANTACCESS_DESCRIPTION: 'Kelola hak akses untuk konten ini'
PERMISSION_GRANTACCESS_HELP: 'Perbolehkan pengaturan pencegahan akses ke laman tertentu di bagian "Laman".'
PLURALNAME: Laman
PageTypNotAllowedOnRoot: 'Jenis laman {type} tidak diperbolehkan di tingkat atas'
PageTypeNotAllowed: 'Jenis laman {type} tidak diperbolehkan menjadi turunan dari laman induk ini'
REMOVEDFROMDRAFTHELP: 'Laman sudah terbit, tapi telah dihapus dari draft'
REMOVEDFROMDRAFTSHORT: 'Dihapus dari draft'
REMOVE_INSTALL_WARNING: 'Peringatan: Anda perlu menghapus instal.php dari penginstalan SilverStripe ini untuk alasan keamanan.'
REORGANISE_DESCRIPTION: 'Ubah stuktur situs' REORGANISE_DESCRIPTION: 'Ubah stuktur situs'
REORGANISE_HELP: 'Atur ulang laman pada struktur situs dengan drag&drop.'
SHOWINMENUS: 'Perlihatkan dalam menu?' SHOWINMENUS: 'Perlihatkan dalam menu?'
SHOWINSEARCH: 'Perlihatkan dalam pencarian' SHOWINSEARCH: 'Perlihatkan dalam pencarian'
SINGULARNAME: Laman
TABBEHAVIOUR: Perilaku TABBEHAVIOUR: Perilaku
TABCONTENT: 'Kontain' TABCONTENT: 'Konten Utama'
TOPLEVEL: 'Konten Situs (Level Atas)' TABDEPENDENT: 'Laman terkait'
TOPLEVEL: 'Konten Situs (Tingkat Atas)'
TOPLEVELCREATORGROUPS: 'Pembuat tingkat atas'
URLSegment: 'Segmen URL' URLSegment: 'Segmen URL'
has_one_Parent: 'Halaman Induk' VIEWERGROUPS: 'Kelompok Penampil'
VIEW_ALL_DESCRIPTION: 'Tampilkan semua laman'
VIEW_ALL_HELP: 'Bolehkan menampilkan semua laman pada situs, terlepas dari pengaturan Akses. Memerlukan perijinan "Akses ke bagian ''Laman''".'
VIEW_DRAFT_CONTENT: 'Tampilkan konten draft'
VIEW_DRAFT_CONTENT_HELP: 'Untuk menampilkan laman di luar CMS pada modus draft. Berguna untuk kolaborasi eksternal tanpa akses CMS.'
Viewers: 'Kelompok Penampil'
Visibility: Visibilitas
has_one_Parent: 'Laman Induk'
many_many_BackLinkTracking: 'Jajaki Tautan Balik'
many_many_ImageTracking: 'Jajaki Gambar'
many_many_LinkTracking: 'Jajaki Tautan'
SiteTreeURLSegmentField:
EMPTY: 'Mohon isikan URL atau klik Batal'
HelpChars: 'Karakter khusus akan dikonversi secara otomatis atau dihapus.'
URLSegmentField:
Cancel: Batal
Edit: Edit
OK: OK
ViewArchivedEmail_ss:
CANACCESS: 'Anda dapat mengakses arsip situs pada tautan ini:'
HAVEASKED: 'Anda telah meminta menampilkan konten situs ini pada'
VirtualPage: VirtualPage:
HEADER: 'Ini adalah halaman virtual' CHOOSE: 'Laman Tertaut'
PLURALNAME: 'Halaman-halaman Virtual' DESCRIPTION: 'Tampilkan konten laman lain'
SINGULARNAME: 'Halaman Virtual' EditLink: edit
HEADER: 'Ini adalah laman virtual'
HEADERWITHLINK: 'Ini adalah laman virtual dengan konten salinan dari "{title}" ({link})'
PLURALNAME: 'Laman Virtual'
PageTypNotAllowedOnRoot: 'Laman asli jenis "{type}" tidak dibolehkan pada tingkat atas untuk laman virtual ini'
SINGULARNAME: 'Laman Virtual'
CMSFileAddController: CMSFileAddController:
MENUTITLE: Berkas MENUTITLE: Berkas
CMSPageEditController: CMSPageEditController:
MENUTITLE: 'Ubah halaman' MENUTITLE: 'Edit Laman'
CMSPageSettingsController: CMSPageSettingsController:
MENUTITLE: 'Ubah halaman' MENUTITLE: 'Edit Laman'
CMSSettingsController: CMSSettingsController:
MENUTITLE: Pengaturan MENUTITLE: Pengaturan
CMSSiteTreeFilter_StatusDeletedPages:
Title: 'Halaman yang dihapus'

View File

@ -215,7 +215,7 @@ sv:
DEFAULTSERVERERRORPAGETITLE: 'Serverfel' DEFAULTSERVERERRORPAGETITLE: 'Serverfel'
DESCRIPTION: 'Anpassat innehåll för olika felärenden (t.ex. "Sidan kan inte hittas")' DESCRIPTION: 'Anpassat innehåll för olika felärenden (t.ex. "Sidan kan inte hittas")'
ERRORFILEPROBLEM: 'Kunde inte skriva filen "{filename}". Var vänlig kontrollera skrivrättigheterna.' ERRORFILEPROBLEM: 'Kunde inte skriva filen "{filename}". Var vänlig kontrollera skrivrättigheterna.'
PLURALNAME: 'Felmeddelandesidor' PLURALNAME: 'Felsidor'
SINGULARNAME: 'Felsida' SINGULARNAME: 'Felsida'
Folder: Folder:
AddFolderButton: 'Skapa mapp' AddFolderButton: 'Skapa mapp'
@ -238,7 +238,7 @@ sv:
HASBEENSETUP: 'En omdirigeringssida har skapats utan att ha något mål.' HASBEENSETUP: 'En omdirigeringssida har skapats utan att ha något mål.'
HEADER: 'Den här sidan omdirigerar användare till en annan sida' HEADER: 'Den här sidan omdirigerar användare till en annan sida'
OTHERURL: 'Andra sidans URL' OTHERURL: 'Andra sidans URL'
PLURALNAME: 'Omdirigerings sidor' PLURALNAME: 'Omdirigeringssida'
REDIRECTTO: 'Omdirigera till' REDIRECTTO: 'Omdirigera till'
REDIRECTTOEXTERNAL: 'En annan sajt' REDIRECTTOEXTERNAL: 'En annan sajt'
REDIRECTTOPAGE: 'En sida på din sajt' REDIRECTTOPAGE: 'En sida på din sajt'

View File

@ -35,7 +35,7 @@ So that only high quality changes are seen by our visitors
When I click "More options" in the "#ActionMenus" element When I click "More options" in the "#ActionMenus" element
Then I should not see "Unpublish" in the "#ActionMenus_MoreOptions" element Then I should not see "Unpublish" in the "#ActionMenus_MoreOptions" element
And I should see "Not published" in the "#ActionMenus_MoreOptions" element And I should see "Not published" in the "#ActionMenus_MoreOptions" element
And I should see "Delete draft" in the "#ActionMenus_MoreOptions" element And I should see "Archive" in the "#ActionMenus_MoreOptions" element
And I should see a "Save & publish" button And I should see a "Save & publish" button
And I should see a "Saved" button And I should see a "Saved" button
@ -48,7 +48,7 @@ So that only high quality changes are seen by our visitors
When I press the "Publish" button When I press the "Publish" button
And I click "More options" in the "#ActionMenus" element And I click "More options" in the "#ActionMenus" element
Then I should see "Unpublish" in the "#ActionMenus_MoreOptions" element Then I should see "Unpublish" in the "#ActionMenus_MoreOptions" element
And I should see "Delete draft" in the "#ActionMenus_MoreOptions" element And I should see "Archive" in the "#ActionMenus_MoreOptions" element
And I should see a "Published" button And I should see a "Published" button
And I should see a "Saved" button And I should see a "Saved" button
@ -64,7 +64,7 @@ So that only high quality changes are seen by our visitors
And I click on "Hello" in the tree And I click on "Hello" in the tree
When I click "More options" in the "#ActionMenus" element When I click "More options" in the "#ActionMenus" element
And I press the "Unpublish" button And I press the "Unpublish" button, confirming the dialog
Then I press the "Log out" button Then I press the "Log out" button
And I go to "/hello" And I go to "/hello"
@ -80,16 +80,16 @@ So that only high quality changes are seen by our visitors
And I click "More options" in the "#ActionMenus" element And I click "More options" in the "#ActionMenus" element
Then I should see "Unpublish" in the "#ActionMenus_MoreOptions" element Then I should see "Unpublish" in the "#ActionMenus_MoreOptions" element
When I press the "Unpublish" button When I press the "Unpublish" button, confirming the dialog
And I click "More options" in the "#ActionMenus" element And I click "More options" in the "#ActionMenus" element
Then I should see "Delete draft" in the "#ActionMenus_MoreOptions" element Then I should see "Archive" in the "#ActionMenus_MoreOptions" element
When I press the "Delete draft" button When I press the "Archive" button, confirming the dialog
Then I should see a "Restore" button Then I should see a "Restore" button
And I should not see a "Published" button And I should not see a "Published" button
And I should not see a "Save & publish" button And I should not see a "Save & publish" button
And I should not see a "Saved" button And I should not see a "Saved" button
And I should not see a "Save draft" button And I should not see a "Save draft" button
When I press the "Restore" button When I press the "Restore" button, confirming the dialog
Then I should see a "Save & publish" button Then I should see a "Save & publish" button

View File

@ -46,7 +46,7 @@ Feature: Search for a page
And the "page" "Deleted Page" is deleted And the "page" "Deleted Page" is deleted
When I press the "Apply Filter" button When I press the "Apply Filter" button
Then I should not see "Deleted Page" in the tree Then I should not see "Deleted Page" in the tree
When I select "All pages, including deleted" from "Pages" When I select "All pages, including archived" from "Pages"
And I press the "Apply Filter" button And I press the "Apply Filter" button
Then I should see "Deleted Page" in the tree Then I should see "Deleted Page" in the tree
@ -56,7 +56,7 @@ Feature: Search for a page
And the "page" "Deleted Page" is deleted And the "page" "Deleted Page" is deleted
When I press the "Apply Filter" button When I press the "Apply Filter" button
Then I should not see "Deleted Page" in the tree Then I should not see "Deleted Page" in the tree
When I select "Deleted pages" from "Pages" When I select "Archived pages" from "Pages"
And I press the "Apply Filter" button And I press the "Apply Filter" button
Then I should see "Deleted Page" in the tree Then I should see "Deleted Page" in the tree
And I should not see "About Us" in the tree And I should not see "About Us" in the tree
@ -66,7 +66,7 @@ Feature: Search for a page
And the "page" "Draft Page" is not published And the "page" "Draft Page" is not published
When I press the "Apply Filter" button When I press the "Apply Filter" button
Then I should see "Draft Page" in the tree Then I should see "Draft Page" in the tree
When I select "Draft unpublished pages" from "Pages" When I select "Draft pages" from "Pages"
And I press the "Apply Filter" button And I press the "Apply Filter" button
Then I should see "Draft Page" in the tree Then I should see "Draft Page" in the tree
And I should not see "About Us" in the tree And I should not see "About Us" in the tree
@ -81,7 +81,7 @@ Feature: Search for a page
When I go to "/admin/pages" When I go to "/admin/pages"
And I expand the "Filter" CMS Panel And I expand the "Filter" CMS Panel
When I select "Changed pages" from "Pages" When I select "Modified pages" from "Pages"
And I press the "Apply Filter" button And I press the "Apply Filter" button
Then I should see "About Us" in the tree Then I should see "About Us" in the tree
And I should not see "Home" in the tree And I should not see "Home" in the tree

View File

@ -0,0 +1,86 @@
<?php
/**
* Tests CMS Specific subclasses of {@see CMSBatchAction}
*/
class CMSBatchActionsTest extends SapphireTest {
protected static $fixture_file = 'CMSBatchActionsTest.yml';
public function setUp() {
parent::setUp();
// published page
$published = $this->objFromFixture('Page', 'published');
$published->doPublish();
// Deleted / archived page
$archived = $this->objFromFixture('Page', 'archived');
$archived->doArchive();
// Unpublished
$unpublished = $this->objFromFixture('Page', 'unpublished');
$unpublished->doPublish();
$unpublished->doUnpublish();
// Modified
$modified = $this->objFromFixture('Page', 'modified');
$modified->doPublish();
$modified->Title = 'modified2';
$modified->write();
}
/**
* Test which pages can be published via batch actions
*/
public function testBatchPublish() {
$this->logInWithPermission('ADMIN');
$pages = Versioned::get_including_deleted('Page');
$ids = $pages->column('ID');
$action = new CMSBatchAction_Publish();
// Test applicable pages
$applicable = $action->applicablePages($ids);
$this->assertContains($this->idFromFixture('Page', 'published'), $applicable);
$this->assertNotContains($this->idFromFixture('Page', 'archived'), $applicable);
$this->assertContains($this->idFromFixture('Page', 'unpublished'), $applicable);
$this->assertContains($this->idFromFixture('Page', 'modified'), $applicable);
}
/**
* Test which pages can be unpublished via batch actions
*/
public function testBatchUnpublish() {
$this->logInWithPermission('ADMIN');
$pages = Versioned::get_including_deleted('Page');
$ids = $pages->column('ID');
$action = new CMSBatchAction_Unpublish();
// Test applicable page
$applicable = $action->applicablePages($ids);
$this->assertContains($this->idFromFixture('Page', 'published'), $applicable);
$this->assertNotContains($this->idFromFixture('Page', 'archived'), $applicable);
$this->assertNotContains($this->idFromFixture('Page', 'unpublished'), $applicable);
$this->assertContains($this->idFromFixture('Page', 'modified'), $applicable);
}
/**
* Test which pages can be published via batch actions
*/
public function testBatchArchive() {
$this->logInWithPermission('ADMIN');
$pages = Versioned::get_including_deleted('Page');
$ids = $pages->column('ID');
$action = new CMSBatchAction_Archive();
// Test applicable pages
$applicable = $action->applicablePages($ids);
$this->assertContains($this->idFromFixture('Page', 'published'), $applicable);
$this->assertNotContains($this->idFromFixture('Page', 'archived'), $applicable);
$this->assertContains($this->idFromFixture('Page', 'unpublished'), $applicable);
$this->assertContains($this->idFromFixture('Page', 'modified'), $applicable);
}
}

View File

@ -0,0 +1,9 @@
Page:
published:
Title: Published
archived:
Title: archived
unpublished:
Title: unpublished
modified:
Title: modified1

View File

@ -263,7 +263,15 @@ class CMSMainTest extends FunctionalTest {
$this->get('admin/pages/add'); $this->get('admin/pages/add');
$response = $this->post( $response = $this->post(
'admin/pages/add/AddForm', 'admin/pages/add/AddForm',
array('ParentID' => '0', 'PageType' => 'Page', 'Locale' => 'en_US', 'action_doAdd' => 1) array(
'ParentID' => '0',
'PageType' => 'Page',
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
), array(
'X-Pjax' => 'CurrentForm,Breadcrumbs',
)
); );
// should redirect, which is a permission error // should redirect, which is a permission error
$this->assertEquals(403, $response->getStatusCode(), 'Add TopLevel page must fail for normal user'); $this->assertEquals(403, $response->getStatusCode(), 'Add TopLevel page must fail for normal user');
@ -274,11 +282,19 @@ class CMSMainTest extends FunctionalTest {
$response = $this->post( $response = $this->post(
'admin/pages/add/AddForm', 'admin/pages/add/AddForm',
array('ParentID' => '0', 'PageType' => 'Page', 'Locale' => 'en_US', 'action_doAdd' => 1) array(
'ParentID' => '0',
'PageType' => 'Page',
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
), array(
'X-Pjax' => 'CurrentForm,Breadcrumbs',
)
); );
$this->assertEquals(302, $response->getStatusCode(), 'Must be a redirect on success'); $location = $response->getHeader('X-ControllerURL');
$location=$response->getHeader('Location'); $this->assertNotEmpty($location, 'Must be a redirect on success');
$this->assertContains('/show/',$location, 'Must redirect to /show/ the new page'); $this->assertContains('/show/',$location, 'Must redirect to /show/ the new page');
// TODO Logout // TODO Logout
$this->session()->inst_set('loggedInAs', NULL); $this->session()->inst_set('loggedInAs', NULL);

View File

@ -71,7 +71,7 @@ class FileLinkTrackingTest extends SapphireTest {
// Publish the source page // Publish the source page
$page = $this->objFromFixture('Page', 'page1'); $page = $this->objFromFixture('Page', 'page1');
$this->assertTrue($page->doPublish()); $this->assertTrue($page->doPublish());
$this->assertFalse($page->IsModifiedOnStage); $this->assertFalse($page->getIsModifiedOnStage());
// Rename the file // Rename the file
$file = $this->objFromFixture('File', 'file1'); $file = $this->objFromFixture('File', 'file1');
@ -83,7 +83,7 @@ class FileLinkTrackingTest extends SapphireTest {
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($page->ID)); Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($page->ID));
// Confirm that the page hasn't gone green. // Confirm that the page hasn't gone green.
$this->assertFalse($page->IsModifiedOnStage); $this->assertFalse($page->getIsModifiedOnStage());
} }
public function testTwoFileRenamesInARowWork() { public function testTwoFileRenamesInARowWork() {

View File

@ -4,7 +4,7 @@
* - action_save * - action_save
* - action_publish * - action_publish
* - action_unpublish * - action_unpublish
* - action_delete * - action_archive
* - action_deletefromlive * - action_deletefromlive
* - action_rollback * - action_rollback
* - action_revert * - action_revert
@ -83,7 +83,7 @@ class SiteTreeActionsTest extends FunctionalTest {
$this->assertNotNull($actions->dataFieldByName('action_save')); $this->assertNotNull($actions->dataFieldByName('action_save'));
$this->assertNotNull($actions->dataFieldByName('action_publish')); $this->assertNotNull($actions->dataFieldByName('action_publish'));
$this->assertNotNull($actions->dataFieldByName('action_unpublish')); $this->assertNotNull($actions->dataFieldByName('action_unpublish'));
$this->assertNotNull($actions->dataFieldByName('action_delete')); $this->assertNotNull($actions->dataFieldByName('action_archive'));
$this->assertNull($actions->dataFieldByName('action_deletefromlive')); $this->assertNull($actions->dataFieldByName('action_deletefromlive'));
$this->assertNull($actions->dataFieldByName('action_rollback')); $this->assertNull($actions->dataFieldByName('action_rollback'));
$this->assertNull($actions->dataFieldByName('action_revert')); $this->assertNull($actions->dataFieldByName('action_revert'));
@ -111,7 +111,7 @@ class SiteTreeActionsTest extends FunctionalTest {
$this->assertNull($actions->dataFieldByName('action_save')); $this->assertNull($actions->dataFieldByName('action_save'));
$this->assertNull($actions->dataFieldByName('action_publish')); $this->assertNull($actions->dataFieldByName('action_publish'));
$this->assertNull($actions->dataFieldByName('action_unpublish')); $this->assertNull($actions->dataFieldByName('action_unpublish'));
$this->assertNull($actions->dataFieldByName('action_delete')); $this->assertNull($actions->dataFieldByName('action_archive'));
$this->assertNotNull($actions->dataFieldByName('action_deletefromlive')); $this->assertNotNull($actions->dataFieldByName('action_deletefromlive'));
$this->assertNull($actions->dataFieldByName('action_rollback')); $this->assertNull($actions->dataFieldByName('action_rollback'));
$this->assertNotNull($actions->dataFieldByName('action_revert')); $this->assertNotNull($actions->dataFieldByName('action_revert'));
@ -135,7 +135,7 @@ class SiteTreeActionsTest extends FunctionalTest {
$this->assertNotNull($actions->dataFieldByName('action_save')); $this->assertNotNull($actions->dataFieldByName('action_save'));
$this->assertNotNull($actions->dataFieldByName('action_publish')); $this->assertNotNull($actions->dataFieldByName('action_publish'));
$this->assertNotNull($actions->dataFieldByName('action_unpublish')); $this->assertNotNull($actions->dataFieldByName('action_unpublish'));
$this->assertNotNull($actions->dataFieldByName('action_delete')); $this->assertNotNull($actions->dataFieldByName('action_archive'));
$this->assertNull($actions->dataFieldByName('action_deletefromlive')); $this->assertNull($actions->dataFieldByName('action_deletefromlive'));
$this->assertNotNull($actions->dataFieldByName('action_rollback')); $this->assertNotNull($actions->dataFieldByName('action_rollback'));
$this->assertNull($actions->dataFieldByName('action_revert')); $this->assertNull($actions->dataFieldByName('action_revert'));
@ -155,7 +155,7 @@ class SiteTreeActionsTest extends FunctionalTest {
$this->assertNull($actions->dataFieldByName('action_save')); $this->assertNull($actions->dataFieldByName('action_save'));
$this->assertNull($actions->dataFieldByName('action_publish')); $this->assertNull($actions->dataFieldByName('action_publish'));
$this->assertNull($actions->dataFieldByName('action_unpublish')); $this->assertNull($actions->dataFieldByName('action_unpublish'));
$this->assertNull($actions->dataFieldByName('action_delete')); $this->assertNull($actions->dataFieldByName('action_archive'));
$this->assertNotNull($actions->dataFieldByName('action_email')); $this->assertNotNull($actions->dataFieldByName('action_email'));
$this->assertNotNull($actions->dataFieldByName('action_rollback')); $this->assertNotNull($actions->dataFieldByName('action_rollback'));
} }

View File

@ -194,17 +194,17 @@ class SiteTreeTest extends SapphireTest {
// newly created page // newly created page
$createdPage = new SiteTree(); $createdPage = new SiteTree();
$createdPage->write(); $createdPage->write();
$this->assertFalse($createdPage->IsDeletedFromStage); $this->assertFalse($createdPage->getIsDeletedFromStage());
$this->assertTrue($createdPage->IsAddedToStage); $this->assertTrue($createdPage->getIsAddedToStage());
$this->assertTrue($createdPage->IsModifiedOnStage); $this->assertTrue($createdPage->getIsModifiedOnStage());
// published page // published page
$publishedPage = new SiteTree(); $publishedPage = new SiteTree();
$publishedPage->write(); $publishedPage->write();
$publishedPage->publish('Stage','Live'); $publishedPage->publish('Stage','Live');
$this->assertFalse($publishedPage->IsDeletedFromStage); $this->assertFalse($publishedPage->getIsDeletedFromStage());
$this->assertFalse($publishedPage->IsAddedToStage); $this->assertFalse($publishedPage->getIsAddedToStage());
$this->assertFalse($publishedPage->IsModifiedOnStage); $this->assertFalse($publishedPage->getIsModifiedOnStage());
// published page, deleted from stage // published page, deleted from stage
$deletedFromDraftPage = new SiteTree(); $deletedFromDraftPage = new SiteTree();
@ -212,9 +212,9 @@ class SiteTreeTest extends SapphireTest {
$deletedFromDraftPageID = $deletedFromDraftPage->ID; $deletedFromDraftPageID = $deletedFromDraftPage->ID;
$deletedFromDraftPage->publish('Stage','Live'); $deletedFromDraftPage->publish('Stage','Live');
$deletedFromDraftPage->deleteFromStage('Stage'); $deletedFromDraftPage->deleteFromStage('Stage');
$this->assertTrue($deletedFromDraftPage->IsDeletedFromStage); $this->assertTrue($deletedFromDraftPage->getIsDeletedFromStage());
$this->assertFalse($deletedFromDraftPage->IsAddedToStage); $this->assertFalse($deletedFromDraftPage->getIsAddedToStage());
$this->assertFalse($deletedFromDraftPage->IsModifiedOnStage); $this->assertFalse($deletedFromDraftPage->getIsModifiedOnStage());
// published page, deleted from live // published page, deleted from live
$deletedFromLivePage = new SiteTree(); $deletedFromLivePage = new SiteTree();
@ -222,9 +222,9 @@ class SiteTreeTest extends SapphireTest {
$deletedFromLivePage->publish('Stage','Live'); $deletedFromLivePage->publish('Stage','Live');
$deletedFromLivePage->deleteFromStage('Stage'); $deletedFromLivePage->deleteFromStage('Stage');
$deletedFromLivePage->deleteFromStage('Live'); $deletedFromLivePage->deleteFromStage('Live');
$this->assertTrue($deletedFromLivePage->IsDeletedFromStage); $this->assertTrue($deletedFromLivePage->getIsDeletedFromStage());
$this->assertFalse($deletedFromLivePage->IsAddedToStage); $this->assertFalse($deletedFromLivePage->getIsAddedToStage());
$this->assertFalse($deletedFromLivePage->IsModifiedOnStage); $this->assertFalse($deletedFromLivePage->getIsModifiedOnStage());
// published page, modified // published page, modified
$modifiedOnDraftPage = new SiteTree(); $modifiedOnDraftPage = new SiteTree();
@ -232,9 +232,9 @@ class SiteTreeTest extends SapphireTest {
$modifiedOnDraftPage->publish('Stage','Live'); $modifiedOnDraftPage->publish('Stage','Live');
$modifiedOnDraftPage->Content = 'modified'; $modifiedOnDraftPage->Content = 'modified';
$modifiedOnDraftPage->write(); $modifiedOnDraftPage->write();
$this->assertFalse($modifiedOnDraftPage->IsDeletedFromStage); $this->assertFalse($modifiedOnDraftPage->getIsDeletedFromStage());
$this->assertFalse($modifiedOnDraftPage->IsAddedToStage); $this->assertFalse($modifiedOnDraftPage->getIsAddedToStage());
$this->assertTrue($modifiedOnDraftPage->IsModifiedOnStage); $this->assertTrue($modifiedOnDraftPage->getIsModifiedOnStage());
} }
/** /**

View File

@ -209,46 +209,46 @@ class VirtualPageTest extends SapphireTest {
$vp->write(); $vp->write();
// VP is oragne // VP is oragne
$this->assertTrue($vp->IsAddedToStage); $this->assertTrue($vp->getIsAddedToStage());
// VP is still orange after we publish // VP is still orange after we publish
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->IsAddedToStage); $this->assertTrue($vp->getIsAddedToStage());
// A new VP created after P's initial construction // A new VP created after P's initial construction
$vp2 = new VirtualPage(); $vp2 = new VirtualPage();
$vp2->CopyContentFromID = $p->ID; $vp2->CopyContentFromID = $p->ID;
$vp2->write(); $vp2->write();
$this->assertTrue($vp2->IsAddedToStage); $this->assertTrue($vp2->getIsAddedToStage());
// Also remains orange after a republish // Also remains orange after a republish
$p->Content = "new content"; $p->Content = "new content";
$p->write(); $p->write();
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp2); $this->fixVersionNumberCache($vp2);
$this->assertTrue($vp2->IsAddedToStage); $this->assertTrue($vp2->getIsAddedToStage());
// VP is now published // VP is now published
$vp->doPublish(); $vp->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->ExistsOnLive); $this->assertTrue($vp->getExistsOnLive());
$this->assertFalse($vp->IsModifiedOnStage); $this->assertFalse($vp->getIsModifiedOnStage());
// P edited, VP and P both go green // P edited, VP and P both go green
$p->Content = "third content"; $p->Content = "third content";
$p->write(); $p->write();
$this->fixVersionNumberCache($vp, $p); $this->fixVersionNumberCache($vp, $p);
$this->assertTrue($p->IsModifiedOnStage); $this->assertTrue($p->getIsModifiedOnStage());
$this->assertTrue($vp->IsModifiedOnStage); $this->assertTrue($vp->getIsModifiedOnStage());
// Publish, VP goes black // Publish, VP goes black
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->ExistsOnLive); $this->assertTrue($vp->getExistsOnLive());
$this->assertFalse($vp->IsModifiedOnStage); $this->assertFalse($vp->getIsModifiedOnStage());
} }
public function testVirtualPagesCreateVersionRecords() { public function testVirtualPagesCreateVersionRecords() {