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') ->setAttribute('data-icon', 'accept')
->setUseButtonTag(true) ->setUseButtonTag(true)
); );
$form->Actions()->removeByName('delete'); $form->Actions()->removeByName('action_delete');
$form->setValidator(new Member_Validator()); $form->setValidator(new Member_Validator());
$form->setTemplate('Form'); $form->setTemplate('Form');
$form->setAttribute('data-pjax-fragment', null); $form->setAttribute('data-pjax-fragment', null);

View File

@ -357,7 +357,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
$response = parent::handleRequest($request, $model); $response = parent::handleRequest($request, $model);
$title = $this->Title(); $title = $this->Title();
if(!$response->getHeader('X-Controller')) $response->addHeader('X-Controller', $this->class); 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; return $response;
} }
@ -645,13 +645,13 @@ class LeftAndMain extends Controller implements PermissionProvider {
$ancestors->push($record); $ancestors->push($record);
foreach($ancestors as $ancestor) { foreach($ancestors as $ancestor) {
$items->push(new ArrayData(array( $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) 'Link' => ($unlinked) ? false : Controller::join_links($this->Link('show'), $ancestor->ID)
))); )));
} }
} else { } else {
$items->push(new ArrayData(array( $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) '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; } .filter-buttons button.ss-gridfield-button-filter { background-position: -18px 4px !important; }
/* Alternative styles for the switch in old IE */ /* Alternative styles for the switch in old IE */
fieldset.switch-states { padding: 0; } fieldset.switch-states { padding-right: 5px; }
fieldset.switch-states .switch { padding: 0 10px 0 0; } 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 { 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.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 .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 */ /* Hide size controls in IE - they won't work as intended */
.cms-content-controls .preview-size-selector { display: none; } .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 { 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 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 */ } .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; } .filter-buttons button.ss-gridfield-button-filter { background-position: -18px 4px !important; }
/* Alternative styles for the switch in old IE */ /* Alternative styles for the switch in old IE */
fieldset.switch-states { padding: 0; } fieldset.switch-states { padding-right: 5px; }
fieldset.switch-states .switch { padding: 0 10px 0 0; } 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 { 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.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 .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 */ /* Hide size controls in IE - they won't work as intended */
.cms-content-controls .preview-size-selector { display: none; } .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, .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 .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 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 { 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-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.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.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: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; } .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 -------------------------------------------- */ /** -------------------------------------------- Tabs -------------------------------------------- */
.ui-tabs { padding: 0; background: none; } .ui-tabs { padding: 0; background: none; }
.ui-tabs .ui-tabs { position: static; } .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-tabs-panel.cms-edit-form { padding: 0; }
.ui-tabs .ui-widget-header { border: 0; background: none; } .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 ~ .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 { 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; } .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.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.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 .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-panel-padded .ui-tabs-panel { padding: 0; }
.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 .ui-tabs-panel { padding: 8px 0 0 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; } .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() */ /** 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, .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 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; } .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.good { background-color: #eaf6e4; border-color: #72c34b; }
.message p { margin: 0; } .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 icons -------------------------------------------- */
.page-icon, a .jstree-pageicon { display: block; width: 16px; height: 16px; background: transparent url(../images/sitetree_ss_pageclass_icons_default.png) no-repeat; } .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; } #PageType ul li .description { font-style: italic; }
/** -------------------------------------------- Content toolbar -------------------------------------------- */ /** -------------------------------------------- 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: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 { float: right; padding-top: 5px; }
.cms-content-toolbar .cms-tree-view-modes * { display: inline-block; } .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; } .cms-content-toolbar .ss-ui-button { margin-bottom: 8px; }
/* -------------------------------------------------------- Content Tools is the sidebar on the left of the main content panel */ /* -------------------------------------------------------- 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.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-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-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 { 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; } .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-content-tools td { border-bottom: 1px solid #ced7dc; padding: 7px 2px; font-size: 11px; }
/** CMS Batch actions */ /** 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 { 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 label { display: none; }
.cms-content-batchactions .view-mode-batchactions-wrapper fieldset, .cms-content-batchactions .view-mode-batchactions-wrapper .Actions { display: inline-block; } .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 -------------------------------------------- */ /** -------------------------------------------- Member Profile -------------------------------------------- */
form.member-profile-form { padding: 0 16px 0 0; } 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_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 #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 #FavouritePageID { margin-top: 8px; }
form.member-profile-form #CsvFile .middleColumn { background: none !important; } 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 { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; }
.cms-panel .collapsed-flyout li a span { display: block !important; } .cms-panel .collapsed-flyout li a span { display: block !important; }
.cms .cms-panel-padded { padding: 16px 16px; margin: 0; }
/** ------------------------------------------------------------------ /** ------------------------------------------------------------------
* Dialog * 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 > 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 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, .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-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.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.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 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, .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; } .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 .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 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 .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-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 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; } .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 */ /* Styling for the preview screen sizes */
.cms-preview { background-color: #eceff1; height: 100%; width: 100%; } .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 { 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-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%; } .cms-preview .preview-scroll { height: 100%; overflow: auto; position: relative; width: 100%; }

View File

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

View File

@ -424,6 +424,7 @@
* Reacts to the user changing the preview mode. * Reacts to the user changing the preview mode.
*/ */
onchange: function(e) { onchange: function(e) {
this._super(e);
e.preventDefault(); e.preventDefault();
var targetStateName = $(this).val(); var targetStateName = $(this).val();
@ -523,38 +524,36 @@
'onliszt:showing_dropdown': function() { 'onliszt:showing_dropdown': function() {
this.siblings().find('.chzn-drop').addClass('open')._alignRight(); this.siblings().find('.chzn-drop').addClass('open')._alignRight();
}, },
'onliszt:hiding_dropdown': function() { 'onliszt:hiding_dropdown': function() {
this.siblings().find('.chzn-drop').removeClass('open')._removeRightAlign(); 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(){ _addIcon: function(){
var selected = this.find(':selected'); var selected = this.find(':selected');
var iconClass = selected.attr('data-icon'); var iconClass = selected.attr('data-icon');
var target = this.parent().find('.chzn-container a.chzn-single'); var target = this.parent().find('.chzn-container a.chzn-single');
var oldIcon = target.attr('data-icon'); var oldIcon = target.attr('data-icon');
if(oldIcon != undefined){ if(typeof oldIcon !== 'undefined'){
target.removeClass(oldIcon); target.removeClass(oldIcon);
} }
target.addClass(iconClass); target.addClass(iconClass);
target.attr('data-icon', 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({ $('.preview-selector .chzn-drop').entwine({
_alignRight: function(){ _alignRight: function(){
var that = this; var that = this;

View File

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

View File

@ -57,20 +57,42 @@
var onchange = function(e) { var onchange = function(e) {
var $field = $(e.target); var $field = $(e.target);
var origVal = $field.data('changetracker.origVal'); var origVal = $field.data('changetracker.origVal'), newVal;
if(origVal === null || e.target.value != origVal) {
// TODO Also add class to radiobutton/checkbox siblings // 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); $field.addClass(options.changedCssClass);
self.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 // setup original values
var fields = this.getFields(); var fields = this.getFields(), origVal;
fields.filter(':radio,:checkbox').bind('click.changetracker', onchange); fields.filter(':radio,:checkbox').bind('click.changetracker', onchange);
fields.not(':radio,:checkbox').bind('change.changetracker', onchange); fields.not(':radio,:checkbox').bind('change.changetracker', onchange);
fields.each(function() { 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); $(this).data('changetracker.origVal', origVal);
}); });

View File

@ -361,9 +361,7 @@ form.small .field, .field.small {
} }
.ss-ui-button { .ss-ui-button {
font-size: 12px;
margin-top:0px; margin-top:0px;
padding: 5px 10px;
font-weight: bold; font-weight: bold;
text-decoration: none; text-decoration: none;
line-height: $grid-y * 2; line-height: $grid-y * 2;
@ -374,22 +372,6 @@ form.small .field, .field.small {
background-color: $color-button-generic; background-color: $color-button-generic;
white-space: nowrap; 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( @include background(
linear-gradient(color-stops( linear-gradient(color-stops(
lighten($color-button-generic, 10%), lighten($color-button-generic, 10%),

View File

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

View File

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

View File

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

View File

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

View File

@ -303,17 +303,3 @@ table.ss-gridfield-table {
width: 190px; /* Width 100% not calculating by ie7 */ 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> </select>
</span> </span>
<% if Items.Count < 5 %> <% if Items %>
<fieldset id="preview-states" class="cms-preview-states switch-states size_{$Items.Count}"> <% if Items.Count < 5 %>
<div class="switch"> <fieldset id="preview-states" class="cms-preview-states switch-states size_{$Items.Count}">
<% loop Items %> <div class="switch">
<input id="$Title" data-name="$Name" class="state-name $FirstLast" data-link="$Link" name="view" type="radio" <% if First %>checked<% end_if %>> <% loop Items %>
<label for="$Title"<% if First %> class="active"<% end_if %>><span>$Title</span></label> <input id="$Title" data-name="$Name" class="state-name $FirstLast" data-link="$Link" name="view" type="radio" <% if First %>checked<% end_if %>>
<% end_loop %> <label for="$Title"<% if First %> class="active"<% end_if %>><span>$Title</span></label>
<span class="slide-button"></span> <% end_loop %>
</div> <span class="slide-button"></span>
</fieldset> </div>
<% else %> </fieldset>
<span id="preview-state-dropdown" class="cms-preview-states field dropdown"> <% else %>
<select title="<% _t('SilverStripeNavigator.PreviewState', 'Preview State') %>" id="preview-states" class="preview-state dropdown nolabel" autocomplete="off" name="preview-state"> <span id="preview-state-dropdown" class="cms-preview-states field dropdown">
<% loop Items %> <select title="<% _t('SilverStripeNavigator.PreviewState', 'Preview State') %>" id="preview-states" class="preview-state dropdown nolabel" autocomplete="off" name="preview-state">
<option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" > <% loop Items %>
$Title <option name="$Name" data-name="$Name" data-link="$Link" class="state-name $FirstLast" value="$Link" >
</option> $Title
<% end_loop %> </option>
</select> <% end_loop %>
</span> </select>
</span>
<% end_if %>
<% end_if %> <% end_if %>
</div> </div>

View File

@ -60,20 +60,25 @@ class HTTP {
if(!is_numeric($tag)) $tagPrefix = "$tag "; if(!is_numeric($tag)) $tagPrefix = "$tag ";
else $tagPrefix = ""; else $tagPrefix = "";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/ie"; $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/i";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/ie"; $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/i";
$regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/ie"; $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/i";
} }
$regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/ie'; $regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/i';
$regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/ie'; $regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/i';
$regExps[] = '/(list-style-image:[^;]*url *\()([^)]+)(\))/ie'; $regExps[] = '/(list-style-image:[^;]*url *\()([^)]+)(\))/i';
$regExps[] = '/(list-style:[^;]*url *\()([^)]+)(\))/ie'; $regExps[] = '/(list-style:[^;]*url *\()([^)]+)(\))/i';
// Make // 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) { foreach($regExps as $regExp) {
$content = preg_replace($regExp, $code, $content); $content = preg_replace_callback($regExp, $callback, $content);
} }
return $content; return $content;

View File

@ -244,9 +244,12 @@ class Convert {
// Expand hyperlinks // Expand hyperlinks
if(!$preserveLinks && !$config['PreserveLinks']) { if(!$preserveLinks && !$config['PreserveLinks']) {
$data = preg_replace('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", $data = preg_replace_callback('/<a[^>]*href\s*=\s*"([^"]*)">(.*?)<\/a>/i', function($matches) {
$data); return Convert::html2raw($matches[2]) . "[$matches[1]]";
$data = preg_replace('/<a[^>]*href\s*=\s*([^ ]*)>(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", $data); }, $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 // 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 // Keep track of the current bucket that we're putting data into
$bucket = &$args; $bucket = &$args;
$bucketStack = array(); $bucketStack = array();
$had_ns = false;
foreach($tokens as $token) { foreach($tokens as $token) {
$tName = is_array($token) ? $token[0] : $token; $tName = is_array($token) ? $token[0] : $token;
// Get the class naem // Get the class naem
if($class == null && is_array($token) && $token[0] == T_STRING) { if($class == null && is_array($token) && $token[0] == T_STRING) {
$class = $token[1]; $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 // Get arguments
} else if(is_array($token)) { } else if(is_array($token)) {
switch($token[0]) { switch($token[0]) {

View File

@ -11,8 +11,9 @@
Used in side panels and action tabs 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 { 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; } .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-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-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-item-info label { font-size: 16px; line-height: 30px; padding: 5px 16px; }
.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-fromcomputer { /*position: relative; .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-fromcomputer { /*position: relative; */ overflow: hidden; display: block; }
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-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-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; } .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; } 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 .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 { 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 { 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-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 { margin: 0 0 0 100px; } .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 { 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 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; } .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", "build" => "Build/rebuild this environment. Call this whenever you have updated your project sources",
"tests" => "See a list of unit tests to run", "tests" => "See a list of unit tests to run",
"tests/all" => "Run all tests", "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" => "See a list of JavaScript tests to run",
"jstests/all" => "Run all JavaScript tests", "jstests/all" => "Run all JavaScript tests",
"tasks" => "See a list of build tasks to run" "tasks" => "See a list of build tasks to run"
@ -191,16 +188,6 @@ Config::inst()->update('Security', 'token', '$token');
TXT; 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() { public function errors() {
$this->redirect("Debug_"); $this->redirect("Debug_");
} }

View File

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

View File

@ -30,7 +30,7 @@ class SS_LogErrorFileFormatter implements Zend_Log_Formatter_Interface {
$urlSuffix = ''; $urlSuffix = '';
$relfile = Director::makeRelative($errfile); $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'])) { if(isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] && isset($_SERVER['REQUEST_URI'])) {
$urlSuffix = " (http://$_SERVER[HTTP_HOST]$_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); ini_set('memory_limit', ($this->originalMemoryLimit) ? $this->originalMemoryLimit : -1);
// Restore email configuration // Restore email configuration
Email::set_mailer($this->originalMailer); if($this->originalMailer) {
$this->originalMailer = null; Email::set_mailer($this->originalMailer);
$this->mailer = null; $this->originalMailer = null;
}
$this->mailer = null;
// Restore password validation // Restore password validation
Member::set_password_validator($this->originalMemberPasswordValidator); if($this->originalMemberPasswordValidator) {
Member::set_password_validator($this->originalMemberPasswordValidator);
}
// Restore requirements // 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 // Mark test as no longer being run - we use originalIsRunningTest to allow for nested SapphireTest calls
self::$is_running_test = $this->originalIsRunningTest; self::$is_running_test = $this->originalIsRunningTest;
$this->originalIsRunningTest = null; $this->originalIsRunningTest = null;
// Reset theme setting // Reset theme setting
SSViewer::set_theme($this->originalTheme); if($this->originalTheme) {
SSViewer::set_theme($this->originalTheme);
}
// Reset mocked datetime // Reset mocked datetime
SS_Datetime::clear_mock_now(); SS_Datetime::clear_mock_now();

View File

@ -29,12 +29,7 @@ class TestRunner extends Controller {
'coverage/module/$ModuleName' => 'coverageModule', 'coverage/module/$ModuleName' => 'coverageModule',
'coverage/$TestCase!' => 'coverageOnly', 'coverage/$TestCase!' => 'coverageOnly',
'coverage' => 'coverageAll', 'coverage' => 'coverageAll',
'sessionloadyml' => 'sessionloadyml',
'startsession' => 'startsession',
'endsession' => 'endsession',
'setdb' => 'setdb',
'cleanupdb' => 'cleanupdb', 'cleanupdb' => 'cleanupdb',
'emptydb' => 'emptydb',
'module/$ModuleName' => 'module', 'module/$ModuleName' => 'module',
'all' => 'all', 'all' => 'all',
'build' => 'build', 'build' => 'build',
@ -48,9 +43,6 @@ class TestRunner extends Controller {
'coverageAll', 'coverageAll',
'coverageModule', 'coverageModule',
'coverageOnly', 'coverageOnly',
'startsession',
'endsession',
'setdb',
'cleanupdb', 'cleanupdb',
'module', 'module',
'all', 'all',
@ -341,204 +333,6 @@ class TestRunner extends Controller {
if(Director::is_cli() && ($results->failureCount() + $results->errorCount()) > 0) exit(2); 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() { public function setUp() {
// The first DB test will sort out the DB, we don't have to // The first DB test will sort out the DB, we don't have to
SSViewer::flush_template_cache(); SSViewer::flush_template_cache();

View File

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

View File

@ -2,6 +2,28 @@
## Overview ## ## 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 ## Upgrading
### Grouped CMS Buttons ### 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 ### 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 * `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 * `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. * `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/) * Deprecated `Profiler` class, use third-party solutions like [xhprof](https://github.com/facebook/xhprof/)
* Removed defunct or unnecessary debug GET parameters: * Removed defunct or unnecessary debug GET parameters:
`debug_profile`, `debug_memory`, `profile_trace`, `debug_javascript`, `debug_behaviour` `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 ## Stable Releases
* [3.1.0](3.1.0) - Unreleased
* [3.0.2](3.0.2) - 17 September 2012 * [3.0.2](3.0.2) - 17 September 2012
* [3.0.1](3.0.1) - 31 July 2012 * [3.0.1](3.0.1) - 31 July 2012
* [3.0.0](3.0.0) - 28 June 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 ## ## 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.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-rc2](rc/3.0.2-rc2) - 12 September 2012
* [3.0.2-rc1](rc/3.0.2-rc1) - 5 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 = " $messageBody = "
<p><strong>Name:</strong> {$data['Name']}</p> <p><strong>Name:</strong> {$data['Name']}</p>
<p><strong>Website:</strong> {$data['Website']}</p>
<p><strong>Message:</strong> {$data['Message']}</p> <p><strong>Message:</strong> {$data['Message']}</p>
"; ";
$email->setBody($messageBody); $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 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. 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. 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 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: 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 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 ## 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: 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. * 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. * Check `composer.lock` into your repository.
* Deploy your project code base, using the deployment tool of your choice. * 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 # 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). 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 ## 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 2. [Branch for new issue and develop on issue branch](code#branch-for-new-issue-and-develop-on-issue-branch)
$ git checkout ###-description
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] 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).
$ git fetch upstream
$ git rebase upstream/master
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 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).
$ git rebase -i upstream/master
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 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! 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.) * **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 ### Editing files directly on GitHub.com

View File

@ -311,6 +311,7 @@ without affecting the response body.
Built-in headers are: 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-Controller`: PHP class name matching a menu entry, which is marked active
* `X-ControllerURL`: Alternative URL to record in the HTML5 browser history * `X-ControllerURL`: Alternative URL to record in the HTML5 browser history
* `X-Status`: Extended status information, used for an information popover. * `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 ## API Documentation

View File

@ -1,6 +1,6 @@
# Form Field Types # 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 ## 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. 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 ## Related
* [ModelAdmin: A UI driven by GridField](/reference/modeladmin) * [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! 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. 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 ## Search Fields
ModelAdmin uses the `[SearchContext](/reference/searchcontext)` class to provide 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. like for instance in creating and managing a simple gallery.
## Usage ## 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 ### 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! 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 This example will save all uploads in the `/assets/customfolder/` folder. If
the folder doesn't exist, it will be created. 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')); $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` `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. 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 $size = $sizeMB * 1024 * 1024; // 2 MB in bytes
$this->getValidator()->setAllowedMaxFileSize($size); $this->getValidator()->setAllowedMaxFileSize($size);
## Other configuration settings
### Preview dimensions ### Preview dimensions
Set the dimensions of the image preview. By default the max width is set to 80 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 In a similar fashion you can use 'fileEditActions' to set the actions for the
editform, or 'fileEditValidator' to determine the validator (eg RequiredFields). 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 ## 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> </a>
</li> </li>
<% end_loop %> <% end_loop %>
<ul> </ul>
<% end_if %> <% end_if %>
</li> </li>
<% end_loop %> <% end_loop %>

View File

@ -368,7 +368,9 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $
function QuotedPrintable_encode($quotprint) { function QuotedPrintable_encode($quotprint) {
$quotprint = (string)str_replace('\r\n',chr(13).chr(10),$quotprint); $quotprint = (string)str_replace('\r\n',chr(13).chr(10),$quotprint);
$quotprint = (string)str_replace('\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',"=0D=0A",$quotprint);
$quotprint = (string)str_replace('=0D=0A',"\n",$quotprint); $quotprint = (string)str_replace('=0D=0A',"\n",$quotprint);
$quotprint = (string)str_replace('=0A=0D',"\n",$quotprint); $quotprint = (string)str_replace('=0A=0D',"\n",$quotprint);

View File

@ -39,7 +39,7 @@ class FileNameFilter extends Object {
static $default_replacements = array( static $default_replacements = array(
'/\s/' => '-', // remove whitespace '/\s/' => '-', // remove whitespace
'/_/' => '-', // underscores to dashes '/_/' => '-', // 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 '/[\-]{2,}/' => '-', // remove duplicate dashes
'/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores '/^[\.\-_]+/' => '', // Remove all leading dots, dashes or underscores
); );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,6 +13,8 @@
* - Edit file * - Edit file
* - allowedExtensions is by default File::$allowed_extensions<li>maxFileSize the value of min(upload_max_filesize, * - allowedExtensions is by default File::$allowed_extensions<li>maxFileSize the value of min(upload_max_filesize,
* post_max_size) from php.ini * post_max_size) from php.ini
*
* <>Usage</b>
* *
* @example <code> * @example <code>
* $UploadField = new UploadField('myFiles', 'Please upload some images <span>(max. 5 files)</span>'); * $UploadField = new UploadField('myFiles', 'Please upload some images <span>(max. 5 files)</span>');
@ -66,9 +68,9 @@ class UploadField extends FileField {
protected $items; protected $items;
/** /**
* Config for this field used in both, php and javascript (will be merged into the config of the javascript file * @var array Config for this field used in both, php and javascript
* upload plugin) * (will be merged into the config of the javascript file upload plugin).
* @var array * See framework/_config/uploadfield.yml for configuration defaults and documentation.
*/ */
protected $ufConfig = array( protected $ufConfig = array(
/** /**
@ -81,6 +83,11 @@ class UploadField extends FileField {
* @var int * @var int
*/ */
'allowedMaxFileNumber' => null, '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 * @var int
*/ */
@ -133,6 +140,8 @@ class UploadField extends FileField {
$this->addExtraClass('ss-upload'); // class, used by js $this->addExtraClass('ss-upload'); // class, used by js
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only $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); parent::__construct($name, $title);
if($items) $this->setItems($items); if($items) $this->setItems($items);
@ -431,7 +440,7 @@ class UploadField extends FileField {
* @return UploadField_ItemHandler * @return UploadField_ItemHandler
*/ */
public function handleSelect(SS_HTTPRequest $request) { 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 * @return string json
*/ */
public function upload(SS_HTTPRequest $request) { 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 // Protect against CSRF on destructive action
$token = $this->getForm()->getSecurityToken(); $token = $this->getForm()->getSecurityToken();
@ -500,7 +511,7 @@ class UploadField extends FileField {
// Get the uploaded file into a new file object. // Get the uploaded file into a new file object.
try { try {
$this->upload->loadIntoFile($tmpfile, $fileObject, $this->folderName); $this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
} catch (Exception $e) { } catch (Exception $e) {
// we shouldn't get an error here, but just in case // we shouldn't get an error here, but just in case
$return['error'] = $e->getMessage(); $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 // Don't allow upload or edit of a relation when the underlying record hasn't been persisted yet
return (!$record || !$this->managesRelation() || $record->exists()); 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 <?php
/** /**
* This component provides a button for opening the add new form provided by {@link GridFieldDetailForm}. * 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 * @package framework
* @subpackage gridfield * @subpackage gridfield
@ -21,9 +22,12 @@ class GridFieldAddNewButton implements GridField_HTMLProvider {
} }
public function getHTMLFragments($gridField) { public function getHTMLFragments($gridField) {
$singleton = singleton($gridField->getModelClass());
if(!$singleton->canCreate()) return array();
if(!$this->buttonName) { if(!$this->buttonName) {
// provide a default button name, can be changed by calling {@link setButtonName()} on this component // 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)); $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) { public function getColumnContent($gridField, $record, $columnName) {
if($this->removeRelation) { if($this->removeRelation) {
if(!$record->canEdit()) return;
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false, $field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false,
"unlinkrelation", array('RecordID' => $record->ID)) "unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink') ->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink")) ->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus'); ->setAttribute('data-icon', 'chain--minus');
} else { } else {
if(!$record->canDelete()) { if(!$record->canDelete()) return;
return;
}
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord", $field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord",
array('RecordID' => $record->ID)) array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete') ->addExtraClass('gridfield-button-delete')
@ -132,13 +133,20 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
if(!$item) { if(!$item) {
return; return;
} }
if($actionName == 'deleterecord' && !$item->canDelete()) {
throw new ValidationException(
_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
if($actionName == 'deleterecord') { if($actionName == 'deleterecord') {
if(!$item->canDelete()) {
throw new ValidationException(
_t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
}
$item->delete(); $item->delete();
} else { } else {
if(!$item->canEdit()) {
throw new ValidationException(
_t('GridFieldAction_Delete.EditPermissionsFailure',"No permission to unlink record"),0);
}
$gridField->getList()->remove($item); $gridField->getList()->remove($item);
} }
} }

View File

@ -310,16 +310,31 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
return $controller->redirect($noActionURL, 302); 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(); $actions = new FieldList();
if($this->record->ID !== 0) { if($this->record->ID !== 0) {
$actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save')) if($canEdit) {
->setUseButtonTag(true) $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
->addExtraClass('ss-ui-action-constructive') ->setUseButtonTag(true)
->setAttribute('data-icon', 'accept')); ->addExtraClass('ss-ui-action-constructive')
->setAttribute('data-icon', 'accept'));
}
$actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete')) if($canDelete) {
->setUseButtonTag(true) $actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
->addExtraClass('ss-ui-action-destructive')); ->setUseButtonTag(true)
->addExtraClass('ss-ui-action-destructive'));
}
}else{ // adding new record }else{ // adding new record
//Change the Save label to 'Create' //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); $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. // Load many_many extraData for record.
// Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields(). // Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields().
if($list instanceof ManyManyList) { if($list instanceof ManyManyList) {
@ -429,6 +452,10 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
$extraData = null; $extraData = null;
} }
if(!$this->record->canEdit()) {
return $controller->httpError(403);
}
try { try {
$form->saveInto($this->record); $form->saveInto($this->record);
$this->record->write(); $this->record->write();
@ -451,10 +478,16 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
// TODO Save this item into the given relationship // TODO Save this item into the given relationship
$message = sprintf( $link = '<a href="' . $this->Link('edit') . '">"'
_t('GridFieldDetailForm.Saved', 'Saved %s %s'), . htmlspecialchars($this->record->Title, ENT_QUOTES)
$this->record->singular_name(), . '"</a>';
'<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'); $form->sessionMessage($message, 'good');

View File

@ -72,9 +72,9 @@ class GridFieldEditButton implements GridField_ColumnProvider {
* @return string - the HTML for the column * @return string - the HTML for the column
*/ */
public function getColumnContent($gridField, $record, $columnName) { public function getColumnContent($gridField, $record, $columnName) {
if(!$record->canEdit()){ // No permission checks, handled through GridFieldDetailForm,
return; // which can make the form readonly if no edit permissions are available.
}
$data = new ArrayData(array( $data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit') '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 // Update item count prior to filter. GridFieldPageCount will rely on this value
$this->totalItems = $dataList->count(); $this->totalItems = $dataList->count();
if(!($dataList instanceof SS_Limitable)) { if(!($dataList instanceof SS_Limitable) || ($dataList instanceof UnsavedRelationList)) {
return $dataList; return $dataList;
} }

View File

@ -1536,7 +1536,13 @@ class i18n extends Object implements TemplateGlobalProvider {
// Legacy mode: If no injection placeholders are found, // Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order. // replace sprintf placeholders in fixed order.
// Fail silently in case the translation is outdated // 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; if($replaced) $returnValue = $replaced;
} else if(!ArrayLib::is_associative($injectionArray)) { } else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found, // 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. * @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(); $entities = array();
// Search for included templates // Search for included templates
@ -299,11 +299,12 @@ class i18nTextCollector extends Object {
$includeFileName = "{$includeName}.ss"; $includeFileName = "{$includeName}.ss";
$filePath = SSViewer::getTemplateFileByType($includeName, 'Includes'); $filePath = SSViewer::getTemplateFileByType($includeName, 'Includes');
if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main'); if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
if($filePath) { if($filePath && !in_array($filePath, $parsedFiles)) {
$parsedFiles[] = $filePath;
$includeContent = file_get_contents($filePath); $includeContent = file_get_contents($filePath);
$entities = array_merge( $entities = array_merge(
$entities, $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 // @todo Will get massively confused if you include the includer -> infinite loop

View File

@ -16,4 +16,17 @@
this.find('.ss-uploadfield-editandorganize').show(); 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)); }(jQuery));

View File

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

View File

@ -480,7 +480,7 @@ ast:
CSVEXPORT: 'Export to CSV' CSVEXPORT: 'Export to CSV'
NOTFOUND: 'No items found' NOTFOUND: 'No items found'
Security: 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' BUTTONSEND: 'Send me the password reset link'
CHANGEPASSWORDBELOW: 'You can change your password below.' CHANGEPASSWORDBELOW: 'You can change your password below.'
CHANGEPASSWORDHEADER: 'Change your password' CHANGEPASSWORDHEADER: 'Change your password'

View File

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

View File

@ -1,6 +1,6 @@
cs: cs:
AssetAdmin: AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions' ALLOWEDEXTS: 'Povolené extenze'
NEWFOLDER: 'Nová složka' NEWFOLDER: 'Nová složka'
AssetTableField: AssetTableField:
CREATED: 'Poprvé nahráno' CREATED: 'Poprvé nahráno'
@ -119,7 +119,7 @@ cs:
MONTHS: měsíce MONTHS: měsíce
SEC: sekunda SEC: sekunda
SECS: sekundy SECS: sekundy
TIMEDIFFAGO: '{difference} před' TIMEDIFFAGO: 'před {difference}'
TIMEDIFFIN: 'v {difference}' TIMEDIFFIN: 'v {difference}'
YEAR: rok YEAR: rok
YEARS: roky YEARS: roky
@ -149,14 +149,14 @@ cs:
DmgType: 'Apple obraz disku' DmgType: 'Apple obraz disku'
DocType: 'Word dokument' DocType: 'Word dokument'
Filename: 'Jméno souboru' Filename: 'Jméno souboru'
GifType: 'GIF obrázke - vhodné pro diagramy' GifType: 'GIF obrázek - vhodné pro diagramy'
GzType: 'GZIP komprimační soubor' GzType: 'GZIP komprimační soubor'
HtlType: 'HTML soubor' HtlType: 'HTML soubor'
HtmlType: 'HTML soubor' HtmlType: 'HTML soubor'
INVALIDEXTENSION: 'Extenze není povolena (platné: {extensions})' INVALIDEXTENSION: 'Extenze není povolena (platné: {extensions})'
INVALIDEXTENSIONSHORT: 'Extenze není povolena' INVALIDEXTENSIONSHORT: 'Extenze není povolena'
IcoType: 'Icon obrázkek' IcoType: 'Ikona obrázek'
JpgType: 'JPEG obrázke - vhodné pro fotografie' JpgType: 'JPEG obrázek - vhodné pro fotografie'
JsType: 'Javascript soubor' JsType: 'Javascript soubor'
Mp3Type: 'MP3 audio soubor' Mp3Type: 'MP3 audio soubor'
MpgType: 'MPEG video soubor' MpgType: 'MPEG video soubor'
@ -503,7 +503,7 @@ cs:
IMPORTGROUPS: 'Importovat skupiny' IMPORTGROUPS: 'Importovat skupiny'
IMPORTUSERS: 'Importovat uživaté' IMPORTUSERS: 'Importovat uživaté'
MEMBERS: Členové 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' 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' NEWGROUP: 'Nová skupina'
PERMISSIONS: Práva PERMISSIONS: Práva

View File

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

View File

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

View File

@ -1,6 +1,6 @@
fi: fi:
AssetAdmin: AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions' ALLOWEDEXTS: 'Sallitut laajennukset'
NEWFOLDER: 'Uusi kansio' NEWFOLDER: 'Uusi kansio'
AssetTableField: AssetTableField:
CREATED: 'Ensimmäisen kerran ladattu palvelimelle' CREATED: 'Ensimmäisen kerran ladattu palvelimelle'
@ -347,7 +347,7 @@ fi:
BUTTONLOGIN: 'Kirjaudu sisään' BUTTONLOGIN: 'Kirjaudu sisään'
BUTTONLOGINOTHER: 'Kirjaudu jonain muuna' BUTTONLOGINOTHER: 'Kirjaudu jonain muuna'
BUTTONLOSTPASSWORD: 'Kadotin salasanani' 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' CONFIRMNEWPASSWORD: 'Syötä uusi salasana uudelleen'
CONFIRMPASSWORD: 'Syötä salasana uudelleen' CONFIRMPASSWORD: 'Syötä salasana uudelleen'
DATEFORMAT: Päivämäärämuoto DATEFORMAT: Päivämäärämuoto
@ -472,7 +472,7 @@ fi:
SINGULARNAME: 'Käyttöoikeiden roolin koodi' SINGULARNAME: 'Käyttöoikeiden roolin koodi'
Permissions: Permissions:
PERMISSIONS_CATEGORY: 'Roolit ja käyttöoikeudet' 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: PhoneNumberField:
VALIDATION: 'Kirjoita pätevä puhelinnumero' VALIDATION: 'Kirjoita pätevä puhelinnumero'
RelationComplexTableField.ss: RelationComplexTableField.ss:

View File

@ -74,7 +74,7 @@ fr:
MENUTITLE: 'Mon profil' MENUTITLE: 'Mon profil'
ChangePasswordEmail.ss: ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Vous avez modifié votre mot de passe pour' 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 EMAIL: Email
HELLO: Salut HELLO: Salut
PASSWORD: 'Mot de passe' PASSWORD: 'Mot de passe'
@ -253,7 +253,7 @@ fr:
RolesAddEditLink: 'Ajouter/éditer les rôles' RolesAddEditLink: 'Ajouter/éditer les rôles'
SINGULARNAME: Groupe SINGULARNAME: Groupe
Sort: 'Ordre de tri' Sort: 'Ordre de tri'
has_many_Permissions: Autorisations has_many_Permissions: Permissions
many_many_Members: Membres many_many_Members: Membres
GroupImportForm: 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>' 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é. DELETED: Supprimé.
DropdownBatchActionsDefault: Actions DropdownBatchActionsDefault: Actions
HELP: Aide 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.' 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' 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.' 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 PreviewButton: Aperçu
REORGANISATIONSUCCESSFUL: 'Larbre du site a été bien réorganisé.' REORGANISATIONSUCCESSFUL: 'Larbre du site a été bien réorganisé.'
SAVEDUP: Enregistré. SAVEDUP: Enregistré.
@ -454,7 +454,7 @@ fr:
AdminGroup: Administrateur AdminGroup: Administrateur
CMS_ACCESS_CATEGORY: 'Accès au CMS' CMS_ACCESS_CATEGORY: 'Accès au CMS'
FULLADMINRIGHTS: 'Droits d''administration complets' 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 PLURALNAME: Autorisations
SINGULARNAME: Autorisation SINGULARNAME: Autorisation
PermissionCheckboxSetField: PermissionCheckboxSetField:
@ -494,26 +494,26 @@ fr:
PASSWORDSENTHEADER: 'Lien de réinitialisation de mot de passe envoyé à « {email} »' 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.' PASSWORDSENTTEXT: 'Merci ! Un lien de réinitialisation vient dêtre envoyé à « {email} », à condition que cette adresse existe.'
SecurityAdmin: 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: '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” ».' APPLY_ROLES_HELP: 'Possibilité d''éditer les rôles assignés à un groupe. Nécessite "Access to ''Security'' section".'
EDITPERMISSIONS: 'Gérer les autorisations des groupes' EDITPERMISSIONS: 'Gérer les permissions des groupes'
EDITPERMISSIONS_HELP: 'Possibilité d''éditer les autorisations et les adresses IP pour un groupe. Nécessite lautorisation « Accès à la section “Securité” ».' 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' GROUPNAME: 'Nom du group'
IMPORTGROUPS: 'Importer groupes' IMPORTGROUPS: 'Importer groupes'
IMPORTUSERS: 'Importer utilisateurs' IMPORTUSERS: 'Importer utilisateurs'
MEMBERS: Membres MEMBERS: Membres
MENUTITLE: Sécurité 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' 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' NEWGROUP: 'Nouveau Groupe'
PERMISSIONS: Autorisations PERMISSIONS: Permissions
ROLES: Rôles 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 TABROLES: Rôles
Users: Utilisateurs Users: Utilisateurs
SecurityAdmin_MemberImportForm: SecurityAdmin_MemberImportForm:
BtnImport: Importer BtnImport: Importer
FileFieldLabel: 'Fichier CSV <small>(extension autorisée&nbsp;: *.csv)</small>' FileFieldLabel: 'Fichier CSV <small>(Extension permise: *.csv)</small>'
SilverStripeNavigator: SilverStripeNavigator:
Edit: 'Tout modifier' Edit: 'Tout modifier'
SimpleImageField: SimpleImageField:
@ -529,7 +529,7 @@ fr:
CSVEXPORT: 'Exporter vers un fichier CSV' CSVEXPORT: 'Exporter vers un fichier CSV'
PRINT: Imprimer PRINT: Imprimer
Print: Imprimer Print: Imprimer
SELECT: 'Sélectionner&nbsp;:' SELECT: 'Sélectionner:'
TableListField.ss: TableListField.ss:
NOITEMSFOUND: 'Aucun élément na été trouvé' NOITEMSFOUND: 'Aucun élément na été trouvé'
SORTASC: 'Classer en ordre croissant' SORTASC: 'Classer en ordre croissant'

View File

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

View File

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

View File

@ -4,14 +4,14 @@ mi_NZ:
NEWFOLDER: KōpakiHōu NEWFOLDER: KōpakiHōu
AssetTableField: AssetTableField:
CREATED: 'Tukuatu tuatahi' CREATED: 'Tukuatu tuatahi'
DIM: 'Ngā Rahinga' DIM: Nuinga
FILENAME: 'Ingoa Kōnae' FILENAME: 'Ingoa Kōnae'
FOLDER: Kōpaki FOLDER: Kōpaki
LASTEDIT: 'Hurihanga tōmuri' LASTEDIT: 'Hurihanga tōmuri'
OWNER: Kaiūmanga OWNER: Kaiūmanga
SIZE: Nuinga SIZE: Nuinga
TITLE: Taitara TITLE: 'Ingoa '
TYPE: 'Momo kōnae' TYPE: 'Tūmomo '
URL: PRO URL: PRO
AssetUploadField: AssetUploadField:
ChooseFiles: 'Kōwhiri kōnae' ChooseFiles: 'Kōwhiri kōnae'
@ -69,7 +69,7 @@ mi_NZ:
ACCESS: 'Uru ki te wāhanga ''{title}''' ACCESS: 'Uru ki te wāhanga ''{title}'''
ACCESSALLINTERFACES: 'Uru ki ngā wāhanga CMS katoa' ACCESSALLINTERFACES: 'Uru ki ngā wāhanga CMS katoa'
ACCESSALLINTERFACESHELP: 'Ka takahi i ngā tautuhinga uru tauwhāiti ake' ACCESSALLINTERFACESHELP: 'Ka takahi i ngā tautuhinga uru tauwhāiti ake'
SAVE: Tiaki SAVE: tiakina
CMSProfileController: CMSProfileController:
MENUTITLE: 'My Profile' MENUTITLE: 'My Profile'
ChangePasswordEmail.ss: ChangePasswordEmail.ss:
@ -505,7 +505,7 @@ mi_NZ:
MEMBERS: 'Ngā Mema' MEMBERS: 'Ngā Mema'
MENUTITLE: Haumarutanga 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' 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' PERMISSIONS: 'Ngā Whakaaetanga'
ROLES: 'Ngā Tūnga' 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.' 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' ChooseFiles: 'Selecteer bestanden'
DRAGFILESHERE: 'Sleep bestanden hiernaar toe' DRAGFILESHERE: 'Sleep bestanden hiernaar toe'
DROPAREA: 'Drop Area' DROPAREA: 'Drop Area'
EDITALL: 'Edit all' EDITALL: 'Alle bewerken'
EDITANDORGANIZE: 'Bewerk en beheer' EDITANDORGANIZE: 'Bewerk en beheer'
EDITINFO: 'Edit files' EDITINFO: 'Edit files'
FILES: Files FILES: Bestanden
FROMCOMPUTER: 'Choose files from your computer' FROMCOMPUTER: 'Choose files from your computer'
FROMCOMPUTERINFO: 'Upload from your computer' FROMCOMPUTERINFO: 'Upload from your computer'
TOTAL: Total TOTAL: Totaal
TOUPLOAD: 'Choose files to upload...' TOUPLOAD: 'Choose files to upload...'
UPLOADINPROGRESS: 'Please wait… upload in progress' UPLOADINPROGRESS: 'Even geduld... bezig met uploaden'
UPLOADOR: OF UPLOADOR: OF
BBCodeParser: BBCodeParser:
ALIGNEMENT: Uitlijning ALIGNEMENT: Uitlijning
@ -63,7 +63,7 @@ nl:
ANY: Elke ANY: Elke
1: Ja 1: Ja
CMSLoadingScreen.ss: CMSLoadingScreen.ss:
LOADING: Loading... LOADING: 'Bezig met laden...'
REQUIREJS: 'The CMS requires that you have JavaScript enabled.' REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
CMSMain: CMSMain:
ACCESS: 'Toegang tot het ''{title}'' gedeelte' ACCESS: 'Toegang tot het ''{title}'' gedeelte'
@ -119,7 +119,7 @@ nl:
MONTHS: maanden MONTHS: maanden
SEC: seconde SEC: seconde
SECS: seconden SECS: seconden
TIMEDIFFAGO: '{difference} ago' TIMEDIFFAGO: '{difference} geleden'
TIMEDIFFIN: 'in {difference}' TIMEDIFFIN: 'in {difference}'
YEAR: jaar YEAR: jaar
YEARS: jaren YEARS: jaren
@ -338,8 +338,8 @@ nl:
LoginAttempt: LoginAttempt:
Email: 'Email adres ' Email: 'Email adres '
IP: 'IP Adres' IP: 'IP Adres'
PLURALNAME: 'Login Attempts' PLURALNAME: 'Pogingen om in te loggen'
SINGULARNAME: 'Login Attempt' SINGULARNAME: 'Poging om in te loggen'
Status: Status Status: Status
Member: Member:
ADDGROUP: 'Add group' ADDGROUP: 'Add group'
@ -393,7 +393,7 @@ nl:
DATEFORMATBAD: 'Datum is niet correct opgegeven' DATEFORMATBAD: 'Datum is niet correct opgegeven'
DAYNOLEADING: 'Dag van de maand zonder voorloop-nul' DAYNOLEADING: 'Dag van de maand zonder voorloop-nul'
DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second' 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)' FULLNAMEMONTH: 'Full name of month (e.g. June)'
HOURNOLEADING: 'Hour without leading zero' HOURNOLEADING: 'Hour without leading zero'
MINUTENOLEADING: 'Minute without leading zero' MINUTENOLEADING: 'Minute without leading zero'
@ -446,7 +446,7 @@ nl:
NullableField: NullableField:
IsNullLabel: 'is nul' IsNullLabel: 'is nul'
NumericField: 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: Pagination:
Page: Page Page: Page
View: View View: View
@ -458,7 +458,7 @@ nl:
PLURALNAME: Permissions PLURALNAME: Permissions
SINGULARNAME: Permission SINGULARNAME: Permission
PermissionCheckboxSetField: PermissionCheckboxSetField:
AssignedTo: 'assigned to "{title}"' AssignedTo: 'toegewezen aan "{title}"'
FromGroup: 'inherited from group "{title}"' FromGroup: 'inherited from group "{title}"'
FromRole: 'inherited from role "{title}"' FromRole: 'inherited from role "{title}"'
FromRoleOnGroup: 'geërfd van rol "%s" in groep "%s"' FromRoleOnGroup: 'geërfd van rol "%s" in groep "%s"'
@ -466,7 +466,7 @@ nl:
OnlyAdminCanApply: 'Only admin can apply' OnlyAdminCanApply: 'Only admin can apply'
PLURALNAME: Roles PLURALNAME: Roles
SINGULARNAME: Role SINGULARNAME: Role
Title: Title Title: Titel
PermissionRoleCode: PermissionRoleCode:
PLURALNAME: 'Permission Role Cods' PLURALNAME: 'Permission Role Cods'
SINGULARNAME: 'Permission Role Code' SINGULARNAME: 'Permission Role Code'
@ -528,7 +528,7 @@ nl:
TableListField: TableListField:
CSVEXPORT: 'Exporteer naar CSV' CSVEXPORT: 'Exporteer naar CSV'
PRINT: Afdrukken PRINT: Afdrukken
Print: Print Print: Afdrukken
SELECT: 'Selecteer:' SELECT: 'Selecteer:'
TableListField.ss: TableListField.ss:
NOITEMSFOUND: 'No items found' NOITEMSFOUND: 'No items found'

View File

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

View File

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

View File

@ -153,7 +153,7 @@ class DB {
* rest of the options, see the specific class. * rest of the options, see the specific class.
*/ */
public static function connect($databaseConfig) { 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()) { if($name = self::get_alternative_database_name()) {
$databaseConfig['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); $whereArguments = func_get_arg(0);
} elseif($numberFuncArgs == 2) { } elseif($numberFuncArgs == 2) {
$whereArguments[func_get_arg(0)] = func_get_arg(1); $whereArguments[func_get_arg(0)] = func_get_arg(1);
} else { } else {
throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()'); throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()');
} }
return $this->alterDataQuery(function($query, $list) use ($whereArguments) { return $this->alterDataQuery(function($query, $list) use ($whereArguments) {
$subquery = $query->disjunctiveGroup(); $subquery = $query->disjunctiveGroup();
foreach($whereArguments as $field => $value) { foreach($whereArguments as $field => $value) {
$fieldArgs = explode(':', $field); $fieldArgs = explode(':',$field);
$field = array_shift($fieldArgs); $field = array_shift($fieldArgs);
$filterType = array_shift($fieldArgs); $filterType = array_shift($fieldArgs);
$modifiers = $fieldArgs; $modifiers = $fieldArgs;
@ -393,16 +393,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$t = singleton($list->dataClass())->dbObject($field); $t = singleton($list->dataClass())->dbObject($field);
if($filterType) { if($filterType) {
$className = "{$filterType}Filter"; $className = "{$filterType}Filter";
} else { } else {
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
} }
if(!class_exists($className)){ if(!class_exists($className)){
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType); array_unshift($modifiers, $filterType);
} }
$t = new $className($field, $value, $modifiers); $t = new $className($field, $value, $modifiers);
$t->apply($subquery); $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 $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 array $modifiers - Modifiers to pass to the filter, ie not,nocase
* @param string $value - the value that the filtercontext will use for matching * @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 * @todo Deprecated SearchContexts and pull their functionality into the core of the ORM
*/ */
private function applyFilterContext($field, $comparisators, $modifiers, $value) { private function applyFilterContext($field, $filter, $modifiers, $value) {
if($comparisators) { if($filter) {
$className = "{$comparisators}Filter"; $className = "{$filter}Filter";
} else { } else {
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
} }
if(!class_exists($className)){ if(!class_exists($className)) {
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
array_unshift($modifiers, $comparisators); array_unshift($modifiers, $filter);
} }
$t = new $className($field, $value, $modifiers); $t = new $className($field, $value, $modifiers);
return $this->alterDataQuery(array($t, 'apply')); return $this->alterDataQuery(array($t, 'apply'));
} }
/** /**
* Return a copy of this list which does not contain any items with these charactaristics * 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); $t = singleton($list->dataClass())->dbObject($field);
if($filterType) { if($filterType) {
$className = "{$filterType}Filter"; $className = "{$filterType}Filter";
} else { } else {
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
} }
if(!class_exists($className)){ if(!class_exists($className)){
$className = 'ExactMatchFilter'; $className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType); array_unshift($modifiers, $filterType);
} }
$t = new $className($field, $value, $modifiers); $t = new $className($field, $value, $modifiers);
$t->exclude($subquery); $t->exclude($subquery);
} }
@ -609,7 +609,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
return $result; return $result;
} }
/** /**
* Walks the list using the specified callback * 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) { public function remove($item) {
// By default, we remove an item from a DataList by deleting it. // By default, we remove an item from a DataList by deleting it.
$this->removeByID($item->ID); $this->removeByID($item->ID);
} }
/** /**
* Remove an item from this DataList by 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 $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 * @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. * 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 * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* will be returned.
* @param string|array $limit A limit expression to be inserted into the LIMIT clause * @param string|array $limit A limit expression to be inserted into the LIMIT clause
* *
* @return HasManyList The components of the one-to-many relationship. * @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); . " 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 we haven't been written yet, we can't save these relations, so use a list that handles this case
if(!$this->ID) { if(!$this->ID) {
if(!isset($this->unsavedRelations[$componentName])) { if(!isset($this->unsavedRelations[$componentName])) {
@ -1395,7 +1400,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->forForeignID($this->ID); $result = $result->forForeignID($this->ID);
$result = $result->where($filter)->limit($limit)->sort($sort); $result = $result->where($filter)->limit($limit)->sort($sort);
if($join) $result = $result->join($join);
return $result; return $result;
} }
@ -1406,7 +1410,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $componentName * @param string $componentName
* @param string $filter * @param string $filter
* @param string|array $sort * @param string|array $sort
* @param string $join * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string|array $limit * @param string|array $limit
* @return SQLQuery * @return SQLQuery
*/ */
@ -1416,6 +1420,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
. " on class '$this->class'", E_USER_ERROR); . " 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'); $joinField = $this->getRemoteJoinField($componentName, 'has_many');
$id = $this->getField("ID"); $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 $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, * @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted,
* self::$default_sort will be used. * 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 * @param string $join Deprecated 3.0 Join clause. Use leftJoin($table, $joinClause) instead.
* will be returned.
* @param string|array $limit A limit expression to be inserted into the LIMIT clause. * @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. * @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()); $result->setDataModel(DataModel::inst());
return $result; 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); $result = DataList::create($callerClass)->where($filter)->sort($sort);
@ -2727,8 +2742,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->limit($limit); $result = $result->limit($limit);
} }
if($join) $result = $result->join($join);
$result->setDataModel(DataModel::inst()); $result->setDataModel(DataModel::inst());
return $result; return $result;
} }
@ -2748,7 +2761,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$list = new DataList(get_class($this)); $list = new DataList(get_class($this));
$list->setDataModel($this->model); $list->setDataModel($this->model);
} else { } 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"); . " a classname");
} }
return $list; return $list;

View File

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

View File

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

View File

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

View File

@ -136,8 +136,8 @@ class Versioned extends DataExtension {
* @todo Should this all go into VersionedDataQuery? * @todo Should this all go into VersionedDataQuery?
*/ */
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) { public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
$baseTable = ClassInfo::baseDataClass($dataQuery->dataClass()); $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
switch($dataQuery->getQueryParam('Versioned.mode')) { switch($dataQuery->getQueryParam('Versioned.mode')) {
// Noop // Noop
case '': case '':
@ -203,6 +203,7 @@ class Versioned extends DataExtension {
// below) // below)
$dataQuery->setQueryParam('Versioned.mode', 'stage'); $dataQuery->setQueryParam('Versioned.mode', 'stage');
$this->augmentSQL($query, $dataQuery); $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 // 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 // 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) { foreach(self::$db_for_versions_table as $name => $type) {
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name); $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->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 // latest_version has one more step
// Return latest version instances, regardless of whether they are on a particular stage // 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\" ) AS \"{$alias}_versions_latest\"
WHERE \"{$alias}_versions_latest\".\"RecordID\" = \"{$alias}_versions\".\"RecordID\" 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; break;
default: default:
@ -266,8 +281,8 @@ class Versioned extends DataExtension {
*/ */
function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $record) { function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $record) {
$dataClass = $dataQuery->dataClass(); $dataClass = $dataQuery->dataClass();
if (isset($record['Version'])){ if (isset($record['Version'])){
$dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']); $dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
$dataQuery->where("\"$dataClass\".\"Version\" = " . $record['Version']); $dataQuery->where("\"$dataClass\".\"Version\" = " . $record['Version']);
$dataQuery->setQueryParam('Versioned.mode', 'all_versions'); $dataQuery->setQueryParam('Versioned.mode', 'all_versions');
} }
@ -739,13 +754,25 @@ class Versioned extends DataExtension {
return !$stagesAreEqual; 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 = "") { public function Versions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
return $this->allVersions($filter, $sort, $limit, $join, $having); return $this->allVersions($filter, $sort, $limit, $join, $having);
} }
/** /**
* Return a list of all the versions available. * 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 = "") { public function allVersions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
// Make sure the table names are not postfixed (e.g. _Live) // 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 $stage The name of the stage.
* @param string $filter A filter to be inserted into the WHERE clause. * @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 $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 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) * @param string $containerClass The container class for the result set (default is DataList)
* @return SS_List * @return SS_List

View File

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

View File

@ -47,11 +47,12 @@
border: 2px dashed $color-medium-separator; border: 2px dashed $color-medium-separator;
background: $color-light-separator; background: $color-light-separator;
display: none; display: none;
margin-right: 15px;
} }
} }
.ss-uploadfield-item-info { .ss-uploadfield-item-info {
margin: 0 0 0 100px; float: left;
.ss-uploadfield-item-name { .ss-uploadfield-item-name {
display: block; display: block;
line-height: 13px; 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. * including all members which are "inherited" from children groups of this record.
* See {@link DirectMembers()} for retrieving members without any inheritance. * 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 * @return ManyManyList
*/ */
public function Members($filter = "", $sort = "", $join = "", $limit = "") { public function Members($filter = "", $sort = "", $join = "", $limit = "") {
@ -231,6 +233,12 @@ class Group extends DataObject {
. " DataList instead."); . " 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 // First get direct members as a base result
$result = $this->DirectMembers(); $result = $this->DirectMembers();
// Remove the default foreign key filter in prep for re-applying a filter containing all children groups. // 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 // Now set all children groups as a new foreign key
$groups = Group::get()->byIDs($this->collateFamilyIDs()); $groups = Group::get()->byIDs($this->collateFamilyIDs());
$result = $result->forForeignID($groups->column('ID'))->where($filter)->sort($sort)->limit($limit); $result = $result->forForeignID($groups->column('ID'))->where($filter)->sort($sort)->limit($limit);
if($join) $result = $result->join($join);
return $result; return $result;
} }

View File

@ -1294,16 +1294,20 @@ class Member extends DataObject implements TemplateGlobalProvider {
*/ */
public function canDelete($member = null) { public function canDelete($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser(); if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
// extended access checks // extended access checks
$results = $this->extend('canDelete', $member); $results = $this->extend('canDelete', $member);
if($results && is_array($results)) { if($results && is_array($results)) {
if(!min($results)) return false; if(!min($results)) return false;
else return true; else return true;
} }
// No member found // No member found
if(!($member && $member->exists())) return false; 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); return $this->canEdit($member);
} }

View File

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

View File

@ -34,6 +34,7 @@
<% end_if %> <% end_if %>
<% else %> <% else %>
<div class="ss-uploadfield-item ss-uploadfield-addfile<% if $Items && $displayInput %> borderTop<% end_if %>" <% if not $displayInput %>style="display: none;"<% end_if %>> <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"> <div class="ss-uploadfield-item-preview ss-uploadfield-dropzone ui-corner-all">
<% if $multiple %> <% if $multiple %>
<% _t('UploadField.DROPFILES', 'drop files') %> <% _t('UploadField.DROPFILES', 'drop files') %>
@ -41,6 +42,7 @@
<% _t('UploadField.DROPFILE', 'drop a file') %> <% _t('UploadField.DROPFILE', 'drop a file') %>
<% end_if %> <% end_if %>
</div> </div>
<% end_if %>
<div class="ss-uploadfield-item-info"> <div class="ss-uploadfield-item-info">
<label class="ss-uploadfield-item-name"><b> <label class="ss-uploadfield-item-name"><b>
<% if $multiple %> <% if $multiple %>
@ -49,10 +51,12 @@
<% _t('UploadField.ATTACHFILE', 'Attach a file') %> <% _t('UploadField.ATTACHFILE', 'Attach a file') %>
<% end_if %> <% end_if %>
</b></label> </b></label>
<label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" data-icon="drive-upload"> <% if canUpload %>
<% _t('UploadField.FROMCOMPUTER', 'From your computer') %> <label class="ss-uploadfield-fromcomputer ss-ui-button ui-corner-all" data-icon="drive-upload">
<input id="$id" name="$getName" class="$extraClass ss-uploadfield-fromcomputer-fileinput" data-config="$configString" type="file"<% if $multiple %> multiple="multiple"<% end_if %> /> <% _t('UploadField.FROMCOMPUTER', 'From your computer') %>
</label> <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> <button class="ss-uploadfield-fromfiles ss-ui-button ui-corner-all" data-icon="network-cloud"><% _t('UploadField.FROMFILES', 'From files') %></button>
<% if not $autoUpload %> <% if not $autoUpload %>
<button class="ss-uploadfield-startall ss-ui-button ui-corner-all" data-icon="navigation"><% _t('UploadField.STARTALL', 'Start all') %></button> <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( $this->assertEquals(
'<a href="http://mysite.com">mysite.com</a> is a link in this HTML content.' '<a href="http://mysite.com">mysite.com</a> is a link in this HTML content.'
. ' <![CDATA[this is some nested CDATA]]>', . ' <![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]'; $page->Content = 'This is some test content [test_shortcode]test[/test_shortcode]';
$xml = new SimpleXMLElement('<?xml version="1.0"?>' . $formatter->convertDataObjectWithoutHeader($page)); $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]'; $page->Content = '[test_shortcode,id=-1]';
$xml = new SimpleXMLElement('<?xml version="1.0"?>' . $formatter->convertDataObjectWithoutHeader($page)); $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]'; $page->Content = '[bad_code,id=1]';
$xml = new SimpleXMLElement('<?xml version="1.0"?>' . $formatter->convertDataObjectWithoutHeader($page)); $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 # /show/0 is to ensure that we are on top level folder
And I go to "/admin/assets/show/0" And I go to "/admin/assets/show/0"
And I click on "folder2" in the "Files" table 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( Object::parse_class_spec(
"Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')") "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); $parser = new CSSContentParser($obj->Content);
$xml = $parser->getByXpath('//img'); $xml = $parser->getByXpath('//img');
$this->assertEquals('', $xml[0]['alt'], 'Alt tags are added by default.'); $this->assertEquals('', (string)$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]['title'], 'Title tags are added by default.');
$editor->setValue('<img src="assets/example.jpg" alt="foo" title="bar" />'); $editor->setValue('<img src="assets/example.jpg" alt="foo" title="bar" />');
$editor->saveInto($obj); $editor->saveInto($obj);
$parser = new CSSContentParser($obj->Content); $parser = new CSSContentParser($obj->Content);
$xml = $parser->getByXpath('//img'); $xml = $parser->getByXpath('//img');
$this->assertEquals('foo', $xml[0]['alt'], 'Alt tags are preserved.'); $this->assertEquals('foo', (string)$xml[0]['alt'], 'Alt tags are preserved.');
$this->assertEquals('bar', $xml[0]['title'], 'Title tags are preserved.'); $this->assertEquals('bar', (string)$xml[0]['title'], 'Title tags are preserved.');
} }
public function testMultiLineSaving() { public function testMultiLineSaving() {

View File

@ -325,6 +325,34 @@ class RequirementsTest extends SapphireTest {
$this->assertContains('</script></body>', $html); $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) { public function assertFileIncluded($backend, $type, $files) {
$type = strtolower($type); $type = strtolower($type);
switch (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('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']); $this->assertEquals('two', (string)$listElTwo->input[0]['value']);
$this->assertEquals('one title', $listElOne->label[0]); $this->assertEquals('one title', (string)$listElOne->label[0]);
$this->assertEquals('two title', $listElTwo->label[0]); $this->assertEquals('two title', (string)$listElTwo->label[0]);
$this->assertContains('one view', (string)$listElOne->div); $this->assertContains('one view', (string)$listElOne->div);
$this->assertContains('two view', (string)$listElTwo->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('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']); $this->assertEquals('two', (string)$listElTwo->input[0]['value']);
$this->assertEquals('one', $listElOne->label[0]); $this->assertEquals('one', (string)$listElOne->label[0]);
$this->assertEquals('two', $listElTwo->label[0]); $this->assertEquals('two', (string)$listElTwo->label[0]);
} }
function testLegacyItemsFieldHolderWithTitle() { function testLegacyItemsFieldHolderWithTitle() {
@ -62,8 +62,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']); $this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']); $this->assertEquals('two', (string)$listElTwo->input[0]['value']);
$this->assertEquals('one title', $listElOne->label[0]); $this->assertEquals('one title', (string)$listElOne->label[0]);
$this->assertEquals('two title', $listElTwo->label[0]); $this->assertEquals('two title', (string)$listElTwo->label[0]);
} }
} }

View File

@ -192,6 +192,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
} }
public function testCustomItemRequestClass() { public function testCustomItemRequestClass() {
$this->logInWithPermission('ADMIN');
$component = new GridFieldDetailForm(); $component = new GridFieldDetailForm();
$this->assertEquals('GridFieldDetailForm_ItemRequest', $component->getItemRequestClass()); $this->assertEquals('GridFieldDetailForm_ItemRequest', $component->getItemRequestClass());
$component->setItemRequestClass('GridFieldDetailFormTest_ItemRequest'); $component->setItemRequestClass('GridFieldDetailFormTest_ItemRequest');
@ -199,6 +201,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
} }
public function testItemEditFormCallback() { public function testItemEditFormCallback() {
$this->logInWithPermission('ADMIN');
$category = new GridFieldDetailFormTest_Category(); $category = new GridFieldDetailFormTest_Category();
$component = new GridFieldDetailForm(); $component = new GridFieldDetailForm();
$component->setItemEditFormCallback(function($form, $component) { $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()); $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(); } if(Member::currentUser()) { Member::currentUser()->logOut(); }
$content = new CSSContentParser($this->gridField->FieldHolder()); $content = new CSSContentParser($this->gridField->FieldHolder());
// Check that there are content // Check that there are content
$this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item'))); $this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item')));
// Make sure that there are no edit links // Make sure that there are edit links, even though the user doesn't have "edit" permissions
$this->assertEquals(0, count($content->getBySelector('.edit-link')), // (he can still view the records)
'Edit links should not show when not logged in.'); $this->assertEquals(2, count($content->getBySelector('.edit-link')),
'Edit links should show when not logged in.');
} }
public function testShowEditLinksWithAdminPermission() { 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() { public function testIsSaveable() {
$form = $this->getMockForm(); $form = $this->getMockForm();
@ -775,6 +811,10 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
$fieldSubfolder->setFolderName('UploadFieldTest/subfolder1'); $fieldSubfolder->setFolderName('UploadFieldTest/subfolder1');
$fieldSubfolder->setRecord($record); $fieldSubfolder->setRecord($record);
$fieldCanUploadFalse = new UploadField('CanUploadFalseField');
$fieldCanUploadFalse->setConfig('canUpload', false);
$fieldCanUploadFalse->setRecord($record);
$form = new Form( $form = new Form(
$this, $this,
'Form', 'Form',
@ -789,7 +829,8 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
$fieldManyMany, $fieldManyMany,
$fieldReadonly, $fieldReadonly,
$fieldDisabled, $fieldDisabled,
$fieldSubfolder $fieldSubfolder,
$fieldCanUploadFalse
), ),
new FieldList( new FieldList(
new FormAction('submit') new FormAction('submit')
@ -805,7 +846,8 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
'ManyManyFiles', 'ManyManyFiles',
'ReadonlyField', 'ReadonlyField',
'DisabledField', 'DisabledField',
'SubfolderField' 'SubfolderField',
'CanUploadFalseField'
) )
); );
return $form; return $form;

View File

@ -300,6 +300,15 @@ class i18nTest extends SapphireTest {
$translated, "Testing sprintf placeholders with named injections" $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( $translated = i18n::_t(
'i18nTestModule.INJECTIONS', // has {name} placeholders 'i18nTestModule.INJECTIONS', // has {name} placeholders
array("Cat", "meow", "meow") array("Cat", "meow", "meow")

View File

@ -367,6 +367,44 @@ class SQLQueryTest extends SapphireTest {
$this->assertEquals('Object 1', $row['Name']); $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( static $db = array(
"Name" => "Varchar", "Name" => "Varchar",
"Meta" => "Varchar", "Meta" => "Varchar",
"Common" => "Varchar",
"Date" => "SS_Datetime"
); );
} }

View File

@ -2,6 +2,10 @@ SQLQueryTest_DO:
test1: test1:
Name: 'Object 1' Name: 'Object 1'
Meta: 'Details 1' Meta: 'Details 1'
Common: 'Common Value'
Date: 2012-01-01 10:00:00
test2: test2:
Name: 'Object 2' 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') $f->filter('Brötchen')
); );
} }
public function testReplacesCommonNonAsciiCharacters() {
$f = new URLSegmentFilter();
$this->assertEquals(
urlencode('aa1-.'),
$f->filter('Aa1~!@#$%^*()_`-=;\':"[]\{}|,./<>?')
);
}
public function testRetainsNonAsciiUrlsWithAllowMultiByteOption() { public function testRetainsNonAsciiUrlsWithAllowMultiByteOption() {
$f = new URLSegmentFilter(); $f = new URLSegmentFilter();
$f->setAllowMultibyte(true); $f->setAllowMultibyte(true);

View File

@ -74,8 +74,15 @@ class VersionedTest extends SapphireTest {
* Test Versioned::get_including_deleted() * Test Versioned::get_including_deleted()
*/ */
public function testGetIncludingDeleted() { public function testGetIncludingDeleted() {
// Delete a page // Get all ids of pages
$this->objFromFixture('VersionedTest_DataObject', 'page3')->delete(); $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 // Get all items, ignoring deleted
$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0", $remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
@ -90,12 +97,17 @@ class VersionedTest extends SapphireTest {
// Check that page 3 is still there // Check that page 3 is still there
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title')); $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 // Check that this still works if we switch to reading the other stage
Versioned::reading_stage("Live"); Versioned::reading_stage("Live");
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0", $allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
"\"VersionedTest_DataObject\".\"ID\" ASC"); "\"VersionedTest_DataObject\".\"ID\" ASC");
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title')); $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() { public function testVersionedFieldsAdded() {

View File

@ -432,7 +432,7 @@ class MemberTest extends FunctionalTest {
/* Logged in users can edit their own record */ /* Logged in users can edit their own record */
$this->session()->inst_set('loggedInAs', $member->ID); $this->session()->inst_set('loggedInAs', $member->ID);
$this->assertTrue($member->canView()); $this->assertTrue($member->canView());
$this->assertTrue($member->canDelete()); $this->assertFalse($member->canDelete());
$this->assertTrue($member->canEdit()); $this->assertTrue($member->canEdit());
/* Other uses cannot view, delete or edit others records */ /* 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.'); $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 { class MemberTest_ViewingAllowedExtension extends DataExtension implements TestOnly {

View File

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