mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge remote-tracking branch 'origin/3.1'
Conflicts: tests/security/MemberTest.php
This commit is contained in:
commit
b56ca812dc
8
_config/requestprocessors.yml
Normal file
8
_config/requestprocessors.yml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
Name: requestprocessors
|
||||||
|
---
|
||||||
|
Injector:
|
||||||
|
RequestProcessor:
|
||||||
|
properties:
|
||||||
|
filters:
|
||||||
|
- '%$VersionedRequestFilter'
|
@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage admin
|
||||||
|
*/
|
||||||
class AdminRootController extends Controller {
|
class AdminRootController extends Controller {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
* _t('CMSBatchActions.PUBLISHED_PAGES', 'published %d pages')));
|
* _t('CMSBatchActions.PUBLISHED_PAGES', 'published %d pages')));
|
||||||
* </code>
|
* </code>
|
||||||
*
|
*
|
||||||
* @package cms
|
* @package framework
|
||||||
* @subpackage batchaction
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
abstract class CMSBatchAction extends Object {
|
abstract class CMSBatchAction extends Object {
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
/**
|
/**
|
||||||
* Special request handler for admin/batchaction
|
* Special request handler for admin/batchaction
|
||||||
*
|
*
|
||||||
* @package cms
|
* @package framework
|
||||||
* @subpackage batchaction
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
class CMSBatchActionHandler extends RequestHandler {
|
class CMSBatchActionHandler extends RequestHandler {
|
||||||
|
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deals with special form handling in CMS, mainly around {@link PjaxResponseNegotiator}
|
* Deals with special form handling in CMS, mainly around
|
||||||
|
* {@link PjaxResponseNegotiator}
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
class CMSForm extends Form {
|
class CMSForm extends Form {
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Interface to provide enough information about a record to make it previewable
|
* Interface to provide enough information about a record to make it previewable
|
||||||
* through the CMS. It uses the record database ID, its "frontend" and "backend" links
|
* through the CMS. It uses the record database ID, its "frontend" and "backend"
|
||||||
* to link up the edit form with its preview.
|
* links to link up the edit form with its preview.
|
||||||
*
|
*
|
||||||
* Also used by {@link SilverStripeNavigator} to generate links -
|
* Also used by {@link SilverStripeNavigator} to generate links - both within
|
||||||
* both within the CMS preview, and as a frontend utility
|
* the CMS preview, and as a frontend utility for logged-in CMS authors in
|
||||||
* for logged-in CMS authors in custom themes (with the $SilverStripeNavigator template marker).
|
* custom themes (with the $SilverStripeNavigator template marker).
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
interface CMSPreviewable {
|
interface CMSPreviewable {
|
||||||
|
|
||||||
@ -17,8 +20,9 @@ interface CMSPreviewable {
|
|||||||
public function Link();
|
public function Link();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return String Absolute URL to the CMS-author view. Should point to a controller subclassing {@link LeftAndMain}.
|
* @return String Absolute URL to the CMS-author view. Should point to a
|
||||||
* Example: http://mysite.com/admin/edit/6
|
* controller subclassing {@link LeftAndMain}. Example:
|
||||||
|
* http://mysite.com/admin/edit/6
|
||||||
*/
|
*/
|
||||||
public function CMSEditLink();
|
public function CMSEditLink();
|
||||||
|
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage admin
|
||||||
|
*/
|
||||||
class CMSProfileController extends LeftAndMain {
|
class CMSProfileController extends LeftAndMain {
|
||||||
|
|
||||||
private static $url_segment = 'myprofile';
|
private static $url_segment = 'myprofile';
|
||||||
@ -22,8 +27,11 @@ class CMSProfileController extends LeftAndMain {
|
|||||||
$this->setCurrentPageID(Member::currentUserID());
|
$this->setCurrentPageID(Member::currentUserID());
|
||||||
|
|
||||||
$form = parent::getEditForm($id, $fields);
|
$form = parent::getEditForm($id, $fields);
|
||||||
if($form instanceof SS_HTTPResponse) return $form;
|
|
||||||
|
|
||||||
|
if($form instanceof SS_HTTPResponse) {
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
$form->Fields()->removeByName('LastVisited');
|
$form->Fields()->removeByName('LastVisited');
|
||||||
$form->Fields()->push(new HiddenField('ID', null, Member::currentUserID()));
|
$form->Fields()->push(new HiddenField('ID', null, Member::currentUserID()));
|
||||||
$form->Actions()->push(
|
$form->Actions()->push(
|
||||||
@ -32,11 +40,21 @@ class CMSProfileController extends LeftAndMain {
|
|||||||
->setAttribute('data-icon', 'accept')
|
->setAttribute('data-icon', 'accept')
|
||||||
->setUseButtonTag(true)
|
->setUseButtonTag(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
$form->Actions()->removeByName('action_delete');
|
$form->Actions()->removeByName('action_delete');
|
||||||
$form->setValidator(new Member_Validator());
|
|
||||||
$form->setTemplate('Form');
|
$form->setTemplate('Form');
|
||||||
$form->setAttribute('data-pjax-fragment', null);
|
$form->setAttribute('data-pjax-fragment', null);
|
||||||
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
|
|
||||||
|
if($member = Member::currentUser()) {
|
||||||
|
$form->setValidator($member->getValidator());
|
||||||
|
} else {
|
||||||
|
$form->setValidator(Injector::inst()->get('Member')->getValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
if($form->Fields()->hasTabset()) {
|
||||||
|
$form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
|
||||||
|
}
|
||||||
|
|
||||||
$form->addExtraClass('member-profile-form root-form cms-edit-form cms-panel-padded center');
|
$form->addExtraClass('member-profile-form root-form cms-edit-form cms-panel-padded center');
|
||||||
|
|
||||||
return $form;
|
return $form;
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports {@link Group} records by CSV upload, as defined in
|
* Imports {@link Group} records by CSV upload, as defined in
|
||||||
* {@link GroupCsvBulkLoader}.
|
* {@link GroupCsvBulkLoader}.
|
||||||
*
|
*
|
||||||
* @package cms
|
* @package framework
|
||||||
* @subpackage batchactions
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
class GroupImportForm extends Form {
|
class GroupImportForm extends Form {
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plug-ins for additional functionality in your LeftAndMain classes.
|
* Plug-ins for additional functionality in your LeftAndMain classes.
|
||||||
*
|
*
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports {@link Member} records by CSV upload, as defined in
|
* Imports {@link Member} records by CSV upload, as defined in
|
||||||
* {@link MemberCsvBulkLoader}.
|
* {@link MemberCsvBulkLoader}.
|
||||||
*
|
*
|
||||||
* @package cms
|
* @package framework
|
||||||
* @subpackage batchactions
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
class MemberImportForm extends Form {
|
class MemberImportForm extends Form {
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Security section of the CMS
|
* Security section of the CMS
|
||||||
* @package cms
|
*
|
||||||
* @subpackage security
|
* @package framework
|
||||||
|
* @subpackage admin
|
||||||
*/
|
*/
|
||||||
class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||||
|
|
||||||
@ -60,7 +62,10 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
|||||||
|
|
||||||
// TODO Duplicate record fetching (see parent implementation)
|
// TODO Duplicate record fetching (see parent implementation)
|
||||||
$record = $this->getRecord($id);
|
$record = $this->getRecord($id);
|
||||||
if($record && !$record->canView()) return Security::permissionFailure($this);
|
|
||||||
|
if($record && !$record->canView()) {
|
||||||
|
return Security::permissionFailure($this);
|
||||||
|
}
|
||||||
|
|
||||||
$memberList = GridField::create(
|
$memberList = GridField::create(
|
||||||
'Members',
|
'Members',
|
||||||
@ -70,7 +75,16 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
|||||||
->addComponent(new GridFieldButtonRow('after'))
|
->addComponent(new GridFieldButtonRow('after'))
|
||||||
->addComponent(new GridFieldExportButton('buttons-after-left'))
|
->addComponent(new GridFieldExportButton('buttons-after-left'))
|
||||||
)->addExtraClass("members_grid");
|
)->addExtraClass("members_grid");
|
||||||
$memberListConfig->getComponentByType('GridFieldDetailForm')->setValidator(new Member_Validator());
|
|
||||||
|
if($record && method_exists($record, 'getValidator')) {
|
||||||
|
$validator = $record->getValidator();
|
||||||
|
} else {
|
||||||
|
$validator = Injector::inst()->get('Member')->getValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
$memberListConfig
|
||||||
|
->getComponentByType('GridFieldDetailForm')
|
||||||
|
->setValidator($validator);
|
||||||
|
|
||||||
$groupList = GridField::create(
|
$groupList = GridField::create(
|
||||||
'Groups',
|
'Groups',
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background-color: #338DC1; background-position: -16px 6px; filter: none; }
|
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background-color: #338DC1; background-position: -16px 6px; filter: none; }
|
||||||
|
|
||||||
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
|
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
|
||||||
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: red url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
|
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: #d81b21 url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
|
||||||
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
|
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #c1181e url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
|
||||||
|
|
||||||
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
|
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
|
||||||
.cms table.ss-gridfield-table tr th { border-right: 1px solid #9a9a9a; }
|
.cms table.ss-gridfield-table tr th { border-right: 1px solid #9a9a9a; }
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background-color: #338DC1; background-position: -16px 6px; filter: none; }
|
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background-color: #338DC1; background-position: -16px 6px; filter: none; }
|
||||||
|
|
||||||
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
|
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
|
||||||
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: red url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
|
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: #d81b21 url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
|
||||||
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
|
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #c1181e url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
|
||||||
|
|
||||||
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
|
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
|
||||||
.cms table.ss-gridfield-table tr th { border-right: 1px solid #9a9a9a; }
|
.cms table.ss-gridfield-table tr th { border-right: 1px solid #9a9a9a; }
|
||||||
|
@ -217,7 +217,9 @@ form.small .field input.text, form.small .field textarea, form.small .field sele
|
|||||||
.cms .ss-ui-button.ss-ui-action-constructive { text-shadow: none; font-weight: bold; color: white; border-color: #1f9433; border-bottom-color: #166a24; background-color: #1f9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzkzYmU0MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzFmOTQzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #93be42), color-stop(100%, #1f9433)); background: -webkit-linear-gradient(#93be42, #1f9433); background: -moz-linear-gradient(#93be42, #1f9433); background: -o-linear-gradient(#93be42, #1f9433); background: linear-gradient(#93be42, #1f9433); text-shadow: #1c872f 0 -1px -1px; }
|
.cms .ss-ui-button.ss-ui-action-constructive { text-shadow: none; font-weight: bold; color: white; border-color: #1f9433; border-bottom-color: #166a24; background-color: #1f9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzkzYmU0MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzFmOTQzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #93be42), color-stop(100%, #1f9433)); background: -webkit-linear-gradient(#93be42, #1f9433); background: -moz-linear-gradient(#93be42, #1f9433); background: -o-linear-gradient(#93be42, #1f9433); background: linear-gradient(#93be42, #1f9433); text-shadow: #1c872f 0 -1px -1px; }
|
||||||
.cms .ss-ui-button.ss-ui-action-constructive.ui-state-hover, .cms .ss-ui-button.ss-ui-action-constructive:hover { border-color: #166a24; background-color: #1f9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2E0Y2EzYSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzIzYTkzYSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #a4ca3a), color-stop(100%, #23a93a)); background: -webkit-linear-gradient(#a4ca3a, #23a93a); background: -moz-linear-gradient(#a4ca3a, #23a93a); background: -o-linear-gradient(#a4ca3a, #23a93a); background: linear-gradient(#a4ca3a, #23a93a); }
|
.cms .ss-ui-button.ss-ui-action-constructive.ui-state-hover, .cms .ss-ui-button.ss-ui-action-constructive:hover { border-color: #166a24; background-color: #1f9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2E0Y2EzYSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzIzYTkzYSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #a4ca3a), color-stop(100%, #23a93a)); background: -webkit-linear-gradient(#a4ca3a, #23a93a); background: -moz-linear-gradient(#a4ca3a, #23a93a); background: -o-linear-gradient(#a4ca3a, #23a93a); background: linear-gradient(#a4ca3a, #23a93a); }
|
||||||
.cms .ss-ui-button.ss-ui-action-constructive:active, .cms .ss-ui-button.ss-ui-action-constructive:focus, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-active, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-focus { background-color: #1d8c30; -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); }
|
.cms .ss-ui-button.ss-ui-action-constructive:active, .cms .ss-ui-button.ss-ui-action-constructive:focus, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-active, .cms .ss-ui-button.ss-ui-action-constructive.ui-state-focus { background-color: #1d8c30; -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); }
|
||||||
.cms .ss-ui-button.ss-ui-action-destructive { color: red; background-color: #e6e6e6; }
|
.cms .ss-ui-button.ss-ui-action-destructive { color: #fff; text-shadow: none; border-color: #980c10; border-bottom-color: #69080b; background-color: #d81b21; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2YzM2Y0NCIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q4MWIyMSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f33f44), color-stop(100%, #d81b21)); background: -webkit-linear-gradient(#f33f44, #d81b21); background: -moz-linear-gradient(#f33f44, #d81b21); background: -o-linear-gradient(#f33f44, #d81b21); background: linear-gradient(#f33f44, #d81b21); text-shadow: #ca191f 0 -1px -1px; }
|
||||||
|
.cms .ss-ui-button.ss-ui-action-destructive.ui-state-hover, .cms .ss-ui-button.ss-ui-action-destructive:hover { border-color: #69080b; background-color: #d81b21; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Y5MzQzYSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U0MjgyZSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f9343a), color-stop(100%, #e4282e)); background: -webkit-linear-gradient(#f9343a, #e4282e); background: -moz-linear-gradient(#f9343a, #e4282e); background: -o-linear-gradient(#f9343a, #e4282e); background: linear-gradient(#f9343a, #e4282e); }
|
||||||
|
.cms .ss-ui-button.ss-ui-action-destructive:active, .cms .ss-ui-button.ss-ui-action-destructive:focus, .cms .ss-ui-button.ss-ui-action-destructive.ui-state-active, .cms .ss-ui-button.ss-ui-action-destructive.ui-state-focus { background-color: #cf1a20; -webkit-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); -moz-box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); box-shadow: inset 0 1px 3px #17181a, 0 1px 0 rgba(255, 255, 255, 0.6); }
|
||||||
.cms .ss-ui-button.ss-ui-button-small .ui-button-text { font-size: 10px; }
|
.cms .ss-ui-button.ss-ui-button-small .ui-button-text { font-size: 10px; }
|
||||||
.cms .ss-ui-button.ui-state-highlight { background-color: #e6e6e6; border: 1px solid #708284; }
|
.cms .ss-ui-button.ui-state-highlight { background-color: #e6e6e6; border: 1px solid #708284; }
|
||||||
.cms .ss-ui-button.ss-ui-action-minor { background: none; border: 0; color: #393939; text-decoration: underline; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; }
|
.cms .ss-ui-button.ss-ui-action-minor { background: none; border: 0; color: #393939; text-decoration: underline; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; }
|
||||||
@ -437,6 +439,7 @@ body.cms { overflow: hidden; }
|
|||||||
|
|
||||||
/** -------------------------------------------- Actions -------------------------------------------- */
|
/** -------------------------------------------- Actions -------------------------------------------- */
|
||||||
.cms-content-actions, .cms-preview-controls { margin: 0; padding: 12px 12px; z-index: 0; border-top: 1px solid #cacacc; -webkit-box-shadow: 1px 0 0 #eceff1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; -moz-box-shadow: 1px 0 0 #eceff1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; box-shadow: 1px 0 0 #eceff1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; height: 28px; background-color: #eceff1; }
|
.cms-content-actions, .cms-preview-controls { margin: 0; padding: 12px 12px; z-index: 0; border-top: 1px solid #cacacc; -webkit-box-shadow: 1px 0 0 #eceff1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; -moz-box-shadow: 1px 0 0 #eceff1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; box-shadow: 1px 0 0 #eceff1, rgba(248, 248, 248, 0.9) 0 1px 0px inset, rgba(201, 205, 206, 0.8) 0 0 1px; height: 28px; background-color: #eceff1; }
|
||||||
|
.cms-content-actions .ss-ui-action-destructive, .cms-preview-controls .ss-ui-action-destructive { float: right; margin-left: 8px; }
|
||||||
|
|
||||||
/** -------------------------------------------- Messages -------------------------------------------- */
|
/** -------------------------------------------- Messages -------------------------------------------- */
|
||||||
.message { display: block; clear: both; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #ccc solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -webkit-border-radius: 3px 3px 3px 3px; -moz-border-radius: 3px 3px 3px 3px; -ms-border-radius: 3px 3px 3px 3px; -o-border-radius: 3px 3px 3px 3px; border-radius: 3px 3px 3px 3px; }
|
.message { display: block; clear: both; margin: 0 0 8px; padding: 10px 12px; font-weight: normal; border: 1px #ccc solid; background: #fff; background: rgba(255, 255, 255, 0.5); text-shadow: none; -webkit-border-radius: 3px 3px 3px 3px; -moz-border-radius: 3px 3px 3px 3px; -ms-border-radius: 3px 3px 3px 3px; -o-border-radius: 3px 3px 3px 3px; border-radius: 3px 3px 3px 3px; }
|
||||||
@ -513,10 +516,10 @@ body.cms { overflow: hidden; }
|
|||||||
.cms-content-tools .field { /* Fields are more compressed in the sidebar compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ }
|
.cms-content-tools .field { /* Fields are more compressed in the sidebar compared to the main content editing window so the below alters the internal spacing of the fields so we can move that spacing to between the form fields rather than padding */ }
|
||||||
.cms-content-tools .field label { float: none; width: auto; font-size: 11px; padding: 0 8px 4px 0; }
|
.cms-content-tools .field label { float: none; width: auto; font-size: 11px; padding: 0 8px 4px 0; }
|
||||||
.cms-content-tools .field .middleColumn { margin: 0; }
|
.cms-content-tools .field .middleColumn { margin: 0; }
|
||||||
.cms-content-tools .field .description { margin-left: 0; }
|
|
||||||
.cms-content-tools .field input.text, .cms-content-tools .field select, .cms-content-tools .field textarea { padding: 5px; font-size: 11px; }
|
.cms-content-tools .field input.text, .cms-content-tools .field select, .cms-content-tools .field textarea { padding: 5px; font-size: 11px; }
|
||||||
.cms-content-tools .field.checkbox { padding: 0 0 8px; }
|
.cms-content-tools .field.checkbox { padding: 0 0 8px; }
|
||||||
.cms-content-tools .field.checkbox input { margin: 2px 0; }
|
.cms-content-tools .field.checkbox input { margin: 2px 0; }
|
||||||
|
.cms-content-tools .field .description { margin-left: 0; }
|
||||||
.cms-content-tools .fieldgroup .fieldgroup-field { padding: 0; }
|
.cms-content-tools .fieldgroup .fieldgroup-field { padding: 0; }
|
||||||
.cms-content-tools .fieldgroup .fieldgroup-field .field { margin: 0; padding: 0; }
|
.cms-content-tools .fieldgroup .fieldgroup-field .field { margin: 0; padding: 0; }
|
||||||
.cms-content-tools table { margin: 8px -4px; }
|
.cms-content-tools table { margin: 8px -4px; }
|
||||||
|
@ -434,8 +434,34 @@ form.small .field, .field.small {
|
|||||||
|
|
||||||
/* destructive */
|
/* destructive */
|
||||||
&.ss-ui-action-destructive {
|
&.ss-ui-action-destructive {
|
||||||
color: $color-button-destructive;
|
color: #fff;
|
||||||
background-color: $color-button-generic;
|
text-shadow:none;
|
||||||
|
border-color: $color-button-destructive-border;
|
||||||
|
border-bottom-color: darken($color-button-destructive-border, 10%);
|
||||||
|
background-color: $color-button-destructive;
|
||||||
|
@include background(
|
||||||
|
linear-gradient(color-stops(
|
||||||
|
scale-color(lighten($color-button-destructive, 10%), $red:50%),
|
||||||
|
$color-button-destructive
|
||||||
|
))
|
||||||
|
);
|
||||||
|
@include text-shadow(darken($color-button-destructive, 3%) 0 -1px -1px);
|
||||||
|
|
||||||
|
&.ui-state-hover, &:hover {
|
||||||
|
border-color: darken($color-button-destructive-border, 10%);
|
||||||
|
background-color: $color-button-destructive;
|
||||||
|
@include background(
|
||||||
|
linear-gradient(color-stops(
|
||||||
|
scale-color(saturate(lighten($color-button-destructive, 10%), 10%), $red:60%),
|
||||||
|
lighten($color-button-destructive, 5%)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active, &:focus, &.ui-state-active, &.ui-state-focus {
|
||||||
|
background-color: darken($color-button-destructive, 2%);
|
||||||
|
@include box-shadow(inset 0 1px 3px rgb(23, 24, 26), 0 1px 0 rgba(255, 255, 255, .6));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ss-ui-button-small {
|
&.ss-ui-button-small {
|
||||||
|
@ -426,6 +426,11 @@ body.cms {
|
|||||||
$color-shadow-light 0 0 1px);
|
$color-shadow-light 0 0 1px);
|
||||||
height: 28px;
|
height: 28px;
|
||||||
background-color: $tab-panel-texture-color;
|
background-color: $tab-panel-texture-color;
|
||||||
|
|
||||||
|
.ss-ui-action-destructive {
|
||||||
|
float: right;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,7 +56,8 @@ $color-button-highlight-border: #708284 !default;
|
|||||||
$color-button-constructive: #1F9433 !default;
|
$color-button-constructive: #1F9433 !default;
|
||||||
$color-button-constructive-border: #1F9433 !default;
|
$color-button-constructive-border: #1F9433 !default;
|
||||||
|
|
||||||
$color-button-destructive: #f00 !default;
|
$color-button-destructive: #d81b21 !default;
|
||||||
|
$color-button-destructive-border: #980c10 !default;
|
||||||
|
|
||||||
$color-button-disabled: #eeeded !default;
|
$color-button-disabled: #eeeded !default;
|
||||||
|
|
||||||
|
@ -614,6 +614,6 @@ class Session {
|
|||||||
*/
|
*/
|
||||||
public static function get_timeout() {
|
public static function get_timeout() {
|
||||||
Deprecation::notice('3.2', 'Use the "Session.timeout" config setting instead');
|
Deprecation::notice('3.2', 'Use the "Session.timeout" config setting instead');
|
||||||
return Config::inst()->update('Session', 'timeout');
|
return Config::inst()->get('Session', 'timeout');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
control/VersionedRequestFilter.php
Normal file
17
control/VersionedRequestFilter.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Initialises the versioned stage when a request is made.
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage control
|
||||||
|
*/
|
||||||
|
class VersionedRequestFilter {
|
||||||
|
|
||||||
|
public function preRequest() {
|
||||||
|
Versioned::choose_site_stage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function postRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -470,12 +470,21 @@ abstract class Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return TRUE if a class has a specified extension
|
* Return TRUE if a class has a specified extension.
|
||||||
*
|
* This supports backwards-compatible format (static Object::has_extension($requiredExtension)) and new format ($object->has_extension($class, $requiredExtension))
|
||||||
* @param string $requiredExtension the class name of the extension to check for.
|
* @param string $classOrExtension if 1 argument supplied, the class name of the extension to check for; if 2 supplied, the class name to test
|
||||||
|
* @param string $requiredExtension used only if 2 arguments supplied
|
||||||
*/
|
*/
|
||||||
public static function has_extension($requiredExtension) {
|
public static function has_extension($classOrExtension, $requiredExtension = null) {
|
||||||
$class = get_called_class();
|
//BC support
|
||||||
|
if(func_num_args() > 1){
|
||||||
|
$class = $classOrExtension;
|
||||||
|
$requiredExtension = $requiredExtension;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$class = get_called_class();
|
||||||
|
$requiredExtension = $classOrExtension;
|
||||||
|
}
|
||||||
|
|
||||||
$requiredExtension = strtolower($requiredExtension);
|
$requiredExtension = strtolower($requiredExtension);
|
||||||
$extensions = Config::inst()->get($class, 'extensions');
|
$extensions = Config::inst()->get($class, 'extensions');
|
||||||
|
@ -30,10 +30,10 @@ Used in side panels and action tabs
|
|||||||
.cms .ss-gridfield .ss-gridfield-buttonrow { font-size: 14.4px; }
|
.cms .ss-gridfield .ss-gridfield-buttonrow { font-size: 14.4px; }
|
||||||
.cms .ss-gridfield .grid-levelup { text-indent: -9999em; margin-bottom: 6px; }
|
.cms .ss-gridfield .grid-levelup { text-indent: -9999em; margin-bottom: 6px; }
|
||||||
.cms .ss-gridfield .grid-levelup a.list-parent-link { background: transparent url(../images/gridfield-level-up.png) no-repeat 0 0; display: block; }
|
.cms .ss-gridfield .grid-levelup a.list-parent-link { background: transparent url(../images/gridfield-level-up.png) no-repeat 0 0; display: block; }
|
||||||
.cms .ss-gridfield .add-existing-autocompleter { width: 500px; }
|
.cms .ss-gridfield .add-existing-autocompleter span { float: left; display: -moz-inline-stack; display: inline-block; vertical-align: top; *vertical-align: auto; zoom: 1; *display: inline; }
|
||||||
.cms .ss-gridfield .add-existing-autocompleter span { display: -moz-inline-stack; display: inline-block; vertical-align: top; *vertical-align: auto; zoom: 1; *display: inline; }
|
.cms .ss-gridfield .add-existing-autocompleter input.relation-search { width: 270px; height: 32px; margin-bottom: 12px; border-top-right-radius: 0; border-bottom-right-radius: 0; }
|
||||||
.cms .ss-gridfield .add-existing-autocompleter input.relation-search { width: 270px; margin-bottom: 12px; }
|
.cms .ss-gridfield .add-existing-autocompleter button#action_gridfield_relationadd { height: 32px; margin-left: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: none; }
|
||||||
.cms .ss-gridfield .grid-csv-button, .cms .ss-gridfield .grid-print-button { font-size: 12px; margin-bottom: 0; display: -moz-inline-stack; display: inline-block; vertical-align: middle; *vertical-align: auto; zoom: 1; *display: inline; }
|
.cms .ss-gridfield .grid-csv-button, .cms .ss-gridfield .grid-print-button { margin-bottom: 0; font-size: 12px; display: -moz-inline-stack; display: inline-block; vertical-align: middle; *vertical-align: auto; zoom: 1; *display: inline; }
|
||||||
.cms table.ss-gridfield-table { display: table; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; padding: 0; border-collapse: separate; border-bottom: 0 none; width: 100%; }
|
.cms table.ss-gridfield-table { display: table; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; padding: 0; border-collapse: separate; border-bottom: 0 none; width: 100%; }
|
||||||
.cms table.ss-gridfield-table thead { color: #323e46; background: transparent; }
|
.cms table.ss-gridfield-table thead { color: #323e46; background: transparent; }
|
||||||
.cms table.ss-gridfield-table thead tr.filter-header .fieldgroup { max-width: 512px; }
|
.cms table.ss-gridfield-table thead tr.filter-header .fieldgroup { max-width: 512px; }
|
||||||
|
@ -23,19 +23,7 @@
|
|||||||
* @subpackage dev
|
* @subpackage dev
|
||||||
*/
|
*/
|
||||||
class Debug {
|
class Debug {
|
||||||
|
|
||||||
/**
|
|
||||||
* @config
|
|
||||||
* @var string Email address to send error notifications
|
|
||||||
*/
|
|
||||||
private static $send_errors_to;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @config
|
|
||||||
* @var string Email address to send warning notifications
|
|
||||||
*/
|
|
||||||
private static $send_warnings_to;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
* @var String indicating the file where errors are logged.
|
* @var String indicating the file where errors are logged.
|
||||||
@ -262,18 +250,6 @@ class Debug {
|
|||||||
if(error_reporting() == 0) return;
|
if(error_reporting() == 0) return;
|
||||||
ini_set('display_errors', 0);
|
ini_set('display_errors', 0);
|
||||||
|
|
||||||
if(Config::inst()->get('Debug', 'send_warnings_to')) {
|
|
||||||
return self::emailError(
|
|
||||||
Config::inst()->get('Debug', 'send_warnings_to'),
|
|
||||||
$errno,
|
|
||||||
$errstr,
|
|
||||||
$errfile,
|
|
||||||
$errline,
|
|
||||||
$errcontext,
|
|
||||||
"Warning"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out the error details to the logger for writing
|
// Send out the error details to the logger for writing
|
||||||
SS_Log::log(
|
SS_Log::log(
|
||||||
array(
|
array(
|
||||||
@ -286,10 +262,6 @@ class Debug {
|
|||||||
SS_Log::WARN
|
SS_Log::WARN
|
||||||
);
|
);
|
||||||
|
|
||||||
if(Config::inst()->get('Debug', 'log_errors_to')) {
|
|
||||||
self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Director::isDev()) {
|
if(Director::isDev()) {
|
||||||
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning");
|
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning");
|
||||||
} else {
|
} else {
|
||||||
@ -310,13 +282,6 @@ class Debug {
|
|||||||
*/
|
*/
|
||||||
public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) {
|
public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||||
ini_set('display_errors', 0);
|
ini_set('display_errors', 0);
|
||||||
|
|
||||||
if(Config::inst()->get('Debug', 'send_errors_to')) {
|
|
||||||
self::emailError(
|
|
||||||
Config::inst()->get('Debug', 'send_errors_to'), $errno,
|
|
||||||
$errstr, $errfile, $errline, $errcontext, "Error"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out the error details to the logger for writing
|
// Send out the error details to the logger for writing
|
||||||
SS_Log::log(
|
SS_Log::log(
|
||||||
@ -330,10 +295,6 @@ class Debug {
|
|||||||
SS_Log::ERR
|
SS_Log::ERR
|
||||||
);
|
);
|
||||||
|
|
||||||
if(Config::inst()->get('Debug', 'log_errors_to')) {
|
|
||||||
self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Error");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Director::isDev() || Director::is_cli()) {
|
if(Director::isDev() || Director::is_cli()) {
|
||||||
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error");
|
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error");
|
||||||
} else {
|
} else {
|
||||||
|
@ -653,7 +653,7 @@ class InstallRequirements {
|
|||||||
|
|
||||||
// special case for display_errors, check the original value before
|
// special case for display_errors, check the original value before
|
||||||
// it was changed at the start of this script.
|
// it was changed at the start of this script.
|
||||||
if($settingName = 'display_errors') {
|
if($settingName == 'display_errors') {
|
||||||
global $originalDisplayErrorsValue;
|
global $originalDisplayErrorsValue;
|
||||||
$val = $originalDisplayErrorsValue;
|
$val = $originalDisplayErrorsValue;
|
||||||
} else {
|
} else {
|
||||||
|
@ -212,6 +212,17 @@ the PHP version of the function.
|
|||||||
|
|
||||||
// Using injection to add variables into the translated strings (note that $Name and $Greeting must be available in the current template scope).
|
// Using injection to add variables into the translated strings (note that $Name and $Greeting must be available in the current template scope).
|
||||||
<%t Header.Greeting "Hello {name} {greeting}" name=$Name greeting=$Greeting %>
|
<%t Header.Greeting "Hello {name} {greeting}" name=$Name greeting=$Greeting %>
|
||||||
|
|
||||||
|
#### Caching in Template Files with locale switching
|
||||||
|
|
||||||
|
When caching a `<% loop %>` or `<% with %>` with `<%t params %>`. It is important to add the Locale to the cache key otherwise it won't pick up locale changes.
|
||||||
|
|
||||||
|
::::ss
|
||||||
|
<% cached 'MyIdentifier', $CurrentLocale %>
|
||||||
|
<% loop $Students %>
|
||||||
|
$Name
|
||||||
|
<% end_loop %>
|
||||||
|
<% end_cached %>
|
||||||
|
|
||||||
## Collecting text
|
## Collecting text
|
||||||
|
|
||||||
|
@ -181,6 +181,7 @@ class Email extends ViewableData {
|
|||||||
'filename' => $filename,
|
'filename' => $filename,
|
||||||
'mimetype' => $mimetype,
|
'mimetype' => $mimetype,
|
||||||
);
|
);
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBounceHandlerURL($bounceHandlerURL) {
|
public function setBounceHandlerURL($bounceHandlerURL) {
|
||||||
@ -195,6 +196,7 @@ class Email extends ViewableData {
|
|||||||
} else {
|
} else {
|
||||||
user_error("Could not attach '$absoluteFileName' to email. File does not exist.", E_USER_NOTICE);
|
user_error("Could not attach '$absoluteFileName' to email. File does not exist.", E_USER_NOTICE);
|
||||||
}
|
}
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Subject() {
|
public function Subject() {
|
||||||
@ -223,26 +225,32 @@ class Email extends ViewableData {
|
|||||||
|
|
||||||
public function setSubject($val) {
|
public function setSubject($val) {
|
||||||
$this->subject = $val;
|
$this->subject = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBody($val) {
|
public function setBody($val) {
|
||||||
$this->body = $val;
|
$this->body = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTo($val) {
|
public function setTo($val) {
|
||||||
$this->to = $val;
|
$this->to = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setFrom($val) {
|
public function setFrom($val) {
|
||||||
$this->from = $val;
|
$this->from = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCc($val) {
|
public function setCc($val) {
|
||||||
$this->cc = $val;
|
$this->cc = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBcc($val) {
|
public function setBcc($val) {
|
||||||
$this->bcc = $val;
|
$this->bcc = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -251,6 +259,7 @@ class Email extends ViewableData {
|
|||||||
*/
|
*/
|
||||||
public function replyTo($email) {
|
public function replyTo($email) {
|
||||||
$this->addCustomHeader('Reply-To', $email);
|
$this->addCustomHeader('Reply-To', $email);
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -267,6 +276,7 @@ class Email extends ViewableData {
|
|||||||
if(isset($this->customHeaders[$headerName])) $this->customHeaders[$headerName] .= ", " . $headerValue;
|
if(isset($this->customHeaders[$headerName])) $this->customHeaders[$headerName] .= ", " . $headerValue;
|
||||||
else $this->customHeaders[$headerName] = $headerValue;
|
else $this->customHeaders[$headerName] = $headerValue;
|
||||||
}
|
}
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function BaseURL() {
|
public function BaseURL() {
|
||||||
@ -295,6 +305,7 @@ class Email extends ViewableData {
|
|||||||
*/
|
*/
|
||||||
public function setTemplate($template) {
|
public function setTemplate($template) {
|
||||||
$this->ss_template = $template;
|
$this->ss_template = $template;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -340,6 +351,8 @@ class Email extends ViewableData {
|
|||||||
$this->template_data = $this->customise($data);
|
$this->template_data = $this->customise($data);
|
||||||
}
|
}
|
||||||
$this->parseVariables_done = false;
|
$this->parseVariables_done = false;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -375,6 +388,8 @@ class Email extends ViewableData {
|
|||||||
$this->body = HTTP::absoluteURLs($fullBody);
|
$this->body = HTTP::absoluteURLs($fullBody);
|
||||||
}
|
}
|
||||||
Config::inst()->update('SSViewer', 'source_file_comments', $origState);
|
Config::inst()->update('SSViewer', 'source_file_comments', $origState);
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -251,8 +251,12 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
// If $data is a DataObject, don't use the value, since it's a hashed value
|
// If $data is a DataObject, don't use the value, since it's a hashed value
|
||||||
if ($data && $data instanceof DataObject) $value = '';
|
if ($data && $data instanceof DataObject) $value = '';
|
||||||
|
|
||||||
|
//store this for later
|
||||||
|
$oldValue = $this->value;
|
||||||
|
|
||||||
if(is_array($value)) {
|
if(is_array($value)) {
|
||||||
if($value['_Password'] || (!$value['_Password'] && !$this->canBeEmpty)) {
|
//only set the value if it's valid!
|
||||||
|
if($this->validate(RequiredFields::create())) {
|
||||||
$this->value = $value['_Password'];
|
$this->value = $value['_Password'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,11 +270,14 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->children->fieldByName($this->getName() . '[_Password]')
|
//looking up field by name is expensive, so lets check it needs to change
|
||||||
->setValue($this->value);
|
if ($oldValue != $this->value) {
|
||||||
|
$this->children->fieldByName($this->getName() . '[_Password]')
|
||||||
|
->setValue($this->value);
|
||||||
|
|
||||||
$this->children->fieldByName($this->getName() . '[_ConfirmPassword]')
|
$this->children->fieldByName($this->getName() . '[_ConfirmPassword]')
|
||||||
->setValue($this->value);
|
->setValue($this->value);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -359,7 +366,9 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
}
|
}
|
||||||
$limitRegex = '/^.' . $limit . '$/';
|
$limitRegex = '/^.' . $limit . '$/';
|
||||||
if(!empty($value) && !preg_match($limitRegex,$value)) {
|
if(!empty($value) && !preg_match($limitRegex,$value)) {
|
||||||
$validator->validationError('Password', $errorMsg,
|
$validator->validationError(
|
||||||
|
$name,
|
||||||
|
$errorMsg,
|
||||||
"validation",
|
"validation",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
@ -369,7 +378,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
if($this->requireStrongPassword) {
|
if($this->requireStrongPassword) {
|
||||||
if(!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/',$value)) {
|
if(!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/',$value)) {
|
||||||
$validator->validationError(
|
$validator->validationError(
|
||||||
'Password',
|
$name,
|
||||||
_t('Form.VALIDATIONSTRONGPASSWORD',
|
_t('Form.VALIDATIONSTRONGPASSWORD',
|
||||||
"Passwords must have at least one digit and one alphanumeric character"),
|
"Passwords must have at least one digit and one alphanumeric character"),
|
||||||
"validation",
|
"validation",
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Required Fields allows you to set which fields
|
* Required Fields allows you to set which fields need to be present before
|
||||||
* need to be present before submitting the form
|
* submitting the form. Submit an array of arguments or each field as a separate
|
||||||
* Submit an array of arguments or each field as a
|
* argument.
|
||||||
* seperate argument. Validation is performed on a name by
|
*
|
||||||
* name basis.
|
* Validation is performed on a field by field basis through
|
||||||
|
* {@link FormField::validate}.
|
||||||
*
|
*
|
||||||
* @package forms
|
* @package forms
|
||||||
* @subpackage validators
|
* @subpackage validators
|
||||||
@ -15,8 +17,8 @@ class RequiredFields extends Validator {
|
|||||||
protected $useLabels = true;
|
protected $useLabels = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pass each field to be validated as a seperate argument
|
* Pass each field to be validated as a seperate argument to the constructor
|
||||||
* to the constructor of this object. (an array of elements are ok)
|
* of this object. (an array of elements are ok).
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$required = func_get_args();
|
$required = func_get_args();
|
||||||
@ -40,9 +42,12 @@ class RequiredFields extends Validator {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all the validation from this object.
|
* Clears all the validation from this object.
|
||||||
|
*
|
||||||
|
* @return RequiredFields
|
||||||
*/
|
*/
|
||||||
public function removeValidation(){
|
public function removeValidation() {
|
||||||
$this->required = array();
|
$this->required = array();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +55,9 @@ class RequiredFields extends Validator {
|
|||||||
* Debug helper
|
* Debug helper
|
||||||
*/
|
*/
|
||||||
public function debug() {
|
public function debug() {
|
||||||
if(!is_array($this->required)) return false;
|
if(!is_array($this->required)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$result = "<ul>";
|
$result = "<ul>";
|
||||||
foreach( $this->required as $name ){
|
foreach( $this->required as $name ){
|
||||||
@ -62,25 +69,40 @@ class RequiredFields extends Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows validation of fields via specification of a php function for validation which is executed after
|
* Allows validation of fields via specification of a php function for
|
||||||
* the form is submitted
|
* validation which is executed after the form is submitted.
|
||||||
*/
|
*
|
||||||
|
* @param array $data
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public function php($data) {
|
public function php($data) {
|
||||||
$valid = true;
|
$valid = true;
|
||||||
|
|
||||||
$fields = $this->form->Fields();
|
$fields = $this->form->Fields();
|
||||||
|
|
||||||
foreach($fields as $field) {
|
foreach($fields as $field) {
|
||||||
$valid = ($field->validate($this) && $valid);
|
$valid = ($field->validate($this) && $valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->required) {
|
if($this->required) {
|
||||||
foreach($this->required as $fieldName) {
|
foreach($this->required as $fieldName) {
|
||||||
if(!$fieldName) continue;
|
if(!$fieldName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$formField = $fields->dataFieldByName($fieldName);
|
if($fieldName instanceof FormField) {
|
||||||
|
$formField = $fieldName;
|
||||||
|
$fieldName = $fieldName->getName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$formField = $fields->dataFieldByName($fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
$error = true;
|
$error = true;
|
||||||
|
|
||||||
// submitted data for file upload fields come back as an array
|
// submitted data for file upload fields come back as an array
|
||||||
$value = isset($data[$fieldName]) ? $data[$fieldName] : null;
|
$value = isset($data[$fieldName]) ? $data[$fieldName] : null;
|
||||||
|
|
||||||
if(is_array($value)) {
|
if(is_array($value)) {
|
||||||
if($formField instanceof FileField && isset($value['error']) && $value['error']) {
|
if($formField instanceof FileField && isset($value['error']) && $value['error']) {
|
||||||
$error = true;
|
$error = true;
|
||||||
@ -106,11 +128,13 @@ class RequiredFields extends Validator {
|
|||||||
if($msg = $formField->getCustomValidationMessage()) {
|
if($msg = $formField->getCustomValidationMessage()) {
|
||||||
$errorMessage = $msg;
|
$errorMessage = $msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->validationError(
|
$this->validationError(
|
||||||
$fieldName,
|
$fieldName,
|
||||||
$errorMessage,
|
$errorMessage,
|
||||||
"required"
|
"required"
|
||||||
);
|
);
|
||||||
|
|
||||||
$valid = false;
|
$valid = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,40 +144,66 @@ class RequiredFields extends Validator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add's a single required field to requiredfields stack
|
* Adds a single required field to required fields stack.
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
*
|
||||||
|
* @return RequiredFields
|
||||||
*/
|
*/
|
||||||
public function addRequiredField( $field ) {
|
public function addRequiredField($field) {
|
||||||
$this->required[$field] = $field;
|
$this->required[$field] = $field;
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeRequiredField($field) {
|
|
||||||
unset($this->required[$field]);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* allows you too add more required fields to this object after construction.
|
* Removes a required field
|
||||||
|
*
|
||||||
|
* @param string $field
|
||||||
|
*
|
||||||
|
* @return RequiredFields
|
||||||
*/
|
*/
|
||||||
public function appendRequiredFields($requiredFields){
|
public function removeRequiredField($field) {
|
||||||
$this->required = $this->required + ArrayLib::valuekey($requiredFields->getRequired());
|
unset($this->required[$field]);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add {@link RequiredField} objects together
|
||||||
|
*
|
||||||
|
* @param RequiredFields
|
||||||
|
*
|
||||||
|
* @return RequiredFields
|
||||||
|
*/
|
||||||
|
public function appendRequiredFields($requiredFields) {
|
||||||
|
$this->required = $this->required + ArrayLib::valuekey(
|
||||||
|
$requiredFields->getRequired()
|
||||||
|
);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the named field is "required".
|
* Returns true if the named field is "required".
|
||||||
* Used by FormField to return a value for FormField::Required(), to do things like show *s on the form template.
|
*
|
||||||
|
* Used by {@link FormField} to return a value for FormField::Required(),
|
||||||
|
* to do things like show *s on the form template.
|
||||||
|
*
|
||||||
|
* @param string $fieldName
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function fieldIsRequired($fieldName) {
|
public function fieldIsRequired($fieldName) {
|
||||||
return isset($this->required[$fieldName]);
|
return isset($this->required[$fieldName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getter function for append
|
* Return the required fields
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getRequired(){
|
public function getRequired() {
|
||||||
return array_values($this->required);
|
return array_values($this->required);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig {
|
|||||||
|
|
||||||
$this->addComponent(new GridFieldButtonRow('before'));
|
$this->addComponent(new GridFieldButtonRow('before'));
|
||||||
$this->addComponent(new GridFieldAddNewButton('buttons-before-left'));
|
$this->addComponent(new GridFieldAddNewButton('buttons-before-left'));
|
||||||
$this->addComponent(new GridFieldAddExistingAutocompleter('buttons-before-left'));
|
$this->addComponent(new GridFieldAddExistingAutocompleter('buttons-before-right'));
|
||||||
$this->addComponent(new GridFieldToolbarHeader());
|
$this->addComponent(new GridFieldToolbarHeader());
|
||||||
$this->addComponent($sort = new GridFieldSortableHeader());
|
$this->addComponent($sort = new GridFieldSortableHeader());
|
||||||
$this->addComponent($filter = new GridFieldFilterHeader());
|
$this->addComponent($filter = new GridFieldFilterHeader());
|
||||||
|
@ -349,7 +349,8 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
|||||||
if($canDelete) {
|
if($canDelete) {
|
||||||
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
|
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
|
||||||
->setUseButtonTag(true)
|
->setUseButtonTag(true)
|
||||||
->addExtraClass('ss-ui-action-destructive action-delete'));
|
->addExtraClass('ss-ui-action-destructive action-delete')
|
||||||
|
->setAttribute('data-icon', 'delete'));
|
||||||
}
|
}
|
||||||
|
|
||||||
}else{ // adding new record
|
}else{ // adding new record
|
||||||
|
@ -637,6 +637,13 @@ class i18nTextCollector_Parser extends SSTemplateParser {
|
|||||||
|
|
||||||
private static $currentEntity = array();
|
private static $currentEntity = array();
|
||||||
|
|
||||||
|
public function __construct($string) {
|
||||||
|
$this->string = $string;
|
||||||
|
$this->pos = 0;
|
||||||
|
$this->depth = 0;
|
||||||
|
$this->regexps = array();
|
||||||
|
}
|
||||||
|
|
||||||
public function Translate__construct(&$res) {
|
public function Translate__construct(&$res) {
|
||||||
self::$currentEntity = array(null,null,null); //start with empty array
|
self::$currentEntity = array(null,null,null); //start with empty array
|
||||||
}
|
}
|
||||||
|
@ -459,7 +459,8 @@
|
|||||||
if(this.height() === 0) {
|
if(this.height() === 0) {
|
||||||
text = ss.i18n._t('UploadField.Editing', "Editing ...");
|
text = ss.i18n._t('UploadField.Editing', "Editing ...");
|
||||||
this.fitHeight();
|
this.fitHeight();
|
||||||
itemInfo.find('.toggle-details-icon').addClass('opened');
|
this.addClass('opened');
|
||||||
|
itemInfo.find('.toggle-details-icon').addClass('opened');
|
||||||
status.removeClass('ui-state-success-text').removeClass('ui-state-warning-text');
|
status.removeClass('ui-state-success-text').removeClass('ui-state-warning-text');
|
||||||
iframe.find('#Form_EditForm_action_doEdit').click(function(){
|
iframe.find('#Form_EditForm_action_doEdit').click(function(){
|
||||||
itemInfo.find('label .name').text(iframe.find('#Name input').val());
|
itemInfo.find('label .name').text(iframe.find('#Name input').val());
|
||||||
@ -470,6 +471,7 @@
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.animate({height: 0}, 500);
|
this.animate({height: 0}, 500);
|
||||||
|
this.removeClass('opened');
|
||||||
itemInfo.find('.toggle-details-icon').removeClass('opened');
|
itemInfo.find('.toggle-details-icon').removeClass('opened');
|
||||||
$('div.ss-upload .ss-uploadfield-item-edit-all').removeClass('opened').find('.toggle-details-icon').removeClass('opened');
|
$('div.ss-upload .ss-uploadfield-item-edit-all').removeClass('opened').find('.toggle-details-icon').removeClass('opened');
|
||||||
if(!this.hasClass('edited')){
|
if(!this.hasClass('edited')){
|
||||||
@ -493,9 +495,11 @@
|
|||||||
});
|
});
|
||||||
$('div.ss-upload .ss-uploadfield-item-editform iframe').entwine({
|
$('div.ss-upload .ss-uploadfield-item-editform iframe').entwine({
|
||||||
onmatch: function() {
|
onmatch: function() {
|
||||||
|
var form = this.closest('.ss-uploadfield-item-editform');
|
||||||
// TODO entwine event binding doesn't work for iframes
|
// TODO entwine event binding doesn't work for iframes
|
||||||
this.load(function() {
|
this.load(function() {
|
||||||
$(this).parent().removeClass('loading');
|
$(this).parent().removeClass('loading');
|
||||||
|
if(form.hasClass('opened')) form.fitHeight();
|
||||||
});
|
});
|
||||||
this._super();
|
this._super();
|
||||||
},
|
},
|
||||||
|
@ -1289,21 +1289,7 @@ class Versioned extends DataExtension {
|
|||||||
|
|
||||||
return $list;
|
return $list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Controller $controller
|
|
||||||
*/
|
|
||||||
public function contentcontrollerInit($controller) {
|
|
||||||
self::choose_site_stage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Controller $controller
|
|
||||||
*/
|
|
||||||
public function modelascontrollerInit($controller) {
|
|
||||||
self::choose_site_stage();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $labels
|
* @param array $labels
|
||||||
*/
|
*/
|
||||||
|
@ -118,14 +118,23 @@ $gf_grid_x: 16px;
|
|||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
.add-existing-autocompleter {
|
.add-existing-autocompleter {
|
||||||
span {
|
span {
|
||||||
|
float: left;
|
||||||
@include inline-block(top);
|
@include inline-block(top);
|
||||||
}
|
}
|
||||||
input.relation-search {
|
input.relation-search {
|
||||||
width: 270px;
|
width: 270px; height: 32px;
|
||||||
margin-bottom: $gf_grid_y;
|
margin-bottom: $gf_grid_y;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
button#action_gridfield_relationadd {
|
||||||
|
height: 32px;
|
||||||
|
margin-left: 0; // Webkit needs this
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-left: none;
|
||||||
}
|
}
|
||||||
width: 500px;
|
|
||||||
}
|
}
|
||||||
.grid-csv-button, .grid-print-button {
|
.grid-csv-button, .grid-print-button {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
@ -602,8 +602,21 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link RequiredFields} instance for the Member object. This
|
||||||
|
* Validator is used when saving a {@link CMSProfileController} or added to
|
||||||
|
* any form responsible for saving a users data.
|
||||||
|
*
|
||||||
|
* To customize the required fields, add a {@link DataExtension} to member
|
||||||
|
* calling the `updateValidator()` method.
|
||||||
|
*
|
||||||
|
* @return Member_Validator
|
||||||
|
*/
|
||||||
public function getValidator() {
|
public function getValidator() {
|
||||||
return new Member_Validator();
|
$validator = Injector::inst()->create('Member_Validator');
|
||||||
|
$this->extend('updateValidator', $validator);
|
||||||
|
|
||||||
|
return $validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -615,6 +628,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
*/
|
*/
|
||||||
public static function currentUser() {
|
public static function currentUser() {
|
||||||
$id = Member::currentUserID();
|
$id = Member::currentUserID();
|
||||||
|
|
||||||
if($id) {
|
if($id) {
|
||||||
return DataObject::get_one("Member", "\"Member\".\"ID\" = $id", true, 1);
|
return DataObject::get_one("Member", "\"Member\".\"ID\" = $id", true, 1);
|
||||||
}
|
}
|
||||||
@ -1503,18 +1517,21 @@ class Member_GroupSet extends ManyManyList {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used as template to send an email saying that the password has been
|
* Class used as template to send an email saying that the password has been
|
||||||
* changed
|
* changed.
|
||||||
|
*
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
class Member_ChangePasswordEmail extends Email {
|
class Member_ChangePasswordEmail extends Email {
|
||||||
|
|
||||||
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
||||||
protected $subject = '';
|
protected $subject = '';
|
||||||
protected $ss_template = 'ChangePasswordEmail';
|
protected $ss_template = 'ChangePasswordEmail';
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->subject = _t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject');
|
|
||||||
|
$this->subject = _t('Member.SUBJECTPASSWORDCHANGED', "Your password has been changed", 'Email subject');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1522,6 +1539,7 @@ class Member_ChangePasswordEmail extends Email {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used as template to send the forgot password email
|
* Class used as template to send the forgot password email
|
||||||
|
*
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
@ -1532,18 +1550,29 @@ class Member_ForgotPasswordEmail extends Email {
|
|||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->subject = _t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject');
|
|
||||||
|
$this->subject = _t('Member.SUBJECTPASSWORDRESET', "Your password reset link", 'Email subject');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Member Validator
|
* Member Validator
|
||||||
|
*
|
||||||
|
* Custom validation for the Member object can be achieved either through an
|
||||||
|
* {@link DataExtension} on the Member object or, by specifying a subclass of
|
||||||
|
* {@link Member_Validator} through the {@link Injector} API.
|
||||||
|
*
|
||||||
|
* {@see Member::getValidator()}
|
||||||
|
*
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage security
|
* @subpackage security
|
||||||
*/
|
*/
|
||||||
class Member_Validator extends RequiredFields {
|
class Member_Validator extends RequiredFields {
|
||||||
|
|
||||||
protected $customRequired = array('FirstName', 'Email'); //, 'Password');
|
protected $customRequired = array(
|
||||||
|
'FirstName',
|
||||||
|
'Email'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1551,15 +1580,16 @@ class Member_Validator extends RequiredFields {
|
|||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$required = func_get_args();
|
$required = func_get_args();
|
||||||
|
|
||||||
if(isset($required[0]) && is_array($required[0])) {
|
if(isset($required[0]) && is_array($required[0])) {
|
||||||
$required = $required[0];
|
$required = $required[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
$required = array_merge($required, $this->customRequired);
|
$required = array_merge($required, $this->customRequired);
|
||||||
|
|
||||||
parent::__construct($required);
|
parent::__construct($required);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the submitted member data is valid (server-side)
|
* Check if the submitted member data is valid (server-side)
|
||||||
*
|
*
|
||||||
|
@ -6,6 +6,7 @@ use SilverStripe\BehatExtension\Context\SilverStripeContext,
|
|||||||
SilverStripe\BehatExtension\Context\BasicContext,
|
SilverStripe\BehatExtension\Context\BasicContext,
|
||||||
SilverStripe\BehatExtension\Context\LoginContext,
|
SilverStripe\BehatExtension\Context\LoginContext,
|
||||||
SilverStripe\BehatExtension\Context\FixtureContext,
|
SilverStripe\BehatExtension\Context\FixtureContext,
|
||||||
|
SilverStripe\BehatExtension\Context\EmailContext,
|
||||||
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
|
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
|
||||||
SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ class FeatureContext extends SilverStripeContext
|
|||||||
$this->useContext('LoginContext', new LoginContext($parameters));
|
$this->useContext('LoginContext', new LoginContext($parameters));
|
||||||
$this->useContext('CmsFormsContext', new CmsFormsContext($parameters));
|
$this->useContext('CmsFormsContext', new CmsFormsContext($parameters));
|
||||||
$this->useContext('CmsUiContext', new CmsUiContext($parameters));
|
$this->useContext('CmsUiContext', new CmsUiContext($parameters));
|
||||||
|
$this->useContext('EmailContext', new EmailContext($parameters));
|
||||||
|
|
||||||
$fixtureContext = new FixtureContext($parameters);
|
$fixtureContext = new FixtureContext($parameters);
|
||||||
$fixtureContext->setFixtureFactory($this->getFixtureFactory());
|
$fixtureContext->setFixtureFactory($this->getFixtureFactory());
|
||||||
|
22
tests/behat/features/lostpassword.feature
Normal file
22
tests/behat/features/lostpassword.feature
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@todo
|
||||||
|
Feature: Lost Password
|
||||||
|
As a site owner
|
||||||
|
I want to be able to reset my password
|
||||||
|
Using my email
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given a "member" "Admin" with "Email"="admin@test.com"
|
||||||
|
|
||||||
|
Scenario: I can request a password reset by email
|
||||||
|
Given I go to "Security/login"
|
||||||
|
When I follow "I've lost my password"
|
||||||
|
And I fill in "admin@test.com" for "Email"
|
||||||
|
And I press the "Send me the password reset link" button
|
||||||
|
Then I should see "Password reset link sent to 'admin@test.com'"
|
||||||
|
And there should be an email to "admin@test.com" titled "Your password reset link"
|
||||||
|
When I click on the "password reset link" link in the email to "admin@test.com"
|
||||||
|
Then I should see "Please enter a new password"
|
||||||
|
When I fill in "newpassword" for "New Password"
|
||||||
|
And I fill in "newpassword" for "Confirm New Password"
|
||||||
|
And I press the "Change Password" button
|
||||||
|
Then the password for "admin@test.com" should be "newpassword"
|
@ -1,4 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
class CMSProfileControllerTest extends FunctionalTest {
|
class CMSProfileControllerTest extends FunctionalTest {
|
||||||
|
|
||||||
protected static $fixture_file = 'CMSProfileControllerTest.yml';
|
protected static $fixture_file = 'CMSProfileControllerTest.yml';
|
||||||
@ -74,6 +79,11 @@ class CMSProfileControllerTest extends FunctionalTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
class CMSProfileControllerTestExtension extends DataExtension {
|
class CMSProfileControllerTestExtension extends DataExtension {
|
||||||
|
|
||||||
public function canEdit($member = null) {
|
public function canEdit($member = null) {
|
||||||
|
@ -8,35 +8,32 @@ class FulltextSearchableTest extends SapphireTest {
|
|||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->orig['File_searchable'] = File::has_extension('FulltextSearchable');
|
FulltextSearchable::enable('File');
|
||||||
|
|
||||||
// TODO This shouldn't need all arguments included
|
|
||||||
File::remove_extension('FulltextSearchable(\'"Filename","Title","Content"\')');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FulltextSearchable::enable() leaves behind remains that don't get cleaned up
|
||||||
|
* properly at the end of the test. This becomes apparent when a later test tries to
|
||||||
|
* ALTER TABLE File and add fulltext indexes with the InnoDB table type.
|
||||||
|
*/
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
// TODO This shouldn't need all arguments included
|
|
||||||
if($this->orig['File_searchable']) {
|
|
||||||
File::add_extension('FulltextSearchable(\'"Filename","Title","Content"\')');
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
|
|
||||||
|
File::remove_extension('FulltextSearchable');
|
||||||
|
Config::inst()->update('File', 'create_table_options', array('MySQLDatabase' => 'ENGINE=InnoDB'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnable() {
|
public function testEnable() {
|
||||||
FulltextSearchable::enable();
|
|
||||||
$this->assertTrue(File::has_extension('FulltextSearchable'));
|
$this->assertTrue(File::has_extension('FulltextSearchable'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEnableWithCustomClasses() {
|
public function testEnableWithCustomClasses() {
|
||||||
FulltextSearchable::enable(array('File'));
|
FulltextSearchable::enable(array('File'));
|
||||||
$this->assertTrue(File::has_extension('FulltextSearchable'));
|
$this->assertTrue(File::has_extension('FulltextSearchable'));
|
||||||
|
|
||||||
// TODO This shouldn't need all arguments included
|
File::remove_extension('FulltextSearchable');
|
||||||
File::remove_extension('FulltextSearchable(\'"Filename","Title","Content"\')');
|
|
||||||
|
|
||||||
$this->assertFalse(File::has_extension('FulltextSearchable'));
|
$this->assertFalse(File::has_extension('FulltextSearchable'));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ class MemberTest extends FunctionalTest {
|
|||||||
protected static $fixture_file = 'MemberTest.yml';
|
protected static $fixture_file = 'MemberTest.yml';
|
||||||
|
|
||||||
protected $orig = array();
|
protected $orig = array();
|
||||||
protected $local = null;
|
protected $local = null;
|
||||||
|
|
||||||
protected $illegalExtensions = array(
|
protected $illegalExtensions = array(
|
||||||
'Member' => array(
|
'Member' => array(
|
||||||
@ -42,10 +42,11 @@ class MemberTest extends FunctionalTest {
|
|||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
Member::config()->unique_identifier_field = $this->orig['Member_unique_identifier_field'];
|
Member::config()->unique_identifier_field = $this->orig['Member_unique_identifier_field'];
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException ValidationException
|
* @expectedException ValidationException
|
||||||
*/
|
*/
|
||||||
@ -720,45 +721,111 @@ class MemberTest extends FunctionalTest {
|
|||||||
$member->isLockedOut(),
|
$member->isLockedOut(),
|
||||||
"Member has been locked out too early"
|
"Member has been locked out too early"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//fail login until max login attempts is reached
|
public function testCustomMemberValidator() {
|
||||||
$member->FailedLoginCount = 0;
|
$member = $this->objFromFixture('Member', 'admin');
|
||||||
for ($i = 0; $i < $maxFailedLoginsAllowed; ++$i) {
|
|
||||||
$member->registerFailedLogin();
|
|
||||||
}
|
|
||||||
//check to see if they've been locked out
|
|
||||||
$this->assertTrue(
|
|
||||||
$member->isLockedOut(),
|
|
||||||
'Member was not locked out when max logins met'
|
|
||||||
);
|
|
||||||
|
|
||||||
//after they're locked out, need to check FailedLoginCount was reset to 0
|
$form = new MemberTest_ValidatorForm();
|
||||||
$this->assertEquals(
|
$form->loadDataFrom($member);
|
||||||
$member->FailedLoginCount,
|
|
||||||
0,
|
|
||||||
'Failed login count was not reset after lockout'
|
|
||||||
);
|
|
||||||
|
|
||||||
//test all done, unnest config
|
$validator = new Member_Validator();
|
||||||
Config::unnest();
|
$validator->setForm($form);
|
||||||
|
|
||||||
|
$pass = $validator->php(array(
|
||||||
|
'FirstName' => 'Borris',
|
||||||
|
'Email' => 'borris@silverstripe.com'
|
||||||
|
));
|
||||||
|
|
||||||
|
$fail = $validator->php(array(
|
||||||
|
'Email' => 'borris@silverstripe.com',
|
||||||
|
'Surname' => ''
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($pass, 'Validator requires on FirstName and Email');
|
||||||
|
$this->assertFalse($fail, 'Missing FirstName');
|
||||||
|
|
||||||
|
$ext = new MemberTest_ValidatorExtension();
|
||||||
|
$ext->updateValidator($validator);
|
||||||
|
|
||||||
|
$pass = $validator->php(array(
|
||||||
|
'FirstName' => 'Borris',
|
||||||
|
'Email' => 'borris@silverstripe.com'
|
||||||
|
));
|
||||||
|
|
||||||
|
$fail = $validator->php(array(
|
||||||
|
'Email' => 'borris@silverstripe.com'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertFalse($pass, 'Missing surname');
|
||||||
|
$this->assertFalse($fail, 'Missing surname value');
|
||||||
|
|
||||||
|
$fail = $validator->php(array(
|
||||||
|
'Email' => 'borris@silverstripe.com',
|
||||||
|
'Surname' => 'Silverman'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertTrue($fail, 'Passes with email and surname now (no firstname)');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
|
class MemberTest_ValidatorForm extends Form implements TestOnly {
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct(Controller::curr(), __CLASS__, new FieldList(
|
||||||
|
new TextField('Email'),
|
||||||
|
new TextField('Surname'),
|
||||||
|
new TextField('ID'),
|
||||||
|
new TextField('FirstName')
|
||||||
|
), new FieldList(
|
||||||
|
new FormAction('someAction')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
|
class MemberTest_ValidatorExtension extends DataExtension implements TestOnly {
|
||||||
|
|
||||||
|
public function updateValidator(&$validator) {
|
||||||
|
$validator->addRequiredField('Surname');
|
||||||
|
$validator->removeRequiredField('FirstName');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
class MemberTest_ViewingAllowedExtension extends DataExtension implements TestOnly {
|
class MemberTest_ViewingAllowedExtension extends DataExtension implements TestOnly {
|
||||||
|
|
||||||
public function canView($member = null) {
|
public function canView($member = null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
class MemberTest_ViewingDeniedExtension extends DataExtension implements TestOnly {
|
class MemberTest_ViewingDeniedExtension extends DataExtension implements TestOnly {
|
||||||
|
|
||||||
public function canView($member = null) {
|
public function canView($member = null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension implements TestOnly {
|
class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension implements TestOnly {
|
||||||
|
|
||||||
public function canView($member = null) {
|
public function canView($member = null) {
|
||||||
@ -775,6 +842,10 @@ class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension imp
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package framework
|
||||||
|
* @subpackage tests
|
||||||
|
*/
|
||||||
class MemberTest_PasswordValidator extends PasswordValidator {
|
class MemberTest_PasswordValidator extends PasswordValidator {
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
@ -65,14 +65,20 @@ class SSTemplateParseException extends Exception {
|
|||||||
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
||||||
* N: eats white space including newlines (using in legacy _t support)
|
* N: eats white space including newlines (using in legacy _t support)
|
||||||
*/
|
*/
|
||||||
class SSTemplateParser extends Parser {
|
class SSTemplateParser extends Parser implements TemplateParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool - Set true by SSTemplateParser::compileString if the template should include comments intended
|
* @var bool - Set true by SSTemplateParser::compileString if the template should include comments intended
|
||||||
* for debugging (template source, included files, etc)
|
* for debugging (template source, included files, etc)
|
||||||
*/
|
*/
|
||||||
protected $includeDebuggingComments = false;
|
protected $includeDebuggingComments = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the Parser constructor to change the requirement of setting a string
|
||||||
|
*/
|
||||||
|
function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
|
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
|
||||||
*/
|
*/
|
||||||
@ -1757,7 +1763,7 @@ class SSTemplateParser extends Parser {
|
|||||||
/* CacheBlockArgument:
|
/* CacheBlockArgument:
|
||||||
!( "if " | "unless " )
|
!( "if " | "unless " )
|
||||||
(
|
(
|
||||||
:DollarMarkedLookup |
|
:DollarMarkedLookup |
|
||||||
:QuotedString |
|
:QuotedString |
|
||||||
:Lookup
|
:Lookup
|
||||||
) */
|
) */
|
||||||
@ -4548,7 +4554,8 @@ class SSTemplateParser extends Parser {
|
|||||||
// non-dynamically calculated
|
// non-dynamically calculated
|
||||||
$text = preg_replace(
|
$text = preg_replace(
|
||||||
'/href\s*\=\s*\"\#/',
|
'/href\s*\=\s*\"\#/',
|
||||||
'href="\' . (Config::inst()->get(\'SSViewer\', \'rewrite_hash_links\') ? strip_tags( $_SERVER[\'REQUEST_URI\'] ) : "") .
|
'href="\' . (Config::inst()->get(\'SSViewer\', \'rewrite_hash_links\') ?' .
|
||||||
|
' strip_tags( $_SERVER[\'REQUEST_URI\'] ) : "") .
|
||||||
\'#',
|
\'#',
|
||||||
$text
|
$text
|
||||||
);
|
);
|
||||||
@ -4563,29 +4570,28 @@ class SSTemplateParser extends Parser {
|
|||||||
/**
|
/**
|
||||||
* Compiles some passed template source code into the php code that will execute as per the template source.
|
* Compiles some passed template source code into the php code that will execute as per the template source.
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @throws SSTemplateParseException
|
* @throws SSTemplateParseException
|
||||||
* @param $string The source of the template
|
* @param $string The source of the template
|
||||||
* @param string $templateName The name of the template, normally the filename the template source was loaded from
|
* @param string $templateName The name of the template, normally the filename the template source was loaded from
|
||||||
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
|
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
|
||||||
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
|
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
|
||||||
*/
|
*/
|
||||||
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
public function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
||||||
if (!trim($string)) {
|
if (!trim($string)) {
|
||||||
$code = '';
|
$code = '';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Construct a parser instance
|
parent::__construct($string);
|
||||||
$parser = new SSTemplateParser($string);
|
|
||||||
$parser->includeDebuggingComments = $includeDebuggingComments;
|
$this->includeDebuggingComments = $includeDebuggingComments;
|
||||||
|
|
||||||
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
|
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
|
||||||
// (and other encodings) properly
|
// (and other encodings) properly
|
||||||
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $parser->pos = 3;
|
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $this->pos = 3;
|
||||||
|
|
||||||
// Match the source against the parser
|
// Match the source against the parser
|
||||||
$result = $parser->match_TopTemplate();
|
$result = $this->match_TopTemplate();
|
||||||
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $parser);
|
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $this);
|
||||||
|
|
||||||
// Get the result
|
// Get the result
|
||||||
$code = $result['php'];
|
$code = $result['php'];
|
||||||
@ -4593,7 +4599,7 @@ class SSTemplateParser extends Parser {
|
|||||||
|
|
||||||
// Include top level debugging comments if desired
|
// Include top level debugging comments if desired
|
||||||
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
|
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
|
||||||
$code = $parser->includeDebuggingComments($code, $templateName);
|
$code = $this->includeDebuggingComments($code, $templateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $code;
|
return $code;
|
||||||
@ -4640,7 +4646,7 @@ class SSTemplateParser extends Parser {
|
|||||||
* @param $template - A file path that contains template source code
|
* @param $template - A file path that contains template source code
|
||||||
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
|
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
|
||||||
*/
|
*/
|
||||||
static function compileFile($template) {
|
public function compileFile($template) {
|
||||||
return self::compileString(file_get_contents($template), $template);
|
return $this->compileString(file_get_contents($template), $template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,14 +86,20 @@ class SSTemplateParseException extends Exception {
|
|||||||
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
||||||
* N: eats white space including newlines (using in legacy _t support)
|
* N: eats white space including newlines (using in legacy _t support)
|
||||||
*/
|
*/
|
||||||
class SSTemplateParser extends Parser {
|
class SSTemplateParser extends Parser implements TemplateParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool - Set true by SSTemplateParser::compileString if the template should include comments intended
|
* @var bool - Set true by SSTemplateParser::compileString if the template should include comments intended
|
||||||
* for debugging (template source, included files, etc)
|
* for debugging (template source, included files, etc)
|
||||||
*/
|
*/
|
||||||
protected $includeDebuggingComments = false;
|
protected $includeDebuggingComments = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the Parser constructor to change the requirement of setting a string
|
||||||
|
*/
|
||||||
|
function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
|
* Override the function that constructs the result arrays to also prepare a 'php' item in the array
|
||||||
*/
|
*/
|
||||||
@ -462,7 +468,7 @@ class SSTemplateParser extends Parser {
|
|||||||
CacheBlockArgument:
|
CacheBlockArgument:
|
||||||
!( "if " | "unless " )
|
!( "if " | "unless " )
|
||||||
(
|
(
|
||||||
:DollarMarkedLookup |
|
:DollarMarkedLookup |
|
||||||
:QuotedString |
|
:QuotedString |
|
||||||
:Lookup
|
:Lookup
|
||||||
)
|
)
|
||||||
@ -1018,29 +1024,28 @@ class SSTemplateParser extends Parser {
|
|||||||
/**
|
/**
|
||||||
* Compiles some passed template source code into the php code that will execute as per the template source.
|
* Compiles some passed template source code into the php code that will execute as per the template source.
|
||||||
*
|
*
|
||||||
* @static
|
|
||||||
* @throws SSTemplateParseException
|
* @throws SSTemplateParseException
|
||||||
* @param $string The source of the template
|
* @param $string The source of the template
|
||||||
* @param string $templateName The name of the template, normally the filename the template source was loaded from
|
* @param string $templateName The name of the template, normally the filename the template source was loaded from
|
||||||
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
|
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
|
||||||
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
|
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
|
||||||
*/
|
*/
|
||||||
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
public function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
||||||
if (!trim($string)) {
|
if (!trim($string)) {
|
||||||
$code = '';
|
$code = '';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Construct a parser instance
|
parent::__construct($string);
|
||||||
$parser = new SSTemplateParser($string);
|
|
||||||
$parser->includeDebuggingComments = $includeDebuggingComments;
|
$this->includeDebuggingComments = $includeDebuggingComments;
|
||||||
|
|
||||||
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
|
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
|
||||||
// (and other encodings) properly
|
// (and other encodings) properly
|
||||||
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $parser->pos = 3;
|
if(substr($string, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) $this->pos = 3;
|
||||||
|
|
||||||
// Match the source against the parser
|
// Match the source against the parser
|
||||||
$result = $parser->match_TopTemplate();
|
$result = $this->match_TopTemplate();
|
||||||
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $parser);
|
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $this);
|
||||||
|
|
||||||
// Get the result
|
// Get the result
|
||||||
$code = $result['php'];
|
$code = $result['php'];
|
||||||
@ -1048,7 +1053,7 @@ class SSTemplateParser extends Parser {
|
|||||||
|
|
||||||
// Include top level debugging comments if desired
|
// Include top level debugging comments if desired
|
||||||
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
|
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
|
||||||
$code = $parser->includeDebuggingComments($code, $templateName);
|
$code = $this->includeDebuggingComments($code, $templateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $code;
|
return $code;
|
||||||
@ -1095,7 +1100,7 @@ class SSTemplateParser extends Parser {
|
|||||||
* @param $template - A file path that contains template source code
|
* @param $template - A file path that contains template source code
|
||||||
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
|
* @return mixed|string - The php that, when executed (via include or exec) will behave as per the template source
|
||||||
*/
|
*/
|
||||||
static function compileFile($template) {
|
public function compileFile($template) {
|
||||||
return self::compileString(file_get_contents($template), $template);
|
return $this->compileString(file_get_contents($template), $template);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -614,6 +614,11 @@ class SSViewer {
|
|||||||
*/
|
*/
|
||||||
protected $includeRequirements = true;
|
protected $includeRequirements = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TemplateParser
|
||||||
|
*/
|
||||||
|
protected $parser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a template from a string instead of a .ss file
|
* Create a template from a string instead of a .ss file
|
||||||
*
|
*
|
||||||
@ -691,7 +696,9 @@ class SSViewer {
|
|||||||
* array('MySpecificPage', 'MyPage', 'Page')
|
* array('MySpecificPage', 'MyPage', 'Page')
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public function __construct($templateList) {
|
public function __construct($templateList, TemplateParser $parser = null) {
|
||||||
|
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser'));
|
||||||
|
|
||||||
// flush template manifest cache if requested
|
// flush template manifest cache if requested
|
||||||
if (isset($_GET['flush']) && $_GET['flush'] == 'all') {
|
if (isset($_GET['flush']) && $_GET['flush'] == 'all') {
|
||||||
if(Director::isDev() || Director::is_cli() || Permission::check('ADMIN')) {
|
if(Director::isDev() || Director::is_cli() || Permission::check('ADMIN')) {
|
||||||
@ -728,7 +735,25 @@ class SSViewer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the template parser that will be used in template generation
|
||||||
|
* @param \TemplateParser $parser
|
||||||
|
*/
|
||||||
|
public function setParser(TemplateParser $parser)
|
||||||
|
{
|
||||||
|
$this->parser = $parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parser that is set for template generation
|
||||||
|
* @return \TemplateParser
|
||||||
|
*/
|
||||||
|
public function getParser()
|
||||||
|
{
|
||||||
|
return $this->parser;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if at least one of the listed templates exists.
|
* Returns true if at least one of the listed templates exists.
|
||||||
*
|
*
|
||||||
@ -970,7 +995,7 @@ class SSViewer {
|
|||||||
|
|
||||||
if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited || isset($_GET['flush'])) {
|
if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited || isset($_GET['flush'])) {
|
||||||
$content = file_get_contents($template);
|
$content = file_get_contents($template);
|
||||||
$content = SSViewer::parseTemplateContent($content, $template);
|
$content = $this->parseTemplateContent($content, $template);
|
||||||
|
|
||||||
$fh = fopen($cacheFile,'w');
|
$fh = fopen($cacheFile,'w');
|
||||||
fwrite($fh, $content);
|
fwrite($fh, $content);
|
||||||
@ -983,7 +1008,7 @@ class SSViewer {
|
|||||||
// through $Content and $Layout placeholders.
|
// through $Content and $Layout placeholders.
|
||||||
foreach(array('Content', 'Layout') as $subtemplate) {
|
foreach(array('Content', 'Layout') as $subtemplate) {
|
||||||
if(isset($this->chosenTemplates[$subtemplate])) {
|
if(isset($this->chosenTemplates[$subtemplate])) {
|
||||||
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate]);
|
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate], $this->parser);
|
||||||
$subtemplateViewer->includeRequirements(false);
|
$subtemplateViewer->includeRequirements(false);
|
||||||
$subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore());
|
$subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore());
|
||||||
|
|
||||||
@ -1028,10 +1053,10 @@ class SSViewer {
|
|||||||
return $v->process($data, $arguments, $scope);
|
return $v->process($data, $arguments, $scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function parseTemplateContent($content, $template="") {
|
public function parseTemplateContent($content, $template="") {
|
||||||
return SSTemplateParser::compileString(
|
return $this->parser->compileString(
|
||||||
$content,
|
$content,
|
||||||
$template,
|
$template,
|
||||||
Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments')
|
Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1079,7 +1104,8 @@ class SSViewer {
|
|||||||
class SSViewer_FromString extends SSViewer {
|
class SSViewer_FromString extends SSViewer {
|
||||||
protected $content;
|
protected $content;
|
||||||
|
|
||||||
public function __construct($content) {
|
public function __construct($content, TemplateParser $parser = null) {
|
||||||
|
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser'));
|
||||||
$this->content = $content;
|
$this->content = $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1091,7 +1117,7 @@ class SSViewer_FromString extends SSViewer {
|
|||||||
$arguments = null;
|
$arguments = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$template = SSViewer::parseTemplateContent($this->content, "string sha1=".sha1($this->content));
|
$template = $this->parseTemplateContent($this->content, "string sha1=".sha1($this->content));
|
||||||
|
|
||||||
$tmpFile = tempnam(TEMP_FOLDER,"");
|
$tmpFile = tempnam(TEMP_FOLDER,"");
|
||||||
$fh = fopen($tmpFile, 'w');
|
$fh = fopen($tmpFile, 'w');
|
||||||
|
19
view/TemplateParser.php
Normal file
19
view/TemplateParser.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface needs to be implemented by any template parser that is used in SSViewer
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage view
|
||||||
|
*/
|
||||||
|
interface TemplateParser {
|
||||||
|
/**
|
||||||
|
* Compiles some passed template source code into the php code that will execute as per the template source.
|
||||||
|
*
|
||||||
|
* @param $string The source of the template
|
||||||
|
* @param string $templateName The name of the template, normally the filename the template source was loaded from
|
||||||
|
* @param bool $includeDebuggingComments True is debugging comments should be included in the output
|
||||||
|
* @return mixed|string The php that, when executed (via include or exec) will behave as per the template source
|
||||||
|
*/
|
||||||
|
public function compileString($string, $templateName = "", $includeDebuggingComments = false);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user