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
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class AdminRootController extends Controller {
|
||||
|
||||
/**
|
||||
|
@ -10,8 +10,8 @@
|
||||
* _t('CMSBatchActions.PUBLISHED_PAGES', 'published %d pages')));
|
||||
* </code>
|
||||
*
|
||||
* @package cms
|
||||
* @subpackage batchaction
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
abstract class CMSBatchAction extends Object {
|
||||
|
||||
|
@ -3,8 +3,8 @@
|
||||
/**
|
||||
* Special request handler for admin/batchaction
|
||||
*
|
||||
* @package cms
|
||||
* @subpackage batchaction
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class CMSBatchActionHandler extends RequestHandler {
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
<?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 {
|
||||
|
||||
|
@ -1,12 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* 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
|
||||
* to link up the edit form with its preview.
|
||||
* through the CMS. It uses the record database ID, its "frontend" and "backend"
|
||||
* links to link up the edit form with its preview.
|
||||
*
|
||||
* Also used by {@link SilverStripeNavigator} to generate links -
|
||||
* both within the CMS preview, and as a frontend utility
|
||||
* for logged-in CMS authors in custom themes (with the $SilverStripeNavigator template marker).
|
||||
* Also used by {@link SilverStripeNavigator} to generate links - both within
|
||||
* the CMS preview, and as a frontend utility for logged-in CMS authors in
|
||||
* custom themes (with the $SilverStripeNavigator template marker).
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
interface CMSPreviewable {
|
||||
|
||||
@ -17,8 +20,9 @@ interface CMSPreviewable {
|
||||
public function Link();
|
||||
|
||||
/**
|
||||
* @return String Absolute URL to the CMS-author view. Should point to a controller subclassing {@link LeftAndMain}.
|
||||
* Example: http://mysite.com/admin/edit/6
|
||||
* @return String Absolute URL to the CMS-author view. Should point to a
|
||||
* controller subclassing {@link LeftAndMain}. Example:
|
||||
* http://mysite.com/admin/edit/6
|
||||
*/
|
||||
public function CMSEditLink();
|
||||
|
||||
|
@ -1,4 +1,9 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class CMSProfileController extends LeftAndMain {
|
||||
|
||||
private static $url_segment = 'myprofile';
|
||||
@ -22,7 +27,10 @@ class CMSProfileController extends LeftAndMain {
|
||||
$this->setCurrentPageID(Member::currentUserID());
|
||||
|
||||
$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()->push(new HiddenField('ID', null, Member::currentUserID()));
|
||||
@ -32,11 +40,21 @@ class CMSProfileController extends LeftAndMain {
|
||||
->setAttribute('data-icon', 'accept')
|
||||
->setUseButtonTag(true)
|
||||
);
|
||||
|
||||
$form->Actions()->removeByName('action_delete');
|
||||
$form->setValidator(new Member_Validator());
|
||||
$form->setTemplate('Form');
|
||||
$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');
|
||||
|
||||
return $form;
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Imports {@link Group} records by CSV upload, as defined in
|
||||
* {@link GroupCsvBulkLoader}.
|
||||
*
|
||||
* @package cms
|
||||
* @subpackage batchactions
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class GroupImportForm extends Form {
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Plug-ins for additional functionality in your LeftAndMain classes.
|
||||
*
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Imports {@link Member} records by CSV upload, as defined in
|
||||
* {@link MemberCsvBulkLoader}.
|
||||
*
|
||||
* @package cms
|
||||
* @subpackage batchactions
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class MemberImportForm extends Form {
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Security section of the CMS
|
||||
* @package cms
|
||||
* @subpackage security
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage admin
|
||||
*/
|
||||
class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
|
||||
@ -60,7 +62,10 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
|
||||
// TODO Duplicate record fetching (see parent implementation)
|
||||
$record = $this->getRecord($id);
|
||||
if($record && !$record->canView()) return Security::permissionFailure($this);
|
||||
|
||||
if($record && !$record->canView()) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$memberList = GridField::create(
|
||||
'Members',
|
||||
@ -70,7 +75,16 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
|
||||
->addComponent(new GridFieldButtonRow('after'))
|
||||
->addComponent(new GridFieldExportButton('buttons-after-left'))
|
||||
)->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(
|
||||
'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-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:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; 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: #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 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-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:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; 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: #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 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.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-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.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; }
|
||||
@ -437,6 +439,7 @@ body.cms { overflow: hidden; }
|
||||
|
||||
/** -------------------------------------------- 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 .ss-ui-action-destructive, .cms-preview-controls .ss-ui-action-destructive { float: right; margin-left: 8px; }
|
||||
|
||||
/** -------------------------------------------- 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; }
|
||||
@ -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 label { float: none; width: auto; font-size: 11px; padding: 0 8px 4px 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.checkbox { padding: 0 0 8px; }
|
||||
.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 .field { margin: 0; padding: 0; }
|
||||
.cms-content-tools table { margin: 8px -4px; }
|
||||
|
@ -434,8 +434,34 @@ form.small .field, .field.small {
|
||||
|
||||
/* destructive */
|
||||
&.ss-ui-action-destructive {
|
||||
color: $color-button-destructive;
|
||||
background-color: $color-button-generic;
|
||||
color: #fff;
|
||||
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 {
|
||||
|
@ -426,6 +426,11 @@ body.cms {
|
||||
$color-shadow-light 0 0 1px);
|
||||
height: 28px;
|
||||
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-border: #1F9433 !default;
|
||||
|
||||
$color-button-destructive: #f00 !default;
|
||||
$color-button-destructive: #d81b21 !default;
|
||||
$color-button-destructive-border: #980c10 !default;
|
||||
|
||||
$color-button-disabled: #eeeded !default;
|
||||
|
||||
|
@ -614,6 +614,6 @@ class Session {
|
||||
*/
|
||||
public static function get_timeout() {
|
||||
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
|
||||
*
|
||||
* @param string $requiredExtension the class name of the extension to check for.
|
||||
* 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 $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) {
|
||||
$class = get_called_class();
|
||||
public static function has_extension($classOrExtension, $requiredExtension = null) {
|
||||
//BC support
|
||||
if(func_num_args() > 1){
|
||||
$class = $classOrExtension;
|
||||
$requiredExtension = $requiredExtension;
|
||||
}
|
||||
else {
|
||||
$class = get_called_class();
|
||||
$requiredExtension = $classOrExtension;
|
||||
}
|
||||
|
||||
$requiredExtension = strtolower($requiredExtension);
|
||||
$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 .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 .add-existing-autocompleter { width: 500px; }
|
||||
.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; margin-bottom: 12px; }
|
||||
.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 .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 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 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 { 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 thead { color: #323e46; background: transparent; }
|
||||
.cms table.ss-gridfield-table thead tr.filter-header .fieldgroup { max-width: 512px; }
|
||||
|
@ -24,18 +24,6 @@
|
||||
*/
|
||||
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
|
||||
* @var String indicating the file where errors are logged.
|
||||
@ -262,18 +250,6 @@ class Debug {
|
||||
if(error_reporting() == 0) return;
|
||||
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
|
||||
SS_Log::log(
|
||||
array(
|
||||
@ -286,10 +262,6 @@ class Debug {
|
||||
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()) {
|
||||
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning");
|
||||
} else {
|
||||
@ -311,13 +283,6 @@ class Debug {
|
||||
public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) {
|
||||
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
|
||||
SS_Log::log(
|
||||
array(
|
||||
@ -330,10 +295,6 @@ class Debug {
|
||||
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()) {
|
||||
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error");
|
||||
} else {
|
||||
|
@ -653,7 +653,7 @@ class InstallRequirements {
|
||||
|
||||
// special case for display_errors, check the original value before
|
||||
// it was changed at the start of this script.
|
||||
if($settingName = 'display_errors') {
|
||||
if($settingName == 'display_errors') {
|
||||
global $originalDisplayErrorsValue;
|
||||
$val = $originalDisplayErrorsValue;
|
||||
} else {
|
||||
|
@ -213,6 +213,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).
|
||||
<%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
|
||||
|
||||
To collect all the text in code and template files we have just to visit:
|
||||
|
@ -181,6 +181,7 @@ class Email extends ViewableData {
|
||||
'filename' => $filename,
|
||||
'mimetype' => $mimetype,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBounceHandlerURL($bounceHandlerURL) {
|
||||
@ -195,6 +196,7 @@ class Email extends ViewableData {
|
||||
} else {
|
||||
user_error("Could not attach '$absoluteFileName' to email. File does not exist.", E_USER_NOTICE);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function Subject() {
|
||||
@ -223,26 +225,32 @@ class Email extends ViewableData {
|
||||
|
||||
public function setSubject($val) {
|
||||
$this->subject = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBody($val) {
|
||||
$this->body = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setTo($val) {
|
||||
$this->to = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setFrom($val) {
|
||||
$this->from = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCc($val) {
|
||||
$this->cc = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setBcc($val) {
|
||||
$this->bcc = $val;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -251,6 +259,7 @@ class Email extends ViewableData {
|
||||
*/
|
||||
public function replyTo($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;
|
||||
else $this->customHeaders[$headerName] = $headerValue;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function BaseURL() {
|
||||
@ -295,6 +305,7 @@ class Email extends ViewableData {
|
||||
*/
|
||||
public function setTemplate($template) {
|
||||
$this->ss_template = $template;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -340,6 +351,8 @@ class Email extends ViewableData {
|
||||
$this->template_data = $this->customise($data);
|
||||
}
|
||||
$this->parseVariables_done = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -375,6 +388,8 @@ class Email extends ViewableData {
|
||||
$this->body = HTTP::absoluteURLs($fullBody);
|
||||
}
|
||||
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 && $data instanceof DataObject) $value = '';
|
||||
|
||||
//store this for later
|
||||
$oldValue = $this->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'];
|
||||
}
|
||||
|
||||
@ -266,11 +270,14 @@ class ConfirmedPasswordField extends FormField {
|
||||
}
|
||||
}
|
||||
|
||||
$this->children->fieldByName($this->getName() . '[_Password]')
|
||||
->setValue($this->value);
|
||||
//looking up field by name is expensive, so lets check it needs to change
|
||||
if ($oldValue != $this->value) {
|
||||
$this->children->fieldByName($this->getName() . '[_Password]')
|
||||
->setValue($this->value);
|
||||
|
||||
$this->children->fieldByName($this->getName() . '[_ConfirmPassword]')
|
||||
->setValue($this->value);
|
||||
$this->children->fieldByName($this->getName() . '[_ConfirmPassword]')
|
||||
->setValue($this->value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -359,7 +366,9 @@ class ConfirmedPasswordField extends FormField {
|
||||
}
|
||||
$limitRegex = '/^.' . $limit . '$/';
|
||||
if(!empty($value) && !preg_match($limitRegex,$value)) {
|
||||
$validator->validationError('Password', $errorMsg,
|
||||
$validator->validationError(
|
||||
$name,
|
||||
$errorMsg,
|
||||
"validation",
|
||||
false
|
||||
);
|
||||
@ -369,7 +378,7 @@ class ConfirmedPasswordField extends FormField {
|
||||
if($this->requireStrongPassword) {
|
||||
if(!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/',$value)) {
|
||||
$validator->validationError(
|
||||
'Password',
|
||||
$name,
|
||||
_t('Form.VALIDATIONSTRONGPASSWORD',
|
||||
"Passwords must have at least one digit and one alphanumeric character"),
|
||||
"validation",
|
||||
|
@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Required Fields allows you to set which fields
|
||||
* need to be present before submitting the form
|
||||
* Submit an array of arguments or each field as a
|
||||
* seperate argument. Validation is performed on a name by
|
||||
* name basis.
|
||||
* Required Fields allows you to set which fields need to be present before
|
||||
* submitting the form. Submit an array of arguments or each field as a separate
|
||||
* argument.
|
||||
*
|
||||
* Validation is performed on a field by field basis through
|
||||
* {@link FormField::validate}.
|
||||
*
|
||||
* @package forms
|
||||
* @subpackage validators
|
||||
@ -15,8 +17,8 @@ class RequiredFields extends Validator {
|
||||
protected $useLabels = true;
|
||||
|
||||
/**
|
||||
* Pass each field to be validated as a seperate argument
|
||||
* to the constructor of this object. (an array of elements are ok)
|
||||
* Pass each field to be validated as a seperate argument to the constructor
|
||||
* of this object. (an array of elements are ok).
|
||||
*/
|
||||
public function __construct() {
|
||||
$required = func_get_args();
|
||||
@ -40,9 +42,12 @@ class RequiredFields extends Validator {
|
||||
|
||||
/**
|
||||
* Clears all the validation from this object.
|
||||
*
|
||||
* @return RequiredFields
|
||||
*/
|
||||
public function removeValidation(){
|
||||
public function removeValidation() {
|
||||
$this->required = array();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -50,7 +55,9 @@ class RequiredFields extends Validator {
|
||||
* Debug helper
|
||||
*/
|
||||
public function debug() {
|
||||
if(!is_array($this->required)) return false;
|
||||
if(!is_array($this->required)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = "<ul>";
|
||||
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
|
||||
* the form is submitted
|
||||
*/
|
||||
* Allows validation of fields via specification of a php function for
|
||||
* validation which is executed after the form is submitted.
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function php($data) {
|
||||
$valid = true;
|
||||
|
||||
$fields = $this->form->Fields();
|
||||
|
||||
foreach($fields as $field) {
|
||||
$valid = ($field->validate($this) && $valid);
|
||||
}
|
||||
|
||||
if($this->required) {
|
||||
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;
|
||||
|
||||
// submitted data for file upload fields come back as an array
|
||||
$value = isset($data[$fieldName]) ? $data[$fieldName] : null;
|
||||
|
||||
if(is_array($value)) {
|
||||
if($formField instanceof FileField && isset($value['error']) && $value['error']) {
|
||||
$error = true;
|
||||
@ -106,11 +128,13 @@ class RequiredFields extends Validator {
|
||||
if($msg = $formField->getCustomValidationMessage()) {
|
||||
$errorMessage = $msg;
|
||||
}
|
||||
|
||||
$this->validationError(
|
||||
$fieldName,
|
||||
$errorMessage,
|
||||
"required"
|
||||
);
|
||||
|
||||
$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;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeRequiredField($field) {
|
||||
unset($this->required[$field]);
|
||||
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){
|
||||
$this->required = $this->required + ArrayLib::valuekey($requiredFields->getRequired());
|
||||
public function removeRequiredField($field) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -236,7 +236,7 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig {
|
||||
|
||||
$this->addComponent(new GridFieldButtonRow('before'));
|
||||
$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($sort = new GridFieldSortableHeader());
|
||||
$this->addComponent($filter = new GridFieldFilterHeader());
|
||||
|
@ -349,7 +349,8 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
if($canDelete) {
|
||||
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
|
||||
->setUseButtonTag(true)
|
||||
->addExtraClass('ss-ui-action-destructive action-delete'));
|
||||
->addExtraClass('ss-ui-action-destructive action-delete')
|
||||
->setAttribute('data-icon', 'delete'));
|
||||
}
|
||||
|
||||
}else{ // adding new record
|
||||
|
@ -637,6 +637,13 @@ class i18nTextCollector_Parser extends SSTemplateParser {
|
||||
|
||||
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) {
|
||||
self::$currentEntity = array(null,null,null); //start with empty array
|
||||
}
|
||||
|
@ -459,6 +459,7 @@
|
||||
if(this.height() === 0) {
|
||||
text = ss.i18n._t('UploadField.Editing', "Editing ...");
|
||||
this.fitHeight();
|
||||
this.addClass('opened');
|
||||
itemInfo.find('.toggle-details-icon').addClass('opened');
|
||||
status.removeClass('ui-state-success-text').removeClass('ui-state-warning-text');
|
||||
iframe.find('#Form_EditForm_action_doEdit').click(function(){
|
||||
@ -470,6 +471,7 @@
|
||||
|
||||
} else {
|
||||
this.animate({height: 0}, 500);
|
||||
this.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');
|
||||
if(!this.hasClass('edited')){
|
||||
@ -493,9 +495,11 @@
|
||||
});
|
||||
$('div.ss-upload .ss-uploadfield-item-editform iframe').entwine({
|
||||
onmatch: function() {
|
||||
var form = this.closest('.ss-uploadfield-item-editform');
|
||||
// TODO entwine event binding doesn't work for iframes
|
||||
this.load(function() {
|
||||
$(this).parent().removeClass('loading');
|
||||
if(form.hasClass('opened')) form.fitHeight();
|
||||
});
|
||||
this._super();
|
||||
},
|
||||
|
@ -1290,20 +1290,6 @@ class Versioned extends DataExtension {
|
||||
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
|
||||
*/
|
||||
|
@ -119,13 +119,22 @@ $gf_grid_x: 16px;
|
||||
}
|
||||
.add-existing-autocompleter {
|
||||
span {
|
||||
float: left;
|
||||
@include inline-block(top);
|
||||
}
|
||||
input.relation-search {
|
||||
width: 270px;
|
||||
width: 270px; height: 32px;
|
||||
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 {
|
||||
margin-bottom: 0;
|
||||
|
@ -602,8 +602,21 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
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() {
|
||||
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() {
|
||||
$id = Member::currentUserID();
|
||||
|
||||
if($id) {
|
||||
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
|
||||
* changed
|
||||
* changed.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*/
|
||||
class Member_ChangePasswordEmail extends Email {
|
||||
|
||||
protected $from = ''; // setting a blank from address uses the site's default administrator email
|
||||
protected $subject = '';
|
||||
protected $ss_template = 'ChangePasswordEmail';
|
||||
|
||||
public function __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
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*/
|
||||
@ -1532,18 +1550,29 @@ class Member_ForgotPasswordEmail extends Email {
|
||||
|
||||
public function __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
|
||||
*
|
||||
* 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
|
||||
* @subpackage security
|
||||
*/
|
||||
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() {
|
||||
$required = func_get_args();
|
||||
|
||||
if(isset($required[0]) && is_array($required[0])) {
|
||||
$required = $required[0];
|
||||
}
|
||||
|
||||
$required = array_merge($required, $this->customRequired);
|
||||
|
||||
parent::__construct($required);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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\LoginContext,
|
||||
SilverStripe\BehatExtension\Context\FixtureContext,
|
||||
SilverStripe\BehatExtension\Context\EmailContext,
|
||||
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
|
||||
SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
||||
|
||||
@ -41,6 +42,7 @@ class FeatureContext extends SilverStripeContext
|
||||
$this->useContext('LoginContext', new LoginContext($parameters));
|
||||
$this->useContext('CmsFormsContext', new CmsFormsContext($parameters));
|
||||
$this->useContext('CmsUiContext', new CmsUiContext($parameters));
|
||||
$this->useContext('EmailContext', new EmailContext($parameters));
|
||||
|
||||
$fixtureContext = new FixtureContext($parameters);
|
||||
$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
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CMSProfileControllerTest extends FunctionalTest {
|
||||
|
||||
protected static $fixture_file = 'CMSProfileControllerTest.yml';
|
||||
@ -74,6 +79,11 @@ class CMSProfileControllerTest extends FunctionalTest {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CMSProfileControllerTestExtension extends DataExtension {
|
||||
|
||||
public function canEdit($member = null) {
|
||||
|
@ -9,23 +9,22 @@ class FulltextSearchableTest extends SapphireTest {
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->orig['File_searchable'] = File::has_extension('FulltextSearchable');
|
||||
|
||||
// TODO This shouldn't need all arguments included
|
||||
File::remove_extension('FulltextSearchable(\'"Filename","Title","Content"\')');
|
||||
FulltextSearchable::enable('File');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// TODO This shouldn't need all arguments included
|
||||
if($this->orig['File_searchable']) {
|
||||
File::add_extension('FulltextSearchable(\'"Filename","Title","Content"\')');
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
|
||||
File::remove_extension('FulltextSearchable');
|
||||
Config::inst()->update('File', 'create_table_options', array('MySQLDatabase' => 'ENGINE=InnoDB'));
|
||||
}
|
||||
|
||||
public function testEnable() {
|
||||
FulltextSearchable::enable();
|
||||
$this->assertTrue(File::has_extension('FulltextSearchable'));
|
||||
}
|
||||
|
||||
@ -33,9 +32,7 @@ class FulltextSearchableTest extends SapphireTest {
|
||||
FulltextSearchable::enable(array('File'));
|
||||
$this->assertTrue(File::has_extension('FulltextSearchable'));
|
||||
|
||||
// TODO This shouldn't need all arguments included
|
||||
File::remove_extension('FulltextSearchable(\'"Filename","Title","Content"\')');
|
||||
|
||||
File::remove_extension('FulltextSearchable');
|
||||
$this->assertFalse(File::has_extension('FulltextSearchable'));
|
||||
}
|
||||
|
||||
|
@ -42,10 +42,11 @@ class MemberTest extends FunctionalTest {
|
||||
|
||||
public function tearDown() {
|
||||
Member::config()->unique_identifier_field = $this->orig['Member_unique_identifier_field'];
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @expectedException ValidationException
|
||||
*/
|
||||
@ -720,45 +721,111 @@ class MemberTest extends FunctionalTest {
|
||||
$member->isLockedOut(),
|
||||
"Member has been locked out too early"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//fail login until max login attempts is reached
|
||||
$member->FailedLoginCount = 0;
|
||||
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'
|
||||
);
|
||||
public function testCustomMemberValidator() {
|
||||
$member = $this->objFromFixture('Member', 'admin');
|
||||
|
||||
//after they're locked out, need to check FailedLoginCount was reset to 0
|
||||
$this->assertEquals(
|
||||
$member->FailedLoginCount,
|
||||
0,
|
||||
'Failed login count was not reset after lockout'
|
||||
);
|
||||
$form = new MemberTest_ValidatorForm();
|
||||
$form->loadDataFrom($member);
|
||||
|
||||
//test all done, unnest config
|
||||
Config::unnest();
|
||||
$validator = new Member_Validator();
|
||||
$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 {
|
||||
|
||||
public function canView($member = null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MemberTest_ViewingDeniedExtension extends DataExtension implements TestOnly {
|
||||
|
||||
public function canView($member = null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension implements TestOnly {
|
||||
|
||||
public function canView($member = null) {
|
||||
@ -775,6 +842,10 @@ class MemberTest_EditingAllowedDeletingDeniedExtension extends DataExtension imp
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class MemberTest_PasswordValidator extends PasswordValidator {
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
@ -65,7 +65,7 @@ class SSTemplateParseException extends Exception {
|
||||
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
||||
* 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
|
||||
@ -73,6 +73,12 @@ class SSTemplateParser extends Parser {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -1757,7 +1763,7 @@ class SSTemplateParser extends Parser {
|
||||
/* CacheBlockArgument:
|
||||
!( "if " | "unless " )
|
||||
(
|
||||
:DollarMarkedLookup |
|
||||
:DollarMarkedLookup |
|
||||
:QuotedString |
|
||||
:Lookup
|
||||
) */
|
||||
@ -4548,7 +4554,8 @@ class SSTemplateParser extends Parser {
|
||||
// non-dynamically calculated
|
||||
$text = preg_replace(
|
||||
'/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
|
||||
);
|
||||
@ -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.
|
||||
*
|
||||
* @static
|
||||
* @throws SSTemplateParseException
|
||||
* @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
|
||||
*/
|
||||
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
||||
public function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
||||
if (!trim($string)) {
|
||||
$code = '';
|
||||
}
|
||||
else {
|
||||
// Construct a parser instance
|
||||
$parser = new SSTemplateParser($string);
|
||||
$parser->includeDebuggingComments = $includeDebuggingComments;
|
||||
parent::__construct($string);
|
||||
|
||||
$this->includeDebuggingComments = $includeDebuggingComments;
|
||||
|
||||
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
|
||||
// (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
|
||||
$result = $parser->match_TopTemplate();
|
||||
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $parser);
|
||||
$result = $this->match_TopTemplate();
|
||||
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $this);
|
||||
|
||||
// Get the result
|
||||
$code = $result['php'];
|
||||
@ -4593,7 +4599,7 @@ class SSTemplateParser extends Parser {
|
||||
|
||||
// Include top level debugging comments if desired
|
||||
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
|
||||
$code = $parser->includeDebuggingComments($code, $templateName);
|
||||
$code = $this->includeDebuggingComments($code, $templateName);
|
||||
}
|
||||
|
||||
return $code;
|
||||
@ -4640,7 +4646,7 @@ class SSTemplateParser extends Parser {
|
||||
* @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
|
||||
*/
|
||||
static function compileFile($template) {
|
||||
return self::compileString(file_get_contents($template), $template);
|
||||
public function compileFile($template) {
|
||||
return $this->compileString(file_get_contents($template), $template);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ class SSTemplateParseException extends Exception {
|
||||
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
||||
* 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
|
||||
@ -94,6 +94,12 @@ class SSTemplateParser extends Parser {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@ -462,7 +468,7 @@ class SSTemplateParser extends Parser {
|
||||
CacheBlockArgument:
|
||||
!( "if " | "unless " )
|
||||
(
|
||||
:DollarMarkedLookup |
|
||||
:DollarMarkedLookup |
|
||||
:QuotedString |
|
||||
: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.
|
||||
*
|
||||
* @static
|
||||
* @throws SSTemplateParseException
|
||||
* @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
|
||||
*/
|
||||
static function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
||||
public function compileString($string, $templateName = "", $includeDebuggingComments=false) {
|
||||
if (!trim($string)) {
|
||||
$code = '';
|
||||
}
|
||||
else {
|
||||
// Construct a parser instance
|
||||
$parser = new SSTemplateParser($string);
|
||||
$parser->includeDebuggingComments = $includeDebuggingComments;
|
||||
parent::__construct($string);
|
||||
|
||||
$this->includeDebuggingComments = $includeDebuggingComments;
|
||||
|
||||
// Ignore UTF8 BOM at begining of string. TODO: Confirm this is needed, make sure SSViewer handles UTF
|
||||
// (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
|
||||
$result = $parser->match_TopTemplate();
|
||||
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $parser);
|
||||
$result = $this->match_TopTemplate();
|
||||
if(!$result) throw new SSTemplateParseException('Unexpected problem parsing template', $this);
|
||||
|
||||
// Get the result
|
||||
$code = $result['php'];
|
||||
@ -1048,7 +1053,7 @@ class SSTemplateParser extends Parser {
|
||||
|
||||
// Include top level debugging comments if desired
|
||||
if($includeDebuggingComments && $templateName && stripos($code, "<?xml") === false) {
|
||||
$code = $parser->includeDebuggingComments($code, $templateName);
|
||||
$code = $this->includeDebuggingComments($code, $templateName);
|
||||
}
|
||||
|
||||
return $code;
|
||||
@ -1095,7 +1100,7 @@ class SSTemplateParser extends Parser {
|
||||
* @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
|
||||
*/
|
||||
static function compileFile($template) {
|
||||
return self::compileString(file_get_contents($template), $template);
|
||||
public function compileFile($template) {
|
||||
return $this->compileString(file_get_contents($template), $template);
|
||||
}
|
||||
}
|
||||
|
@ -614,6 +614,11 @@ class SSViewer {
|
||||
*/
|
||||
protected $includeRequirements = true;
|
||||
|
||||
/**
|
||||
* @var TemplateParser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
/**
|
||||
* Create a template from a string instead of a .ss file
|
||||
*
|
||||
@ -691,7 +696,9 @@ class SSViewer {
|
||||
* array('MySpecificPage', 'MyPage', 'Page')
|
||||
* </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
|
||||
if (isset($_GET['flush']) && $_GET['flush'] == 'all') {
|
||||
if(Director::isDev() || Director::is_cli() || Permission::check('ADMIN')) {
|
||||
@ -729,6 +736,24 @@ 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.
|
||||
*
|
||||
@ -970,7 +995,7 @@ class SSViewer {
|
||||
|
||||
if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited || isset($_GET['flush'])) {
|
||||
$content = file_get_contents($template);
|
||||
$content = SSViewer::parseTemplateContent($content, $template);
|
||||
$content = $this->parseTemplateContent($content, $template);
|
||||
|
||||
$fh = fopen($cacheFile,'w');
|
||||
fwrite($fh, $content);
|
||||
@ -983,7 +1008,7 @@ class SSViewer {
|
||||
// through $Content and $Layout placeholders.
|
||||
foreach(array('Content', 'Layout') as $subtemplate) {
|
||||
if(isset($this->chosenTemplates[$subtemplate])) {
|
||||
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate]);
|
||||
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate], $this->parser);
|
||||
$subtemplateViewer->includeRequirements(false);
|
||||
$subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore());
|
||||
|
||||
@ -1028,8 +1053,8 @@ class SSViewer {
|
||||
return $v->process($data, $arguments, $scope);
|
||||
}
|
||||
|
||||
public static function parseTemplateContent($content, $template="") {
|
||||
return SSTemplateParser::compileString(
|
||||
public function parseTemplateContent($content, $template="") {
|
||||
return $this->parser->compileString(
|
||||
$content,
|
||||
$template,
|
||||
Director::isDev() && Config::inst()->get('SSViewer', 'source_file_comments')
|
||||
@ -1079,7 +1104,8 @@ class SSViewer {
|
||||
class SSViewer_FromString extends SSViewer {
|
||||
protected $content;
|
||||
|
||||
public function __construct($content) {
|
||||
public function __construct($content, TemplateParser $parser = null) {
|
||||
$this->setParser($parser ?: Injector::inst()->get('SSTemplateParser'));
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
@ -1091,7 +1117,7 @@ class SSViewer_FromString extends SSViewer {
|
||||
$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,"");
|
||||
$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