Merge remote-tracking branch 'origin/3.1'

This commit is contained in:
Ingo Schommer 2013-05-31 18:10:26 +02:00
commit c21c9cba0d
19 changed files with 272 additions and 90 deletions

View File

@ -2,7 +2,7 @@
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-cms.png?branch=3.1)](http://travis-ci.org/silverstripe/silverstripe-cms)
PHP5 Content Management System (CMS), see [http://silverstripe.org](http://silverstripe.org). Requires the [`framework`](http://github.com/silverstripe/sapphire) module and a [`silverstripe-installer`](http://github.com/silverstripe/silverstripe-installer) base project.
PHP5 Content Management System (CMS), see [http://silverstripe.org](http://silverstripe.org). Requires the [`framework`](http://github.com/silverstripe/silverstripe-framework) module and a [`silverstripe-installer`](http://github.com/silverstripe/silverstripe-installer) base project.
## Installation ##
@ -22,7 +22,7 @@ If you would like to make changes to the SilverStripe core codebase, we have an
* [Requirements](http://doc.silverstripe.org/framework/en/installation/server-requirements)
* [Changelogs](http://doc.silverstripe.org/framework/en/changelogs/)
* [Bugtracker: Framework](https://github.com/silverstripe/sapphire/issues)
* [Bugtracker: Framework](https://github.com/silverstripe/silverstripe-framework/issues)
* [Bugtracker: CMS](https://github.com/silverstripe/silverstripe-cms/issues)
* [Bugtracker: Installer](https://github.com/silverstripe/silverstripe-installer/issues)
* [Forums](http://silverstripe.org/forums)

8
_config/i18n.yml Normal file
View File

@ -0,0 +1,8 @@
---
Name: cmsi18n
Before: '/i18n'
After: '/i18n#basei18n'
---
i18n:
module_priority:
- cms

View File

@ -158,7 +158,7 @@ JS
GridFieldLevelup::create($folder->ID)->setLinkSpec('admin/assets/show/%d')
);
$gridField = new GridField('File', $title, $this->getList(), $gridFieldConfig);
$gridField = GridField::create('File', $title, $this->getList(), $gridFieldConfig);
$columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
$columns->setDisplayFields(array(
'StripThumbnail' => '',
@ -491,6 +491,7 @@ JS
return $folder;
}
}
$this->setCurrentPageID(null);
return new Folder();
}

View File

@ -1092,15 +1092,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
if($version) {
$record->doRollbackTo($version);
$message = _t(
'CMSMain.ROLLEDBACKVERSION',
"Rolled back to version #%d. New version number is #%d",
array('version' => $data['Version'], 'versionnew' => $record->Version)
'CMSMain.ROLLEDBACKVERSIONv2',
"Rolled back to version #%d.",
array('version' => $data['Version'])
);
} else {
$record->doRollbackTo('Live');
$message = _t(
'CMSMain.ROLLEDBACKPUB',"Rolled back to published version. New version number is #{version}",
array('version' => $record->Version)
'CMSMain.ROLLEDBACKPUBv2',"Rolled back to published version."
);
}
@ -1260,7 +1259,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
rawurlencode(_t(
'CMSMain.RESTORED',
"Restored '{title}' successfully",
array('title' => $restoredPage->TreeTitle)
array('title' => $restoredPage->Title)
))
);

View File

@ -142,13 +142,16 @@ class CMSPageHistoryController extends CMSMain {
);
$revert->setReadonly(true);
}
else {
$message = _t(
'CMSPageHistoryController.VIEWINGVERSION',
"Currently viewing version {version}.",
array('version' => $versionID)
);
} else {
if($record->isLatestVersion()) {
$message = _t('CMSPageHistoryController.VIEWINGLATEST', 'Currently viewing the latest version.');
} else {
$message = _t(
'CMSPageHistoryController.VIEWINGVERSION',
"Currently viewing version {version}.",
array('version' => $versionID)
);
}
}
$fields->addFieldToTab('Root.Main',

View File

@ -372,6 +372,10 @@ HTML;
* This action is called by the installation system
*/
public function successfullyinstalled() {
// Return 410 Gone if this site is not actually a fresh installation
if (!file_exists(BASE_PATH . '/install.php')) {
$this->httpError(410);
}
// The manifest should be built by now, so it's safe to publish the 404 page
$fourohfour = Versioned::get_one_by_stage('ErrorPage', 'Stage', '"ErrorCode" = 404');
if($fourohfour) {

View File

@ -400,11 +400,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
) {
return; // There were no suitable matches at all.
}
$link = Convert::raw2att($page->Link());
if($content) {
return sprintf('<a href="%s">%s</a>', $page->Link(), $parser->parse($content));
return sprintf('<a href="%s">%s</a>', $link, $parser->parse($content));
} else {
return $page->Link();
return $link;
}
}
@ -444,7 +446,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
* @return string
*/
public function PreviewLink($action = null) {
return $this->AbsoluteLink($action);
if($this->hasMethod('alternatePreviewLink')) {
return $this->alternatePreviewLink($action);
} else {
return $this->AbsoluteLink($action);
}
}
/**
@ -462,22 +468,19 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
public function RelativeLink($action = null) {
if($this->ParentID && self::config()->nested_urls) {
$base = $this->Parent()->RelativeLink($this->URLSegment);
} elseif(!$action && $this->URLSegment == RootURLController::get_homepage_link()) {
// Unset base for root-level homepages.
// Note: Homepages with action parameters (or $action === true)
// need to retain their URLSegment.
$base = null;
} else {
$base = $this->URLSegment;
}
// Unset base for homepage URLSegments in their default language.
// Homepages with action parameters or in different languages
// need to retain their URLSegment. We can only do this if the homepage
// is on the root level.
if(!$action && $base == RootURLController::get_homepage_link() && !$this->ParentID) {
$base = null;
if(class_exists('Translatable') && $this->hasExtension('Translatable') && $this->Locale != Translatable::default_locale()){
$base = $this->URLSegment;
}
}
$this->extend('updateRelativeLink', $base, $action);
// Legacy support
// Legacy support: If $action === true, retain URLSegment for homepages,
// but don't append any action
if($action === true) $action = null;
return Controller::join_links($base, '/', $action);
@ -1305,8 +1308,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if($this->ExtraMeta) {
$tags .= $this->ExtraMeta . "\n";
}
if(Permission::check('CMS_ACCESS_CMSMain') && in_array('CMSPreviewable', class_implements($this))) {
if(Permission::check('CMS_ACCESS_CMSMain') && in_array('CMSPreviewable', class_implements($this)) && !$this instanceof ErrorPage) {
$tags .= "<meta name=\"x-page-id\" content=\"{$this->ID}\" />\n";
$tags .= "<meta name=\"x-cms-edit-link\" content=\"" . $this->CMSEditLink() . "\" />\n";
}
@ -2618,7 +2621,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if(is_string($data)) $data = array('text' => $data);
$treeTitle .= sprintf(
"<span class=\"badge %s\"%s>%s</span>",
Convert::raw2xml($class),
'status-' . Convert::raw2xml($class),
(isset($data['title'])) ? sprintf(' title="%s"', Convert::raw2xml($data['title'])) : '',
Convert::raw2xml($data['text'])
);
@ -2654,7 +2657,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if(!$this->canAddChildren())
$classes .= " nochildren";
if(!$this->canEdit() && !$this->canAddChildren())
if(!$this->canView() && !$this->canEdit() && !$this->canAddChildren())
$classes .= " disabled";
if(!$this->ShowInMenus)
@ -2758,7 +2761,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
'name' => _t('SiteTree.VIEW_ALL_DESCRIPTION', 'View any page'),
'category' => _t('Permissions.CONTENT_CATEGORY', 'Content permissions'),
'sort' => -100,
'help' => _t('SiteTree.VIEW_ALL_HELP', 'Ability to view any page on the site, regardless of the settings on the Access tab. Requires the "Access to Site Content" permission')
'help' => _t('SiteTree.VIEW_ALL_HELP', 'Ability to view any page on the site, regardless of the settings on the Access tab. Requires the "Access to \'Pages\' section" permission')
),
'SITETREE_EDIT_ALL' => array(
'name' => _t('SiteTree.EDIT_ALL_DESCRIPTION', 'Edit any page'),

View File

@ -1,4 +1,5 @@
<?php
/**
* Plug-ins for additional functionality in your SiteTree classes.
*
@ -7,23 +8,71 @@
*/
abstract class SiteTreeExtension extends DataExtension {
/**
* Hook called before the page's {@link SiteTree::doPublish()} action is completed
*
* @param SiteTree &$original The current Live SiteTree record prior to publish
*/
public function onBeforePublish(&$original) {
}
/**
* Hook called after the page's {@link SiteTree::doPublish()} action is completed
*
* @param SiteTree &$original The current Live SiteTree record prior to publish
*/
public function onAfterPublish(&$original) {
}
/**
* Hook called before the page's {@link SiteTree::doUnpublish()} action is completed
*/
public function onBeforeUnpublish() {
}
/**
* Hook called after the page's {@link SiteTree::doUnpublish()} action is completed
*/
public function onAfterUnpublish() {
}
/**
* Hook called to determine if a user may add children to this SiteTree object
*
* @see SiteTree::canAddChildren()
*
* @param Member $member The member to check permission against, or the currently
* logged in user
* @return boolean|null Return false to deny rights, or null to yield to default
*/
public function canAddChildren($member) {
}
/**
* Hook called to determine if a user may publish this SiteTree object
*
* @see SiteTree::canPublish()
*
* @param Member $member The member to check permission against, or the currently
* logged in user
* @return boolean|null Return false to deny rights, or null to yield to default
*/
public function canPublish($member) {
}
/**
* Hook called to modify the $base url of this page, with a given $action,
* before {@link SiteTree::RelativeLink()} calls {@link Controller::join_links()}
* on the $base and $action
*
* @param string &$base The URL of this page relative to siteroot, not including
* the action
* @param string|boolean &$action The action or subpage called on this page.
* (Legacy support) If this is true, then do not reduce the 'home' urlsegment
* to an empty link
*/
public function updateRelativeLink(&$base, &$action) {
}
}

View File

@ -43,7 +43,7 @@ class SearchForm extends Form {
}
if(class_exists('Translatable') && singleton('SiteTree')->hasExtension('Translatable')) {
$fields->push(new HiddenField('locale', 'locale', Translatable::get_current_locale()));
$fields->push(new HiddenField('searchlocale', 'searchlocale', Translatable::get_current_locale()));
}
if(!$actions) {
@ -101,11 +101,18 @@ class SearchForm extends Form {
if(!isset($data) || !is_array($data)) $data = $_REQUEST;
// set language (if present)
if(class_exists('Translatable') && singleton('SiteTree')->hasExtension('Translatable') && isset($data['locale'])) {
$origLocale = Translatable::get_current_locale();
Translatable::set_current_locale($data['locale']);
if(class_exists('Translatable')) {
if(singleton('SiteTree')->hasExtension('Translatable') && isset($data['searchlocale'])) {
if($data['searchlocale'] == "ALL") {
Translatable::disable_locale_filter();
} else {
$origLocale = Translatable::get_current_locale();
Translatable::set_current_locale($data['searchlocale']);
}
}
}
$keywords = $data['Search'];
$andProcessor = create_function('$matches','
@ -137,8 +144,14 @@ class SearchForm extends Form {
}
// reset locale
if(class_exists('Translatable') && singleton('SiteTree')->hasExtension('Translatable') && isset($data['locale'])) {
Translatable::set_current_locale($origLocale);
if(class_exists('Translatable')) {
if(singleton('SiteTree')->hasExtension('Translatable') && isset($data['searchlocale'])) {
if($data['searchlocale'] == "ALL") {
Translatable::enable_locale_filter();
} else {
Translatable::set_current_locale($origLocale);
}
}
}
return $results;

View File

@ -98,8 +98,13 @@
if(state) parentId = parseInt(JSON.parse(state).ParentID, 10);
}
var data = {selector: this.data('targetPanel'),pjax: this.data('pjax')},
url = parentId ? ss.i18n.sprintf(this.data('urlAddpage'), parentId) : this.attr('href');
var data = {selector: this.data('targetPanel'),pjax: this.data('pjax')}, url;
if(parentId) {
extraParams = this.data('extraParams') ? this.data('extraParams') : '';
url = $.path.addSearchParams(ss.i18n.sprintf(this.data('urlAddpage'), parentId), extraParams);
} else {
url = this.attr('href');
}
$('.cms-container').loadPanel(url, null, data);
e.preventDefault();

View File

@ -9,15 +9,15 @@
'items': function(node) {
var menuitems = {
'edit': {
'label': ss.i18n._t('Tree.EditPage'),
'action': function(obj) {
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
self.data('urlEditpage'), obj.data('id')
));
'edit': {
'label': ss.i18n._t('Tree.EditPage', 'Edit page', 100, 'Used in the context menu when right-clicking on a page node in the CMS tree'),
'action': function(obj) {
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
self.data('urlEditpage'), obj.data('id')
));
}
}
}
};
};
// Add "show as list"
if(!node.hasClass('nochildren')) {
@ -60,18 +60,21 @@
'label': '<span class="jstree-pageicon"></span>' + klassData.title,
'_class': 'class-' + klass,
'action': function(obj) {
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
self.data('urlAddpage'), id, klass
));
$('.cms-container').entwine('.ss').loadPanel(
$.path.addSearchParams(
ss.i18n.sprintf(self.data('urlAddpage'), id, klass),
self.data('extraParams')
)
);
}
};
});
if(hasAllowedChildren) {
menuitems['addsubpage'] = {
'label': ss.i18n._t('Tree.AddSubPage'),
'submenu': menuAllowedChildren
};
'label': ss.i18n._t('Tree.AddSubPage', 'Add page under this page', 100, 'Used in the context menu when right-clicking on a page node in the CMS tree'),
'submenu': menuAllowedChildren
};
}
menuitems['duplicate'] = {
@ -80,16 +83,22 @@
{
'label': ss.i18n._t('Tree.ThisPageOnly'),
'action': function(obj) {
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
self.data('urlDuplicate'), obj.data('id')
));
$('.cms-container').entwine('.ss').loadPanel(
$.path.addSearchParams(
ss.i18n.sprintf(self.data('urlDuplicate'), obj.data('id')),
self.data('extraParams')
)
);
}
},{
'label': ss.i18n._t('Tree.ThisPageAndSubpages'),
'action': function(obj) {
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
self.data('urlDuplicatewithchildren'), obj.data('id')
));
$('.cms-container').entwine('.ss').loadPanel(
$.path.addSearchParams(
ss.i18n.sprintf(self.data('urlDuplicatewithchildren'), obj.data('id')),
self.data('extraParams')
)
);
}
}
]

View File

@ -8,6 +8,7 @@
*/
$.entwine('ss', function($){
/**
* Class: #Form_VersionsForm
*
@ -19,24 +20,6 @@
* Constructor
*/
onmatch: function() {
var self = this;
/**
* Event: :input[name=ShowUnpublished] change
*
* Changing the show unpublished checkbox toggles whether to show
* or hide the unpublished versions. Because those rows may be being
* compared this also ensures those rows are unselected.
*/
this.find(':input[name=ShowUnpublished]').bind('change', function(e) {
if($(this).attr("checked")) {
self.find("tr[data-published=false]").show();
}
else {
self.find("tr[data-published=false]").hide()._unselect();
}
});
this._super();
},
onunmatch: function() {
@ -83,6 +66,41 @@
}
});
/**
* Class: :input[name=ShowUnpublished]
*
* Used for toggling whether to show or hide unpublished versions.
*/
$('#Form_VersionsForm input[name=ShowUnpublished]').entwine({
onmatch: function() {
this.toggle();
this._super();
},
onunmatch: function() {
this._super();
},
/**
* Event: :input[name=ShowUnpublished] change
*
* Changing the show unpublished checkbox toggles whether to show
* or hide the unpublished versions. Because those rows may be being
* compared this also ensures those rows are unselected.
*/
onchange: function() {
this.toggle();
},
toggle: function() {
var self = $(this);
var form = self.parents('form');
if(self.attr('checked')) {
form.find('tr[data-published=false]').show();
} else {
form.find("tr[data-published=false]").hide()._unselect();
}
}
});
/**
* Class: #Form_VersionsForm tr
*

View File

@ -101,8 +101,8 @@ en:
RESTORE: Restore
RESTORED: 'Restored ''{title}'' successfully'
ROLLBACK: 'Roll back to this version'
ROLLEDBACKPUB: 'Rolled back to published version. New version number is #{version}'
ROLLEDBACKVERSION: 'Rolled back to version #%d. New version number is #%d'
ROLLEDBACKPUBv2: 'Rolled back to published version.'
ROLLEDBACKVERSIONv2: 'Rolled back to version #%d.'
SAVE: Save
SAVEDRAFT: 'Save Draft'
TabContent: Content

54
lang/lc_XX.yml Normal file
View File

@ -0,0 +1,54 @@
lc_XX:
ContentController:
DRAFT_SITE_ACCESS_RESTRICTION: "U MUST LOG IN WIF UR CMS PASWORD IN ORDR 2 VIEW TEH DRAFT OR ARCHIVD CONTENT. <a href=\"%s\">CLICK HEAR 2 GO BAK 2 TEH PUBLISHD SIET.</a>"
ErrorPage:
CODE: "TEH ERRUR CODE"
Folder:
UNUSEDFILESTITLE: "UNUSD FILEZ"
RedirectorPage:
HASBEENSETUP: "A REDIRECTOR PAEG HAS BEEN SET UP WITHOUT ANYWHERE 2 REDIRECT 2."
HEADER: "DIS PAEG WILL REDIRECT USERS 2 ANOTHR PAEG"
OTHERURL: "UDDR WEBSITEZ URL"
REDIRECTTO: "REDIRECT 2"
REDIRECTTOEXTERNAL: "ANOTHR WEBSIET"
REDIRECTTOPAGE: "A PAEG ON UR WEBSEIT"
YOURPAGE: "PAEG ON UR WEBSEIT"
SiteTree:
ACCESSANYONE: "ANY1"
ACCESSHEADER: "WAT PEEPS CAN C DAT PAEG ON MY SITE?"
ACCESSLOGGEDIN: "LOGGD-IN USERS"
ACCESSONLYTHESE: "ONLY THEES PEEPS (CHOOSE FRUM LIST)"
ALLOWCOMMENTS: "ALLOW COMMENTZ ON DIS PAEG?"
APPEARSVIRTUALPAGES: "DIS CONTENT ALSO APPEARz ON TEH VIRTUAL PAGEZ IN DA %s SECSHUNS."
BUTTONCANCELDRAFT: "CANCEL DRAFT CHANGEZ"
BUTTONCANCELDRAFTDESC: "DELETE UR DRAFT AN REVERT 2 TEH CURRENTLY PUBLISHD PAEG"
BUTTONSAVEPUBLISH: "SAV N PUBLISH"
BUTTONUNPUBLISH: "UNPUBLISH"
BUTTONUNPUBLISHDESC: "REMOOV DIS PAEG FRUM TEH PUBLISHD SIET"
EDITANYONE: "ANYONE HOO CAN LOG-IN 2 TEH CMS"
EDITHEADER: "WAT PEEPS CAN EDIT DIS INSIDE TEH CMS?"
EDITONLYTHESE: "ONLY THEEZ PEEPS (CHOOSE FRUM LIST)"
HASBROKENLINKS: |
DIS PAEG HAS BROKD LINKZ.
HTMLEDITORTITLE: "TEH CONTENT"
MENUTITLE: "NAVIGASHUN LABEL"
METADESC: "DESCRIPSHUN"
METAEXTRA: "CUSTOM META TAGZ"
METAKEYWORDS: "KEYWURDZ"
METATITLE: "TITLE"
PAGETITLE: "NAYM OV TEH PAEG"
PAGETYPE: "TYPE OV TEH PAEG"
SHOWINMENUS: "SHOU IN MENUZ?"
SHOWINSEARCH: "SHOU IN SEARCH?"
TABACCESS: "ACCESZ"
TABBEHAVIOUR: "BEHAVIOUR"
TABCONTENT: "CONTENT"
TABMETA: "META-DATA"
TOPLEVEL: |
SIET CONTENT (TOP LEVEL)
VirtualPage:
CHOOSE: "CHOOSE PAEG 2 LINK 2"
EDITCONTENT: "kLICK HEER 2 EDIT TEH CONTENT"
HEADER: "DIS R A VIRTUAL PAEG"

View File

@ -1,5 +1,5 @@
<% if Pages %>
<% loop Pages %>
<% if Last %>$Title.XML<% else %><a href="$Link">$MenuTitle.XML</a> &raquo;<% end_if %>
<% if Last %>$MenuTitle.XML<% else %><a href="$Link" class="breadcrumb-$Pos">$MenuTitle.XML</a> &raquo;<% end_if %>
<% end_loop %>
<% end_if %>
<% end_if %>

View File

@ -19,7 +19,7 @@ $ExtraTreeTools
</div>
<% end_if %>
<div class="cms-tree" data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" data-url-updatetreenodes="$Link(updatetreenodes)" data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1')}&amp;ParentID=%s&amp;PageType=%s&amp;SecurityID=$SecurityID" data-url-editpage="$LinkPageEdit('%s')" data-url-duplicate="{$Link('duplicate/%s')}?SecurityID=$SecurityID" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s')}?SecurityID=$SecurityID" data-url-listview="{$Link('?view=list')}" data-hints="$SiteTreeHints.XML">
<div class="cms-tree" data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" data-url-updatetreenodes="$Link(updatetreenodes)" data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1')}&amp;ParentID=%s&amp;PageType=%s" data-url-editpage="$LinkPageEdit('%s')" data-url-duplicate="{$Link('duplicate/%s')}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s')}" data-url-listview="{$Link('?view=list')}" data-hints="$SiteTreeHints.XML" data-extra-params="SecurityID=$SecurityID">
$SiteTreeAsUL
</div>
</div>

View File

@ -52,7 +52,7 @@ class CMSPageHistoryControllerTest extends FunctionalTest {
$this->assertEquals($this->versionPublishCheck2, $form->Fields()->dataFieldByName('Version')->Value());
$this->assertContains(
sprintf("Currently viewing version %s.", $this->versionPublishCheck2),
'Currently viewing the latest version',
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
);

View File

@ -554,6 +554,7 @@ class SiteTreeTest extends SapphireTest {
public function testLinkShortcodeHandler() {
$aboutPage = $this->objFromFixture('Page', 'about');
$errorPage = $this->objFromFixture('ErrorPage', '404');
$redirectPage = $this->objFromFixture('RedirectorPage', 'external');
$parser = new ShortcodeParser();
$parser->register('sitetree_link', array('SiteTree', 'link_shortcode_handler'));
@ -580,6 +581,13 @@ class SiteTreeTest extends SapphireTest {
$this->assertEquals($aboutShortcodeExpected, $parser->parse($aboutShortcode), 'Test link to 404 page if no suitable matches.');
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed));
$redirectShortcode = sprintf('[sitetree_link,id=%d]', $redirectPage->ID);
$redirectEnclosed = sprintf('[sitetree_link,id=%d]Example Content[/sitetree_link]', $redirectPage->ID);
$redirectExpected = 'http://www.google.com?a&amp;b';
$this->assertEquals($redirectExpected, $parser->parse($redirectShortcode));
$this->assertEquals(sprintf('<a href="%s">Example Content</a>', $redirectExpected), $parser->parse($redirectEnclosed));
$this->assertEquals('', $parser->parse('[sitetree_link]'), 'Test that invalid ID attributes are not parsed.');
$this->assertEquals('', $parser->parse('[sitetree_link,id="text"]'));

View File

@ -79,4 +79,12 @@ SiteTreeTest_Conflicted:
ErrorPage:
404:
Title: Page not Found
ErrorCode: 404
ErrorCode: 404
RedirectorPage:
external:
Title: External
URLSegment: external
RedirectionType: External
ExternalURL: "http://www.google.com?a&b"