Merge remote-tracking branch 'origin/3.1'

This commit is contained in:
Sam Minnee 2013-01-10 13:25:09 +13:00
commit 27bf3cf95e
102 changed files with 1963 additions and 1272 deletions

14
_config/uploadfield.yml Normal file
View File

@ -0,0 +1,14 @@
name: uploadfield
---
UploadField:
defaultConfig:
autoUpload: true
allowedMaxFileNumber:
canUpload: true
previewMaxWidth: 80
previewMaxHeight: 60
uploadTemplateName: 'ss-uploadfield-uploadtemplate'
downloadTemplateName: 'ss-uploadfield-downloadtemplate'
fileEditFields:
fileEditActions:
fileEditValidator:

View File

@ -30,7 +30,7 @@ class CMSProfileController extends LeftAndMain {
->setAttribute('data-icon', 'accept')
->setUseButtonTag(true)
);
$form->Actions()->removeByName('delete');
$form->Actions()->removeByName('action_delete');
$form->setValidator(new Member_Validator());
$form->setTemplate('Form');
$form->setAttribute('data-pjax-fragment', null);

View File

@ -357,7 +357,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
$response = parent::handleRequest($request, $model);
$title = $this->Title();
if(!$response->getHeader('X-Controller')) $response->addHeader('X-Controller', $this->class);
if(!$response->getHeader('X-Title')) $response->addHeader('X-Title', $title);
if(!$response->getHeader('X-Title')) $response->addHeader('X-Title', urlencode($title));
return $response;
}
@ -645,13 +645,13 @@ class LeftAndMain extends Controller implements PermissionProvider {
$ancestors->push($record);
foreach($ancestors as $ancestor) {
$items->push(new ArrayData(array(
'Title' => $ancestor->Title,
'Title' => ($ancestor->MenuTitle) ? $ancestor->MenuTitle : $ancestor->Title,
'Link' => ($unlinked) ? false : Controller::join_links($this->Link('show'), $ancestor->ID)
)));
}
} else {
$items->push(new ArrayData(array(
'Title' => $record->Title,
'Title' => ($record->MenuTitle) ? $record->MenuTitle : $record->Title,
'Link' => ($unlinked) ? false : Controller::join_links($this->Link('show'), $record->ID)
)));
}

View File

@ -42,12 +42,13 @@
.filter-buttons button.ss-gridfield-button-filter { background-position: -18px 4px !important; }
/* Alternative styles for the switch in old IE */
fieldset.switch-states { padding: 0; }
fieldset.switch-states .switch { padding: 0 10px 0 0; }
fieldset.switch-states { padding-right: 5px; }
fieldset.switch-states .switch { padding: 0; width: 132%; left: -32px; }
fieldset.switch-states .switch label { overflow: visible; text-overflow: visible; white-space: normal; padding: 0; }
fieldset.switch-states .switch label.active { color: #fff; background-color: #2b9c32; }
fieldset.switch-states .switch label span { display: inline; padding: 0 10px; padding-right: 15px; overflow: visible; text-overflow: visible; white-space: wrap; }
fieldset.switch-states .switch label span { display: inline; padding: 0 8px; overflow: visible; text-overflow: visible; white-space: wrap; }
fieldset.switch-states .switch .slide-button { display: none; }
fieldset.switch-states .switch input.state-name { margin-left: -20px; }
/* Hide size controls in IE - they won't work as intended */
.cms-content-controls .preview-size-selector { display: none; }
@ -217,8 +218,3 @@ table.ss-gridfield-table tr.ss-gridfield-item.even { background: #F0F4F7; }
.cms .cms-content-actions .Actions .action-menus.ss-ui-action-tabset ul.ui-tabs-nav .ui-state-active a.ui-tabs-anchor { background: transparent url(../images/sprites-32x32/arrow_up_lighter.png) no-repeat right top; }
.cms .cms-content-actions .Actions .action-menus.ss-ui-action-tabset ul.ui-tabs-nav .ui-state-active a.ui-tabs-anchor:hover { background: transparent url(../images/sprites-32x32/arrow_up_darker.png) no-repeat right top; }
.cms .cms-content-actions .Actions .action-menus.ss-ui-action-tabset .ui-tabs-panel button.ss-ui-button { width: 190px; /* Width 100% not calculating by ie7 */ }
/* Tempory fix as jquery loads too slow to add icons */
button.ui-button-text-icon-primary { padding-left: 30px !important; }
button.ui-button-text-icon-primary span.ui-button-icon-primary { position: absolute !important; top: 5px !important; left: 8px !important; }
button.ui-button-text-icon-primary .ui-button-text { margin-left: 0 !important; }

View File

@ -42,12 +42,13 @@
.filter-buttons button.ss-gridfield-button-filter { background-position: -18px 4px !important; }
/* Alternative styles for the switch in old IE */
fieldset.switch-states { padding: 0; }
fieldset.switch-states .switch { padding: 0 10px 0 0; }
fieldset.switch-states { padding-right: 5px; }
fieldset.switch-states .switch { padding: 0; width: 132%; left: -32px; }
fieldset.switch-states .switch label { overflow: visible; text-overflow: visible; white-space: normal; padding: 0; }
fieldset.switch-states .switch label.active { color: #fff; background-color: #2b9c32; }
fieldset.switch-states .switch label span { display: inline; padding: 0 10px; padding-right: 15px; overflow: visible; text-overflow: visible; white-space: wrap; }
fieldset.switch-states .switch label span { display: inline; padding: 0 8px; overflow: visible; text-overflow: visible; white-space: wrap; }
fieldset.switch-states .switch .slide-button { display: none; }
fieldset.switch-states .switch input.state-name { margin-left: -20px; }
/* Hide size controls in IE - they won't work as intended */
.cms-content-controls .preview-size-selector { display: none; }

View File

@ -209,9 +209,7 @@ form.small .field input.text, form.small .field textarea, form.small .field sele
.cms input.loading, .cms button.loading, .cms input.ui-state-default.loading, .cms .ui-widget-content input.ui-state-default.loading, .cms .ui-widget-header input.ui-state-default.loading { color: #525252; border-color: #d5d3d3; cursor: default; }
.cms input.loading .ui-icon, .cms button.loading .ui-icon, .cms input.ui-state-default.loading .ui-icon, .cms .ui-widget-content input.ui-state-default.loading .ui-icon, .cms .ui-widget-header input.ui-state-default.loading .ui-icon { background: transparent url(../../images/network-save.gif) no-repeat 0 0; }
.cms input.loading.ss-ui-action-constructive .ui-icon, .cms button.loading.ss-ui-action-constructive .ui-icon { background: transparent url(../../images/network-save-constructive.gif) no-repeat 0 0; }
.cms .ss-ui-button { font-size: 12px; margin-top: 0px; padding: 5px 10px; font-weight: bold; text-decoration: none; line-height: 16px; color: #393939; border: 1px solid #c0c0c2; border-bottom: 1px solid #a6a6a9; cursor: pointer; background-color: #e6e6e6; white-space: nowrap; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -o-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); text-shadow: white 0 1px 1px; /* constructive */ /* destructive */ }
.cms .ss-ui-button .ui-icon, .cms .ss-ui-button .ui-button-text { display: inline-block; line-height: 16px; padding: 0; }
.cms .ss-ui-button .ui-icon { width: 16px; padding: 0 2px; position: relative; left: -2px; margin-top: 0; top: 0; height: 16px; float: left; }
.cms .ss-ui-button { margin-top: 0px; font-weight: bold; text-decoration: none; line-height: 16px; color: #393939; border: 1px solid #c0c0c2; border-bottom: 1px solid #a6a6a9; cursor: pointer; background-color: #e6e6e6; white-space: nowrap; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -o-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); text-shadow: white 0 1px 1px; /* constructive */ /* destructive */ }
.cms .ss-ui-button.ui-state-hover, .cms .ss-ui-button:hover { text-decoration: none; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -o-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -webkit-box-shadow: 0 0 5px #b3b3b3; -moz-box-shadow: 0 0 5px #b3b3b3; box-shadow: 0 0 5px #b3b3b3; }
.cms .ss-ui-button:active, .cms .ss-ui-button:focus, .cms .ss-ui-button.ui-state-active, .cms .ss-ui-button.ui-state-focus { border: 1px solid #b3b3b3; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -o-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -webkit-box-shadow: 0 0 5px #b3b3b3 inset; -moz-box-shadow: 0 0 5px #b3b3b3 inset; box-shadow: 0 0 5px #b3b3b3 inset; }
.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; }
@ -373,10 +371,10 @@ body.cms { overflow: hidden; }
/** -------------------------------------------- Tabs -------------------------------------------- */
.ui-tabs { padding: 0; background: none; }
.ui-tabs .ui-tabs { position: static; }
.ui-tabs .ui-tabs-panel { padding: 8px 0; background: transparent; border: 0; }
.ui-tabs .ui-tabs-panel { padding: 16px; background: transparent; border: 0; }
.ui-tabs .ui-tabs-panel.cms-edit-form { padding: 0; }
.ui-tabs .ui-widget-header { border: 0; background: none; }
.ui-tabs .ui-tabs-nav { float: right; margin: 0 0 -1px 0; padding: 0 12px 0 0; border-bottom: none; }
.ui-tabs .ui-tabs-nav { float: right; margin: 16px 0 -1px 0; padding: 0 12px 0 0; border-bottom: none; }
.ui-tabs .ui-tabs-nav ~ .ui-tabs-panel { border-top: 1px solid #c0c0c2; clear: both; }
.ui-tabs .ui-tabs-nav li { top: 0; float: left; border-bottom: 0 !important; }
.ui-tabs .ui-tabs-nav li a { display: -moz-inline-stack; display: inline-block; vertical-align: middle; *vertical-align: auto; zoom: 1; *display: inline; float: none; font-weight: bold; color: #444444; line-height: 32px; padding: 0 16px 0; }
@ -398,20 +396,13 @@ body.cms { overflow: hidden; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.gallery.ui-state-active a { background: url('../images/sprites-64x64-s88957ee578.png') 0 -54px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.edit.ui-state-active a { background: url('../images/sprites-64x64-s88957ee578.png') 0 -404px no-repeat; }
.ui-tabs .ui-tabs-nav li.cms-tabset-icon.search.ui-state-active a { background: url('../images/sprites-64x64-s88957ee578.png') 0 -104px no-repeat; }
.ui-tabs .cms-edit-form, .ui-tabs .cms-content-fields { /*not sure if .cms-content-fields effects other areas*/ }
.ui-tabs .cms-edit-form .cms-panel-padded, .ui-tabs .cms-content-fields .cms-panel-padded { /* Has padded area inside it */ padding: 0; margin: 0; }
.ui-tabs .cms-edit-form .ui-tabs-panel, .ui-tabs .cms-edit-form .ss-gridfield, .ui-tabs .cms-content-fields .ui-tabs-panel, .ui-tabs .cms-content-fields .ss-gridfield { margin: 12px; padding: 0 0 12px; }
.ui-tabs .cms-edit-form .ui-tabs-panel .ss-gridfield, .ui-tabs .cms-edit-form .ss-gridfield .ss-gridfield, .ui-tabs .cms-content-fields .ui-tabs-panel .ss-gridfield, .ui-tabs .cms-content-fields .ss-gridfield .ss-gridfield { /* Files area & inside second level tabs */ padding: 0; /* should be zero ideally */ margin: 0 0 12px; }
.ui-tabs .cms-edit-form .ui-tabs-nav, .ui-tabs .cms-content-fields .ui-tabs-nav { margin: 10px 12px 0; padding: 0 8px 0 0; /* second set of tabs */ }
.ui-tabs .cms-edit-form #tree_actions .ui-tabs-nav, .ui-tabs .cms-content-fields #tree_actions .ui-tabs-nav { margin: 0; }
.ui-tabs .cms-panel-padded h3 { margin-left: 12px; /* reports headers, probably too specific */ }
.ui-tabs .cms-panel-padded .ui-tabs-panel { margin: 0; padding: 12px 12px 12px; }
.ui-tabs .cms-panel-padded .ui-tabs-panel { padding: 0; }
.ui-tabs .cms-panel-padded .ui-tabs-panel .ui-tabs-panel { padding: 8px 0 0 0; }
.ui-tabs .ui-tabs .ui-tabs-panel { /* second level tabs */ padding-top: 8px; }
.ui-tabs .cms-panel-padded .Actions { padding: 0; }
.ui-tabs.ss-tabset-tabshidden .ui-tabs-panel { border-top: none; }
/** Primary styles which sit on top of screen, with different tab colors. TODO Only use one "primary" selector and fix HTMLEditorField TabSet addExtraClass() */
.ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { border-left: 1px solid #b3b3b3; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { margin-top: 0; border-left: 1px solid #b3b3b3; float: none; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav li, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li { margin-right: 0; margin-top: 0; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav li a, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li a, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li a { margin: 0; line-height: 39px; }
.ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-all, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-top, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-right, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tr, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tl, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-all, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-top, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-right, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tr, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tl, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-right, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tr, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tl { border-radius: 0; }
@ -444,7 +435,8 @@ body.cms { overflow: hidden; }
.message.good { background-color: #eaf6e4; border-color: #72c34b; }
.message p { margin: 0; }
p.message { margin-bottom: 12px; }
.cms-edit-form .message { margin: 16px; }
.cms-edit-form .ui-tabs-panel .message { margin: 0 0 16px 0; }
/** -------------------------------------------- Page icons -------------------------------------------- */
.page-icon, a .jstree-pageicon { display: block; width: 16px; height: 16px; background: transparent url(../images/sitetree_ss_pageclass_icons_default.png) no-repeat; }
@ -477,7 +469,7 @@ p.message { margin-bottom: 12px; }
#PageType ul li .description { font-style: italic; }
/** -------------------------------------------- Content toolbar -------------------------------------------- */
.cms-content-toolbar { min-height: 29px; display: block; margin: 0 0 15px 0; padding-bottom: 9px; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); *zoom: 1; /* smaller treedropdown */ }
.cms-content-toolbar { min-height: 29px; display: block; margin: 0 0 8px 0; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); *zoom: 1; /* smaller treedropdown */ }
.cms-content-toolbar:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; }
.cms-content-toolbar .cms-tree-view-modes { float: right; padding-top: 5px; }
.cms-content-toolbar .cms-tree-view-modes * { display: inline-block; }
@ -491,10 +483,10 @@ p.message { margin-bottom: 12px; }
.cms-content-toolbar .ss-ui-button { margin-bottom: 8px; }
/* -------------------------------------------------------- Content Tools is the sidebar on the left of the main content panel */
.cms-content-tools { background: #eceff1; width: 192px; overflow-y: auto; overflow-x: hidden; z-index: 70; border-right: 1px solid #c0c0c2; -webkit-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); -moz-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); float: left; position: relative; }
.cms-content-tools { background: #eceff1; width: 200px; overflow-y: auto; overflow-x: hidden; z-index: 70; border-right: 1px solid #c0c0c2; -webkit-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); -moz-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); float: left; position: relative; }
.cms-content-tools.filter { padding: 0 !important; }
.cms-content-tools .cms-panel-header { clear: both; margin: 0 0 7px; line-height: 24px; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); }
.cms-content-tools .cms-panel-content { width: 176px; padding: 8px 8px 0; overflow: auto; height: 100%; }
.cms-content-tools .cms-panel-content { width: 184px; padding: 16px 8px 0; overflow: auto; height: 100%; }
.cms-content-tools .cms-panel-content .Actions .ss-ui-action-constructive { margin-right: 5px; }
.cms-content-tools .cms-content-header { background-color: #748d9d; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzc0OGQ5ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #748d9d)); background-image: -webkit-linear-gradient(#b0bec7, #748d9d); background-image: -moz-linear-gradient(#b0bec7, #748d9d); background-image: -o-linear-gradient(#b0bec7, #748d9d); background-image: linear-gradient(#b0bec7, #748d9d); }
.cms-content-tools .cms-content-header h2 { text-shadow: #5c7382 -1px -1px 0; width: 176px; color: white; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; }
@ -519,7 +511,7 @@ p.message { margin-bottom: 12px; }
.cms-content-tools td { border-bottom: 1px solid #ced7dc; padding: 7px 2px; font-size: 11px; }
/** CMS Batch actions */
.cms-content-batchactions { float: left; position: relative; display: block; margin-left: 8px; }
.cms-content-batchactions { float: left; position: relative; display: block; }
.cms-content-batchactions .view-mode-batchactions-wrapper { float: left; padding: 4px 6px; border: 1px solid #aaa; margin-bottom: 8px; background-color: #D9D9D9; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -o-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(top, #ffffff, #d9d9d9); border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.cms-content-batchactions .view-mode-batchactions-wrapper label { display: none; }
.cms-content-batchactions .view-mode-batchactions-wrapper fieldset, .cms-content-batchactions .view-mode-batchactions-wrapper .Actions { display: inline-block; }
@ -540,7 +532,7 @@ p.message { margin-bottom: 12px; }
/** -------------------------------------------- Member Profile -------------------------------------------- */
form.member-profile-form { padding: 0 16px 0 0; }
form.member-profile-form #Root_Permissions { clear: both; border-top: 1px solid #a6a6a6; }
form.member-profile-form #Root_Main { clear: both; border-top: 1px solid #a6a6a6; padding-top: 16px; }
form.member-profile-form #Root_Main { clear: both; border-top: 1px solid #a6a6a6; }
form.member-profile-form #Root_Main .cms-help-toggle { text-indent: -9999em; display: inline-block; width: 20px; background: url(../images/question.png) no-repeat 0px 0px; }
form.member-profile-form #FavouritePageID { margin-top: 8px; }
form.member-profile-form #CsvFile .middleColumn { background: none !important; }
@ -597,6 +589,8 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto;
.cms-panel .collapsed-flyout { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; }
.cms-panel .collapsed-flyout li a span { display: block !important; }
.cms .cms-panel-padded { padding: 16px 16px; margin: 0; }
/** ------------------------------------------------------------------
* Dialog
*
@ -719,11 +713,10 @@ form.import-form label.left { width: 250px; }
.cms .jstree > ul > li, .TreeDropdownField .treedropdownfield-panel .jstree > ul > li { margin-left: 0px; }
.cms .jstree ul, .cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree ul, .TreeDropdownField .treedropdownfield-panel .jstree li { display: block; margin: 0; padding: 0; background: none; list-style-type: none; }
.cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { min-height: 18px; line-height: 25px; white-space: nowrap; margin-left: 18px; min-width: 18px; }
.cms .jstree li ins.jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li ins.jstree-icon { display: none; }
.cms .jstree li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-open > ul { display: block; }
.cms .jstree li.jstree-closed > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-closed > ul { display: none; }
.cms .jstree li.disabled > a, .TreeDropdownField .treedropdownfield-panel .jstree li.disabled > a { color: #aaaaaa; }
.cms .jstree li ul ins.jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li ul ins.jstree-icon { display: block; }
.cms .jstree li > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li > .jstree-icon { cursor: pointer; }
.cms .jstree ins, .TreeDropdownField .treedropdownfield-panel .jstree ins { display: inline-block; text-decoration: none; width: 18px; height: 18px; margin: 0 0 0 0; padding: 0; float: left; }
.cms .jstree a, .TreeDropdownField .treedropdownfield-panel .jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; border: 1px solid #fff; }
.cms .jstree a:focus, .cms .jstree a:active, .cms .jstree a:hover, .TreeDropdownField .treedropdownfield-panel .jstree a:focus, .TreeDropdownField .treedropdownfield-panel .jstree a:active, .TreeDropdownField .treedropdownfield-panel .jstree a:hover { outline: none; text-decoration: none; cursor: pointer; text-shadow: none; }
@ -744,7 +737,7 @@ form.import-form label.left { width: 250px; }
.cms .jstree-apple.jstree-focused .jstree-apple > ul, .TreeDropdownField .treedropdownfield-panel .jstree-apple.jstree-focused .jstree-apple > ul { background: none; }
.cms a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel a > .jstree-icon { display: none; }
.cms .draggable a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .draggable a > .jstree-icon { display: block; }
.cms li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel li.jstree-open > ul { display: block; margin-left: -18px; }
.cms li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel li.jstree-open > ul { display: block; margin-left: -13px; }
.cms li.jstree-open > ul li ul, .TreeDropdownField .treedropdownfield-panel li.jstree-open > ul li ul { margin-left: 2px; }
.cms li.jstree-closed > ul, .TreeDropdownField .treedropdownfield-panel li.jstree-closed > ul { display: none; }
.cms .jstree-rtl a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree-rtl a > .jstree-icon { margin-left: 3px; margin-right: 0; }
@ -921,6 +914,7 @@ li.class-ErrorPage > a a .jstree-pageicon { background-position: 0 -112px; }
/* Styling for the preview screen sizes */
.cms-preview { background-color: #eceff1; height: 100%; width: 100%; }
.cms-preview .cms-preview-overlay { width: 100%; height: 100%; }
.cms-preview .preview-note { color: #CDD7DC; display: block; font-size: 22px; font-weight: bold; height: 82px; margin-top: -50px; margin-left: -150px; /* half of width */ position: absolute; text-align: center; text-shadow: 0 1px 0 #fff; top: 50%; left: 50%; width: 300px; }
.cms-preview .preview-note span { background: url('../images/sprites-64x64-s88957ee578.png') 0 0 no-repeat; display: block; height: 41px; margin: 0 auto 20px; width: 50px; }
.cms-preview .preview-scroll { height: 100%; overflow: auto; position: relative; width: 100%; }

View File

@ -20,7 +20,7 @@
descriptionEl.remove();
}
}
});
});
$(".cms .field.cms-description-tooltip :input").entwine({
onfocusin: function(e) {
@ -28,8 +28,8 @@
},
onfocusout: function(e) {
this.closest('.field').tooltip('close');
}
}
});
});
});
}(jQuery));

View File

@ -424,6 +424,7 @@
* Reacts to the user changing the preview mode.
*/
onchange: function(e) {
this._super(e);
e.preventDefault();
var targetStateName = $(this).val();
@ -523,38 +524,36 @@
'onliszt:showing_dropdown': function() {
this.siblings().find('.chzn-drop').addClass('open')._alignRight();
},
'onliszt:hiding_dropdown': function() {
this.siblings().find('.chzn-drop').removeClass('open')._removeRightAlign();
},
},
/**
* Trigger additional initial icon update when the control is fully loaded.
* Solves an IE8 timing issue.
*/
'onliszt:ready': function() {
this._super();
this._addIcon();
},
_addIcon: function(){
var selected = this.find(':selected');
var iconClass = selected.attr('data-icon');
var target = this.parent().find('.chzn-container a.chzn-single');
var oldIcon = target.attr('data-icon');
if(oldIcon != undefined){
if(typeof oldIcon !== 'undefined'){
target.removeClass(oldIcon);
}
target.addClass(iconClass);
target.attr('data-icon', iconClass);
return this;
}
});
/*
* When chzn initiated run select addIcon
* Apply description text if applicable
*/
$('.preview-selector a.chzn-single').entwine({
onmatch: function() {
this.closest('.preview-selector').find('select')._addIcon();
this._super();
},
onunmatch: function() {
this._super();
}
});
$('.preview-selector .chzn-drop').entwine({
_alignRight: function(){
var that = this;

View File

@ -5,6 +5,11 @@ jQuery.noConflict();
*/
(function($) {
window.onresize = function(e) {
// Entwine's 'fromWindow::onresize' does not trigger on IE8. Use synthetic event.
$('.cms-container').trigger('windowresize');
};
// setup jquery.entwine
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
$.entwine('ss', function($) {
@ -28,7 +33,7 @@ jQuery.noConflict();
disable_search_threshold: 20
});
var title = el.prop('title')
var title = el.prop('title');
if(title) {
el.siblings('.chzn-container').prop('title', title);
@ -144,8 +149,11 @@ jQuery.noConflict();
},
fromWindow: {
onstatechange: function(){ this.handleStateChange(); },
onresize: function(){ this.redraw(); }
onstatechange: function(){ this.handleStateChange(); }
},
'onwindowresize': function() {
this.redraw();
},
'from .cms-panel': {
@ -442,6 +450,12 @@ jQuery.noConflict();
handleAjaxResponse: function(data, status, xhr) {
var self = this, url, selectedTabs, guessFragment;
// Support a full reload
if(xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) {
document.location.href = xhr.getResponseHeader('X-ControllerURL');
return;
}
// Pseudo-redirects via X-ControllerURL might return empty data, in which
// case we'll ignore the response
if(!data) return;
@ -454,7 +468,7 @@ jQuery.noConflict();
// Update title
var title = xhr.getResponseHeader('X-Title');
if(title) document.title = title;
if(title) document.title = decodeURIComponent(title.replace(/\+/g, ' '));
var newFragments = {}, newContentEls;
// If content type is text/json (ignoring charset and other parameters)
@ -569,7 +583,7 @@ jQuery.noConflict();
* Requires HTML5 sessionStorage support.
*/
saveTabState: function() {
if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage == null) return;
if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage === null) return;
var selectedTabs = [], url = this._tabStateUrl();
this.find('.cms-tabset,.ss-tabset').each(function(i, el) {
@ -605,7 +619,7 @@ jQuery.noConflict();
* Requires HTML5 sessionStorage support.
*/
restoreTabState: function() {
if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage == null) return;
if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage === null) return;
var self = this, url = this._tabStateUrl(),
data = window.sessionStorage.getItem('tabs-' + url),
@ -675,7 +689,7 @@ jQuery.noConflict();
this._super();
},
onremove: function() {
this.button('destroy');
if(this.data('button')) this.button('destroy');
this._super();
}
});
@ -727,7 +741,7 @@ jQuery.noConflict();
var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText;
try {
if (typeof msg != "undefined" && msg != null) eval(msg);
if (typeof msg != "undefined" && msg !== null) eval(msg);
}
catch(e) {}

View File

@ -57,20 +57,42 @@
var onchange = function(e) {
var $field = $(e.target);
var origVal = $field.data('changetracker.origVal');
if(origVal === null || e.target.value != origVal) {
// TODO Also add class to radiobutton/checkbox siblings
var origVal = $field.data('changetracker.origVal'), newVal;
// Determine value based on field type
if($field.is(':checkbox')) {
newVal = $field.is(':checked') ? 1 : 0;
} else {
newVal = $field.val();
}
// Determine changed state based on value comparisons
if(origVal === null || newVal != origVal) {
$field.addClass(options.changedCssClass);
self.addClass(options.changedCssClass);
} else {
$field.removeClass(options.changedCssClass);
// Unset changed state on all radio buttons of the same name
if($field.is(':radio')) {
self.find(':radio[name=' + $field.attr('name') + ']').removeClass(options.changedCssClass);
}
// Only unset form state if no other fields are changed as well
if(!self.getFields().filter('.' + options.changedCssClass).length) {
self.removeClass(options.changedCssClass);
}
}
};
// setup original values
var fields = this.getFields();
var fields = this.getFields(), origVal;
fields.filter(':radio,:checkbox').bind('click.changetracker', onchange);
fields.not(':radio,:checkbox').bind('change.changetracker', onchange);
fields.each(function() {
var origVal = $(this).is(':radio,:checkbox') ? self.find(':input[name=' + $(this).attr('name') + ']:checked').val() : $(this).val();
if($(this).is(':radio,:checkbox')) {
origVal = self.find(':input[name=' + $(this).attr('name') + ']:checked').val();
} else {
origVal = $(this).val();
}
$(this).data('changetracker.origVal', origVal);
});

View File

@ -361,9 +361,7 @@ form.small .field, .field.small {
}
.ss-ui-button {
font-size: 12px;
margin-top:0px;
padding: 5px 10px;
font-weight: bold;
text-decoration: none;
line-height: $grid-y * 2;
@ -374,22 +372,6 @@ form.small .field, .field.small {
background-color: $color-button-generic;
white-space: nowrap;
.ui-icon, .ui-button-text {
display: inline-block;
line-height: $grid-x*2;
padding: 0;
}
.ui-icon {
width: 16px;
padding: 0 2px;
position: relative;
left: -2px;
margin-top: 0;
top: 0;
height: 16px;
float: left;
}
@include background(
linear-gradient(color-stops(
lighten($color-button-generic, 10%),

View File

@ -142,9 +142,11 @@
/* Alternative styles for the switch in old IE */
fieldset.switch-states{
padding:0;
padding-right: 5px;
.switch{
padding:0 10px 0 0;
padding: 0;
width: 100%+32;
left: -32px;
label{
overflow:visible;
text-overflow:visible;
@ -156,8 +158,7 @@ fieldset.switch-states{
}
span{
display:inline;
padding:0 10px;
padding-right:15px;
padding:0 8px;
overflow:visible;
text-overflow:visible;
white-space:wrap;
@ -166,6 +167,9 @@ fieldset.switch-states{
.slide-button{
display:none;
}
input.state-name {
margin-left: -20px;
}
}
}
/* Hide size controls in IE - they won't work as intended */

View File

@ -214,6 +214,11 @@
height: 100%;
width: 100%;
.cms-preview-overlay {
width: 100%;
height: 100%;
}
.preview-note {
color: #CDD7DC;
display: block;

View File

@ -159,7 +159,7 @@ body.cms {
}
.ui-tabs-panel {
padding: $grid-x 0;
padding: $grid-x*2;
background: transparent; // default it's white
border: 0; // suppress default borders
&.cms-edit-form {
@ -174,7 +174,7 @@ body.cms {
.ui-tabs-nav {
float: right;
margin: 0 0 -1px 0;
margin: $grid-x*2 0 -1px 0;
padding: 0 $grid-x*1.5 0 0;
border-bottom: none;
@ -250,43 +250,17 @@ body.cms {
}
}
.cms-edit-form, .cms-content-fields { /*not sure if .cms-content-fields effects other areas*/
.cms-panel-padded { /* Has padded area inside it */
padding: 0;
margin: 0;
}
.ui-tabs-panel, .ss-gridfield {
margin: 12px;
padding: 0 0 12px;
.ss-gridfield { /* Files area & inside second level tabs */
padding: 0; /* should be zero ideally */
margin: 0 0 12px;
}
}
.ui-tabs-nav {
margin: 10px 12px 0;
padding: 0 8px 0 0; /* second set of tabs */
}
#tree_actions .ui-tabs-nav{
margin: 0;
}
}
.cms-panel-padded {
h3 {
margin-left: 12px; /* reports headers, probably too specific */
}
.ui-tabs-panel {
margin: 0;
padding: 12px 12px 12px;
padding: 0; // Avoid double padding with parent
.ui-tabs-panel {
padding: $grid-x 0 0 0;
}
}
}
.ui-tabs .ui-tabs-panel { /* second level tabs */
padding-top: 8px;
.Actions {
padding: 0; // Avoid double padding with parent
}
}
&.ss-tabset-tabshidden .ui-tabs-panel {
@ -301,8 +275,10 @@ body.cms {
.ui-tabs.cms-tabset-primary .ui-tabs-nav,
.ui-tabs .ui-tabs-nav.cms-tabset-nav-primary,
.ui-tabs .cms-content-header-tabs .ui-tabs-nav {
margin-top: 0;
border-left: 1px solid darken($color-tab, 15%);
float: none; // parent container is already right floated
li {
margin-right: 0; // tabs are directly adjacent
margin-top: 0;
@ -482,8 +458,17 @@ body.cms {
margin: 0;
}
}
p.message {
margin-bottom: $grid-y*1.5;
.cms-edit-form {
.message {
margin: $grid-x*2; // TODO Remove double padding when adjacent to a padded tabs panel
}
.ui-tabs-panel {
.message {
margin: 0 0 $grid-x*2 0; // gets padding from tab panel
}
}
}
/** --------------------------------------------
@ -609,8 +594,7 @@ p.message {
.cms-content-toolbar {
min-height: 29px;
display: block;
margin: 0 0 15px 0;
padding-bottom: 9px;
margin: 0 0 $grid-y 0;
@include doubleborder(bottom, $color-light-separator, $box-shadow-shine);
@include legacy-pie-clearfix();
@ -681,7 +665,7 @@ p.message {
*/
.cms-content-tools {
background: $tab-panel-texture-color;
width: $grid-x * 24;
width: $grid-x * 25;
overflow-y: auto;
overflow-x: hidden;
z-index: 70;
@ -703,8 +687,8 @@ p.message {
}
.cms-panel-content {
width: ($grid-x * 22);
padding: $grid-x $grid-x 0;
width: ($grid-x * 23);
padding: $grid-x*2 $grid-x 0; // smaller left/right padding to use space efficiently
overflow: auto;
height:100%;
@ -837,7 +821,6 @@ p.message {
float: left;
position: relative;
display: block;
margin-left: 8px;
.view-mode-batchactions-wrapper {
float: left;
@ -925,7 +908,6 @@ form.member-profile-form {
#Root_Main {
clear:both;
border-top: 1px solid darken($color-tab, 20%);
padding-top:$grid-y*2;
.cms-help-toggle {
text-indent: -9999em;
display: inline-block;
@ -1211,6 +1193,13 @@ form.member-profile-form {
}
}
.cms {
.cms-panel-padded {
padding: $grid-y*2 $grid-x*2;
margin:0;
}
}
/** ------------------------------------------------------------------
* Dialog
*

View File

@ -24,9 +24,6 @@
white-space: nowrap;
margin-left: 18px;
min-width: 18px;
ins.jstree-icon {
display: none;
}
&.jstree-open > ul {
display: block;
}
@ -36,10 +33,9 @@
&.disabled > a {
color: #aaaaaa;
}
ul {
ins.jstree-icon {
display: block;
}
// Expand/collapse arrows
& > .jstree-icon {
cursor: pointer;
}
}
ins {
@ -158,7 +154,7 @@
li.jstree-open > ul {
display: block;
margin-left: -18px;
margin-left: -13px;
li ul {
margin-left:2px;
}

View File

@ -303,17 +303,3 @@ table.ss-gridfield-table {
width: 190px; /* Width 100% not calculating by ie7 */
}
}
/* Tempory fix as jquery loads too slow to add icons */
button.ui-button-text-icon-primary {
padding-left: 30px !important;
span.ui-button-icon-primary {
position: absolute !important;
top: 5px !important;
left: 8px !important;
}
.ui-button-text {
margin-left: 0 !important;
}
}

View File

@ -19,25 +19,27 @@
</select>
</span>
<% if Items.Count < 5 %>
<fieldset id="preview-states" class="cms-preview-states switch-states size_{$Items.Count}">
<div class="switch">
<% loop Items %>
<input id="$Title" data-name="$Name" class="state-name $FirstLast" data-link="$Link" name="view" type="radio" <% if First %>checked<% end_if %>>
<label for="$Title"<% if First %> class="active"<% end_if %>><span>$Title</span></label>
<% end_loop %>
<span class="slide-button"></span>
</div>
</fieldset>
<% else %>
<span id="preview-state-dropdown" class="cms-preview-states field dropdown">
<select title="<% _t('SilverStripeNavigator.PreviewState', 'Preview State') %>" id="preview-states" class="preview-state dropdown nolabel" autocomplete="off" name="preview-state">
<% loop Items %>
<option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" >
$Title
</option>
<% end_loop %>
</select>
</span>
<% if Items %>
<% if Items.Count < 5 %>
<fieldset id="preview-states" class="cms-preview-states switch-states size_{$Items.Count}">
<div class="switch">
<% loop Items %>
<input id="$Title" data-name="$Name" class="state-name $FirstLast" data-link="$Link" name="view" type="radio" <% if First %>checked<% end_if %>>
<label for="$Title"<% if First %> class="active"<% end_if %>><span>$Title</span></label>
<% end_loop %>
<span class="slide-button"></span>
</div>
</fieldset>
<% else %>
<span id="preview-state-dropdown" class="cms-preview-states field dropdown">
<select title="<% _t('SilverStripeNavigator.PreviewState', 'Preview State') %>" id="preview-states" class="preview-state dropdown nolabel" autocomplete="off" name="preview-state">
<% loop Items %>
<option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" >
$Title
</option>
<% end_loop %>
</select>
</span>
<% end_if %>
<% end_if %>
</div>

View File

@ -60,20 +60,25 @@ class HTTP {
if(!is_numeric($tag)) $tagPrefix = "$tag ";
else $tagPrefix = "";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/ie";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/ie";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/ie";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/i";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/i";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/i";
}
$regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/ie';
$regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/ie';
$regExps[] = '/(list-style-image:[^;]*url *\()([^)]+)(\))/ie';
$regExps[] = '/(list-style:[^;]*url *\()([^)]+)(\))/ie';
$regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/i';
$regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/i';
$regExps[] = '/(list-style-image:[^;]*url *\()([^)]+)(\))/i';
$regExps[] = '/(list-style:[^;]*url *\()([^)]+)(\))/i';
// Make
$code = 'stripslashes("$1") . (' . str_replace('$URL', 'stripslashes("$2")', $code) . ') . stripslashes("$3")';
$callback = function($matches) use($code) {
return
stripslashes($matches[1]) .
str_replace('$URL', stripslashes($matches[2]), $code) .
stripslashes($matches[3]);
};
foreach($regExps as $regExp) {
$content = preg_replace($regExp, $code, $content);
$content = preg_replace_callback($regExp, $callback, $content);
}
return $content;

View File

@ -244,9 +244,12 @@ class Convert {
// Expand hyperlinks
if(!$preserveLinks && !$config['PreserveLinks']) {
$data = preg_replace('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'",
$data);
$data = preg_replace('/<a[^>]*href\s*=\s*([^ ]*)>(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", $data);
$data = preg_replace_callback('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/i', function($matches) {
return Convert::html2raw($matches[2]) . "[$matches[1]]";
}, $data);
$data = preg_replace_callback('/<a[^>]*href\s*=\s*([^ ]*)>(.*?)<\/a>/i', function($matches) {
return Convert::html2raw($matches[2]) . "[$matches[1]]";
}, $data);
}
// Replace images with their alt tags

View File

@ -164,12 +164,19 @@ abstract class Object {
// Keep track of the current bucket that we're putting data into
$bucket = &$args;
$bucketStack = array();
$had_ns = false;
foreach($tokens as $token) {
$tName = is_array($token) ? $token[0] : $token;
// Get the class naem
if($class == null && is_array($token) && $token[0] == T_STRING) {
$class = $token[1];
} elseif(is_array($token) && $token[0] == T_NS_SEPARATOR) {
$class .= $token[1];
$had_ns = true;
} elseif ($had_ns && is_array($token) && $token[0] == T_STRING) {
$class .= $token[1];
$had_ns = false;
// Get arguments
} else if(is_array($token)) {
switch($token[0]) {

View File

@ -11,8 +11,9 @@
Used in side panels and action tabs
*/
.ss-uploadfield-view-allowed-extensions { padding-top: 20px; clear: both; max-width: 750px; display: block; }
.ss-uploadfield-view-allowed-extensions .toggle { font-style: normal; font-size: 11px; }
#AssetUploadField { border-bottom: 0; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; padding: 12px; }
#AssetUploadField { border-bottom: 0; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; }
.backlink { padding-left: 12px; }
@ -68,9 +69,7 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info { float: left; margin: 34px 0 0; }
.ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info { margin: 15px 0px 0 20px; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info label { font-size: 16px; line-height: 30px; padding: 5px 16px; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-fromcomputer { /*position: relative;
overflow: hidden;
display: block;*/ }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-fromcomputer { /*position: relative; */ overflow: hidden; display: block; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-uploador { float: left; font-weight: bold; font-size: 22px; padding: 0 20px; line-height: 70px; margin-top: 16px; display: none; }
.ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-uploador { font-size: 18px; margin-top: 0; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone { margin-top: 9px; -webkit-border-radius: 13px; -moz-border-radius: 13px; -ms-border-radius: 13px; -o-border-radius: 13px; border-radius: 13px; -webkit-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #fafafa; -moz-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #fafafa; box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #fafafa; border: 2px dashed gray; background: #d4dbe0; display: none; height: 82px; width: 360px; float: left; }

View File

@ -1,4 +1,4 @@
#Remember { margin: 0.5em 0 0.5em 11em !important; }
#Remember { margin: 0.5em 0 0.5em 11em; }
p#Remember label { display: inline-block; margin: 0; }

View File

@ -16,8 +16,8 @@ Used in side panels and action tabs
.ss-uploadfield .middleColumn { width: 526px; padding: 0; background: #fff; border: 1px solid #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -o-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); }
.ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -webkit-box-shadow: gray 0 0 4px 0 inset; -moz-box-shadow: gray 0 0 4px 0 inset; box-shadow: gray 0 0 4px 0 inset; border: 2px dashed gray; background: #d0d3d5; display: none; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { margin: 0 0 0 100px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -webkit-box-shadow: gray 0 0 4px 0 inset; -moz-box-shadow: gray 0 0 4px 0 inset; box-shadow: gray 0 0 4px 0 inset; border: 2px dashed gray; background: #d0d3d5; display: none; margin-right: 15px; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { float: left; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name { display: block; line-height: 13px; height: 26px; margin: 0; text-align: left; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name b { font-weight: bold; padding: 0 5px 0 0; }
.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .name { font-size: 11px; color: #848484; width: 290px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; display: inline; float: left; }

View File

@ -79,9 +79,6 @@ class DevelopmentAdmin extends Controller {
"build" => "Build/rebuild this environment. Call this whenever you have updated your project sources",
"tests" => "See a list of unit tests to run",
"tests/all" => "Run all tests",
"tests/startsession" => "Start a test session in your browser"
. " (gives you a temporary database with default content)",
"tests/endsession" => "Ends a test session",
"jstests" => "See a list of JavaScript tests to run",
"jstests/all" => "Run all JavaScript tests",
"tasks" => "See a list of build tasks to run"
@ -191,16 +188,6 @@ Config::inst()->update('Security', 'token', '$token');
TXT;
}
public function reset() {
$link = BASE_URL.'/dev/tests/startsession';
return "<p>The dev/reset feature has been removed. If you are trying to test your site " .
"with a clean datababase, we recommend that you use " .
"<a href=\"$link\">dev/test/startsession</a> ".
"instead.</P>";
}
public function errors() {
$this->redirect("Debug_");
}

View File

@ -129,8 +129,9 @@ class FixtureBlueprint {
$parsedItems = array();
$items = preg_split('/ *, */',trim($fieldVal));
foreach($items as $item) {
// Check for correct format: =><relationname>.<identifier>
if(!preg_match('/^=>[^\.]+\.[^\.]+/', $item)) {
// Check for correct format: =><relationname>.<identifier>.
// Ignore if the item has already been replaced with a numeric DB identifier
if(!is_numeric($item) && !preg_match('/^=>[^\.]+\.[^\.]+/', $item)) {
throw new InvalidArgumentException(sprintf(
'Invalid format for relation "%s" on class "%s" ("%s")',
$fieldName,

View File

@ -30,7 +30,7 @@ class SS_LogErrorFileFormatter implements Zend_Log_Formatter_Interface {
$urlSuffix = '';
$relfile = Director::makeRelative($errfile);
if($relfile[0] == '/') $relfile = substr($relfile, 1);
if(strlen($relfile) && $relfile[0] == '/') $relfile = substr($relfile, 1);
if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] && isset($_SERVER['REQUEST_URI'])) {
$urlSuffix = " (http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI])";
}

View File

@ -460,22 +460,30 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1);
// Restore email configuration
Email::set_mailer($this->originalMailer);
$this->originalMailer = null;
$this->mailer = null;
if($this->originalMailer) {
Email::set_mailer($this->originalMailer);
$this->originalMailer = null;
}
$this->mailer = null;
// Restore password validation
Member::set_password_validator($this->originalMemberPasswordValidator);
if($this->originalMemberPasswordValidator) {
Member::set_password_validator($this->originalMemberPasswordValidator);
}
// Restore requirements
Requirements::set_backend($this->originalRequirements);
if($this->originalRequirements) {
Requirements::set_backend($this->originalRequirements);
}
// Mark test as no longer being run - we use originalIsRunningTest to allow for nested SapphireTest calls
self::$is_running_test = $this->originalIsRunningTest;
$this->originalIsRunningTest = null;
// Reset theme setting
SSViewer::set_theme($this->originalTheme);
if($this->originalTheme) {
SSViewer::set_theme($this->originalTheme);
}
// Reset mocked datetime
SS_Datetime::clear_mock_now();

View File

@ -29,12 +29,7 @@ class TestRunner extends Controller {
'coverage/module/$ModuleName' => 'coverageModule',
'coverage/$TestCase!' => 'coverageOnly',
'coverage' => 'coverageAll',
'sessionloadyml' => 'sessionloadyml',
'startsession' => 'startsession',
'endsession' => 'endsession',
'setdb' => 'setdb',
'cleanupdb' => 'cleanupdb',
'emptydb' => 'emptydb',
'module/$ModuleName' => 'module',
'all' => 'all',
'build' => 'build',
@ -48,9 +43,6 @@ class TestRunner extends Controller {
'coverageAll',
'coverageModule',
'coverageOnly',
'startsession',
'endsession',
'setdb',
'cleanupdb',
'module',
'all',
@ -341,204 +333,6 @@ class TestRunner extends Controller {
if(Director::is_cli() && ($results->failureCount() + $results->errorCount()) > 0) exit(2);
}
/**
* Start a test session.
* Usage: visit dev/tests/startsession?fixture=(fixturefile). A test database will be constructed, and your
* browser session will be amended to use this database. This can only be run on dev and test sites.
*
* See {@link setdb()} for an alternative approach which just sets a database
* name, and is used for more advanced use cases like interacting with test databases
* directly during functional tests.
*
* Requires PHP's mycrypt extension in order to set the database name
* as an encrypted cookie.
*/
public function startsession() {
if(!Director::isLive()) {
if(SapphireTest::using_temp_db()) {
$endLink = Director::baseURL() . "dev/tests/endsession";
return "<p><a id=\"end-session\" href=\"$endLink\">You're in the middle of a test session;"
. " click here to end it.</a></p>";
} else if(!isset($_GET['fixture'])) {
$me = Director::baseURL() . "dev/tests/startsession";
return <<<HTML
<form action="$me">
<p>Enter a fixture file name to start a new test session. Don't forget to visit dev/tests/endsession when
you're done!</p>
<p>Fixture file (leave blank to start with default set-up): <input id="fixture-file" name="fixture" /></p>
<input type="hidden" name="flush" value="1">
<p><input id="start-session" value="Start test session" type="submit" /></p>
</form>
HTML;
} else {
$fixtureFile = $_GET['fixture'];
if($fixtureFile) {
// Validate fixture file
$realFile = realpath(BASE_PATH.'/'.$fixtureFile);
$baseDir = realpath(Director::baseFolder());
if(!$realFile || !file_exists($realFile)) {
return "<p>Fixture file doesn't exist</p>";
} else if(substr($realFile,0,strlen($baseDir)) != $baseDir) {
return "<p>Fixture file must be inside $baseDir</p>";
} else if(substr($realFile,-4) != '.yml') {
return "<p>Fixture file must be a .yml file</p>";
} else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) {
return "<p>Fixture file must be inside the tests subfolder of one of your modules.</p>";
}
}
$dbname = SapphireTest::create_temp_db();
DB::set_alternative_database_name($dbname);
// Fixture
if($fixtureFile) {
$fixture = Injector::inst()->create('YamlFixture', $fixtureFile);
$fixture->saveIntoDatabase();
// If no fixture, then use defaults
} else {
$dataClasses = ClassInfo::subclassesFor('DataObject');
array_shift($dataClasses);
foreach($dataClasses as $dataClass) singleton($dataClass)->requireDefaultRecords();
}
return "<p>Started testing session with fixture '$fixtureFile'.
Time to start testing; where would you like to start?</p>
<ul>
<li><a id=\"home-link\" href=\"" .Director::baseURL() . "\">Homepage - published site</a></li>
<li><a id=\"draft-link\" href=\"" .Director::baseURL() . "?stage=Stage\">Homepage - draft site
</a></li>
<li><a id=\"admin-link\" href=\"" .Director::baseURL() . "admin/\">CMS Admin</a></li>
<li><a id=\"endsession-link\" href=\"" .Director::baseURL() . "dev/tests/endsession\">
End your test session</a></li>
</ul>";
}
} else {
return "<p>startession can only be used on dev and test sites</p>";
}
}
/**
* Set an alternative database name in the current browser session as a cookie.
* Useful for functional testing libraries like behat to create a "clean slate".
* Does not actually create the database, that's usually handled
* by {@link SapphireTest::create_temp_db()}.
*
* The database names are limited to a specific naming convention as a security measure:
* The "tmpdb" prefix and a random sequence of seven digits.
* This avoids the user gaining access to other production databases
* available on the same connection.
*
* See {@link startsession()} for a different approach which actually creates
* the DB and loads a fixture file instead.
*
* Requires PHP's mycrypt extension in order to set the database name
* as an encrypted cookie.
*/
public function setdb() {
if(Director::isLive()) {
return $this->httpError(403, "dev/tests/setdb can only be used on dev and test sites");
}
if(!isset($_GET['database'])) {
return $this->httpError(400, "dev/tests/setdb must be used with a 'database' parameter");
}
$name = $_GET['database'];
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
$pattern = strtolower(sprintf('#^%stmpdb\d{7}#', $prefix));
if($name && !preg_match($pattern, $name)) {
return $this->httpError(400, "Invalid database name format");
}
DB::set_alternative_database_name($name);
if($name) {
return "<p>Set database session to '$name'.</p>";
} else {
return "<p>Unset database session.</p>";
}
}
public function emptydb() {
if(SapphireTest::using_temp_db()) {
SapphireTest::empty_temp_db();
if(isset($_GET['fixture']) && ($fixtureFile = $_GET['fixture'])) {
$fixture = Injector::inst()->create('YamlFixture', $fixtureFile);
$fixture->saveIntoDatabase();
return "<p>Re-test the test database with fixture '$fixtureFile'. Time to start testing; where would"
. " you like to start?</p>";
} else {
return "<p>Re-test the test database. Time to start testing; where would you like to start?</p>";
}
} else {
return "<p>dev/tests/emptydb can only be used with a temporary database. Perhaps you should use"
. " dev/tests/startsession first?</p>";
}
}
public function endsession() {
SapphireTest::kill_temp_db();
DB::set_alternative_database_name(null);
return "<p>Test session ended.</p>
<ul>
<li><a id=\"home-link\" href=\"" .Director::baseURL() . "\">Return to your site</a></li>
<li><a id=\"startsession-link\" href=\"" .Director::baseURL() . "dev/tests/startsession\">
Start a new test session</a></li>
</ul>";
}
public function sessionloadyml() {
// Load incremental YAML fixtures
// TODO: We will probably have to filter out the admin member here,
// as it is supplied by Bare.yml
if(Director::isLive()) {
return "<p>sessionloadyml can only be used on dev and test sites</p>";
}
if (!SapphireTest::using_temp_db()) {
return "<p>Please load /dev/tests/startsession first</p>";
}
$fixtureFile = isset($_GET['fixture']) ? $_GET['fixture'] : null;
if (empty($fixtureFile)) {
$me = Director::baseURL() . "/dev/tests/sessionloadyml";
return <<<HTML
<form action="$me">
<p>Enter a fixture file name to load a new YAML fixture into the session.</p>
<p>Fixture file <input id="fixture-file" name="fixture" /></p>
<input type="hidden" name="flush" value="1">
<p><input id="session-load-yaml" value="Load yml fixture" type="submit" /></p>
</form>
HTML;
}
// Validate fixture file
$realFile = realpath(BASE_PATH.'/'.$fixtureFile);
$baseDir = realpath(Director::baseFolder());
if(!$realFile || !file_exists($realFile)) {
return "<p>Fixture file doesn't exist</p>";
} else if(substr($realFile,0,strlen($baseDir)) != $baseDir) {
return "<p>Fixture file must be inside $baseDir</p>";
} else if(substr($realFile,-4) != '.yml') {
return "<p>Fixture file must be a .yml file</p>";
} else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) {
return "<p>Fixture file must be inside the tests subfolder of one of your modules.</p>";
}
// Fixture
$fixture = Injector::inst()->create('YamlFixture', $fixtureFile);
$fixture->saveIntoDatabase();
return "<p>Loaded fixture '$fixtureFile' into session</p>";
}
public function setUp() {
// The first DB test will sort out the DB, we don't have to
SSViewer::flush_template_cache();

View File

@ -1281,6 +1281,7 @@ ErrorDocument 500 /assets/error-500.html
$baseClause
RewriteCond %{REQUEST_URI} ^(.*)$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !\.php$
RewriteRule .* $modulePath/main.php?url=%1&%{QUERY_STRING} [L]
</IfModule>
TEXT;

View File

@ -2,6 +2,28 @@
## Overview ##
### CMS
* "Split view" editing with side-by-side preview of the edited website
* Resizing of preview to common screen widths ("desktop", "tablet" and "smartphone")
* Decluttered "Edit Page" buttons by moving minor actions into a "more options" panel
* Auto-detect CMS changes and highlight the save button for better informancy
* Display "last edited" and "last published" data for pages in CMS
* CMS form fields now support help text through `setDescription()`, both inline and as tooltips
* Removed SiteTree "MetaTitle" and "MetaKeywords" fields
* More legible and simplified tab and menu styling in the CMS
### Framework
* `DataList` and `ArrayList` are now immutable, they'll return cloned instances on modification
* Behaviour testing support through [Behat](http://behat.org), with CMS test coverage
(see the [SilverStripe Behat Extension]() for details)
* Removed legacy table APIs (e.g. `TableListField`), use GridField instead
* Editing of relation table data (`$many_many_extraFields`) in `GridField`
* Optional integration with ImageMagick as a new image manipulation backend
* Support for PHP 5.4's built-in webserver
* Support for [Composer](http://getcomposer.org) dependency manager (also works with 3.0)
## Upgrading
### Grouped CMS Buttons
@ -44,12 +66,46 @@ you'll need to adjust your code.
}
}
### GridField and ModelAdmin Permission Checks
`GridFieldDetailForm` now checks for `canEdit()` and `canDelete()` permissions
on your model. `GridFieldAddNewButton` checks `canCreate()`.
The default implementation requires `ADMIN` permissions.
You'll need to loosen those permissions if you want other users with CMS
access to interact with your data.
Since `GridField` is used in `ModelAdmin`, this change will affect both classes.
Example: Require "CMS: Pages section" access
:::php
class MyModel extends DataObject {
public function canView($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canEdit($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canDelete($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canCreate($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
You can also implement [custom permission codes](/topics/permissions).
For 3.1.0 stable, we aim to further simplify the permission definitions,
in order to reduce the boilerplate code required to get a model editable in the CMS.
Note: GridField is already relying on the permission checks performed
through the CMS controllers, providing a simple level of security.
### Other
* `TableListField`, `ComplexTableField`, `TableField`, `HasOneComplexTableField`, `HasManyComplexTableField` and `ManyManyComplexTableField` have been removed from the core and placed into a module called "legacytablefields" located at https://github.com/silverstripe-labs/legacytablefields
* `prototype.js` and `behaviour.js` have been removed from the core, they are no longer used. If you have custom code relying on these two libraries, please update your code to include the files yourself
* `Object::has_extension()` and `Object::add_extension()` deprecated in favour of using late static binding, please use `{class}::has_extension()` and `{class}::add_extension()` instead, where {class} is the class name of your DataObject class.
* Removed `SiteTree.MetaTitle` and `SiteTree.MetaKeywords` since they are irrelevant in terms of SEO ([1](http://www.seomoz.org/learn-seo/title-tag), [2](http://www.mattcutts.com/blog/keywords-meta-tag-in-web-search/)) and general page informancy
* Removed `SiteTree.MetaKeywords` since they are irrelevant in terms of SEO ([seomoz article](http://www.mattcutts.com/blog/keywords-meta-tag-in-web-search/)) and general page informancy
* Removed `SiteTree.MetaTitle` as a means to customize the window title, use `SiteTree.Title` instead
* Deprecated `Profiler` class, use third-party solutions like [xhprof](https://github.com/facebook/xhprof/)
* Removed defunct or unnecessary debug GET parameters:
`debug_profile`, `debug_memory`, `profile_trace`, `debug_javascript`, `debug_behaviour`

View File

@ -0,0 +1,342 @@
# 3.1.0-beta1 (unreleased) #
## Overview ##
### CMS
* "Split view" editing with side-by-side preview of the edited website
* Resizing of preview to common screen widths ("desktop", "tablet" and "smartphone")
* Decluttered "Edit Page" buttons by moving minor actions into a "more options" panel
* Auto-detect CMS changes and highlight the save button for better informancy
* Display "last edited" and "last published" data for pages in CMS
* CMS form fields now support help text through `setDescription()`, both inline and as tooltips
* More legible and simplified tab and menu styling in the CMS
### Framework
* `DataList` and `ArrayList` are now immutable, they'll return cloned instances on modification
* Behaviour testing support through [Behat](http://behat.org), with CMS test coverage
(see the [SilverStripe Behat Extension]() for details)
* Removed legacy table APIs (e.g. `TableListField`), use GridField instead
* Editing of relation table data (`$many_many_extraFields`) in `GridField`
* Optional integration with ImageMagick as a new image manipulation backend
* Support for PHP 5.4's built-in webserver
## Upgrading
See [3.1.0 release notes](/changelogs/3.1.0)
## Changelog
### API Changes
* 2012-12-17 [bbc8e06](https://github.com/silverstripe/sapphire/commit/bbc8e06) Show GridFieldEditButton even without edit permissions (for readonly forms) (Ingo Schommer)
* 2012-12-17 [1848d7e](https://github.com/silverstripe/sapphire/commit/1848d7e) Check model permissions in GridField (Ingo Schommer)
* 2012-12-14 [644cc79](https://github.com/silverstripe/sapphire/commit/644cc79) Removed methods previously deprecated in 3.0 (Ingo Schommer)
* 2012-12-13 [6f9d01f](https://github.com/silverstripe/sapphire/commit/6f9d01f) FormField-&gt;setDescription() visible in default template (Ingo Schommer)
* 2012-12-13 [559abec](https://github.com/silverstripe/sapphire/commit/559abec) Copying instance props on FormField readonly/disabled transformations (Ingo Schommer)
* 2012-12-13 [2e164ea](https://github.com/silverstripe/silverstripe-cms/commit/2e164ea) Report::get_reports() returns native array (fixes #8096) (Ingo Schommer)
* 2012-12-12 [27113f8](https://github.com/silverstripe/sapphire/commit/27113f8) Make DataList and ArrayList immutable (Hamish Friedlander)
* 2012-12-10 [e6e47cb](https://github.com/silverstripe/sapphire/commit/e6e47cb) DB-specific comparisators in SearchFilter and DataList (Ingo Schommer)
* 2012-12-11 [4d6d823](https://github.com/silverstripe/sapphire/commit/4d6d823) Allow ignoring persistent tab state through entwine property. (Mateusz Uzdowski)
* 2012-12-04 [4fa2b0f](https://github.com/silverstripe/sapphire/commit/4fa2b0f) Support disabling/enabling of previews. (Mateusz Uzdowski)
* 2012-12-03 [5fed5b9](https://github.com/silverstripe/sapphire/commit/5fed5b9) Moved email bounce handling to new 'emailbouncehandler' module (Ingo Schommer)
* 2012-12-03 [fb076c0](https://github.com/silverstripe/sapphire/commit/fb076c0) Deprecated global email methods, moved to Mailer class (Ingo Schommer)
* 2012-11-30 [548ad50](https://github.com/silverstripe/sapphire/commit/548ad50) Removed keyed arrays for title/value setting in SelectionGroup (Ingo Schommer)
* 2012-11-29 [8f5acd7](https://github.com/silverstripe/sapphire/commit/8f5acd7) Move state to enwtine properties, provide API for preview. (Mateusz Uzdowski)
* 2012-11-29 [47f41d8](https://github.com/silverstripe/silverstripe-cms/commit/47f41d8) Machine-friendly name for CMS states navigator (stages). (Mateusz Uzdowski)
* 2012-11-26 [d4f13fe](https://github.com/silverstripe/sapphire/commit/d4f13fe) Refactor the CMS layouting to provide access to options. (Mateusz Uzdowski)
* 2012-11-22 [fe08236](https://github.com/silverstripe/sapphire/commit/fe08236) Add action tabsets as a interface idiom. (Mateusz Uzdowski)
* 2012-11-22 [26cc14a](https://github.com/silverstripe/silverstripe-cms/commit/26cc14a) Rework the CMS actions to use alternating buttons and drop-ups. (Mateusz Uzdowski)
* 2012-11-15 [8b0bb8d](https://github.com/silverstripe/sapphire/commit/8b0bb8d) Replace deprecated FormField::createTag() with static create_tag() (Sean Harvey)
* 2012-11-07 [b02b1e6](https://github.com/silverstripe/sapphire/commit/b02b1e6) Forward the editor events to underlying textarea. (Mateusz Uzdowski)
* 2012-11-02 [683db8d](https://github.com/silverstripe/sapphire/commit/683db8d) Explicitly load project template files after modules (Will Rossiter)
* 2012-10-30 [d54b1b4](https://github.com/silverstripe/sapphire/commit/d54b1b4) Removed permission checks from XML/JSON data formatters (Ingo Schommer)
* 2012-10-12 [a3295e2](https://github.com/silverstripe/sapphire/commit/a3295e2) File-&gt;canEdit() returns TRUE by default (not checking CMS perms) (Ingo Schommer)
* 2012-10-10 [b31188f](https://github.com/silverstripe/silverstripe-cms/commit/b31188f) Use late static binding for Object::has_extension() (Andrew O'Neil)
* 2012-10-10 [0c8de0a](https://github.com/silverstripe/sapphire/commit/0c8de0a) Use late static binding for Object::has_extension() (Andrew O'Neil)
* 2012-10-10 [48a9bcf](https://github.com/silverstripe/silverstripe-cms/commit/48a9bcf) Use late static binding for Object::remove_extension() (Andrew O'Neil)
* 2012-10-10 [6dd6a5c](https://github.com/silverstripe/sapphire/commit/6dd6a5c) Use late static binding for Object::remove_extension() (Andrew O'Neil)
* 2012-10-09 [1722f00](https://github.com/silverstripe/silverstripe-cms/commit/1722f00) add_extension() is now called directly on the class, instead of on Object (Andrew O'Neil)
* 2012-10-09 [fdea532](https://github.com/silverstripe/sapphire/commit/fdea532) add_extension() is now called directly on the class, instead of on Object (Andrew O'Neil)
* 2012-09-27 [39952f4](https://github.com/silverstripe/sapphire/commit/39952f4) Added 'onBeforeHTTPError' and 'onBeforeHTTPError&lt;code&gt;' extension points to RequestHandler::httpError(). (Sam Minnee)
* 2012-09-26 [aa6f345](https://github.com/silverstripe/sapphire/commit/aa6f345) FormField::name_to_label() for unlabelled fields (Howard Grigg)
* 2012-09-21 [cbd31e3](https://github.com/silverstripe/silverstripe-cms/commit/cbd31e3) Removed SiteTree.MetaTitle and MetaKeywords (Ingo Schommer)
* 2012-09-21 [a3007b6](https://github.com/silverstripe/silverstripe-cms/commit/a3007b6) moved StaticCache / StaticPublisher to module. (Will Rossiter)
* 2012-09-21 [e72114d](https://github.com/silverstripe/sapphire/commit/e72114d) Remove static main and dev/buildcache (Will Rossiter)
* 2012-09-20 [6d696d5](https://github.com/silverstripe/sapphire/commit/6d696d5) Allow subgroups in the WHERE clause of a Data/SQLQuery (Simon Welsh)
* 2012-09-20 [c49f756](https://github.com/silverstripe/sapphire/commit/c49f756) Allow use of :not, :nocase and :case modifiers to SearchFilters. (Simon Welsh)
* 2012-09-06 [79b3f8a](https://github.com/silverstripe/sapphire/commit/79b3f8a) Add exclude() method to SearchFilters that excludes items that match the filter. (Simon Welsh)
* 2012-09-06 [2faf7d1](https://github.com/silverstripe/sapphire/commit/2faf7d1) Allow using SearchFilters in DataList::exclude() (Simon Welsh)
* 2012-07-05 [0fe515e](https://github.com/silverstripe/sapphire/commit/0fe515e) Deprecated Profiler class, removed related debug GET params (Ingo Schommer)
* 2012-06-30 [78fdcc5](https://github.com/silverstripe/sapphire/commit/78fdcc5) DataList-&gt;leftJoin()/innerJoin() args no longer escaped (Simon Welsh)
### Features and Enhancements
* 2012-12-13 [7e46290](https://github.com/silverstripe/sapphire/commit/7e46290) Date-&gt;Ago() with "less than a minute" support (Ingo Schommer)
* 2012-12-13 [1ca3883](https://github.com/silverstripe/sapphire/commit/1ca3883) Tooltip and inline help text support for CMS form fields (Ingo Schommer)
* 2012-11-22 [f4b080e](https://github.com/silverstripe/sapphire/commit/f4b080e) Side by side editing functionality - first cut (os#7412) (Mateusz Uzdowski)
* 2012-11-15 [639f6e4](https://github.com/silverstripe/silverstripe-cms/commit/639f6e4) Side by side editing functionality - first cut (os#7412) (Naomi Guyer)
* 2012-11-08 [c8136f5](https://github.com/silverstripe/sapphire/commit/c8136f5) Many-many relation data editing in GridFieldDetailForm (Ingo Schommer)
* 2012-11-06 [a52514a](https://github.com/silverstripe/silverstripe-cms/commit/a52514a) Tab style consolidation and design consistency (Ingo Schommer)
* 2012-11-06 [2d07567](https://github.com/silverstripe/sapphire/commit/2d07567) Tab style consolidation and design consistency (Ingo Schommer)
* 2012-11-06 [dbbcd08](https://github.com/silverstripe/sapphire/commit/dbbcd08) Extend the ssui.button with alternate appearances. (Mateusz Uzdowski)
* 2012-11-05 [411673e](https://github.com/silverstripe/sapphire/commit/411673e) general css enhancements (Paul Clarke)
* 2012-11-03 [bbc4443](https://github.com/silverstripe/sapphire/commit/bbc4443) Allows setting of has_many and many_many relations before writing (Simon Welsh)
* 2012-11-02 [26e5afc](https://github.com/silverstripe/sapphire/commit/26e5afc) Add new method "each" to SS_List and core implementors thereof (Justin Martin)
* 2012-10-24 [d24b586](https://github.com/silverstripe/sapphire/commit/d24b586) Enable multiple image manipulation back-ends on the Image class (Justin Martin)
* 2012-10-12 [5be3a4c](https://github.com/silverstripe/sapphire/commit/5be3a4c) DataList-&gt;filterAny() (Ingo Schommer)
* 2012-10-08 [1711303](https://github.com/silverstripe/silverstripe-cms/commit/1711303) Enable SiteTree::$nested_urls by default (Ingo Schommer)
* 2012-10-08 [38e7df2](https://github.com/silverstripe/sapphire/commit/38e7df2) Enable SiteTree::$nested_urls by default (Ingo Schommer)
* 2012-10-05 [76e569a](https://github.com/silverstripe/silverstripe-cms/commit/76e569a) open/7886 added preview button to the settings page so that when a user changes the theme they can preview the change. (Jeremy Bridson)
* 2012-10-05 [ad7383a](https://github.com/silverstripe/sapphire/commit/ad7383a) open/7886 added preview button to the settings page so that when a user changes the theme they can preview the change. (Jeremy Bridson)
* 2012-10-04 [8108f7f](https://github.com/silverstripe/sapphire/commit/8108f7f) Relation search for GridFieldAddExistingAutocompleter (Ingo Schommer)
* 2012-09-20 [a670e4c](https://github.com/silverstripe/sapphire/commit/a670e4c) open/7875 - added help labels to metadata fields on page content edit screen. (Jeremy Bridson)
* 2012-09-20 [05d5bd7](https://github.com/silverstripe/silverstripe-cms/commit/05d5bd7) open/7875 - added help labels to metadata fields on page content edit screen. (Jeremy Bridson)
* 2012-09-11 [1005571](https://github.com/silverstripe/sapphire/commit/1005571) Added support for PHP 5.4's built-in webserver. (Sam Minnee)
* 2012-07-23 [f1db583](https://github.com/silverstripe/sapphire/commit/f1db583) Allow arguments to be passed to allowed_action checkers (Simon Welsh)
* 2012-07-15 [6e2d6c2](https://github.com/silverstripe/sapphire/commit/6e2d6c2) Hide the search bar in Chosen dropdown fields when list is reasonably short. (unclecheese)
* 2012-06-29 [ebb2458](https://github.com/silverstripe/sapphire/commit/ebb2458) Improving Cookie class to allow for extendability (Matt Lewis)
### Bugfixes
* 2012-12-17 [6028cf1](https://github.com/silverstripe/sapphire/commit/6028cf1) ed panel spacing regressions from 544d2eb6 (Ingo Schommer)
* 2012-12-17 [cc536f6](https://github.com/silverstripe/silverstripe-cms/commit/cc536f6) ed "last edited" display in CMS actions (Ingo Schommer)
* 2012-12-17 [a823c38](https://github.com/silverstripe/sapphire/commit/a823c38) ed JS syntax error (Ingo Schommer)
* 2012-12-16 [bf5590d](https://github.com/silverstripe/sapphire/commit/bf5590d) Fix side-by-side initial icon display issue in IE8. (Mateusz Uzdowski)
* 2012-12-16 [8455686](https://github.com/silverstripe/sapphire/commit/8455686) Fix the re-layouting not being triggered in IE8. (Mateusz Uzdowski)
* 2012-12-15 [22eeaa4](https://github.com/silverstripe/sapphire/commit/22eeaa4) Members should not be allowed to delete themselves (fixes #8121) (Ingo Schommer)
* 2012-12-15 [b365714](https://github.com/silverstripe/sapphire/commit/b365714) Remove "delete" button from "My Profile" (fixes #8121) (Ingo Schommer)
* 2012-12-15 [c2d31e5](https://github.com/silverstripe/silverstripe-cms/commit/c2d31e5) Hiding group selections in "Settings" (Ingo Schommer)
* 2012-12-14 [d2c1d53](https://github.com/silverstripe/sapphire/commit/d2c1d53) ed UploadField-&gt;setDescription() handlgin (Ingo Schommer)
* 2012-12-14 [74d6379](https://github.com/silverstripe/silverstripe-cms/commit/74d6379) ed regression in SiteTree-&gt;getCMSActions() (Ingo Schommer)
* 2012-12-13 [a355e1d](https://github.com/silverstripe/sapphire/commit/a355e1d) Set visibility on login form methods to public. (Justin Martin)
* 2012-12-13 [006790b](https://github.com/silverstripe/sapphire/commit/006790b) ed IE7 GridField "add row" alignment issue (Ingo Schommer)
* 2012-12-13 [0ba51c1](https://github.com/silverstripe/sapphire/commit/0ba51c1) to allow buttons to align inline (fixes #8099) (Paul Clarke)
* 2012-12-13 [bdc3e91](https://github.com/silverstripe/sapphire/commit/bdc3e91) ed wrong floating on GridField certain buttons (Ingo Schommer)
* 2012-12-13 [bd59f84](https://github.com/silverstripe/sapphire/commit/bd59f84) Make sure you can only remove items from a DataList that are actually in it (Hamish Friedlander)
* 2012-12-13 [9979b11](https://github.com/silverstripe/sapphire/commit/9979b11) Make sure ArrayList#limit uses clone so for subclasses it returns instances of same subclass (Hamish Friedlander)
* 2012-12-12 [546762e](https://github.com/silverstripe/silverstripe-cms/commit/546762e) use of DataList#innerJoin expecting it to be mutable (Hamish Friedlander)
* 2012-12-12 [c0a1226](https://github.com/silverstripe/sapphire/commit/c0a1226) fix and code clean up (Paul Clarke)
* 2012-12-12 [5d93f8d](https://github.com/silverstripe/sapphire/commit/5d93f8d) fix preview note "Website preview" (Paul Clarke)
* 2012-12-11 [0f60ca7](https://github.com/silverstripe/sapphire/commit/0f60ca7) Confirmed Password Field now copies attributes to child fields. (Justin Martin)
* 2012-12-11 [9803459](https://github.com/silverstripe/sapphire/commit/9803459) ed SelectionGroupTest (Ingo Schommer)
* 2012-12-10 [084acc0](https://github.com/silverstripe/silverstripe-cms/commit/084acc0) ed Behat tests for preview feature (Ingo Schommer)
* 2012-12-10 [0fd6d14](https://github.com/silverstripe/sapphire/commit/0fd6d14) ed Behat steps for preview feature (Ingo Schommer)
* 2012-12-04 [4d106aa](https://github.com/silverstripe/sapphire/commit/4d106aa) Fixed unintentional ParentID reset in "add page" form (Ingo Schommer)
* 2012-12-04 [b8c656b](https://github.com/silverstripe/sapphire/commit/b8c656b) ed cms extension docs to remove zzz_admin workaround (Ingo Schommer)
* 2012-12-04 [65002f6](https://github.com/silverstripe/sapphire/commit/65002f6) GD::greyscale did not correctly preserve alpha component of images Added test cases to test greyscale operation across various image formats Replaced various magic numbers with IMAGETYPE_XXX definitions (Damian Mooyman)
* 2012-12-03 [f9a5601](https://github.com/silverstripe/silverstripe-cms/commit/f9a5601) Enforce "add page" restrictions, improve UI (fixes #7879) (Ingo Schommer)
* 2012-12-03 [a63a9f0](https://github.com/silverstripe/silverstripe-cms/commit/a63a9f0) removed class cms-panel-link as it was calling loadPanel to be called twice trac 8041 (Kirk Mayo)
* 2012-12-03 [da1a6e7](https://github.com/silverstripe/sapphire/commit/da1a6e7) Unable to return to site tree admin from Preview mode trac 8063 (Kirk Mayo)
* 2012-11-30 [0808a1c](https://github.com/silverstripe/sapphire/commit/0808a1c) ed JS syntax error (Ingo Schommer)
* 2012-11-30 [dbaf407](https://github.com/silverstripe/sapphire/commit/dbaf407) ed help text alignment for checkbox and grid fields (Ingo Schommer)
* 2012-11-30 [2614171](https://github.com/silverstripe/sapphire/commit/2614171) Deep cloning for DateTimeField (Ingo Schommer)
* 2012-11-30 [20a5bc1](https://github.com/silverstripe/sapphire/commit/20a5bc1) iOS safari navigation bug (fixes #8039) (Kirk Mayo)
* 2012-11-29 [7d0e10f](https://github.com/silverstripe/sapphire/commit/7d0e10f) Extends too generic (Naomi Guyer)
* 2012-11-27 [414c006](https://github.com/silverstripe/sapphire/commit/414c006) Restore GD class to avoid breaking GD::set_default_quality() calls (Ingo Schommer)
* 2012-11-26 [670c579](https://github.com/silverstripe/silverstripe-cms/commit/670c579) Namespaces for CmsFormsContext and CmsUiContext are wrong (Kirk Mayo)
* 2012-11-23 [76b99e4](https://github.com/silverstripe/silverstripe-cms/commit/76b99e4) ed tests without assertions (Ingo Schommer)
* 2012-11-16 [4651e9b](https://github.com/silverstripe/sapphire/commit/4651e9b) Fixing ToggleField to work correctly with jQuery (Sean Harvey)
* 2012-11-15 [94b37db](https://github.com/silverstripe/silverstripe-cms/commit/94b37db) ing AssetAdmin to use static FormField::create_tag() (Sean Harvey)
* 2012-11-15 [1edfeef](https://github.com/silverstripe/sapphire/commit/1edfeef) Remove extraneous layout calls. (Mateusz Uzdowski)
* 2012-11-12 [4fab9b8](https://github.com/silverstripe/silverstripe-cms/commit/4fab9b8) Incorrect html nesting of breadcrumbs (Naomi Guyer)
* 2012-11-12 [a933847](https://github.com/silverstripe/sapphire/commit/a933847) Incorrect html nesting of breadcrumbs (Naomi Guyer)
* 2012-11-12 [9d74c99](https://github.com/silverstripe/sapphire/commit/9d74c99) ArrayList now discards keys of the array passed in and keeps the numerically indexed array sequential. This fixes FirstLast and EvenOdd in templates, and makes ArrayList more consistent, as several methods already discarded the keys. (Andrew O'Neil)
* 2012-11-11 [af2ac1d](https://github.com/silverstripe/sapphire/commit/af2ac1d) include ImagickBackend only when Imagick installed (Will Rossiter)
* 2012-11-07 [91b69bf](https://github.com/silverstripe/sapphire/commit/91b69bf) ed tab alignment and padding (Ingo Schommer)
* 2012-11-05 [7ae73ea](https://github.com/silverstripe/sapphire/commit/7ae73ea) Border at top of tabs when no subtabs (Naomi Guyer)
* 2012-11-02 [f2a709d](https://github.com/silverstripe/sapphire/commit/f2a709d) DataObject::write overwrites Created on first write (Justin Martin)
* 2012-11-02 [95b5f65](https://github.com/silverstripe/sapphire/commit/95b5f65) GridField add existing auto complete has no max height (fixes #7965) (Naomi Guyer)
* 2012-11-01 [a651d73](https://github.com/silverstripe/sapphire/commit/a651d73) DataObject::__construct() now accepts stdClass for $record (Justin Martin)
* 2012-11-02 [2dabaeb](https://github.com/silverstripe/sapphire/commit/2dabaeb) File Uploading Notifications (fixes #7883) (Naomi Guyer)
* 2012-11-01 [eb23f50](https://github.com/silverstripe/sapphire/commit/eb23f50) Site Tree checkboxes and refactoring (Naomi Guyer)
* 2012-11-01 [2a67715](https://github.com/silverstripe/sapphire/commit/2a67715) One too many brackets in _style.scss (Naomi Guyer)
* 2012-10-30 [0883226](https://github.com/silverstripe/sapphire/commit/0883226) ed merge errors in CMSProfileController (Ingo Schommer)
* 2012-10-23 [0d642af](https://github.com/silverstripe/silverstripe-cms/commit/0d642af) Filter in asset grid appears in incorrect place (Naomi Guyer)
* 2012-10-17 [8a7f9ed](https://github.com/silverstripe/sapphire/commit/8a7f9ed) ed empty string always on scaffolded enum fields (icecaster)
* 2012-10-16 [d61f16d](https://github.com/silverstripe/silverstripe-cms/commit/d61f16d) File Uploading Notifications (fixes #7883) (Naomi Guyer)
* 2012-10-10 [fbfff8d](https://github.com/silverstripe/sapphire/commit/fbfff8d) 7934 When lazy loading fields respect version of the record (jean)
* 2012-09-27 [39792de](https://github.com/silverstripe/silverstripe-cms/commit/39792de) Use RequestHandler::httpError() for all HTTP errors. (Sam Minnee)
* 2012-09-27 [b92f759](https://github.com/silverstripe/silverstripe-cms/commit/b92f759) ing test to be less fragile (selects the input ID directly instead of holder) (Sean Harvey)
* 2012-09-27 [e9ce89e](https://github.com/silverstripe/sapphire/commit/e9ce89e) ing broken FulltextSearchableTest (Sean Harvey)
* 2012-09-24 [0470219](https://github.com/silverstripe/sapphire/commit/0470219) Output the title of the task instead of Array when listing in the CLI (Simon Welsh)
* 2012-09-17 [b6c1a64](https://github.com/silverstripe/sapphire/commit/b6c1a64) ed link to RC3 changelog (Sean Harvey)
* 2012-09-04 [6b6571c](https://github.com/silverstripe/silverstripe-cms/commit/6b6571c) Only rely on request var ParentID, instead of using both $this-&gt;currentPage() and the request var. This will hopefully fix issues around the parent ID getting lost. (Andrew O'Neil)
* 2012-08-23 [cd61b61](https://github.com/silverstripe/sapphire/commit/cd61b61) Use array_intersect() with expected values so that the order matches. (Simon Welsh)
* 2012-08-21 [cbdc3bf](https://github.com/silverstripe/sapphire/commit/cbdc3bf) ed bug in Travis matrix definition (Sam Minnee)
* 2012-08-21 [597bc08](https://github.com/silverstripe/sapphire/commit/597bc08) ed bug in Travis exclusion of 5.4/SQlite and 5.4/PostgreSQL (Sam Minnee)
* 2012-08-15 [c621a6d](https://github.com/silverstripe/sapphire/commit/c621a6d) fixed trac 7665 - CMS Menu header now changes height depending on the name of the admin and greeting message. position and height were being set inline so added !important to override this. (Jeremy Bridson)
* 2012-08-14 [0e08840](https://github.com/silverstripe/sapphire/commit/0e08840) ed Travis CI and make it use SQLite (Sam Minnee)
* 2012-08-14 [50b4d80](https://github.com/silverstripe/sapphire/commit/50b4d80) ed bugs in Travis CI set-up (Sam Minnee)
* 2012-07-23 [c058f97](https://github.com/silverstripe/sapphire/commit/c058f97) Allow using instances for search filters. (Andrew Short)
* 2012-07-17 [dbc862e](https://github.com/silverstripe/sapphire/commit/dbc862e) Attempt to create log path before writing file (Simon Elvery)
* 2012-07-10 [c91e855](https://github.com/silverstripe/sapphire/commit/c91e855) resolve errors with commits from (#572) (Will Rossiter)
* 2012-07-09 [0ef0c9c](https://github.com/silverstripe/sapphire/commit/0ef0c9c) removed text shadow off confirmation message links trac 7637 (Jeremy Bridson)
* 2012-07-06 [2a9a4be](https://github.com/silverstripe/sapphire/commit/2a9a4be) ed nested tab styling in other CMS interfaces. (Andrew Short)
* 2012-07-06 [7ff2a79](https://github.com/silverstripe/sapphire/commit/7ff2a79) links in profiling documentation. (Will Rossiter)
* 2012-07-01 [a67b964](https://github.com/silverstripe/sapphire/commit/a67b964) improve Director::makeRelative() to ignore SSL changes. (Tim Klein)
* 2012-07-01 [9f6eeb4](https://github.com/silverstripe/sapphire/commit/9f6eeb4) insert javascript requirements before the first inline script. (Simon Welsh)
* 2012-07-01 [9babb01](https://github.com/silverstripe/sapphire/commit/9babb01) ensure that permissions_for_member() accounts for denied permissions. (Will Rossiter)
* 2012-06-29 [e050540](https://github.com/silverstripe/sapphire/commit/e050540) Director::is_absolute_url() now ignores query and fragment strings (Simon Welsh)
* 2012-06-25 [606d86a](https://github.com/silverstripe/sapphire/commit/606d86a) DateField javascript fails when it is included in a GroupField (Jeremy Shipman)
### Other
* 2012-12-17 [7950584](https://github.com/silverstripe/sapphire/commit/7950584) SimpleXML string casting in tests for older PHPUnit (Ingo Schommer)
* 2012-12-17 [546d202](https://github.com/silverstripe/sapphire/commit/546d202) Don't complain about pre-replaced YAML fixture relations (Ingo Schommer)
* 2012-12-17 [407a19c](https://github.com/silverstripe/sapphire/commit/407a19c) Beta changelog links (Ingo Schommer)
* 2012-12-17 [c1bd143](https://github.com/silverstripe/sapphire/commit/c1bd143) Tab spacing (regression from 2d075671) (Ingo Schommer)
* 2012-12-17 [375c33e](https://github.com/silverstripe/sapphire/commit/375c33e) Wider sidebar to accommodate "add" and "edit" buttons (Ingo Schommer)
* 2012-12-17 [58d316e](https://github.com/silverstripe/silverstripe-cms/commit/58d316e) Moving "edit tree" button next to "add new" (fixes #8119) (Ingo Schommer)
* 2012-12-17 [9cfa7b7](https://github.com/silverstripe/sapphire/commit/9cfa7b7) Wider side panel to fit "add" and "edit" button (Ingo Schommer)
* 2012-12-17 [17908b6](https://github.com/silverstripe/sapphire/commit/17908b6) Revert CMS button style (regression from fe08236) (Ingo Schommer)
* 2012-12-17 [3da41ef](https://github.com/silverstripe/sapphire/commit/3da41ef) Permission docs (Ingo Schommer)
* 2012-12-15 [5b2cc19](https://github.com/silverstripe/silverstripe-cms/commit/5b2cc19) Added placeholder text to group listboxes (Ingo Schommer)
* 2012-12-14 [e6bf199](https://github.com/silverstripe/sapphire/commit/e6bf199) Less far-future date assertions, seems to throw off some PHP installs (Ingo Schommer)
* 2012-12-14 [4f5b3fa](https://github.com/silverstripe/sapphire/commit/4f5b3fa) Readd SQlite to travis builds, having it fail harms TDD (Ingo Schommer)
* 2012-12-14 [90084bb](https://github.com/silverstripe/sapphire/commit/90084bb) Separate PHPCS run on travis, don't fail whole build for it (Ingo Schommer)
* 2012-12-14 [681a024](https://github.com/silverstripe/sapphire/commit/681a024) DOC Removed link to missing 'extending-the-cms.md' (Stig Lindqvist)
* 2012-12-14 [244bc97](https://github.com/silverstripe/sapphire/commit/244bc97) Don't register a PGSQL failure as a Travis build failure. (Sam Minnee)
* 2012-12-14 [b65180a](https://github.com/silverstripe/sapphire/commit/b65180a) Changelog update for grouped CMS buttons (Ingo Schommer)
* 2012-12-13 [1d470fe](https://github.com/silverstripe/silverstripe-cms/commit/1d470fe) Removed duplciate success status feedback on CMS save/publish (Ingo Schommer)
* 2012-12-13 [aed58a5](https://github.com/silverstripe/sapphire/commit/aed58a5) Loading indicator for "more options" buttons (Ingo Schommer)
* 2012-12-13 [7dd224d](https://github.com/silverstripe/sapphire/commit/7dd224d) Made GridField font size settings less cryptic (Ingo Schommer)
* 2012-12-13 [abf1ee9](https://github.com/silverstripe/sapphire/commit/abf1ee9) Suppress jQuery UI's borders around tabs in the CMS (Ingo Schommer)
* 2012-12-13 [2369cc4](https://github.com/silverstripe/sapphire/commit/2369cc4) Moved group member listing utility buttons after field (Ingo Schommer)
* 2012-12-13 [236e335](https://github.com/silverstripe/sapphire/commit/236e335) Permission list styling improvements (#8100) (Joel Edwards)
* 2012-12-13 [f4128a0](https://github.com/silverstripe/silverstripe-cms/commit/f4128a0) Revert "BUG removed class cms-panel-link as it was calling loadPanel to be called twice trac 8041" (Ingo Schommer)
* 2012-12-12 [611c3f1](https://github.com/silverstripe/silverstripe-cms/commit/611c3f1) Added travis environment info output (Ingo Schommer)
* 2012-12-12 [441bb5f](https://github.com/silverstripe/sapphire/commit/441bb5f) Added travis environment info output (Ingo Schommer)
* 2012-12-12 [6100eb9](https://github.com/silverstripe/sapphire/commit/6100eb9) Remove or comment magic numbers, whitespace (Naomi Guyer)
* 2012-12-11 [40b5366](https://github.com/silverstripe/silverstripe-installer/commit/40b5366) Updated composer.json (Ingo Schommer)
* 2012-12-11 [ed11970](https://github.com/silverstripe/sapphire/commit/ed11970) Updated composer.json (Ingo Schommer)
* 2012-12-11 [4cd166a](https://github.com/silverstripe/silverstripe-cms/commit/4cd166a) Updated composer.json (Ingo Schommer)
* 2012-12-11 [df41fcd](https://github.com/silverstripe/silverstripe-cms/commit/df41fcd) Skip SearchFormTest if DB driver doesn't support fulltext (Ingo Schommer)
* 2012-12-11 [d92258d](https://github.com/silverstripe/sapphire/commit/d92258d) Allow calling SSViewer_Scope on empty sets (Ingo Schommer)
* 2012-12-10 [d5dcecf](https://github.com/silverstripe/sapphire/commit/d5dcecf) Disable change tracking for preview state switch (Ingo Schommer)
* 2012-12-09 [fc5dd29](https://github.com/silverstripe/sapphire/commit/fc5dd29) Add codesniffer that ensures indentation is with tabs. (Simon Welsh)
* 2012-12-09 [b0121b5](https://github.com/silverstripe/sapphire/commit/b0121b5) Add codesniffer that ensures indentation is with tabs. (Simon Welsh)
* 2012-12-06 [a9004b9](https://github.com/silverstripe/silverstripe-cms/commit/a9004b9) Restore numbering to navigator items so we can use iterator. (Mateusz Uzdowski)
* 2012-12-06 [dbee4a1](https://github.com/silverstripe/sapphire/commit/dbee4a1) Clean up the side-by-side code. (Naomi Guyer)
* 2012-12-06 [747346b](https://github.com/silverstripe/sapphire/commit/747346b) Ability to rotate the mobile preview in side-by-side preview. (Paul Clarke)
* 2012-12-05 [5cef05e](https://github.com/silverstripe/sapphire/commit/5cef05e) Separate out ActionTabSet functionality into a new file & clean up. (Naomi Guyer)
* 2012-12-05 [35cbe28](https://github.com/silverstripe/silverstripe-cms/commit/35cbe28) Re-add preview button for IE&lt;=7. Side-by-side disabled for these. (Naomi Guyer)
* 2012-12-04 [98e824b](https://github.com/silverstripe/silverstripe-cms/commit/98e824b) Avoid duplicating ReportAdmin search params (fixes #8046) (Ingo Schommer)
* 2012-12-04 [00f1ba4](https://github.com/silverstripe/sapphire/commit/00f1ba4) Side-by-side preview browser compatibility fixes. (Naomi Guyer)
* 2012-12-04 [230182f](https://github.com/silverstripe/silverstripe-cms/commit/230182f) Remove preview button from history section. (Mateusz Uzdowski)
* 2012-12-04 [fa3ef8c](https://github.com/silverstripe/sapphire/commit/fa3ef8c) Side-by-side preview initialisation and navigation fixes. (Mateusz Uzdowski)
* 2012-12-04 [aaae8c9](https://github.com/silverstripe/silverstripe-cms/commit/aaae8c9) Explicitly mark the section as previewable. (Mateusz Uzdowski)
* 2012-12-03 [8ce2728](https://github.com/silverstripe/sapphire/commit/8ce2728) Replace the state selector switch to support more than 2 states. (Naomi Guyer)
* 2012-11-30 [2cd46ff](https://github.com/silverstripe/silverstripe-cms/commit/2cd46ff) Use new SelectionGroup_Item API in "add page" UI (Ingo Schommer)
* 2012-11-30 [963f02e](https://github.com/silverstripe/sapphire/commit/963f02e) Using new description style in MemberDateTimeOptionSetField (Ingo Schommer)
* 2012-11-30 [255b4c4](https://github.com/silverstripe/sapphire/commit/255b4c4) UploadField-&gt;setDescription() support, removed extraneous "title" attrs (Ingo Schommer)
* 2012-11-30 [212c427](https://github.com/silverstripe/sapphire/commit/212c427) Pass setDescription() through to sub-fields in DatetimeField and PhoneNumberField (Ingo Schommer)
* 2012-11-30 [ee797e4](https://github.com/silverstripe/sapphire/commit/ee797e4) More CSS fixes for the ActionTabSets. (Naomi Guyer)
* 2012-11-30 [618e639](https://github.com/silverstripe/sapphire/commit/618e639) Refactor and comment TabSet.js (Naomi Guyer)
* 2012-11-29 [027a41a](https://github.com/silverstripe/silverstripe-cms/commit/027a41a) Consistent naming for root breadcrumb on page controllers (fixes #8057) (Ingo Schommer)
* 2012-11-29 [235e8c8](https://github.com/silverstripe/sapphire/commit/235e8c8) CSS fixes for the ActionTabSet. (Naomi Guyer)
* 2012-11-29 [a80aa3c](https://github.com/silverstripe/sapphire/commit/a80aa3c) Provide new save icon for the use in the framework. (Naomi Guyer)
* 2012-11-29 [772961c](https://github.com/silverstripe/silverstripe-cms/commit/772961c) Add a secondary side-by-side state selector to the edit form (Mateusz Uzdowski)
* 2012-11-28 [7bd200f](https://github.com/silverstripe/silverstripe-cms/commit/7bd200f) Re-adding usage of $TRAVIS_BRANCH, fixing wrong 3.0 dependency (Ingo Schommer)
* 2012-11-27 [09f382d](https://github.com/silverstripe/silverstripe-cms/commit/09f382d) Composer require framework at 'dev-master' (Ingo Schommer)
* 2012-11-27 [9312c70](https://github.com/silverstripe/sapphire/commit/9312c70) Side-by-side preview options fixes. (Naomi Guyer)
* 2012-11-27 [715b62f](https://github.com/silverstripe/sapphire/commit/715b62f) Updating chosen dependency (Naomi Guyer)
* 2012-11-27 [0711c32](https://github.com/silverstripe/silverstripe-cms/commit/0711c32) Add side-by-side translation context. (Naomi Guyer)
* 2012-11-22 [477cf6b](https://github.com/silverstripe/sapphire/commit/477cf6b) Removing redundant DatabaseAdmin::testinstall() (Sean Harvey)
* 2012-11-22 [544d2eb](https://github.com/silverstripe/sapphire/commit/544d2eb) Side-by-side preview options styling. (Paul Clarke)
* 2012-11-19 [3f2ddbb](https://github.com/silverstripe/sapphire/commit/3f2ddbb) Future-proof the submitForm for use with forms without validation. (Mateusz Uzdowski)
* 2012-11-16 [8168a7d](https://github.com/silverstripe/sapphire/commit/8168a7d) Remove deprecated DBField::create(), use create_field() instead (Sean Harvey)
* 2012-11-16 [b3d5b68](https://github.com/silverstripe/sapphire/commit/b3d5b68) Remove deprecated SQLQuery::setWhere() multiple arguments (Sean Harvey)
* 2012-11-16 [a2bd378](https://github.com/silverstripe/sapphire/commit/a2bd378) Remove deprecated ArrayList::getRange(), use limit() instead (Sean Harvey)
* 2012-11-16 [7042d87](https://github.com/silverstripe/sapphire/commit/7042d87) Remove deprecated Object::set_uninherited() (Sean Harvey)
* 2012-11-16 [d13b067](https://github.com/silverstripe/sapphire/commit/d13b067) Remove deprecated HTTP::getMimeType() use get_mime_type() instead (Sean Harvey)
* 2012-11-16 [a46838c](https://github.com/silverstripe/sapphire/commit/a46838c) Removed deprecated Folder::findOrMake(), use find_or_make() instead (Sean Harvey)
* 2012-11-16 [d1c5cd1](https://github.com/silverstripe/sapphire/commit/d1c5cd1) Removing unused entities from en.yml (Sean Harvey)
* 2012-11-16 [4c73f23](https://github.com/silverstripe/silverstripe-cms/commit/4c73f23) Removing deprecated FileList as it relies on TableListField (Sean Harvey)
* 2012-11-16 [4ea5bc5](https://github.com/silverstripe/sapphire/commit/4ea5bc5) adding notes about deprecated things in the core (Sean Harvey)
* 2012-11-16 [4330ecf](https://github.com/silverstripe/sapphire/commit/4330ecf) Removing redundant templates (moved to legacytablefields module) (Sean Harvey)
* 2012-11-16 [6a868e7](https://github.com/silverstripe/sapphire/commit/6a868e7) Removing deprecated prototype/behaviour libraries (Sean Harvey)
* 2012-11-16 [77337ae](https://github.com/silverstripe/sapphire/commit/77337ae) Removing deprecated TableListField and subclasses (Sean Harvey)
* 2012-11-16 [aeef4d6](https://github.com/silverstripe/sapphire/commit/aeef4d6) Removing deprecated JS from AjaxUniqueTextField (Sean Harvey)
* 2012-11-15 [26a3c1c](https://github.com/silverstripe/sapphire/commit/26a3c1c) Re-adding Debug::caller() which was inadvertently removed in 9eca2d6 (Sean Harvey)
* 2012-11-15 [cef087f](https://github.com/silverstripe/silverstripe-cms/commit/cef087f) Removed deprecated SiteTree::TreeTitle(), use getTreeTitle() instead (Sean Harvey)
* 2012-11-15 [d236bb5](https://github.com/silverstripe/silverstripe-cms/commit/d236bb5) Removed deprecated SiteTree::prepopuplate_permission_cache() (Sean Harvey)
* 2012-11-15 [33884ac](https://github.com/silverstripe/silverstripe-cms/commit/33884ac) Removed deprecated ContentController::LangAttributes() (Sean Harvey)
* 2012-11-15 [555ecd7](https://github.com/silverstripe/silverstripe-cms/commit/555ecd7) Removed deprecated SiteTreeDecorator, use SiteTreeExtension instead (Sean Harvey)
* 2012-11-15 [41efeed](https://github.com/silverstripe/sapphire/commit/41efeed) Update javascript/GridField.js (Vitaliy)
* 2012-11-15 [35bcf69](https://github.com/silverstripe/silverstripe-cms/commit/35bcf69) Removed deprecated Register::register() and unregister() (Sean Harvey)
* 2012-11-15 [8c3ecab](https://github.com/silverstripe/sapphire/commit/8c3ecab) Removed deprecated ToggleCompositeField::startClosed() (Sean Harvey)
* 2012-11-15 [4c803a2](https://github.com/silverstripe/sapphire/commit/4c803a2) Removed deprecated arguments of row, cols to TextareaField (Sean Harvey)
* 2012-11-15 [4d11080](https://github.com/silverstripe/sapphire/commit/4d11080) Remove deprecated rows and columns argument support from HtmlEditorField (Sean Harvey)
* 2012-11-15 [b3b071a](https://github.com/silverstripe/sapphire/commit/b3b071a) Removing deprecated FormField functions (Sean Harvey)
* 2012-11-15 [0d659a5](https://github.com/silverstripe/sapphire/commit/0d659a5) Removing deprecated FormField::Name(), use getName() instead (Sean Harvey)
* 2012-11-15 [c99ed7d](https://github.com/silverstripe/sapphire/commit/c99ed7d) Extending deprecation of legacy table fields to 3.1 (Sean Harvey)
* 2012-11-15 [e1c5f08](https://github.com/silverstripe/sapphire/commit/e1c5f08) Removing deprecated container class argument to DataObject::get() (Sean Harvey)
* 2012-11-15 [3a198c3](https://github.com/silverstripe/sapphire/commit/3a198c3) Removing deprecated DataObject::databaseFields() and customDatabaseFields() (Sean Harvey)
* 2012-11-15 [e4088fe](https://github.com/silverstripe/sapphire/commit/e4088fe) Removing deprecated instance_get_one() and instance_get() (Sean Harvey)
* 2012-11-15 [a8d779b](https://github.com/silverstripe/sapphire/commit/a8d779b) Removing deprecated DataObject::buildDataObjectSet() (Sean Harvey)
* 2012-11-15 [dde820d](https://github.com/silverstripe/sapphire/commit/dde820d) Extend deprecation of DataObject::Aggregate() and RelationshipAggregate() (Sean Harvey)
* 2012-11-15 [0db33f7](https://github.com/silverstripe/sapphire/commit/0db33f7) Removing DataObject::buildSQL() and extendedSQL(), use DataList instead (Sean Harvey)
* 2012-11-15 [651d4b3](https://github.com/silverstripe/sapphire/commit/651d4b3) Removing DataObject::getAllFields(), use toMap() instead (Sean Harvey)
* 2012-11-15 [5f852ae](https://github.com/silverstripe/sapphire/commit/5f852ae) Removing deprecated DataObject::setComponent() (Sean Harvey)
* 2012-11-15 [3108dea](https://github.com/silverstripe/sapphire/commit/3108dea) Removing deprecated DataList::limit() arguments (Sean Harvey)
* 2012-11-15 [68bb748](https://github.com/silverstripe/sapphire/commit/68bb748) Removing join() on DataList/DataQuery (Sean Harvey)
* 2012-11-15 [b43b023](https://github.com/silverstripe/sapphire/commit/b43b023) Remove deprecated security token methods on Form (Sean Harvey)
* 2012-11-15 [6382013](https://github.com/silverstripe/sapphire/commit/6382013) Remove deprecated Form::FormEncType(), use getEncType() instead (Sean Harvey)
* 2012-11-15 [4e355bd](https://github.com/silverstripe/sapphire/commit/4e355bd) Removing deprecated methods on Form (Sean Harvey)
* 2012-11-15 [2080867](https://github.com/silverstripe/sapphire/commit/2080867) Removing deprecated arguments to FileField for setting folder name (Sean Harvey)
* 2012-11-15 [a9d7c9e](https://github.com/silverstripe/sapphire/commit/a9d7c9e) Removing deprecated variables from FileField (Sean Harvey)
* 2012-11-15 [0a5d43f](https://github.com/silverstripe/sapphire/commit/0a5d43f) Removing deprecated CompositeField::FieldSet(), use FieldList() instead (Sean Harvey)
* 2012-11-15 [6448cd7](https://github.com/silverstripe/sapphire/commit/6448cd7) Removing deprecated Validator javascript methods (Sean Harvey)
* 2012-11-15 [5c983a2](https://github.com/silverstripe/sapphire/commit/5c983a2) emove deprecated StringField::Lower() and Upper() methods (Sean Harvey)
* 2012-11-15 [9e7bdb3](https://github.com/silverstripe/sapphire/commit/9e7bdb3) Removing deprecated Text::EscapeXML(), use DBField-&gt;XML() instead (Sean Harvey)
* 2012-11-15 [f41650c](https://github.com/silverstripe/sapphire/commit/f41650c) Remove deprecated i18n::include_locale_file() (Sean Harvey)
* 2012-11-15 [587d669](https://github.com/silverstripe/sapphire/commit/587d669) Removing deprecated PasswordEncryptor::compare() method (Sean Harvey)
* 2012-11-15 [f122b10](https://github.com/silverstripe/sapphire/commit/f122b10) Remove deprecated Group::addToGroupByName() (Sean Harvey)
* 2012-11-15 [d038cd7](https://github.com/silverstripe/sapphire/commit/d038cd7) Removing deprecated tests (Sean Harvey)
* 2012-11-15 [0d79897](https://github.com/silverstripe/sapphire/commit/0d79897) Removing deprecated ArrayData::getArray() (Sean Harvey)
* 2012-11-15 [de2509c](https://github.com/silverstripe/sapphire/commit/de2509c) Removing deprecated SimpleImageField (Sean Harvey)
* 2012-11-15 [6ce33d5](https://github.com/silverstripe/sapphire/commit/6ce33d5) Removing deprecated ImageFormAction (Sean Harvey)
* 2012-11-15 [8156f0f](https://github.com/silverstripe/sapphire/commit/8156f0f) Removing deprecated ImageField, use UploadField instead (Sean Harvey)
* 2012-11-15 [594faf7](https://github.com/silverstripe/sapphire/commit/594faf7) Removing deprecated FileIFrameField, use UploadField instead (Sean Harvey)
* 2012-11-15 [5a98cdd](https://github.com/silverstripe/sapphire/commit/5a98cdd) Removing deprecated File::TreeTitle(), use File::getTreeTitle() instead (Sean Harvey)
* 2012-11-15 [e52db56](https://github.com/silverstripe/sapphire/commit/e52db56) Removing deprecated validator functions on Upload (Sean Harvey)
* 2012-11-15 [9eca2d6](https://github.com/silverstripe/sapphire/commit/9eca2d6) Removing deprecated Debug functions, use SS_Log and SS_Backtrace instead (Sean Harvey)
* 2012-11-15 [b6870ad](https://github.com/silverstripe/sapphire/commit/b6870ad) Removing deprecated Core.php functions (Sean Harvey)
* 2012-11-15 [b5ee9f9](https://github.com/silverstripe/sapphire/commit/b5ee9f9) Removing ClassInfo::is_subclass_of(), use is_subclass_of() instead (Sean Harvey)
* 2012-11-15 [78311c9](https://github.com/silverstripe/sapphire/commit/78311c9) Remove deprecated PaginatedList::getPageLimits() and setPageLimits() (Sean Harvey)
* 2012-11-15 [63983ad](https://github.com/silverstripe/sapphire/commit/63983ad) Remove deprecated RequestHandler::isAjax(), use SS_HTTPRequest-&gt;isAjax() instead (Sean Harvey)
* 2012-11-15 [491057f](https://github.com/silverstripe/sapphire/commit/491057f) Remove deprecated Director dev/test server functions (Sean Harvey)
* 2012-11-15 [66d8ff9](https://github.com/silverstripe/sapphire/commit/66d8ff9) Remove deprecated Director static functions (Sean Harvey)
* 2012-11-15 [de0ade9](https://github.com/silverstripe/sapphire/commit/de0ade9) Remove deprecated Director::urlParam() and Director::urlParams() (Sean Harvey)
* 2012-11-15 [6718814](https://github.com/silverstripe/sapphire/commit/6718814) Remove deprecated PartialMatchFilter, use PartialMatchFilter instead (Sean Harvey)
* 2012-11-15 [4c3b804](https://github.com/silverstripe/sapphire/commit/4c3b804) Remove deprecated ComponentSet, use ManyManyList or HasManyList instead (Sean Harvey)
* 2012-11-15 [0a046af](https://github.com/silverstripe/sapphire/commit/0a046af) Remove deprecated DataObjectDecorator, use DataExtension instead (Sean Harvey)
* 2012-11-15 [a371db4](https://github.com/silverstripe/sapphire/commit/a371db4) Remove deprecated DataObjectSet, use DataList or ArrayList instead (Sean Harvey)
* 2012-11-15 [0673f27](https://github.com/silverstripe/sapphire/commit/0673f27) Remove deprecated FieldSet, use FieldList instead (Sean Harvey)
* 2012-11-15 [6a9617b](https://github.com/silverstripe/sapphire/commit/6a9617b) Remove deprecated LeftAndMainDecorator, use LeftAndMainExtension instead (Sean Harvey)
* 2012-11-10 [d006c08](https://github.com/silverstripe/silverstripe-cms/commit/d006c08) Reverts test code committed in a52514a3 (Simon Welsh)
* 2012-11-08 [0fe9379](https://github.com/silverstripe/silverstripe-cms/commit/0fe9379) Composer branch alias from dev-master to 3.1 (Ingo Schommer)
* 2012-11-08 [bde6344](https://github.com/silverstripe/sapphire/commit/bde6344) Composer branch alias from dev-master to 3.1 (Ingo Schommer)
* 2012-11-06 [ada4210](https://github.com/silverstripe/sapphire/commit/ada4210) Tabs docs for CMS (Ingo Schommer)
* 2012-11-07 [078a8e9](https://github.com/silverstripe/sapphire/commit/078a8e9) Adding note about Object::add_extension() and has_extension() changes (Sean Harvey)
* 2012-11-06 [f988ae3](https://github.com/silverstripe/sapphire/commit/f988ae3) ENHANCEMENT Message colours updated (Paul Clarke)
* 2012-11-05 [f30759e](https://github.com/silverstripe/silverstripe-installer/commit/f30759e) Updating favicon (Sean Harvey)
* 2012-11-01 [f9e32f5](https://github.com/silverstripe/silverstripe-cms/commit/f9e32f5) Added composer.json (Ingo Schommer)
* 2012-11-01 [43cd54b](https://github.com/silverstripe/sapphire/commit/43cd54b) Added composer.json (Ingo Schommer)
* 2012-10-23 [84851c9](https://github.com/silverstripe/sapphire/commit/84851c9) Remove sub navigation for "Files" (fixes 7956) (Naomi Guyer)
* 2012-10-23 [92e4b4f](https://github.com/silverstripe/sapphire/commit/92e4b4f) Remove sub navigation for "Files" (fixes 7956) (Naomi Guyer)
* 2012-10-16 [f365134](https://github.com/silverstripe/sapphire/commit/f365134) Added 2.4.8-rc1 changelog (Ingo Schommer)
* 2012-10-08 [e3a27ea](https://github.com/silverstripe/sapphire/commit/e3a27ea) CMS member profile now is no longer in a popup (#7880) (Saophalkun Ponlu)
* 2012-10-03 [fb5e488](https://github.com/silverstripe/sapphire/commit/fb5e488) Line length fixes (Ingo Schommer)
* 2012-09-21 [7f1b6cf](https://github.com/silverstripe/sapphire/commit/7f1b6cf) HTTPRequest and HTTPResponse now return $this on all setters MINOR: also added some docs (Zauberfisch)
* 2012-09-21 [5df519c](https://github.com/silverstripe/sapphire/commit/5df519c) Removed SiteTree.MetaTitle and MetaKeywords usage (Ingo Schommer)
* 2012-09-21 [4506e92](https://github.com/silverstripe/silverstripe-cms/commit/4506e92) Adds Travis testing to the CMS (Simon Welsh)
* 2012-08-21 [9f4fb13](https://github.com/silverstripe/sapphire/commit/9f4fb13) Added PHP 5.4 + MySQL to build grid (Sam Minnee)
* 2012-08-21 [866d9a9](https://github.com/silverstripe/sapphire/commit/866d9a9) Updated Travis-CI configuration to have a 3 database build grid. (Sam Minnee)
* 2012-08-14 [2dadc77](https://github.com/silverstripe/sapphire/commit/2dadc77) Revert "Make PHPUnit bootstrap add flush=1" (Sam Minnee)
* 2012-08-14 [4d1c2ed](https://github.com/silverstripe/sapphire/commit/4d1c2ed) Make PHPUnit bootstrap add flush=1 (Sam Minnee)
* 2012-08-14 [8d92046](https://github.com/silverstripe/sapphire/commit/8d92046) Added support for Travis CI (Sam Minnee)
* 2012-08-01 [f5b25d2](https://github.com/silverstripe/sapphire/commit/f5b25d2) Make the list used for autocomplete search results settable. (Andrew Short)
* 2012-07-17 [9a5baaf](https://github.com/silverstripe/sapphire/commit/9a5baaf) Don't capture form submits to new windows. (Andrew Short)

View File

@ -9,6 +9,8 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins
## Stable Releases
* [3.1.0](3.1.0) - Unreleased
* [3.0.2](3.0.2) - 17 September 2012
* [3.0.1](3.0.1) - 31 July 2012
* [3.0.0](3.0.0) - 28 June 2012
@ -65,6 +67,8 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins
## Alpha/beta/release candidate ##
* [3.1.0-beta1](beta/3.1.0-beta1) - 17 December 2012
* [3.0.3-rc1](rc/3.0.3-rc1) - 6 November 2012
* [3.0.2-rc2](rc/3.0.2-rc2) - 12 September 2012
* [3.0.2-rc1](rc/3.0.2-rc1) - 5 September 2012

View File

@ -72,7 +72,6 @@ Now that we have a contact form, we need some way of collecting the data submitt
$messageBody = "
<p><strong>Name:</strong> {$data['Name']}</p>
<p><strong>Website:</strong> {$data['Website']}</p>
<p><strong>Message:</strong> {$data['Message']}</p>
";
$email->setBody($messageBody);

View File

@ -44,18 +44,20 @@ Composer can create a new site for you, using the installer as a template. To d
composer create-project silverstripe/installer ./my/website/folder
`./my/website/folder` should be the root directory where your site will live. For example, on OS X, you might use a subdirectory of `~/Sites`.
`./my/website/folder` should be the root directory where your site will live.
For example, on OS X, you might use a subdirectory of `~/Sites`.
As long as your web server is up and running, this will get all the code that you need.
Now visit the site in your web browser, and the installation process will be completed.
#### Selecting a version
By default composer will download the latest stable version. You can also specify
a version to download that version explicitly, i.e. this will download 3.0.3:
composer create-project silverstripe/installer ./my/website/folder 3.0.3
When `create-project` is used with a release version like above,
it will try to get the code from archives instead of creating
git repositories. If you're planning to contribute to SilverStripe,
see [Using development versions](#using-development-versions).
## Adding modules to your project
@ -94,14 +96,35 @@ The `composer.lock` file helps with this. It references the specific commits th
So, your deployment process, as it relates to Composer, should be as follows:
* Run `composer update` on your development version before you start whatever testing you have planned. Perform all the necessary testing.
* Check `composer.lock` into your repository.
* Deploy your project code base, using the deployment tool of your choice.
* Run `composer install` on your production version.
* Run the following command on your production version.
# Setting up an environment for contributing to SilverStripe {#contributing}
composer install
So you want to contribute to SilverStripe? Fantastic! You can do this with composer too.
You have to tell composer three things in order to be able to do this:
- Keep the full git repository information
- Include dependancies marked as "developer" requirements
- Use the development version, not the latest stable version
The first two steps are done as part of the initial create project using additional arguments. For instance:
composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.0.x-dev
The process will take a bit longer, since all modules are checked out as full git repositories which you can work on.
The `--keep-vcs` flag will make sure you have access to the git history of the installer and the requirements
The `--dev` flag will add a couple modules which are useful for SilverStripe development:
* The `docsviewer` module will let you preview changes to the project documentation
* The `buildtools` module which adds [phing](http://phing.info) tasks for creating SilverStripe releases
Note that you can also include those into an existing project by running `composer update --dev`.
Please read the ["Contributing Code"](/misc/contributing/code) documentation to find out how to
create forks and send pull requests.
# Advanced usage
@ -212,27 +235,3 @@ Both the version and the alias are specified as Composer versions, not branch na
This is not the only way to set things up in Composer. For more information on this topic, read the ["Aliases" chapter of the Composer documentation](http://getcomposer.org/doc/articles/aliases.md).
## Setting up an environment for contributing to SilverStripe
So you want to contribute to SilverStripe? Fantastic! You can do this with composer too.
You have to tell composer three things in order to be able to do this:
- Keep the full git repository information
- Include dependancies marked as "developer" requirements
- Use the development version, not the latest stable version
The first two steps are done as part of the initial create project using additional arguments. For instance:
composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.0.x-dev
The process will take a bit longer, since all modules are checked out as full git repositories which you can work on.
The `--keep-vcs` flag will make sure you have access to the git history of the installer and the requirements
The `--dev` flag will add a couple modules which are useful for SilverStripe development:
* The `compass` module will regenerate CSS if you update the SCSS files
* The `docsviewer` module will let you preview changes to the project documentation
* The `buildtools` module which adds [phing](http://phing.info) tasks for creating SilverStripe releases
Note that you can also include those into an existing project by running `composer update --dev`.

View File

@ -16,29 +16,32 @@ We ask for this so that the ownership in the license is clear and unambiguous, a
## Step-by-step: From forking to sending the pull request
1. Follow the [Installation for contributions](../../installation/from-source#option-2-installation-for-contributions) instructions, which explain how to fork the core modules and add the correct "upstream" remote.
1. Follow the [Installation through Composer](../../installation/composer#contributing) instructions,
which explain how to fork the core modules and add the correct "upstream" remote. In short:
1. [Branch for new issue and develop on issue branch](code#branch-for-new-issue-and-develop-on-issue-branch)
composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.0.x-dev
$ git branch ###-description
$ git checkout ###-description
2. [Branch for new issue and develop on issue branch](code#branch-for-new-issue-and-develop-on-issue-branch)
1. As time passes, the upstream repository accumulates new commits. Keep your working copy's master branch and issue branch up to date by periodically [rebasing your development branch on the latest upstream](code#rebase-your-development-branch-on-the-latest-upstream).
git branch ###-description
git checkout ###-description
# [make sure all your changes are committed as necessary in branch]
$ git fetch upstream
$ git rebase upstream/master
3. As time passes, the upstream repository accumulates new commits. Keep your working copy's master branch and issue branch up to date by periodically [rebasing your development branch on the latest upstream](code#rebase-your-development-branch-on-the-latest-upstream).
1. When development is complete, [squash all commit related to a single issue into a single commit](code#squash-all-commits-related-to-a-single-issue-into-a-single-commit).
# [make sure all your changes are committed as necessary in branch]
git fetch upstream
git rebase upstream/master
$ git fetch upstream
$ git rebase -i upstream/master
4. When development is complete, [squash all commit related to a single issue into a single commit](code#squash-all-commits-related-to-a-single-issue-into-a-single-commit).
1. Push release candidate branch to GitHub
git fetch upstream
git rebase -i upstream/master
$ git push origin ###-description
5. Push release candidate branch to GitHub
1. Issue pull request on GitHub. Visit your forked respoistory on GitHub.com and click the "Create Pull Request" button nex tot the new branch.
git push origin ###-description
6. Issue pull request on GitHub. Visit your forked respoistory on GitHub.com and click the "Create Pull Request" button nex tot the new branch.
The core team is then responsible for reviewing patches and deciding if they will make it into core. If
there are any problems they will follow up with you, so please ensure they have a way to contact you!
@ -61,7 +64,10 @@ If you're familiar with it, here's the short version of what you need to know. O
* **Squash your commits, so that each commit addresses a single issue.** After you rebase your work on top of the upstream master, you can squash multiple commits into one. Say, for instance, you've got three commits in related to Issue #100. Squash all three into one with the message "Issue #100 Description of the issue here." We won't accept pull requests for multiple commits related to a single issue; it's up to you to squash and clean your commit tree. (Remember, if you squash commits you've already pushed to GitHub, you won't be able to push that same branch again. Create a new local branch, squash, and push the new squashed branch.)
* **Choose the correct branch**: Assume the current release is 3.0.3, and 3.1.0 is in beta state.
Most pull requests should go against the `3.1.x-dev` *pre-release branch*, only critical bugfixes
against the `3.0.x-dev` *release branch*. If you're changing an API or introducing a major feature,
the pull request should go against `master` (read more about our [release process](/misc/release-process)).
### Editing files directly on GitHub.com

View File

@ -311,6 +311,7 @@ without affecting the response body.
Built-in headers are:
* `X-Title`: Set window title (requires URL encoding)
* `X-Controller`: PHP class name matching a menu entry, which is marked active
* `X-ControllerURL`: Alternative URL to record in the HTML5 browser history
* `X-Status`: Extended status information, used for an information popover.

View File

@ -192,6 +192,41 @@ To include relations in your summaries, you can use a dot-notation.
);
}
## Permissions
Models can be modified in a variety of controllers and user interfaces,
all of which can implement their own security checks. But often it makes
sense to centralize those checks on the model, regardless of the used controller.
The API provides four methods for this purpose:
`canEdit()`, `canCreate()`, `canView()` and `canDelete()`.
Since they're PHP methods, they can contain arbitrary logic
matching your own requirements. They can optionally receive a `$member` argument,
and default to the currently logged in member (through `Member::currentUser()`).
Example: Check for CMS access permissions
class MyDataObject extends DataObject {
// ...
public function canView($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canEdit($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canDelete($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canCreate($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
}
**Important**: These checks are not enforced on low-level ORM operations
such as `write()` or `delete()`, but rather rely on being checked in the invoking code.
The CMS default sections as well as custom interfaces like
`[ModelAdmin](/reference/modeladmin)` or `[GridField](/reference/gridfield)`
already enforce these permissions.
## API Documentation

View File

@ -1,6 +1,6 @@
# Form Field Types
This is a highlevel overview of available `[api:apiFormField]` subclasses. An automatically generated list is available through our [API]
This is a highlevel overview of available `[api:FormField]` subclasses. An automatically generated list is available through our [API]
## Basic

View File

@ -310,6 +310,16 @@ transfered between page requests by being inserted as a hidden field in the form
A GridFieldComponent sets and gets data from the GridState.
## Permissions
Since GridField is mostly used in the CMS, the controller managing a GridField instance
will already do some permission checks for you, and can decline display or executing
any logic on your field.
If you need more granular control, e.g. to consistently deny non-admins from deleting
records, use the `DataObject->can...()` methods
(see [DataObject permissions](/reference/dataobject#permissions)).
## Related
* [ModelAdmin: A UI driven by GridField](/reference/modeladmin)

View File

@ -44,6 +44,34 @@ We'll name it `MyAdmin`, but the class name can be anything you want.
This will automatically add a new menu entry to the CMS, and you're ready to go!
Try opening http://localhost/admin/products/?flush=all.
## Permissions
Each new `ModelAdmin` subclass creates its own [permission code](/reference/permission),
for the example above this would be `CMS_ACCESS_MyAdmin`. Users with access to the CMS
need to have this permission assigned through `admin/security/` in order to gain
access to the controller (unless they're admins).
The `DataObject` API has more granular permission control, which is enforced in ModelAdmin by default.
Available checks are `canEdit()`, `canCreate()`, `canView()` and `canDelete()`.
Models check for administrator permissions by default. For most cases,
less restrictive checks make sense, e.g. checking for general CMS access rights.
:::php
class Category extends DataObject {
// ...
public function canView($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canEdit($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canDelete($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
public function canCreate($member = null) {
return Permission::check('CMS_ACCESS_CMSMain', 'any', $member);
}
## Search Fields
ModelAdmin uses the `[SearchContext](/reference/searchcontext)` class to provide

View File

@ -9,7 +9,9 @@ as well. That makes it flexible enough to sometimes even replace the Gridfield,
like for instance in creating and managing a simple gallery.
## Usage
The UploadField can be used in two ways:
The field can be used in two ways: To upload a single file into a `has_one` relationship,
or allow multiple files into a fixed folder (or relationship).
### Single fileupload
@ -76,7 +78,23 @@ UploadField will detect the relation based on its $name property value:
WARNING: Currently the UploadField doesn't fully support has_many relations, so use a many_many relation instead!
## Set a custom folder
## Configuration
### Overview
The field can either be configured on an instance level through `setConfig(<key>, <value>)`,
or globally by overriding the YAML defaults.
Example: mysite/_config/uploadfield.yml
after: framework#uploadfield
---
UploadField:
defaultConfig:
canUpload: false
### Set a custom folder
This example will save all uploads in the `/assets/customfolder/` folder. If
the folder doesn't exist, it will be created.
@ -98,7 +116,7 @@ the folder doesn't exist, it will be created.
$uploadField->getValidator()->setAllowedExtensions(array('jpg', 'jpeg', 'png', 'gif'));
## Limit the maximum file size
### Limit the maximum file size
`AllowedMaxFileSize` is by default set to the lower value of the 2 php.ini configurations: `upload_max_filesize` and `post_max_size`
The value is set as bytes.
@ -110,8 +128,6 @@ NOTE: this only sets the configuration for your UploadField, this does NOT chang
$size = $sizeMB * 1024 * 1024; // 2 MB in bytes
$this->getValidator()->setAllowedMaxFileSize($size);
## Other configuration settings
### Preview dimensions
Set the dimensions of the image preview. By default the max width is set to 80
@ -182,6 +198,27 @@ Then, in your GalleryPage, tell the UploadField to use this function:
In a similar fashion you can use 'fileEditActions' to set the actions for the
editform, or 'fileEditValidator' to determine the validator (eg RequiredFields).
### Configuration Reference
- `autoUpload`: (boolean)
- `allowedMaxFileNumber`: (int) php validation of allowedMaxFileNumber
only works when a db relation is available, set to null to allow
unlimited if record has a has_one and allowedMaxFileNumber is null, it will be set to 1
- `canUpload`: (boolean) Can the user upload new files, or just select from existing files.
String values are interpreted as permission codes.
- `previewMaxWidth`: (int)
- `previewMaxHeight`: (int)
- `uploadTemplateName`: (string) javascript template used to display uploading
files, see javascript/UploadField_uploadtemplate.js
- `downloadTemplateName`: (string) javascript template used to display already
uploaded files, see javascript/UploadField_downloadtemplate.js
- `fileEditFields`: (FieldList|string) FieldList $fields or string $name
(of a method on File to provide a fields) for the EditForm (Example: 'getCMSFields')
- `fileEditActions`: (FieldList|string) FieldList $actions or string $name
(of a method on File to provide a actions) for the EditForm (Example: 'getCMSActions')
- `fileEditValidator`: (string) Validator (eg RequiredFields) or string $name
(of a method on File to provide a Validator) for the EditForm (Example: 'getCMSValidator')
## TODO: Using the UploadField in a frontend form

View File

@ -308,7 +308,7 @@ The following example runs an if statement, and a loop on *Children*, checking t
</a>
</li>
<% end_loop %>
<ul>
</ul>
<% end_if %>
</li>
<% end_loop %>

View File

@ -368,7 +368,9 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $
function QuotedPrintable_encode($quotprint) {
$quotprint = (string)str_replace('\r\n',chr(13).chr(10),$quotprint);
$quotprint = (string)str_replace('\n', chr(13).chr(10),$quotprint);
$quotprint = (string)preg_replace("~([\x01-\x1F\x3D\x7F-\xFF])~e", "sprintf('=%02X', ord('\\1'))", $quotprint);
$quotprint = (string)preg_replace_callback("~([\x01-\x1F\x3D\x7F-\xFF])~", function($matches) {
return sprintf('=%02X', ord($matches[1]));
}, $quotprint);
//$quotprint = (string)str_replace('\=0D=0A',"=0D=0A",$quotprint);
$quotprint = (string)str_replace('=0D=0A',"\n",$quotprint);
$quotprint = (string)str_replace('=0A=0D',"\n",$quotprint);

View File

@ -39,7 +39,7 @@ class FileNameFilter extends Object {
static $default_replacements = array(
'/\s/' => '-', // remove whitespace
'/_/' => '-', // underscores to dashes
'/[^A-Za-z0-9+.-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[^A-Za-z0-9+.\-]+/' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/' => '-', // remove duplicate dashes
'/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
);

View File

@ -45,7 +45,7 @@ require_once 'Zend/Date.php';
*
* $f = new DateField('MyDate');
* $f->setLocale('de_DE');
* $f->setConfig('dmyfields');
* $f->setConfig('dmyfields', true);
*
* # Validation
*

View File

@ -64,11 +64,11 @@ class FileField extends FormField {
/**
* Partial filesystem path relative to /assets directory.
* Defaults to 'Uploads'.
* Defaults to Upload::$uploads_folder.
*
* @var string
*/
protected $folderName = 'Uploads';
protected $folderName = false;
/**
* Create a new file field.
@ -111,7 +111,7 @@ class FileField extends FormField {
$file = new $fileClass();
}
$this->upload->loadIntoFile($_FILES[$this->name], $file, $this->folderName);
$this->upload->loadIntoFile($_FILES[$this->name], $file, $this->getFolderName());
if($this->upload->isError()) return false;
$file = $this->upload->getFile();
@ -160,7 +160,7 @@ class FileField extends FormField {
* @return string
*/
public function getFolderName() {
return $this->folderName;
return ($this->folderName !== false) ? $this->folderName : Upload::$uploads_folder;
}
public function validate($validator) {

View File

@ -78,7 +78,6 @@ class FormAction extends FormField {
public function getAttributes() {
$type = (isset($this->attributes['src'])) ? 'image' : 'submit';
$type = ($this->useButtonTag) ? null : $type;
return array_merge(
parent::getAttributes(),

View File

@ -125,7 +125,7 @@ class FormField extends RequestHandler {
foreach($attributes as $k => $v) {
// Note: as indicated by the $k == value item here; the decisions over what to include in the attributes
// can sometimes get finicky
if(!empty($v) || $v === '0' || $k == 'value') {
if(!empty($v) || $v === '0' || ($k == 'value' && $v !== null) ) {
$preparedAttributes .= " $k=\"" . Convert::raw2att($v) . "\"";
}
}

View File

@ -85,8 +85,15 @@ class RequiredFields extends Validator {
}
if($formField && $error) {
$errorMessage = sprintf(_t('Form.FIELDISREQUIRED', '%s is required'),
strip_tags('"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"'));
$errorMessage = _t(
'Form.FIELDISREQUIRED',
'{name} is required',
array(
'name' => strip_tags(
'"' . ($formField->Title() ? $formField->Title() : $fieldName) . '"'
)
)
);
if($msg = $formField->getCustomValidationMessage()) {
$errorMessage = $msg;

View File

@ -85,7 +85,7 @@ class TreeDropdownField extends FormField {
* @param bool $showSearch enable the ability to search the tree by
* entering the text in the input field.
*/
public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'Title',
public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'TreeTitle',
$showSearch = false) {
$this->sourceObject = $sourceObject;
@ -159,6 +159,7 @@ class TreeDropdownField extends FormField {
*/
public function setChildrenMethod($method) {
$this->childrenMethod = $method;
return $this;
}
/**
@ -229,8 +230,7 @@ class TreeDropdownField extends FormField {
? (int)$request->latestparam('ID')
: (int)$request->requestVar('ID');
$forceFullTree = $request->requestVar('forceFullTree')?$request->requestVar('forceFullTree'):false;
if($ID && !$forceFullTree) {
if($ID && !$request->requestVar('forceFullTree')) {
$obj = DataObject::get_by_id($this->sourceObject, $ID);
$isSubTree = true;
if(!$obj) {
@ -297,6 +297,7 @@ class TreeDropdownField extends FormField {
*/
public function setLabelField($field) {
$this->labelField = $field;
return $this;
}
/**
@ -311,6 +312,7 @@ class TreeDropdownField extends FormField {
*/
public function setKeyField($field) {
$this->keyField = $field;
return $this;
}
/**
@ -325,6 +327,7 @@ class TreeDropdownField extends FormField {
*/
public function setSourceObject($class) {
$this->sourceObject = $class;
return $this;
}
/**

View File

@ -13,6 +13,8 @@
* - Edit file
* - allowedExtensions is by default File::$allowed_extensions<li>maxFileSize the value of min(upload_max_filesize,
* post_max_size) from php.ini
*
* <>Usage</b>
*
* @example <code>
* $UploadField = new UploadField('myFiles', 'Please upload some images <span>(max. 5 files)</span>');
@ -66,9 +68,9 @@ class UploadField extends FileField {
protected $items;
/**
* Config for this field used in both, php and javascript (will be merged into the config of the javascript file
* upload plugin)
* @var array
* @var array Config for this field used in both, php and javascript
* (will be merged into the config of the javascript file upload plugin).
* See framework/_config/uploadfield.yml for configuration defaults and documentation.
*/
protected $ufConfig = array(
/**
@ -81,6 +83,11 @@ class UploadField extends FileField {
* @var int
*/
'allowedMaxFileNumber' => null,
/**
* @var boolean|string Can the user upload new files, or just select from existing files.
* String values are interpreted as permission codes.
*/
'canUpload' => true,
/**
* @var int
*/
@ -133,6 +140,8 @@ class UploadField extends FileField {
$this->addExtraClass('ss-upload'); // class, used by js
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
$this->ufConfig = array_merge($this->ufConfig, Config::inst()->get('UploadField', 'defaultConfig'));
parent::__construct($name, $title);
if($items) $this->setItems($items);
@ -431,7 +440,7 @@ class UploadField extends FileField {
* @return UploadField_ItemHandler
*/
public function handleSelect(SS_HTTPRequest $request) {
return UploadField_SelectHandler::create($this, $this->folderName);
return UploadField_SelectHandler::create($this, $this->getFolderName());
}
/**
@ -441,7 +450,9 @@ class UploadField extends FileField {
* @return string json
*/
public function upload(SS_HTTPRequest $request) {
if($this->isDisabled() || $this->isReadonly()) return $this->httpError(403);
if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
return $this->httpError(403);
}
// Protect against CSRF on destructive action
$token = $this->getForm()->getSecurityToken();
@ -500,7 +511,7 @@ class UploadField extends FileField {
// Get the uploaded file into a new file object.
try {
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->folderName);
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
} catch (Exception $e) {
// we shouldn't get an error here, but just in case
$return['error'] = $e->getMessage();
@ -629,6 +640,12 @@ class UploadField extends FileField {
// Don't allow upload or edit of a relation when the underlying record hasn't been persisted yet
return (!$record || !$this->managesRelation() || $record->exists());
}
public function canUpload() {
$can = $this->getConfig('canUpload');
return (is_bool($can)) ? $can : Permission::check($can);
}
}
/**

View File

@ -1,6 +1,7 @@
<?php
/**
* This component provides a button for opening the add new form provided by {@link GridFieldDetailForm}.
* Only returns a button if {@link DataObject->canCreate()} for this record returns true.
*
* @package framework
* @subpackage gridfield
@ -21,9 +22,12 @@ class GridFieldAddNewButton implements GridField_HTMLProvider {
}
public function getHTMLFragments($gridField) {
$singleton = singleton($gridField->getModelClass());
if(!$singleton->canCreate()) return array();
if(!$this->buttonName) {
// provide a default button name, can be changed by calling {@link setButtonName()} on this component
$objectName = singleton($gridField->getModelClass())->i18n_singular_name();
$objectName = $singleton->i18n_singular_name();
$this->buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
}

View File

@ -98,15 +98,16 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
*/
public function getColumnContent($gridField, $record, $columnName) {
if($this->removeRelation) {
if(!$record->canEdit()) return;
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false,
"unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus');
} else {
if(!$record->canDelete()) {
return;
}
if(!$record->canDelete()) return;
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord",
array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete')
@ -132,13 +133,20 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
if(!$item) {
return;
}
if($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(
_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
if($actionName == 'deleterecord') {
if(!$item->canDelete()) {
throw new ValidationException(
_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
$item->delete();
} else {
if(!$item->canEdit()) {
throw new ValidationException(
_t('GridFieldAction_Delete.EditPermissionsFailure',"No permission to unlink record"),0);
}
$gridField->getList()->remove($item);
}
}

View File

@ -310,16 +310,31 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
return $controller->redirect($noActionURL, 302);
}
$canView = $this->record->canView();
$canEdit = $this->record->canEdit();
$canDelete = $this->record->canDelete();
$canCreate = $this->record->canCreate();
if(!$canView) {
$controller = Controller::curr();
// TODO More friendly error
return $controller->httpError(403);
}
$actions = new FieldList();
if($this->record->ID !== 0) {
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
->setUseButtonTag(true)
->addExtraClass('ss-ui-action-constructive')
->setAttribute('data-icon', 'accept'));
if($canEdit) {
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
->setUseButtonTag(true)
->addExtraClass('ss-ui-action-constructive')
->setAttribute('data-icon', 'accept'));
}
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
->setUseButtonTag(true)
->addExtraClass('ss-ui-action-destructive'));
if($canDelete) {
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
->setUseButtonTag(true)
->addExtraClass('ss-ui-action-destructive'));
}
}else{ // adding new record
//Change the Save label to 'Create'
@ -353,6 +368,14 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
$form->loadDataFrom($this->record, $this->record->ID == 0 ? Form::MERGE_IGNORE_FALSEISH : Form::MERGE_DEFAULT);
if($this->record->ID && !$canEdit) {
// Restrict editing of existing records
$form->makeReadonly();
} elseif(!$this->record->ID && !$canCreate) {
// Restrict creation of new records
$form->makeReadonly();
}
// Load many_many extraData for record.
// Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields().
if($list instanceof ManyManyList) {
@ -429,6 +452,10 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
$extraData = null;
}
if(!$this->record->canEdit()) {
return $controller->httpError(403);
}
try {
$form->saveInto($this->record);
$this->record->write();
@ -451,10 +478,16 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
// TODO Save this item into the given relationship
$message = sprintf(
_t('GridFieldDetailForm.Saved', 'Saved %s %s'),
$this->record->singular_name(),
'<a href="' . $this->Link('edit') . '">"' . htmlspecialchars($this->record->Title, ENT_QUOTES) . '"</a>'
$link = '<a href="' . $this->Link('edit') . '">"'
. htmlspecialchars($this->record->Title, ENT_QUOTES)
. '"</a>';
$message = _t(
'GridFieldDetailForm.Saved',
'Saved {name} {link}',
array(
'name' => $this->record->singular_name(),
'link' => $link
)
);
$form->sessionMessage($message, 'good');

View File

@ -72,9 +72,9 @@ class GridFieldEditButton implements GridField_ColumnProvider {
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
if(!$record->canEdit()){
return;
}
// No permission checks, handled through GridFieldDetailForm,
// which can make the form readonly if no edit permissions are available.
$data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));

View File

@ -130,7 +130,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
// Update item count prior to filter. GridFieldPageCount will rely on this value
$this->totalItems = $dataList->count();
if(!($dataList instanceof SS_Limitable)) {
if(!($dataList instanceof SS_Limitable) || ($dataList instanceof UnsavedRelationList)) {
return $dataList;
}

View File

@ -1536,7 +1536,13 @@ class i18n extends Object implements TemplateGlobalProvider {
// Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order.
// Fail silently in case the translation is outdated
$replaced = @vsprintf($returnValue, array_values($injectionArray));
preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs);
if($returnValueArgs) foreach($returnValueArgs[0] as $i => $returnValueArg) {
if($i >= count($injectionArray)) {
$injectionArray[] = '';
}
}
$replaced = vsprintf($returnValue, array_values($injectionArray));
if($replaced) $returnValue = $replaced;
} else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found,

View File

@ -289,7 +289,7 @@ class i18nTextCollector extends Object {
*
* @todo Why the type juggling for $this->collectFromTemplate()? It always returns an array.
*/
public function collectFromTemplate($content, $fileName, $module) {
public function collectFromTemplate($content, $fileName, $module, &$parsedFiles = array()) {
$entities = array();
// Search for included templates
@ -299,11 +299,12 @@ class i18nTextCollector extends Object {
$includeFileName = "{$includeName}.ss";
$filePath = SSViewer::getTemplateFileByType($includeName, 'Includes');
if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
if($filePath) {
if($filePath && !in_array($filePath, $parsedFiles)) {
$parsedFiles[] = $filePath;
$includeContent = file_get_contents($filePath);
$entities = array_merge(
$entities,
(array)$this->collectFromTemplate($includeContent, $module, $includeFileName)
(array)$this->collectFromTemplate($includeContent, $module, $includeFileName, $parsedFiles)
);
}
// @todo Will get massively confused if you include the includer -> infinite loop

View File

@ -16,4 +16,17 @@
this.find('.ss-uploadfield-editandorganize').show();
}
});
$('.ss-uploadfield-view-allowed-extensions').entwine({
onmatch: function() {
this.find('.description .toggle-content').hide();
this._super();
}
});
$('.ss-uploadfield-view-allowed-extensions .toggle').entwine({
onclick: function(e) {
jQuery(this).closest('.description').find('.toggle-content').toggle();
return false;
}
});
}(jQuery));

View File

@ -1,98 +1,98 @@
af:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
NEWFOLDER: NewFolder
NEWFOLDER: 'Nuwe Dossier'
AssetTableField:
CREATED: 'First uploaded'
DIM: Dimensions
FILENAME: Filename
FOLDER: Folder
LASTEDIT: 'Last changed'
OWNER: Owner
SIZE: 'File size'
TITLE: Title
TYPE: 'File type'
CREATED: 'Eerste opgelaai'
DIM: Afmetings
FILENAME: 'Lêer naam'
FOLDER: Dossier
LASTEDIT: 'Laaste verander'
OWNER: Eienaar
SIZE: 'Lêer grootte'
TITLE: Titel
TYPE: 'Lêer tipe'
URL: URL
AssetUploadField:
ChooseFiles: 'Choose files'
DRAGFILESHERE: 'Drag files here'
DROPAREA: 'Drop Area'
EDITALL: 'Edit all'
EDITANDORGANIZE: 'Edit & organize'
EDITINFO: 'Edit files'
FILES: Files
FROMCOMPUTER: 'Choose files from your computer'
FROMCOMPUTERINFO: 'Upload from your computer'
TOTAL: Total
TOUPLOAD: 'Choose files to upload...'
ChooseFiles: 'Kies lêers'
DRAGFILESHERE: 'Trek lêers hiernatoe'
DROPAREA: 'Laat val area'
EDITALL: 'Verander alles'
EDITANDORGANIZE: 'Verander en organiseer'
EDITINFO: 'Verander lêers'
FILES: Lêers
FROMCOMPUTER: 'Kies lêers van jou rekenaar af'
FROMCOMPUTERINFO: 'Laai op van jou rekenaar af'
TOTAL: Totaal
TOUPLOAD: 'Kies lêers om op te laai...'
UPLOADINPROGRESS: 'Please wait… upload in progress'
UPLOADOR: OR
UPLOADOR: OF
BBCodeParser:
ALIGNEMENT: Belyning
ALIGNEMENTEXAMPLE: 'right aligned'
ALIGNEMENTEXAMPLE: 'Rig na regs'
BOLD: 'Vet Teks'
BOLDEXAMPLE: Vet
CODE: 'Code Block'
CODEDESCRIPTION: 'Unformatted code block'
CODEEXAMPLE: 'Code block'
CODE: 'Kode blok'
CODEDESCRIPTION: 'Ongeformateerde kode blok'
CODEEXAMPLE: 'Kode blok'
COLORED: 'Gekleurde teks'
COLOREDEXAMPLE: 'blou teks'
EMAILLINK: 'Email link'
EMAILLINKDESCRIPTION: 'Create link to an email address'
IMAGE: Image
IMAGEDESCRIPTION: 'Show an image in your post'
EMAILLINK: 'Epos skakel'
EMAILLINKDESCRIPTION: 'Skep skakel na epos adres'
IMAGE: 'Geen prentjie gelaai nie'
IMAGEDESCRIPTION: 'Wys ''n foto in wat jy gepos het'
ITALIC: 'Skuinsstrepe Teks'
ITALICEXAMPLE: Skuinsstrepe
LINK: 'Website link'
LINKDESCRIPTION: 'Link to another website or URL'
LINK: 'Webwerf skakel'
LINKDESCRIPTION: 'Koppel aan ''n ander webwerf of URL'
STRUCK: 'Deur-gestreepde Teks'
STRUCKEXAMPLE: Deur-gestreep
UNDERLINE: 'Beklemtoonde Teks'
UNDERLINEEXAMPLE: Beklemtoon
UNORDERED: 'Unordered list'
UNORDEREDDESCRIPTION: 'Unordered list'
UNORDEREDEXAMPLE1: 'unordered item 1'
UNORDERED: 'Ongeorganiseerde lys'
UNORDEREDDESCRIPTION: 'Ongeorganiseerde lys'
UNORDEREDEXAMPLE1: 'Ongeorganiseerde item 1'
BackLink_Button.ss:
Back: Back
Back: Terug
BasicAuth:
ENTERINFO: 'Please enter a username and password.'
ERRORNOTADMIN: 'That user is not an administrator.'
ERRORNOTREC: 'That username / password isn''t recognised'
ENTERINFO: 'Tik asseblief ''n verbruikersnaam en wagwoord in'
ERRORNOTADMIN: 'Daardie verbruiker is nie ''n administreerder nie'
ERRORNOTREC: 'Daar die verbruikersnaam / wagwoord is nie herken nie'
Boolean:
0: 'False'
ANY: Any
1: 'True'
0: Onwaar
ANY: Enige
1: Waar
CMSLoadingScreen.ss:
LOADING: Loading...
REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
LOADING: 'Besig om te laai...'
REQUIREJS: 'Die IBS vereis dat JavaScript aangeskakel is'
CMSMain:
ACCESS: 'Access to ''{title}'' section'
ACCESSALLINTERFACES: 'Access to all CMS sections'
ACCESSALLINTERFACESHELP: 'Overrules more specific access settings.'
SAVE: Save
ACCESSALLINTERFACES: 'Toegang tot alle IBS gedeeltes'
ACCESSALLINTERFACESHELP: 'Oorheers meer spesifieke toegans verstellings'
SAVE: Stoor
CMSProfileController:
MENUTITLE: 'My Profile'
MENUTITLE: 'My profiel'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'U het die wagwoord vir'
CHANGEPASSWORDTEXT2: 'You can now use the following credentials to log in:'
EMAIL: Email
HELLO: Hi
PASSWORD: Password
CHANGEPASSWORDTEXT2: 'Jy kan nou die volgende sekuriteitsbesonderhede gebruik om in te teken'
EMAIL: Epos
HELLO: 'Hi daar'
PASSWORD: Wagwoord
CheckboxField:
- 'False'
- 'True'
- Onwaar
- Waar
ComplexTableField:
CLOSEPOPUP: 'Close Popup'
SUCCESSADD2: 'Added {name}'
SUCCESSEDIT: 'Saved %s %s %s'
CLOSEPOPUP: 'Maak wipop toe'
SUCCESSADD2: '{name} bygesit'
SUCCESSEDIT: 'Gestoor %s %s %s'
ComplexTableField.ss:
ADDITEM: 'Byvoeg %s'
NOITEMSFOUND: 'No items found'
SORTASC: 'Sort ascending'
SORTDESC: 'Sort descending'
NOITEMSFOUND: 'Geen item gevind nie'
SORTASC: 'Sorteer in stygende orde'
SORTDESC: 'Sorteer in dalende orde'
ComplexTableField_popup.ss:
NEXT: Next
PREVIOUS: Previous
NEXT: Volgende
PREVIOUS: Vorige
ConfirmedPasswordField:
ATLEAST: 'Passwords must be at least {min} characters long.'
BETWEEN: 'Passwords must be {min} to {max} characters long.'
@ -109,20 +109,20 @@ af:
PLURALNAME: 'Data Voorwerpe'
SINGULARNAME: 'Data Voorwerp'
Date:
DAY: ' day'
DAYS: ' days'
HOUR: ' hour'
HOURS: ' hours'
MIN: ' min'
MINS: ' mins'
MONTH: ' month'
MONTHS: ' months'
SEC: ' sec'
SECS: ' secs'
TIMEDIFFAGO: '{difference} ago'
DAY: dag
DAYS: dae
HOUR: uur
HOURS: ure
MIN: minuut
MINS: minute
MONTH: maand
MONTHS: maande
SEC: sekonde
SECS: sekondes
TIMEDIFFAGO: '{difference} terug'
TIMEDIFFIN: 'in {difference}'
YEAR: ' year'
YEARS: ' years'
YEAR: jaar
YEARS: jare
DateField:
NOTSET: 'nier gestel'
TODAY: vandag
@ -130,68 +130,68 @@ af:
VALIDDATEMAXDATE: 'Your date has to be older or matching the maximum allowed date ({date})'
VALIDDATEMINDATE: 'Your date has to be newer or matching the minimum allowed date ({date})'
DatetimeField:
NOTSET: 'Not set'
NOTSET: 'Nie gestel nie'
Director:
INVALID_REQUEST: 'Invalid request'
INVALID_REQUEST: 'Ongeldige versoek'
DropdownField:
CHOOSE: (Kies)
EmailField:
VALIDATION: 'Please enter an email address'
VALIDATION: 'Verskaf asseblief ''n epos adres '
Email_BounceRecord:
PLURALNAME: 'Email Bounce Records'
SINGULARNAME: 'Email Bounce Record'
PLURALNAME: 'Epos hop rekords'
SINGULARNAME: 'Epos hop rekord'
Enum:
ANY: Any
ANY: Enige
File:
AviType: 'AVI video file'
Content: Content
CssType: 'CSS file'
Content: Inhoud
CssType: 'CSS lêer'
DmgType: 'Apple disk image'
DocType: 'Word document'
Filename: Filename
DocType: 'Word dokument'
Filename: 'Lêer naam'
GifType: 'GIF image - good for diagrams'
GzType: 'GZIP compressed file'
HtlType: 'HTML file'
HtlType: 'HTML lêer'
HtmlType: 'HTML file'
INVALIDEXTENSION: 'Extension is not allowed (valid: {extensions})'
INVALIDEXTENSIONSHORT: 'Extension is not allowed'
IcoType: 'Icon image'
JpgType: 'JPEG image - good for photos'
IcoType: 'Ikoon prentjie'
JpgType: 'JPEG prentjie - werk goed vir fotos'
JsType: 'Javascript file'
Mp3Type: 'MP3 audio file'
Mp3Type: 'MP3 klank lêer'
MpgType: 'MPEG video file'
NOFILESIZE: 'Lêergrootte is nul grepe.'
NOVALIDUPLOAD: 'File is not a valid upload'
Name: Name
NOVALIDUPLOAD: 'Lêer is nie geld vir oplaai nie'
Name: Naam
PLURALNAME: Lêers
PdfType: 'Adobe Acrobat PDF file'
PngType: 'PNG image - good general-purpose format'
SINGULARNAME: Lêer
TOOLARGE: 'Filesize is too large, maximum {size} allowed'
TOOLARGESHORT: 'Filesize exceeds {size}'
TOOLARGESHORT: 'Lêer is groter as {size}'
TiffType: 'Tagged image format'
Title: Title
WavType: 'WAV audo file'
Title: Titel
WavType: 'WAV klank lêer'
XlsType: 'Excel spreadsheet'
ZipType: 'ZIP compressed file'
FileIFrameField:
ATTACH: 'Attach {type}'
ATTACHONCESAVED: '{type}s can be attached once you have saved the record for the first time.'
ATTACH: 'Heg {type} aan'
ATTACHONCESAVED: '{type}e kan aangeheg word sodra jy die rekord vir die eerste keer gestoor het'
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
DELETE: 'Delete {type}'
DISALLOWEDFILETYPE: 'This filetype is not allowed to be uploaded'
FILE: File
FROMCOMPUTER: 'From your Computer'
FROMFILESTORE: 'From the File Store'
DELETE: 'Verwyder {type}'
DISALLOWEDFILETYPE: 'Dit word nie toegelaat om hierde lêer tipe op te laai nie'
FILE: Lêer
FROMCOMPUTER: 'Van jou rekenaar'
FROMFILESTORE: 'Vanuit die lêer stoor'
NOSOURCE: 'Kies asseblief ''n bron lêer om by te voeg'
REPLACE: 'Replace {type}'
REPLACE: 'Vervang {type}'
FileIFrameField_iframe.ss:
TITLE: 'Image Uploading Iframe'
TITLE: 'Prentjie oplaaiende ''Iframe'''
Filesystem:
SYNCRESULTS: 'Sync complete: {createdcount} items created, {deletedcount} items deleted'
Folder:
PLURALNAME: Folders
SINGULARNAME: Folder
PLURALNAME: Dossiers
SINGULARNAME: Dossier
ForgotPasswordEmail.ss:
HELLO: Hi
TEXT1: 'Hier is u'
@ -199,214 +199,214 @@ af:
TEXT3: vir
Form:
FIELDISREQUIRED: '%s word benodig.'
SubmitBtnLabel: Go
SubmitBtnLabel: Gaan
VALIDATIONCREDITNUMBER: 'Please ensure you have entered the {number} credit card number correctly'
VALIDATIONNOTUNIQUE: 'The waarde wat ingesleutel is is nie uniek nie'
VALIDATIONPASSWORDSDONTMATCH: 'Wagwoorde pas nie'
VALIDATIONPASSWORDSNOTEMPTY: 'Wagwoorde kan nie leeg wees nie'
VALIDATIONSTRONGPASSWORD: 'Passwords must have at least one digit and one alphanumeric character'
VALIDATIONSTRONGPASSWORD: 'Wagwoorde moet minstens een nommer en een alfa-numeriese karaketer bevat'
VALIDATOR: Vergeldiger
VALIDCURRENCY: 'Please enter a valid currency'
VALIDCURRENCY: 'Tik asseblief ''n geldige geldeenheid in '
FormField:
NONE: geen
GridAction:
DELETE_DESCRIPTION: Delete
Delete: Delete
UnlinkRelation: Unlink
DELETE_DESCRIPTION: Verwyder
Delete: Verwyder
UnlinkRelation: Ontkoppel
GridField:
Add: 'Add {name}'
Add: 'Voeg {name}'
Filter: Filter
FilterBy: 'Filter by '
Find: Find
LEVELUP: 'Level up'
LinkExisting: 'Link Existing'
NewRecord: 'New %s'
NoItemsFound: 'No items found'
PRINTEDAT: 'Printed at'
PRINTEDBY: 'Printed by'
PlaceHolder: 'Find {type}'
Find: Vind
LEVELUP: 'Op een vlak'
LinkExisting: 'Koppel bestaande'
NewRecord: 'Nuwe %s'
NoItemsFound: 'Geen items gevind nie'
PRINTEDAT: 'Gedruk te'
PRINTEDBY: 'Gedruk deur'
PlaceHolder: 'Vind {type}'
PlaceHolderWithLabels: 'Find {type} by {name}'
RelationSearch: 'Relation search'
ResetFilter: Reset
RelationSearch: 'Soek vir verwantskap'
ResetFilter: Herstel
GridFieldAction_Delete:
DeletePermissionsFailure: 'No delete permissions'
DeletePermissionsFailure: 'Geen toestemming om te verwyder nie'
GridFieldDetailForm:
CancelBtn: Cancel
Create: Create
Delete: Delete
DeletePermissionsFailure: 'No delete permissions'
Deleted: 'Deleted %s %s'
Save: Save
Saved: 'Saved %s %s'
CancelBtn: 'Kanselleer '
Create: Skep
Delete: Verwyder
DeletePermissionsFailure: 'Geen toestemming om te verwyder nie'
Deleted: 'Verwyderde %s %s'
Save: Stoor
Saved: 'Gestoor %s %s'
GridFieldItemEditView.ss: null
Group:
AddRole: 'Add a role for this group'
AddRole: 'Voeg nog ''n rol by hierdie groep'
Code: 'Groep Kode'
DefaultGroupTitleAdministrators: Administrateurs
DefaultGroupTitleContentAuthors: 'Inhouds Outeurs'
Description: Description
GroupReminder: 'If you choose a parent group, this group will take all it''s roles'
Description: Beskrywing
GroupReminder: 'As jy ''n ouer groep kies sal hierdie groep al daardie rolle aanneem'
Locked: 'Gesluit?'
NoRoles: 'No roles found'
PLURALNAME: Groups
NoRoles: 'Geen rolle gevind nie'
PLURALNAME: Groepe
Parent: 'Ouer Groep'
RolesAddEditLink: 'Manage roles'
SINGULARNAME: Group
Sort: 'Sort Order'
RolesAddEditLink: 'Bestuur rolle'
SINGULARNAME: Groep
Sort: 'Sorteerings orde'
has_many_Permissions: Toestemmings
many_many_Members: Lidde
GroupImportForm:
Help1: '<p>Import one or more groups in <em>CSV</em> format (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
Help1: '<p> Voer een of meer groepe in <em>CSV</em>formaat (komma geskeide waardes).<small> <a href="#" class="toggle-advanced">Wys gevorderde gebruike</a></small></p>'
Help2: '<div class="advanced"> <h4>Advanced usage</h4> <ul> <li>Allowed columns: <em>%s</em></li> <li>Existing groups are matched by their unique <em>Code</em> value, and updated with any new values from the imported file</li> <li>Group hierarchies can be created by using a <em>ParentCode</em> column.</li> <li>Permission codes can be assigned by the <em>PermissionCode</em> column. Existing permission codes are not cleared.</li> </ul></div>'
ResultCreated: 'Created {count} groups'
ResultDeleted: 'Deleted %d groups'
ResultUpdated: 'Updated %d groups'
ResultDeleted: 'Verwyderde %d groepe'
ResultUpdated: '%d Groepe was opgedateer'
Hierarchy:
InfiniteLoopNotAllowed: 'Infinite loop found within the "{type}" hierarchy. Please change the parent to resolve this'
HtmlEditorField:
ADDURL: 'Add URL'
ADJUSTDETAILSDIMENSIONS: 'Details &amp; dimensions'
ANCHORVALUE: Anker
BUTTONINSERT: Insert
BUTTONINSERTLINK: 'Insert link'
BUTTONREMOVELINK: 'Remove link'
BUTTONUpdate: Update
CAPTIONTEXT: 'Caption text'
CSSCLASS: 'Alignment / style'
CSSCLASSCENTER: 'Centered, on its own.'
CSSCLASSLEFT: 'On the left, with text wrapping around.'
BUTTONINSERT: 'Plaas in'
BUTTONINSERTLINK: 'Sit in'
BUTTONREMOVELINK: 'Verwyder skakel'
BUTTONUpdate: Verander
CAPTIONTEXT: 'Onderskrif teks'
CSSCLASS: 'Belyning styl'
CSSCLASSCENTER: 'In die middel op sy eie'
CSSCLASSLEFT: ' Links met teks wat rondom vloei'
CSSCLASSLEFTALONE: 'Op die linkerkant, op sy eie.'
CSSCLASSRIGHT: 'On the right, with text wrapping around.'
DETAILS: Details
EMAIL: 'Email address'
FILE: File
FOLDER: Folder
FROMCMS: 'From the CMS'
FROMCOMPUTER: 'From your computer'
CSSCLASSRIGHT: 'Regs met teks wat rondom vloei'
DETAILS: Besonderhede
EMAIL: 'Epos Adres'
FILE: Lêer
FOLDER: Dossier
FROMCMS: 'Van die IBS'
FROMCOMPUTER: 'Van you rekenaar'
FROMWEB: 'From the web'
FindInFolder: 'Find in Folder'
FindInFolder: 'Find in dossier'
IMAGEALT: 'Alternative text (alt)'
IMAGEALTTEXT: 'Alternative text (alt) - shown if image cannot be displayed'
IMAGEALTTEXT: 'Alternatiewe teks (alt) - word gewys as prentjie nie beskikbaar is nie'
IMAGEALTTEXTDESC: 'Shown to screen readers or if image can not be displayed'
IMAGEDIMENSIONS: Dimensions
IMAGEHEIGHTPX: Height
IMAGETITLE: 'Title text (tooltip) - for additional information about the image'
IMAGETITLETEXT: 'Title text (tooltip)'
IMAGEDIMENSIONS: Afmetings
IMAGEHEIGHTPX: Hoogte
IMAGETITLE: 'Titel teks (leidraad) - vir addisionele informasie oor die prentjie'
IMAGETITLETEXT: 'Titel teks (leidraad)'
IMAGETITLETEXTDESC: 'For additional information about the image'
IMAGEWIDTHPX: Width
INSERTMEDIA: 'Insert Media'
LINK: 'Insert Link'
IMAGEWIDTHPX: Wydte
INSERTMEDIA: 'Voeg Media In'
LINK: 'Sit skakel in'
LINKANCHOR: 'Anker op hierdie bladsy'
LINKDESCR: 'Link description'
LINKEMAIL: 'Email address'
LINKEXTERNAL: 'Another website'
LINKFILE: 'Download a file'
LINKINTERNAL: 'Page on the site'
LINKOPENNEWWIN: 'Open link in a new window?'
LINKTO: 'Link to'
PAGE: Page
LINKDESCR: 'Skakel beskrywing'
LINKEMAIL: 'Epos Adres'
LINKEXTERNAL: 'Ander webwerf'
LINKFILE: 'Laai ''n lêer af'
LINKINTERNAL: 'Bladsy op die webwerf'
LINKOPENNEWWIN: 'Wil jy die skakel in ''n nuwe venster oop maak?'
LINKTO: 'Koppel aan'
PAGE: Bladsy
URL: URL
URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
UpdateMEDIA: 'Update Media'
UpdateMEDIA: 'Verander Media'
Image:
PLURALNAME: Files
SINGULARNAME: File
PLURALNAME: Lêers
SINGULARNAME: Lêer
ImageField:
IMAGE: Image
IMAGE: 'Geen prentjie gelaai nie'
Image_Cached:
PLURALNAME: Files
SINGULARNAME: File
PLURALNAME: Lêers
SINGULARNAME: Lêer
Image_iframe.ss:
TITLE: 'Image Uploading Iframe'
TITLE: 'Iframe wat fotos laai'
LeftAndMain:
CANT_REORGANISE: 'You do not have permission to alter Top level pages. Your change was not saved.'
DELETED: Deleted.
DropdownBatchActionsDefault: Actions
DELETED: 'Was verwyder'
DropdownBatchActionsDefault: Aksies
HELP: Help
PAGETYPE: 'Page type: '
PERMAGAIN: 'You have been logged out of the CMS. If you would like to log in again, enter a username and password below.'
PERMALREADY: 'I''m sorry, but you can''t access that part of the CMS. If you want to log in as someone else, do so below'
PAGETYPE: 'Bladsy tipe:'
PERMAGAIN: 'Jy is uit die IBS uitgeteken. As jy weer wil inteken, moet jy ''n gebruikersnaam en wagwoord onder in tik'
PERMALREADY: 'Ek is jammer, maar jy het nie toestemming om dié gedeelte van die IBS te besugtig nie. As jy as iemand anders wil inteken doen so hieronder'
PERMDEFAULT: 'Please choose an authentication method and enter your credentials to access the CMS.'
PLEASESAVE: 'Please Save Page: This page could not be upated because it hasn''t been saved yet.'
PreviewButton: Preview
PLEASESAVE: 'Stoor asseblief die bladsy: Die bladsy kon nie opgedateer word nie omdat dit nog nie gestoor is nie'
PreviewButton: Beskou
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
SAVEDUP: Saved.
SAVEDUP: Gestoor
VersionUnknown: Unknown
LeftAndMain_Menu.ss:
Hello: Hi
LOGOUT: 'Log out'
LOGOUT: 'Teken af'
LoginAttempt:
Email: 'Email Address'
IP: 'IP Address'
PLURALNAME: 'Login Attempts'
SINGULARNAME: 'Login Attempt'
Status: Status
Email: 'Epos Adres'
IP: 'IP Adres'
PLURALNAME: 'Inteken Pogings'
SINGULARNAME: 'Inteken Poging'
Status: Posisie
Member:
ADDGROUP: 'Add group'
ADDGROUP: 'Voeg groep by'
BUTTONCHANGEPASSWORD: 'Verander Wagwoord'
BUTTONLOGIN: 'Log in'
BUTTONLOGINOTHER: 'Log in as someone else'
BUTTONLOSTPASSWORD: 'I''ve lost my password'
CANTEDIT: 'You don''t have permission to do that'
BUTTONLOGIN: 'Teken in'
BUTTONLOGINOTHER: 'Teken in as iemand anders'
BUTTONLOSTPASSWORD: 'Ek het my wagwoord verloor'
CANTEDIT: 'Jy het nie toestemming om dit te doen nie'
CONFIRMNEWPASSWORD: 'Bevestig Nuwe Wagwoord'
CONFIRMPASSWORD: 'Bevestig wagwoord'
DATEFORMAT: 'Date format'
DATEFORMAT: 'Datum formaat'
DefaultAdminFirstname: 'Verstek Admin'
DefaultDateTime: default
DefaultDateTime: Gewone
EMAIL: Epos
EMPTYNEWPASSWORD: 'The new password can''t be empty, please try again'
ENTEREMAIL: 'Please enter an email address to get a password reset link.'
ERRORLOCKEDOUT: 'Your account has been temporarily disabled because of too many failed attempts at logging in. Please try again in 20 minutes.'
ERRORNEWPASSWORD: 'You have entered your new password differently, try again'
EMPTYNEWPASSWORD: 'Die nuwe wagwoord kan nie leeg wees nie. Probeer asseblief weer'
ENTEREMAIL: 'Verskaf asseblief ''n epos adres sodat ons vir u ''n wagwoord herstel skakel kan epos'
ERRORLOCKEDOUT: 'Jou rekening is tydelik onklaar gemaak weens die feit dat jy te veel keer verkeerdelik probeer inteken het. Probeer asseblief weer in 20 minute.'
ERRORNEWPASSWORD: 'Jy het jou nuwe wagwoord anders ingetik. Probeer weer'
ERRORPASSWORDNOTMATCH: 'U huidige wagwoord pas nie, probeer asseblief weer'
ERRORWRONGCRED: 'That doesn''t seem to be the right e-mail address or password. Please try again.'
FIRSTNAME: 'First Name'
ERRORWRONGCRED: 'Dit blyk nie of dit die regte e-pos adres of wagwoord is nie. Probeer asseblief weer.'
FIRSTNAME: Voornaam
INTERFACELANG: 'Koppelvlak Taal'
INVALIDNEWPASSWORD: 'We couldn''t accept that password: {password}'
LOGGEDINAS: 'You''re logged in as {name}.'
LOGGEDINAS: 'Jy is ingeteken as {name}'
NEWPASSWORD: 'Nuwe wagwoord'
PASSWORD: Password
PASSWORD: Wagwoord
PLURALNAME: Lidde
REMEMBERME: 'Remember me next time?'
REMEMBERME: 'Onthou volgende keer vir my?'
SINGULARNAME: Lid
SUBJECTPASSWORDCHANGED: 'U wagwoord het verander.'
SUBJECTPASSWORDRESET: 'U wagwoord herlaai skakel'
SURNAME: Surname
TIMEFORMAT: 'Time format'
VALIDATIONMEMBEREXISTS: 'A member already exists with the same %s'
SURNAME: Van
TIMEFORMAT: 'Tyd formaat'
VALIDATIONMEMBEREXISTS: '''n Ander lid bestaan al klaar met dieselfde %s'
ValidationIdentifierFailed: 'Can''t overwrite existing member #{id} with identical identifier ({name} = {value}))'
WELCOMEBACK: 'Welcome Back, {firstname}'
WELCOMEBACK: 'Welkom terug, {firstname}'
YOUROLDPASSWORD: 'U ou wagwoord'
belongs_many_many_Groups: Groepe
db_LastVisited: 'Last Visited Date'
db_LastVisited: 'Laaste datum besoek'
db_Locale: 'Interface Locale'
db_LockedOutUntil: 'Utgesluit tot en met'
db_NumVisit: 'Number of Visits'
db_Password: Password
db_NumVisit: 'Aantal besoeke'
db_Password: Wagwoord
db_PasswordExpiry: 'Wagwoord Vervaldatum'
MemberAuthenticator:
TITLE: 'E-mail &amp; Password'
TITLE: 'Epos &amp; Wagwoord'
MemberDatetimeOptionsetField:
AMORPM: 'AM (Ante meridiem) or PM (Post meridiem)'
'APPLY FILTER': 'Apply Filter'
Custom: Custom
DATEFORMATBAD: 'Date format is invalid'
DAYNOLEADING: 'Day of month without leading zero'
AMORPM: 'AM (Oggend) of PM (Middag)'
'APPLY FILTER': 'Wend filter aan'
Custom: 'Maak pas'
DATEFORMATBAD: 'Die datum formaat is ongeldig'
DAYNOLEADING: 'Dag van die maand sonder ''n zero vooraan'
DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second'
FOURDIGITYEAR: 'Four-digit year'
FULLNAMEMONTH: 'Full name of month (e.g. June)'
FOURDIGITYEAR: 'View syfer jaar'
FULLNAMEMONTH: 'Volle naam van maand (bv Junie)'
HOURNOLEADING: 'Hour without leading zero'
MINUTENOLEADING: 'Minute without leading zero'
MONTHNOLEADING: 'Month digit without leading zero'
Preview: Preview
Preview: Voorskou
SHORTMONTH: 'Short name of month (e.g. Jun)'
TOGGLEHELP: 'Toggle formatting help'
TWODIGITDAY: 'Two-digit day of month'
TOGGLEHELP: 'Skakel formateringshelp aan'
TWODIGITDAY: 'Twee syfer dag van die maand'
TWODIGITHOUR: 'Two digits of hour (00 through 23)'
TWODIGITMINUTE: 'Two digits of minute (00 through 59)'
TWODIGITMONTH: 'Two-digit month (01=January, etc.)'
TWODIGITSECOND: 'Two digits of second (00 through 59)'
TWODIGITYEAR: 'Two-digit year'
TWODIGITYEAR: 'Twee syfer jaar'
MemberImportForm:
Help1: '<p>Import users in <em>CSV format</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
Help2: '<div class="advanced"> <h4>Advanced usage</h4> <ul> <li>Allowed columns: <em>%s</em></li> <li>Existing users are matched by their unique <em>Code</em> property, and updated with any new values from the imported file.</li> <li>Groups can be assigned by the <em>Groups</em> column. Groups are identified by their <em>Code</em> property, multiple groups can be separated by comma. Existing group memberships are not cleared.</li> </ul></div>'
@ -415,58 +415,58 @@ af:
ResultNone: 'Geen veranderinge'
ResultUpdated: 'Updated {count} members'
MemberPassword:
PLURALNAME: 'Member Passwords'
SINGULARNAME: 'Member Password'
PLURALNAME: 'Lid Wagwoorde'
SINGULARNAME: 'Lid Wagwoord'
MemberTableField: null
ModelAdmin:
DELETE: Delete
DELETEDRECORDS: 'Deleted {count} records.'
EMPTYBEFOREIMPORT: 'Clear Database before import'
IMPORT: 'Import from CSV'
DELETE: Verwyder
DELETEDRECORDS: 'Verwyder {count} rekords'
EMPTYBEFOREIMPORT: 'Maak databasis skoon voordat data ingevoer word'
IMPORT: 'Voer in van CSV'
IMPORTEDRECORDS: 'Imported {count} records.'
NOCSVFILE: 'Please browse for a CSV file to import'
NOIMPORT: 'Nothing to import'
RESET: Reset
Title: 'Data Models'
NOIMPORT: 'Niks om in te voer nie'
RESET: Herstel
Title: 'Data modelle'
UPDATEDRECORDS: 'Updated {count} records.'
ModelAdmin_ImportSpec.ss:
IMPORTSPECFIELDS: 'Database columns'
IMPORTSPECFIELDS: 'Databasis kolomme'
IMPORTSPECLINK: 'Show Specification for %s'
IMPORTSPECRELATIONS: Relations
IMPORTSPECRELATIONS: Verhoudings
IMPORTSPECTITLE: 'Specification for %s'
ModelAdmin_Tools.ss:
FILTER: Filter
IMPORT: Import
IMPORT: 'Voer in'
ModelSidebar.ss:
IMPORT_TAB_HEADER: Import
SEARCHLISTINGS: Search
IMPORT_TAB_HEADER: 'Voer in'
SEARCHLISTINGS: Soek
MoneyField:
FIELDLABELAMOUNT: Amount
FIELDLABELCURRENCY: Currency
FIELDLABELAMOUNT: Bedrag
FIELDLABELCURRENCY: 'Geld eenheid'
NullableField:
IsNullLabel: 'Is Null'
NumericField:
VALIDATION: '''{value}'' is not a number, only numbers can be accepted for this field'
Pagination:
Page: Page
View: View
Page: Bladsy
View: Wys
Permission:
AdminGroup: Administrateur
CMS_ACCESS_CATEGORY: 'IBS Toegang'
FULLADMINRIGHTS: 'Full administrative rights'
FULLADMINRIGHTS: 'Volle administratiewe regte '
FULLADMINRIGHTS_HELP: 'Impliseer en oorskryf alle ander toegekende permissies.'
PLURALNAME: Permissions
SINGULARNAME: Permission
PLURALNAME: Toestemmings
SINGULARNAME: Toestemming
PermissionCheckboxSetField:
AssignedTo: 'assigned to "{title}"'
FromGroup: 'inherited from group "{title}"'
FromRole: 'inherited from role "{title}"'
FromRoleOnGroup: 'oorgeërf van rol "%s" op groep "%s"'
PermissionRole:
OnlyAdminCanApply: 'Only admin can apply'
PLURALNAME: Roles
SINGULARNAME: Role
Title: Title
OnlyAdminCanApply: 'Slegs administrateur daarvoor aansoek doen'
PLURALNAME: Rolle
SINGULARNAME: Rol
Title: Tietel
PermissionRoleCode:
PLURALNAME: 'Permission Role Cods'
SINGULARNAME: 'Permission Role Code'
@ -474,103 +474,103 @@ af:
PERMISSIONS_CATEGORY: 'Rolle en toegang permissies'
UserPermissionsIntro: 'Assigning groups to this user will adjust the permissions they have. See the groups section for details of permissions on individual groups.'
PhoneNumberField:
VALIDATION: 'Please enter a valid phone number'
VALIDATION: 'Tik asseblief ''n geldige telefoon nommer in'
RelationComplexTableField.ss:
ADD: 'Voeg by'
CSVEXPORT: 'Export to CSV'
NOTFOUND: 'No items found'
CSVEXPORT: 'Voer uit na CSV'
NOTFOUND: 'Geen items gevind nie'
Security:
ALREADYLOGGEDIN: 'U het nie toegang tot hierdie bladsy nie. As u n'' ander rekening het wat toegang tot hierdie bladsy het, kan u weer <a href="%s">inteken</a>.'
BUTTONSEND: 'Send me the password reset link'
CHANGEPASSWORDBELOW: 'You can change your password below.'
CHANGEPASSWORDHEADER: 'Change your password'
ENTERNEWPASSWORD: 'Please enter a new password.'
ERRORPASSWORDPERMISSION: 'You must be logged in in order to change your password!'
LOGGEDOUT: 'You have been logged out. If you would like to log in again, enter your credentials below.'
LOGIN: 'Log in'
NOTEPAGESECURED: 'That page is secured. Enter your credentials below and we will send you right along.'
BUTTONSEND: 'Stuur vir my die wagwoord herstel skakel'
CHANGEPASSWORDBELOW: 'Jy kan jou wagwoord onder verander'
CHANGEPASSWORDHEADER: 'Verander jou wagwoord'
ENTERNEWPASSWORD: 'Sleutel asseblief ''n nuwe wagwoord in'
ERRORPASSWORDPERMISSION: 'Jy moet ingeteken wees om jou wagwoord te verander'
LOGGEDOUT: 'Jy is uit uitgeteken. As jy weer wil inteken, moet jy ''n gebruikersnaam en wagwoord onder in tik'
LOGIN: 'Teken in'
NOTEPAGESECURED: 'Daai bladsy is beveilig. Sleutel jou informasie onder in sodat ons jou op jou pad kan stuur'
NOTERESETLINKINVALID: '<p>The password reset link is invalid or expired.</p><p>You can request a new one <a href="{link1}">here</a> or change your password after you <a href="{link2}">logged in</a>.</p>'
NOTERESETPASSWORD: 'Enter your e-mail address and we will send you a link with which you can reset your password'
NOTERESETPASSWORD: 'Sleutel you epos adres in sodat ons vir jou ''n herstel skakel kan epos'
PASSWORDSENTHEADER: 'Password reset link sent to ''{email}'''
PASSWORDSENTTEXT: 'Thank you! A reset link has been sent to ''{email}'', provided an account exists for this email address.'
SecurityAdmin:
ACCESS_HELP: 'Laat toe wys, byvoeging en verandering van gebruikers, so wel as die toekenning van permissies en rolle aan hulle.'
APPLY_ROLES: 'Wend rolle tot groepe toe'
APPLY_ROLES_HELP: 'Vermoë om rolle toegeken aan ''n groep te verander. Benodig die "Toegang tot ''Sekuriteit'' afdeling'' permissie.'
EDITPERMISSIONS: 'Manage permissions for groups'
EDITPERMISSIONS: 'Bestuur toegangsregte vir groepe'
EDITPERMISSIONS_HELP: 'Vermoë om Permissies en IP Adresse vir ''n groep te verander. Benodig die "Toegang tot ''Sekuriteit'' afdeling" permissie.'
GROUPNAME: 'Group name'
IMPORTGROUPS: 'Import groups'
IMPORTUSERS: 'Import users'
MEMBERS: Members
MENUTITLE: Security
GROUPNAME: 'Groep naam'
IMPORTGROUPS: 'Voer groepe in'
IMPORTUSERS: 'Belangrike gebruikers'
MEMBERS: Lede
MENUTITLE: Sekuriteit
MemberListCaution: 'Waarskuwing: Deur lede te verwyder van hierdie lys sal hulle ook van alle groepe en die databasis verwyder'
NEWGROUP: 'New Group'
PERMISSIONS: Permissions
NEWGROUP: 'Nuwe groep'
PERMISSIONS: Toegangsregte
ROLES: Rolle
ROLESDESCRIPTION: ' '
TABROLES: Rolle
Users: Users
Users: Gebruikers
SecurityAdmin_MemberImportForm:
BtnImport: 'Voer In'
FileFieldLabel: 'CSV Lêer <small>(Laat toe uitbreidings: *.csv)</small>'
SilverStripeNavigator:
Edit: Edit
Edit: Verander
SimpleImageField:
NOUPLOAD: 'No Image Uploaded'
NOUPLOAD: 'Geen foto gelaai nie'
SiteTree:
TABMAIN: Hoof
TableField:
ISREQUIRED: 'In %s ''%s'' is required'
ISREQUIRED: 'In %s ''%s'' word benodig.'
TableField.ss:
ADD: 'Voeg nuwe ry by'
ADDITEM: 'Add %s'
ADDITEM: 'Voeg %s by'
TableListField:
CSVEXPORT: 'Export to CSV'
CSVEXPORT: 'Voer uit na CSV lêer'
PRINT: Druk
Print: Print
SELECT: 'Select:'
Print: Druk
SELECT: Kies
TableListField.ss:
NOITEMSFOUND: 'No items found'
NOITEMSFOUND: 'Geen item gevind nie'
SORTASC: 'Sorteer in stygende orde'
SORTDESC: 'Sorteer in dalende orde'
TableListField_PageControls.ss:
DISPLAYING: Displaying
OF: of
TO: to
VIEWFIRST: 'View first'
VIEWLAST: 'View last'
VIEWNEXT: 'View next'
VIEWPREVIOUS: 'View previous'
DISPLAYING: 'Wys huidiglik'
OF: van
TO: na
VIEWFIRST: 'Wys eerste'
VIEWLAST: 'Wys laaste'
VIEWNEXT: 'Wys volgende'
VIEWPREVIOUS: 'Wys vorige'
TimeField:
VALIDATEFORMAT: 'Please enter a valid time format ({format})'
VALIDATEFORMAT: 'Sleutel asseblief ''n geldige tyd formaat ({format})'
ToggleField:
LESS: less
MORE: more
LESS: minder
MORE: meer
UploadField:
ATTACHFILE: 'Attach a file'
ATTACHFILES: 'Attach files'
AttachFile: 'Attach file(s)'
DELETE: 'Delete from files'
DELETEINFO: 'Permanently delete this file from the file store'
DOEDIT: Save
DROPFILE: 'drop a file'
DROPFILES: 'drop files'
Dimensions: Dimensions
EDIT: Edit
EDITINFO: 'Edit this file'
FIELDNOTSET: 'File information not found'
FROMCOMPUTER: 'From your computer'
FROMCOMPUTERINFO: 'Select from files'
FROMFILES: 'From files'
ATTACHFILE: 'Heg lêer aan'
ATTACHFILES: 'Heg lêer(s) aan'
AttachFile: 'Aangehegde lêer(s)'
DELETE: 'Verwyder van lêers af'
DELETEINFO: 'Wis die lêer uit die lêer stoor uit'
DOEDIT: Stoor
DROPFILE: 'Laat val ''n lêer'
DROPFILES: 'Skuif lêers hiernatoe'
Dimensions: Afmetings
EDIT: Verander
EDITINFO: 'Verander die lêer'
FIELDNOTSET: 'Die lêer informasie kan nie gevind word nie'
FROMCOMPUTER: 'Van jou rekenaar'
FROMCOMPUTERINFO: 'Kied uit lêers uit'
FROMFILES: 'Van die lêers afdeling'
HOTLINKINFO: 'Info: This image will be hotlinked. Please ensure you have permissions from the original site creator to do so.'
MAXNUMBEROFFILES: 'Max number of {count} file(s) exceeded.'
MAXNUMBEROFFILESSHORT: 'Can only upload {count} files'
REMOVE: Remove
REMOVEERROR: 'Error removing file'
REMOVEINFO: 'Remove this file from here, but do not delete it from the file store'
STARTALL: 'Start all'
STARTALLINFO: 'Start all uploads'
Saved: Saved
MAXNUMBEROFFILES: 'Maksimum aantal van {count} lêer(s) oorskry '
MAXNUMBEROFFILESSHORT: 'Kan net {count} lêer oplaai'
REMOVE: Verwyder
REMOVEERROR: 'Daar het ''n fout onstaan met die verwydering van die lêer'
REMOVEINFO: 'Verwyder die lêer van hier af maar moet dit nie uit die lêer stoor verwyder nie'
STARTALL: 'Begin alles'
STARTALLINFO: 'Begin op alles op te laai'
Saved: Gestoor
Versioned:
has_many_Versions: Weergawe

View File

@ -480,7 +480,7 @@ ast:
CSVEXPORT: 'Export to CSV'
NOTFOUND: 'No items found'
Security:
ALREADYLOGGEDIN: 'Nun tienes accesu a esta páxina. Si tienes otra cuenta que pueda entrar nesta páxina, puedes <a href="%s">volver conectate</a>.'
ALREADYLOGGEDIN: 'Nun tienes accesu a esta páxina. Si tienes otra cuenta que pueda entrar nesta páxina, pues <a href="%s">volver a coneutate</a>.'
BUTTONSEND: 'Send me the password reset link'
CHANGEPASSWORDBELOW: 'You can change your password below.'
CHANGEPASSWORDHEADER: 'Change your password'

View File

@ -1,7 +1,7 @@
bg:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
NEWFOLDER: NewFolder
NEWFOLDER: НоваПапка
AssetTableField:
CREATED: Създаден
DIM: Размери
@ -10,18 +10,18 @@ bg:
LASTEDIT: 'Последна промяна'
OWNER: Собственик
SIZE: 'Големина на файла'
TITLE: Title
TITLE: Заглавие
TYPE: 'Тип на файла'
URL: URL
AssetUploadField:
ChooseFiles: 'Избери файлове'
DRAGFILESHERE: 'Завлечете файловете тук'
DROPAREA: 'Drop Area'
DROPAREA: 'Зона за пускане'
EDITALL: 'Редакция на всички'
EDITANDORGANIZE: 'Редактиране и подреждане'
EDITINFO: 'Edit files'
FILES: Файлове
FROMCOMPUTER: 'Choose files from your computer'
FROMCOMPUTER: 'Избери файлове от компютъра'
FROMCOMPUTERINFO: 'Upload from your computer'
TOTAL: Общо
TOUPLOAD: 'Choose files to upload...'
@ -66,12 +66,12 @@ bg:
LOADING: 'Зареждане ...'
REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
CMSMain:
ACCESS: 'Access to ''{title}'' section'
ACCESS: 'Достъп до секция ''{title}'''
ACCESSALLINTERFACES: 'Достъп до всички секции на CMS'
ACCESSALLINTERFACESHELP: 'Overrules more specific access settings.'
SAVE: Запис
CMSProfileController:
MENUTITLE: 'My Profile'
MENUTITLE: 'Моят профил'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Вие сменихте вашата парола за'
CHANGEPASSWORDTEXT2: 'Вече можете да ползвате следните данни за вход:'
@ -79,10 +79,10 @@ bg:
HELLO: Здравей!
PASSWORD: Парола
CheckboxField:
- 'False'
- 'True'
- 'не е чекнато'
- чекнато
ComplexTableField:
CLOSEPOPUP: 'Close Popup'
CLOSEPOPUP: 'Затвори прозореца'
SUCCESSADD2: 'Беше добавен {name}'
SUCCESSEDIT: 'Съхранено %s %s %s'
ComplexTableField.ss:
@ -94,9 +94,9 @@ bg:
NEXT: Следващо
PREVIOUS: Предишно
ConfirmedPasswordField:
ATLEAST: 'Passwords must be at least {min} characters long.'
BETWEEN: 'Passwords must be {min} to {max} characters long.'
MAXIMUM: 'Passwords must be at most {max} characters long.'
ATLEAST: 'Паролата трябва да е дълга мин. {min} символа.'
BETWEEN: 'Паролата трябва да е дълга от {min} до {max} символа.'
MAXIMUM: 'Паролата трябва да е дълга макс. {max} символа.'
SHOWONCLICKTITLE: 'Промяна на парола'
CreditCardField:
FIRST: първи
@ -138,10 +138,10 @@ bg:
EmailField:
VALIDATION: 'Моля, въведете имейл адрес'
Email_BounceRecord:
PLURALNAME: 'Email Bounce Records'
SINGULARNAME: 'Email Bounce Record'
PLURALNAME: 'Изпращане на отпадналите записи'
SINGULARNAME: 'Изпращане на отпаднал запис'
Enum:
ANY: Any
ANY: Някой
File:
AviType: 'AVI video file'
Content: Съдържание
@ -153,38 +153,38 @@ bg:
GzType: 'GZIP compressed file'
HtlType: 'HTML file'
HtmlType: 'HTML file'
INVALIDEXTENSION: 'Extension is not allowed (valid: {extensions})'
INVALIDEXTENSIONSHORT: 'Extension is not allowed'
INVALIDEXTENSION: 'Това разширение не е разрешено (разрешени са: {extensions})'
INVALIDEXTENSIONSHORT: 'Това разширение не е разрешено'
IcoType: 'Icon image'
JpgType: 'JPEG image - good for photos'
JsType: 'Javascript file'
Mp3Type: 'MP3 audio file'
MpgType: 'MPEG video file'
NOFILESIZE: 'Размер на файла е нула байта.'
NOVALIDUPLOAD: 'File is not a valid upload'
NOVALIDUPLOAD: 'Невалиден файл за качване'
Name: Име
PLURALNAME: Файлове
PdfType: 'Adobe Acrobat PDF file'
PngType: 'PNG image - good general-purpose format'
SINGULARNAME: Файл
TOOLARGE: 'Filesize is too large, maximum {size} allowed'
TOOLARGESHORT: 'Filesize exceeds {size}'
TOOLARGE: 'Много голям файл, разрешено е до {size}'
TOOLARGESHORT: 'Големината на файла надхвърля {size}'
TiffType: 'Tagged image format'
Title: Title
Title: Заглавие
WavType: 'WAV audo file'
XlsType: 'Excel spreadsheet'
ZipType: 'ZIP compressed file'
FileIFrameField:
ATTACH: 'Attach {type}'
ATTACHONCESAVED: '{type}s can be attached once you have saved the record for the first time.'
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
DELETE: 'Delete {type}'
ATTACH: 'Прикачи {type}'
ATTACHONCESAVED: '{type} може да бъде прикачен след като записът се съхрани за първи път.'
ATTACHONCESAVED2: 'Файлове могат да бъдат прикачвани след като записът се съхрани за първи път.'
DELETE: 'Изтрий {type}'
DISALLOWEDFILETYPE: 'Не може да бъде качен файл от този тип'
FILE: Файл
FROMCOMPUTER: 'От компютъра'
FROMFILESTORE: 'От Файлове и Изображения'
NOSOURCE: 'Please select a source file to attach'
REPLACE: 'Replace {type}'
NOSOURCE: 'Избери файл за прикачване'
REPLACE: 'Замести {type}'
FileIFrameField_iframe.ss:
TITLE: 'Iframe за качване на изображение'
Filesystem:
@ -204,30 +204,30 @@ bg:
VALIDATIONNOTUNIQUE: 'Въведената стойност не е уникална'
VALIDATIONPASSWORDSDONTMATCH: 'Паролите не съвпадат'
VALIDATIONPASSWORDSNOTEMPTY: 'Паролите не може да бъдат празни'
VALIDATIONSTRONGPASSWORD: 'Passwords must have at least one digit and one alphanumeric character'
VALIDATIONSTRONGPASSWORD: 'Паролите трябва да съдържат поне една цифра и една буква.'
VALIDATOR: Валидатор
VALIDCURRENCY: 'Please enter a valid currency'
VALIDCURRENCY: 'Моля, въведете коректна валута.'
FormField:
NONE: нищо
NONE: никой
GridAction:
DELETE_DESCRIPTION: Delete
Delete: Delete
DELETE_DESCRIPTION: Изтриване
Delete: Изтрий
UnlinkRelation: Откачане
GridField:
Add: 'Добави {name}'
Filter: Филтър
FilterBy: 'Филтриране по'
Find: Find
Find: Търси
LEVELUP: 'Ниво нагоре'
LinkExisting: 'Link Existing'
LinkExisting: 'Свържи към съществуващ'
NewRecord: 'Нов %s'
NoItemsFound: 'Няма намерени елементи'
PRINTEDAT: 'Printed at'
PRINTEDBY: 'Printed by'
PRINTEDAT: 'Отпечатано на'
PRINTEDBY: 'Отпечатано от'
PlaceHolder: 'Намери {type}'
PlaceHolderWithLabels: 'Намери {type} по {name}'
RelationSearch: 'Relation search'
ResetFilter: Reset
RelationSearch: 'Търсене на връзка'
ResetFilter: Изчистване
GridFieldAction_Delete:
DeletePermissionsFailure: 'Изтриването не е разрешено'
GridFieldDetailForm:
@ -241,26 +241,26 @@ bg:
GridFieldItemEditView.ss: null
Group:
AddRole: 'Добавяне на роля към групата'
Code: 'Group Code'
Code: 'Код на група'
DefaultGroupTitleAdministrators: Администратори
DefaultGroupTitleContentAuthors: 'Редактори на съдържание'
Description: Описание
GroupReminder: 'If you choose a parent group, this group will take all it''s roles'
GroupReminder: 'Ако изберете родителска група, тази група ще наследи всички нейни роли'
Locked: 'Заключена?'
NoRoles: 'Няма намерени роли'
PLURALNAME: Groups
Parent: 'Parent Group'
Parent: 'Група източник'
RolesAddEditLink: 'Управление на ролите'
SINGULARNAME: Group
Sort: Сортиране
has_many_Permissions: Разрешения
many_many_Members: Потребители
many_many_Members: Членове
GroupImportForm:
Help1: '<p>Import one or more groups in <em>CSV</em> format (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
Help1: '<p>Внасяне на една или повече групи в <em>CSV формат</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Покажи начин на употреба</a></small></p>'
Help2: '<div class="advanced"> <h4>Advanced usage</h4> <ul> <li>Allowed columns: <em>%s</em></li> <li>Existing groups are matched by their unique <em>Code</em> value, and updated with any new values from the imported file</li> <li>Group hierarchies can be created by using a <em>ParentCode</em> column.</li> <li>Permission codes can be assigned by the <em>PermissionCode</em> column. Existing permission codes are not cleared.</li> </ul></div>'
ResultCreated: 'Created {count} groups'
ResultDeleted: 'Deleted %d groups'
ResultUpdated: 'Updated %d groups'
ResultCreated: 'Бяха създадени {count} група/и'
ResultDeleted: 'Бяха изтрити %d групи'
ResultUpdated: 'Бяха обновени %d групи'
Hierarchy:
InfiniteLoopNotAllowed: 'Infinite loop found within the "{type}" hierarchy. Please change the parent to resolve this'
HtmlEditorField:
@ -277,7 +277,7 @@ bg:
CSSCLASSLEFT: 'В ляво, с текст който да се нанася около него'
CSSCLASSLEFTALONE: 'В ляво, самостоятелно.'
CSSCLASSRIGHT: 'В дясно, с текст който да се нанася около него'
DETAILS: Details
DETAILS: Детайли
EMAIL: 'email адрес'
FILE: Файл
FOLDER: Папка
@ -287,7 +287,7 @@ bg:
FindInFolder: 'Прегледай папка'
IMAGEALT: 'Алтернативен текст (alt)'
IMAGEALTTEXT: 'Алтернативен текст (alt) - показва се ако изображението не е заредено'
IMAGEALTTEXTDESC: 'Shown to screen readers or if image can not be displayed'
IMAGEALTTEXTDESC: 'Вижда се на екранните четци или ако картинката не може да бъде показана'
IMAGEDIMENSIONS: Размери
IMAGEHEIGHTPX: Височина
IMAGETITLE: 'Описание (tooltip) - за допълнителна информация към изображението'
@ -306,48 +306,48 @@ bg:
LINKTO: 'Препратка към'
PAGE: Страница
URL: URL
URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
URLNOTANOEMBEDRESOURCE: 'URL адресът ''{url}'' не може да бъде превърнат в медиен ресурс.'
UpdateMEDIA: 'Актуализация на медиа'
Image:
PLURALNAME: Files
SINGULARNAME: File
ImageField:
IMAGE: Image
IMAGE: Изображение
Image_Cached:
PLURALNAME: Files
SINGULARNAME: File
Image_iframe.ss:
TITLE: 'Iframe за качване на изображение'
TITLE: 'Качване на изображението Iрамка'
LeftAndMain:
CANT_REORGANISE: 'You do not have permission to alter Top level pages. Your change was not saved.'
CANT_REORGANISE: 'Нямаш права да променяш страници от най-горно ниво. Твоите промени не бяха записани.'
DELETED: Изтрит
DropdownBatchActionsDefault: Действия
HELP: Help
PAGETYPE: 'Page type: '
HELP: Помощ
PAGETYPE: 'Тип на страницата'
PERMAGAIN: 'Вие излязохте от CMS. Ако искате да влезете отново, моля, въведете потребителско име и парола.'
PERMALREADY: 'Съжалявам, но нямате достъп до тази част от CMS. Ако искате да влезете с друго потребителско име, моля, направете го по-долу'
PERMDEFAULT: 'Въведете имейл адреса и паролата си, за да влезете в CMS.'
PLEASESAVE: 'Please Save Page: This page could not be upated because it hasn''t been saved yet.'
PLEASESAVE: 'Съхрани страницата: Тази страница не може да бъде обновена, защото още не е записана.'
PreviewButton: Преглед
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
REORGANISATIONSUCCESSFUL: 'Реорганизацията на дървото на сайта беше успешна.'
SAVEDUP: Записано
VersionUnknown: непозната
LeftAndMain_Menu.ss:
Hello: Здравей
LOGOUT: 'Log out'
LOGOUT: Излизане
LoginAttempt:
Email: 'Email Address'
Email: 'Email адрес'
IP: 'IP адрес'
PLURALNAME: 'Login Attempts'
SINGULARNAME: 'Login Attempt'
Status: Status
Status: Статус
Member:
ADDGROUP: 'Добави група'
BUTTONCHANGEPASSWORD: 'Променете паролата'
BUTTONLOGIN: Влез
BUTTONLOGINOTHER: 'Влез като някой друг'
BUTTONLOSTPASSWORD: 'Загубих си паролата'
CANTEDIT: 'You don''t have permission to do that'
CANTEDIT: 'Нямаш права за това действие'
CONFIRMNEWPASSWORD: 'Потвърдете новата парола'
CONFIRMPASSWORD: 'Потвърдете паролата'
DATEFORMAT: 'Date format'
@ -355,14 +355,14 @@ bg:
DefaultDateTime: 'по подразбиране'
EMAIL: Еmail
EMPTYNEWPASSWORD: 'Не е въведена нова парола'
ENTEREMAIL: 'Връзка за анулиране на парола'
ENTEREMAIL: 'Въведете email, на който ще изпратим връзка за анулиране на парола.'
ERRORLOCKEDOUT: 'Вашата сметка бе изключена временно защото имаше много неуспешни опити за влизане. Моля опитайте отново след 20 минути.'
ERRORNEWPASSWORD: 'Въвели сте новата парола различно, моля опитайте пак'
ERRORPASSWORDNOTMATCH: 'Вашата текуща парола не съвпада, моля опитайте пак'
ERRORWRONGCRED: 'Това не изглежда да е правилен email адрес или парола. Моля опитайте отново.'
FIRSTNAME: Име
INTERFACELANG: Език
INVALIDNEWPASSWORD: 'We couldn''t accept that password: {password}'
INVALIDNEWPASSWORD: 'Не може да бъде приета паролата: {password}'
LOGGEDINAS: 'Вие сте влезли като {name}.'
NEWPASSWORD: 'Нова парола'
PASSWORD: Парола
@ -389,10 +389,10 @@ bg:
MemberDatetimeOptionsetField:
AMORPM: 'АМ (преди обед) или РМ (следобед)'
'APPLY FILTER': 'Приложи филтър'
Custom: Custom
Custom: Произволно
DATEFORMATBAD: 'Невалиден формат на датата'
DAYNOLEADING: 'Ден от месеца без водеща нула'
DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second'
DIGITSDECFRACTIONSECOND: 'Една или повече цифри, представляващи десетичната част на секундата'
FOURDIGITYEAR: 'Четирицифрена година'
FULLNAMEMONTH: 'Пълно наименование на месец (напр. Януари)'
HOURNOLEADING: 'Час без водеща нула'
@ -408,11 +408,11 @@ bg:
TWODIGITSECOND: 'Секунди с водеща нула (00 до 59)'
TWODIGITYEAR: 'Двуцифрена година'
MemberImportForm:
Help1: '<p>Import users in <em>CSV format</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Show advanced usage</a></small></p>'
Help1: '<p>Внасяне на потебители в <em>CSV формат</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Покажи начин на употреба</a></small></p>'
Help2: '<div class="advanced"> <h4>Advanced usage</h4> <ul> <li>Allowed columns: <em>%s</em></li> <li>Existing users are matched by their unique <em>Code</em> property, and updated with any new values from the imported file.</li> <li>Groups can be assigned by the <em>Groups</em> column. Groups are identified by their <em>Code</em> property, multiple groups can be separated by comma. Existing group memberships are not cleared.</li> </ul></div>'
ResultCreated: 'Бяха добавени {count} потребители'
ResultDeleted: 'Deleted %d members'
ResultNone: 'No changes'
ResultDeleted: 'Бяха изтрити %d членове'
ResultNone: 'Нямаше промени'
ResultUpdated: 'Бяха актуализирани {count} потребители'
MemberPassword:
PLURALNAME: 'Member Passwords'
@ -420,15 +420,15 @@ bg:
MemberTableField: null
ModelAdmin:
DELETE: Изтрий
DELETEDRECORDS: 'Deleted {count} records.'
DELETEDRECORDS: 'Бяха изтрити {count} записа.'
EMPTYBEFOREIMPORT: 'Clear Database before import'
IMPORT: 'Import from CSV'
IMPORTEDRECORDS: 'Imported {count} records.'
IMPORT: 'Внасяне от CSV'
IMPORTEDRECORDS: 'Бяха внесени {count} записа.'
NOCSVFILE: 'Преглед на CSV файл за внасяне'
NOIMPORT: 'Нищо за внасяне'
RESET: Reset
RESET: Нулиране
Title: 'Data Models'
UPDATEDRECORDS: 'Updated {count} records.'
UPDATEDRECORDS: 'Бяха обновени {count} записа.'
ModelAdmin_ImportSpec.ss:
IMPORTSPECFIELDS: 'Database columns'
IMPORTSPECLINK: 'Show Specification for %s'
@ -486,13 +486,13 @@ bg:
CHANGEPASSWORDHEADER: 'Сменете вашата парола'
ENTERNEWPASSWORD: 'Моля, въведете нова парола.'
ERRORPASSWORDPERMISSION: 'Трябва да сте влезли, за да можете да промените вашата парола!'
LOGGEDOUT: 'Вие излязохте. Ако искате да влезете отново, въведете вашите данни по-долу.'
LOGGEDOUT: 'Вие излязохте. Ако искате да влезнете отново, въведете вашите данни по-долу.'
LOGIN: 'Влезте в системата'
NOTEPAGESECURED: 'Тази страница е защитена. Въведете вашите данни по-долу, за да продължите.'
NOTERESETLINKINVALID: '<p>The password reset link is invalid or expired.</p><p>You can request a new one <a href="{link1}">here</a> or change your password after you <a href="{link2}">logged in</a>.</p>'
NOTERESETPASSWORD: 'Въведете вашият email адрес и ще ви изпратим линк, с който ще можете да смените паролата си'
PASSWORDSENTHEADER: 'Password reset link sent to ''{email}'''
PASSWORDSENTTEXT: 'Thank you! A reset link has been sent to ''{email}'', provided an account exists for this email address.'
NOTEPAGESECURED: 'Тази страница е защитена. Вкарайте вашите данни по-долу и ще ви препратим по-нататък.'
NOTERESETLINKINVALID: '<p>Връзката за нулиране на парола не е вярна или е просрочена.</p><p>Можете да заявите нова <a href="{link1}">тук</a> или да промените паролата си след като <a href="{link2}">влезете</a>.</p>'
NOTERESETPASSWORD: 'Въведете вашият email адрес и ще ви изпратим линк с който ще можете да смените паролата си'
PASSWORDSENTHEADER: 'Връзка за нулиране на парола беше изпратена на ''{email}'''
PASSWORDSENTTEXT: 'Благодарим ви! Връзка за нулиране на паролата беше изпратен на ''{email}'', ако съществува акаунт с този имейл адрес.'
SecurityAdmin:
ACCESS_HELP: 'Позволява преглед, добавяне и редактиране на потребители, както и задаване на разрешения и роли за тях.'
APPLY_ROLES: 'Задаване роли на групи'
@ -503,7 +503,7 @@ bg:
IMPORTGROUPS: 'Внасяне на файл с групи'
IMPORTUSERS: 'Внасяне на файл с потребители'
MEMBERS: Потребители
MENUTITLE: Security
MENUTITLE: Сигурност
MemberListCaution: 'Внимание: изтривайки потребители от този списък, ще ги премахне от всички групи и от базата данни.'
NEWGROUP: 'Нова група'
PERMISSIONS: Разрешения
@ -521,7 +521,7 @@ bg:
SiteTree:
TABMAIN: Главно
TableField:
ISREQUIRED: 'In %s ''%s'' is required'
ISREQUIRED: 'В %s е необходимо ''%s'''
TableField.ss:
ADD: 'Добави нов ред'
ADDITEM: 'Add %s'
@ -532,8 +532,8 @@ bg:
SELECT: 'Избери:'
TableListField.ss:
NOITEMSFOUND: 'No items found'
SORTASC: 'Sort in ascending order'
SORTDESC: 'Sort in descending order'
SORTASC: 'Сортирай възходящо'
SORTDESC: 'Сортирай низходящо'
TableListField_PageControls.ss:
DISPLAYING: Displaying
OF: of
@ -554,22 +554,22 @@ bg:
DELETE: 'Delete from files'
DELETEINFO: 'Изтрий файла от сървъра'
DOEDIT: Запис
DROPFILE: 'drop a file'
DROPFILES: 'drop files'
DROPFILE: 'пуснете файл'
DROPFILES: 'пускане на файлове'
Dimensions: Размери
EDIT: Edit
EDITINFO: 'Редактирай този файл'
FIELDNOTSET: 'File information not found'
FIELDNOTSET: 'Информация за файла не беше намерена'
FROMCOMPUTER: 'От компютъра'
FROMCOMPUTERINFO: 'Select from files'
FROMFILES: 'From files'
FROMFILES: 'От файлове'
HOTLINKINFO: 'Info: This image will be hotlinked. Please ensure you have permissions from the original site creator to do so.'
MAXNUMBEROFFILES: 'Максималния брой файлове ({count}) е надхвърлен.'
MAXNUMBEROFFILESSHORT: 'Максималният брой файлове за качване е {count}'
REMOVE: Премахни
REMOVEERROR: 'Грешка при премахване на файл'
REMOVEINFO: 'Премахни файла без да го изтриваш'
STARTALL: 'Start all'
STARTALL: 'Старт на всички'
STARTALLINFO: 'Start all uploads'
Saved: Записано
Versioned:

View File

@ -1,6 +1,6 @@
cs:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
ALLOWEDEXTS: 'Povolené extenze'
NEWFOLDER: 'Nová složka'
AssetTableField:
CREATED: 'Poprvé nahráno'
@ -119,7 +119,7 @@ cs:
MONTHS: měsíce
SEC: sekunda
SECS: sekundy
TIMEDIFFAGO: '{difference} před'
TIMEDIFFAGO: 'před {difference}'
TIMEDIFFIN: 'v {difference}'
YEAR: rok
YEARS: roky
@ -149,14 +149,14 @@ cs:
DmgType: 'Apple obraz disku'
DocType: 'Word dokument'
Filename: 'Jméno souboru'
GifType: 'GIF obrázke - vhodné pro diagramy'
GifType: 'GIF obrázek - vhodné pro diagramy'
GzType: 'GZIP komprimační soubor'
HtlType: 'HTML soubor'
HtmlType: 'HTML soubor'
INVALIDEXTENSION: 'Extenze není povolena (platné: {extensions})'
INVALIDEXTENSIONSHORT: 'Extenze není povolena'
IcoType: 'Icon obrázkek'
JpgType: 'JPEG obrázke - vhodné pro fotografie'
IcoType: 'Ikona obrázek'
JpgType: 'JPEG obrázek - vhodné pro fotografie'
JsType: 'Javascript soubor'
Mp3Type: 'MP3 audio soubor'
MpgType: 'MPEG video soubor'
@ -503,7 +503,7 @@ cs:
IMPORTGROUPS: 'Importovat skupiny'
IMPORTUSERS: 'Importovat uživaté'
MEMBERS: Členové
MENUTITLE: Bezbečnost
MENUTITLE: Bezpečnost
MemberListCaution: 'Varování: Odstranění členů z tohoto seznamu způsobí, že členové budou odtraněni ze všech skupin a databáze'
NEWGROUP: 'Nová skupina'
PERMISSIONS: Práva

View File

@ -7,7 +7,7 @@ de:
DIM: Dimensionen
FILENAME: Dateiname
FOLDER: Ordner
LASTEDIT: 'Letzte Änderung'
LASTEDIT: 'Letztmals geändert'
OWNER: Eigentümer
SIZE: Größe
TITLE: Titel
@ -73,7 +73,7 @@ de:
CMSProfileController:
MENUTITLE: 'Mein Profil'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Sie haben Ihr Passwort geändert für'
CHANGEPASSWORDTEXT1: 'Sie haben ihr Passwort geändert für'
CHANGEPASSWORDTEXT2: 'Sie können nun folgende Angaben benutzen um sich einzuloggen'
EMAIL: E-Mail
HELLO: Hi
@ -424,7 +424,7 @@ de:
EMPTYBEFOREIMPORT: 'Datenbank vor Import leeren'
IMPORT: 'CSV Import'
IMPORTEDRECORDS: '{count} Datensätze wurden importiert.'
NOCSVFILE: 'Wählen Sie eine CSV-Datei zum Importieren'
NOCSVFILE: 'Wählen sie eine CSV-Datei zum Importieren'
NOIMPORT: 'Kein Import notwendig.'
RESET: Zurücksetzen
Title: Datenmodelle
@ -526,7 +526,7 @@ de:
ADD: 'Eine neue Zeile hinzufügen'
ADDITEM: '%s hinzufügen'
TableListField:
CSVEXPORT: 'Als CSV-Datei exportieren'
CSVEXPORT: 'Exportieren zu CSV'
PRINT: drucken
Print: Drucken
SELECT: 'Auswählen:'

View File

@ -1,7 +1,7 @@
en:
AssetAdmin:
ADDFILES: 'Add files'
EditOrgMenu: 'Edit &amp; organize'
ALLOWEDEXTS: 'Allowed extensions'
SHOWALLOWEDEXTS: 'Show allowed extensions'
NEWFOLDER: NewFolder
AssetTableField:
CREATED: 'First uploaded'
@ -176,7 +176,7 @@ en:
TEXT2: 'password reset link'
TEXT3: for
Form:
FIELDISREQUIRED: '%s is required'
FIELDISREQUIRED: '{name} is required'
SubmitBtnLabel: Go
VALIDATIONCREDITNUMBER: 'Please ensure you have entered the {number} credit card number correctly'
VALIDATIONNOTUNIQUE: 'The value entered is not unique'
@ -215,7 +215,7 @@ en:
DeletePermissionsFailure: 'No delete permissions'
Deleted: 'Deleted %s %s'
Save: Save
Saved: 'Saved %s %s'
Saved: 'Saved {name} {link}'
GridFieldEditButton.ss:
EDIT: Edit
GridFieldItemEditView.ss:

View File

@ -1,6 +1,6 @@
fi:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
ALLOWEDEXTS: 'Sallitut laajennukset'
NEWFOLDER: 'Uusi kansio'
AssetTableField:
CREATED: 'Ensimmäisen kerran ladattu palvelimelle'
@ -347,7 +347,7 @@ fi:
BUTTONLOGIN: 'Kirjaudu sisään'
BUTTONLOGINOTHER: 'Kirjaudu jonain muuna'
BUTTONLOSTPASSWORD: 'Kadotin salasanani'
CANTEDIT: 'You don''t have permission to do that'
CANTEDIT: 'Sinulla ei ole oikeuksia tähän toimintoon.'
CONFIRMNEWPASSWORD: 'Syötä uusi salasana uudelleen'
CONFIRMPASSWORD: 'Syötä salasana uudelleen'
DATEFORMAT: Päivämäärämuoto
@ -472,7 +472,7 @@ fi:
SINGULARNAME: 'Käyttöoikeiden roolin koodi'
Permissions:
PERMISSIONS_CATEGORY: 'Roolit ja käyttöoikeudet'
UserPermissionsIntro: 'Assigning groups to this user will adjust the permissions they have. See the groups section for details of permissions on individual groups.'
UserPermissionsIntro: 'Määriteltäessä käyttäjälle ryhmä, hänen käyttöoikeutensa mukautuvat ryhmälle tehtyjen asetusten mukaisesti. Katso tarkemmat ryhmäkohtaiset käyttöoikeusasetukset Ryhmät-välilehdeltä.'
PhoneNumberField:
VALIDATION: 'Kirjoita pätevä puhelinnumero'
RelationComplexTableField.ss:

View File

@ -74,7 +74,7 @@ fr:
MENUTITLE: 'Mon profil'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Vous avez modifié votre mot de passe pour'
CHANGEPASSWORDTEXT2: 'Vous pouvez maintenant utiliser les identifiants suivants pour vous connecter&nbsp;:'
CHANGEPASSWORDTEXT2: 'Vous pouvez maintenant utiliser les détails suivants pour vous connecter :'
EMAIL: Email
HELLO: Salut
PASSWORD: 'Mot de passe'
@ -253,7 +253,7 @@ fr:
RolesAddEditLink: 'Ajouter/éditer les rôles'
SINGULARNAME: Groupe
Sort: 'Ordre de tri'
has_many_Permissions: Autorisations
has_many_Permissions: Permissions
many_many_Members: Membres
GroupImportForm:
Help1: '<p>Importer un ou plusieurs groupe(s) au format <em>CSV</em> (comma-separated values). <small><a href="#" class="toggle-advanced">Montrer l''usage avancé</a></small></p>'
@ -323,11 +323,11 @@ fr:
DELETED: Supprimé.
DropdownBatchActionsDefault: Actions
HELP: Aide
PAGETYPE: 'Type de page&nbsp;:'
PAGETYPE: 'Type de page :'
PERMAGAIN: 'Vous avez été déconnecté du CMS. Si vous voulez vous reconnecter, entrez un nom d''utilisateur et un mot de passe ci-dessous.'
PERMALREADY: 'Désolé, mais vous ne pouvez pas accéder à cette partie du CMS. Si vous voulez changer d''identité, faites le ci-dessous'
PERMDEFAULT: 'Saisissez votre adresse de courriel et votre mot de passe pour accéder au CMS.'
PLEASESAVE: 'Enregistrez la page sil vous plaît&nbsp;: elle ne pouvait pas être mise à jour car elle navait pas encore été sauvegardée.'
PLEASESAVE: 'Enregistez la page s''il vous plaît : Cette page ne pouvait pas être actualisée, car elle n''a pas encore été enregistrée.'
PreviewButton: Aperçu
REORGANISATIONSUCCESSFUL: 'Larbre du site a été bien réorganisé.'
SAVEDUP: Enregistré.
@ -454,7 +454,7 @@ fr:
AdminGroup: Administrateur
CMS_ACCESS_CATEGORY: 'Accès au CMS'
FULLADMINRIGHTS: 'Droits d''administration complets'
FULLADMINRIGHTS_HELP: 'Implique et prévaut sur toutes les autres autorisations assignées.'
FULLADMINRIGHTS_HELP: 'Implique et écrase toute les autres permissions assignées.'
PLURALNAME: Autorisations
SINGULARNAME: Autorisation
PermissionCheckboxSetField:
@ -494,26 +494,26 @@ fr:
PASSWORDSENTHEADER: 'Lien de réinitialisation de mot de passe envoyé à « {email} »'
PASSWORDSENTTEXT: 'Merci ! Un lien de réinitialisation vient dêtre envoyé à « {email} », à condition que cette adresse existe.'
SecurityAdmin:
ACCESS_HELP: 'Permet de consulter, dajouter et déditer les utilisateurs, aussi bien que de leur assigner des autorisations et des rôles.'
ACCESS_HELP: 'Permettre la visualisation, l''addition et l''édition des utilisateurs, aussi bien que leur assigner des permissions et des rôles.'
APPLY_ROLES: 'Appliquer des rôles aux groupes'
APPLY_ROLES_HELP: 'Possibilité d''éditer les rôle assignés à un groupe. Nécessite lautorisation « Accès à la section “Utilisateurs” ».'
EDITPERMISSIONS: 'Gérer les autorisations des groupes'
EDITPERMISSIONS_HELP: 'Possibilité d''éditer les autorisations et les adresses IP pour un groupe. Nécessite lautorisation « Accès à la section “Securité” ».'
APPLY_ROLES_HELP: 'Possibilité d''éditer les rôles assignés à un groupe. Nécessite "Access to ''Security'' section".'
EDITPERMISSIONS: 'Gérer les permissions des groupes'
EDITPERMISSIONS_HELP: 'Possibilité d''éditer les permissions et les l''adresses IP pour un groupe. Nécessite "Access to ''Security'' section".'
GROUPNAME: 'Nom du group'
IMPORTGROUPS: 'Importer groupes'
IMPORTUSERS: 'Importer utilisateurs'
MEMBERS: Membres
MENUTITLE: Sécurité
MemberListCaution: 'Attention&nbsp;: en supprimant des membres de cette liste vous les enlèverez de tous les groupes ainsi que de la base de données'
NEWGROUP: 'Nouveau groupe'
PERMISSIONS: Autorisations
MemberListCaution: 'Attention : Enlever des membres de cette liste va les enlever de tous les groupes et de la base de donnée'
NEWGROUP: 'Nouveau Groupe'
PERMISSIONS: Permissions
ROLES: Rôles
ROLESDESCRIPTION: 'Les rôles sont des regroupements logiques dautorisations qui peuvent être assignés à des groupes.<br />Ils peuvent être hérités de groupes parents, si nécessaire.'
ROLESDESCRIPTION: 'Cette section vous permet d''ajouter des rôles à ce groupe. Les rôles sont des regroupements logiques d''autorisations, qui peuvent être modifiés dans l''onglet Rôles'
TABROLES: Rôles
Users: Utilisateurs
SecurityAdmin_MemberImportForm:
BtnImport: Importer
FileFieldLabel: 'Fichier CSV <small>(extension autorisée&nbsp;: *.csv)</small>'
FileFieldLabel: 'Fichier CSV <small>(Extension permise: *.csv)</small>'
SilverStripeNavigator:
Edit: 'Tout modifier'
SimpleImageField:
@ -529,7 +529,7 @@ fr:
CSVEXPORT: 'Exporter vers un fichier CSV'
PRINT: Imprimer
Print: Imprimer
SELECT: 'Sélectionner&nbsp;:'
SELECT: 'Sélectionner:'
TableListField.ss:
NOITEMSFOUND: 'Aucun élément na été trouvé'
SORTASC: 'Classer en ordre croissant'

View File

@ -3,15 +3,15 @@ it:
ALLOWEDEXTS: 'Allowed extensions'
NEWFOLDER: NuovaCartella
AssetTableField:
CREATED: 'Inizialmente caricato'
CREATED: 'Primo inserito'
DIM: Dimensioni
FILENAME: 'Nome del file'
FOLDER: Cartella
LASTEDIT: 'Ultima modifica'
LASTEDIT: 'Ultimo modificato'
OWNER: Proprietario
SIZE: Dimensione
TITLE: Titolo
TYPE: 'Tipo di file'
TYPE: Tipo
URL: URL
AssetUploadField:
ChooseFiles: 'Scegli file'
@ -67,14 +67,14 @@ it:
REQUIREJS: 'Il CMS richiede JavaScript abilitato.'
CMSMain:
ACCESS: 'Accesso alla sezione ''{title}'''
ACCESSALLINTERFACES: 'Accesso a tutte le sezioni del CMS'
ACCESSALLINTERFACES: 'Accesso a tutte le interfaccia CMS'
ACCESSALLINTERFACESHELP: 'Annulla le impostazioni di accesso più specifiche.'
SAVE: Salva
CMSProfileController:
MENUTITLE: 'Il mio Profilo'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Hai cambiato la password per'
CHANGEPASSWORDTEXT2: 'Ora puoi utilizzare le seguenti credenziali per accedere:'
CHANGEPASSWORDTEXT2: 'Puoi ora utilizzare le seguenti credenziali per accedere:'
EMAIL: Email
HELLO: Ciao
PASSWORD: Password
@ -86,10 +86,10 @@ it:
SUCCESSADD2: 'Aggiunto {name}'
SUCCESSEDIT: 'Salvato %s %s %s'
ComplexTableField.ss:
ADDITEM: 'Inserisci %s'
ADDITEM: 'Aggiungi %s'
NOITEMSFOUND: 'Nessun elemento trovato'
SORTASC: 'Ordina in modo ascendente'
SORTDESC: 'Ordina in modo discendente'
SORTASC: 'Ordina in modo crescente'
SORTDESC: 'Ordina in modo decrescente'
ComplexTableField_popup.ss:
NEXT: Prossimo
PREVIOUS: Precedente
@ -97,7 +97,7 @@ it:
ATLEAST: 'La password deve essere lunga almeno {min} caratteri.'
BETWEEN: 'La password deve essere lunga da {min} a {max} caratteri.'
MAXIMUM: 'La password deve essere lunga almeno {max} caratteri.'
SHOWONCLICKTITLE: 'Cambia password'
SHOWONCLICKTITLE: 'Cambia la password'
CreditCardField:
FIRST: primo
FOURTH: quarto
@ -106,8 +106,8 @@ it:
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
PLURALNAME: 'Data Objects'
SINGULARNAME: 'Data Object'
PLURALNAME: 'Oggetti dati'
SINGULARNAME: 'Oggetto dati'
Date:
DAY: giorno
DAYS: giorni
@ -250,10 +250,10 @@ it:
NoRoles: 'Nessun ruolo trovato'
PLURALNAME: Gruppi
Parent: 'Gruppo padre'
RolesAddEditLink: 'Gestisci ruoli'
RolesAddEditLink: 'Aggiungi/modifica ruoli'
SINGULARNAME: Gruppo
Sort: 'Tipo ordinamento'
has_many_Permissions: Permessi
has_many_Permissions: Autorizzazioni
many_many_Members: Membri
GroupImportForm:
Help1: '<p>Importa gruppi in formato <em>CSV</em> (valori separati da virgole). <small><a href="#" class="toggle-advanced">Mostra utilizzo avanzato</a></small></p>'
@ -345,7 +345,7 @@ it:
ADDGROUP: 'Aggiungi gruppo'
BUTTONCHANGEPASSWORD: 'Cambia password'
BUTTONLOGIN: Accedi
BUTTONLOGINOTHER: 'Autenticati come qualcun altro'
BUTTONLOGINOTHER: 'Autenticato come qualcun altro'
BUTTONLOSTPASSWORD: 'Ho perso la mia password'
CANTEDIT: 'You don''t have permission to do that'
CONFIRMNEWPASSWORD: 'Conferma nuova password'
@ -355,25 +355,25 @@ it:
DefaultDateTime: predefinito
EMAIL: Email
EMPTYNEWPASSWORD: 'La nuova password non può essere vuota, riprova'
ENTEREMAIL: 'Inserisci un indirizzo e-mail per ricevere il link di azzeramento della password'
ENTEREMAIL: 'Indica un indirizzo e-mail per ricevere il collegamento di azzeramento della password'
ERRORLOCKEDOUT: 'Il tuo account è stato temporaneamente disabilitato perchè ci sono stati troppi tentativi di accesso errati. Riprova tra 20 minuti.'
ERRORNEWPASSWORD: 'Hai inserito la tua nuova password in modo differente, prova di nuovo'
ERRORPASSWORDNOTMATCH: 'La tua password attuale non corrisponde, per favore prova ancora'
ERRORWRONGCRED: 'E-mail o password non sembrano essere corretti. Per favore, prova di nuovo.'
ERRORWRONGCRED: 'Non sembra esserci l''indirizzo e-mail corretto o la password. Per favore, prova di nuovo.'
FIRSTNAME: Nome
INTERFACELANG: 'Lingua dell''interfaccia'
INVALIDNEWPASSWORD: 'Non possiamo accettare questa password: {password}'
LOGGEDINAS: 'Sei collegato come {name}.'
NEWPASSWORD: 'Nuova password'
PASSWORD: Password
PLURALNAME: Utenti
PLURALNAME: Membri
REMEMBERME: 'Ricordati di me la prossima volta?'
SINGULARNAME: Utente
SINGULARNAME: Membro
SUBJECTPASSWORDCHANGED: 'La tua password è stata cambiata'
SUBJECTPASSWORDRESET: 'Link per azzerare la tua password'
SUBJECTPASSWORDRESET: 'Indirizzo per reimpostare la tua password'
SURNAME: Cognome
TIMEFORMAT: 'Formato dell''ora'
VALIDATIONMEMBEREXISTS: 'Esiste già un utente con l''e-mail %s'
VALIDATIONMEMBEREXISTS: 'Esiste già un membro con questa e-mail'
ValidationIdentifierFailed: 'Non posso sovrascrivere l''utente esistente #{id} con identificatore identico ({name} = {value}))'
WELCOMEBACK: 'Bentornato, {firstname}'
YOUROLDPASSWORD: 'La tua vecchia password'
@ -419,12 +419,12 @@ it:
SINGULARNAME: 'Password utente'
MemberTableField: null
ModelAdmin:
DELETE: Elimina
DELETE: Cancella
DELETEDRECORDS: 'Eliminati {count} record.'
EMPTYBEFOREIMPORT: 'Cancella database prima dell''import'
IMPORT: 'Importa da CSV'
IMPORTEDRECORDS: 'Importati {count} record.'
NOCSVFILE: 'Scegli un file CSV da importare'
NOCSVFILE: 'Cerca un file CSV da importare'
NOIMPORT: 'Nulla da importare.'
RESET: Azzera
Title: 'Modelli di dati'
@ -441,7 +441,7 @@ it:
IMPORT_TAB_HEADER: Importa
SEARCHLISTINGS: Cerca
MoneyField:
FIELDLABELAMOUNT: Importo
FIELDLABELAMOUNT: Totale
FIELDLABELCURRENCY: Valuta
NullableField:
IsNullLabel: 'è nullo.'
@ -461,7 +461,7 @@ it:
AssignedTo: 'assegnato a "{title}"'
FromGroup: 'ereditato dal gruppo "{title}"'
FromRole: 'ereditato dal ruolo "{title}"'
FromRoleOnGroup: 'ereditato dal ruolo "%s" nel gruppo "%s"'
FromRoleOnGroup: 'eredita dal ruolo "%s" sul gruppo "%s"'
PermissionRole:
OnlyAdminCanApply: 'Solo l''amministratore può applicare'
PLURALNAME: Ruoli
@ -481,16 +481,16 @@ it:
NOTFOUND: 'Nessun elemento trovato'
Security:
ALREADYLOGGEDIN: 'Non hai accesso a questa pagina. Se hai un altro account che può accederci, puoi autenticarti qui sotto.'
BUTTONSEND: 'Inviami il link per azzerare la password'
CHANGEPASSWORDBELOW: 'Puoi cambiare la tua password qui sotto.'
BUTTONSEND: 'Inviami il link per reimpostare la password'
CHANGEPASSWORDBELOW: 'Puoi cambiare la tua password qui di seguito.'
CHANGEPASSWORDHEADER: 'Cambia la tua password'
ENTERNEWPASSWORD: 'Per favore inserisci una nuova password.'
ERRORPASSWORDPERMISSION: 'Devi essere autenticato per poter cambiare la tua password!'
LOGGEDOUT: 'Sei stato disconnesso. Se vuoi autenticarti nuovamente, inserisci qui sotto le tue credenziali.'
LOGGEDOUT: 'Sei stato sloggato. Se vuoi autenticarti nuovamente, inserisci qui sotto le tue credenziali.'
LOGIN: Entra
NOTEPAGESECURED: 'La pagina è protetta. Inserisci le credenziali qui sotto per poter andare avanti.'
NOTEPAGESECURED: 'La pagina è sicura. Inserisci le credenziali qui di seguito per poter andare avanti.'
NOTERESETLINKINVALID: '<p>Il link per azzerare la password non è valido o è scaduto.</p><p>Puoi richiederne uno nuovo <a href="{link1}">qui</a> o cambiare la tua password dopo che ti sei <a href="{link2}">connesso</a>.</p>'
NOTERESETPASSWORD: 'Inserisci il tuo indirizzo e-mail e ti verrà inviato un link per poter azzerare la tua password.'
NOTERESETPASSWORD: 'Inserisci il tuo indirizzo e-mail e ti verrà inviato un link per poter reimpostare la tua password.'
PASSWORDSENTHEADER: 'Link per azzeramento della password inviato a ''{email}'''
PASSWORDSENTTEXT: 'Grazie! Un link di azzeramento è stato inviato a ''{email}'', fornito un account esistente per questo indirizzo e-mail.'
SecurityAdmin:

View File

@ -86,7 +86,7 @@ ja_JP:
SUCCESSADD2: '{name}を追加しました'
SUCCESSEDIT: '更新日時 %s %s %s'
ComplexTableField.ss:
ADDITEM: '%sを追加する'
ADDITEM: '%sを追加'
NOITEMSFOUND: 項目が見つかりませんでした
SORTASC: 昇順
SORTDESC: ソート(下順)

View File

@ -4,14 +4,14 @@ mi_NZ:
NEWFOLDER: KōpakiHōu
AssetTableField:
CREATED: 'Tukuatu tuatahi'
DIM: 'Ngā Rahinga'
DIM: Nuinga
FILENAME: 'Ingoa Kōnae'
FOLDER: Kōpaki
LASTEDIT: 'Hurihanga tōmuri'
OWNER: Kaiūmanga
SIZE: Nuinga
TITLE: Taitara
TYPE: 'Momo kōnae'
TITLE: 'Ingoa '
TYPE: 'Tūmomo '
URL: PRO
AssetUploadField:
ChooseFiles: 'Kōwhiri kōnae'
@ -69,7 +69,7 @@ mi_NZ:
ACCESS: 'Uru ki te wāhanga ''{title}'''
ACCESSALLINTERFACES: 'Uru ki ngā wāhanga CMS katoa'
ACCESSALLINTERFACESHELP: 'Ka takahi i ngā tautuhinga uru tauwhāiti ake'
SAVE: Tiaki
SAVE: tiakina
CMSProfileController:
MENUTITLE: 'My Profile'
ChangePasswordEmail.ss:
@ -505,7 +505,7 @@ mi_NZ:
MEMBERS: 'Ngā Mema'
MENUTITLE: Haumarutanga
MemberListCaution: 'Whakatūpato: Mā te tango mema i tēnei rārangi, ka tangohia i ngā rōpū katoa me te pātengi raraunga'
NEWGROUP: 'Rōpū Hōu'
NEWGROUP: 'Roopu hou'
PERMISSIONS: 'Ngā Whakaaetanga'
ROLES: 'Ngā Tūnga'
ROLESDESCRIPTION: 'Ko ngā tūnga he huinga o ngā whakaaetanga i tautuhia i mua, ā, ka taea te tautapa i ēnei ki ngā rōpū.<br />I tukuna iho i ngā rōpū matua ki te hiahiatia.'

View File

@ -17,15 +17,15 @@ nl:
ChooseFiles: 'Selecteer bestanden'
DRAGFILESHERE: 'Sleep bestanden hiernaar toe'
DROPAREA: 'Drop Area'
EDITALL: 'Edit all'
EDITALL: 'Alle bewerken'
EDITANDORGANIZE: 'Bewerk en beheer'
EDITINFO: 'Edit files'
FILES: Files
FILES: Bestanden
FROMCOMPUTER: 'Choose files from your computer'
FROMCOMPUTERINFO: 'Upload from your computer'
TOTAL: Total
TOTAL: Totaal
TOUPLOAD: 'Choose files to upload...'
UPLOADINPROGRESS: 'Please wait… upload in progress'
UPLOADINPROGRESS: 'Even geduld... bezig met uploaden'
UPLOADOR: OF
BBCodeParser:
ALIGNEMENT: Uitlijning
@ -63,7 +63,7 @@ nl:
ANY: Elke
1: Ja
CMSLoadingScreen.ss:
LOADING: Loading...
LOADING: 'Bezig met laden...'
REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
CMSMain:
ACCESS: 'Toegang tot het ''{title}'' gedeelte'
@ -119,7 +119,7 @@ nl:
MONTHS: maanden
SEC: seconde
SECS: seconden
TIMEDIFFAGO: '{difference} ago'
TIMEDIFFAGO: '{difference} geleden'
TIMEDIFFIN: 'in {difference}'
YEAR: jaar
YEARS: jaren
@ -338,8 +338,8 @@ nl:
LoginAttempt:
Email: 'Email adres '
IP: 'IP Adres'
PLURALNAME: 'Login Attempts'
SINGULARNAME: 'Login Attempt'
PLURALNAME: 'Pogingen om in te loggen'
SINGULARNAME: 'Poging om in te loggen'
Status: Status
Member:
ADDGROUP: 'Add group'
@ -393,7 +393,7 @@ nl:
DATEFORMATBAD: 'Datum is niet correct opgegeven'
DAYNOLEADING: 'Dag van de maand zonder voorloop-nul'
DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second'
FOURDIGITYEAR: 'Four-digit year'
FOURDIGITYEAR: 'jaar (yyyy)'
FULLNAMEMONTH: 'Full name of month (e.g. June)'
HOURNOLEADING: 'Hour without leading zero'
MINUTENOLEADING: 'Minute without leading zero'
@ -446,7 +446,7 @@ nl:
NullableField:
IsNullLabel: 'is nul'
NumericField:
VALIDATION: '''{value}'' is not a number, only numbers can be accepted for this field'
VALIDATION: '''{value}'' is geen getal. Dit velt accepteert alleen getallen.'
Pagination:
Page: Page
View: View
@ -458,7 +458,7 @@ nl:
PLURALNAME: Permissions
SINGULARNAME: Permission
PermissionCheckboxSetField:
AssignedTo: 'assigned to "{title}"'
AssignedTo: 'toegewezen aan "{title}"'
FromGroup: 'inherited from group "{title}"'
FromRole: 'inherited from role "{title}"'
FromRoleOnGroup: 'geërfd van rol "%s" in groep "%s"'
@ -466,7 +466,7 @@ nl:
OnlyAdminCanApply: 'Only admin can apply'
PLURALNAME: Roles
SINGULARNAME: Role
Title: Title
Title: Titel
PermissionRoleCode:
PLURALNAME: 'Permission Role Cods'
SINGULARNAME: 'Permission Role Code'
@ -528,7 +528,7 @@ nl:
TableListField:
CSVEXPORT: 'Exporteer naar CSV'
PRINT: Afdrukken
Print: Print
Print: Afdrukken
SELECT: 'Selecteer:'
TableListField.ss:
NOITEMSFOUND: 'No items found'

View File

@ -380,7 +380,7 @@ ro:
belongs_many_many_Groups: Grupuri
db_LastVisited: 'Data ultimei vizite'
db_Locale: 'Interface Locale'
db_LockedOutUntil: 'Blocat pana la'
db_LockedOutUntil: 'Blocat pana cand'
db_NumVisit: 'Numarul de vizite'
db_Password: Parola
db_PasswordExpiry: 'Data de Expirare a Parolei'

View File

@ -1,7 +1,7 @@
sk:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
NEWFOLDER: 'Nový priečinok'
ALLOWEDEXTS: 'Povolené extenzie'
NEWFOLDER: 'Nový Adresár'
AssetTableField:
CREATED: 'Prvýkrát nahrané'
DIM: Rozmery
@ -155,7 +155,7 @@ sk:
HtmlType: 'HTML súbor'
INVALIDEXTENSION: 'Extenzia nie je povolená (platné: {extensions})'
INVALIDEXTENSIONSHORT: 'Extenzia nie je povolená'
IcoType: 'Icon obrázok'
IcoType: 'Ikona obrázok'
JpgType: 'JPEG obrázok - vhodné pre fotografie'
JsType: 'Javascript súbor'
Mp3Type: 'MP3 audio súbor'
@ -240,21 +240,21 @@ sk:
Saved: 'Uložené %s %s'
GridFieldItemEditView.ss: null
Group:
AddRole: 'Pridať úlohu pre túto skupinu'
AddRole: 'Pridať novú úlohu pre túto skupinu'
Code: 'Kód skupiny'
DefaultGroupTitleAdministrators: Administratori
DefaultGroupTitleContentAuthors: 'Autori obsahu'
Description: Popis
GroupReminder: 'Ak vyberiete nadriadenú skupinu, bude táto skupina mať všetky úlohy'
Locked: 'Zamknuté?'
NoRoles: 'Nenašli sa úlohy'
NoRoles: 'Nenašli sa žiadne úlohy'
PLURALNAME: Skupiny
Parent: 'Nadradená skupina'
RolesAddEditLink: 'Spravovať úlohy'
RolesAddEditLink: 'Pridať/upraviť úlohy'
SINGULARNAME: Skupina
Sort: 'Poradie zoradenia'
Sort: 'Zoradiť podľa'
has_many_Permissions: Právomoci
many_many_Members: Členovia
many_many_Members: Uživatelia
GroupImportForm:
Help1: 'Importovať jednu alebo viac skupín v CSV formáte (čiarkov oddelené hodnoty). Zobraziť pokročilé použitie'
Help2: "<div class=\"advanced\">\\n<h4>Pokročilé použitie</h4>\\n<ul>\\n<li>Povolené stĺpce: <em>%s</em></li>\\n<li>Existujúce skupiny sú porovnávané ich unikátnou vlastnostou <em>Code</em>, a aktualizované s novými hodnotami z\\nimportovaného súboru.</li>\\n<li>Hierarchia skupín môže byť tvorená použitím stĺpce <em>ParentCode</em>.</li>\\n<li>Kódy oprávnení môžu byť priradené stĺpcom <em>PermissionCode</em>. Existujúce oprávnenia nie sú smazáné.</li>\\n</ul>\\n</div>"
@ -366,9 +366,9 @@ sk:
LOGGEDINAS: 'Ste prihlásený/á ako {name}.'
NEWPASSWORD: 'Nové heslo'
PASSWORD: Heslo
PLURALNAME: Členovia
PLURALNAME: Uživatelia
REMEMBERME: 'Pamätať si ma nabudúce?'
SINGULARNAME: Člen
SINGULARNAME: Uživatel
SUBJECTPASSWORDCHANGED: 'Vaše heslo bolo zmenené'
SUBJECTPASSWORDRESET: 'Odkaz na resetovanie hesla'
SURNAME: Priezvisko
@ -419,7 +419,7 @@ sk:
SINGULARNAME: 'Heslo člena'
MemberTableField: null
ModelAdmin:
DELETE: Zmazať
DELETE: Vymazať
DELETEDRECORDS: 'Zmazaných {count} záznamov.'
EMPTYBEFOREIMPORT: 'Vyčistiť databázu pred importovaním'
IMPORT: 'Importovať z CSV'
@ -503,7 +503,7 @@ sk:
IMPORTGROUPS: 'Importovať skupiny'
IMPORTUSERS: 'Importovať požívateľov'
MEMBERS: Členovia
MENUTITLE: Zabezpečenie
MENUTITLE: Bezpečnosť
MemberListCaution: 'Upozornenie: Odstánenie členov z tohto zoznamu ich odstáni zo všetkých skupín a databázy.'
NEWGROUP: 'Nová skupina'
PERMISSIONS: Právomoci
@ -532,8 +532,8 @@ sk:
SELECT: 'Vyberte:'
TableListField.ss:
NOITEMSFOUND: 'Žiadne položky'
SORTASC: 'Triedit v vzostupnom poradí'
SORTDESC: 'Triediť v zostupnom poradí'
SORTASC: 'Zoradiť vzostupne'
SORTDESC: 'Zoradiť zostupne'
TableListField_PageControls.ss:
DISPLAYING: Zobrazujem
OF: z

View File

@ -153,7 +153,7 @@ class DB {
* rest of the options, see the specific class.
*/
public static function connect($databaseConfig) {
// This is used by TestRunner::startsession() to test up a test session using an alt
// This is used by the "testsession" module to test up a test session using an alternative name
if($name = self::get_alternative_database_name()) {
$databaseConfig['database'] = $name;
}

View File

@ -376,15 +376,15 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$whereArguments = func_get_arg(0);
} elseif($numberFuncArgs == 2) {
$whereArguments[func_get_arg(0)] = func_get_arg(1);
} else {
} else {
throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()');
}
}
return $this->alterDataQuery(function($query, $list) use ($whereArguments) {
$subquery = $query->disjunctiveGroup();
foreach($whereArguments as $field => $value) {
$fieldArgs = explode(':', $field);
$fieldArgs = explode(':',$field);
$field = array_shift($fieldArgs);
$filterType = array_shift($fieldArgs);
$modifiers = $fieldArgs;
@ -393,16 +393,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$t = singleton($list->dataClass())->dbObject($field);
if($filterType) {
$className = "{$filterType}Filter";
} else {
} else {
$className = 'ExactMatchFilter';
}
if(!class_exists($className)){
$className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType);
}
}
$t = new $className($field, $value, $modifiers);
$t->apply($subquery);
}
}
});
}
@ -453,29 +453,29 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
}
/**
* Translates the comparisator to the sql query
* Translates a filter type to a SQL query.
*
* @param string $field - the fieldname in the db
* @param string $comparisators - example StartsWith, relates to a filtercontext
* @param string $filter - example StartsWith, relates to a filtercontext
* @param array $modifiers - Modifiers to pass to the filter, ie not,nocase
* @param string $value - the value that the filtercontext will use for matching
* @todo Deprecated SearchContexts and pull their functionality into the core of the ORM
*/
private function applyFilterContext($field, $comparisators, $modifiers, $value) {
if($comparisators) {
$className = "{$comparisators}Filter";
private function applyFilterContext($field, $filter, $modifiers, $value) {
if($filter) {
$className = "{$filter}Filter";
} else {
$className = 'ExactMatchFilter';
}
if(!class_exists($className)){
if(!class_exists($className)) {
$className = 'ExactMatchFilter';
array_unshift($modifiers, $comparisators);
array_unshift($modifiers, $filter);
}
$t = new $className($field, $value, $modifiers);
return $this->alterDataQuery(array($t, 'apply'));
}
/**
* Return a copy of this list which does not contain any items with these charactaristics
*
@ -517,13 +517,13 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$t = singleton($list->dataClass())->dbObject($field);
if($filterType) {
$className = "{$filterType}Filter";
} else {
} else {
$className = 'ExactMatchFilter';
}
}
if(!class_exists($className)){
$className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType);
}
}
$t = new $className($field, $value, $modifiers);
$t->exclude($subquery);
}
@ -609,7 +609,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
return $result;
}
/**
* Walks the list using the specified callback
*
@ -979,7 +979,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
public function remove($item) {
// By default, we remove an item from a DataList by deleting it.
$this->removeByID($item->ID);
}
}
/**
* Remove an item from this DataList by ID

View File

@ -1365,8 +1365,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $filter A filter to be inserted into the WHERE clause
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, the static
* field $default_sort on the component class will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
* will be returned.
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string|array $limit A limit expression to be inserted into the LIMIT clause
*
* @return HasManyList The components of the one-to-many relationship.
@ -1379,6 +1378,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
. " on class '$this->class'", E_USER_ERROR);
}
if($join) {
throw new \InvalidArgumentException(
'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
);
}
// If we haven't been written yet, we can't save these relations, so use a list that handles this case
if(!$this->ID) {
if(!isset($this->unsavedRelations[$componentName])) {
@ -1395,7 +1400,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->forForeignID($this->ID);
$result = $result->where($filter)->limit($limit)->sort($sort);
if($join) $result = $result->join($join);
return $result;
}
@ -1406,7 +1410,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $componentName
* @param string $filter
* @param string|array $sort
* @param string $join
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string|array $limit
* @return SQLQuery
*/
@ -1416,6 +1420,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
. " on class '$this->class'", E_USER_ERROR);
}
if($join) {
throw new \InvalidArgumentException(
'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
);
}
$joinField = $this->getRemoteJoinField($componentName, 'has_many');
$id = $this->getField("ID");
@ -2692,8 +2702,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted,
* self::$default_sort will be used.
* @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
* will be returned.
* @param string $join Deprecated 3.0 Join clause. Use leftJoin($table, $joinClause) instead.
* @param string|array $limit A limit expression to be inserted into the LIMIT clause.
* @param string $containerClass The container class to return the results in.
*
@ -2717,6 +2726,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result->setDataModel(DataModel::inst());
return $result;
}
if($join) {
throw new \InvalidArgumentException(
'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
);
}
$result = DataList::create($callerClass)->where($filter)->sort($sort);
@ -2727,8 +2742,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->limit($limit);
}
if($join) $result = $result->join($join);
$result->setDataModel(DataModel::inst());
return $result;
}
@ -2748,7 +2761,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$list = new DataList(get_class($this));
$list->setDataModel($this->model);
} else {
throw new InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed"
throw new \InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed"
. " a classname");
}
return $list;

View File

@ -647,12 +647,12 @@ class DataQuery {
* @param string $field
*/
public function subtract(DataQuery $subtractQuery, $field='ID') {
$subSelect= $subtractQuery->getFinalisedQuery();
$fieldExpression = $this->expressionForField($field, $subSelect);
$fieldExpression = $subtractQuery->expressionForField($field);
$subSelect = $subtractQuery->getFinalisedQuery();
$subSelect->setSelect(array());
$subSelect->selectField($fieldExpression, $field);
$subSelect->setOrderBy(null);
$this->where($this->expressionForField($field, $this).' NOT IN ('.$subSelect->sql().')');
$this->where($this->expressionForField($field).' NOT IN ('.$subSelect->sql().')');
return $this;
}
@ -679,9 +679,9 @@ class DataQuery {
* @param String $field See {@link expressionForField()}.
*/
public function column($field = 'ID') {
$fieldExpression = $this->expressionForField($field);
$query = $this->getFinalisedQuery(array($field));
$originalSelect = $query->getSelect();
$fieldExpression = $this->expressionForField($field, $query);
$query->setSelect(array());
$query->selectField($fieldExpression, $field);
$this->ensureSelectContainsOrderbyColumns($query, $originalSelect);
@ -692,17 +692,21 @@ class DataQuery {
/**
* @param String $field Select statement identifier, either the unquoted column name,
* the full composite SQL statement, or the alias set through {@link SQLQuery->selectField()}.
* @param SQLQuery $query
* @return String
* @return String The expression used to query this field via this DataQuery
*/
protected function expressionForField($field, $query) {
// Special case for ID
if($field == 'ID') {
protected function expressionForField($field) {
// Prepare query object for selecting this field
$query = $this->getFinalisedQuery(array($field));
// Allow query to define the expression for this field
$expression = $query->expressionForField($field);
if(!empty($expression)) return $expression;
// Special case for ID, if not provided
if($field === 'ID') {
$baseClass = ClassInfo::baseDataClass($this->dataClass);
return "\"$baseClass\".\"ID\"";
} else {
return $query->expressionForField($field);
return "\"$baseClass\".\"ID\"";
}
}

View File

@ -566,6 +566,7 @@ class SQLQuery {
if($this->orderby) {
$i = 0;
foreach($this->orderby as $clause => $dir) {
// public function calls and multi-word columns like "CASE WHEN ..."
if(strpos($clause, '(') !== false || strpos($clause, " ") !== false ) {
// remove the old orderby
@ -1059,18 +1060,22 @@ class SQLQuery {
/**
* Return a new SQLQuery that calls the given aggregate functions on this data.
*
* @param $column An aggregate expression, such as 'MAX("Balance")', or a set of them (as an escaped SQL statement)
* @param $alias An optional alias for the aggregate column.
*/
public function aggregate($column) {
if($this->groupby || $this->limit) {
throw new Exception("SQLQuery::aggregate() doesn't work with groupby or limit, yet");
}
public function aggregate($column, $alias = null) {
$clone = clone $this;
$clone->setLimit(array());
$clone->setOrderBy(array());
$clone->setGroupBy(array());
$clone->setSelect($column);
$clone->setLimit($this->limit);
$clone->setOrderBy($this->orderby);
$clone->setGroupBy($this->groupby);
if($alias) {
$clone->selectField($column, $alias);
} else {
$clone->setSelect($column);
}
return $clone;
}

View File

@ -29,7 +29,7 @@ class URLSegmentFilter extends Object {
'/&/u' => '-and-',
'/\s/u' => '-', // remove whitespace
'/_/u' => '-', // underscores to dashes
'/[^A-Za-z0-9+.-]+/u' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[^A-Za-z0-9+.\-]+/u' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/u' => '-', // remove duplicate dashes
'/^[\.\-_]/u' => '', // Remove all leading dots, dashes or underscores
);
@ -66,8 +66,8 @@ class URLSegmentFilter extends Object {
$replacements = $this->getReplacements();
// Unset automated removal of non-ASCII characters, and don't try to transliterate
if($this->getAllowMultibyte() && isset($replacements['/[^A-Za-z0-9+.-]+/u'])) {
unset($replacements['/[^A-Za-z0-9+.-]+/u']);
if($this->getAllowMultibyte() && isset($replacements['/[^A-Za-z0-9+.\-]+/u'])) {
unset($replacements['/[^A-Za-z0-9+.\-]+/u']);
}
foreach($replacements as $regex => $replace) {

View File

@ -136,8 +136,8 @@ class Versioned extends DataExtension {
* @todo Should this all go into VersionedDataQuery?
*/
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
$baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
$baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
switch($dataQuery->getQueryParam('Versioned.mode')) {
// Noop
case '':
@ -203,6 +203,7 @@ class Versioned extends DataExtension {
// below)
$dataQuery->setQueryParam('Versioned.mode', 'stage');
$this->augmentSQL($query, $dataQuery);
$dataQuery->setQueryParam('Versioned.mode', 'stage_unique');
// Now exclude any ID from any other stage. Note that we double rename to avoid the regular stage rename
// renaming all subquery references to be Versioned.stage
@ -232,8 +233,19 @@ class Versioned extends DataExtension {
foreach(self::$db_for_versions_table as $name => $type) {
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name);
}
// Alias the record ID as the row ID
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID");
$query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version'));
// Ensure that any sort order referring to this ID is correctly aliased
$orders = $query->getOrderBy();
foreach($orders as $order => $dir) {
if($order === "\"$baseTable\".\"ID\"") {
unset($orders[$order]);
$orders["\"{$baseTable}_versions\".\"RecordID\""] = $dir;
}
}
$query->setOrderBy($orders);
// latest_version has one more step
// Return latest version instances, regardless of whether they are on a particular stage
@ -250,6 +262,9 @@ class Versioned extends DataExtension {
) AS \"{$alias}_versions_latest\"
WHERE \"{$alias}_versions_latest\".\"RecordID\" = \"{$alias}_versions\".\"RecordID\"
)");
} else {
// If all versions are requested, ensure that records are sorted by this field
$query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version'));
}
break;
default:
@ -266,8 +281,8 @@ class Versioned extends DataExtension {
*/
function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $record) {
$dataClass = $dataQuery->dataClass();
if (isset($record['Version'])){
$dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
if (isset($record['Version'])){
$dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
$dataQuery->where("\"$dataClass\".\"Version\" = " . $record['Version']);
$dataQuery->setQueryParam('Versioned.mode', 'all_versions');
}
@ -739,13 +754,25 @@ class Versioned extends DataExtension {
return !$stagesAreEqual;
}
/**
* @param string $filter
* @param string $sort
* @param string $limit
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string $having
*/
public function Versions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
return $this->allVersions($filter, $sort, $limit, $join, $having);
}
/**
* Return a list of all the versions available.
* @param string $filter
*
* @param string $filter
* @param string $sort
* @param string $limit
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string $having
*/
public function allVersions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
// Make sure the table names are not postfixed (e.g. _Live)
@ -994,7 +1021,7 @@ class Versioned extends DataExtension {
* @param string $stage The name of the stage.
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string $sort A sort expression to be inserted into the ORDER BY clause.
* @param string $join A join expression, such as LEFT JOIN or INNER JOIN
* @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param int $limit A limit on the number of records returned from the database.
* @param string $containerClass The container class for the result set (default is DataList)
* @return SS_List

View File

@ -12,12 +12,16 @@
clear:both;
max-width:750px;
display:block;
.toggle {
font-style: normal;
font-size: $font-base-size;
}
}
#AssetUploadField {
border-bottom: 0;
@include box-shadow(none);
padding: 12px;
}
.backlink {
padding-left: 12px;
@ -274,9 +278,9 @@ body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fie
}
}
.ss-uploadfield-fromcomputer {
/*position: relative;
/*position: relative; */
overflow: hidden;
display: block;*/
display: block;
}
.ss-uploadfield-item-uploador {
float: left;

View File

@ -47,11 +47,12 @@
border: 2px dashed $color-medium-separator;
background: $color-light-separator;
display: none;
margin-right: 15px;
}
}
.ss-uploadfield-item-info {
margin: 0 0 0 100px;
float: left;
.ss-uploadfield-item-name {
display: block;
line-height: 13px;

View File

@ -221,7 +221,9 @@ class Group extends DataObject {
* including all members which are "inherited" from children groups of this record.
* See {@link DirectMembers()} for retrieving members without any inheritance.
*
* @param String
* @param String $filter
* @param String $sort
* @param String $join Deprecated, use leftJoin($table, $joinClause) instead
* @return ManyManyList
*/
public function Members($filter = "", $sort = "", $join = "", $limit = "") {
@ -231,6 +233,12 @@ class Group extends DataObject {
. " DataList instead.");
}
if($join) {
throw new \InvalidArgumentException(
'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
);
}
// First get direct members as a base result
$result = $this->DirectMembers();
// Remove the default foreign key filter in prep for re-applying a filter containing all children groups.
@ -242,7 +250,6 @@ class Group extends DataObject {
// Now set all children groups as a new foreign key
$groups = Group::get()->byIDs($this->collateFamilyIDs());
$result = $result->forForeignID($groups->column('ID'))->where($filter)->sort($sort)->limit($limit);
if($join) $result = $result->join($join);
return $result;
}

View File

@ -1294,16 +1294,20 @@ class Member extends DataObject implements TemplateGlobalProvider {
*/
public function canDelete($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// extended access checks
$results = $this->extend('canDelete', $member);
if($results && is_array($results)) {
if(!min($results)) return false;
else return true;
}
// No member found
if(!($member && $member->exists())) return false;
// Members are not allowed to remove themselves,
// since it would create inconsistencies in the admin UIs.
if($this->ID && $member->ID == $this->ID) return false;
return $this->canEdit($member);
}

View File

@ -28,9 +28,11 @@
</div>
<span class="ss-uploadfield-view-allowed-extensions">
<span class="description">
<em><% _t('AssetAdmin.ALLOWEDEXTS', 'Allowed extensions') %></em>
$Extensions
<a href="#" class="toggle"><% _t('AssetAdmin.SHOWALLOWEDEXTS', 'Show allowed extensions') %></a>
<p class="toggle-content">$Extensions</p>
</span>
</span>
<div class="clear"><!-- --></div>

View File

@ -34,6 +34,7 @@
<% end_if %>
<% else %>
<div class="ss-uploadfield-item ss-uploadfield-addfile<% if $Items && $displayInput %> borderTop<% end_if %>" <% if not $displayInput %>style="display: none;"<% end_if %>>
<% if canUpload %>
<div class="ss-uploadfield-item-preview ss-uploadfield-dropzone ui-corner-all">
<% if $multiple %>
<% _t('UploadField.DROPFILES', 'drop files') %>
@ -41,6 +42,7 @@
<% _t('UploadField.DROPFILE', 'drop a file') %>
<% end_if %>
</div>
<% end_if %>
<div class="ss-uploadfield-item-info">
<label class="ss-uploadfield-item-name"><b>
<% if $multiple %>
@ -49,10 +51,12 @@
<% _t('UploadField.ATTACHFILE', 'Attach a file') %>
<% end_if %>
</b></label>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" data-icon="drive-upload">
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
</label>
<% if canUpload %>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" data-icon="drive-upload">
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> />
</label>
<% end_if %>
<button class="ss-uploadfield-fromfiles ss-ui-button ui-corner-all" data-icon="network-cloud"><% _t('UploadField.FROMFILES', 'From files') %></button>
<% if not $autoUpload %>
<button class="ss-uploadfield-startall ss-ui-button ui-corner-all" data-icon="navigation"><% _t('UploadField.STARTALL', 'Start all') %></button>

View File

@ -39,7 +39,7 @@ class XMLDataFormatterTest extends SapphireTest {
$this->assertEquals(
'<a href="http://mysite.com">mysite.com</a> is a link in this HTML content.'
. ' <![CDATA[this is some nested CDATA]]>',
(string) $xml->Content
(string)$xml->Content
);
}
@ -50,16 +50,16 @@ class XMLDataFormatterTest extends SapphireTest {
$page->Content = 'This is some test content [test_shortcode]test[/test_shortcode]';
$xml = new SimpleXMLElement('<?xml version="1.0"?>' . $formatter->convertDataObjectWithoutHeader($page));
$this->assertEquals('This is some test content test', $xml->Content);
$this->assertEquals('This is some test content test', (string)$xml->Content);
$page->Content = '[test_shortcode,id=-1]';
$xml = new SimpleXMLElement('<?xml version="1.0"?>' . $formatter->convertDataObjectWithoutHeader($page));
$this->assertEmpty('', $xml->Content);
$this->assertEmpty('', (string)$xml->Content);
$page->Content = '[bad_code,id=1]';
$xml = new SimpleXMLElement('<?xml version="1.0"?>' . $formatter->convertDataObjectWithoutHeader($page));
$this->assertContains('[bad_code,id=1]', $xml->Content);
$this->assertContains('[bad_code,id=1]', (string)$xml->Content);
}
/**

View File

@ -81,4 +81,9 @@ Feature: Manage files
# /show/0 is to ensure that we are on top level folder
And I go to "/admin/assets/show/0"
And I click on "folder2" in the "Files" table
And the "folder2" table should contain "file1"
And the "folder2" table should contain "file1"
Scenario: I can see allowed extensions help
When I go to "/admin/assets/add"
And I follow "Show allowed extensions"
Then I should see "png,"

View File

@ -361,6 +361,16 @@ class ObjectTest extends SapphireTest {
Object::parse_class_spec(
"Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")
);
// Namespaced class
$this->assertEquals(
array('Test\MyClass', array()),
Object::parse_class_spec('Test\MyClass')
);
// Fully qualified namespaced class
$this->assertEquals(
array('\Test\MyClass', array()),
Object::parse_class_spec('\Test\MyClass')
);
}
}

View File

@ -46,16 +46,16 @@ class HtmlEditorFieldTest extends FunctionalTest {
$parser = new CSSContentParser($obj->Content);
$xml = $parser->getByXpath('//img');
$this->assertEquals('', $xml[0]['alt'], 'Alt tags are added by default.');
$this->assertEquals('', $xml[0]['title'], 'Title tags are added by default.');
$this->assertEquals('', (string)$xml[0]['alt'], 'Alt tags are added by default.');
$this->assertEquals('', (string)$xml[0]['title'], 'Title tags are added by default.');
$editor->setValue('<img src="assets/example.jpg" alt="foo" title="bar" />');
$editor->saveInto($obj);
$parser = new CSSContentParser($obj->Content);
$xml = $parser->getByXpath('//img');
$this->assertEquals('foo', $xml[0]['alt'], 'Alt tags are preserved.');
$this->assertEquals('bar', $xml[0]['title'], 'Title tags are preserved.');
$this->assertEquals('foo', (string)$xml[0]['alt'], 'Alt tags are preserved.');
$this->assertEquals('bar', (string)$xml[0]['title'], 'Title tags are preserved.');
}
public function testMultiLineSaving() {

View File

@ -325,6 +325,34 @@ class RequirementsTest extends SapphireTest {
$this->assertContains('</script></body>', $html);
}
public function testSuffix() {
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
$basePath = $this->getCurrentRelativePath();
$basePath = 'framework' . substr($basePath, strlen(FRAMEWORK_DIR));
$backend = new Requirements_Backend;
$backend->javascript($basePath .'/RequirementsTest_a.js');
$backend->javascript($basePath .'/RequirementsTest_b.js?foo=bar&bla=blubb');
$backend->css($basePath .'/RequirementsTest_a.css');
$backend->css($basePath .'/RequirementsTest_b.css?foo=bar&bla=blubb');
$backend->set_suffix_requirements(true);
$html = $backend->includeInHTML(false, $template);
$this->assertRegexp('/RequirementsTest_a\.js\?m=[\d]*/', $html);
$this->assertRegexp('/RequirementsTest_b\.js\?m=[\d]*&foo=bar&bla=blubb/', $html);
$this->assertRegexp('/RequirementsTest_a\.css\?m=[\d]*/', $html);
$this->assertRegexp('/RequirementsTest_b\.css\?m=[\d]*&foo=bar&bla=blubb/', $html);
$backend->set_suffix_requirements(false);
$html = $backend->includeInHTML(false, $template);
$this->assertNotContains('RequirementsTest_a.js=', $html);
$this->assertNotRegexp('/RequirementsTest_a\.js\?m=[\d]*/', $html);
$this->assertNotRegexp('/RequirementsTest_b\.js\?m=[\d]*&foo=bar&bla=blubb/', $html);
$this->assertNotRegexp('/RequirementsTest_a\.css\?m=[\d]*/', $html);
$this->assertNotRegexp('/RequirementsTest_b\.css\?m=[\d]*&foo=bar&bla=blubb/', $html);
}
public function assertFileIncluded($backend, $type, $files) {
$type = strtolower($type);
switch (strtolower($type)) {

View File

@ -23,8 +23,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']);
$this->assertEquals('one title', $listElOne->label[0]);
$this->assertEquals('two title', $listElTwo->label[0]);
$this->assertEquals('one title', (string)$listElOne->label[0]);
$this->assertEquals('two title', (string)$listElTwo->label[0]);
$this->assertContains('one view', (string)$listElOne->div);
$this->assertContains('two view', (string)$listElTwo->div);
@ -44,8 +44,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']);
$this->assertEquals('one', $listElOne->label[0]);
$this->assertEquals('two', $listElTwo->label[0]);
$this->assertEquals('one', (string)$listElOne->label[0]);
$this->assertEquals('two', (string)$listElTwo->label[0]);
}
function testLegacyItemsFieldHolderWithTitle() {
@ -62,8 +62,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']);
$this->assertEquals('one title', $listElOne->label[0]);
$this->assertEquals('two title', $listElTwo->label[0]);
$this->assertEquals('one title', (string)$listElOne->label[0]);
$this->assertEquals('two title', (string)$listElTwo->label[0]);
}
}

View File

@ -192,6 +192,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
}
public function testCustomItemRequestClass() {
$this->logInWithPermission('ADMIN');
$component = new GridFieldDetailForm();
$this->assertEquals('GridFieldDetailForm_ItemRequest', $component->getItemRequestClass());
$component->setItemRequestClass('GridFieldDetailFormTest_ItemRequest');
@ -199,6 +201,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
}
public function testItemEditFormCallback() {
$this->logInWithPermission('ADMIN');
$category = new GridFieldDetailFormTest_Category();
$component = new GridFieldDetailForm();
$component->setItemEditFormCallback(function($form, $component) {

View File

@ -25,15 +25,16 @@ class GridFieldEditButtonTest extends SapphireTest {
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
public function testDontShowEditLinks() {
public function testShowEditLinks() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$content = new CSSContentParser($this->gridField->FieldHolder());
// Check that there are content
$this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are no edit links
$this->assertEquals(0, count($content->getBySelector('.edit-link')),
'Edit links should not show when not logged in.');
// Make sure that there are edit links, even though the user doesn't have "edit" permissions
// (he can still view the records)
$this->assertEquals(2, count($content->getBySelector('.edit-link')),
'Edit links should show when not logged in.');
}
public function testShowEditLinksWithAdminPermission() {

View File

@ -476,6 +476,42 @@ class UploadFieldTest extends FunctionalTest {
}
public function testCanUpload() {
$this->loginWithPermission('ADMIN');
$response = $this->get('UploadFieldTest_Controller');
$this->assertFalse($response->isError());
$parser = new CSSContentParser($response->getBody());
$this->assertFalse(
(bool)$parser->getBySelector('#CanUploadFalseField .ss-uploadfield-fromcomputer-fileinput'),
'Removes input file control'
);
$this->assertFalse((bool)$parser->getBySelector('#CanUploadFalseField .ss-uploadfield-dropzone'),
'Removes dropzone');
$this->assertTrue(
(bool)$parser->getBySelector('#CanUploadFalseField .ss-uploadfield-fromfiles'),
'Keeps "From files" button'
);
}
public function testCanUploadWithPermissionCode() {
$field = new UploadField('MyField');
$field->setConfig('canUpload', true);
$this->assertTrue($field->canUpload());
$field->setConfig('canUpload', false);
$this->assertFalse($field->canUpload());
$this->loginWithPermission('ADMIN');
$field->setConfig('canUpload', false);
$this->assertFalse($field->canUpload());
$field->setConfig('canUpload', 'ADMIN');
$this->assertTrue($field->canUpload());
}
public function testIsSaveable() {
$form = $this->getMockForm();
@ -775,6 +811,10 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
$fieldSubfolder->setFolderName('UploadFieldTest/subfolder1');
$fieldSubfolder->setRecord($record);
$fieldCanUploadFalse = new UploadField('CanUploadFalseField');
$fieldCanUploadFalse->setConfig('canUpload', false);
$fieldCanUploadFalse->setRecord($record);
$form = new Form(
$this,
'Form',
@ -789,7 +829,8 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
$fieldManyMany,
$fieldReadonly,
$fieldDisabled,
$fieldSubfolder
$fieldSubfolder,
$fieldCanUploadFalse
),
new FieldList(
new FormAction('submit')
@ -805,7 +846,8 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
'ManyManyFiles',
'ReadonlyField',
'DisabledField',
'SubfolderField'
'SubfolderField',
'CanUploadFalseField'
)
);
return $form;

View File

@ -300,6 +300,15 @@ class i18nTest extends SapphireTest {
$translated, "Testing sprintf placeholders with named injections"
);
$translated = i18n::_t(
'i18nTestModule.INJECTIONSLEGACY', // has %s placeholders
array("Cat", "meow"/*, "meow" */) // remove third arg
);
$this->assertContains(
"TRANS Hello Cat meow. But it is late, ",
$translated, "Testing sprintf placeholders with unnamed injections and too few args"
);
$translated = i18n::_t(
'i18nTestModule.INJECTIONS', // has {name} placeholders
array("Cat", "meow", "meow")

View File

@ -367,6 +367,44 @@ class SQLQueryTest extends SapphireTest {
$this->assertEquals('Object 1', $row['Name']);
}
}
/**
* Tests aggregate() function
*/
public function testAggregate() {
$query = new SQLQuery();
$query->setFrom('"SQLQueryTest_DO"');
$query->setGroupBy("Common");
$queryClone = $query->aggregate('COUNT(*)', 'cnt');
$result = $queryClone->execute();
$this->assertEquals(array(2), $result->column('cnt'));
}
/**
* Test that "_SortColumn0" is added for an aggregate in the ORDER BY
* clause, in combination with a LIMIT and GROUP BY clause.
* For some databases, like MSSQL, this is a complicated scenario
* because a subselect needs to be done to query paginated data.
*/
public function testOrderByContainingAggregateAndLimitOffset() {
$query = new SQLQuery();
$query->setSelect(array('"Name"', '"Meta"'));
$query->setFrom('"SQLQueryTest_DO"');
$query->setOrderBy(array('MAX(Date)'));
$query->setGroupBy(array('"Name"', '"Meta"'));
$query->setLimit('1', '1');
$records = array();
foreach($query->execute() as $record) {
$records[] = $record;
}
$this->assertCount(1, $records);
$this->assertEquals('Object 2', $records[0]['Name']);
$this->assertEquals('2012-05-01 09:00:00', $records['0']['_SortColumn0']);
}
}
@ -374,6 +412,8 @@ class SQLQueryTest_DO extends DataObject implements TestOnly {
static $db = array(
"Name" => "Varchar",
"Meta" => "Varchar",
"Common" => "Varchar",
"Date" => "SS_Datetime"
);
}

View File

@ -2,6 +2,10 @@ SQLQueryTest_DO:
test1:
Name: 'Object 1'
Meta: 'Details 1'
Common: 'Common Value'
Date: 2012-01-01 10:00:00
test2:
Name: 'Object 2'
Meta: 'Details 2'
Meta: 'Details 2'
Date: 2012-05-01 09:00:00
Common: 'Common Value'

View File

@ -22,7 +22,15 @@ class URLSegmentFilterTest extends SapphireTest {
$f->filter('Brötchen')
);
}
public function testReplacesCommonNonAsciiCharacters() {
$f = new URLSegmentFilter();
$this->assertEquals(
urlencode('aa1-.'),
$f->filter('Aa1~!@#$%^*()_`-=;\':"[]\{}|,./<>?')
);
}
public function testRetainsNonAsciiUrlsWithAllowMultiByteOption() {
$f = new URLSegmentFilter();
$f->setAllowMultibyte(true);

View File

@ -74,8 +74,15 @@ class VersionedTest extends SapphireTest {
* Test Versioned::get_including_deleted()
*/
public function testGetIncludingDeleted() {
// Delete a page
$this->objFromFixture('VersionedTest_DataObject', 'page3')->delete();
// Get all ids of pages
$allPageIDs = DataObject::get('VersionedTest_DataObject', "\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC")->column('ID');
// Modify a page, ensuring that the Version ID and Record ID will differ,
// and then subsequently delete it
$targetPage = $this->objFromFixture('VersionedTest_DataObject', 'page3');
$targetPage->Content = 'To be deleted';
$targetPage->write();
$targetPage->delete();
// Get all items, ignoring deleted
$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
@ -90,12 +97,17 @@ class VersionedTest extends SapphireTest {
// Check that page 3 is still there
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title'));
// Check that the returned pages have the correct IDs
$this->assertEquals($allPageIDs, $allPages->column('ID'));
// Check that this still works if we switch to reading the other stage
Versioned::reading_stage("Live");
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
"\"VersionedTest_DataObject\".\"ID\" ASC");
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title'));
// Check that the returned pages still have the correct IDs
$this->assertEquals($allPageIDs, $allPages->column('ID'));
}
public function testVersionedFieldsAdded() {

View File

@ -432,7 +432,7 @@ class MemberTest extends FunctionalTest {
/* Logged in users can edit their own record */
$this->session()->inst_set('loggedInAs', $member->ID);
$this->assertTrue($member->canView());
$this->assertTrue($member->canDelete());
$this->assertFalse($member->canDelete());
$this->assertTrue($member->canEdit());
/* Other uses cannot view, delete or edit others records */
@ -653,6 +653,34 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($m2->validateAutoLoginToken($m1Token), 'Fails token validity test against other member.');
}
public function testCanDelete() {
$admin1 = $this->objFromFixture('Member', 'admin');
$admin2 = $this->objFromFixture('Member', 'other-admin');
$member1 = $this->objFromFixture('Member', 'grouplessmember');
$member2 = $this->objFromFixture('Member', 'noformatmember');
$this->assertTrue(
$admin1->canDelete($admin2),
'Admins can delete other admins'
);
$this->assertTrue(
$member1->canDelete($admin2),
'Admins can delete non-admins'
);
$this->assertFalse(
$admin1->canDelete($admin1),
'Admins can not delete themselves'
);
$this->assertFalse(
$member1->canDelete($member2),
'Non-admins can not delete other non-admins'
);
$this->assertFalse(
$member1->canDelete($member1),
'Non-admins can not delete themselves'
);
}
}
class MemberTest_ViewingAllowedExtension extends DataExtension implements TestOnly {

View File

@ -3,12 +3,12 @@
/**
* Requirements tracker, for javascript and css.
* @todo Document the requirements tracker, and discuss it with the others.
*
*
* @package framework
* @subpackage view
*/
class Requirements {
/**
* Enable combining of css/javascript files.
* @param boolean $enable
@ -34,38 +34,38 @@ class Requirements {
}
/**
* Set whether we want to suffix requirements with the time /
* Set whether we want to suffix requirements with the time /
* location on to the requirements
*
*
* @param bool
*/
public static function set_suffix_requirements($var) {
self::backend()->set_suffix_requirements($var);
}
/**
* Return whether we want to suffix requirements
*
*
* @return bool
*/
public static function get_suffix_requirements() {
return self::backend()->get_suffix_requirements();
}
/**
* Instance of requirements for storage
*
* @var Requirements
*/
private static $backend = null;
public static function backend() {
if(!self::$backend) {
self::$backend = new Requirements_Backend();
}
return self::$backend;
}
/**
* Setter method for changing the Requirements backend
*
@ -74,20 +74,20 @@ class Requirements {
public static function set_backend(Requirements_Backend $backend) {
self::$backend = $backend;
}
/**
* Register the given javascript file as required.
*
*
* See {@link Requirements_Backend::javascript()} for more info
*
*
*/
public static function javascript($file) {
self::backend()->javascript($file);
}
/**
* Add the javascript code to the header of the page
*
*
* See {@link Requirements_Backend::customScript()} for more info
* @param script The script content
* @param uniquenessID Use this to ensure that pieces of code only get added once.
@ -98,50 +98,50 @@ class Requirements {
/**
* Include custom CSS styling to the header of the page.
*
*
* See {@link Requirements_Backend::customCSS()}
*
*
* @param string $script CSS selectors as a string (without <style> tag enclosing selectors).
* @param int $uniquenessID Group CSS by a unique ID as to avoid duplicate custom CSS in header
*/
public static function customCSS($script, $uniquenessID = null) {
self::backend()->customCSS($script, $uniquenessID);
}
/**
* Add the following custom code to the <head> section of the page.
* See {@link Requirements_Backend::insertHeadTags()}
*
*
* @param string $html
* @param string $uniquenessID
*/
public static function insertHeadTags($html, $uniquenessID = null) {
self::backend()->insertHeadTags($html, $uniquenessID);
}
/**
* Load the given javascript template with the page.
* See {@link Requirements_Backend::javascriptTemplate()}
*
*
* @param file The template file to load.
* @param vars The array of variables to load. These variables are loaded via string search & replace.
*/
public static function javascriptTemplate($file, $vars, $uniquenessID = null) {
self::backend()->javascriptTemplate($file, $vars, $uniquenessID);
}
/**
* Register the given stylesheet file as required.
* See {@link Requirements_Backend::css()}
*
*
* @param $file String Filenames should be relative to the base, eg, 'framework/javascript/tree/tree.css'
* @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @see http://www.w3.org/TR/REC-CSS2/media.html
*/
public static function css($file, $media = null) {
self::backend()->css($file, $media);
}
/**
* Registers the given themeable stylesheet as required.
*
@ -163,10 +163,10 @@ class Requirements {
/**
* Clear either a single or all requirements.
* Caution: Clearing single rules works only with customCSS and customScript if you specified a {@uniquenessID}.
*
* Caution: Clearing single rules works only with customCSS and customScript if you specified a {@uniquenessID}.
*
* See {@link Requirements_Backend::clear()}
*
*
* @param $file String
*/
public static function clear($fileOrID = null) {
@ -186,7 +186,7 @@ class Requirements {
/**
* Removes an item from the blocking-list.
* See {@link Requirements_Backend::unblock()}
*
*
* @param string $fileOrID
*/
public static function unblock($fileOrID) {
@ -200,7 +200,7 @@ class Requirements {
public static function unblock_all() {
self::backend()->unblock_all();
}
/**
* Restore requirements cleared by call to Requirements::clear
* See {@link Requirements_Backend::restore()}
@ -208,12 +208,12 @@ class Requirements {
public static function restore() {
self::backend()->restore();
}
/**
* Update the given HTML content with the appropriate include tags for the registered
* requirements.
* requirements.
* See {@link Requirements_Backend::includeInHTML()} for more information.
*
*
* @param string $templateFilePath Absolute path for the *.ss template file
* @param string $content HTML content that has already been parsed from the $templateFilePath
* through {@link SSViewer}.
@ -222,24 +222,24 @@ class Requirements {
public static function includeInHTML($templateFile, $content) {
return self::backend()->includeInHTML($templateFile, $content);
}
public static function include_in_response(SS_HTTPResponse $response) {
return self::backend()->include_in_response($response);
}
/**
* Add i18n files from the given javascript directory.
*
*
* @param String
* @param Boolean
* @param Boolean
*
*
* See {@link Requirements_Backend::add_i18n_javascript()} for more information.
*/
public static function add_i18n_javascript($langDir, $return = false, $langOnly = false) {
return self::backend()->add_i18n_javascript($langDir, $return, $langOnly);
}
/**
* Concatenate several css or javascript files into a single dynamically generated file.
* See {@link Requirements_Backend::combine_files()} for more info.
@ -250,27 +250,27 @@ class Requirements {
public static function combine_files($combinedFileName, $files) {
self::backend()->combine_files($combinedFileName, $files);
}
/**
* Returns all combined files.
* See {@link Requirements_Backend::get_combine_files()}
*
*
* @return array
*/
public static function get_combine_files() {
return self::backend()->get_combine_files();
}
/**
* Deletes all dynamically generated combined files from the filesystem.
* Deletes all dynamically generated combined files from the filesystem.
* See {@link Requirements_Backend::delete_combine_files()}
*
*
* @param string $combinedFileName If left blank, all combined files are deleted.
*/
public static function delete_combined_files($combinedFileName = null) {
return self::backend()->delete_combined_files($combinedFileName);
}
/**
* Re-sets the combined files definition. See {@link Requirements_Backend::clear_combined_files()}
@ -278,7 +278,7 @@ class Requirements {
public static function clear_combined_files() {
self::backend()->clear_combined_files();
}
/**
* See {@link combine_files()}.
*/
@ -295,18 +295,18 @@ class Requirements {
public static function get_custom_scripts() {
return self::backend()->get_custom_scripts();
}
/**
* Set whether you want to write the JS to the body of the page or
* in the head section
*
* Set whether you want to write the JS to the body of the page or
* in the head section
*
* @see Requirements_Backend::set_write_js_to_body()
* @param boolean
*/
public static function set_write_js_to_body($var) {
self::backend()->set_write_js_to_body($var);
}
public static function debug() {
return self::backend()->debug();
}
@ -431,7 +431,7 @@ class Requirements_Backend {
public function set_combined_files_enabled($enable) {
$this->combined_files_enabled = (bool) $enable;
}
public function get_combined_files_enabled() {
return $this->combined_files_enabled;
}
@ -442,33 +442,33 @@ class Requirements_Backend {
public function setCombinedFilesFolder($folder) {
$this->combinedFilesFolder = $folder;
}
/**
* @return String Folder relative to the webroot
*/
public function getCombinedFilesFolder() {
return ($this->combinedFilesFolder) ? $this->combinedFilesFolder : ASSETS_DIR . '/_combinedfiles';
}
/**
* Set whether we want to suffix requirements with the time /
* Set whether we want to suffix requirements with the time /
* location on to the requirements
*
*
* @param bool
*/
public function set_suffix_requirements($var) {
$this->suffix_requirements = $var;
}
/**
* Return whether we want to suffix requirements
*
*
* @return bool
*/
public function get_suffix_requirements() {
return $this->suffix_requirements;
}
/**
* Set whether you want the files written to the head or the body. It
* writes to the body by default which can break some scripts
@ -482,11 +482,11 @@ class Requirements_Backend {
* Register the given javascript file as required.
* Filenames should be relative to the base, eg, 'framework/javascript/loader.js'
*/
public function javascript($file) {
$this->javascript[$file] = true;
}
/**
* Returns an array of all included javascript
*
@ -495,7 +495,7 @@ class Requirements_Backend {
public function get_javascript() {
return array_keys(array_diff_key($this->javascript,$this->blocked));
}
/**
* Add the javascript code to the header of the page
* @todo Make Requirements automatically put this into a separate file :-)
@ -505,10 +505,10 @@ class Requirements_Backend {
public function customScript($script, $uniquenessID = null) {
if($uniquenessID) $this->customScript[$uniquenessID] = $script;
else $this->customScript[] = $script;
$script .= "\n";
}
/**
* Include custom CSS styling to the header of the page.
*
@ -519,7 +519,7 @@ class Requirements_Backend {
if($uniquenessID) $this->customCSS[$uniquenessID] = $script;
else $this->customCSS[] = $script;
}
/**
* Add the following custom code to the <head> section of the page.
*
@ -530,7 +530,7 @@ class Requirements_Backend {
if($uniquenessID) $this->customHeadTags[$uniquenessID] = $html;
else $this->customHeadTags[] = $html;
}
/**
* Load the given javascript template with the page.
* @param file The template file to load.
@ -545,16 +545,16 @@ class Requirements_Backend {
$search[] = '$' . $k;
$replace[] = str_replace("\\'","'", Convert::raw2js($v));
}
$script = str_replace($search, $replace, $script);
$this->customScript($script, $uniquenessID);
}
/**
* Register the given stylesheet file as required.
*
*
* @param $file String Filenames should be relative to the base, eg, 'framework/javascript/tree/tree.css'
* @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @param $media String Comma-separated list of media-types (e.g. "screen,projector")
* @see http://www.w3.org/TR/REC-CSS2/media.html
*/
public function css($file, $media = null) {
@ -562,28 +562,28 @@ class Requirements_Backend {
"media" => $media
);
}
public function get_css() {
return array_diff_key($this->css, $this->blocked);
}
/**
* Needed to actively prevent the inclusion of a file,
* e.g. when using your own jQuery version.
* Blocking should only be used as an exception, because
* it is hard to trace back. You can just block items with an
* ID, so make sure you add an unique identifier to customCSS() and customScript().
*
*
* @param string $fileOrID
*/
public function block($fileOrID) {
$this->blocked[$fileOrID] = $fileOrID;
}
/**
* Clear either a single or all requirements.
* Caution: Clearing single rules works only with customCSS and customScript if you specified a {@uniquenessID}.
*
* Caution: Clearing single rules works only with customCSS and customScript if you specified a {@uniquenessID}.
*
* @param $file String
*/
public function clear($fileOrID = null) {
@ -600,7 +600,7 @@ class Requirements_Backend {
$this->disabled['customScript'] = $this->customScript;
$this->disabled['customCSS'] = $this->customCSS;
$this->disabled['customHeadTags'] = $this->customHeadTags;
$this->javascript = array();
$this->css = array();
$this->customScript = array();
@ -608,7 +608,7 @@ class Requirements_Backend {
$this->customHeadTags = array();
}
}
/**
* Removes an item from the blocking-list.
* CAUTION: Does not "re-add" any previously blocked elements.
@ -623,7 +623,7 @@ class Requirements_Backend {
public function unblock_all() {
$this->blocked = array();
}
/**
* Restore requirements cleared by call to Requirements::clear
*/
@ -634,14 +634,14 @@ class Requirements_Backend {
$this->customCSS = $this->disabled['customCSS'];
$this->customHeadTags = $this->disabled['customHeadTags'];
}
/**
* Update the given HTML content with the appropriate include tags for the registered
* requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
* including a <head> tag. The requirements will insert before the closing <head> tag automatically.
*
* @todo Calculate $prefix properly
*
*
* @param string $templateFilePath Absolute path for the *.ss template file
* @param string $content HTML content that has already been parsed from the $templateFilePath
* through {@link SSViewer}.
@ -649,73 +649,73 @@ class Requirements_Backend {
*/
public function includeInHTML($templateFile, $content) {
if(
(strpos($content, '</head>') !== false || strpos($content, '</head ') !== false)
(strpos($content, '</head>') !== false || strpos($content, '</head ') !== false)
&& ($this->css || $this->javascript || $this->customCSS || $this->customScript || $this->customHeadTags)
) {
$requirements = '';
$jsRequirements = '';
// Combine files - updates $this->javascript and $this->css
$this->process_combined_files();
// Combine files - updates $this->javascript and $this->css
$this->process_combined_files();
foreach(array_diff_key($this->javascript,$this->blocked) as $file => $dummy) {
$path = $this->path_for_file($file);
if($path) {
$jsRequirements .= "<script type=\"text/javascript\" src=\"$path\"></script>\n";
}
}
// add all inline javascript *after* including external files which
// they might rely on
if($this->customScript) {
foreach(array_diff_key($this->customScript,$this->blocked) as $script) {
foreach(array_diff_key($this->customScript,$this->blocked) as $script) {
$jsRequirements .= "<script type=\"text/javascript\">\n//<![CDATA[\n";
$jsRequirements .= "$script\n";
$jsRequirements .= "\n//]]>\n</script>\n";
}
}
foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
$path = $this->path_for_file($file);
if($path) {
$media = (isset($params['media']) && !empty($params['media']))
$media = (isset($params['media']) && !empty($params['media']))
? " media=\"{$params['media']}\"" : "";
$requirements .= "<link rel=\"stylesheet\" type=\"text/css\"{$media} href=\"$path\" />\n";
}
}
foreach(array_diff_key($this->customCSS, $this->blocked) as $css) {
foreach(array_diff_key($this->customCSS, $this->blocked) as $css) {
$requirements .= "<style type=\"text/css\">\n$css\n</style>\n";
}
foreach(array_diff_key($this->customHeadTags,$this->blocked) as $customHeadTag) {
$requirements .= "$customHeadTag\n";
foreach(array_diff_key($this->customHeadTags,$this->blocked) as $customHeadTag) {
$requirements .= "$customHeadTag\n";
}
if($this->write_js_to_body) {
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
// We put script tags into the body, for performance.
// If your template already has script tags in the body, then we put our script
// If your template already has script tags in the body, then we put our script
// tags just before those. Otherwise, we put it at the bottom.
$p2 = stripos($content, '<body');
$p1 = stripos($content, '<script', $p2);
if($p1 !== false) {
$content = substr($content,0,$p1) . $jsRequirements . substr($content,$p1);
} else {
$content = preg_replace("/(<\/body[^>]*>)/i", $jsRequirements . "\\1", $content);
}
// Put CSS at the bottom of the head
// Put CSS at the bottom of the head
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
} else {
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
$content = preg_replace("/(<\/head>)/i", $jsRequirements . "\\1", $content);
}
}
}
return $content;
}
@ -723,20 +723,20 @@ class Requirements_Backend {
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the HTTP response
*/
public function include_in_response(SS_HTTPResponse $response) {
$this->process_combined_files();
$this->process_combined_files();
$jsRequirements = array();
$cssRequirements = array();
foreach(array_diff_key($this->javascript, $this->blocked) as $file => $dummy) {
foreach(array_diff_key($this->javascript, $this->blocked) as $file => $dummy) {
$path = $this->path_for_file($file);
if($path) {
$jsRequirements[] = str_replace(',', '%2C', $path);
}
}
$response->addHeader('X-Include-JS', implode(',', $jsRequirements));
foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
$path = $this->path_for_file($file);
if($path) {
$path = str_replace(',', '%2C', $path);
@ -746,11 +746,11 @@ class Requirements_Backend {
$response->addHeader('X-Include-CSS', implode(',', $cssRequirements));
}
/**
* Add i18n files from the given javascript directory. SilverStripe expects that the given directory
* will contain a number of java script files named by language: en_US.js, de_DE.js, etc.
*
*
* @param String The javascript lang directory, relative to the site root, e.g., 'framework/javascript/lang'
* @param Boolean Return all relative file paths rather than including them in requirements
* @param Boolean Only include language files, not the base libraries
@ -764,10 +764,15 @@ class Requirements_Backend {
if(!$langOnly) $files[] = FRAMEWORK_DIR . '/javascript/i18n.js';
if(substr($langDir,-1) != '/') $langDir .= '/';
$files[] = $langDir . i18n::default_locale() . '.js';
$files[] = $langDir . i18n::get_locale() . '.js';
// If both files don't exist, hard fallback to en_US
if(!Director::fileExists($files[0]) && !Director::fileExists($files[1])) {
$files[] = $langDir . 'en_US.js';
}
// Stub i18n implementation for when i18n is disabled.
} else {
if(!$langOnly) $files[] = FRAMEWORK_DIR . '/javascript/i18nx.js';
@ -778,49 +783,54 @@ class Requirements_Backend {
} else {
foreach($files as $file) $this->javascript($file);
}
}
}
/**
* Finds the path for specified file.
*
* @param string $fileOrUrl
* @return string|boolean
* @return string|boolean
*/
protected function path_for_file($fileOrUrl) {
if(preg_match('{^//|http[s]?}', $fileOrUrl)) {
return $fileOrUrl;
} elseif(Director::fileExists($fileOrUrl)) {
$filePath = preg_replace('/\?.*/', '', Director::baseFolder() . '/' . $fileOrUrl);
$prefix = Director::baseURL();
$mtimesuffix = "";
$suffix = '';
if(strpos($fileOrUrl, '?') !== false) {
$suffix = '&' . substr($fileOrUrl, strpos($fileOrUrl, '?')+1);
$fileOrUrl = substr($fileOrUrl, 0, strpos($fileOrUrl, '?'));
}
if($this->suffix_requirements) {
$mtimesuffix = "?m=" . filemtime(Director::baseFolder() . '/' . $fileOrUrl);
$mtimesuffix = "?m=" . filemtime($filePath);
$suffix = '&';
}
if(strpos($fileOrUrl, '?') !== false) {
if (strlen($suffix) == 0) {
$suffix = '?';
}
$suffix .= substr($fileOrUrl, strpos($fileOrUrl, '?')+1);
$fileOrUrl = substr($fileOrUrl, 0, strpos($fileOrUrl, '?'));
}
return "{$prefix}{$fileOrUrl}{$mtimesuffix}{$suffix}";
} else {
return false;
}
}
/**
* Concatenate several css or javascript files into a single dynamically generated
* file (stored in {@link Director::baseFolder()}). This increases performance
* by fewer HTTP requests.
*
*
* The combined file is regenerated
* based on every file modification time. Optionally a rebuild can be triggered
* by appending ?flush=1 to the URL.
* If all files to be combined are javascript, we use the external JSMin library
* to minify the javascript. This can be controlled by {@link $combine_js_with_jsmin}.
*
*
* All combined files will have a comment on the start of each concatenated file
* denoting their original position. For easier debugging, we recommend to only
* minify javascript if not in development mode ({@link Director::isDev()}).
*
*
* CAUTION: You're responsible for ensuring that the load order for combined files
* is retained - otherwise combining javascript files can lead to functional errors
* in the javascript logic, and combining css can lead to wrong styling inheritance.
@ -828,7 +838,7 @@ class Requirements_Backend {
* in more than one combine_files() call.
* Best practice is to include every javascript file in exactly *one* combine_files()
* directive to avoid the issues mentioned above - this is enforced by this function.
*
*
* CAUTION: Combining CSS Files discards any "media" information.
*
* Example for combined JavaScript:
@ -854,10 +864,10 @@ class Requirements_Backend {
* </code>
*
* @see http://code.google.com/p/jsmin-php/
*
*
* @todo Should we enforce unique inclusion of files, or leave it to the developer? Can auto-detection cause
* breaks?
*
*
* @param string $combinedFileName Filename of the combined file (will be stored in {@link Director::baseFolder()}
* by default)
* @param array $files Array of filenames relative to the webroot
@ -913,7 +923,7 @@ class Requirements_Backend {
}
$this->combine_files[$combinedFileName] = $files;
}
/**
* Returns all combined files.
* @return array
@ -921,15 +931,15 @@ class Requirements_Backend {
public function get_combine_files() {
return $this->combine_files;
}
/**
* Deletes all dynamically generated combined files from the filesystem.
*
* Deletes all dynamically generated combined files from the filesystem.
*
* @param string $combinedFileName If left blank, all combined files are deleted.
*/
public function delete_combined_files($combinedFileName = null) {
$combinedFiles = ($combinedFileName) ? array($combinedFileName => null) : $this->combine_files;
$combinedFolder = ($this->getCombinedFilesFolder()) ?
$combinedFolder = ($this->getCombinedFilesFolder()) ?
(Director::baseFolder() . '/' . $this->combinedFilesFolder) : Director::baseFolder();
foreach($combinedFiles as $combinedFile => $sourceItems) {
$filePath = $combinedFolder . '/' . $combinedFile;
@ -938,7 +948,7 @@ class Requirements_Backend {
}
}
}
public function clear_combined_files() {
$this->combine_files = array();
}
@ -952,7 +962,7 @@ class Requirements_Backend {
// SapphireTest isn't running :-)
if(class_exists('SapphireTest', false)) $runningTest = SapphireTest::is_running_test();
else $runningTest = false;
if((Director::isDev() && !$runningTest && !isset($_REQUEST['combine'])) || !$this->combined_files_enabled) {
return;
}
@ -961,12 +971,12 @@ class Requirements_Backend {
$combinerCheck = array();
foreach($this->combine_files as $combinedFile => $sourceItems) {
foreach($sourceItems as $sourceItem) {
if(isset($combinerCheck[$sourceItem]) && $combinerCheck[$sourceItem] != $combinedFile){
if(isset($combinerCheck[$sourceItem]) && $combinerCheck[$sourceItem] != $combinedFile){
user_error("Requirements_Backend::process_combined_files - file '$sourceItem' appears in two " .
"combined files:" . " '{$combinerCheck[$sourceItem]}' and '$combinedFile'", E_USER_WARNING);
}
$combinerCheck[$sourceItem] = $combinedFile;
}
}
@ -985,7 +995,7 @@ class Requirements_Backend {
$newJSRequirements[$file] = true;
}
}
foreach($this->css as $file => $params) {
if(isset($combinerCheck[$file])) {
$newCSSRequirements[$combinedFilesFolder . $combinerCheck[$file]] = true;
@ -1041,7 +1051,7 @@ class Requirements_Backend {
$isJS = stripos($file, '.js');
if($isJS && $this->combine_js_with_jsmin) {
require_once('thirdparty/jsmin/jsmin.php');
increase_time_limit_to();
$fileContent = JSMin::minify($fileContent);
}
@ -1074,13 +1084,13 @@ class Requirements_Backend {
public function get_custom_scripts() {
$requirements = "";
if($this->customScript) {
foreach($this->customScript as $script) {
$requirements .= "$script\n";
}
}
return $requirements;
}
@ -1102,7 +1112,7 @@ class Requirements_Backend {
$this->css($module.$css);
}
}
public function debug() {
Debug::show($this->javascript);
Debug::show($this->css);
@ -1111,5 +1121,5 @@ class Requirements_Backend {
Debug::show($this->customHeadTags);
Debug::show($this->combine_files);
}
}