diff --git a/_config/uploadfield.yml b/_config/uploadfield.yml new file mode 100644 index 000000000..6227f3e50 --- /dev/null +++ b/_config/uploadfield.yml @@ -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: \ No newline at end of file diff --git a/admin/code/CMSProfileController.php b/admin/code/CMSProfileController.php index 7eb7ee9df..8e69338b8 100644 --- a/admin/code/CMSProfileController.php +++ b/admin/code/CMSProfileController.php @@ -30,7 +30,7 @@ class CMSProfileController extends LeftAndMain { ->setAttribute('data-icon', 'accept') ->setUseButtonTag(true) ); - $form->Actions()->removeByName('delete'); + $form->Actions()->removeByName('action_delete'); $form->setValidator(new Member_Validator()); $form->setTemplate('Form'); $form->setAttribute('data-pjax-fragment', null); diff --git a/admin/code/LeftAndMain.php b/admin/code/LeftAndMain.php index 27c2e6128..bcaf4f3e8 100644 --- a/admin/code/LeftAndMain.php +++ b/admin/code/LeftAndMain.php @@ -357,7 +357,7 @@ class LeftAndMain extends Controller implements PermissionProvider { $response = parent::handleRequest($request, $model); $title = $this->Title(); if(!$response->getHeader('X-Controller')) $response->addHeader('X-Controller', $this->class); - if(!$response->getHeader('X-Title')) $response->addHeader('X-Title', $title); + if(!$response->getHeader('X-Title')) $response->addHeader('X-Title', urlencode($title)); return $response; } @@ -645,13 +645,13 @@ class LeftAndMain extends Controller implements PermissionProvider { $ancestors->push($record); foreach($ancestors as $ancestor) { $items->push(new ArrayData(array( - 'Title' => $ancestor->Title, + 'Title' => ($ancestor->MenuTitle) ? $ancestor->MenuTitle : $ancestor->Title, 'Link' => ($unlinked) ? false : Controller::join_links($this->Link('show'), $ancestor->ID) ))); } } else { $items->push(new ArrayData(array( - 'Title' => $record->Title, + 'Title' => ($record->MenuTitle) ? $record->MenuTitle : $record->Title, 'Link' => ($unlinked) ? false : Controller::join_links($this->Link('show'), $record->ID) ))); } diff --git a/admin/css/ie7.css b/admin/css/ie7.css index 375915673..afffeb927 100644 --- a/admin/css/ie7.css +++ b/admin/css/ie7.css @@ -42,12 +42,13 @@ .filter-buttons button.ss-gridfield-button-filter { background-position: -18px 4px !important; } /* Alternative styles for the switch in old IE */ -fieldset.switch-states { padding: 0; } -fieldset.switch-states .switch { padding: 0 10px 0 0; } +fieldset.switch-states { padding-right: 5px; } +fieldset.switch-states .switch { padding: 0; width: 132%; left: -32px; } fieldset.switch-states .switch label { overflow: visible; text-overflow: visible; white-space: normal; padding: 0; } fieldset.switch-states .switch label.active { color: #fff; background-color: #2b9c32; } -fieldset.switch-states .switch label span { display: inline; padding: 0 10px; padding-right: 15px; overflow: visible; text-overflow: visible; white-space: wrap; } +fieldset.switch-states .switch label span { display: inline; padding: 0 8px; overflow: visible; text-overflow: visible; white-space: wrap; } fieldset.switch-states .switch .slide-button { display: none; } +fieldset.switch-states .switch input.state-name { margin-left: -20px; } /* Hide size controls in IE - they won't work as intended */ .cms-content-controls .preview-size-selector { display: none; } @@ -217,8 +218,3 @@ table.ss-gridfield-table tr.ss-gridfield-item.even { background: #F0F4F7; } .cms .cms-content-actions .Actions .action-menus.ss-ui-action-tabset ul.ui-tabs-nav .ui-state-active a.ui-tabs-anchor { background: transparent url(../images/sprites-32x32/arrow_up_lighter.png) no-repeat right top; } .cms .cms-content-actions .Actions .action-menus.ss-ui-action-tabset ul.ui-tabs-nav .ui-state-active a.ui-tabs-anchor:hover { background: transparent url(../images/sprites-32x32/arrow_up_darker.png) no-repeat right top; } .cms .cms-content-actions .Actions .action-menus.ss-ui-action-tabset .ui-tabs-panel button.ss-ui-button { width: 190px; /* Width 100% not calculating by ie7 */ } - -/* Tempory fix as jquery loads too slow to add icons */ -button.ui-button-text-icon-primary { padding-left: 30px !important; } -button.ui-button-text-icon-primary span.ui-button-icon-primary { position: absolute !important; top: 5px !important; left: 8px !important; } -button.ui-button-text-icon-primary .ui-button-text { margin-left: 0 !important; } diff --git a/admin/css/ie8.css b/admin/css/ie8.css index dfdd196a4..569c11ab2 100644 --- a/admin/css/ie8.css +++ b/admin/css/ie8.css @@ -42,12 +42,13 @@ .filter-buttons button.ss-gridfield-button-filter { background-position: -18px 4px !important; } /* Alternative styles for the switch in old IE */ -fieldset.switch-states { padding: 0; } -fieldset.switch-states .switch { padding: 0 10px 0 0; } +fieldset.switch-states { padding-right: 5px; } +fieldset.switch-states .switch { padding: 0; width: 132%; left: -32px; } fieldset.switch-states .switch label { overflow: visible; text-overflow: visible; white-space: normal; padding: 0; } fieldset.switch-states .switch label.active { color: #fff; background-color: #2b9c32; } -fieldset.switch-states .switch label span { display: inline; padding: 0 10px; padding-right: 15px; overflow: visible; text-overflow: visible; white-space: wrap; } +fieldset.switch-states .switch label span { display: inline; padding: 0 8px; overflow: visible; text-overflow: visible; white-space: wrap; } fieldset.switch-states .switch .slide-button { display: none; } +fieldset.switch-states .switch input.state-name { margin-left: -20px; } /* Hide size controls in IE - they won't work as intended */ .cms-content-controls .preview-size-selector { display: none; } diff --git a/admin/css/screen.css b/admin/css/screen.css index b19bcdfe5..fb0ade240 100644 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -209,9 +209,7 @@ form.small .field input.text, form.small .field textarea, form.small .field sele .cms input.loading, .cms button.loading, .cms input.ui-state-default.loading, .cms .ui-widget-content input.ui-state-default.loading, .cms .ui-widget-header input.ui-state-default.loading { color: #525252; border-color: #d5d3d3; cursor: default; } .cms input.loading .ui-icon, .cms button.loading .ui-icon, .cms input.ui-state-default.loading .ui-icon, .cms .ui-widget-content input.ui-state-default.loading .ui-icon, .cms .ui-widget-header input.ui-state-default.loading .ui-icon { background: transparent url(../../images/network-save.gif) no-repeat 0 0; } .cms input.loading.ss-ui-action-constructive .ui-icon, .cms button.loading.ss-ui-action-constructive .ui-icon { background: transparent url(../../images/network-save-constructive.gif) no-repeat 0 0; } -.cms .ss-ui-button { font-size: 12px; margin-top: 0px; padding: 5px 10px; font-weight: bold; text-decoration: none; line-height: 16px; color: #393939; border: 1px solid #c0c0c2; border-bottom: 1px solid #a6a6a9; cursor: pointer; background-color: #e6e6e6; white-space: nowrap; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -o-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); text-shadow: white 0 1px 1px; /* constructive */ /* destructive */ } -.cms .ss-ui-button .ui-icon, .cms .ss-ui-button .ui-button-text { display: inline-block; line-height: 16px; padding: 0; } -.cms .ss-ui-button .ui-icon { width: 16px; padding: 0 2px; position: relative; left: -2px; margin-top: 0; top: 0; height: 16px; float: left; } +.cms .ss-ui-button { margin-top: 0px; font-weight: bold; text-decoration: none; line-height: 16px; color: #393939; border: 1px solid #c0c0c2; border-bottom: 1px solid #a6a6a9; cursor: pointer; background-color: #e6e6e6; white-space: nowrap; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background: -webkit-linear-gradient(#ffffff, #d9d9d9); background: -moz-linear-gradient(#ffffff, #d9d9d9); background: -o-linear-gradient(#ffffff, #d9d9d9); background: linear-gradient(#ffffff, #d9d9d9); text-shadow: white 0 1px 1px; /* constructive */ /* destructive */ } .cms .ss-ui-button.ui-state-hover, .cms .ss-ui-button:hover { text-decoration: none; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -o-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -webkit-box-shadow: 0 0 5px #b3b3b3; -moz-box-shadow: 0 0 5px #b3b3b3; box-shadow: 0 0 5px #b3b3b3; } .cms .ss-ui-button:active, .cms .ss-ui-button:focus, .cms .ss-ui-button.ui-state-active, .cms .ss-ui-button.ui-state-focus { border: 1px solid #b3b3b3; background-color: white; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2U2ZTZlNiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #e6e6e6)); background: -webkit-linear-gradient(#ffffff, #e6e6e6); background: -moz-linear-gradient(#ffffff, #e6e6e6); background: -o-linear-gradient(#ffffff, #e6e6e6); background: linear-gradient(#ffffff, #e6e6e6); -webkit-box-shadow: 0 0 5px #b3b3b3 inset; -moz-box-shadow: 0 0 5px #b3b3b3 inset; box-shadow: 0 0 5px #b3b3b3 inset; } .cms .ss-ui-button.ss-ui-action-constructive { text-shadow: none; font-weight: bold; color: white; border-color: #1f9433; border-bottom-color: #166a24; background-color: #1f9433; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzkzYmU0MiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzFmOTQzMyIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #93be42), color-stop(100%, #1f9433)); background: -webkit-linear-gradient(#93be42, #1f9433); background: -moz-linear-gradient(#93be42, #1f9433); background: -o-linear-gradient(#93be42, #1f9433); background: linear-gradient(#93be42, #1f9433); text-shadow: #1c872f 0 -1px -1px; } @@ -373,10 +371,10 @@ body.cms { overflow: hidden; } /** -------------------------------------------- Tabs -------------------------------------------- */ .ui-tabs { padding: 0; background: none; } .ui-tabs .ui-tabs { position: static; } -.ui-tabs .ui-tabs-panel { padding: 8px 0; background: transparent; border: 0; } +.ui-tabs .ui-tabs-panel { padding: 16px; background: transparent; border: 0; } .ui-tabs .ui-tabs-panel.cms-edit-form { padding: 0; } .ui-tabs .ui-widget-header { border: 0; background: none; } -.ui-tabs .ui-tabs-nav { float: right; margin: 0 0 -1px 0; padding: 0 12px 0 0; border-bottom: none; } +.ui-tabs .ui-tabs-nav { float: right; margin: 16px 0 -1px 0; padding: 0 12px 0 0; border-bottom: none; } .ui-tabs .ui-tabs-nav ~ .ui-tabs-panel { border-top: 1px solid #c0c0c2; clear: both; } .ui-tabs .ui-tabs-nav li { top: 0; float: left; border-bottom: 0 !important; } .ui-tabs .ui-tabs-nav li a { display: -moz-inline-stack; display: inline-block; vertical-align: middle; *vertical-align: auto; zoom: 1; *display: inline; float: none; font-weight: bold; color: #444444; line-height: 32px; padding: 0 16px 0; } @@ -398,20 +396,13 @@ body.cms { overflow: hidden; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.gallery.ui-state-active a { background: url('../images/sprites-64x64-s88957ee578.png') 0 -54px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.edit.ui-state-active a { background: url('../images/sprites-64x64-s88957ee578.png') 0 -404px no-repeat; } .ui-tabs .ui-tabs-nav li.cms-tabset-icon.search.ui-state-active a { background: url('../images/sprites-64x64-s88957ee578.png') 0 -104px no-repeat; } -.ui-tabs .cms-edit-form, .ui-tabs .cms-content-fields { /*not sure if .cms-content-fields effects other areas*/ } -.ui-tabs .cms-edit-form .cms-panel-padded, .ui-tabs .cms-content-fields .cms-panel-padded { /* Has padded area inside it */ padding: 0; margin: 0; } -.ui-tabs .cms-edit-form .ui-tabs-panel, .ui-tabs .cms-edit-form .ss-gridfield, .ui-tabs .cms-content-fields .ui-tabs-panel, .ui-tabs .cms-content-fields .ss-gridfield { margin: 12px; padding: 0 0 12px; } -.ui-tabs .cms-edit-form .ui-tabs-panel .ss-gridfield, .ui-tabs .cms-edit-form .ss-gridfield .ss-gridfield, .ui-tabs .cms-content-fields .ui-tabs-panel .ss-gridfield, .ui-tabs .cms-content-fields .ss-gridfield .ss-gridfield { /* Files area & inside second level tabs */ padding: 0; /* should be zero ideally */ margin: 0 0 12px; } -.ui-tabs .cms-edit-form .ui-tabs-nav, .ui-tabs .cms-content-fields .ui-tabs-nav { margin: 10px 12px 0; padding: 0 8px 0 0; /* second set of tabs */ } -.ui-tabs .cms-edit-form #tree_actions .ui-tabs-nav, .ui-tabs .cms-content-fields #tree_actions .ui-tabs-nav { margin: 0; } -.ui-tabs .cms-panel-padded h3 { margin-left: 12px; /* reports headers, probably too specific */ } -.ui-tabs .cms-panel-padded .ui-tabs-panel { margin: 0; padding: 12px 12px 12px; } +.ui-tabs .cms-panel-padded .ui-tabs-panel { padding: 0; } .ui-tabs .cms-panel-padded .ui-tabs-panel .ui-tabs-panel { padding: 8px 0 0 0; } -.ui-tabs .ui-tabs .ui-tabs-panel { /* second level tabs */ padding-top: 8px; } +.ui-tabs .cms-panel-padded .Actions { padding: 0; } .ui-tabs.ss-tabset-tabshidden .ui-tabs-panel { border-top: none; } /** Primary styles which sit on top of screen, with different tab colors. TODO Only use one "primary" selector and fix HTMLEditorField TabSet addExtraClass() */ -.ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { border-left: 1px solid #b3b3b3; } +.ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { margin-top: 0; border-left: 1px solid #b3b3b3; float: none; } .ui-tabs.cms-tabset-primary .ui-tabs-nav li, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li { margin-right: 0; margin-top: 0; } .ui-tabs.cms-tabset-primary .ui-tabs-nav li a, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary li a, .ui-tabs .cms-content-header-tabs .ui-tabs-nav li a { margin: 0; line-height: 39px; } .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-all, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-top, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-right, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tr, .ui-tabs.cms-tabset-primary .ui-tabs-nav .ui-corner-tl, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-all, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-top, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-right, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tr, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary .ui-corner-tl, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-all, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-top, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-right, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tr, .ui-tabs .cms-content-header-tabs .ui-tabs-nav .ui-corner-tl { border-radius: 0; } @@ -444,7 +435,8 @@ body.cms { overflow: hidden; } .message.good { background-color: #eaf6e4; border-color: #72c34b; } .message p { margin: 0; } -p.message { margin-bottom: 12px; } +.cms-edit-form .message { margin: 16px; } +.cms-edit-form .ui-tabs-panel .message { margin: 0 0 16px 0; } /** -------------------------------------------- Page icons -------------------------------------------- */ .page-icon, a .jstree-pageicon { display: block; width: 16px; height: 16px; background: transparent url(../images/sitetree_ss_pageclass_icons_default.png) no-repeat; } @@ -477,7 +469,7 @@ p.message { margin-bottom: 12px; } #PageType ul li .description { font-style: italic; } /** -------------------------------------------- Content toolbar -------------------------------------------- */ -.cms-content-toolbar { min-height: 29px; display: block; margin: 0 0 15px 0; padding-bottom: 9px; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); *zoom: 1; /* smaller treedropdown */ } +.cms-content-toolbar { min-height: 29px; display: block; margin: 0 0 8px 0; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); *zoom: 1; /* smaller treedropdown */ } .cms-content-toolbar:after { content: "\0020"; display: block; height: 0; clear: both; overflow: hidden; visibility: hidden; } .cms-content-toolbar .cms-tree-view-modes { float: right; padding-top: 5px; } .cms-content-toolbar .cms-tree-view-modes * { display: inline-block; } @@ -491,10 +483,10 @@ p.message { margin-bottom: 12px; } .cms-content-toolbar .ss-ui-button { margin-bottom: 8px; } /* -------------------------------------------------------- Content Tools is the sidebar on the left of the main content panel */ -.cms-content-tools { background: #eceff1; width: 192px; overflow-y: auto; overflow-x: hidden; z-index: 70; border-right: 1px solid #c0c0c2; -webkit-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); -moz-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); float: left; position: relative; } +.cms-content-tools { background: #eceff1; width: 200px; overflow-y: auto; overflow-x: hidden; z-index: 70; border-right: 1px solid #c0c0c2; -webkit-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); -moz-box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); box-shadow: rgba(248, 248, 248, 0.9) -1px 0 0 inset, 0 0 1px rgba(201, 205, 206, 0.8); float: left; position: relative; } .cms-content-tools.filter { padding: 0 !important; } .cms-content-tools .cms-panel-header { clear: both; margin: 0 0 7px; line-height: 24px; border-bottom: 1px solid #d0d3d5; -webkit-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -moz-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); -o-box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); box-shadow: 0 1px 0 rgba(248, 248, 248, 0.9); } -.cms-content-tools .cms-panel-content { width: 176px; padding: 8px 8px 0; overflow: auto; height: 100%; } +.cms-content-tools .cms-panel-content { width: 184px; padding: 16px 8px 0; overflow: auto; height: 100%; } .cms-content-tools .cms-panel-content .Actions .ss-ui-action-constructive { margin-right: 5px; } .cms-content-tools .cms-content-header { background-color: #748d9d; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2IwYmVjNyIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzc0OGQ5ZCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b0bec7), color-stop(100%, #748d9d)); background-image: -webkit-linear-gradient(#b0bec7, #748d9d); background-image: -moz-linear-gradient(#b0bec7, #748d9d); background-image: -o-linear-gradient(#b0bec7, #748d9d); background-image: linear-gradient(#b0bec7, #748d9d); } .cms-content-tools .cms-content-header h2 { text-shadow: #5c7382 -1px -1px 0; width: 176px; color: white; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; } @@ -519,7 +511,7 @@ p.message { margin-bottom: 12px; } .cms-content-tools td { border-bottom: 1px solid #ced7dc; padding: 7px 2px; font-size: 11px; } /** CMS Batch actions */ -.cms-content-batchactions { float: left; position: relative; display: block; margin-left: 8px; } +.cms-content-batchactions { float: left; position: relative; display: block; } .cms-content-batchactions .view-mode-batchactions-wrapper { float: left; padding: 4px 6px; border: 1px solid #aaa; margin-bottom: 8px; background-color: #D9D9D9; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmZmZmZiIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2Q5ZDlkOSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JhZCkiIC8+PC9zdmc+IA=='); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #ffffff), color-stop(100%, #d9d9d9)); background-image: -webkit-linear-gradient(top, #ffffff, #d9d9d9); background-image: -moz-linear-gradient(top, #ffffff, #d9d9d9); background-image: -o-linear-gradient(top, #ffffff, #d9d9d9); background-image: linear-gradient(top, #ffffff, #d9d9d9); border-top-left-radius: 4px; border-bottom-left-radius: 4px; } .cms-content-batchactions .view-mode-batchactions-wrapper label { display: none; } .cms-content-batchactions .view-mode-batchactions-wrapper fieldset, .cms-content-batchactions .view-mode-batchactions-wrapper .Actions { display: inline-block; } @@ -540,7 +532,7 @@ p.message { margin-bottom: 12px; } /** -------------------------------------------- Member Profile -------------------------------------------- */ form.member-profile-form { padding: 0 16px 0 0; } form.member-profile-form #Root_Permissions { clear: both; border-top: 1px solid #a6a6a6; } -form.member-profile-form #Root_Main { clear: both; border-top: 1px solid #a6a6a6; padding-top: 16px; } +form.member-profile-form #Root_Main { clear: both; border-top: 1px solid #a6a6a6; } form.member-profile-form #Root_Main .cms-help-toggle { text-indent: -9999em; display: inline-block; width: 20px; background: url(../images/question.png) no-repeat 0px 0px; } form.member-profile-form #FavouritePageID { margin-top: 8px; } form.member-profile-form #CsvFile .middleColumn { background: none !important; } @@ -597,6 +589,8 @@ form.member-profile-form #Permissions .optionset li { float: none; width: auto; .cms-panel .collapsed-flyout { display: block !important; left: 41px; margin-top: -40px; position: fixed; width: 191px; } .cms-panel .collapsed-flyout li a span { display: block !important; } +.cms .cms-panel-padded { padding: 16px 16px; margin: 0; } + /** ------------------------------------------------------------------ * Dialog * @@ -719,11 +713,10 @@ form.import-form label.left { width: 250px; } .cms .jstree > ul > li, .TreeDropdownField .treedropdownfield-panel .jstree > ul > li { margin-left: 0px; } .cms .jstree ul, .cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree ul, .TreeDropdownField .treedropdownfield-panel .jstree li { display: block; margin: 0; padding: 0; background: none; list-style-type: none; } .cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { min-height: 18px; line-height: 25px; white-space: nowrap; margin-left: 18px; min-width: 18px; } -.cms .jstree li ins.jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li ins.jstree-icon { display: none; } .cms .jstree li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-open > ul { display: block; } .cms .jstree li.jstree-closed > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-closed > ul { display: none; } .cms .jstree li.disabled > a, .TreeDropdownField .treedropdownfield-panel .jstree li.disabled > a { color: #aaaaaa; } -.cms .jstree li ul ins.jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li ul ins.jstree-icon { display: block; } +.cms .jstree li > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li > .jstree-icon { cursor: pointer; } .cms .jstree ins, .TreeDropdownField .treedropdownfield-panel .jstree ins { display: inline-block; text-decoration: none; width: 18px; height: 18px; margin: 0 0 0 0; padding: 0; float: left; } .cms .jstree a, .TreeDropdownField .treedropdownfield-panel .jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; border: 1px solid #fff; } .cms .jstree a:focus, .cms .jstree a:active, .cms .jstree a:hover, .TreeDropdownField .treedropdownfield-panel .jstree a:focus, .TreeDropdownField .treedropdownfield-panel .jstree a:active, .TreeDropdownField .treedropdownfield-panel .jstree a:hover { outline: none; text-decoration: none; cursor: pointer; text-shadow: none; } @@ -744,7 +737,7 @@ form.import-form label.left { width: 250px; } .cms .jstree-apple.jstree-focused .jstree-apple > ul, .TreeDropdownField .treedropdownfield-panel .jstree-apple.jstree-focused .jstree-apple > ul { background: none; } .cms a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel a > .jstree-icon { display: none; } .cms .draggable a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .draggable a > .jstree-icon { display: block; } -.cms li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel li.jstree-open > ul { display: block; margin-left: -18px; } +.cms li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel li.jstree-open > ul { display: block; margin-left: -13px; } .cms li.jstree-open > ul li ul, .TreeDropdownField .treedropdownfield-panel li.jstree-open > ul li ul { margin-left: 2px; } .cms li.jstree-closed > ul, .TreeDropdownField .treedropdownfield-panel li.jstree-closed > ul { display: none; } .cms .jstree-rtl a > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree-rtl a > .jstree-icon { margin-left: 3px; margin-right: 0; } @@ -921,6 +914,7 @@ li.class-ErrorPage > a a .jstree-pageicon { background-position: 0 -112px; } /* Styling for the preview screen sizes */ .cms-preview { background-color: #eceff1; height: 100%; width: 100%; } +.cms-preview .cms-preview-overlay { width: 100%; height: 100%; } .cms-preview .preview-note { color: #CDD7DC; display: block; font-size: 22px; font-weight: bold; height: 82px; margin-top: -50px; margin-left: -150px; /* half of width */ position: absolute; text-align: center; text-shadow: 0 1px 0 #fff; top: 50%; left: 50%; width: 300px; } .cms-preview .preview-note span { background: url('../images/sprites-64x64-s88957ee578.png') 0 0 no-repeat; display: block; height: 41px; margin: 0 auto 20px; width: 50px; } .cms-preview .preview-scroll { height: 100%; overflow: auto; position: relative; width: 100%; } diff --git a/admin/javascript/LeftAndMain.FieldHelp.js b/admin/javascript/LeftAndMain.FieldHelp.js index d5e05c980..60ada3097 100644 --- a/admin/javascript/LeftAndMain.FieldHelp.js +++ b/admin/javascript/LeftAndMain.FieldHelp.js @@ -20,7 +20,7 @@ descriptionEl.remove(); } } - }); + }); $(".cms .field.cms-description-tooltip :input").entwine({ onfocusin: function(e) { @@ -28,8 +28,8 @@ }, onfocusout: function(e) { this.closest('.field').tooltip('close'); - } + } }); - }); + }); }(jQuery)); diff --git a/admin/javascript/LeftAndMain.Preview.js b/admin/javascript/LeftAndMain.Preview.js index d4c01419d..c0ef8e464 100644 --- a/admin/javascript/LeftAndMain.Preview.js +++ b/admin/javascript/LeftAndMain.Preview.js @@ -424,6 +424,7 @@ * Reacts to the user changing the preview mode. */ onchange: function(e) { + this._super(e); e.preventDefault(); var targetStateName = $(this).val(); @@ -523,38 +524,36 @@ 'onliszt:showing_dropdown': function() { this.siblings().find('.chzn-drop').addClass('open')._alignRight(); }, + 'onliszt:hiding_dropdown': function() { this.siblings().find('.chzn-drop').removeClass('open')._removeRightAlign(); - }, + }, + + /** + * Trigger additional initial icon update when the control is fully loaded. + * Solves an IE8 timing issue. + */ + 'onliszt:ready': function() { + this._super(); + this._addIcon(); + }, + _addIcon: function(){ var selected = this.find(':selected'); var iconClass = selected.attr('data-icon'); var target = this.parent().find('.chzn-container a.chzn-single'); var oldIcon = target.attr('data-icon'); - if(oldIcon != undefined){ + if(typeof oldIcon !== 'undefined'){ target.removeClass(oldIcon); } target.addClass(iconClass); target.attr('data-icon', iconClass); + + return this; } }); - /* - * When chzn initiated run select addIcon - * Apply description text if applicable - */ - $('.preview-selector a.chzn-single').entwine({ - onmatch: function() { - this.closest('.preview-selector').find('select')._addIcon(); - this._super(); - }, - onunmatch: function() { - this._super(); - } - }); - - $('.preview-selector .chzn-drop').entwine({ _alignRight: function(){ var that = this; diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 9d9141b0f..63ef139f0 100644 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -5,6 +5,11 @@ jQuery.noConflict(); */ (function($) { + window.onresize = function(e) { + // Entwine's 'fromWindow::onresize' does not trigger on IE8. Use synthetic event. + $('.cms-container').trigger('windowresize'); + }; + // setup jquery.entwine $.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE; $.entwine('ss', function($) { @@ -28,7 +33,7 @@ jQuery.noConflict(); disable_search_threshold: 20 }); - var title = el.prop('title') + var title = el.prop('title'); if(title) { el.siblings('.chzn-container').prop('title', title); @@ -144,8 +149,11 @@ jQuery.noConflict(); }, fromWindow: { - onstatechange: function(){ this.handleStateChange(); }, - onresize: function(){ this.redraw(); } + onstatechange: function(){ this.handleStateChange(); } + }, + + 'onwindowresize': function() { + this.redraw(); }, 'from .cms-panel': { @@ -442,6 +450,12 @@ jQuery.noConflict(); handleAjaxResponse: function(data, status, xhr) { var self = this, url, selectedTabs, guessFragment; + // Support a full reload + if(xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) { + document.location.href = xhr.getResponseHeader('X-ControllerURL'); + return; + } + // Pseudo-redirects via X-ControllerURL might return empty data, in which // case we'll ignore the response if(!data) return; @@ -454,7 +468,7 @@ jQuery.noConflict(); // Update title var title = xhr.getResponseHeader('X-Title'); - if(title) document.title = title; + if(title) document.title = decodeURIComponent(title.replace(/\+/g, ' ')); var newFragments = {}, newContentEls; // If content type is text/json (ignoring charset and other parameters) @@ -569,7 +583,7 @@ jQuery.noConflict(); * Requires HTML5 sessionStorage support. */ saveTabState: function() { - if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage == null) return; + if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage === null) return; var selectedTabs = [], url = this._tabStateUrl(); this.find('.cms-tabset,.ss-tabset').each(function(i, el) { @@ -605,7 +619,7 @@ jQuery.noConflict(); * Requires HTML5 sessionStorage support. */ restoreTabState: function() { - if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage == null) return; + if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage === null) return; var self = this, url = this._tabStateUrl(), data = window.sessionStorage.getItem('tabs-' + url), @@ -675,7 +689,7 @@ jQuery.noConflict(); this._super(); }, onremove: function() { - this.button('destroy'); + if(this.data('button')) this.button('destroy'); this._super(); } }); @@ -727,7 +741,7 @@ jQuery.noConflict(); var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText; try { - if (typeof msg != "undefined" && msg != null) eval(msg); + if (typeof msg != "undefined" && msg !== null) eval(msg); } catch(e) {} diff --git a/admin/javascript/jquery-changetracker/lib/jquery.changetracker.js b/admin/javascript/jquery-changetracker/lib/jquery.changetracker.js index 0aa5d18f4..854668b40 100644 --- a/admin/javascript/jquery-changetracker/lib/jquery.changetracker.js +++ b/admin/javascript/jquery-changetracker/lib/jquery.changetracker.js @@ -57,20 +57,42 @@ var onchange = function(e) { var $field = $(e.target); - var origVal = $field.data('changetracker.origVal'); - if(origVal === null || e.target.value != origVal) { - // TODO Also add class to radiobutton/checkbox siblings + var origVal = $field.data('changetracker.origVal'), newVal; + + // Determine value based on field type + if($field.is(':checkbox')) { + newVal = $field.is(':checked') ? 1 : 0; + } else { + newVal = $field.val(); + } + + // Determine changed state based on value comparisons + if(origVal === null || newVal != origVal) { $field.addClass(options.changedCssClass); self.addClass(options.changedCssClass); + } else { + $field.removeClass(options.changedCssClass); + // Unset changed state on all radio buttons of the same name + if($field.is(':radio')) { + self.find(':radio[name=' + $field.attr('name') + ']').removeClass(options.changedCssClass); + } + // Only unset form state if no other fields are changed as well + if(!self.getFields().filter('.' + options.changedCssClass).length) { + self.removeClass(options.changedCssClass); + } } }; // setup original values - var fields = this.getFields(); + var fields = this.getFields(), origVal; fields.filter(':radio,:checkbox').bind('click.changetracker', onchange); fields.not(':radio,:checkbox').bind('change.changetracker', onchange); fields.each(function() { - var origVal = $(this).is(':radio,:checkbox') ? self.find(':input[name=' + $(this).attr('name') + ']:checked').val() : $(this).val(); + if($(this).is(':radio,:checkbox')) { + origVal = self.find(':input[name=' + $(this).attr('name') + ']:checked').val(); + } else { + origVal = $(this).val(); + } $(this).data('changetracker.origVal', origVal); }); diff --git a/admin/scss/_forms.scss b/admin/scss/_forms.scss index 79c68631c..ee53c3033 100644 --- a/admin/scss/_forms.scss +++ b/admin/scss/_forms.scss @@ -361,9 +361,7 @@ form.small .field, .field.small { } .ss-ui-button { - font-size: 12px; margin-top:0px; - padding: 5px 10px; font-weight: bold; text-decoration: none; line-height: $grid-y * 2; @@ -374,22 +372,6 @@ form.small .field, .field.small { background-color: $color-button-generic; white-space: nowrap; - .ui-icon, .ui-button-text { - display: inline-block; - line-height: $grid-x*2; - padding: 0; - } - .ui-icon { - width: 16px; - padding: 0 2px; - position: relative; - left: -2px; - margin-top: 0; - top: 0; - height: 16px; - float: left; - } - @include background( linear-gradient(color-stops( lighten($color-button-generic, 10%), diff --git a/admin/scss/_ieShared.scss b/admin/scss/_ieShared.scss index 2c989544f..342344de9 100644 --- a/admin/scss/_ieShared.scss +++ b/admin/scss/_ieShared.scss @@ -142,9 +142,11 @@ /* Alternative styles for the switch in old IE */ fieldset.switch-states{ - padding:0; + padding-right: 5px; .switch{ - padding:0 10px 0 0; + padding: 0; + width: 100%+32; + left: -32px; label{ overflow:visible; text-overflow:visible; @@ -156,8 +158,7 @@ fieldset.switch-states{ } span{ display:inline; - padding:0 10px; - padding-right:15px; + padding:0 8px; overflow:visible; text-overflow:visible; white-space:wrap; @@ -166,6 +167,9 @@ fieldset.switch-states{ .slide-button{ display:none; } + input.state-name { + margin-left: -20px; + } } } /* Hide size controls in IE - they won't work as intended */ diff --git a/admin/scss/_preview.scss b/admin/scss/_preview.scss index 8aae9d4f6..483911a22 100644 --- a/admin/scss/_preview.scss +++ b/admin/scss/_preview.scss @@ -214,6 +214,11 @@ height: 100%; width: 100%; + .cms-preview-overlay { + width: 100%; + height: 100%; + } + .preview-note { color: #CDD7DC; display: block; diff --git a/admin/scss/_style.scss b/admin/scss/_style.scss index 31265f62c..2b2482229 100644 --- a/admin/scss/_style.scss +++ b/admin/scss/_style.scss @@ -159,7 +159,7 @@ body.cms { } .ui-tabs-panel { - padding: $grid-x 0; + padding: $grid-x*2; background: transparent; // default it's white border: 0; // suppress default borders &.cms-edit-form { @@ -174,7 +174,7 @@ body.cms { .ui-tabs-nav { float: right; - margin: 0 0 -1px 0; + margin: $grid-x*2 0 -1px 0; padding: 0 $grid-x*1.5 0 0; border-bottom: none; @@ -250,43 +250,17 @@ body.cms { } } - .cms-edit-form, .cms-content-fields { /*not sure if .cms-content-fields effects other areas*/ - .cms-panel-padded { /* Has padded area inside it */ - padding: 0; - margin: 0; - } - .ui-tabs-panel, .ss-gridfield { - margin: 12px; - padding: 0 0 12px; - .ss-gridfield { /* Files area & inside second level tabs */ - padding: 0; /* should be zero ideally */ - margin: 0 0 12px; - } - } - .ui-tabs-nav { - margin: 10px 12px 0; - padding: 0 8px 0 0; /* second set of tabs */ - } - #tree_actions .ui-tabs-nav{ - margin: 0; - } - } - .cms-panel-padded { - h3 { - margin-left: 12px; /* reports headers, probably too specific */ - } .ui-tabs-panel { - margin: 0; - padding: 12px 12px 12px; + padding: 0; // Avoid double padding with parent .ui-tabs-panel { padding: $grid-x 0 0 0; } } - } - .ui-tabs .ui-tabs-panel { /* second level tabs */ - padding-top: 8px; + .Actions { + padding: 0; // Avoid double padding with parent + } } &.ss-tabset-tabshidden .ui-tabs-panel { @@ -301,8 +275,10 @@ body.cms { .ui-tabs.cms-tabset-primary .ui-tabs-nav, .ui-tabs .ui-tabs-nav.cms-tabset-nav-primary, .ui-tabs .cms-content-header-tabs .ui-tabs-nav { + margin-top: 0; border-left: 1px solid darken($color-tab, 15%); - + float: none; // parent container is already right floated + li { margin-right: 0; // tabs are directly adjacent margin-top: 0; @@ -482,8 +458,17 @@ body.cms { margin: 0; } } -p.message { - margin-bottom: $grid-y*1.5; + + +.cms-edit-form { + .message { + margin: $grid-x*2; // TODO Remove double padding when adjacent to a padded tabs panel + } + .ui-tabs-panel { + .message { + margin: 0 0 $grid-x*2 0; // gets padding from tab panel + } + } } /** -------------------------------------------- @@ -609,8 +594,7 @@ p.message { .cms-content-toolbar { min-height: 29px; display: block; - margin: 0 0 15px 0; - padding-bottom: 9px; + margin: 0 0 $grid-y 0; @include doubleborder(bottom, $color-light-separator, $box-shadow-shine); @include legacy-pie-clearfix(); @@ -681,7 +665,7 @@ p.message { */ .cms-content-tools { background: $tab-panel-texture-color; - width: $grid-x * 24; + width: $grid-x * 25; overflow-y: auto; overflow-x: hidden; z-index: 70; @@ -703,8 +687,8 @@ p.message { } .cms-panel-content { - width: ($grid-x * 22); - padding: $grid-x $grid-x 0; + width: ($grid-x * 23); + padding: $grid-x*2 $grid-x 0; // smaller left/right padding to use space efficiently overflow: auto; height:100%; @@ -837,7 +821,6 @@ p.message { float: left; position: relative; display: block; - margin-left: 8px; .view-mode-batchactions-wrapper { float: left; @@ -925,7 +908,6 @@ form.member-profile-form { #Root_Main { clear:both; border-top: 1px solid darken($color-tab, 20%); - padding-top:$grid-y*2; .cms-help-toggle { text-indent: -9999em; display: inline-block; @@ -1211,6 +1193,13 @@ form.member-profile-form { } } +.cms { + .cms-panel-padded { + padding: $grid-y*2 $grid-x*2; + margin:0; + } +} + /** ------------------------------------------------------------------ * Dialog * diff --git a/admin/scss/_tree.scss b/admin/scss/_tree.scss index 5f67260a7..8d938ca5e 100644 --- a/admin/scss/_tree.scss +++ b/admin/scss/_tree.scss @@ -24,9 +24,6 @@ white-space: nowrap; margin-left: 18px; min-width: 18px; - ins.jstree-icon { - display: none; - } &.jstree-open > ul { display: block; } @@ -36,10 +33,9 @@ &.disabled > a { color: #aaaaaa; } - ul { - ins.jstree-icon { - display: block; - } + // Expand/collapse arrows + & > .jstree-icon { + cursor: pointer; } } ins { @@ -158,7 +154,7 @@ li.jstree-open > ul { display: block; - margin-left: -18px; + margin-left: -13px; li ul { margin-left:2px; } diff --git a/admin/scss/ie7.scss b/admin/scss/ie7.scss index 4c5833e40..dba1f8290 100644 --- a/admin/scss/ie7.scss +++ b/admin/scss/ie7.scss @@ -303,17 +303,3 @@ table.ss-gridfield-table { width: 190px; /* Width 100% not calculating by ie7 */ } } - - -/* Tempory fix as jquery loads too slow to add icons */ -button.ui-button-text-icon-primary { - padding-left: 30px !important; - span.ui-button-icon-primary { - position: absolute !important; - top: 5px !important; - left: 8px !important; - } - .ui-button-text { - margin-left: 0 !important; - } -} diff --git a/admin/templates/Includes/LeftAndMain_SilverStripeNavigator.ss b/admin/templates/Includes/LeftAndMain_SilverStripeNavigator.ss index e75488f2d..c80f555f5 100644 --- a/admin/templates/Includes/LeftAndMain_SilverStripeNavigator.ss +++ b/admin/templates/Includes/LeftAndMain_SilverStripeNavigator.ss @@ -19,25 +19,27 @@ - <% if Items.Count < 5 %> -
- <% else %> - - - + <% if Items %> + <% if Items.Count < 5 %> + + <% else %> + + + + <% end_if %> <% end_if %> \ No newline at end of file diff --git a/control/HTTP.php b/control/HTTP.php index 71a4d6362..6621a7246 100644 --- a/control/HTTP.php +++ b/control/HTTP.php @@ -60,20 +60,25 @@ class HTTP { if(!is_numeric($tag)) $tagPrefix = "$tag "; else $tagPrefix = ""; - $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/ie"; - $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/ie"; - $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/ie"; + $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/i"; + $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/i"; + $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/i"; } - $regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/ie'; - $regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/ie'; - $regExps[] = '/(list-style-image:[^;]*url *\()([^)]+)(\))/ie'; - $regExps[] = '/(list-style:[^;]*url *\()([^)]+)(\))/ie'; + $regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/i'; + $regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/i'; + $regExps[] = '/(list-style-image:[^;]*url *\()([^)]+)(\))/i'; + $regExps[] = '/(list-style:[^;]*url *\()([^)]+)(\))/i'; // Make - $code = 'stripslashes("$1") . (' . str_replace('$URL', 'stripslashes("$2")', $code) . ') . stripslashes("$3")'; - + $callback = function($matches) use($code) { + return + stripslashes($matches[1]) . + str_replace('$URL', stripslashes($matches[2]), $code) . + stripslashes($matches[3]); + }; + foreach($regExps as $regExp) { - $content = preg_replace($regExp, $code, $content); + $content = preg_replace_callback($regExp, $callback, $content); } return $content; diff --git a/core/Convert.php b/core/Convert.php index 3df0b37b7..94c6234c0 100644 --- a/core/Convert.php +++ b/core/Convert.php @@ -244,9 +244,12 @@ class Convert { // Expand hyperlinks if(!$preserveLinks && !$config['PreserveLinks']) { - $data = preg_replace('/]*href\s*=\s*"([^"]*)">(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", - $data); - $data = preg_replace('/]*href\s*=\s*([^ ]*)>(.*?)<\/a>/ie', "Convert::html2raw('\\2').'[\\1]'", $data); + $data = preg_replace_callback('/]*href\s*=\s*"([^"]*)">(.*?)<\/a>/i', function($matches) { + return Convert::html2raw($matches[2]) . "[$matches[1]]"; + }, $data); + $data = preg_replace_callback('/]*href\s*=\s*([^ ]*)>(.*?)<\/a>/i', function($matches) { + return Convert::html2raw($matches[2]) . "[$matches[1]]"; + }, $data); } // Replace images with their alt tags diff --git a/core/Object.php b/core/Object.php index 3fafb7035..8c3ac83c6 100755 --- a/core/Object.php +++ b/core/Object.php @@ -164,12 +164,19 @@ abstract class Object { // Keep track of the current bucket that we're putting data into $bucket = &$args; $bucketStack = array(); + $had_ns = false; foreach($tokens as $token) { $tName = is_array($token) ? $token[0] : $token; // Get the class naem if($class == null && is_array($token) && $token[0] == T_STRING) { $class = $token[1]; + } elseif(is_array($token) && $token[0] == T_NS_SEPARATOR) { + $class .= $token[1]; + $had_ns = true; + } elseif ($had_ns && is_array($token) && $token[0] == T_STRING) { + $class .= $token[1]; + $had_ns = false; // Get arguments } else if(is_array($token)) { switch($token[0]) { diff --git a/css/AssetUploadField.css b/css/AssetUploadField.css index ad090f63c..4881de652 100644 --- a/css/AssetUploadField.css +++ b/css/AssetUploadField.css @@ -11,8 +11,9 @@ Used in side panels and action tabs */ .ss-uploadfield-view-allowed-extensions { padding-top: 20px; clear: both; max-width: 750px; display: block; } +.ss-uploadfield-view-allowed-extensions .toggle { font-style: normal; font-size: 11px; } -#AssetUploadField { border-bottom: 0; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; padding: 12px; } +#AssetUploadField { border-bottom: 0; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } .backlink { padding-left: 12px; } @@ -68,9 +69,7 @@ body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-asse .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info { float: left; margin: 34px 0 0; } .ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info { margin: 15px 0px 0 20px; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-info label { font-size: 16px; line-height: 30px; padding: 5px 16px; } -.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-fromcomputer { /*position: relative; -overflow: hidden; -display: block;*/ } +.ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-fromcomputer { /*position: relative; */ overflow: hidden; display: block; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-uploador { float: left; font-weight: bold; font-size: 22px; padding: 0 20px; line-height: 70px; margin-top: 16px; display: none; } .ss-insert-media .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-item-uploador { font-size: 18px; margin-top: 0; } .ss-assetuploadfield .ss-uploadfield-addfile .ss-uploadfield-dropzone { margin-top: 9px; -webkit-border-radius: 13px; -moz-border-radius: 13px; -ms-border-radius: 13px; -o-border-radius: 13px; border-radius: 13px; -webkit-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #fafafa; -moz-box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #fafafa; box-shadow: rgba(128, 128, 128, 0.4) 0 0 4px 0 inset, 0 1px 0 #fafafa; border: 2px dashed gray; background: #d4dbe0; display: none; height: 82px; width: 360px; float: left; } diff --git a/css/Security_login.css b/css/Security_login.css index 92c56085e..8a3ba03c9 100644 --- a/css/Security_login.css +++ b/css/Security_login.css @@ -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; } diff --git a/css/UploadField.css b/css/UploadField.css index 70e18efca..481d3f6df 100644 --- a/css/UploadField.css +++ b/css/UploadField.css @@ -16,8 +16,8 @@ Used in side panels and action tabs .ss-uploadfield .middleColumn { width: 526px; padding: 0; background: #fff; border: 1px solid #b3b3b3; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #efefef), color-stop(10%, #ffffff), color-stop(90%, #ffffff), color-stop(100%, #efefef)); background-image: -webkit-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -moz-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: -o-linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); background-image: linear-gradient(#efefef, #ffffff 10%, #ffffff 90%, #efefef); } .ss-uploadfield .ss-uploadfield-item { margin: 0; padding: 15px; overflow: auto; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview { height: 60px; line-height: 60px; width: 80px; text-align: center; font-weight: bold; float: left; overflow: hidden; } -.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -webkit-box-shadow: gray 0 0 4px 0 inset; -moz-box-shadow: gray 0 0 4px 0 inset; box-shadow: gray 0 0 4px 0 inset; border: 2px dashed gray; background: #d0d3d5; display: none; } -.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { margin: 0 0 0 100px; } +.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-preview.ss-uploadfield-dropzone { -webkit-box-shadow: gray 0 0 4px 0 inset; -moz-box-shadow: gray 0 0 4px 0 inset; box-shadow: gray 0 0 4px 0 inset; border: 2px dashed gray; background: #d0d3d5; display: none; margin-right: 15px; } +.ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info { float: left; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name { display: block; line-height: 13px; height: 26px; margin: 0; text-align: left; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name b { font-weight: bold; padding: 0 5px 0 0; } .ss-uploadfield .ss-uploadfield-item .ss-uploadfield-item-info .ss-uploadfield-item-name .name { font-size: 11px; color: #848484; width: 290px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; display: inline; float: left; } diff --git a/dev/DevelopmentAdmin.php b/dev/DevelopmentAdmin.php index ac76a27fa..dd458bed3 100644 --- a/dev/DevelopmentAdmin.php +++ b/dev/DevelopmentAdmin.php @@ -79,9 +79,6 @@ class DevelopmentAdmin extends Controller { "build" => "Build/rebuild this environment. Call this whenever you have updated your project sources", "tests" => "See a list of unit tests to run", "tests/all" => "Run all tests", - "tests/startsession" => "Start a test session in your browser" - . " (gives you a temporary database with default content)", - "tests/endsession" => "Ends a test session", "jstests" => "See a list of JavaScript tests to run", "jstests/all" => "Run all JavaScript tests", "tasks" => "See a list of build tasks to run" @@ -191,16 +188,6 @@ Config::inst()->update('Security', 'token', '$token'); TXT; } - public function reset() { - $link = BASE_URL.'/dev/tests/startsession'; - - return "The dev/reset feature has been removed. If you are trying to test your site " . - "with a clean datababase, we recommend that you use " . - "dev/test/startsession ". - "instead.
"; - - } - public function errors() { $this->redirect("Debug_"); } diff --git a/dev/FixtureBlueprint.php b/dev/FixtureBlueprint.php index 0b01458d6..c3cc97c4e 100644 --- a/dev/FixtureBlueprint.php +++ b/dev/FixtureBlueprint.php @@ -129,8 +129,9 @@ class FixtureBlueprint { $parsedItems = array(); $items = preg_split('/ *, */',trim($fieldVal)); foreach($items as $item) { - // Check for correct format: =>You're in the middle of a test session;" - . " click here to end it.
"; - - } else if(!isset($_GET['fixture'])) { - $me = Director::baseURL() . "dev/tests/startsession"; - return << -Enter a fixture file name to start a new test session. Don't forget to visit dev/tests/endsession when - you're done!
-Fixture file (leave blank to start with default set-up):
- - - -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 "Fixture file doesn't exist
"; - } else if(substr($realFile,0,strlen($baseDir)) != $baseDir) { - return "Fixture file must be inside $baseDir
"; - } else if(substr($realFile,-4) != '.yml') { - return "Fixture file must be a .yml file
"; - } else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) { - return "Fixture file must be inside the tests subfolder of one of your modules.
"; - } - } - - $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 "Started testing session with fixture '$fixtureFile'. - Time to start testing; where would you like to start?
- "; - } - - } else { - return "startession can only be used on dev and test sites
"; - } - } - - /** - * 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 "Set database session to '$name'.
"; - } else { - return "Unset database session.
"; - } - - } - - 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 "Re-test the test database with fixture '$fixtureFile'. Time to start testing; where would" - . " you like to start?
"; - - } else { - return "Re-test the test database. Time to start testing; where would you like to start?
"; - } - - } else { - return "dev/tests/emptydb can only be used with a temporary database. Perhaps you should use" - . " dev/tests/startsession first?
"; - } - } - - public function endsession() { - SapphireTest::kill_temp_db(); - DB::set_alternative_database_name(null); - - return "Test session ended.
- "; - } - - 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 "sessionloadyml can only be used on dev and test sites
"; - } - if (!SapphireTest::using_temp_db()) { - return "Please load /dev/tests/startsession first
"; - } - - $fixtureFile = isset($_GET['fixture']) ? $_GET['fixture'] : null; - if (empty($fixtureFile)) { - $me = Director::baseURL() . "/dev/tests/sessionloadyml"; - return << -Enter a fixture file name to load a new YAML fixture into the session.
-Fixture file
- - - -HTML; - } - // Validate fixture file - $realFile = realpath(BASE_PATH.'/'.$fixtureFile); - $baseDir = realpath(Director::baseFolder()); - if(!$realFile || !file_exists($realFile)) { - return "Fixture file doesn't exist
"; - } else if(substr($realFile,0,strlen($baseDir)) != $baseDir) { - return "Fixture file must be inside $baseDir
"; - } else if(substr($realFile,-4) != '.yml') { - return "Fixture file must be a .yml file
"; - } else if(!preg_match('/^([^\/.][^\/]+)\/tests\//', $fixtureFile)) { - return "Fixture file must be inside the tests subfolder of one of your modules.
"; - } - - // Fixture - $fixture = Injector::inst()->create('YamlFixture', $fixtureFile); - $fixture->saveIntoDatabase(); - - return "Loaded fixture '$fixtureFile' into session
"; - } - public function setUp() { // The first DB test will sort out the DB, we don't have to SSViewer::flush_template_cache(); diff --git a/dev/install/install.php5 b/dev/install/install.php5 index 0b6d9b151..d3ec3783e 100644 --- a/dev/install/install.php5 +++ b/dev/install/install.php5 @@ -1281,6 +1281,7 @@ ErrorDocument 500 /assets/error-500.html $baseClause RewriteCond %{REQUEST_URI} ^(.*)$ RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_URI} !\.php$ RewriteRule .* $modulePath/main.php?url=%1&%{QUERY_STRING} [L] TEXT; diff --git a/docs/en/changelogs/3.1.0.md b/docs/en/changelogs/3.1.0.md index a880275c9..2e28f3063 100644 --- a/docs/en/changelogs/3.1.0.md +++ b/docs/en/changelogs/3.1.0.md @@ -2,6 +2,28 @@ ## Overview ## +### CMS + + * "Split view" editing with side-by-side preview of the edited website + * Resizing of preview to common screen widths ("desktop", "tablet" and "smartphone") + * Decluttered "Edit Page" buttons by moving minor actions into a "more options" panel + * Auto-detect CMS changes and highlight the save button for better informancy + * Display "last edited" and "last published" data for pages in CMS + * CMS form fields now support help text through `setDescription()`, both inline and as tooltips + * Removed SiteTree "MetaTitle" and "MetaKeywords" fields + * More legible and simplified tab and menu styling in the CMS + +### Framework + + * `DataList` and `ArrayList` are now immutable, they'll return cloned instances on modification + * Behaviour testing support through [Behat](http://behat.org), with CMS test coverage + (see the [SilverStripe Behat Extension]() for details) + * Removed legacy table APIs (e.g. `TableListField`), use GridField instead + * Editing of relation table data (`$many_many_extraFields`) in `GridField` + * Optional integration with ImageMagick as a new image manipulation backend + * Support for PHP 5.4's built-in webserver + * Support for [Composer](http://getcomposer.org) dependency manager (also works with 3.0) + ## Upgrading ### Grouped CMS Buttons @@ -44,12 +66,46 @@ you'll need to adjust your code. } } +### GridField and ModelAdmin Permission Checks + +`GridFieldDetailForm` now checks for `canEdit()` and `canDelete()` permissions +on your model. `GridFieldAddNewButton` checks `canCreate()`. +The default implementation requires `ADMIN` permissions. +You'll need to loosen those permissions if you want other users with CMS +access to interact with your data. +Since `GridField` is used in `ModelAdmin`, this change will affect both classes. + + Example: Require "CMS: Pages section" access + + :::php + class MyModel extends DataObject { + public function canView($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + public function canEdit($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + public function canDelete($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + public function canCreate($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + +You can also implement [custom permission codes](/topics/permissions). +For 3.1.0 stable, we aim to further simplify the permission definitions, +in order to reduce the boilerplate code required to get a model editable in the CMS. + +Note: GridField is already relying on the permission checks performed +through the CMS controllers, providing a simple level of security. + ### Other * `TableListField`, `ComplexTableField`, `TableField`, `HasOneComplexTableField`, `HasManyComplexTableField` and `ManyManyComplexTableField` have been removed from the core and placed into a module called "legacytablefields" located at https://github.com/silverstripe-labs/legacytablefields * `prototype.js` and `behaviour.js` have been removed from the core, they are no longer used. If you have custom code relying on these two libraries, please update your code to include the files yourself * `Object::has_extension()` and `Object::add_extension()` deprecated in favour of using late static binding, please use `{class}::has_extension()` and `{class}::add_extension()` instead, where {class} is the class name of your DataObject class. - * Removed `SiteTree.MetaTitle` and `SiteTree.MetaKeywords` since they are irrelevant in terms of SEO ([1](http://www.seomoz.org/learn-seo/title-tag), [2](http://www.mattcutts.com/blog/keywords-meta-tag-in-web-search/)) and general page informancy + * Removed `SiteTree.MetaKeywords` since they are irrelevant in terms of SEO ([seomoz article](http://www.mattcutts.com/blog/keywords-meta-tag-in-web-search/)) and general page informancy + * Removed `SiteTree.MetaTitle` as a means to customize the window title, use `SiteTree.Title` instead * Deprecated `Profiler` class, use third-party solutions like [xhprof](https://github.com/facebook/xhprof/) * Removed defunct or unnecessary debug GET parameters: `debug_profile`, `debug_memory`, `profile_trace`, `debug_javascript`, `debug_behaviour` diff --git a/docs/en/changelogs/beta/3.1.0-beta1.md b/docs/en/changelogs/beta/3.1.0-beta1.md new file mode 100644 index 000000000..9e3e13042 --- /dev/null +++ b/docs/en/changelogs/beta/3.1.0-beta1.md @@ -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->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->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<code>' 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->leftJoin()/innerJoin() args no longer escaped (Simon Welsh) + +### Features and Enhancements + + * 2012-12-13 [7e46290](https://github.com/silverstripe/sapphire/commit/7e46290) Date->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->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->setDescription() handlgin (Ingo Schommer) + * 2012-12-14 [74d6379](https://github.com/silverstripe/silverstripe-cms/commit/74d6379) ed regression in SiteTree->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->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<=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->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->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->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) \ No newline at end of file diff --git a/docs/en/changelogs/index.md b/docs/en/changelogs/index.md index 034d4d1e5..78a80e545 100644 --- a/docs/en/changelogs/index.md +++ b/docs/en/changelogs/index.md @@ -9,6 +9,8 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins ## Stable Releases + * [3.1.0](3.1.0) - Unreleased + * [3.0.2](3.0.2) - 17 September 2012 * [3.0.1](3.0.1) - 31 July 2012 * [3.0.0](3.0.0) - 28 June 2012 @@ -65,6 +67,8 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins ## Alpha/beta/release candidate ## + * [3.1.0-beta1](beta/3.1.0-beta1) - 17 December 2012 + * [3.0.3-rc1](rc/3.0.3-rc1) - 6 November 2012 * [3.0.2-rc2](rc/3.0.2-rc2) - 12 September 2012 * [3.0.2-rc1](rc/3.0.2-rc1) - 5 September 2012 diff --git a/docs/en/howto/simple-contact-form.md b/docs/en/howto/simple-contact-form.md index f785270ff..d29730f2c 100644 --- a/docs/en/howto/simple-contact-form.md +++ b/docs/en/howto/simple-contact-form.md @@ -72,7 +72,6 @@ Now that we have a contact form, we need some way of collecting the data submitt $messageBody = "Name: {$data['Name']}
-Website: {$data['Website']}
Message: {$data['Message']}
"; $email->setBody($messageBody); diff --git a/docs/en/installation/composer.md b/docs/en/installation/composer.md index c738cc8c6..f237ff3bb 100644 --- a/docs/en/installation/composer.md +++ b/docs/en/installation/composer.md @@ -44,18 +44,20 @@ Composer can create a new site for you, using the installer as a template. To d composer create-project silverstripe/installer ./my/website/folder -`./my/website/folder` should be the root directory where your site will live. For example, on OS X, you might use a subdirectory of `~/Sites`. - +`./my/website/folder` should be the root directory where your site will live. +For example, on OS X, you might use a subdirectory of `~/Sites`. As long as your web server is up and running, this will get all the code that you need. - Now visit the site in your web browser, and the installation process will be completed. -#### Selecting a version - By default composer will download the latest stable version. You can also specify a version to download that version explicitly, i.e. this will download 3.0.3: composer create-project silverstripe/installer ./my/website/folder 3.0.3 + +When `create-project` is used with a release version like above, +it will try to get the code from archives instead of creating +git repositories. If you're planning to contribute to SilverStripe, +see [Using development versions](#using-development-versions). ## Adding modules to your project @@ -94,14 +96,35 @@ The `composer.lock` file helps with this. It references the specific commits th So, your deployment process, as it relates to Composer, should be as follows: * Run `composer update` on your development version before you start whatever testing you have planned. Perform all the necessary testing. - * Check `composer.lock` into your repository. - * Deploy your project code base, using the deployment tool of your choice. + * Run `composer install` on your production version. - * Run the following command on your production version. +# Setting up an environment for contributing to SilverStripe {#contributing} - composer install +So you want to contribute to SilverStripe? Fantastic! You can do this with composer too. +You have to tell composer three things in order to be able to do this: + + - Keep the full git repository information + - Include dependancies marked as "developer" requirements + - Use the development version, not the latest stable version + +The first two steps are done as part of the initial create project using additional arguments. For instance: + + composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.0.x-dev + +The process will take a bit longer, since all modules are checked out as full git repositories which you can work on. + +The `--keep-vcs` flag will make sure you have access to the git history of the installer and the requirements + +The `--dev` flag will add a couple modules which are useful for SilverStripe development: + + * The `docsviewer` module will let you preview changes to the project documentation + * The `buildtools` module which adds [phing](http://phing.info) tasks for creating SilverStripe releases + +Note that you can also include those into an existing project by running `composer update --dev`. +Please read the ["Contributing Code"](/misc/contributing/code) documentation to find out how to +create forks and send pull requests. # Advanced usage @@ -212,27 +235,3 @@ Both the version and the alias are specified as Composer versions, not branch na This is not the only way to set things up in Composer. For more information on this topic, read the ["Aliases" chapter of the Composer documentation](http://getcomposer.org/doc/articles/aliases.md). -## Setting up an environment for contributing to SilverStripe - -So you want to contribute to SilverStripe? Fantastic! You can do this with composer too. -You have to tell composer three things in order to be able to do this: - - - Keep the full git repository information - - Include dependancies marked as "developer" requirements - - Use the development version, not the latest stable version - -The first two steps are done as part of the initial create project using additional arguments. For instance: - - composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.0.x-dev - -The process will take a bit longer, since all modules are checked out as full git repositories which you can work on. - -The `--keep-vcs` flag will make sure you have access to the git history of the installer and the requirements - -The `--dev` flag will add a couple modules which are useful for SilverStripe development: - - * The `compass` module will regenerate CSS if you update the SCSS files - * The `docsviewer` module will let you preview changes to the project documentation - * The `buildtools` module which adds [phing](http://phing.info) tasks for creating SilverStripe releases - -Note that you can also include those into an existing project by running `composer update --dev`. \ No newline at end of file diff --git a/docs/en/misc/contributing/code.md b/docs/en/misc/contributing/code.md index c78f8514a..677f1e5a4 100644 --- a/docs/en/misc/contributing/code.md +++ b/docs/en/misc/contributing/code.md @@ -16,29 +16,32 @@ We ask for this so that the ownership in the license is clear and unambiguous, a ## Step-by-step: From forking to sending the pull request - 1. Follow the [Installation for contributions](../../installation/from-source#option-2-installation-for-contributions) instructions, which explain how to fork the core modules and add the correct "upstream" remote. +1. Follow the [Installation through Composer](../../installation/composer#contributing) instructions, +which explain how to fork the core modules and add the correct "upstream" remote. In short: - 1. [Branch for new issue and develop on issue branch](code#branch-for-new-issue-and-develop-on-issue-branch) + composer create-project --keep-vcs --dev silverstripe/installer ./my/website/folder 3.0.x-dev - $ git branch ###-description - $ git checkout ###-description +2. [Branch for new issue and develop on issue branch](code#branch-for-new-issue-and-develop-on-issue-branch) - 1. As time passes, the upstream repository accumulates new commits. Keep your working copy's master branch and issue branch up to date by periodically [rebasing your development branch on the latest upstream](code#rebase-your-development-branch-on-the-latest-upstream). + git branch ###-description + git checkout ###-description - # [make sure all your changes are committed as necessary in branch] - $ git fetch upstream - $ git rebase upstream/master +3. As time passes, the upstream repository accumulates new commits. Keep your working copy's master branch and issue branch up to date by periodically [rebasing your development branch on the latest upstream](code#rebase-your-development-branch-on-the-latest-upstream). - 1. When development is complete, [squash all commit related to a single issue into a single commit](code#squash-all-commits-related-to-a-single-issue-into-a-single-commit). + # [make sure all your changes are committed as necessary in branch] + git fetch upstream + git rebase upstream/master - $ git fetch upstream - $ git rebase -i upstream/master +4. When development is complete, [squash all commit related to a single issue into a single commit](code#squash-all-commits-related-to-a-single-issue-into-a-single-commit). - 1. Push release candidate branch to GitHub + git fetch upstream + git rebase -i upstream/master - $ git push origin ###-description +5. Push release candidate branch to GitHub - 1. Issue pull request on GitHub. Visit your forked respoistory on GitHub.com and click the "Create Pull Request" button nex tot the new branch. + git push origin ###-description + +6. Issue pull request on GitHub. Visit your forked respoistory on GitHub.com and click the "Create Pull Request" button nex tot the new branch. The core team is then responsible for reviewing patches and deciding if they will make it into core. If there are any problems they will follow up with you, so please ensure they have a way to contact you! @@ -61,7 +64,10 @@ If you're familiar with it, here's the short version of what you need to know. O * **Squash your commits, so that each commit addresses a single issue.** After you rebase your work on top of the upstream master, you can squash multiple commits into one. Say, for instance, you've got three commits in related to Issue #100. Squash all three into one with the message "Issue #100 Description of the issue here." We won't accept pull requests for multiple commits related to a single issue; it's up to you to squash and clean your commit tree. (Remember, if you squash commits you've already pushed to GitHub, you won't be able to push that same branch again. Create a new local branch, squash, and push the new squashed branch.) - + * **Choose the correct branch**: Assume the current release is 3.0.3, and 3.1.0 is in beta state. + Most pull requests should go against the `3.1.x-dev` *pre-release branch*, only critical bugfixes + against the `3.0.x-dev` *release branch*. If you're changing an API or introducing a major feature, + the pull request should go against `master` (read more about our [release process](/misc/release-process)). ### Editing files directly on GitHub.com diff --git a/docs/en/reference/cms-architecture.md b/docs/en/reference/cms-architecture.md index eaf67207e..9caeca734 100644 --- a/docs/en/reference/cms-architecture.md +++ b/docs/en/reference/cms-architecture.md @@ -311,6 +311,7 @@ without affecting the response body. Built-in headers are: + * `X-Title`: Set window title (requires URL encoding) * `X-Controller`: PHP class name matching a menu entry, which is marked active * `X-ControllerURL`: Alternative URL to record in the HTML5 browser history * `X-Status`: Extended status information, used for an information popover. diff --git a/docs/en/reference/dataobject.md b/docs/en/reference/dataobject.md index 5aea366f3..b9602d308 100644 --- a/docs/en/reference/dataobject.md +++ b/docs/en/reference/dataobject.md @@ -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 diff --git a/docs/en/reference/form-field-types.md b/docs/en/reference/form-field-types.md index 0b59da7f9..a38c606d5 100644 --- a/docs/en/reference/form-field-types.md +++ b/docs/en/reference/form-field-types.md @@ -1,6 +1,6 @@ # Form Field Types -This is a highlevel overview of available `[api:apiFormField]` subclasses. An automatically generated list is available through our [API] +This is a highlevel overview of available `[api:FormField]` subclasses. An automatically generated list is available through our [API] ## Basic diff --git a/docs/en/reference/grid-field.md b/docs/en/reference/grid-field.md index dfe52bf06..79d0083d4 100644 --- a/docs/en/reference/grid-field.md +++ b/docs/en/reference/grid-field.md @@ -310,6 +310,16 @@ transfered between page requests by being inserted as a hidden field in the form A GridFieldComponent sets and gets data from the GridState. +## Permissions + +Since GridField is mostly used in the CMS, the controller managing a GridField instance +will already do some permission checks for you, and can decline display or executing +any logic on your field. + +If you need more granular control, e.g. to consistently deny non-admins from deleting +records, use the `DataObject->can...()` methods +(see [DataObject permissions](/reference/dataobject#permissions)). + ## Related * [ModelAdmin: A UI driven by GridField](/reference/modeladmin) diff --git a/docs/en/reference/modeladmin.md b/docs/en/reference/modeladmin.md index 5531e0477..5e122b854 100644 --- a/docs/en/reference/modeladmin.md +++ b/docs/en/reference/modeladmin.md @@ -44,6 +44,34 @@ We'll name it `MyAdmin`, but the class name can be anything you want. This will automatically add a new menu entry to the CMS, and you're ready to go! Try opening http://localhost/admin/products/?flush=all. +## Permissions + +Each new `ModelAdmin` subclass creates its own [permission code](/reference/permission), +for the example above this would be `CMS_ACCESS_MyAdmin`. Users with access to the CMS +need to have this permission assigned through `admin/security/` in order to gain +access to the controller (unless they're admins). + +The `DataObject` API has more granular permission control, which is enforced in ModelAdmin by default. +Available checks are `canEdit()`, `canCreate()`, `canView()` and `canDelete()`. +Models check for administrator permissions by default. For most cases, +less restrictive checks make sense, e.g. checking for general CMS access rights. + + :::php + class Category extends DataObject { + // ... + public function canView($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + public function canEdit($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + public function canDelete($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + public function canCreate($member = null) { + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + ## Search Fields ModelAdmin uses the `[SearchContext](/reference/searchcontext)` class to provide diff --git a/docs/en/reference/uploadfield.md b/docs/en/reference/uploadfield.md index 37dbbfac9..1d8db946f 100644 --- a/docs/en/reference/uploadfield.md +++ b/docs/en/reference/uploadfield.md @@ -9,7 +9,9 @@ as well. That makes it flexible enough to sometimes even replace the Gridfield, like for instance in creating and managing a simple gallery. ## Usage -The UploadField can be used in two ways: + +The field can be used in two ways: To upload a single file into a `has_one` relationship, +or allow multiple files into a fixed folder (or relationship). ### Single fileupload @@ -76,7 +78,23 @@ UploadField will detect the relation based on its $name property value: WARNING: Currently the UploadField doesn't fully support has_many relations, so use a many_many relation instead! -## Set a custom folder +## Configuration + +### Overview + +The field can either be configured on an instance level through `setConfig(
* $UploadField = new UploadField('myFiles', 'Please upload some images (max. 5 files)');
@@ -66,9 +68,9 @@ class UploadField extends FileField {
protected $items;
/**
- * Config for this field used in both, php and javascript (will be merged into the config of the javascript file
- * upload plugin)
- * @var array
+ * @var array Config for this field used in both, php and javascript
+ * (will be merged into the config of the javascript file upload plugin).
+ * See framework/_config/uploadfield.yml for configuration defaults and documentation.
*/
protected $ufConfig = array(
/**
@@ -81,6 +83,11 @@ class UploadField extends FileField {
* @var int
*/
'allowedMaxFileNumber' => null,
+ /**
+ * @var boolean|string Can the user upload new files, or just select from existing files.
+ * String values are interpreted as permission codes.
+ */
+ 'canUpload' => true,
/**
* @var int
*/
@@ -133,6 +140,8 @@ class UploadField extends FileField {
$this->addExtraClass('ss-upload'); // class, used by js
$this->addExtraClass('ss-uploadfield'); // class, used by css for uploadfield only
+ $this->ufConfig = array_merge($this->ufConfig, Config::inst()->get('UploadField', 'defaultConfig'));
+
parent::__construct($name, $title);
if($items) $this->setItems($items);
@@ -431,7 +440,7 @@ class UploadField extends FileField {
* @return UploadField_ItemHandler
*/
public function handleSelect(SS_HTTPRequest $request) {
- return UploadField_SelectHandler::create($this, $this->folderName);
+ return UploadField_SelectHandler::create($this, $this->getFolderName());
}
/**
@@ -441,7 +450,9 @@ class UploadField extends FileField {
* @return string json
*/
public function upload(SS_HTTPRequest $request) {
- if($this->isDisabled() || $this->isReadonly()) return $this->httpError(403);
+ if($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
+ return $this->httpError(403);
+ }
// Protect against CSRF on destructive action
$token = $this->getForm()->getSecurityToken();
@@ -500,7 +511,7 @@ class UploadField extends FileField {
// Get the uploaded file into a new file object.
try {
- $this->upload->loadIntoFile($tmpfile, $fileObject, $this->folderName);
+ $this->upload->loadIntoFile($tmpfile, $fileObject, $this->getFolderName());
} catch (Exception $e) {
// we shouldn't get an error here, but just in case
$return['error'] = $e->getMessage();
@@ -629,6 +640,12 @@ class UploadField extends FileField {
// Don't allow upload or edit of a relation when the underlying record hasn't been persisted yet
return (!$record || !$this->managesRelation() || $record->exists());
}
+
+ public function canUpload() {
+ $can = $this->getConfig('canUpload');
+ return (is_bool($can)) ? $can : Permission::check($can);
+ }
+
}
/**
diff --git a/forms/gridfield/GridFieldAddNewButton.php b/forms/gridfield/GridFieldAddNewButton.php
index 2c1e0bf87..ba3089490 100644
--- a/forms/gridfield/GridFieldAddNewButton.php
+++ b/forms/gridfield/GridFieldAddNewButton.php
@@ -1,6 +1,7 @@
canCreate()} for this record returns true.
*
* @package framework
* @subpackage gridfield
@@ -21,9 +22,12 @@ class GridFieldAddNewButton implements GridField_HTMLProvider {
}
public function getHTMLFragments($gridField) {
+ $singleton = singleton($gridField->getModelClass());
+ if(!$singleton->canCreate()) return array();
+
if(!$this->buttonName) {
// provide a default button name, can be changed by calling {@link setButtonName()} on this component
- $objectName = singleton($gridField->getModelClass())->i18n_singular_name();
+ $objectName = $singleton->i18n_singular_name();
$this->buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
}
diff --git a/forms/gridfield/GridFieldDeleteAction.php b/forms/gridfield/GridFieldDeleteAction.php
index 16c24a10b..9c2aeb83b 100644
--- a/forms/gridfield/GridFieldDeleteAction.php
+++ b/forms/gridfield/GridFieldDeleteAction.php
@@ -98,15 +98,16 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
*/
public function getColumnContent($gridField, $record, $columnName) {
if($this->removeRelation) {
+ if(!$record->canEdit()) return;
+
$field = GridField_FormAction::create($gridField, 'UnlinkRelation'.$record->ID, false,
"unlinkrelation", array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-unlink')
->setAttribute('title', _t('GridAction.UnlinkRelation', "Unlink"))
->setAttribute('data-icon', 'chain--minus');
} else {
- if(!$record->canDelete()) {
- return;
- }
+ if(!$record->canDelete()) return;
+
$field = GridField_FormAction::create($gridField, 'DeleteRecord'.$record->ID, false, "deleterecord",
array('RecordID' => $record->ID))
->addExtraClass('gridfield-button-delete')
@@ -132,13 +133,20 @@ class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_Actio
if(!$item) {
return;
}
- if($actionName == 'deleterecord' && !$item->canDelete()) {
- throw new ValidationException(
- _t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
- }
+
if($actionName == 'deleterecord') {
+ if(!$item->canDelete()) {
+ throw new ValidationException(
+ _t('GridFieldAction_Delete.DeletePermissionsFailure',"No delete permissions"),0);
+ }
+
$item->delete();
} else {
+ if(!$item->canEdit()) {
+ throw new ValidationException(
+ _t('GridFieldAction_Delete.EditPermissionsFailure',"No permission to unlink record"),0);
+ }
+
$gridField->getList()->remove($item);
}
}
diff --git a/forms/gridfield/GridFieldDetailForm.php b/forms/gridfield/GridFieldDetailForm.php
index 23818c217..c8765fa3d 100644
--- a/forms/gridfield/GridFieldDetailForm.php
+++ b/forms/gridfield/GridFieldDetailForm.php
@@ -310,16 +310,31 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
return $controller->redirect($noActionURL, 302);
}
+ $canView = $this->record->canView();
+ $canEdit = $this->record->canEdit();
+ $canDelete = $this->record->canDelete();
+ $canCreate = $this->record->canCreate();
+
+ if(!$canView) {
+ $controller = Controller::curr();
+ // TODO More friendly error
+ return $controller->httpError(403);
+ }
+
$actions = new FieldList();
if($this->record->ID !== 0) {
- $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
- ->setUseButtonTag(true)
- ->addExtraClass('ss-ui-action-constructive')
- ->setAttribute('data-icon', 'accept'));
+ if($canEdit) {
+ $actions->push(FormAction::create('doSave', _t('GridFieldDetailForm.Save', 'Save'))
+ ->setUseButtonTag(true)
+ ->addExtraClass('ss-ui-action-constructive')
+ ->setAttribute('data-icon', 'accept'));
+ }
- $actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
- ->setUseButtonTag(true)
- ->addExtraClass('ss-ui-action-destructive'));
+ if($canDelete) {
+ $actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))
+ ->setUseButtonTag(true)
+ ->addExtraClass('ss-ui-action-destructive'));
+ }
}else{ // adding new record
//Change the Save label to 'Create'
@@ -353,6 +368,14 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
$form->loadDataFrom($this->record, $this->record->ID == 0 ? Form::MERGE_IGNORE_FALSEISH : Form::MERGE_DEFAULT);
+ if($this->record->ID && !$canEdit) {
+ // Restrict editing of existing records
+ $form->makeReadonly();
+ } elseif(!$this->record->ID && !$canCreate) {
+ // Restrict creation of new records
+ $form->makeReadonly();
+ }
+
// Load many_many extraData for record.
// Fields with the correct 'ManyMany' namespace need to be added manually through getCMSFields().
if($list instanceof ManyManyList) {
@@ -429,6 +452,10 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
$extraData = null;
}
+ if(!$this->record->canEdit()) {
+ return $controller->httpError(403);
+ }
+
try {
$form->saveInto($this->record);
$this->record->write();
@@ -451,10 +478,16 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
// TODO Save this item into the given relationship
- $message = sprintf(
- _t('GridFieldDetailForm.Saved', 'Saved %s %s'),
- $this->record->singular_name(),
- '"' . htmlspecialchars($this->record->Title, ENT_QUOTES) . '"'
+ $link = '"'
+ . htmlspecialchars($this->record->Title, ENT_QUOTES)
+ . '"';
+ $message = _t(
+ 'GridFieldDetailForm.Saved',
+ 'Saved {name} {link}',
+ array(
+ 'name' => $this->record->singular_name(),
+ 'link' => $link
+ )
);
$form->sessionMessage($message, 'good');
diff --git a/forms/gridfield/GridFieldEditButton.php b/forms/gridfield/GridFieldEditButton.php
index 1c6836ed6..d2801d535 100644
--- a/forms/gridfield/GridFieldEditButton.php
+++ b/forms/gridfield/GridFieldEditButton.php
@@ -72,9 +72,9 @@ class GridFieldEditButton implements GridField_ColumnProvider {
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
- if(!$record->canEdit()){
- return;
- }
+ // No permission checks, handled through GridFieldDetailForm,
+ // which can make the form readonly if no edit permissions are available.
+
$data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));
diff --git a/forms/gridfield/GridFieldPaginator.php b/forms/gridfield/GridFieldPaginator.php
index 76f0cf4e4..c79e1f838 100755
--- a/forms/gridfield/GridFieldPaginator.php
+++ b/forms/gridfield/GridFieldPaginator.php
@@ -130,7 +130,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
// Update item count prior to filter. GridFieldPageCount will rely on this value
$this->totalItems = $dataList->count();
- if(!($dataList instanceof SS_Limitable)) {
+ if(!($dataList instanceof SS_Limitable) || ($dataList instanceof UnsavedRelationList)) {
return $dataList;
}
diff --git a/i18n/i18n.php b/i18n/i18n.php
index ee21590da..9735f5cc7 100644
--- a/i18n/i18n.php
+++ b/i18n/i18n.php
@@ -1536,7 +1536,13 @@ class i18n extends Object implements TemplateGlobalProvider {
// Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order.
// Fail silently in case the translation is outdated
- $replaced = @vsprintf($returnValue, array_values($injectionArray));
+ preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs);
+ if($returnValueArgs) foreach($returnValueArgs[0] as $i => $returnValueArg) {
+ if($i >= count($injectionArray)) {
+ $injectionArray[] = '';
+ }
+ }
+ $replaced = vsprintf($returnValue, array_values($injectionArray));
if($replaced) $returnValue = $replaced;
} else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found,
diff --git a/i18n/i18nTextCollector.php b/i18n/i18nTextCollector.php
index 40da3f8e6..abb9e064b 100644
--- a/i18n/i18nTextCollector.php
+++ b/i18n/i18nTextCollector.php
@@ -289,7 +289,7 @@ class i18nTextCollector extends Object {
*
* @todo Why the type juggling for $this->collectFromTemplate()? It always returns an array.
*/
- public function collectFromTemplate($content, $fileName, $module) {
+ public function collectFromTemplate($content, $fileName, $module, &$parsedFiles = array()) {
$entities = array();
// Search for included templates
@@ -299,11 +299,12 @@ class i18nTextCollector extends Object {
$includeFileName = "{$includeName}.ss";
$filePath = SSViewer::getTemplateFileByType($includeName, 'Includes');
if(!$filePath) $filePath = SSViewer::getTemplateFileByType($includeName, 'main');
- if($filePath) {
+ if($filePath && !in_array($filePath, $parsedFiles)) {
+ $parsedFiles[] = $filePath;
$includeContent = file_get_contents($filePath);
$entities = array_merge(
$entities,
- (array)$this->collectFromTemplate($includeContent, $module, $includeFileName)
+ (array)$this->collectFromTemplate($includeContent, $module, $includeFileName, $parsedFiles)
);
}
// @todo Will get massively confused if you include the includer -> infinite loop
diff --git a/javascript/AssetUploadField.js b/javascript/AssetUploadField.js
index 93dddde1f..a63c3a130 100644
--- a/javascript/AssetUploadField.js
+++ b/javascript/AssetUploadField.js
@@ -16,4 +16,17 @@
this.find('.ss-uploadfield-editandorganize').show();
}
});
+ $('.ss-uploadfield-view-allowed-extensions').entwine({
+ onmatch: function() {
+ this.find('.description .toggle-content').hide();
+ this._super();
+ }
+ });
+
+ $('.ss-uploadfield-view-allowed-extensions .toggle').entwine({
+ onclick: function(e) {
+ jQuery(this).closest('.description').find('.toggle-content').toggle();
+ return false;
+ }
+ });
}(jQuery));
diff --git a/lang/af.yml b/lang/af.yml
index 9cd84e9f4..62f371557 100644
--- a/lang/af.yml
+++ b/lang/af.yml
@@ -1,98 +1,98 @@
af:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
- NEWFOLDER: NewFolder
+ NEWFOLDER: 'Nuwe Dossier'
AssetTableField:
- CREATED: 'First uploaded'
- DIM: Dimensions
- FILENAME: Filename
- FOLDER: Folder
- LASTEDIT: 'Last changed'
- OWNER: Owner
- SIZE: 'File size'
- TITLE: Title
- TYPE: 'File type'
+ CREATED: 'Eerste opgelaai'
+ DIM: Afmetings
+ FILENAME: 'Lêer naam'
+ FOLDER: Dossier
+ LASTEDIT: 'Laaste verander'
+ OWNER: Eienaar
+ SIZE: 'Lêer grootte'
+ TITLE: Titel
+ TYPE: 'Lêer tipe'
URL: URL
AssetUploadField:
- ChooseFiles: 'Choose files'
- DRAGFILESHERE: 'Drag files here'
- DROPAREA: 'Drop Area'
- EDITALL: 'Edit all'
- EDITANDORGANIZE: 'Edit & organize'
- EDITINFO: 'Edit files'
- FILES: Files
- FROMCOMPUTER: 'Choose files from your computer'
- FROMCOMPUTERINFO: 'Upload from your computer'
- TOTAL: Total
- TOUPLOAD: 'Choose files to upload...'
+ ChooseFiles: 'Kies lêers'
+ DRAGFILESHERE: 'Trek lêers hiernatoe'
+ DROPAREA: 'Laat val area'
+ EDITALL: 'Verander alles'
+ EDITANDORGANIZE: 'Verander en organiseer'
+ EDITINFO: 'Verander lêers'
+ FILES: Lêers
+ FROMCOMPUTER: 'Kies lêers van jou rekenaar af'
+ FROMCOMPUTERINFO: 'Laai op van jou rekenaar af'
+ TOTAL: Totaal
+ TOUPLOAD: 'Kies lêers om op te laai...'
UPLOADINPROGRESS: 'Please wait… upload in progress'
- UPLOADOR: OR
+ UPLOADOR: OF
BBCodeParser:
ALIGNEMENT: Belyning
- ALIGNEMENTEXAMPLE: 'right aligned'
+ ALIGNEMENTEXAMPLE: 'Rig na regs'
BOLD: 'Vet Teks'
BOLDEXAMPLE: Vet
- CODE: 'Code Block'
- CODEDESCRIPTION: 'Unformatted code block'
- CODEEXAMPLE: 'Code block'
+ CODE: 'Kode blok'
+ CODEDESCRIPTION: 'Ongeformateerde kode blok'
+ CODEEXAMPLE: 'Kode blok'
COLORED: 'Gekleurde teks'
COLOREDEXAMPLE: 'blou teks'
- EMAILLINK: 'Email link'
- EMAILLINKDESCRIPTION: 'Create link to an email address'
- IMAGE: Image
- IMAGEDESCRIPTION: 'Show an image in your post'
+ EMAILLINK: 'Epos skakel'
+ EMAILLINKDESCRIPTION: 'Skep skakel na epos adres'
+ IMAGE: 'Geen prentjie gelaai nie'
+ IMAGEDESCRIPTION: 'Wys ''n foto in wat jy gepos het'
ITALIC: 'Skuinsstrepe Teks'
ITALICEXAMPLE: Skuinsstrepe
- LINK: 'Website link'
- LINKDESCRIPTION: 'Link to another website or URL'
+ LINK: 'Webwerf skakel'
+ LINKDESCRIPTION: 'Koppel aan ''n ander webwerf of URL'
STRUCK: 'Deur-gestreepde Teks'
STRUCKEXAMPLE: Deur-gestreep
UNDERLINE: 'Beklemtoonde Teks'
UNDERLINEEXAMPLE: Beklemtoon
- UNORDERED: 'Unordered list'
- UNORDEREDDESCRIPTION: 'Unordered list'
- UNORDEREDEXAMPLE1: 'unordered item 1'
+ UNORDERED: 'Ongeorganiseerde lys'
+ UNORDEREDDESCRIPTION: 'Ongeorganiseerde lys'
+ UNORDEREDEXAMPLE1: 'Ongeorganiseerde item 1'
BackLink_Button.ss:
- Back: Back
+ Back: Terug
BasicAuth:
- ENTERINFO: 'Please enter a username and password.'
- ERRORNOTADMIN: 'That user is not an administrator.'
- ERRORNOTREC: 'That username / password isn''t recognised'
+ ENTERINFO: 'Tik asseblief ''n verbruikersnaam en wagwoord in'
+ ERRORNOTADMIN: 'Daardie verbruiker is nie ''n administreerder nie'
+ ERRORNOTREC: 'Daar die verbruikersnaam / wagwoord is nie herken nie'
Boolean:
- 0: 'False'
- ANY: Any
- 1: 'True'
+ 0: Onwaar
+ ANY: Enige
+ 1: Waar
CMSLoadingScreen.ss:
- LOADING: Loading...
- REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
+ LOADING: 'Besig om te laai...'
+ REQUIREJS: 'Die IBS vereis dat JavaScript aangeskakel is'
CMSMain:
ACCESS: 'Access to ''{title}'' section'
- ACCESSALLINTERFACES: 'Access to all CMS sections'
- ACCESSALLINTERFACESHELP: 'Overrules more specific access settings.'
- SAVE: Save
+ ACCESSALLINTERFACES: 'Toegang tot alle IBS gedeeltes'
+ ACCESSALLINTERFACESHELP: 'Oorheers meer spesifieke toegans verstellings'
+ SAVE: Stoor
CMSProfileController:
- MENUTITLE: 'My Profile'
+ MENUTITLE: 'My profiel'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'U het die wagwoord vir'
- CHANGEPASSWORDTEXT2: 'You can now use the following credentials to log in:'
- EMAIL: Email
- HELLO: Hi
- PASSWORD: Password
+ CHANGEPASSWORDTEXT2: 'Jy kan nou die volgende sekuriteitsbesonderhede gebruik om in te teken'
+ EMAIL: Epos
+ HELLO: 'Hi daar'
+ PASSWORD: Wagwoord
CheckboxField:
- - 'False'
- - 'True'
+ - Onwaar
+ - Waar
ComplexTableField:
- CLOSEPOPUP: 'Close Popup'
- SUCCESSADD2: 'Added {name}'
- SUCCESSEDIT: 'Saved %s %s %s'
+ CLOSEPOPUP: 'Maak wipop toe'
+ SUCCESSADD2: '{name} bygesit'
+ SUCCESSEDIT: 'Gestoor %s %s %s'
ComplexTableField.ss:
ADDITEM: 'Byvoeg %s'
- NOITEMSFOUND: 'No items found'
- SORTASC: 'Sort ascending'
- SORTDESC: 'Sort descending'
+ NOITEMSFOUND: 'Geen item gevind nie'
+ SORTASC: 'Sorteer in stygende orde'
+ SORTDESC: 'Sorteer in dalende orde'
ComplexTableField_popup.ss:
- NEXT: Next
- PREVIOUS: Previous
+ NEXT: Volgende
+ PREVIOUS: Vorige
ConfirmedPasswordField:
ATLEAST: 'Passwords must be at least {min} characters long.'
BETWEEN: 'Passwords must be {min} to {max} characters long.'
@@ -109,20 +109,20 @@ af:
PLURALNAME: 'Data Voorwerpe'
SINGULARNAME: 'Data Voorwerp'
Date:
- DAY: ' day'
- DAYS: ' days'
- HOUR: ' hour'
- HOURS: ' hours'
- MIN: ' min'
- MINS: ' mins'
- MONTH: ' month'
- MONTHS: ' months'
- SEC: ' sec'
- SECS: ' secs'
- TIMEDIFFAGO: '{difference} ago'
+ DAY: dag
+ DAYS: dae
+ HOUR: uur
+ HOURS: ure
+ MIN: minuut
+ MINS: minute
+ MONTH: maand
+ MONTHS: maande
+ SEC: sekonde
+ SECS: sekondes
+ TIMEDIFFAGO: '{difference} terug'
TIMEDIFFIN: 'in {difference}'
- YEAR: ' year'
- YEARS: ' years'
+ YEAR: jaar
+ YEARS: jare
DateField:
NOTSET: 'nier gestel'
TODAY: vandag
@@ -130,68 +130,68 @@ af:
VALIDDATEMAXDATE: 'Your date has to be older or matching the maximum allowed date ({date})'
VALIDDATEMINDATE: 'Your date has to be newer or matching the minimum allowed date ({date})'
DatetimeField:
- NOTSET: 'Not set'
+ NOTSET: 'Nie gestel nie'
Director:
- INVALID_REQUEST: 'Invalid request'
+ INVALID_REQUEST: 'Ongeldige versoek'
DropdownField:
CHOOSE: (Kies)
EmailField:
- VALIDATION: 'Please enter an email address'
+ VALIDATION: 'Verskaf asseblief ''n epos adres '
Email_BounceRecord:
- PLURALNAME: 'Email Bounce Records'
- SINGULARNAME: 'Email Bounce Record'
+ PLURALNAME: 'Epos hop rekords'
+ SINGULARNAME: 'Epos hop rekord'
Enum:
- ANY: Any
+ ANY: Enige
File:
AviType: 'AVI video file'
- Content: Content
- CssType: 'CSS file'
+ Content: Inhoud
+ CssType: 'CSS lêer'
DmgType: 'Apple disk image'
- DocType: 'Word document'
- Filename: Filename
+ DocType: 'Word dokument'
+ Filename: 'Lêer naam'
GifType: 'GIF image - good for diagrams'
GzType: 'GZIP compressed file'
- HtlType: 'HTML file'
+ HtlType: 'HTML lêer'
HtmlType: 'HTML file'
INVALIDEXTENSION: 'Extension is not allowed (valid: {extensions})'
INVALIDEXTENSIONSHORT: 'Extension is not allowed'
- IcoType: 'Icon image'
- JpgType: 'JPEG image - good for photos'
+ IcoType: 'Ikoon prentjie'
+ JpgType: 'JPEG prentjie - werk goed vir fotos'
JsType: 'Javascript file'
- Mp3Type: 'MP3 audio file'
+ Mp3Type: 'MP3 klank lêer'
MpgType: 'MPEG video file'
NOFILESIZE: 'Lêergrootte is nul grepe.'
- NOVALIDUPLOAD: 'File is not a valid upload'
- Name: Name
+ NOVALIDUPLOAD: 'Lêer is nie geld vir oplaai nie'
+ Name: Naam
PLURALNAME: Lêers
PdfType: 'Adobe Acrobat PDF file'
PngType: 'PNG image - good general-purpose format'
SINGULARNAME: Lêer
TOOLARGE: 'Filesize is too large, maximum {size} allowed'
- TOOLARGESHORT: 'Filesize exceeds {size}'
+ TOOLARGESHORT: 'Lêer is groter as {size}'
TiffType: 'Tagged image format'
- Title: Title
- WavType: 'WAV audo file'
+ Title: Titel
+ WavType: 'WAV klank lêer'
XlsType: 'Excel spreadsheet'
ZipType: 'ZIP compressed file'
FileIFrameField:
- ATTACH: 'Attach {type}'
- ATTACHONCESAVED: '{type}s can be attached once you have saved the record for the first time.'
+ ATTACH: 'Heg {type} aan'
+ ATTACHONCESAVED: '{type}e kan aangeheg word sodra jy die rekord vir die eerste keer gestoor het'
ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
- DELETE: 'Delete {type}'
- DISALLOWEDFILETYPE: 'This filetype is not allowed to be uploaded'
- FILE: File
- FROMCOMPUTER: 'From your Computer'
- FROMFILESTORE: 'From the File Store'
+ DELETE: 'Verwyder {type}'
+ DISALLOWEDFILETYPE: 'Dit word nie toegelaat om hierde lêer tipe op te laai nie'
+ FILE: Lêer
+ FROMCOMPUTER: 'Van jou rekenaar'
+ FROMFILESTORE: 'Vanuit die lêer stoor'
NOSOURCE: 'Kies asseblief ''n bron lêer om by te voeg'
- REPLACE: 'Replace {type}'
+ REPLACE: 'Vervang {type}'
FileIFrameField_iframe.ss:
- TITLE: 'Image Uploading Iframe'
+ TITLE: 'Prentjie oplaaiende ''Iframe'''
Filesystem:
SYNCRESULTS: 'Sync complete: {createdcount} items created, {deletedcount} items deleted'
Folder:
- PLURALNAME: Folders
- SINGULARNAME: Folder
+ PLURALNAME: Dossiers
+ SINGULARNAME: Dossier
ForgotPasswordEmail.ss:
HELLO: Hi
TEXT1: 'Hier is u'
@@ -199,214 +199,214 @@ af:
TEXT3: vir
Form:
FIELDISREQUIRED: '%s word benodig.'
- SubmitBtnLabel: Go
+ SubmitBtnLabel: Gaan
VALIDATIONCREDITNUMBER: 'Please ensure you have entered the {number} credit card number correctly'
VALIDATIONNOTUNIQUE: 'The waarde wat ingesleutel is is nie uniek nie'
VALIDATIONPASSWORDSDONTMATCH: 'Wagwoorde pas nie'
VALIDATIONPASSWORDSNOTEMPTY: 'Wagwoorde kan nie leeg wees nie'
- VALIDATIONSTRONGPASSWORD: 'Passwords must have at least one digit and one alphanumeric character'
+ VALIDATIONSTRONGPASSWORD: 'Wagwoorde moet minstens een nommer en een alfa-numeriese karaketer bevat'
VALIDATOR: Vergeldiger
- VALIDCURRENCY: 'Please enter a valid currency'
+ VALIDCURRENCY: 'Tik asseblief ''n geldige geldeenheid in '
FormField:
NONE: geen
GridAction:
- DELETE_DESCRIPTION: Delete
- Delete: Delete
- UnlinkRelation: Unlink
+ DELETE_DESCRIPTION: Verwyder
+ Delete: Verwyder
+ UnlinkRelation: Ontkoppel
GridField:
- Add: 'Add {name}'
+ Add: 'Voeg {name}'
Filter: Filter
FilterBy: 'Filter by '
- Find: Find
- LEVELUP: 'Level up'
- LinkExisting: 'Link Existing'
- NewRecord: 'New %s'
- NoItemsFound: 'No items found'
- PRINTEDAT: 'Printed at'
- PRINTEDBY: 'Printed by'
- PlaceHolder: 'Find {type}'
+ Find: Vind
+ LEVELUP: 'Op een vlak'
+ LinkExisting: 'Koppel bestaande'
+ NewRecord: 'Nuwe %s'
+ NoItemsFound: 'Geen items gevind nie'
+ PRINTEDAT: 'Gedruk te'
+ PRINTEDBY: 'Gedruk deur'
+ PlaceHolder: 'Vind {type}'
PlaceHolderWithLabels: 'Find {type} by {name}'
- RelationSearch: 'Relation search'
- ResetFilter: Reset
+ RelationSearch: 'Soek vir verwantskap'
+ ResetFilter: Herstel
GridFieldAction_Delete:
- DeletePermissionsFailure: 'No delete permissions'
+ DeletePermissionsFailure: 'Geen toestemming om te verwyder nie'
GridFieldDetailForm:
- CancelBtn: Cancel
- Create: Create
- Delete: Delete
- DeletePermissionsFailure: 'No delete permissions'
- Deleted: 'Deleted %s %s'
- Save: Save
- Saved: 'Saved %s %s'
+ CancelBtn: 'Kanselleer '
+ Create: Skep
+ Delete: Verwyder
+ DeletePermissionsFailure: 'Geen toestemming om te verwyder nie'
+ Deleted: 'Verwyderde %s %s'
+ Save: Stoor
+ Saved: 'Gestoor %s %s'
GridFieldItemEditView.ss: null
Group:
- AddRole: 'Add a role for this group'
+ AddRole: 'Voeg nog ''n rol by hierdie groep'
Code: 'Groep Kode'
DefaultGroupTitleAdministrators: Administrateurs
DefaultGroupTitleContentAuthors: 'Inhouds Outeurs'
- Description: Description
- GroupReminder: 'If you choose a parent group, this group will take all it''s roles'
+ Description: Beskrywing
+ GroupReminder: 'As jy ''n ouer groep kies sal hierdie groep al daardie rolle aanneem'
Locked: 'Gesluit?'
- NoRoles: 'No roles found'
- PLURALNAME: Groups
+ NoRoles: 'Geen rolle gevind nie'
+ PLURALNAME: Groepe
Parent: 'Ouer Groep'
- RolesAddEditLink: 'Manage roles'
- SINGULARNAME: Group
- Sort: 'Sort Order'
+ RolesAddEditLink: 'Bestuur rolle'
+ SINGULARNAME: Groep
+ Sort: 'Sorteerings orde'
has_many_Permissions: Toestemmings
many_many_Members: Lidde
GroupImportForm:
- Help1: 'Import one or more groups in CSV format (comma-separated values). Show advanced usage
'
+ Help1: ' Voer een of meer groepe in CSVformaat (komma geskeide waardes). Wys gevorderde gebruike
'
Help2: ' Advanced usage
- Allowed columns: %s
- Existing groups are matched by their unique Code value, and updated with any new values from the imported file
- Group hierarchies can be created by using a ParentCode column.
- Permission codes can be assigned by the PermissionCode column. Existing permission codes are not cleared.
'
ResultCreated: 'Created {count} groups'
- ResultDeleted: 'Deleted %d groups'
- ResultUpdated: 'Updated %d groups'
+ ResultDeleted: 'Verwyderde %d groepe'
+ ResultUpdated: '%d Groepe was opgedateer'
Hierarchy:
InfiniteLoopNotAllowed: 'Infinite loop found within the "{type}" hierarchy. Please change the parent to resolve this'
HtmlEditorField:
ADDURL: 'Add URL'
ADJUSTDETAILSDIMENSIONS: 'Details & dimensions'
ANCHORVALUE: Anker
- BUTTONINSERT: Insert
- BUTTONINSERTLINK: 'Insert link'
- BUTTONREMOVELINK: 'Remove link'
- BUTTONUpdate: Update
- CAPTIONTEXT: 'Caption text'
- CSSCLASS: 'Alignment / style'
- CSSCLASSCENTER: 'Centered, on its own.'
- CSSCLASSLEFT: 'On the left, with text wrapping around.'
+ BUTTONINSERT: 'Plaas in'
+ BUTTONINSERTLINK: 'Sit in'
+ BUTTONREMOVELINK: 'Verwyder skakel'
+ BUTTONUpdate: Verander
+ CAPTIONTEXT: 'Onderskrif teks'
+ CSSCLASS: 'Belyning styl'
+ CSSCLASSCENTER: 'In die middel op sy eie'
+ CSSCLASSLEFT: ' Links met teks wat rondom vloei'
CSSCLASSLEFTALONE: 'Op die linkerkant, op sy eie.'
- CSSCLASSRIGHT: 'On the right, with text wrapping around.'
- DETAILS: Details
- EMAIL: 'Email address'
- FILE: File
- FOLDER: Folder
- FROMCMS: 'From the CMS'
- FROMCOMPUTER: 'From your computer'
+ CSSCLASSRIGHT: 'Regs met teks wat rondom vloei'
+ DETAILS: Besonderhede
+ EMAIL: 'Epos Adres'
+ FILE: Lêer
+ FOLDER: Dossier
+ FROMCMS: 'Van die IBS'
+ FROMCOMPUTER: 'Van you rekenaar'
FROMWEB: 'From the web'
- FindInFolder: 'Find in Folder'
+ FindInFolder: 'Find in dossier'
IMAGEALT: 'Alternative text (alt)'
- IMAGEALTTEXT: 'Alternative text (alt) - shown if image cannot be displayed'
+ IMAGEALTTEXT: 'Alternatiewe teks (alt) - word gewys as prentjie nie beskikbaar is nie'
IMAGEALTTEXTDESC: 'Shown to screen readers or if image can not be displayed'
- IMAGEDIMENSIONS: Dimensions
- IMAGEHEIGHTPX: Height
- IMAGETITLE: 'Title text (tooltip) - for additional information about the image'
- IMAGETITLETEXT: 'Title text (tooltip)'
+ IMAGEDIMENSIONS: Afmetings
+ IMAGEHEIGHTPX: Hoogte
+ IMAGETITLE: 'Titel teks (leidraad) - vir addisionele informasie oor die prentjie'
+ IMAGETITLETEXT: 'Titel teks (leidraad)'
IMAGETITLETEXTDESC: 'For additional information about the image'
- IMAGEWIDTHPX: Width
- INSERTMEDIA: 'Insert Media'
- LINK: 'Insert Link'
+ IMAGEWIDTHPX: Wydte
+ INSERTMEDIA: 'Voeg Media In'
+ LINK: 'Sit skakel in'
LINKANCHOR: 'Anker op hierdie bladsy'
- LINKDESCR: 'Link description'
- LINKEMAIL: 'Email address'
- LINKEXTERNAL: 'Another website'
- LINKFILE: 'Download a file'
- LINKINTERNAL: 'Page on the site'
- LINKOPENNEWWIN: 'Open link in a new window?'
- LINKTO: 'Link to'
- PAGE: Page
+ LINKDESCR: 'Skakel beskrywing'
+ LINKEMAIL: 'Epos Adres'
+ LINKEXTERNAL: 'Ander webwerf'
+ LINKFILE: 'Laai ''n lêer af'
+ LINKINTERNAL: 'Bladsy op die webwerf'
+ LINKOPENNEWWIN: 'Wil jy die skakel in ''n nuwe venster oop maak?'
+ LINKTO: 'Koppel aan'
+ PAGE: Bladsy
URL: URL
URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
- UpdateMEDIA: 'Update Media'
+ UpdateMEDIA: 'Verander Media'
Image:
- PLURALNAME: Files
- SINGULARNAME: File
+ PLURALNAME: Lêers
+ SINGULARNAME: Lêer
ImageField:
- IMAGE: Image
+ IMAGE: 'Geen prentjie gelaai nie'
Image_Cached:
- PLURALNAME: Files
- SINGULARNAME: File
+ PLURALNAME: Lêers
+ SINGULARNAME: Lêer
Image_iframe.ss:
- TITLE: 'Image Uploading Iframe'
+ TITLE: 'Iframe wat fotos laai'
LeftAndMain:
CANT_REORGANISE: 'You do not have permission to alter Top level pages. Your change was not saved.'
- DELETED: Deleted.
- DropdownBatchActionsDefault: Actions
+ DELETED: 'Was verwyder'
+ DropdownBatchActionsDefault: Aksies
HELP: Help
- PAGETYPE: 'Page type: '
- PERMAGAIN: 'You have been logged out of the CMS. If you would like to log in again, enter a username and password below.'
- PERMALREADY: 'I''m sorry, but you can''t access that part of the CMS. If you want to log in as someone else, do so below'
+ PAGETYPE: 'Bladsy tipe:'
+ PERMAGAIN: 'Jy is uit die IBS uitgeteken. As jy weer wil inteken, moet jy ''n gebruikersnaam en wagwoord onder in tik'
+ PERMALREADY: 'Ek is jammer, maar jy het nie toestemming om dié gedeelte van die IBS te besugtig nie. As jy as iemand anders wil inteken doen so hieronder'
PERMDEFAULT: 'Please choose an authentication method and enter your credentials to access the CMS.'
- PLEASESAVE: 'Please Save Page: This page could not be upated because it hasn''t been saved yet.'
- PreviewButton: Preview
+ PLEASESAVE: 'Stoor asseblief die bladsy: Die bladsy kon nie opgedateer word nie omdat dit nog nie gestoor is nie'
+ PreviewButton: Beskou
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
- SAVEDUP: Saved.
+ SAVEDUP: Gestoor
VersionUnknown: Unknown
LeftAndMain_Menu.ss:
Hello: Hi
- LOGOUT: 'Log out'
+ LOGOUT: 'Teken af'
LoginAttempt:
- Email: 'Email Address'
- IP: 'IP Address'
- PLURALNAME: 'Login Attempts'
- SINGULARNAME: 'Login Attempt'
- Status: Status
+ Email: 'Epos Adres'
+ IP: 'IP Adres'
+ PLURALNAME: 'Inteken Pogings'
+ SINGULARNAME: 'Inteken Poging'
+ Status: Posisie
Member:
- ADDGROUP: 'Add group'
+ ADDGROUP: 'Voeg groep by'
BUTTONCHANGEPASSWORD: 'Verander Wagwoord'
- BUTTONLOGIN: 'Log in'
- BUTTONLOGINOTHER: 'Log in as someone else'
- BUTTONLOSTPASSWORD: 'I''ve lost my password'
- CANTEDIT: 'You don''t have permission to do that'
+ BUTTONLOGIN: 'Teken in'
+ BUTTONLOGINOTHER: 'Teken in as iemand anders'
+ BUTTONLOSTPASSWORD: 'Ek het my wagwoord verloor'
+ CANTEDIT: 'Jy het nie toestemming om dit te doen nie'
CONFIRMNEWPASSWORD: 'Bevestig Nuwe Wagwoord'
CONFIRMPASSWORD: 'Bevestig wagwoord'
- DATEFORMAT: 'Date format'
+ DATEFORMAT: 'Datum formaat'
DefaultAdminFirstname: 'Verstek Admin'
- DefaultDateTime: default
+ DefaultDateTime: Gewone
EMAIL: Epos
- EMPTYNEWPASSWORD: 'The new password can''t be empty, please try again'
- ENTEREMAIL: 'Please enter an email address to get a password reset link.'
- ERRORLOCKEDOUT: 'Your account has been temporarily disabled because of too many failed attempts at logging in. Please try again in 20 minutes.'
- ERRORNEWPASSWORD: 'You have entered your new password differently, try again'
+ EMPTYNEWPASSWORD: 'Die nuwe wagwoord kan nie leeg wees nie. Probeer asseblief weer'
+ ENTEREMAIL: 'Verskaf asseblief ''n epos adres sodat ons vir u ''n wagwoord herstel skakel kan epos'
+ ERRORLOCKEDOUT: 'Jou rekening is tydelik onklaar gemaak weens die feit dat jy te veel keer verkeerdelik probeer inteken het. Probeer asseblief weer in 20 minute.'
+ ERRORNEWPASSWORD: 'Jy het jou nuwe wagwoord anders ingetik. Probeer weer'
ERRORPASSWORDNOTMATCH: 'U huidige wagwoord pas nie, probeer asseblief weer'
- ERRORWRONGCRED: 'That doesn''t seem to be the right e-mail address or password. Please try again.'
- FIRSTNAME: 'First Name'
+ ERRORWRONGCRED: 'Dit blyk nie of dit die regte e-pos adres of wagwoord is nie. Probeer asseblief weer.'
+ FIRSTNAME: Voornaam
INTERFACELANG: 'Koppelvlak Taal'
INVALIDNEWPASSWORD: 'We couldn''t accept that password: {password}'
- LOGGEDINAS: 'You''re logged in as {name}.'
+ LOGGEDINAS: 'Jy is ingeteken as {name}'
NEWPASSWORD: 'Nuwe wagwoord'
- PASSWORD: Password
+ PASSWORD: Wagwoord
PLURALNAME: Lidde
- REMEMBERME: 'Remember me next time?'
+ REMEMBERME: 'Onthou volgende keer vir my?'
SINGULARNAME: Lid
SUBJECTPASSWORDCHANGED: 'U wagwoord het verander.'
SUBJECTPASSWORDRESET: 'U wagwoord herlaai skakel'
- SURNAME: Surname
- TIMEFORMAT: 'Time format'
- VALIDATIONMEMBEREXISTS: 'A member already exists with the same %s'
+ SURNAME: Van
+ TIMEFORMAT: 'Tyd formaat'
+ VALIDATIONMEMBEREXISTS: '''n Ander lid bestaan al klaar met dieselfde %s'
ValidationIdentifierFailed: 'Can''t overwrite existing member #{id} with identical identifier ({name} = {value}))'
- WELCOMEBACK: 'Welcome Back, {firstname}'
+ WELCOMEBACK: 'Welkom terug, {firstname}'
YOUROLDPASSWORD: 'U ou wagwoord'
belongs_many_many_Groups: Groepe
- db_LastVisited: 'Last Visited Date'
+ db_LastVisited: 'Laaste datum besoek'
db_Locale: 'Interface Locale'
db_LockedOutUntil: 'Utgesluit tot en met'
- db_NumVisit: 'Number of Visits'
- db_Password: Password
+ db_NumVisit: 'Aantal besoeke'
+ db_Password: Wagwoord
db_PasswordExpiry: 'Wagwoord Vervaldatum'
MemberAuthenticator:
- TITLE: 'E-mail & Password'
+ TITLE: 'Epos & Wagwoord'
MemberDatetimeOptionsetField:
- AMORPM: 'AM (Ante meridiem) or PM (Post meridiem)'
- 'APPLY FILTER': 'Apply Filter'
- Custom: Custom
- DATEFORMATBAD: 'Date format is invalid'
- DAYNOLEADING: 'Day of month without leading zero'
+ AMORPM: 'AM (Oggend) of PM (Middag)'
+ 'APPLY FILTER': 'Wend filter aan'
+ Custom: 'Maak pas'
+ DATEFORMATBAD: 'Die datum formaat is ongeldig'
+ DAYNOLEADING: 'Dag van die maand sonder ''n zero vooraan'
DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second'
- FOURDIGITYEAR: 'Four-digit year'
- FULLNAMEMONTH: 'Full name of month (e.g. June)'
+ FOURDIGITYEAR: 'View syfer jaar'
+ FULLNAMEMONTH: 'Volle naam van maand (bv Junie)'
HOURNOLEADING: 'Hour without leading zero'
MINUTENOLEADING: 'Minute without leading zero'
MONTHNOLEADING: 'Month digit without leading zero'
- Preview: Preview
+ Preview: Voorskou
SHORTMONTH: 'Short name of month (e.g. Jun)'
- TOGGLEHELP: 'Toggle formatting help'
- TWODIGITDAY: 'Two-digit day of month'
+ TOGGLEHELP: 'Skakel formateringshelp aan'
+ TWODIGITDAY: 'Twee syfer dag van die maand'
TWODIGITHOUR: 'Two digits of hour (00 through 23)'
TWODIGITMINUTE: 'Two digits of minute (00 through 59)'
TWODIGITMONTH: 'Two-digit month (01=January, etc.)'
TWODIGITSECOND: 'Two digits of second (00 through 59)'
- TWODIGITYEAR: 'Two-digit year'
+ TWODIGITYEAR: 'Twee syfer jaar'
MemberImportForm:
Help1: 'Import users in CSV format (comma-separated values). Show advanced usage
'
Help2: ' Advanced usage
- Allowed columns: %s
- Existing users are matched by their unique Code property, and updated with any new values from the imported file.
- Groups can be assigned by the Groups column. Groups are identified by their Code property, multiple groups can be separated by comma. Existing group memberships are not cleared.
'
@@ -415,58 +415,58 @@ af:
ResultNone: 'Geen veranderinge'
ResultUpdated: 'Updated {count} members'
MemberPassword:
- PLURALNAME: 'Member Passwords'
- SINGULARNAME: 'Member Password'
+ PLURALNAME: 'Lid Wagwoorde'
+ SINGULARNAME: 'Lid Wagwoord'
MemberTableField: null
ModelAdmin:
- DELETE: Delete
- DELETEDRECORDS: 'Deleted {count} records.'
- EMPTYBEFOREIMPORT: 'Clear Database before import'
- IMPORT: 'Import from CSV'
+ DELETE: Verwyder
+ DELETEDRECORDS: 'Verwyder {count} rekords'
+ EMPTYBEFOREIMPORT: 'Maak databasis skoon voordat data ingevoer word'
+ IMPORT: 'Voer in van CSV'
IMPORTEDRECORDS: 'Imported {count} records.'
NOCSVFILE: 'Please browse for a CSV file to import'
- NOIMPORT: 'Nothing to import'
- RESET: Reset
- Title: 'Data Models'
+ NOIMPORT: 'Niks om in te voer nie'
+ RESET: Herstel
+ Title: 'Data modelle'
UPDATEDRECORDS: 'Updated {count} records.'
ModelAdmin_ImportSpec.ss:
- IMPORTSPECFIELDS: 'Database columns'
+ IMPORTSPECFIELDS: 'Databasis kolomme'
IMPORTSPECLINK: 'Show Specification for %s'
- IMPORTSPECRELATIONS: Relations
+ IMPORTSPECRELATIONS: Verhoudings
IMPORTSPECTITLE: 'Specification for %s'
ModelAdmin_Tools.ss:
FILTER: Filter
- IMPORT: Import
+ IMPORT: 'Voer in'
ModelSidebar.ss:
- IMPORT_TAB_HEADER: Import
- SEARCHLISTINGS: Search
+ IMPORT_TAB_HEADER: 'Voer in'
+ SEARCHLISTINGS: Soek
MoneyField:
- FIELDLABELAMOUNT: Amount
- FIELDLABELCURRENCY: Currency
+ FIELDLABELAMOUNT: Bedrag
+ FIELDLABELCURRENCY: 'Geld eenheid'
NullableField:
IsNullLabel: 'Is Null'
NumericField:
VALIDATION: '''{value}'' is not a number, only numbers can be accepted for this field'
Pagination:
- Page: Page
- View: View
+ Page: Bladsy
+ View: Wys
Permission:
AdminGroup: Administrateur
CMS_ACCESS_CATEGORY: 'IBS Toegang'
- FULLADMINRIGHTS: 'Full administrative rights'
+ FULLADMINRIGHTS: 'Volle administratiewe regte '
FULLADMINRIGHTS_HELP: 'Impliseer en oorskryf alle ander toegekende permissies.'
- PLURALNAME: Permissions
- SINGULARNAME: Permission
+ PLURALNAME: Toestemmings
+ SINGULARNAME: Toestemming
PermissionCheckboxSetField:
AssignedTo: 'assigned to "{title}"'
FromGroup: 'inherited from group "{title}"'
FromRole: 'inherited from role "{title}"'
FromRoleOnGroup: 'oorgeërf van rol "%s" op groep "%s"'
PermissionRole:
- OnlyAdminCanApply: 'Only admin can apply'
- PLURALNAME: Roles
- SINGULARNAME: Role
- Title: Title
+ OnlyAdminCanApply: 'Slegs administrateur daarvoor aansoek doen'
+ PLURALNAME: Rolle
+ SINGULARNAME: Rol
+ Title: Tietel
PermissionRoleCode:
PLURALNAME: 'Permission Role Cods'
SINGULARNAME: 'Permission Role Code'
@@ -474,103 +474,103 @@ af:
PERMISSIONS_CATEGORY: 'Rolle en toegang permissies'
UserPermissionsIntro: 'Assigning groups to this user will adjust the permissions they have. See the groups section for details of permissions on individual groups.'
PhoneNumberField:
- VALIDATION: 'Please enter a valid phone number'
+ VALIDATION: 'Tik asseblief ''n geldige telefoon nommer in'
RelationComplexTableField.ss:
ADD: 'Voeg by'
- CSVEXPORT: 'Export to CSV'
- NOTFOUND: 'No items found'
+ CSVEXPORT: 'Voer uit na CSV'
+ NOTFOUND: 'Geen items gevind nie'
Security:
ALREADYLOGGEDIN: 'U het nie toegang tot hierdie bladsy nie. As u n'' ander rekening het wat toegang tot hierdie bladsy het, kan u weer inteken.'
- BUTTONSEND: 'Send me the password reset link'
- CHANGEPASSWORDBELOW: 'You can change your password below.'
- CHANGEPASSWORDHEADER: 'Change your password'
- ENTERNEWPASSWORD: 'Please enter a new password.'
- ERRORPASSWORDPERMISSION: 'You must be logged in in order to change your password!'
- LOGGEDOUT: 'You have been logged out. If you would like to log in again, enter your credentials below.'
- LOGIN: 'Log in'
- NOTEPAGESECURED: 'That page is secured. Enter your credentials below and we will send you right along.'
+ BUTTONSEND: 'Stuur vir my die wagwoord herstel skakel'
+ CHANGEPASSWORDBELOW: 'Jy kan jou wagwoord onder verander'
+ CHANGEPASSWORDHEADER: 'Verander jou wagwoord'
+ ENTERNEWPASSWORD: 'Sleutel asseblief ''n nuwe wagwoord in'
+ ERRORPASSWORDPERMISSION: 'Jy moet ingeteken wees om jou wagwoord te verander'
+ LOGGEDOUT: 'Jy is uit uitgeteken. As jy weer wil inteken, moet jy ''n gebruikersnaam en wagwoord onder in tik'
+ LOGIN: 'Teken in'
+ NOTEPAGESECURED: 'Daai bladsy is beveilig. Sleutel jou informasie onder in sodat ons jou op jou pad kan stuur'
NOTERESETLINKINVALID: 'The password reset link is invalid or expired.
You can request a new one here or change your password after you logged in.
'
- NOTERESETPASSWORD: 'Enter your e-mail address and we will send you a link with which you can reset your password'
+ NOTERESETPASSWORD: 'Sleutel you epos adres in sodat ons vir jou ''n herstel skakel kan epos'
PASSWORDSENTHEADER: 'Password reset link sent to ''{email}'''
PASSWORDSENTTEXT: 'Thank you! A reset link has been sent to ''{email}'', provided an account exists for this email address.'
SecurityAdmin:
ACCESS_HELP: 'Laat toe wys, byvoeging en verandering van gebruikers, so wel as die toekenning van permissies en rolle aan hulle.'
APPLY_ROLES: 'Wend rolle tot groepe toe'
APPLY_ROLES_HELP: 'Vermoë om rolle toegeken aan ''n groep te verander. Benodig die "Toegang tot ''Sekuriteit'' afdeling'' permissie.'
- EDITPERMISSIONS: 'Manage permissions for groups'
+ EDITPERMISSIONS: 'Bestuur toegangsregte vir groepe'
EDITPERMISSIONS_HELP: 'Vermoë om Permissies en IP Adresse vir ''n groep te verander. Benodig die "Toegang tot ''Sekuriteit'' afdeling" permissie.'
- GROUPNAME: 'Group name'
- IMPORTGROUPS: 'Import groups'
- IMPORTUSERS: 'Import users'
- MEMBERS: Members
- MENUTITLE: Security
+ GROUPNAME: 'Groep naam'
+ IMPORTGROUPS: 'Voer groepe in'
+ IMPORTUSERS: 'Belangrike gebruikers'
+ MEMBERS: Lede
+ MENUTITLE: Sekuriteit
MemberListCaution: 'Waarskuwing: Deur lede te verwyder van hierdie lys sal hulle ook van alle groepe en die databasis verwyder'
- NEWGROUP: 'New Group'
- PERMISSIONS: Permissions
+ NEWGROUP: 'Nuwe groep'
+ PERMISSIONS: Toegangsregte
ROLES: Rolle
ROLESDESCRIPTION: ' '
TABROLES: Rolle
- Users: Users
+ Users: Gebruikers
SecurityAdmin_MemberImportForm:
BtnImport: 'Voer In'
FileFieldLabel: 'CSV Lêer (Laat toe uitbreidings: *.csv)'
SilverStripeNavigator:
- Edit: Edit
+ Edit: Verander
SimpleImageField:
- NOUPLOAD: 'No Image Uploaded'
+ NOUPLOAD: 'Geen foto gelaai nie'
SiteTree:
TABMAIN: Hoof
TableField:
- ISREQUIRED: 'In %s ''%s'' is required'
+ ISREQUIRED: 'In %s ''%s'' word benodig.'
TableField.ss:
ADD: 'Voeg nuwe ry by'
- ADDITEM: 'Add %s'
+ ADDITEM: 'Voeg %s by'
TableListField:
- CSVEXPORT: 'Export to CSV'
+ CSVEXPORT: 'Voer uit na CSV lêer'
PRINT: Druk
- Print: Print
- SELECT: 'Select:'
+ Print: Druk
+ SELECT: Kies
TableListField.ss:
- NOITEMSFOUND: 'No items found'
+ NOITEMSFOUND: 'Geen item gevind nie'
SORTASC: 'Sorteer in stygende orde'
SORTDESC: 'Sorteer in dalende orde'
TableListField_PageControls.ss:
- DISPLAYING: Displaying
- OF: of
- TO: to
- VIEWFIRST: 'View first'
- VIEWLAST: 'View last'
- VIEWNEXT: 'View next'
- VIEWPREVIOUS: 'View previous'
+ DISPLAYING: 'Wys huidiglik'
+ OF: van
+ TO: na
+ VIEWFIRST: 'Wys eerste'
+ VIEWLAST: 'Wys laaste'
+ VIEWNEXT: 'Wys volgende'
+ VIEWPREVIOUS: 'Wys vorige'
TimeField:
- VALIDATEFORMAT: 'Please enter a valid time format ({format})'
+ VALIDATEFORMAT: 'Sleutel asseblief ''n geldige tyd formaat ({format})'
ToggleField:
- LESS: less
- MORE: more
+ LESS: minder
+ MORE: meer
UploadField:
- ATTACHFILE: 'Attach a file'
- ATTACHFILES: 'Attach files'
- AttachFile: 'Attach file(s)'
- DELETE: 'Delete from files'
- DELETEINFO: 'Permanently delete this file from the file store'
- DOEDIT: Save
- DROPFILE: 'drop a file'
- DROPFILES: 'drop files'
- Dimensions: Dimensions
- EDIT: Edit
- EDITINFO: 'Edit this file'
- FIELDNOTSET: 'File information not found'
- FROMCOMPUTER: 'From your computer'
- FROMCOMPUTERINFO: 'Select from files'
- FROMFILES: 'From files'
+ ATTACHFILE: 'Heg lêer aan'
+ ATTACHFILES: 'Heg lêer(s) aan'
+ AttachFile: 'Aangehegde lêer(s)'
+ DELETE: 'Verwyder van lêers af'
+ DELETEINFO: 'Wis die lêer uit die lêer stoor uit'
+ DOEDIT: Stoor
+ DROPFILE: 'Laat val ''n lêer'
+ DROPFILES: 'Skuif lêers hiernatoe'
+ Dimensions: Afmetings
+ EDIT: Verander
+ EDITINFO: 'Verander die lêer'
+ FIELDNOTSET: 'Die lêer informasie kan nie gevind word nie'
+ FROMCOMPUTER: 'Van jou rekenaar'
+ FROMCOMPUTERINFO: 'Kied uit lêers uit'
+ FROMFILES: 'Van die lêers afdeling'
HOTLINKINFO: 'Info: This image will be hotlinked. Please ensure you have permissions from the original site creator to do so.'
- MAXNUMBEROFFILES: 'Max number of {count} file(s) exceeded.'
- MAXNUMBEROFFILESSHORT: 'Can only upload {count} files'
- REMOVE: Remove
- REMOVEERROR: 'Error removing file'
- REMOVEINFO: 'Remove this file from here, but do not delete it from the file store'
- STARTALL: 'Start all'
- STARTALLINFO: 'Start all uploads'
- Saved: Saved
+ MAXNUMBEROFFILES: 'Maksimum aantal van {count} lêer(s) oorskry '
+ MAXNUMBEROFFILESSHORT: 'Kan net {count} lêer oplaai'
+ REMOVE: Verwyder
+ REMOVEERROR: 'Daar het ''n fout onstaan met die verwydering van die lêer'
+ REMOVEINFO: 'Verwyder die lêer van hier af maar moet dit nie uit die lêer stoor verwyder nie'
+ STARTALL: 'Begin alles'
+ STARTALLINFO: 'Begin op alles op te laai'
+ Saved: Gestoor
Versioned:
has_many_Versions: Weergawe
diff --git a/lang/ast.yml b/lang/ast.yml
index 15c142df6..f4cdfca14 100644
--- a/lang/ast.yml
+++ b/lang/ast.yml
@@ -480,7 +480,7 @@ ast:
CSVEXPORT: 'Export to CSV'
NOTFOUND: 'No items found'
Security:
- ALREADYLOGGEDIN: 'Nun tienes accesu a esta páxina. Si tienes otra cuenta que pueda entrar nesta páxina, puedes volver conectate.'
+ ALREADYLOGGEDIN: 'Nun tienes accesu a esta páxina. Si tienes otra cuenta que pueda entrar nesta páxina, pues volver a coneutate.'
BUTTONSEND: 'Send me the password reset link'
CHANGEPASSWORDBELOW: 'You can change your password below.'
CHANGEPASSWORDHEADER: 'Change your password'
diff --git a/lang/bg.yml b/lang/bg.yml
index b3e8b5302..83f50bb73 100644
--- a/lang/bg.yml
+++ b/lang/bg.yml
@@ -1,7 +1,7 @@
bg:
AssetAdmin:
ALLOWEDEXTS: 'Allowed extensions'
- NEWFOLDER: NewFolder
+ NEWFOLDER: НоваПапка
AssetTableField:
CREATED: Създаден
DIM: Размери
@@ -10,18 +10,18 @@ bg:
LASTEDIT: 'Последна промяна'
OWNER: Собственик
SIZE: 'Големина на файла'
- TITLE: Title
+ TITLE: Заглавие
TYPE: 'Тип на файла'
URL: URL
AssetUploadField:
ChooseFiles: 'Избери файлове'
DRAGFILESHERE: 'Завлечете файловете тук'
- DROPAREA: 'Drop Area'
+ DROPAREA: 'Зона за пускане'
EDITALL: 'Редакция на всички'
EDITANDORGANIZE: 'Редактиране и подреждане'
EDITINFO: 'Edit files'
FILES: Файлове
- FROMCOMPUTER: 'Choose files from your computer'
+ FROMCOMPUTER: 'Избери файлове от компютъра'
FROMCOMPUTERINFO: 'Upload from your computer'
TOTAL: Общо
TOUPLOAD: 'Choose files to upload...'
@@ -66,12 +66,12 @@ bg:
LOADING: 'Зареждане ...'
REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
CMSMain:
- ACCESS: 'Access to ''{title}'' section'
+ ACCESS: 'Достъп до секция ''{title}'''
ACCESSALLINTERFACES: 'Достъп до всички секции на CMS'
ACCESSALLINTERFACESHELP: 'Overrules more specific access settings.'
SAVE: Запис
CMSProfileController:
- MENUTITLE: 'My Profile'
+ MENUTITLE: 'Моят профил'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Вие сменихте вашата парола за'
CHANGEPASSWORDTEXT2: 'Вече можете да ползвате следните данни за вход:'
@@ -79,10 +79,10 @@ bg:
HELLO: Здравей!
PASSWORD: Парола
CheckboxField:
- - 'False'
- - 'True'
+ - 'не е чекнато'
+ - чекнато
ComplexTableField:
- CLOSEPOPUP: 'Close Popup'
+ CLOSEPOPUP: 'Затвори прозореца'
SUCCESSADD2: 'Беше добавен {name}'
SUCCESSEDIT: 'Съхранено %s %s %s'
ComplexTableField.ss:
@@ -94,9 +94,9 @@ bg:
NEXT: Следващо
PREVIOUS: Предишно
ConfirmedPasswordField:
- ATLEAST: 'Passwords must be at least {min} characters long.'
- BETWEEN: 'Passwords must be {min} to {max} characters long.'
- MAXIMUM: 'Passwords must be at most {max} characters long.'
+ ATLEAST: 'Паролата трябва да е дълга мин. {min} символа.'
+ BETWEEN: 'Паролата трябва да е дълга от {min} до {max} символа.'
+ MAXIMUM: 'Паролата трябва да е дълга макс. {max} символа.'
SHOWONCLICKTITLE: 'Промяна на парола'
CreditCardField:
FIRST: първи
@@ -138,10 +138,10 @@ bg:
EmailField:
VALIDATION: 'Моля, въведете имейл адрес'
Email_BounceRecord:
- PLURALNAME: 'Email Bounce Records'
- SINGULARNAME: 'Email Bounce Record'
+ PLURALNAME: 'Изпращане на отпадналите записи'
+ SINGULARNAME: 'Изпращане на отпаднал запис'
Enum:
- ANY: Any
+ ANY: Някой
File:
AviType: 'AVI video file'
Content: Съдържание
@@ -153,38 +153,38 @@ bg:
GzType: 'GZIP compressed file'
HtlType: 'HTML file'
HtmlType: 'HTML file'
- INVALIDEXTENSION: 'Extension is not allowed (valid: {extensions})'
- INVALIDEXTENSIONSHORT: 'Extension is not allowed'
+ INVALIDEXTENSION: 'Това разширение не е разрешено (разрешени са: {extensions})'
+ INVALIDEXTENSIONSHORT: 'Това разширение не е разрешено'
IcoType: 'Icon image'
JpgType: 'JPEG image - good for photos'
JsType: 'Javascript file'
Mp3Type: 'MP3 audio file'
MpgType: 'MPEG video file'
NOFILESIZE: 'Размер на файла е нула байта.'
- NOVALIDUPLOAD: 'File is not a valid upload'
+ NOVALIDUPLOAD: 'Невалиден файл за качване'
Name: Име
PLURALNAME: Файлове
PdfType: 'Adobe Acrobat PDF file'
PngType: 'PNG image - good general-purpose format'
SINGULARNAME: Файл
- TOOLARGE: 'Filesize is too large, maximum {size} allowed'
- TOOLARGESHORT: 'Filesize exceeds {size}'
+ TOOLARGE: 'Много голям файл, разрешено е до {size}'
+ TOOLARGESHORT: 'Големината на файла надхвърля {size}'
TiffType: 'Tagged image format'
- Title: Title
+ Title: Заглавие
WavType: 'WAV audo file'
XlsType: 'Excel spreadsheet'
ZipType: 'ZIP compressed file'
FileIFrameField:
- ATTACH: 'Attach {type}'
- ATTACHONCESAVED: '{type}s can be attached once you have saved the record for the first time.'
- ATTACHONCESAVED2: 'Files can be attached once you have saved the record for the first time.'
- DELETE: 'Delete {type}'
+ ATTACH: 'Прикачи {type}'
+ ATTACHONCESAVED: '{type} може да бъде прикачен след като записът се съхрани за първи път.'
+ ATTACHONCESAVED2: 'Файлове могат да бъдат прикачвани след като записът се съхрани за първи път.'
+ DELETE: 'Изтрий {type}'
DISALLOWEDFILETYPE: 'Не може да бъде качен файл от този тип'
FILE: Файл
FROMCOMPUTER: 'От компютъра'
FROMFILESTORE: 'От Файлове и Изображения'
- NOSOURCE: 'Please select a source file to attach'
- REPLACE: 'Replace {type}'
+ NOSOURCE: 'Избери файл за прикачване'
+ REPLACE: 'Замести {type}'
FileIFrameField_iframe.ss:
TITLE: 'Iframe за качване на изображение'
Filesystem:
@@ -204,30 +204,30 @@ bg:
VALIDATIONNOTUNIQUE: 'Въведената стойност не е уникална'
VALIDATIONPASSWORDSDONTMATCH: 'Паролите не съвпадат'
VALIDATIONPASSWORDSNOTEMPTY: 'Паролите не може да бъдат празни'
- VALIDATIONSTRONGPASSWORD: 'Passwords must have at least one digit and one alphanumeric character'
+ VALIDATIONSTRONGPASSWORD: 'Паролите трябва да съдържат поне една цифра и една буква.'
VALIDATOR: Валидатор
- VALIDCURRENCY: 'Please enter a valid currency'
+ VALIDCURRENCY: 'Моля, въведете коректна валута.'
FormField:
- NONE: нищо
+ NONE: никой
GridAction:
- DELETE_DESCRIPTION: Delete
- Delete: Delete
+ DELETE_DESCRIPTION: Изтриване
+ Delete: Изтрий
UnlinkRelation: Откачане
GridField:
Add: 'Добави {name}'
Filter: Филтър
FilterBy: 'Филтриране по'
- Find: Find
+ Find: Търси
LEVELUP: 'Ниво нагоре'
- LinkExisting: 'Link Existing'
+ LinkExisting: 'Свържи към съществуващ'
NewRecord: 'Нов %s'
NoItemsFound: 'Няма намерени елементи'
- PRINTEDAT: 'Printed at'
- PRINTEDBY: 'Printed by'
+ PRINTEDAT: 'Отпечатано на'
+ PRINTEDBY: 'Отпечатано от'
PlaceHolder: 'Намери {type}'
PlaceHolderWithLabels: 'Намери {type} по {name}'
- RelationSearch: 'Relation search'
- ResetFilter: Reset
+ RelationSearch: 'Търсене на връзка'
+ ResetFilter: Изчистване
GridFieldAction_Delete:
DeletePermissionsFailure: 'Изтриването не е разрешено'
GridFieldDetailForm:
@@ -241,26 +241,26 @@ bg:
GridFieldItemEditView.ss: null
Group:
AddRole: 'Добавяне на роля към групата'
- Code: 'Group Code'
+ Code: 'Код на група'
DefaultGroupTitleAdministrators: Администратори
DefaultGroupTitleContentAuthors: 'Редактори на съдържание'
Description: Описание
- GroupReminder: 'If you choose a parent group, this group will take all it''s roles'
+ GroupReminder: 'Ако изберете родителска група, тази група ще наследи всички нейни роли'
Locked: 'Заключена?'
NoRoles: 'Няма намерени роли'
PLURALNAME: Groups
- Parent: 'Parent Group'
+ Parent: 'Група източник'
RolesAddEditLink: 'Управление на ролите'
SINGULARNAME: Group
Sort: Сортиране
has_many_Permissions: Разрешения
- many_many_Members: Потребители
+ many_many_Members: Членове
GroupImportForm:
- Help1: 'Import one or more groups in CSV format (comma-separated values). Show advanced usage
'
+ Help1: 'Внасяне на една или повече групи в CSV формат (comma-separated values). Покажи начин на употреба
'
Help2: ' Advanced usage
- Allowed columns: %s
- Existing groups are matched by their unique Code value, and updated with any new values from the imported file
- Group hierarchies can be created by using a ParentCode column.
- Permission codes can be assigned by the PermissionCode column. Existing permission codes are not cleared.
'
- ResultCreated: 'Created {count} groups'
- ResultDeleted: 'Deleted %d groups'
- ResultUpdated: 'Updated %d groups'
+ ResultCreated: 'Бяха създадени {count} група/и'
+ ResultDeleted: 'Бяха изтрити %d групи'
+ ResultUpdated: 'Бяха обновени %d групи'
Hierarchy:
InfiniteLoopNotAllowed: 'Infinite loop found within the "{type}" hierarchy. Please change the parent to resolve this'
HtmlEditorField:
@@ -277,7 +277,7 @@ bg:
CSSCLASSLEFT: 'В ляво, с текст който да се нанася около него'
CSSCLASSLEFTALONE: 'В ляво, самостоятелно.'
CSSCLASSRIGHT: 'В дясно, с текст който да се нанася около него'
- DETAILS: Details
+ DETAILS: Детайли
EMAIL: 'email адрес'
FILE: Файл
FOLDER: Папка
@@ -287,7 +287,7 @@ bg:
FindInFolder: 'Прегледай папка'
IMAGEALT: 'Алтернативен текст (alt)'
IMAGEALTTEXT: 'Алтернативен текст (alt) - показва се ако изображението не е заредено'
- IMAGEALTTEXTDESC: 'Shown to screen readers or if image can not be displayed'
+ IMAGEALTTEXTDESC: 'Вижда се на екранните четци или ако картинката не може да бъде показана'
IMAGEDIMENSIONS: Размери
IMAGEHEIGHTPX: Височина
IMAGETITLE: 'Описание (tooltip) - за допълнителна информация към изображението'
@@ -306,48 +306,48 @@ bg:
LINKTO: 'Препратка към'
PAGE: Страница
URL: URL
- URLNOTANOEMBEDRESOURCE: 'The URL ''{url}'' could not be turned into a media resource.'
+ URLNOTANOEMBEDRESOURCE: 'URL адресът ''{url}'' не може да бъде превърнат в медиен ресурс.'
UpdateMEDIA: 'Актуализация на медиа'
Image:
PLURALNAME: Files
SINGULARNAME: File
ImageField:
- IMAGE: Image
+ IMAGE: Изображение
Image_Cached:
PLURALNAME: Files
SINGULARNAME: File
Image_iframe.ss:
- TITLE: 'Iframe за качване на изображение'
+ TITLE: 'Качване на изображението Iрамка'
LeftAndMain:
- CANT_REORGANISE: 'You do not have permission to alter Top level pages. Your change was not saved.'
+ CANT_REORGANISE: 'Нямаш права да променяш страници от най-горно ниво. Твоите промени не бяха записани.'
DELETED: Изтрит
DropdownBatchActionsDefault: Действия
- HELP: Help
- PAGETYPE: 'Page type: '
+ HELP: Помощ
+ PAGETYPE: 'Тип на страницата'
PERMAGAIN: 'Вие излязохте от CMS. Ако искате да влезете отново, моля, въведете потребителско име и парола.'
PERMALREADY: 'Съжалявам, но нямате достъп до тази част от CMS. Ако искате да влезете с друго потребителско име, моля, направете го по-долу'
PERMDEFAULT: 'Въведете имейл адреса и паролата си, за да влезете в CMS.'
- PLEASESAVE: 'Please Save Page: This page could not be upated because it hasn''t been saved yet.'
+ PLEASESAVE: 'Съхрани страницата: Тази страница не може да бъде обновена, защото още не е записана.'
PreviewButton: Преглед
- REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
+ REORGANISATIONSUCCESSFUL: 'Реорганизацията на дървото на сайта беше успешна.'
SAVEDUP: Записано
VersionUnknown: непозната
LeftAndMain_Menu.ss:
Hello: Здравей
- LOGOUT: 'Log out'
+ LOGOUT: Излизане
LoginAttempt:
- Email: 'Email Address'
+ Email: 'Email адрес'
IP: 'IP адрес'
PLURALNAME: 'Login Attempts'
SINGULARNAME: 'Login Attempt'
- Status: Status
+ Status: Статус
Member:
ADDGROUP: 'Добави група'
BUTTONCHANGEPASSWORD: 'Променете паролата'
BUTTONLOGIN: Влез
BUTTONLOGINOTHER: 'Влез като някой друг'
BUTTONLOSTPASSWORD: 'Загубих си паролата'
- CANTEDIT: 'You don''t have permission to do that'
+ CANTEDIT: 'Нямаш права за това действие'
CONFIRMNEWPASSWORD: 'Потвърдете новата парола'
CONFIRMPASSWORD: 'Потвърдете паролата'
DATEFORMAT: 'Date format'
@@ -355,14 +355,14 @@ bg:
DefaultDateTime: 'по подразбиране'
EMAIL: Еmail
EMPTYNEWPASSWORD: 'Не е въведена нова парола'
- ENTEREMAIL: 'Връзка за анулиране на парола'
+ ENTEREMAIL: 'Въведете email, на който ще изпратим връзка за анулиране на парола.'
ERRORLOCKEDOUT: 'Вашата сметка бе изключена временно защото имаше много неуспешни опити за влизане. Моля опитайте отново след 20 минути.'
ERRORNEWPASSWORD: 'Въвели сте новата парола различно, моля опитайте пак'
ERRORPASSWORDNOTMATCH: 'Вашата текуща парола не съвпада, моля опитайте пак'
ERRORWRONGCRED: 'Това не изглежда да е правилен email адрес или парола. Моля опитайте отново.'
FIRSTNAME: Име
INTERFACELANG: Език
- INVALIDNEWPASSWORD: 'We couldn''t accept that password: {password}'
+ INVALIDNEWPASSWORD: 'Не може да бъде приета паролата: {password}'
LOGGEDINAS: 'Вие сте влезли като {name}.'
NEWPASSWORD: 'Нова парола'
PASSWORD: Парола
@@ -389,10 +389,10 @@ bg:
MemberDatetimeOptionsetField:
AMORPM: 'АМ (преди обед) или РМ (следобед)'
'APPLY FILTER': 'Приложи филтър'
- Custom: Custom
+ Custom: Произволно
DATEFORMATBAD: 'Невалиден формат на датата'
DAYNOLEADING: 'Ден от месеца без водеща нула'
- DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second'
+ DIGITSDECFRACTIONSECOND: 'Една или повече цифри, представляващи десетичната част на секундата'
FOURDIGITYEAR: 'Четирицифрена година'
FULLNAMEMONTH: 'Пълно наименование на месец (напр. Януари)'
HOURNOLEADING: 'Час без водеща нула'
@@ -408,11 +408,11 @@ bg:
TWODIGITSECOND: 'Секунди с водеща нула (00 до 59)'
TWODIGITYEAR: 'Двуцифрена година'
MemberImportForm:
- Help1: 'Import users in CSV format (comma-separated values). Show advanced usage
'
+ Help1: 'Внасяне на потебители в CSV формат (comma-separated values). Покажи начин на употреба
'
Help2: ' Advanced usage
- Allowed columns: %s
- Existing users are matched by their unique Code property, and updated with any new values from the imported file.
- Groups can be assigned by the Groups column. Groups are identified by their Code property, multiple groups can be separated by comma. Existing group memberships are not cleared.
'
ResultCreated: 'Бяха добавени {count} потребители'
- ResultDeleted: 'Deleted %d members'
- ResultNone: 'No changes'
+ ResultDeleted: 'Бяха изтрити %d членове'
+ ResultNone: 'Нямаше промени'
ResultUpdated: 'Бяха актуализирани {count} потребители'
MemberPassword:
PLURALNAME: 'Member Passwords'
@@ -420,15 +420,15 @@ bg:
MemberTableField: null
ModelAdmin:
DELETE: Изтрий
- DELETEDRECORDS: 'Deleted {count} records.'
+ DELETEDRECORDS: 'Бяха изтрити {count} записа.'
EMPTYBEFOREIMPORT: 'Clear Database before import'
- IMPORT: 'Import from CSV'
- IMPORTEDRECORDS: 'Imported {count} records.'
+ IMPORT: 'Внасяне от CSV'
+ IMPORTEDRECORDS: 'Бяха внесени {count} записа.'
NOCSVFILE: 'Преглед на CSV файл за внасяне'
NOIMPORT: 'Нищо за внасяне'
- RESET: Reset
+ RESET: Нулиране
Title: 'Data Models'
- UPDATEDRECORDS: 'Updated {count} records.'
+ UPDATEDRECORDS: 'Бяха обновени {count} записа.'
ModelAdmin_ImportSpec.ss:
IMPORTSPECFIELDS: 'Database columns'
IMPORTSPECLINK: 'Show Specification for %s'
@@ -486,13 +486,13 @@ bg:
CHANGEPASSWORDHEADER: 'Сменете вашата парола'
ENTERNEWPASSWORD: 'Моля, въведете нова парола.'
ERRORPASSWORDPERMISSION: 'Трябва да сте влезли, за да можете да промените вашата парола!'
- LOGGEDOUT: 'Вие излязохте. Ако искате да влезете отново, въведете вашите данни по-долу.'
+ LOGGEDOUT: 'Вие излязохте. Ако искате да влезнете отново, въведете вашите данни по-долу.'
LOGIN: 'Влезте в системата'
- NOTEPAGESECURED: 'Тази страница е защитена. Въведете вашите данни по-долу, за да продължите.'
- NOTERESETLINKINVALID: 'The password reset link is invalid or expired.
You can request a new one here or change your password after you logged in.
'
- NOTERESETPASSWORD: 'Въведете вашият email адрес и ще ви изпратим линк, с който ще можете да смените паролата си'
- PASSWORDSENTHEADER: 'Password reset link sent to ''{email}'''
- PASSWORDSENTTEXT: 'Thank you! A reset link has been sent to ''{email}'', provided an account exists for this email address.'
+ NOTEPAGESECURED: 'Тази страница е защитена. Вкарайте вашите данни по-долу и ще ви препратим по-нататък.'
+ NOTERESETLINKINVALID: 'Връзката за нулиране на парола не е вярна или е просрочена.
Можете да заявите нова тук или да промените паролата си след като влезете.
'
+ NOTERESETPASSWORD: 'Въведете вашият email адрес и ще ви изпратим линк с който ще можете да смените паролата си'
+ PASSWORDSENTHEADER: 'Връзка за нулиране на парола беше изпратена на ''{email}'''
+ PASSWORDSENTTEXT: 'Благодарим ви! Връзка за нулиране на паролата беше изпратен на ''{email}'', ако съществува акаунт с този имейл адрес.'
SecurityAdmin:
ACCESS_HELP: 'Позволява преглед, добавяне и редактиране на потребители, както и задаване на разрешения и роли за тях.'
APPLY_ROLES: 'Задаване роли на групи'
@@ -503,7 +503,7 @@ bg:
IMPORTGROUPS: 'Внасяне на файл с групи'
IMPORTUSERS: 'Внасяне на файл с потребители'
MEMBERS: Потребители
- MENUTITLE: Security
+ MENUTITLE: Сигурност
MemberListCaution: 'Внимание: изтривайки потребители от този списък, ще ги премахне от всички групи и от базата данни.'
NEWGROUP: 'Нова група'
PERMISSIONS: Разрешения
@@ -521,7 +521,7 @@ bg:
SiteTree:
TABMAIN: Главно
TableField:
- ISREQUIRED: 'In %s ''%s'' is required'
+ ISREQUIRED: 'В %s е необходимо ''%s'''
TableField.ss:
ADD: 'Добави нов ред'
ADDITEM: 'Add %s'
@@ -532,8 +532,8 @@ bg:
SELECT: 'Избери:'
TableListField.ss:
NOITEMSFOUND: 'No items found'
- SORTASC: 'Sort in ascending order'
- SORTDESC: 'Sort in descending order'
+ SORTASC: 'Сортирай възходящо'
+ SORTDESC: 'Сортирай низходящо'
TableListField_PageControls.ss:
DISPLAYING: Displaying
OF: of
@@ -554,22 +554,22 @@ bg:
DELETE: 'Delete from files'
DELETEINFO: 'Изтрий файла от сървъра'
DOEDIT: Запис
- DROPFILE: 'drop a file'
- DROPFILES: 'drop files'
+ DROPFILE: 'пуснете файл'
+ DROPFILES: 'пускане на файлове'
Dimensions: Размери
EDIT: Edit
EDITINFO: 'Редактирай този файл'
- FIELDNOTSET: 'File information not found'
+ FIELDNOTSET: 'Информация за файла не беше намерена'
FROMCOMPUTER: 'От компютъра'
FROMCOMPUTERINFO: 'Select from files'
- FROMFILES: 'From files'
+ FROMFILES: 'От файлове'
HOTLINKINFO: 'Info: This image will be hotlinked. Please ensure you have permissions from the original site creator to do so.'
MAXNUMBEROFFILES: 'Максималния брой файлове ({count}) е надхвърлен.'
MAXNUMBEROFFILESSHORT: 'Максималният брой файлове за качване е {count}'
REMOVE: Премахни
REMOVEERROR: 'Грешка при премахване на файл'
REMOVEINFO: 'Премахни файла без да го изтриваш'
- STARTALL: 'Start all'
+ STARTALL: 'Старт на всички'
STARTALLINFO: 'Start all uploads'
Saved: Записано
Versioned:
diff --git a/lang/cs.yml b/lang/cs.yml
index dfdc5157c..67ea86f8b 100644
--- a/lang/cs.yml
+++ b/lang/cs.yml
@@ -1,6 +1,6 @@
cs:
AssetAdmin:
- ALLOWEDEXTS: 'Allowed extensions'
+ ALLOWEDEXTS: 'Povolené extenze'
NEWFOLDER: 'Nová složka'
AssetTableField:
CREATED: 'Poprvé nahráno'
@@ -119,7 +119,7 @@ cs:
MONTHS: měsíce
SEC: sekunda
SECS: sekundy
- TIMEDIFFAGO: '{difference} před'
+ TIMEDIFFAGO: 'před {difference}'
TIMEDIFFIN: 'v {difference}'
YEAR: rok
YEARS: roky
@@ -149,14 +149,14 @@ cs:
DmgType: 'Apple obraz disku'
DocType: 'Word dokument'
Filename: 'Jméno souboru'
- GifType: 'GIF obrázke - vhodné pro diagramy'
+ GifType: 'GIF obrázek - vhodné pro diagramy'
GzType: 'GZIP komprimační soubor'
HtlType: 'HTML soubor'
HtmlType: 'HTML soubor'
INVALIDEXTENSION: 'Extenze není povolena (platné: {extensions})'
INVALIDEXTENSIONSHORT: 'Extenze není povolena'
- IcoType: 'Icon obrázkek'
- JpgType: 'JPEG obrázke - vhodné pro fotografie'
+ IcoType: 'Ikona obrázek'
+ JpgType: 'JPEG obrázek - vhodné pro fotografie'
JsType: 'Javascript soubor'
Mp3Type: 'MP3 audio soubor'
MpgType: 'MPEG video soubor'
@@ -503,7 +503,7 @@ cs:
IMPORTGROUPS: 'Importovat skupiny'
IMPORTUSERS: 'Importovat uživaté'
MEMBERS: Členové
- MENUTITLE: Bezbečnost
+ MENUTITLE: Bezpečnost
MemberListCaution: 'Varování: Odstranění členů z tohoto seznamu způsobí, že členové budou odtraněni ze všech skupin a databáze'
NEWGROUP: 'Nová skupina'
PERMISSIONS: Práva
diff --git a/lang/de.yml b/lang/de.yml
index eaf5cda3b..b52876873 100644
--- a/lang/de.yml
+++ b/lang/de.yml
@@ -7,7 +7,7 @@ de:
DIM: Dimensionen
FILENAME: Dateiname
FOLDER: Ordner
- LASTEDIT: 'Letzte Änderung'
+ LASTEDIT: 'Letztmals geändert'
OWNER: Eigentümer
SIZE: Größe
TITLE: Titel
@@ -73,7 +73,7 @@ de:
CMSProfileController:
MENUTITLE: 'Mein Profil'
ChangePasswordEmail.ss:
- CHANGEPASSWORDTEXT1: 'Sie haben Ihr Passwort geändert für'
+ CHANGEPASSWORDTEXT1: 'Sie haben ihr Passwort geändert für'
CHANGEPASSWORDTEXT2: 'Sie können nun folgende Angaben benutzen um sich einzuloggen'
EMAIL: E-Mail
HELLO: Hi
@@ -424,7 +424,7 @@ de:
EMPTYBEFOREIMPORT: 'Datenbank vor Import leeren'
IMPORT: 'CSV Import'
IMPORTEDRECORDS: '{count} Datensätze wurden importiert.'
- NOCSVFILE: 'Wählen Sie eine CSV-Datei zum Importieren'
+ NOCSVFILE: 'Wählen sie eine CSV-Datei zum Importieren'
NOIMPORT: 'Kein Import notwendig.'
RESET: Zurücksetzen
Title: Datenmodelle
@@ -526,7 +526,7 @@ de:
ADD: 'Eine neue Zeile hinzufügen'
ADDITEM: '%s hinzufügen'
TableListField:
- CSVEXPORT: 'Als CSV-Datei exportieren'
+ CSVEXPORT: 'Exportieren zu CSV'
PRINT: drucken
Print: Drucken
SELECT: 'Auswählen:'
diff --git a/lang/en.yml b/lang/en.yml
index 21bb56a7a..6fec27a88 100644
--- a/lang/en.yml
+++ b/lang/en.yml
@@ -1,7 +1,7 @@
en:
AssetAdmin:
- ADDFILES: 'Add files'
- EditOrgMenu: 'Edit & organize'
+ ALLOWEDEXTS: 'Allowed extensions'
+ SHOWALLOWEDEXTS: 'Show allowed extensions'
NEWFOLDER: NewFolder
AssetTableField:
CREATED: 'First uploaded'
@@ -176,7 +176,7 @@ en:
TEXT2: 'password reset link'
TEXT3: for
Form:
- FIELDISREQUIRED: '%s is required'
+ FIELDISREQUIRED: '{name} is required'
SubmitBtnLabel: Go
VALIDATIONCREDITNUMBER: 'Please ensure you have entered the {number} credit card number correctly'
VALIDATIONNOTUNIQUE: 'The value entered is not unique'
@@ -215,7 +215,7 @@ en:
DeletePermissionsFailure: 'No delete permissions'
Deleted: 'Deleted %s %s'
Save: Save
- Saved: 'Saved %s %s'
+ Saved: 'Saved {name} {link}'
GridFieldEditButton.ss:
EDIT: Edit
GridFieldItemEditView.ss:
diff --git a/lang/fi.yml b/lang/fi.yml
index fd2e7a41a..71ee073fb 100644
--- a/lang/fi.yml
+++ b/lang/fi.yml
@@ -1,6 +1,6 @@
fi:
AssetAdmin:
- ALLOWEDEXTS: 'Allowed extensions'
+ ALLOWEDEXTS: 'Sallitut laajennukset'
NEWFOLDER: 'Uusi kansio'
AssetTableField:
CREATED: 'Ensimmäisen kerran ladattu palvelimelle'
@@ -347,7 +347,7 @@ fi:
BUTTONLOGIN: 'Kirjaudu sisään'
BUTTONLOGINOTHER: 'Kirjaudu jonain muuna'
BUTTONLOSTPASSWORD: 'Kadotin salasanani'
- CANTEDIT: 'You don''t have permission to do that'
+ CANTEDIT: 'Sinulla ei ole oikeuksia tähän toimintoon.'
CONFIRMNEWPASSWORD: 'Syötä uusi salasana uudelleen'
CONFIRMPASSWORD: 'Syötä salasana uudelleen'
DATEFORMAT: Päivämäärämuoto
@@ -472,7 +472,7 @@ fi:
SINGULARNAME: 'Käyttöoikeiden roolin koodi'
Permissions:
PERMISSIONS_CATEGORY: 'Roolit ja käyttöoikeudet'
- UserPermissionsIntro: 'Assigning groups to this user will adjust the permissions they have. See the groups section for details of permissions on individual groups.'
+ UserPermissionsIntro: 'Määriteltäessä käyttäjälle ryhmä, hänen käyttöoikeutensa mukautuvat ryhmälle tehtyjen asetusten mukaisesti. Katso tarkemmat ryhmäkohtaiset käyttöoikeusasetukset Ryhmät-välilehdeltä.'
PhoneNumberField:
VALIDATION: 'Kirjoita pätevä puhelinnumero'
RelationComplexTableField.ss:
diff --git a/lang/fr.yml b/lang/fr.yml
index c39aa29c0..0ff29d77d 100644
--- a/lang/fr.yml
+++ b/lang/fr.yml
@@ -74,7 +74,7 @@ fr:
MENUTITLE: 'Mon profil'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Vous avez modifié votre mot de passe pour'
- CHANGEPASSWORDTEXT2: 'Vous pouvez maintenant utiliser les identifiants suivants pour vous connecter :'
+ CHANGEPASSWORDTEXT2: 'Vous pouvez maintenant utiliser les détails suivants pour vous connecter :'
EMAIL: Email
HELLO: Salut
PASSWORD: 'Mot de passe'
@@ -253,7 +253,7 @@ fr:
RolesAddEditLink: 'Ajouter/éditer les rôles'
SINGULARNAME: Groupe
Sort: 'Ordre de tri'
- has_many_Permissions: Autorisations
+ has_many_Permissions: Permissions
many_many_Members: Membres
GroupImportForm:
Help1: 'Importer un ou plusieurs groupe(s) au format CSV (comma-separated values). Montrer l''usage avancé
'
@@ -323,11 +323,11 @@ fr:
DELETED: Supprimé.
DropdownBatchActionsDefault: Actions
HELP: Aide
- PAGETYPE: 'Type de page :'
+ PAGETYPE: 'Type de page :'
PERMAGAIN: 'Vous avez été déconnecté du CMS. Si vous voulez vous reconnecter, entrez un nom d''utilisateur et un mot de passe ci-dessous.'
PERMALREADY: 'Désolé, mais vous ne pouvez pas accéder à cette partie du CMS. Si vous voulez changer d''identité, faites le ci-dessous'
PERMDEFAULT: 'Saisissez votre adresse de courriel et votre mot de passe pour accéder au CMS.'
- PLEASESAVE: 'Enregistrez la page s’il vous plaît : elle ne pouvait pas être mise à jour car elle n’avait pas encore été sauvegardée.'
+ PLEASESAVE: 'Enregistez la page s''il vous plaît : Cette page ne pouvait pas être actualisée, car elle n''a pas encore été enregistrée.'
PreviewButton: Aperçu
REORGANISATIONSUCCESSFUL: 'L’arbre du site a été bien réorganisé.'
SAVEDUP: Enregistré.
@@ -454,7 +454,7 @@ fr:
AdminGroup: Administrateur
CMS_ACCESS_CATEGORY: 'Accès au CMS'
FULLADMINRIGHTS: 'Droits d''administration complets'
- FULLADMINRIGHTS_HELP: 'Implique et prévaut sur toutes les autres autorisations assignées.'
+ FULLADMINRIGHTS_HELP: 'Implique et écrase toute les autres permissions assignées.'
PLURALNAME: Autorisations
SINGULARNAME: Autorisation
PermissionCheckboxSetField:
@@ -494,26 +494,26 @@ fr:
PASSWORDSENTHEADER: 'Lien de réinitialisation de mot de passe envoyé à « {email} »'
PASSWORDSENTTEXT: 'Merci ! Un lien de réinitialisation vient d’être envoyé à « {email} », à condition que cette adresse existe.'
SecurityAdmin:
- ACCESS_HELP: 'Permet de consulter, d’ajouter et d’éditer les utilisateurs, aussi bien que de leur assigner des autorisations et des rôles.'
+ ACCESS_HELP: 'Permettre la visualisation, l''addition et l''édition des utilisateurs, aussi bien que leur assigner des permissions et des rôles.'
APPLY_ROLES: 'Appliquer des rôles aux groupes'
- APPLY_ROLES_HELP: 'Possibilité d''éditer les rôle assignés à un groupe. Nécessite l’autorisation « Accès à la section “Utilisateurs” ».'
- EDITPERMISSIONS: 'Gérer les autorisations des groupes'
- EDITPERMISSIONS_HELP: 'Possibilité d''éditer les autorisations et les adresses IP pour un groupe. Nécessite l’autorisation « Accès à la section “Securité” ».'
+ APPLY_ROLES_HELP: 'Possibilité d''éditer les rôles assignés à un groupe. Nécessite "Access to ''Security'' section".'
+ EDITPERMISSIONS: 'Gérer les permissions des groupes'
+ EDITPERMISSIONS_HELP: 'Possibilité d''éditer les permissions et les l''adresses IP pour un groupe. Nécessite "Access to ''Security'' section".'
GROUPNAME: 'Nom du group'
IMPORTGROUPS: 'Importer groupes'
IMPORTUSERS: 'Importer utilisateurs'
MEMBERS: Membres
MENUTITLE: Sécurité
- MemberListCaution: 'Attention : en supprimant des membres de cette liste vous les enlèverez de tous les groupes ainsi que de la base de données'
- NEWGROUP: 'Nouveau groupe'
- PERMISSIONS: Autorisations
+ MemberListCaution: 'Attention : Enlever des membres de cette liste va les enlever de tous les groupes et de la base de donnée'
+ NEWGROUP: 'Nouveau Groupe'
+ PERMISSIONS: Permissions
ROLES: Rôles
- ROLESDESCRIPTION: 'Les rôles sont des regroupements logiques d’autorisations qui peuvent être assignés à des groupes.
Ils peuvent être hérités de groupes parents, si nécessaire.'
+ ROLESDESCRIPTION: 'Cette section vous permet d''ajouter des rôles à ce groupe. Les rôles sont des regroupements logiques d''autorisations, qui peuvent être modifiés dans l''onglet Rôles'
TABROLES: Rôles
Users: Utilisateurs
SecurityAdmin_MemberImportForm:
BtnImport: Importer
- FileFieldLabel: 'Fichier CSV (extension autorisée : *.csv)'
+ FileFieldLabel: 'Fichier CSV (Extension permise: *.csv)'
SilverStripeNavigator:
Edit: 'Tout modifier'
SimpleImageField:
@@ -529,7 +529,7 @@ fr:
CSVEXPORT: 'Exporter vers un fichier CSV'
PRINT: Imprimer
Print: Imprimer
- SELECT: 'Sélectionner :'
+ SELECT: 'Sélectionner:'
TableListField.ss:
NOITEMSFOUND: 'Aucun élément n’a été trouvé'
SORTASC: 'Classer en ordre croissant'
diff --git a/lang/it.yml b/lang/it.yml
index fb827f710..7448d91f4 100644
--- a/lang/it.yml
+++ b/lang/it.yml
@@ -3,15 +3,15 @@ it:
ALLOWEDEXTS: 'Allowed extensions'
NEWFOLDER: NuovaCartella
AssetTableField:
- CREATED: 'Inizialmente caricato'
+ CREATED: 'Primo inserito'
DIM: Dimensioni
FILENAME: 'Nome del file'
FOLDER: Cartella
- LASTEDIT: 'Ultima modifica'
+ LASTEDIT: 'Ultimo modificato'
OWNER: Proprietario
SIZE: Dimensione
TITLE: Titolo
- TYPE: 'Tipo di file'
+ TYPE: Tipo
URL: URL
AssetUploadField:
ChooseFiles: 'Scegli file'
@@ -67,14 +67,14 @@ it:
REQUIREJS: 'Il CMS richiede JavaScript abilitato.'
CMSMain:
ACCESS: 'Accesso alla sezione ''{title}'''
- ACCESSALLINTERFACES: 'Accesso a tutte le sezioni del CMS'
+ ACCESSALLINTERFACES: 'Accesso a tutte le interfaccia CMS'
ACCESSALLINTERFACESHELP: 'Annulla le impostazioni di accesso più specifiche.'
SAVE: Salva
CMSProfileController:
MENUTITLE: 'Il mio Profilo'
ChangePasswordEmail.ss:
CHANGEPASSWORDTEXT1: 'Hai cambiato la password per'
- CHANGEPASSWORDTEXT2: 'Ora puoi utilizzare le seguenti credenziali per accedere:'
+ CHANGEPASSWORDTEXT2: 'Puoi ora utilizzare le seguenti credenziali per accedere:'
EMAIL: Email
HELLO: Ciao
PASSWORD: Password
@@ -86,10 +86,10 @@ it:
SUCCESSADD2: 'Aggiunto {name}'
SUCCESSEDIT: 'Salvato %s %s %s'
ComplexTableField.ss:
- ADDITEM: 'Inserisci %s'
+ ADDITEM: 'Aggiungi %s'
NOITEMSFOUND: 'Nessun elemento trovato'
- SORTASC: 'Ordina in modo ascendente'
- SORTDESC: 'Ordina in modo discendente'
+ SORTASC: 'Ordina in modo crescente'
+ SORTDESC: 'Ordina in modo decrescente'
ComplexTableField_popup.ss:
NEXT: Prossimo
PREVIOUS: Precedente
@@ -97,7 +97,7 @@ it:
ATLEAST: 'La password deve essere lunga almeno {min} caratteri.'
BETWEEN: 'La password deve essere lunga da {min} a {max} caratteri.'
MAXIMUM: 'La password deve essere lunga almeno {max} caratteri.'
- SHOWONCLICKTITLE: 'Cambia password'
+ SHOWONCLICKTITLE: 'Cambia la password'
CreditCardField:
FIRST: primo
FOURTH: quarto
@@ -106,8 +106,8 @@ it:
CurrencyField:
CURRENCYSYMBOL: $
DataObject:
- PLURALNAME: 'Data Objects'
- SINGULARNAME: 'Data Object'
+ PLURALNAME: 'Oggetti dati'
+ SINGULARNAME: 'Oggetto dati'
Date:
DAY: giorno
DAYS: giorni
@@ -250,10 +250,10 @@ it:
NoRoles: 'Nessun ruolo trovato'
PLURALNAME: Gruppi
Parent: 'Gruppo padre'
- RolesAddEditLink: 'Gestisci ruoli'
+ RolesAddEditLink: 'Aggiungi/modifica ruoli'
SINGULARNAME: Gruppo
Sort: 'Tipo ordinamento'
- has_many_Permissions: Permessi
+ has_many_Permissions: Autorizzazioni
many_many_Members: Membri
GroupImportForm:
Help1: 'Importa gruppi in formato CSV (valori separati da virgole). Mostra utilizzo avanzato
'
@@ -345,7 +345,7 @@ it:
ADDGROUP: 'Aggiungi gruppo'
BUTTONCHANGEPASSWORD: 'Cambia password'
BUTTONLOGIN: Accedi
- BUTTONLOGINOTHER: 'Autenticati come qualcun altro'
+ BUTTONLOGINOTHER: 'Autenticato come qualcun altro'
BUTTONLOSTPASSWORD: 'Ho perso la mia password'
CANTEDIT: 'You don''t have permission to do that'
CONFIRMNEWPASSWORD: 'Conferma nuova password'
@@ -355,25 +355,25 @@ it:
DefaultDateTime: predefinito
EMAIL: Email
EMPTYNEWPASSWORD: 'La nuova password non può essere vuota, riprova'
- ENTEREMAIL: 'Inserisci un indirizzo e-mail per ricevere il link di azzeramento della password'
+ ENTEREMAIL: 'Indica un indirizzo e-mail per ricevere il collegamento di azzeramento della password'
ERRORLOCKEDOUT: 'Il tuo account è stato temporaneamente disabilitato perchè ci sono stati troppi tentativi di accesso errati. Riprova tra 20 minuti.'
ERRORNEWPASSWORD: 'Hai inserito la tua nuova password in modo differente, prova di nuovo'
ERRORPASSWORDNOTMATCH: 'La tua password attuale non corrisponde, per favore prova ancora'
- ERRORWRONGCRED: 'E-mail o password non sembrano essere corretti. Per favore, prova di nuovo.'
+ ERRORWRONGCRED: 'Non sembra esserci l''indirizzo e-mail corretto o la password. Per favore, prova di nuovo.'
FIRSTNAME: Nome
INTERFACELANG: 'Lingua dell''interfaccia'
INVALIDNEWPASSWORD: 'Non possiamo accettare questa password: {password}'
LOGGEDINAS: 'Sei collegato come {name}.'
NEWPASSWORD: 'Nuova password'
PASSWORD: Password
- PLURALNAME: Utenti
+ PLURALNAME: Membri
REMEMBERME: 'Ricordati di me la prossima volta?'
- SINGULARNAME: Utente
+ SINGULARNAME: Membro
SUBJECTPASSWORDCHANGED: 'La tua password è stata cambiata'
- SUBJECTPASSWORDRESET: 'Link per azzerare la tua password'
+ SUBJECTPASSWORDRESET: 'Indirizzo per reimpostare la tua password'
SURNAME: Cognome
TIMEFORMAT: 'Formato dell''ora'
- VALIDATIONMEMBEREXISTS: 'Esiste già un utente con l''e-mail %s'
+ VALIDATIONMEMBEREXISTS: 'Esiste già un membro con questa e-mail'
ValidationIdentifierFailed: 'Non posso sovrascrivere l''utente esistente #{id} con identificatore identico ({name} = {value}))'
WELCOMEBACK: 'Bentornato, {firstname}'
YOUROLDPASSWORD: 'La tua vecchia password'
@@ -419,12 +419,12 @@ it:
SINGULARNAME: 'Password utente'
MemberTableField: null
ModelAdmin:
- DELETE: Elimina
+ DELETE: Cancella
DELETEDRECORDS: 'Eliminati {count} record.'
EMPTYBEFOREIMPORT: 'Cancella database prima dell''import'
IMPORT: 'Importa da CSV'
IMPORTEDRECORDS: 'Importati {count} record.'
- NOCSVFILE: 'Scegli un file CSV da importare'
+ NOCSVFILE: 'Cerca un file CSV da importare'
NOIMPORT: 'Nulla da importare.'
RESET: Azzera
Title: 'Modelli di dati'
@@ -441,7 +441,7 @@ it:
IMPORT_TAB_HEADER: Importa
SEARCHLISTINGS: Cerca
MoneyField:
- FIELDLABELAMOUNT: Importo
+ FIELDLABELAMOUNT: Totale
FIELDLABELCURRENCY: Valuta
NullableField:
IsNullLabel: 'è nullo.'
@@ -461,7 +461,7 @@ it:
AssignedTo: 'assegnato a "{title}"'
FromGroup: 'ereditato dal gruppo "{title}"'
FromRole: 'ereditato dal ruolo "{title}"'
- FromRoleOnGroup: 'ereditato dal ruolo "%s" nel gruppo "%s"'
+ FromRoleOnGroup: 'eredita dal ruolo "%s" sul gruppo "%s"'
PermissionRole:
OnlyAdminCanApply: 'Solo l''amministratore può applicare'
PLURALNAME: Ruoli
@@ -481,16 +481,16 @@ it:
NOTFOUND: 'Nessun elemento trovato'
Security:
ALREADYLOGGEDIN: 'Non hai accesso a questa pagina. Se hai un altro account che può accederci, puoi autenticarti qui sotto.'
- BUTTONSEND: 'Inviami il link per azzerare la password'
- CHANGEPASSWORDBELOW: 'Puoi cambiare la tua password qui sotto.'
+ BUTTONSEND: 'Inviami il link per reimpostare la password'
+ CHANGEPASSWORDBELOW: 'Puoi cambiare la tua password qui di seguito.'
CHANGEPASSWORDHEADER: 'Cambia la tua password'
ENTERNEWPASSWORD: 'Per favore inserisci una nuova password.'
ERRORPASSWORDPERMISSION: 'Devi essere autenticato per poter cambiare la tua password!'
- LOGGEDOUT: 'Sei stato disconnesso. Se vuoi autenticarti nuovamente, inserisci qui sotto le tue credenziali.'
+ LOGGEDOUT: 'Sei stato sloggato. Se vuoi autenticarti nuovamente, inserisci qui sotto le tue credenziali.'
LOGIN: Entra
- NOTEPAGESECURED: 'La pagina è protetta. Inserisci le credenziali qui sotto per poter andare avanti.'
+ NOTEPAGESECURED: 'La pagina è sicura. Inserisci le credenziali qui di seguito per poter andare avanti.'
NOTERESETLINKINVALID: 'Il link per azzerare la password non è valido o è scaduto.
Puoi richiederne uno nuovo qui o cambiare la tua password dopo che ti sei connesso.
'
- NOTERESETPASSWORD: 'Inserisci il tuo indirizzo e-mail e ti verrà inviato un link per poter azzerare la tua password.'
+ NOTERESETPASSWORD: 'Inserisci il tuo indirizzo e-mail e ti verrà inviato un link per poter reimpostare la tua password.'
PASSWORDSENTHEADER: 'Link per azzeramento della password inviato a ''{email}'''
PASSWORDSENTTEXT: 'Grazie! Un link di azzeramento è stato inviato a ''{email}'', fornito un account esistente per questo indirizzo e-mail.'
SecurityAdmin:
diff --git a/lang/ja_JP.yml b/lang/ja_JP.yml
index d2c6102a3..5cad90b9c 100644
--- a/lang/ja_JP.yml
+++ b/lang/ja_JP.yml
@@ -86,7 +86,7 @@ ja_JP:
SUCCESSADD2: '{name}を追加しました'
SUCCESSEDIT: '更新日時 %s %s %s'
ComplexTableField.ss:
- ADDITEM: '%sを追加する'
+ ADDITEM: '%sを追加'
NOITEMSFOUND: 項目が見つかりませんでした
SORTASC: 昇順
SORTDESC: ソート(下順)
diff --git a/lang/mi_NZ.yml b/lang/mi_NZ.yml
index 9e3a035ff..2dd2e714e 100644
--- a/lang/mi_NZ.yml
+++ b/lang/mi_NZ.yml
@@ -4,14 +4,14 @@ mi_NZ:
NEWFOLDER: KōpakiHōu
AssetTableField:
CREATED: 'Tukuatu tuatahi'
- DIM: 'Ngā Rahinga'
+ DIM: Nuinga
FILENAME: 'Ingoa Kōnae'
FOLDER: Kōpaki
LASTEDIT: 'Hurihanga tōmuri'
OWNER: Kaiūmanga
SIZE: Nuinga
- TITLE: Taitara
- TYPE: 'Momo kōnae'
+ TITLE: 'Ingoa '
+ TYPE: 'Tūmomo '
URL: PRO
AssetUploadField:
ChooseFiles: 'Kōwhiri kōnae'
@@ -69,7 +69,7 @@ mi_NZ:
ACCESS: 'Uru ki te wāhanga ''{title}'''
ACCESSALLINTERFACES: 'Uru ki ngā wāhanga CMS katoa'
ACCESSALLINTERFACESHELP: 'Ka takahi i ngā tautuhinga uru tauwhāiti ake'
- SAVE: Tiaki
+ SAVE: tiakina
CMSProfileController:
MENUTITLE: 'My Profile'
ChangePasswordEmail.ss:
@@ -505,7 +505,7 @@ mi_NZ:
MEMBERS: 'Ngā Mema'
MENUTITLE: Haumarutanga
MemberListCaution: 'Whakatūpato: Mā te tango mema i tēnei rārangi, ka tangohia i ngā rōpū katoa me te pātengi raraunga'
- NEWGROUP: 'Rōpū Hōu'
+ NEWGROUP: 'Roopu hou'
PERMISSIONS: 'Ngā Whakaaetanga'
ROLES: 'Ngā Tūnga'
ROLESDESCRIPTION: 'Ko ngā tūnga he huinga o ngā whakaaetanga i tautuhia i mua, ā, ka taea te tautapa i ēnei ki ngā rōpū.
I tukuna iho i ngā rōpū matua ki te hiahiatia.'
diff --git a/lang/nl.yml b/lang/nl.yml
index 0bdf29a93..016bc4097 100644
--- a/lang/nl.yml
+++ b/lang/nl.yml
@@ -17,15 +17,15 @@ nl:
ChooseFiles: 'Selecteer bestanden'
DRAGFILESHERE: 'Sleep bestanden hiernaar toe'
DROPAREA: 'Drop Area'
- EDITALL: 'Edit all'
+ EDITALL: 'Alle bewerken'
EDITANDORGANIZE: 'Bewerk en beheer'
EDITINFO: 'Edit files'
- FILES: Files
+ FILES: Bestanden
FROMCOMPUTER: 'Choose files from your computer'
FROMCOMPUTERINFO: 'Upload from your computer'
- TOTAL: Total
+ TOTAL: Totaal
TOUPLOAD: 'Choose files to upload...'
- UPLOADINPROGRESS: 'Please wait… upload in progress'
+ UPLOADINPROGRESS: 'Even geduld... bezig met uploaden'
UPLOADOR: OF
BBCodeParser:
ALIGNEMENT: Uitlijning
@@ -63,7 +63,7 @@ nl:
ANY: Elke
1: Ja
CMSLoadingScreen.ss:
- LOADING: Loading...
+ LOADING: 'Bezig met laden...'
REQUIREJS: 'The CMS requires that you have JavaScript enabled.'
CMSMain:
ACCESS: 'Toegang tot het ''{title}'' gedeelte'
@@ -119,7 +119,7 @@ nl:
MONTHS: maanden
SEC: seconde
SECS: seconden
- TIMEDIFFAGO: '{difference} ago'
+ TIMEDIFFAGO: '{difference} geleden'
TIMEDIFFIN: 'in {difference}'
YEAR: jaar
YEARS: jaren
@@ -338,8 +338,8 @@ nl:
LoginAttempt:
Email: 'Email adres '
IP: 'IP Adres'
- PLURALNAME: 'Login Attempts'
- SINGULARNAME: 'Login Attempt'
+ PLURALNAME: 'Pogingen om in te loggen'
+ SINGULARNAME: 'Poging om in te loggen'
Status: Status
Member:
ADDGROUP: 'Add group'
@@ -393,7 +393,7 @@ nl:
DATEFORMATBAD: 'Datum is niet correct opgegeven'
DAYNOLEADING: 'Dag van de maand zonder voorloop-nul'
DIGITSDECFRACTIONSECOND: 'One or more digits representing a decimal fraction of a second'
- FOURDIGITYEAR: 'Four-digit year'
+ FOURDIGITYEAR: 'jaar (yyyy)'
FULLNAMEMONTH: 'Full name of month (e.g. June)'
HOURNOLEADING: 'Hour without leading zero'
MINUTENOLEADING: 'Minute without leading zero'
@@ -446,7 +446,7 @@ nl:
NullableField:
IsNullLabel: 'is nul'
NumericField:
- VALIDATION: '''{value}'' is not a number, only numbers can be accepted for this field'
+ VALIDATION: '''{value}'' is geen getal. Dit velt accepteert alleen getallen.'
Pagination:
Page: Page
View: View
@@ -458,7 +458,7 @@ nl:
PLURALNAME: Permissions
SINGULARNAME: Permission
PermissionCheckboxSetField:
- AssignedTo: 'assigned to "{title}"'
+ AssignedTo: 'toegewezen aan "{title}"'
FromGroup: 'inherited from group "{title}"'
FromRole: 'inherited from role "{title}"'
FromRoleOnGroup: 'geërfd van rol "%s" in groep "%s"'
@@ -466,7 +466,7 @@ nl:
OnlyAdminCanApply: 'Only admin can apply'
PLURALNAME: Roles
SINGULARNAME: Role
- Title: Title
+ Title: Titel
PermissionRoleCode:
PLURALNAME: 'Permission Role Cods'
SINGULARNAME: 'Permission Role Code'
@@ -528,7 +528,7 @@ nl:
TableListField:
CSVEXPORT: 'Exporteer naar CSV'
PRINT: Afdrukken
- Print: Print
+ Print: Afdrukken
SELECT: 'Selecteer:'
TableListField.ss:
NOITEMSFOUND: 'No items found'
diff --git a/lang/ro.yml b/lang/ro.yml
index 6c72973bc..61cac0920 100644
--- a/lang/ro.yml
+++ b/lang/ro.yml
@@ -380,7 +380,7 @@ ro:
belongs_many_many_Groups: Grupuri
db_LastVisited: 'Data ultimei vizite'
db_Locale: 'Interface Locale'
- db_LockedOutUntil: 'Blocat pana la'
+ db_LockedOutUntil: 'Blocat pana cand'
db_NumVisit: 'Numarul de vizite'
db_Password: Parola
db_PasswordExpiry: 'Data de Expirare a Parolei'
diff --git a/lang/sk.yml b/lang/sk.yml
index 31ec9ff75..29eed7414 100644
--- a/lang/sk.yml
+++ b/lang/sk.yml
@@ -1,7 +1,7 @@
sk:
AssetAdmin:
- ALLOWEDEXTS: 'Allowed extensions'
- NEWFOLDER: 'Nový priečinok'
+ ALLOWEDEXTS: 'Povolené extenzie'
+ NEWFOLDER: 'Nový Adresár'
AssetTableField:
CREATED: 'Prvýkrát nahrané'
DIM: Rozmery
@@ -155,7 +155,7 @@ sk:
HtmlType: 'HTML súbor'
INVALIDEXTENSION: 'Extenzia nie je povolená (platné: {extensions})'
INVALIDEXTENSIONSHORT: 'Extenzia nie je povolená'
- IcoType: 'Icon obrázok'
+ IcoType: 'Ikona obrázok'
JpgType: 'JPEG obrázok - vhodné pre fotografie'
JsType: 'Javascript súbor'
Mp3Type: 'MP3 audio súbor'
@@ -240,21 +240,21 @@ sk:
Saved: 'Uložené %s %s'
GridFieldItemEditView.ss: null
Group:
- AddRole: 'Pridať úlohu pre túto skupinu'
+ AddRole: 'Pridať novú úlohu pre túto skupinu'
Code: 'Kód skupiny'
DefaultGroupTitleAdministrators: Administratori
DefaultGroupTitleContentAuthors: 'Autori obsahu'
Description: Popis
GroupReminder: 'Ak vyberiete nadriadenú skupinu, bude táto skupina mať všetky úlohy'
Locked: 'Zamknuté?'
- NoRoles: 'Nenašli sa úlohy'
+ NoRoles: 'Nenašli sa žiadne úlohy'
PLURALNAME: Skupiny
Parent: 'Nadradená skupina'
- RolesAddEditLink: 'Spravovať úlohy'
+ RolesAddEditLink: 'Pridať/upraviť úlohy'
SINGULARNAME: Skupina
- Sort: 'Poradie zoradenia'
+ Sort: 'Zoradiť podľa'
has_many_Permissions: Právomoci
- many_many_Members: Členovia
+ many_many_Members: Uživatelia
GroupImportForm:
Help1: 'Importovať jednu alebo viac skupín v CSV formáte (čiarkov oddelené hodnoty). Zobraziť pokročilé použitie'
Help2: "\\nPokročilé použitie
\\n\\n- Povolené stĺpce: %s
\\n- Existujúce skupiny sú porovnávané ich unikátnou vlastnostou Code, a aktualizované s novými hodnotami z\\nimportovaného súboru.
\\n- Hierarchia skupín môže byť tvorená použitím stĺpce ParentCode.
\\n- Kódy oprávnení môžu byť priradené stĺpcom PermissionCode. Existujúce oprávnenia nie sú smazáné.
\\n
\\n"
@@ -366,9 +366,9 @@ sk:
LOGGEDINAS: 'Ste prihlásený/á ako {name}.'
NEWPASSWORD: 'Nové heslo'
PASSWORD: Heslo
- PLURALNAME: Členovia
+ PLURALNAME: Uživatelia
REMEMBERME: 'Pamätať si ma nabudúce?'
- SINGULARNAME: Člen
+ SINGULARNAME: Uživatel
SUBJECTPASSWORDCHANGED: 'Vaše heslo bolo zmenené'
SUBJECTPASSWORDRESET: 'Odkaz na resetovanie hesla'
SURNAME: Priezvisko
@@ -419,7 +419,7 @@ sk:
SINGULARNAME: 'Heslo člena'
MemberTableField: null
ModelAdmin:
- DELETE: Zmazať
+ DELETE: Vymazať
DELETEDRECORDS: 'Zmazaných {count} záznamov.'
EMPTYBEFOREIMPORT: 'Vyčistiť databázu pred importovaním'
IMPORT: 'Importovať z CSV'
@@ -503,7 +503,7 @@ sk:
IMPORTGROUPS: 'Importovať skupiny'
IMPORTUSERS: 'Importovať požívateľov'
MEMBERS: Členovia
- MENUTITLE: Zabezpečenie
+ MENUTITLE: Bezpečnosť
MemberListCaution: 'Upozornenie: Odstánenie členov z tohto zoznamu ich odstáni zo všetkých skupín a databázy.'
NEWGROUP: 'Nová skupina'
PERMISSIONS: Právomoci
@@ -532,8 +532,8 @@ sk:
SELECT: 'Vyberte:'
TableListField.ss:
NOITEMSFOUND: 'Žiadne položky'
- SORTASC: 'Triedit v vzostupnom poradí'
- SORTDESC: 'Triediť v zostupnom poradí'
+ SORTASC: 'Zoradiť vzostupne'
+ SORTDESC: 'Zoradiť zostupne'
TableListField_PageControls.ss:
DISPLAYING: Zobrazujem
OF: z
diff --git a/model/DB.php b/model/DB.php
index d9c1a3dac..b4eb45ac6 100644
--- a/model/DB.php
+++ b/model/DB.php
@@ -153,7 +153,7 @@ class DB {
* rest of the options, see the specific class.
*/
public static function connect($databaseConfig) {
- // This is used by TestRunner::startsession() to test up a test session using an alt
+ // This is used by the "testsession" module to test up a test session using an alternative name
if($name = self::get_alternative_database_name()) {
$databaseConfig['database'] = $name;
}
diff --git a/model/DataList.php b/model/DataList.php
index f1a0e62aa..25fafb3d6 100644
--- a/model/DataList.php
+++ b/model/DataList.php
@@ -376,15 +376,15 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$whereArguments = func_get_arg(0);
} elseif($numberFuncArgs == 2) {
$whereArguments[func_get_arg(0)] = func_get_arg(1);
- } else {
+ } else {
throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()');
- }
-
+ }
+
return $this->alterDataQuery(function($query, $list) use ($whereArguments) {
$subquery = $query->disjunctiveGroup();
foreach($whereArguments as $field => $value) {
- $fieldArgs = explode(':', $field);
+ $fieldArgs = explode(':',$field);
$field = array_shift($fieldArgs);
$filterType = array_shift($fieldArgs);
$modifiers = $fieldArgs;
@@ -393,16 +393,16 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$t = singleton($list->dataClass())->dbObject($field);
if($filterType) {
$className = "{$filterType}Filter";
- } else {
+ } else {
$className = 'ExactMatchFilter';
}
if(!class_exists($className)){
$className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType);
- }
+ }
$t = new $className($field, $value, $modifiers);
$t->apply($subquery);
- }
+ }
});
}
@@ -453,29 +453,29 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
}
/**
- * Translates the comparisator to the sql query
+ * Translates a filter type to a SQL query.
*
* @param string $field - the fieldname in the db
- * @param string $comparisators - example StartsWith, relates to a filtercontext
+ * @param string $filter - example StartsWith, relates to a filtercontext
* @param array $modifiers - Modifiers to pass to the filter, ie not,nocase
* @param string $value - the value that the filtercontext will use for matching
* @todo Deprecated SearchContexts and pull their functionality into the core of the ORM
*/
- private function applyFilterContext($field, $comparisators, $modifiers, $value) {
- if($comparisators) {
- $className = "{$comparisators}Filter";
+ private function applyFilterContext($field, $filter, $modifiers, $value) {
+ if($filter) {
+ $className = "{$filter}Filter";
} else {
$className = 'ExactMatchFilter';
}
- if(!class_exists($className)){
+ if(!class_exists($className)) {
$className = 'ExactMatchFilter';
- array_unshift($modifiers, $comparisators);
+ array_unshift($modifiers, $filter);
}
$t = new $className($field, $value, $modifiers);
return $this->alterDataQuery(array($t, 'apply'));
}
-
+
/**
* Return a copy of this list which does not contain any items with these charactaristics
*
@@ -517,13 +517,13 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
$t = singleton($list->dataClass())->dbObject($field);
if($filterType) {
$className = "{$filterType}Filter";
- } else {
+ } else {
$className = 'ExactMatchFilter';
- }
+ }
if(!class_exists($className)){
$className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType);
- }
+ }
$t = new $className($field, $value, $modifiers);
$t->exclude($subquery);
}
@@ -609,7 +609,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
return $result;
}
-
+
/**
* Walks the list using the specified callback
*
@@ -979,7 +979,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
public function remove($item) {
// By default, we remove an item from a DataList by deleting it.
$this->removeByID($item->ID);
- }
+ }
/**
* Remove an item from this DataList by ID
diff --git a/model/DataObject.php b/model/DataObject.php
index 962533bc5..7d71748f9 100644
--- a/model/DataObject.php
+++ b/model/DataObject.php
@@ -1365,8 +1365,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $filter A filter to be inserted into the WHERE clause
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted, the static
* field $default_sort on the component class will be used.
- * @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
- * will be returned.
+ * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string|array $limit A limit expression to be inserted into the LIMIT clause
*
* @return HasManyList The components of the one-to-many relationship.
@@ -1379,6 +1378,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
. " on class '$this->class'", E_USER_ERROR);
}
+ if($join) {
+ throw new \InvalidArgumentException(
+ 'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
+ );
+ }
+
// If we haven't been written yet, we can't save these relations, so use a list that handles this case
if(!$this->ID) {
if(!isset($this->unsavedRelations[$componentName])) {
@@ -1395,7 +1400,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->forForeignID($this->ID);
$result = $result->where($filter)->limit($limit)->sort($sort);
- if($join) $result = $result->join($join);
return $result;
}
@@ -1406,7 +1410,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $componentName
* @param string $filter
* @param string|array $sort
- * @param string $join
+ * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param string|array $limit
* @return SQLQuery
*/
@@ -1416,6 +1420,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
. " on class '$this->class'", E_USER_ERROR);
}
+ if($join) {
+ throw new \InvalidArgumentException(
+ 'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
+ );
+ }
+
$joinField = $this->getRemoteJoinField($componentName, 'has_many');
$id = $this->getField("ID");
@@ -2692,8 +2702,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string|array $sort A sort expression to be inserted into the ORDER BY clause. If omitted,
* self::$default_sort will be used.
- * @param string $join A single join clause. This can be used for filtering, only 1 instance of each DataObject
- * will be returned.
+ * @param string $join Deprecated 3.0 Join clause. Use leftJoin($table, $joinClause) instead.
* @param string|array $limit A limit expression to be inserted into the LIMIT clause.
* @param string $containerClass The container class to return the results in.
*
@@ -2717,6 +2726,12 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result->setDataModel(DataModel::inst());
return $result;
}
+
+ if($join) {
+ throw new \InvalidArgumentException(
+ 'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
+ );
+ }
$result = DataList::create($callerClass)->where($filter)->sort($sort);
@@ -2727,8 +2742,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$result = $result->limit($limit);
}
- if($join) $result = $result->join($join);
-
$result->setDataModel(DataModel::inst());
return $result;
}
@@ -2748,7 +2761,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
$list = new DataList(get_class($this));
$list->setDataModel($this->model);
} else {
- throw new InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed"
+ throw new \InvalidArgumentException("DataObject::aggregate() must be called as an instance method or passed"
. " a classname");
}
return $list;
diff --git a/model/DataQuery.php b/model/DataQuery.php
index 16d3a1276..bc0382423 100644
--- a/model/DataQuery.php
+++ b/model/DataQuery.php
@@ -647,12 +647,12 @@ class DataQuery {
* @param string $field
*/
public function subtract(DataQuery $subtractQuery, $field='ID') {
- $subSelect= $subtractQuery->getFinalisedQuery();
- $fieldExpression = $this->expressionForField($field, $subSelect);
+ $fieldExpression = $subtractQuery->expressionForField($field);
+ $subSelect = $subtractQuery->getFinalisedQuery();
$subSelect->setSelect(array());
$subSelect->selectField($fieldExpression, $field);
$subSelect->setOrderBy(null);
- $this->where($this->expressionForField($field, $this).' NOT IN ('.$subSelect->sql().')');
+ $this->where($this->expressionForField($field).' NOT IN ('.$subSelect->sql().')');
return $this;
}
@@ -679,9 +679,9 @@ class DataQuery {
* @param String $field See {@link expressionForField()}.
*/
public function column($field = 'ID') {
+ $fieldExpression = $this->expressionForField($field);
$query = $this->getFinalisedQuery(array($field));
$originalSelect = $query->getSelect();
- $fieldExpression = $this->expressionForField($field, $query);
$query->setSelect(array());
$query->selectField($fieldExpression, $field);
$this->ensureSelectContainsOrderbyColumns($query, $originalSelect);
@@ -692,17 +692,21 @@ class DataQuery {
/**
* @param String $field Select statement identifier, either the unquoted column name,
* the full composite SQL statement, or the alias set through {@link SQLQuery->selectField()}.
- * @param SQLQuery $query
- * @return String
+ * @return String The expression used to query this field via this DataQuery
*/
- protected function expressionForField($field, $query) {
- // Special case for ID
- if($field == 'ID') {
+ protected function expressionForField($field) {
+
+ // Prepare query object for selecting this field
+ $query = $this->getFinalisedQuery(array($field));
+
+ // Allow query to define the expression for this field
+ $expression = $query->expressionForField($field);
+ if(!empty($expression)) return $expression;
+
+ // Special case for ID, if not provided
+ if($field === 'ID') {
$baseClass = ClassInfo::baseDataClass($this->dataClass);
- return "\"$baseClass\".\"ID\"";
-
- } else {
- return $query->expressionForField($field);
+ return "\"$baseClass\".\"ID\"";
}
}
diff --git a/model/SQLQuery.php b/model/SQLQuery.php
index 6d823ae74..e23e191c1 100644
--- a/model/SQLQuery.php
+++ b/model/SQLQuery.php
@@ -566,6 +566,7 @@ class SQLQuery {
if($this->orderby) {
$i = 0;
foreach($this->orderby as $clause => $dir) {
+
// public function calls and multi-word columns like "CASE WHEN ..."
if(strpos($clause, '(') !== false || strpos($clause, " ") !== false ) {
// remove the old orderby
@@ -1059,18 +1060,22 @@ class SQLQuery {
/**
* Return a new SQLQuery that calls the given aggregate functions on this data.
+ *
* @param $column An aggregate expression, such as 'MAX("Balance")', or a set of them (as an escaped SQL statement)
+ * @param $alias An optional alias for the aggregate column.
*/
- public function aggregate($column) {
- if($this->groupby || $this->limit) {
- throw new Exception("SQLQuery::aggregate() doesn't work with groupby or limit, yet");
- }
-
+ public function aggregate($column, $alias = null) {
+
$clone = clone $this;
- $clone->setLimit(array());
- $clone->setOrderBy(array());
- $clone->setGroupBy(array());
- $clone->setSelect($column);
+ $clone->setLimit($this->limit);
+ $clone->setOrderBy($this->orderby);
+ $clone->setGroupBy($this->groupby);
+ if($alias) {
+ $clone->selectField($column, $alias);
+ } else {
+ $clone->setSelect($column);
+ }
+
return $clone;
}
diff --git a/model/URLSegmentFilter.php b/model/URLSegmentFilter.php
index 983d6a0cb..77240e282 100644
--- a/model/URLSegmentFilter.php
+++ b/model/URLSegmentFilter.php
@@ -29,7 +29,7 @@ class URLSegmentFilter extends Object {
'/&/u' => '-and-',
'/\s/u' => '-', // remove whitespace
'/_/u' => '-', // underscores to dashes
- '/[^A-Za-z0-9+.-]+/u' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
+ '/[^A-Za-z0-9+.\-]+/u' => '', // remove non-ASCII chars, only allow alphanumeric plus dash and dot
'/[\-]{2,}/u' => '-', // remove duplicate dashes
'/^[\.\-_]/u' => '', // Remove all leading dots, dashes or underscores
);
@@ -66,8 +66,8 @@ class URLSegmentFilter extends Object {
$replacements = $this->getReplacements();
// Unset automated removal of non-ASCII characters, and don't try to transliterate
- if($this->getAllowMultibyte() && isset($replacements['/[^A-Za-z0-9+.-]+/u'])) {
- unset($replacements['/[^A-Za-z0-9+.-]+/u']);
+ if($this->getAllowMultibyte() && isset($replacements['/[^A-Za-z0-9+.\-]+/u'])) {
+ unset($replacements['/[^A-Za-z0-9+.\-]+/u']);
}
foreach($replacements as $regex => $replace) {
diff --git a/model/Versioned.php b/model/Versioned.php
index 560358475..6d6b618b3 100644
--- a/model/Versioned.php
+++ b/model/Versioned.php
@@ -136,8 +136,8 @@ class Versioned extends DataExtension {
* @todo Should this all go into VersionedDataQuery?
*/
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) {
- $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
-
+ $baseTable = ClassInfo::baseDataClass($dataQuery->dataClass());
+
switch($dataQuery->getQueryParam('Versioned.mode')) {
// Noop
case '':
@@ -203,6 +203,7 @@ class Versioned extends DataExtension {
// below)
$dataQuery->setQueryParam('Versioned.mode', 'stage');
$this->augmentSQL($query, $dataQuery);
+ $dataQuery->setQueryParam('Versioned.mode', 'stage_unique');
// Now exclude any ID from any other stage. Note that we double rename to avoid the regular stage rename
// renaming all subquery references to be Versioned.stage
@@ -232,8 +233,19 @@ class Versioned extends DataExtension {
foreach(self::$db_for_versions_table as $name => $type) {
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, $name), $name);
}
+
+ // Alias the record ID as the row ID
$query->selectField(sprintf('"%s_versions"."%s"', $baseTable, 'RecordID'), "ID");
- $query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version'));
+
+ // Ensure that any sort order referring to this ID is correctly aliased
+ $orders = $query->getOrderBy();
+ foreach($orders as $order => $dir) {
+ if($order === "\"$baseTable\".\"ID\"") {
+ unset($orders[$order]);
+ $orders["\"{$baseTable}_versions\".\"RecordID\""] = $dir;
+ }
+ }
+ $query->setOrderBy($orders);
// latest_version has one more step
// Return latest version instances, regardless of whether they are on a particular stage
@@ -250,6 +262,9 @@ class Versioned extends DataExtension {
) AS \"{$alias}_versions_latest\"
WHERE \"{$alias}_versions_latest\".\"RecordID\" = \"{$alias}_versions\".\"RecordID\"
)");
+ } else {
+ // If all versions are requested, ensure that records are sorted by this field
+ $query->addOrderBy(sprintf('"%s_versions"."%s"', $baseTable, 'Version'));
}
break;
default:
@@ -266,8 +281,8 @@ class Versioned extends DataExtension {
*/
function augmentLoadLazyFields(SQLQuery &$query, DataQuery &$dataQuery = null, $record) {
$dataClass = $dataQuery->dataClass();
- if (isset($record['Version'])){
- $dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
+ if (isset($record['Version'])){
+ $dataQuery->where("\"$dataClass\".\"RecordID\" = " . $record['ID']);
$dataQuery->where("\"$dataClass\".\"Version\" = " . $record['Version']);
$dataQuery->setQueryParam('Versioned.mode', 'all_versions');
}
@@ -739,13 +754,25 @@ class Versioned extends DataExtension {
return !$stagesAreEqual;
}
+ /**
+ * @param string $filter
+ * @param string $sort
+ * @param string $limit
+ * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
+ * @param string $having
+ */
public function Versions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
return $this->allVersions($filter, $sort, $limit, $join, $having);
}
/**
* Return a list of all the versions available.
- * @param string $filter
+ *
+ * @param string $filter
+ * @param string $sort
+ * @param string $limit
+ * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
+ * @param string $having
*/
public function allVersions($filter = "", $sort = "", $limit = "", $join = "", $having = "") {
// Make sure the table names are not postfixed (e.g. _Live)
@@ -994,7 +1021,7 @@ class Versioned extends DataExtension {
* @param string $stage The name of the stage.
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string $sort A sort expression to be inserted into the ORDER BY clause.
- * @param string $join A join expression, such as LEFT JOIN or INNER JOIN
+ * @param string $join Deprecated, use leftJoin($table, $joinClause) instead
* @param int $limit A limit on the number of records returned from the database.
* @param string $containerClass The container class for the result set (default is DataList)
* @return SS_List
diff --git a/scss/AssetUploadField.scss b/scss/AssetUploadField.scss
index e3b0b2ef0..dedb5b80b 100644
--- a/scss/AssetUploadField.scss
+++ b/scss/AssetUploadField.scss
@@ -12,12 +12,16 @@
clear:both;
max-width:750px;
display:block;
+
+ .toggle {
+ font-style: normal;
+ font-size: $font-base-size;
+ }
}
#AssetUploadField {
border-bottom: 0;
@include box-shadow(none);
- padding: 12px;
}
.backlink {
padding-left: 12px;
@@ -274,9 +278,9 @@ body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fie
}
}
.ss-uploadfield-fromcomputer {
- /*position: relative;
+ /*position: relative; */
overflow: hidden;
- display: block;*/
+ display: block;
}
.ss-uploadfield-item-uploador {
float: left;
diff --git a/scss/UploadField.scss b/scss/UploadField.scss
index d2474504e..d77dbe009 100644
--- a/scss/UploadField.scss
+++ b/scss/UploadField.scss
@@ -47,11 +47,12 @@
border: 2px dashed $color-medium-separator;
background: $color-light-separator;
display: none;
+ margin-right: 15px;
}
}
.ss-uploadfield-item-info {
- margin: 0 0 0 100px;
-
+ float: left;
+
.ss-uploadfield-item-name {
display: block;
line-height: 13px;
diff --git a/security/Group.php b/security/Group.php
index 837143c37..fc4e3eec6 100755
--- a/security/Group.php
+++ b/security/Group.php
@@ -221,7 +221,9 @@ class Group extends DataObject {
* including all members which are "inherited" from children groups of this record.
* See {@link DirectMembers()} for retrieving members without any inheritance.
*
- * @param String
+ * @param String $filter
+ * @param String $sort
+ * @param String $join Deprecated, use leftJoin($table, $joinClause) instead
* @return ManyManyList
*/
public function Members($filter = "", $sort = "", $join = "", $limit = "") {
@@ -231,6 +233,12 @@ class Group extends DataObject {
. " DataList instead.");
}
+ if($join) {
+ throw new \InvalidArgumentException(
+ 'The $join argument has been removed. Use leftJoin($table, $joinClause) instead.'
+ );
+ }
+
// First get direct members as a base result
$result = $this->DirectMembers();
// Remove the default foreign key filter in prep for re-applying a filter containing all children groups.
@@ -242,7 +250,6 @@ class Group extends DataObject {
// Now set all children groups as a new foreign key
$groups = Group::get()->byIDs($this->collateFamilyIDs());
$result = $result->forForeignID($groups->column('ID'))->where($filter)->sort($sort)->limit($limit);
- if($join) $result = $result->join($join);
return $result;
}
diff --git a/security/Member.php b/security/Member.php
index 964b329c8..9a5bf8d96 100644
--- a/security/Member.php
+++ b/security/Member.php
@@ -1294,16 +1294,20 @@ class Member extends DataObject implements TemplateGlobalProvider {
*/
public function canDelete($member = null) {
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) $member = Member::currentUser();
-
+
// extended access checks
$results = $this->extend('canDelete', $member);
if($results && is_array($results)) {
if(!min($results)) return false;
else return true;
}
-
+
// No member found
if(!($member && $member->exists())) return false;
+
+ // Members are not allowed to remove themselves,
+ // since it would create inconsistencies in the admin UIs.
+ if($this->ID && $member->ID == $this->ID) return false;
return $this->canEdit($member);
}
diff --git a/templates/AssetUploadField.ss b/templates/AssetUploadField.ss
index 4a596696c..9abddc6b9 100644
--- a/templates/AssetUploadField.ss
+++ b/templates/AssetUploadField.ss
@@ -28,9 +28,11 @@
+
- <% _t('AssetAdmin.ALLOWEDEXTS', 'Allowed extensions') %>
- $Extensions
+ <% _t('AssetAdmin.SHOWALLOWEDEXTS', 'Show allowed extensions') %>
+ $Extensions
+
diff --git a/templates/UploadField.ss b/templates/UploadField.ss
index 91cef77d6..aa3450f80 100644
--- a/templates/UploadField.ss
+++ b/templates/UploadField.ss
@@ -34,6 +34,7 @@
<% end_if %>
<% else %>
style="display: none;"<% end_if %>>
+ <% if canUpload %>
<% if $multiple %>
<% _t('UploadField.DROPFILES', 'drop files') %>
@@ -41,6 +42,7 @@
<% _t('UploadField.DROPFILE', 'drop a file') %>
<% end_if %>
+ <% end_if %>
-
+ <% if canUpload %>
+
+ <% end_if %>
<% if not $autoUpload %>
diff --git a/tests/api/XMLDataFormatterTest.php b/tests/api/XMLDataFormatterTest.php
index 414cce108..cca690fec 100644
--- a/tests/api/XMLDataFormatterTest.php
+++ b/tests/api/XMLDataFormatterTest.php
@@ -39,7 +39,7 @@ class XMLDataFormatterTest extends SapphireTest {
$this->assertEquals(
'mysite.com is a link in this HTML content.'
. ' ',
- (string) $xml->Content
+ (string)$xml->Content
);
}
@@ -50,16 +50,16 @@ class XMLDataFormatterTest extends SapphireTest {
$page->Content = 'This is some test content [test_shortcode]test[/test_shortcode]';
$xml = new SimpleXMLElement('' . $formatter->convertDataObjectWithoutHeader($page));
- $this->assertEquals('This is some test content test', $xml->Content);
+ $this->assertEquals('This is some test content test', (string)$xml->Content);
$page->Content = '[test_shortcode,id=-1]';
$xml = new SimpleXMLElement('' . $formatter->convertDataObjectWithoutHeader($page));
- $this->assertEmpty('', $xml->Content);
+ $this->assertEmpty('', (string)$xml->Content);
$page->Content = '[bad_code,id=1]';
$xml = new SimpleXMLElement('' . $formatter->convertDataObjectWithoutHeader($page));
- $this->assertContains('[bad_code,id=1]', $xml->Content);
+ $this->assertContains('[bad_code,id=1]', (string)$xml->Content);
}
/**
diff --git a/tests/behat/features/manage-files.feature b/tests/behat/features/manage-files.feature
index a0c09985d..23996cebd 100644
--- a/tests/behat/features/manage-files.feature
+++ b/tests/behat/features/manage-files.feature
@@ -81,4 +81,9 @@ Feature: Manage files
# /show/0 is to ensure that we are on top level folder
And I go to "/admin/assets/show/0"
And I click on "folder2" in the "Files" table
- And the "folder2" table should contain "file1"
\ No newline at end of file
+ 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,"
\ No newline at end of file
diff --git a/tests/core/ObjectTest.php b/tests/core/ObjectTest.php
index da73064b3..784f50fcd 100644
--- a/tests/core/ObjectTest.php
+++ b/tests/core/ObjectTest.php
@@ -361,6 +361,16 @@ class ObjectTest extends SapphireTest {
Object::parse_class_spec(
"Enum(array('Accepted', 'Pending', 'Declined', array('UnsubmittedA','UnsubmittedB')), 'Unsubmitted')")
);
+ // Namespaced class
+ $this->assertEquals(
+ array('Test\MyClass', array()),
+ Object::parse_class_spec('Test\MyClass')
+ );
+ // Fully qualified namespaced class
+ $this->assertEquals(
+ array('\Test\MyClass', array()),
+ Object::parse_class_spec('\Test\MyClass')
+ );
}
}
diff --git a/tests/forms/HtmlEditorFieldTest.php b/tests/forms/HtmlEditorFieldTest.php
index cb11563ea..9d55226d3 100644
--- a/tests/forms/HtmlEditorFieldTest.php
+++ b/tests/forms/HtmlEditorFieldTest.php
@@ -46,16 +46,16 @@ class HtmlEditorFieldTest extends FunctionalTest {
$parser = new CSSContentParser($obj->Content);
$xml = $parser->getByXpath('//img');
- $this->assertEquals('', $xml[0]['alt'], 'Alt tags are added by default.');
- $this->assertEquals('', $xml[0]['title'], 'Title tags are added by default.');
+ $this->assertEquals('', (string)$xml[0]['alt'], 'Alt tags are added by default.');
+ $this->assertEquals('', (string)$xml[0]['title'], 'Title tags are added by default.');
$editor->setValue('');
$editor->saveInto($obj);
$parser = new CSSContentParser($obj->Content);
$xml = $parser->getByXpath('//img');
- $this->assertEquals('foo', $xml[0]['alt'], 'Alt tags are preserved.');
- $this->assertEquals('bar', $xml[0]['title'], 'Title tags are preserved.');
+ $this->assertEquals('foo', (string)$xml[0]['alt'], 'Alt tags are preserved.');
+ $this->assertEquals('bar', (string)$xml[0]['title'], 'Title tags are preserved.');
}
public function testMultiLineSaving() {
diff --git a/tests/forms/RequirementsTest.php b/tests/forms/RequirementsTest.php
index 9c53e0ec9..af54ce2a7 100644
--- a/tests/forms/RequirementsTest.php
+++ b/tests/forms/RequirementsTest.php
@@ -325,6 +325,34 @@ class RequirementsTest extends SapphireTest {
$this->assertContains('', $html);
}
+ public function testSuffix() {
+ $template = 'My header Body
';
+ $basePath = $this->getCurrentRelativePath();
+ $basePath = 'framework' . substr($basePath, strlen(FRAMEWORK_DIR));
+
+ $backend = new Requirements_Backend;
+
+ $backend->javascript($basePath .'/RequirementsTest_a.js');
+ $backend->javascript($basePath .'/RequirementsTest_b.js?foo=bar&bla=blubb');
+ $backend->css($basePath .'/RequirementsTest_a.css');
+ $backend->css($basePath .'/RequirementsTest_b.css?foo=bar&bla=blubb');
+
+ $backend->set_suffix_requirements(true);
+ $html = $backend->includeInHTML(false, $template);
+ $this->assertRegexp('/RequirementsTest_a\.js\?m=[\d]*/', $html);
+ $this->assertRegexp('/RequirementsTest_b\.js\?m=[\d]*&foo=bar&bla=blubb/', $html);
+ $this->assertRegexp('/RequirementsTest_a\.css\?m=[\d]*/', $html);
+ $this->assertRegexp('/RequirementsTest_b\.css\?m=[\d]*&foo=bar&bla=blubb/', $html);
+
+ $backend->set_suffix_requirements(false);
+ $html = $backend->includeInHTML(false, $template);
+ $this->assertNotContains('RequirementsTest_a.js=', $html);
+ $this->assertNotRegexp('/RequirementsTest_a\.js\?m=[\d]*/', $html);
+ $this->assertNotRegexp('/RequirementsTest_b\.js\?m=[\d]*&foo=bar&bla=blubb/', $html);
+ $this->assertNotRegexp('/RequirementsTest_a\.css\?m=[\d]*/', $html);
+ $this->assertNotRegexp('/RequirementsTest_b\.css\?m=[\d]*&foo=bar&bla=blubb/', $html);
+ }
+
public function assertFileIncluded($backend, $type, $files) {
$type = strtolower($type);
switch (strtolower($type)) {
diff --git a/tests/forms/SelectionGroupTest.php b/tests/forms/SelectionGroupTest.php
index e418639c6..924a54627 100644
--- a/tests/forms/SelectionGroupTest.php
+++ b/tests/forms/SelectionGroupTest.php
@@ -23,8 +23,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']);
- $this->assertEquals('one title', $listElOne->label[0]);
- $this->assertEquals('two title', $listElTwo->label[0]);
+ $this->assertEquals('one title', (string)$listElOne->label[0]);
+ $this->assertEquals('two title', (string)$listElTwo->label[0]);
$this->assertContains('one view', (string)$listElOne->div);
$this->assertContains('two view', (string)$listElTwo->div);
@@ -44,8 +44,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']);
- $this->assertEquals('one', $listElOne->label[0]);
- $this->assertEquals('two', $listElTwo->label[0]);
+ $this->assertEquals('one', (string)$listElOne->label[0]);
+ $this->assertEquals('two', (string)$listElTwo->label[0]);
}
function testLegacyItemsFieldHolderWithTitle() {
@@ -62,8 +62,8 @@ class SelectionGroupTest extends SapphireTest {
$this->assertEquals('one', (string)$listElOne->input[0]['value']);
$this->assertEquals('two', (string)$listElTwo->input[0]['value']);
- $this->assertEquals('one title', $listElOne->label[0]);
- $this->assertEquals('two title', $listElTwo->label[0]);
+ $this->assertEquals('one title', (string)$listElOne->label[0]);
+ $this->assertEquals('two title', (string)$listElTwo->label[0]);
}
}
\ No newline at end of file
diff --git a/tests/forms/gridfield/GridFieldDetailFormTest.php b/tests/forms/gridfield/GridFieldDetailFormTest.php
index c883aefd3..6c50b2e5c 100644
--- a/tests/forms/gridfield/GridFieldDetailFormTest.php
+++ b/tests/forms/gridfield/GridFieldDetailFormTest.php
@@ -192,6 +192,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
}
public function testCustomItemRequestClass() {
+ $this->logInWithPermission('ADMIN');
+
$component = new GridFieldDetailForm();
$this->assertEquals('GridFieldDetailForm_ItemRequest', $component->getItemRequestClass());
$component->setItemRequestClass('GridFieldDetailFormTest_ItemRequest');
@@ -199,6 +201,8 @@ class GridFieldDetailFormTest extends FunctionalTest {
}
public function testItemEditFormCallback() {
+ $this->logInWithPermission('ADMIN');
+
$category = new GridFieldDetailFormTest_Category();
$component = new GridFieldDetailForm();
$component->setItemEditFormCallback(function($form, $component) {
diff --git a/tests/forms/gridfield/GridFieldEditButtonTest.php b/tests/forms/gridfield/GridFieldEditButtonTest.php
index ee04dc0ce..69235eee0 100644
--- a/tests/forms/gridfield/GridFieldEditButtonTest.php
+++ b/tests/forms/gridfield/GridFieldEditButtonTest.php
@@ -25,15 +25,16 @@ class GridFieldEditButtonTest extends SapphireTest {
$this->form = new Form(new Controller(), 'mockform', new FieldList(array($this->gridField)), new FieldList());
}
- public function testDontShowEditLinks() {
+ public function testShowEditLinks() {
if(Member::currentUser()) { Member::currentUser()->logOut(); }
$content = new CSSContentParser($this->gridField->FieldHolder());
// Check that there are content
$this->assertEquals(3, count($content->getBySelector('.ss-gridfield-item')));
- // Make sure that there are no edit links
- $this->assertEquals(0, count($content->getBySelector('.edit-link')),
- 'Edit links should not show when not logged in.');
+ // Make sure that there are edit links, even though the user doesn't have "edit" permissions
+ // (he can still view the records)
+ $this->assertEquals(2, count($content->getBySelector('.edit-link')),
+ 'Edit links should show when not logged in.');
}
public function testShowEditLinksWithAdminPermission() {
diff --git a/tests/forms/uploadfield/UploadFieldTest.php b/tests/forms/uploadfield/UploadFieldTest.php
index 2ceb16b17..8831e6206 100644
--- a/tests/forms/uploadfield/UploadFieldTest.php
+++ b/tests/forms/uploadfield/UploadFieldTest.php
@@ -476,6 +476,42 @@ class UploadFieldTest extends FunctionalTest {
}
+ public function testCanUpload() {
+ $this->loginWithPermission('ADMIN');
+ $response = $this->get('UploadFieldTest_Controller');
+ $this->assertFalse($response->isError());
+
+ $parser = new CSSContentParser($response->getBody());
+ $this->assertFalse(
+ (bool)$parser->getBySelector('#CanUploadFalseField .ss-uploadfield-fromcomputer-fileinput'),
+ 'Removes input file control'
+ );
+ $this->assertFalse((bool)$parser->getBySelector('#CanUploadFalseField .ss-uploadfield-dropzone'),
+ 'Removes dropzone');
+ $this->assertTrue(
+ (bool)$parser->getBySelector('#CanUploadFalseField .ss-uploadfield-fromfiles'),
+ 'Keeps "From files" button'
+ );
+ }
+
+ public function testCanUploadWithPermissionCode() {
+ $field = new UploadField('MyField');
+
+ $field->setConfig('canUpload', true);
+ $this->assertTrue($field->canUpload());
+
+ $field->setConfig('canUpload', false);
+ $this->assertFalse($field->canUpload());
+
+ $this->loginWithPermission('ADMIN');
+
+ $field->setConfig('canUpload', false);
+ $this->assertFalse($field->canUpload());
+
+ $field->setConfig('canUpload', 'ADMIN');
+ $this->assertTrue($field->canUpload());
+ }
+
public function testIsSaveable() {
$form = $this->getMockForm();
@@ -775,6 +811,10 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
$fieldSubfolder->setFolderName('UploadFieldTest/subfolder1');
$fieldSubfolder->setRecord($record);
+ $fieldCanUploadFalse = new UploadField('CanUploadFalseField');
+ $fieldCanUploadFalse->setConfig('canUpload', false);
+ $fieldCanUploadFalse->setRecord($record);
+
$form = new Form(
$this,
'Form',
@@ -789,7 +829,8 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
$fieldManyMany,
$fieldReadonly,
$fieldDisabled,
- $fieldSubfolder
+ $fieldSubfolder,
+ $fieldCanUploadFalse
),
new FieldList(
new FormAction('submit')
@@ -805,7 +846,8 @@ class UploadFieldTest_Controller extends Controller implements TestOnly {
'ManyManyFiles',
'ReadonlyField',
'DisabledField',
- 'SubfolderField'
+ 'SubfolderField',
+ 'CanUploadFalseField'
)
);
return $form;
diff --git a/tests/i18n/i18nTest.php b/tests/i18n/i18nTest.php
index ec8ee5b5e..736ffb1fa 100644
--- a/tests/i18n/i18nTest.php
+++ b/tests/i18n/i18nTest.php
@@ -300,6 +300,15 @@ class i18nTest extends SapphireTest {
$translated, "Testing sprintf placeholders with named injections"
);
+ $translated = i18n::_t(
+ 'i18nTestModule.INJECTIONSLEGACY', // has %s placeholders
+ array("Cat", "meow"/*, "meow" */) // remove third arg
+ );
+ $this->assertContains(
+ "TRANS Hello Cat meow. But it is late, ",
+ $translated, "Testing sprintf placeholders with unnamed injections and too few args"
+ );
+
$translated = i18n::_t(
'i18nTestModule.INJECTIONS', // has {name} placeholders
array("Cat", "meow", "meow")
diff --git a/tests/model/SQLQueryTest.php b/tests/model/SQLQueryTest.php
index f59c2d519..922444bbf 100755
--- a/tests/model/SQLQueryTest.php
+++ b/tests/model/SQLQueryTest.php
@@ -367,6 +367,44 @@ class SQLQueryTest extends SapphireTest {
$this->assertEquals('Object 1', $row['Name']);
}
}
+
+ /**
+ * Tests aggregate() function
+ */
+ public function testAggregate() {
+ $query = new SQLQuery();
+ $query->setFrom('"SQLQueryTest_DO"');
+ $query->setGroupBy("Common");
+
+ $queryClone = $query->aggregate('COUNT(*)', 'cnt');
+ $result = $queryClone->execute();
+ $this->assertEquals(array(2), $result->column('cnt'));
+ }
+
+ /**
+ * Test that "_SortColumn0" is added for an aggregate in the ORDER BY
+ * clause, in combination with a LIMIT and GROUP BY clause.
+ * For some databases, like MSSQL, this is a complicated scenario
+ * because a subselect needs to be done to query paginated data.
+ */
+ public function testOrderByContainingAggregateAndLimitOffset() {
+ $query = new SQLQuery();
+ $query->setSelect(array('"Name"', '"Meta"'));
+ $query->setFrom('"SQLQueryTest_DO"');
+ $query->setOrderBy(array('MAX(Date)'));
+ $query->setGroupBy(array('"Name"', '"Meta"'));
+ $query->setLimit('1', '1');
+
+ $records = array();
+ foreach($query->execute() as $record) {
+ $records[] = $record;
+ }
+
+ $this->assertCount(1, $records);
+
+ $this->assertEquals('Object 2', $records[0]['Name']);
+ $this->assertEquals('2012-05-01 09:00:00', $records['0']['_SortColumn0']);
+ }
}
@@ -374,6 +412,8 @@ class SQLQueryTest_DO extends DataObject implements TestOnly {
static $db = array(
"Name" => "Varchar",
"Meta" => "Varchar",
+ "Common" => "Varchar",
+ "Date" => "SS_Datetime"
);
}
diff --git a/tests/model/SQLQueryTest.yml b/tests/model/SQLQueryTest.yml
index 725b7dee4..ad9a11238 100644
--- a/tests/model/SQLQueryTest.yml
+++ b/tests/model/SQLQueryTest.yml
@@ -2,6 +2,10 @@ SQLQueryTest_DO:
test1:
Name: 'Object 1'
Meta: 'Details 1'
+ Common: 'Common Value'
+ Date: 2012-01-01 10:00:00
test2:
Name: 'Object 2'
- Meta: 'Details 2'
\ No newline at end of file
+ Meta: 'Details 2'
+ Date: 2012-05-01 09:00:00
+ Common: 'Common Value'
\ No newline at end of file
diff --git a/tests/model/URLSegmentFilterTest.php b/tests/model/URLSegmentFilterTest.php
index 38adedef2..0d1d084fe 100644
--- a/tests/model/URLSegmentFilterTest.php
+++ b/tests/model/URLSegmentFilterTest.php
@@ -22,7 +22,15 @@ class URLSegmentFilterTest extends SapphireTest {
$f->filter('Brötchen')
);
}
-
+
+ public function testReplacesCommonNonAsciiCharacters() {
+ $f = new URLSegmentFilter();
+ $this->assertEquals(
+ urlencode('aa1-.'),
+ $f->filter('Aa1~!@#$%^*()_`-=;\':"[]\{}|,./<>?')
+ );
+ }
+
public function testRetainsNonAsciiUrlsWithAllowMultiByteOption() {
$f = new URLSegmentFilter();
$f->setAllowMultibyte(true);
diff --git a/tests/model/VersionedTest.php b/tests/model/VersionedTest.php
index 6c1aec3b9..a671adc77 100644
--- a/tests/model/VersionedTest.php
+++ b/tests/model/VersionedTest.php
@@ -74,8 +74,15 @@ class VersionedTest extends SapphireTest {
* Test Versioned::get_including_deleted()
*/
public function testGetIncludingDeleted() {
- // Delete a page
- $this->objFromFixture('VersionedTest_DataObject', 'page3')->delete();
+ // Get all ids of pages
+ $allPageIDs = DataObject::get('VersionedTest_DataObject', "\"ParentID\" = 0", "\"VersionedTest_DataObject\".\"ID\" ASC")->column('ID');
+
+ // Modify a page, ensuring that the Version ID and Record ID will differ,
+ // and then subsequently delete it
+ $targetPage = $this->objFromFixture('VersionedTest_DataObject', 'page3');
+ $targetPage->Content = 'To be deleted';
+ $targetPage->write();
+ $targetPage->delete();
// Get all items, ignoring deleted
$remainingPages = DataObject::get("VersionedTest_DataObject", "\"ParentID\" = 0",
@@ -90,12 +97,17 @@ class VersionedTest extends SapphireTest {
// Check that page 3 is still there
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title'));
+ // Check that the returned pages have the correct IDs
+ $this->assertEquals($allPageIDs, $allPages->column('ID'));
+
// Check that this still works if we switch to reading the other stage
Versioned::reading_stage("Live");
$allPages = Versioned::get_including_deleted("VersionedTest_DataObject", "\"ParentID\" = 0",
"\"VersionedTest_DataObject\".\"ID\" ASC");
$this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title'));
+ // Check that the returned pages still have the correct IDs
+ $this->assertEquals($allPageIDs, $allPages->column('ID'));
}
public function testVersionedFieldsAdded() {
diff --git a/tests/security/MemberTest.php b/tests/security/MemberTest.php
index 099c8dcfb..c61f77cc7 100644
--- a/tests/security/MemberTest.php
+++ b/tests/security/MemberTest.php
@@ -432,7 +432,7 @@ class MemberTest extends FunctionalTest {
/* Logged in users can edit their own record */
$this->session()->inst_set('loggedInAs', $member->ID);
$this->assertTrue($member->canView());
- $this->assertTrue($member->canDelete());
+ $this->assertFalse($member->canDelete());
$this->assertTrue($member->canEdit());
/* Other uses cannot view, delete or edit others records */
@@ -653,6 +653,34 @@ class MemberTest extends FunctionalTest {
$this->assertFalse($m2->validateAutoLoginToken($m1Token), 'Fails token validity test against other member.');
}
+ public function testCanDelete() {
+ $admin1 = $this->objFromFixture('Member', 'admin');
+ $admin2 = $this->objFromFixture('Member', 'other-admin');
+ $member1 = $this->objFromFixture('Member', 'grouplessmember');
+ $member2 = $this->objFromFixture('Member', 'noformatmember');
+
+ $this->assertTrue(
+ $admin1->canDelete($admin2),
+ 'Admins can delete other admins'
+ );
+ $this->assertTrue(
+ $member1->canDelete($admin2),
+ 'Admins can delete non-admins'
+ );
+ $this->assertFalse(
+ $admin1->canDelete($admin1),
+ 'Admins can not delete themselves'
+ );
+ $this->assertFalse(
+ $member1->canDelete($member2),
+ 'Non-admins can not delete other non-admins'
+ );
+ $this->assertFalse(
+ $member1->canDelete($member1),
+ 'Non-admins can not delete themselves'
+ );
+ }
+
}
class MemberTest_ViewingAllowedExtension extends DataExtension implements TestOnly {
diff --git a/view/Requirements.php b/view/Requirements.php
index 8cbc5a322..b128cf5a2 100644
--- a/view/Requirements.php
+++ b/view/Requirements.php
@@ -3,12 +3,12 @@
/**
* Requirements tracker, for javascript and css.
* @todo Document the requirements tracker, and discuss it with the others.
- *
+ *
* @package framework
* @subpackage view
*/
class Requirements {
-
+
/**
* Enable combining of css/javascript files.
* @param boolean $enable
@@ -34,38 +34,38 @@ class Requirements {
}
/**
- * Set whether we want to suffix requirements with the time /
+ * Set whether we want to suffix requirements with the time /
* location on to the requirements
- *
+ *
* @param bool
*/
public static function set_suffix_requirements($var) {
self::backend()->set_suffix_requirements($var);
}
-
+
/**
* Return whether we want to suffix requirements
- *
+ *
* @return bool
*/
public static function get_suffix_requirements() {
return self::backend()->get_suffix_requirements();
}
-
+
/**
* Instance of requirements for storage
*
* @var Requirements
*/
private static $backend = null;
-
+
public static function backend() {
if(!self::$backend) {
self::$backend = new Requirements_Backend();
}
return self::$backend;
}
-
+
/**
* Setter method for changing the Requirements backend
*
@@ -74,20 +74,20 @@ class Requirements {
public static function set_backend(Requirements_Backend $backend) {
self::$backend = $backend;
}
-
+
/**
* Register the given javascript file as required.
- *
+ *
* See {@link Requirements_Backend::javascript()} for more info
- *
+ *
*/
public static function javascript($file) {
self::backend()->javascript($file);
}
-
+
/**
* Add the javascript code to the header of the page
- *
+ *
* See {@link Requirements_Backend::customScript()} for more info
* @param script The script content
* @param uniquenessID Use this to ensure that pieces of code only get added once.
@@ -98,50 +98,50 @@ class Requirements {
/**
* Include custom CSS styling to the header of the page.
- *
+ *
* See {@link Requirements_Backend::customCSS()}
- *
+ *
* @param string $script CSS selectors as a string (without \n";
}
-
- foreach(array_diff_key($this->customHeadTags,$this->blocked) as $customHeadTag) {
- $requirements .= "$customHeadTag\n";
+
+ foreach(array_diff_key($this->customHeadTags,$this->blocked) as $customHeadTag) {
+ $requirements .= "$customHeadTag\n";
}
-
+
if($this->write_js_to_body) {
// Remove all newlines from code to preserve layout
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
-
+
// We put script tags into the body, for performance.
- // If your template already has script tags in the body, then we put our script
+ // If your template already has script tags in the body, then we put our script
// tags just before those. Otherwise, we put it at the bottom.
$p2 = stripos($content, ']*>)/i", $jsRequirements . "\\1", $content);
}
-
- // Put CSS at the bottom of the head
+
+ // Put CSS at the bottom of the head
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
} else {
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
$content = preg_replace("/(<\/head>)/i", $jsRequirements . "\\1", $content);
}
- }
-
+ }
+
return $content;
}
@@ -723,20 +723,20 @@ class Requirements_Backend {
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the HTTP response
*/
public function include_in_response(SS_HTTPResponse $response) {
- $this->process_combined_files();
+ $this->process_combined_files();
$jsRequirements = array();
$cssRequirements = array();
- foreach(array_diff_key($this->javascript, $this->blocked) as $file => $dummy) {
+ foreach(array_diff_key($this->javascript, $this->blocked) as $file => $dummy) {
$path = $this->path_for_file($file);
if($path) {
$jsRequirements[] = str_replace(',', '%2C', $path);
}
}
-
+
$response->addHeader('X-Include-JS', implode(',', $jsRequirements));
- foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
+ foreach(array_diff_key($this->css,$this->blocked) as $file => $params) {
$path = $this->path_for_file($file);
if($path) {
$path = str_replace(',', '%2C', $path);
@@ -746,11 +746,11 @@ class Requirements_Backend {
$response->addHeader('X-Include-CSS', implode(',', $cssRequirements));
}
-
+
/**
* Add i18n files from the given javascript directory. SilverStripe expects that the given directory
* will contain a number of java script files named by language: en_US.js, de_DE.js, etc.
- *
+ *
* @param String The javascript lang directory, relative to the site root, e.g., 'framework/javascript/lang'
* @param Boolean Return all relative file paths rather than including them in requirements
* @param Boolean Only include language files, not the base libraries
@@ -764,10 +764,15 @@ class Requirements_Backend {
if(!$langOnly) $files[] = FRAMEWORK_DIR . '/javascript/i18n.js';
if(substr($langDir,-1) != '/') $langDir .= '/';
-
+
$files[] = $langDir . i18n::default_locale() . '.js';
$files[] = $langDir . i18n::get_locale() . '.js';
-
+
+ // If both files don't exist, hard fallback to en_US
+ if(!Director::fileExists($files[0]) && !Director::fileExists($files[1])) {
+ $files[] = $langDir . 'en_US.js';
+ }
+
// Stub i18n implementation for when i18n is disabled.
} else {
if(!$langOnly) $files[] = FRAMEWORK_DIR . '/javascript/i18nx.js';
@@ -778,49 +783,54 @@ class Requirements_Backend {
} else {
foreach($files as $file) $this->javascript($file);
}
- }
-
+ }
+
/**
* Finds the path for specified file.
*
* @param string $fileOrUrl
- * @return string|boolean
+ * @return string|boolean
*/
protected function path_for_file($fileOrUrl) {
if(preg_match('{^//|http[s]?}', $fileOrUrl)) {
return $fileOrUrl;
} elseif(Director::fileExists($fileOrUrl)) {
+ $filePath = preg_replace('/\?.*/', '', Director::baseFolder() . '/' . $fileOrUrl);
$prefix = Director::baseURL();
$mtimesuffix = "";
$suffix = '';
- if(strpos($fileOrUrl, '?') !== false) {
- $suffix = '&' . substr($fileOrUrl, strpos($fileOrUrl, '?')+1);
- $fileOrUrl = substr($fileOrUrl, 0, strpos($fileOrUrl, '?'));
- }
if($this->suffix_requirements) {
- $mtimesuffix = "?m=" . filemtime(Director::baseFolder() . '/' . $fileOrUrl);
+ $mtimesuffix = "?m=" . filemtime($filePath);
+ $suffix = '&';
+ }
+ if(strpos($fileOrUrl, '?') !== false) {
+ if (strlen($suffix) == 0) {
+ $suffix = '?';
+ }
+ $suffix .= substr($fileOrUrl, strpos($fileOrUrl, '?')+1);
+ $fileOrUrl = substr($fileOrUrl, 0, strpos($fileOrUrl, '?'));
}
return "{$prefix}{$fileOrUrl}{$mtimesuffix}{$suffix}";
} else {
return false;
}
}
-
+
/**
* Concatenate several css or javascript files into a single dynamically generated
* file (stored in {@link Director::baseFolder()}). This increases performance
* by fewer HTTP requests.
- *
+ *
* The combined file is regenerated
* based on every file modification time. Optionally a rebuild can be triggered
* by appending ?flush=1 to the URL.
* If all files to be combined are javascript, we use the external JSMin library
* to minify the javascript. This can be controlled by {@link $combine_js_with_jsmin}.
- *
+ *
* All combined files will have a comment on the start of each concatenated file
* denoting their original position. For easier debugging, we recommend to only
* minify javascript if not in development mode ({@link Director::isDev()}).
- *
+ *
* CAUTION: You're responsible for ensuring that the load order for combined files
* is retained - otherwise combining javascript files can lead to functional errors
* in the javascript logic, and combining css can lead to wrong styling inheritance.
@@ -828,7 +838,7 @@ class Requirements_Backend {
* in more than one combine_files() call.
* Best practice is to include every javascript file in exactly *one* combine_files()
* directive to avoid the issues mentioned above - this is enforced by this function.
- *
+ *
* CAUTION: Combining CSS Files discards any "media" information.
*
* Example for combined JavaScript:
@@ -854,10 +864,10 @@ class Requirements_Backend {
*
*
* @see http://code.google.com/p/jsmin-php/
- *
+ *
* @todo Should we enforce unique inclusion of files, or leave it to the developer? Can auto-detection cause
* breaks?
- *
+ *
* @param string $combinedFileName Filename of the combined file (will be stored in {@link Director::baseFolder()}
* by default)
* @param array $files Array of filenames relative to the webroot
@@ -913,7 +923,7 @@ class Requirements_Backend {
}
$this->combine_files[$combinedFileName] = $files;
}
-
+
/**
* Returns all combined files.
* @return array
@@ -921,15 +931,15 @@ class Requirements_Backend {
public function get_combine_files() {
return $this->combine_files;
}
-
+
/**
- * Deletes all dynamically generated combined files from the filesystem.
- *
+ * Deletes all dynamically generated combined files from the filesystem.
+ *
* @param string $combinedFileName If left blank, all combined files are deleted.
*/
public function delete_combined_files($combinedFileName = null) {
$combinedFiles = ($combinedFileName) ? array($combinedFileName => null) : $this->combine_files;
- $combinedFolder = ($this->getCombinedFilesFolder()) ?
+ $combinedFolder = ($this->getCombinedFilesFolder()) ?
(Director::baseFolder() . '/' . $this->combinedFilesFolder) : Director::baseFolder();
foreach($combinedFiles as $combinedFile => $sourceItems) {
$filePath = $combinedFolder . '/' . $combinedFile;
@@ -938,7 +948,7 @@ class Requirements_Backend {
}
}
}
-
+
public function clear_combined_files() {
$this->combine_files = array();
}
@@ -952,7 +962,7 @@ class Requirements_Backend {
// SapphireTest isn't running :-)
if(class_exists('SapphireTest', false)) $runningTest = SapphireTest::is_running_test();
else $runningTest = false;
-
+
if((Director::isDev() && !$runningTest && !isset($_REQUEST['combine'])) || !$this->combined_files_enabled) {
return;
}
@@ -961,12 +971,12 @@ class Requirements_Backend {
$combinerCheck = array();
foreach($this->combine_files as $combinedFile => $sourceItems) {
foreach($sourceItems as $sourceItem) {
- if(isset($combinerCheck[$sourceItem]) && $combinerCheck[$sourceItem] != $combinedFile){
+ if(isset($combinerCheck[$sourceItem]) && $combinerCheck[$sourceItem] != $combinedFile){
user_error("Requirements_Backend::process_combined_files - file '$sourceItem' appears in two " .
"combined files:" . " '{$combinerCheck[$sourceItem]}' and '$combinedFile'", E_USER_WARNING);
}
$combinerCheck[$sourceItem] = $combinedFile;
-
+
}
}
@@ -985,7 +995,7 @@ class Requirements_Backend {
$newJSRequirements[$file] = true;
}
}
-
+
foreach($this->css as $file => $params) {
if(isset($combinerCheck[$file])) {
$newCSSRequirements[$combinedFilesFolder . $combinerCheck[$file]] = true;
@@ -1041,7 +1051,7 @@ class Requirements_Backend {
$isJS = stripos($file, '.js');
if($isJS && $this->combine_js_with_jsmin) {
require_once('thirdparty/jsmin/jsmin.php');
-
+
increase_time_limit_to();
$fileContent = JSMin::minify($fileContent);
}
@@ -1074,13 +1084,13 @@ class Requirements_Backend {
public function get_custom_scripts() {
$requirements = "";
-
+
if($this->customScript) {
foreach($this->customScript as $script) {
$requirements .= "$script\n";
}
}
-
+
return $requirements;
}
@@ -1102,7 +1112,7 @@ class Requirements_Backend {
$this->css($module.$css);
}
}
-
+
public function debug() {
Debug::show($this->javascript);
Debug::show($this->css);
@@ -1111,5 +1121,5 @@ class Requirements_Backend {
Debug::show($this->customHeadTags);
Debug::show($this->combine_files);
}
-
+
}