diff --git a/admin/code/ModelAdmin.php b/admin/code/ModelAdmin.php index 66e73ba6f..d8d503e8d 100644 --- a/admin/code/ModelAdmin.php +++ b/admin/code/ModelAdmin.php @@ -1,29 +1,12 @@ - * Director::config()->rules = array(array('admin/mymodel/$Class/$Action/$ID' => 'MyModelAdmin')); - * * - * @todo saving logic (should mostly use Form->saveInto() and iterate over relations) - * @todo ajax form loading and saving - * @todo ajax result display - * @todo relation formfield scaffolding (one tab per relation) - relations don't have DBField sublclasses, we do - * we define the scaffold defaults. can be ComplexTableField instances for a start. - * @todo has_many/many_many relation autocomplete field (HasManyComplexTableField doesn't work well with larger - * datasets) - * - * Long term TODOs: - * @todo Hook into RESTful interface on DataObjects (yet to be developed) - * @todo Permission control via datamodel and Form class - * * @uses SearchContext * * @package framework diff --git a/admin/css/screen.css b/admin/css/screen.css index 464807969..a4deff496 100644 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -462,18 +462,18 @@ body.cms { overflow: hidden; } .cms-add-form ul.SelectionGroup { padding-left: 28px; } .cms-add-form .parent-mode { padding: 8px; overflow: auto; } -#PageType ul { padding-left: 20px; } -#PageType ul li { float: none; width: 100%; padding: 9px 0 9px 15px; overflow: hidden; border-bottom-width: 2px; border-bottom: 2px groove rgba(255, 255, 255, 0.8); -webkit-border-image: url(../images/textures/bg_fieldset_elements_border.png) 2 stretch stretch; border-image: url(../images/textures/bg_fieldset_elements_border.png) 2 stretch stretch; } -#PageType ul li:last-child { border-bottom: none; } -#PageType ul li:hover, #PageType ul li.selected { background-color: rgba(255, 255, 102, 0.3); } -#PageType ul li.disabled { color: #aaaaaa; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); opacity: 0.5; } -#PageType ul li.disabled:hover { background: none; } -#PageType ul li input { margin: inherit; } -#PageType ul li label { padding-left: 0; padding-bottom: 0; } -#PageType ul li input, #PageType ul li label, #PageType ul li .page-icon, #PageType ul li .title { float: left; line-height: 1.3em; } -#PageType ul li .page-icon { margin: 0 4px; } -#PageType ul li .title { width: 120px; font-weight: bold; padding-right: 10px; } -#PageType ul li .description { font-style: italic; display: inline; clear: none; margin: 0; } +#Form_AddForm_PageType_Holder ul { padding-left: 20px; } +#Form_AddForm_PageType_Holder ul li { float: none; width: 100%; padding: 9px 0 9px 15px; overflow: hidden; border-bottom-width: 2px; border-bottom: 2px groove rgba(255, 255, 255, 0.8); -webkit-border-image: url(../images/textures/bg_fieldset_elements_border.png) 2 stretch stretch; border-image: url(../images/textures/bg_fieldset_elements_border.png) 2 stretch stretch; } +#Form_AddForm_PageType_Holder ul li:last-child { border-bottom: none; } +#Form_AddForm_PageType_Holder ul li:hover, #Form_AddForm_PageType_Holder ul li.selected { background-color: rgba(255, 255, 102, 0.3); } +#Form_AddForm_PageType_Holder ul li.disabled { color: #aaaaaa; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50); opacity: 0.5; } +#Form_AddForm_PageType_Holder ul li.disabled:hover { background: none; } +#Form_AddForm_PageType_Holder ul li input { margin: inherit; } +#Form_AddForm_PageType_Holder ul li label { padding-left: 0; padding-bottom: 0; } +#Form_AddForm_PageType_Holder ul li input, #Form_AddForm_PageType_Holder ul li label, #Form_AddForm_PageType_Holder ul li .page-icon, #Form_AddForm_PageType_Holder ul li .title { float: left; line-height: 1.3em; } +#Form_AddForm_PageType_Holder ul li .page-icon { margin: 0 4px; } +#Form_AddForm_PageType_Holder ul li .title { width: 120px; font-weight: bold; padding-right: 10px; } +#Form_AddForm_PageType_Holder ul li .description { font-style: italic; display: inline; clear: none; margin: 0; } /** -------------------------------------------- Content toolbar -------------------------------------------- */ .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 */ } @@ -1017,8 +1017,8 @@ visible. Added and removed with js in TabSet.js */ /*************************** .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel.first { left: 0; width: 203px; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .ui-icon { padding-right: 0; } .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .tab-nav-link, .cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel .ss-ui-button { font-size: 12px; } -.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel #PageType ul { padding: 0; } -.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel #PageType ul li { padding: 4px 5px; } +.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel #Form_AddForm_PageType_Holder ul { padding: 0; } +.cms .ss-ui-action-tabset.multi .ss-ui-action-tab.ui-tabs-panel #Form_AddForm_PageType_Holder ul li { padding: 4px 5px; } .cms .ss-ui-action-tabset.tabset-open ul.ui-tabs-nav, .cms .ss-ui-action-tabset.tabset-open ul.ui-tabs-nav li.first { -moz-border-radius-bottomleft: 0; -webkit-border-bottom-left-radius: 0; border-bottom-left-radius: 0; } .cms .ss-ui-action-tabset.tabset-open-last ul.ui-tabs-nav li.last { -moz-border-radius-bottomright: 0; -webkit-border-bottom-right-radius: 0; border-bottom-right-radius: 0; } .cms .ss-ui-action-tabset .batch-check, .cms .ss-ui-action-tabset .ui-icon { display: inline-block; float: left; margin-left: -2px; padding-right: 6px; } diff --git a/admin/javascript/SecurityAdmin.js b/admin/javascript/SecurityAdmin.js index 90c494715..cb37954f2 100644 --- a/admin/javascript/SecurityAdmin.js +++ b/admin/javascript/SecurityAdmin.js @@ -37,7 +37,7 @@ * As they're disabled, any changes won't be submitted (which is intended behaviour), * checking all boxes is purely presentational. */ - $('#Permissions .checkbox[value=ADMIN]').entwine({ + $('.permissioncheckboxset .checkbox[value=ADMIN]').entwine({ onmatch: function() { this.toggleCheckboxes(); @@ -56,7 +56,8 @@ * Function: toggleCheckboxes */ toggleCheckboxes: function() { - var self = this, checkboxes = this.parents('.field:eq(0)').find('.checkbox').not(this); + var self = this, + checkboxes = this.parents('.field:eq(0)').find('.checkbox').not(this); if(this.is(':checked')) { checkboxes.each(function() { diff --git a/admin/scss/_actionTabs.scss b/admin/scss/_actionTabs.scss index d88f6e1b5..661b5b475 100644 --- a/admin/scss/_actionTabs.scss +++ b/admin/scss/_actionTabs.scss @@ -124,8 +124,10 @@ $border: 1px solid darken(#D9D9D9, 15%); .tab-nav-link, .ss-ui-button { font-size: 12px; } - #PageType ul{ - padding:0; + + #Form_AddForm_PageType_Holder ul { + padding: 0; + li{ padding:4px 5px; } diff --git a/admin/scss/_style.scss b/admin/scss/_style.scss index bf1207398..464c92a87 100644 --- a/admin/scss/_style.scss +++ b/admin/scss/_style.scss @@ -531,7 +531,7 @@ body.cms { } } -#PageType { +#Form_AddForm_PageType_Holder { ul { padding-left: 20px; li { diff --git a/core/Convert.php b/core/Convert.php index 94c6234c0..f5fce0fcf 100644 --- a/core/Convert.php +++ b/core/Convert.php @@ -43,22 +43,49 @@ class Convert { } /** - * Convert a value to be suitable for an HTML attribute. - * - * This is useful for converting human readable values into - * a value suitable for an ID or NAME attribute. + * Convert a value to be suitable for an HTML ID attribute. Replaces non + * supported characters with a space. * * @see http://www.w3.org/TR/REC-html40/types.html#type-cdata - * @uses Convert::raw2att() + * * @param array|string $val String to escape, or array of strings + * * @return array|string */ public static function raw2htmlname($val) { if(is_array($val)) { - foreach($val as $k => $v) $val[$k] = self::raw2htmlname($v); + foreach($val as $k => $v) { + $val[$k] = self::raw2htmlname($v); + } + return $val; } else { - return preg_replace('/[^a-zA-Z0-9\-_:.]+/','', $val); + return self::raw2att($val); + } + } + + /** + * Convert a value to be suitable for an HTML ID attribute. Replaces non + * supported characters with an underscore. + * + * @see http://www.w3.org/TR/REC-html40/types.html#type-cdata + * + * @param array|string $val String to escape, or array of strings + * + * @return array|string + */ + public static function raw2htmlid($val) { + if(is_array($val)) { + foreach($val as $k => $v) { + $val[$k] = self::raw2htmlid($v); + } + + return $val; + } else { + return trim(preg_replace( + '/_+/', '_', preg_replace('/[^a-zA-Z0-9\-_:.]+/','_', $val)), + '_' + ); } } diff --git a/css/AssetUploadField.css b/css/AssetUploadField.css index a6380044f..f45ec08bf 100644 --- a/css/AssetUploadField.css +++ b/css/AssetUploadField.css @@ -17,8 +17,6 @@ Used in side panels and action tabs .backlink { padding-left: 12px; } -#Form_EditorToolbarMediaForm .ui-tabs-panel { padding-left: 0px; } - body.cms.ss-uploadfield-edit-iframe, .composite.ss-assetuploadfield .details fieldset { padding: 16px; overflow: auto; background: #E2E2E2; } body.cms.ss-uploadfield-edit-iframe span.readonly, .composite.ss-assetuploadfield .details fieldset span.readonly { font-style: italic; color: #777777; text-shadow: 0px 1px 0px #fff; } body.cms.ss-uploadfield-edit-iframe .fieldholder-small label, .composite.ss-assetuploadfield .details fieldset .fieldholder-small label { margin-left: 0; } diff --git a/css/ComplexTableField_popup.css b/css/ComplexTableField_popup.css deleted file mode 100644 index 76bdc4ee9..000000000 --- a/css/ComplexTableField_popup.css +++ /dev/null @@ -1,35 +0,0 @@ -html { overflow-y: auto !important; } - -body { height: 100%; } - -#ComplexTableField_Popup_DetailForm input.loading { background: white url(../images/network-save.gif) left center no-repeat; padding-left: 16px; } - -.PageControls { padding: 5px; width: 100%; } -.PageControls * { vertical-align: middle; } -.PageControls .Left { width: 33%; } -.PageControls .Count { width: 33%; text-align: center; } -.PageControls .Right { width: 33%; text-align: right; } - -.ComplexTableField_Popup td.hidden { display: none; } -.ComplexTableField_Popup th.HiddenField { display: none; } -.ComplexTableField_Popup span.right { float: right; clear: none; } -.ComplexTableField_Popup span.left { float: left; clear: none; } -.ComplexTableField_Popup form p.checkbox input { margin: 0pt 1px; } -.ComplexTableField_Popup form ul.optionset { margin: 0; padding: 0; } -.ComplexTableField_Popup form ul.optionset li { margin: 4px 0; } -.ComplexTableField_Popup form div.Actions input { font-size: 11px; margin-top: 10px; } - -/* Pagination */ -#ComplexTableField_Pagination, #ComplexTableField_Pagination * { vertical-align: middle; } - -#ComplexTableField_Pagination { margin-top: 10px; margin-left: auto; margin-right: auto; font-size: 11px; } -#ComplexTableField_Pagination a { /*font-size: 1.2em;*/ font-size: 13px; font-weight: bold; text-decoration: none; width: 1px; height: 1px; margin: 1px; } -#ComplexTableField_Pagination a:hover { background: none; } -#ComplexTableField_Pagination span { display: inline; font-weight: bold; font-size: 15px; color: #f00; } -#ComplexTableField_Pagination div { display: inline; } - -#ComplexTableField_Pagination_Previous { padding-right: 10px; } - -#ComplexTableField_Pagination_Next { padding-left: 10px; } - -#ComplexTableField_Pagination_Next img, #ComplexTableField_Pagination_Previous img { margin: 0 3px 2px; } diff --git a/docs/en/changelogs/rc/3.2.0.md b/docs/en/changelogs/rc/3.2.0.md index 44698bbed..afb542352 100644 --- a/docs/en/changelogs/rc/3.2.0.md +++ b/docs/en/changelogs/rc/3.2.0.md @@ -14,6 +14,7 @@ Otherwise, you'll need to include the module yourself * API: Removed URL routing by controller name * Security: The multiple authenticator login page should now be styled manually - i.e. without the default jQuery UI layout. A new template, Security_MultiAuthenticatorLogin.ss is available. * Security: This controller's templates can be customised by overriding the `getTemplate` function. + * API: Form and FormField ID attributes rewritten. ## Details @@ -63,3 +64,78 @@ you can reinstate the old behaviour through a director rule: Director: rules: '$Controller//$Action/$ID/$OtherID': '*' + +### API: Default Form and FormField ID attributes rewritten. + +Previously the automatic generation of ID attributes throughout the Form API +could generate invalid ID values such as Password[ConfirmedPassword] as well +as duplicate ID values between forms on the same page. For example, if you +created a field called `Email` on more than one form on the page, the resulting +HTML would have multiple instances of `#Email`. ID should be a unique +identifier for a single element within the document. + +This rewrite has several angles, each of which is described below. If you rely +on ID values in your CSS files, Javascript code or application unit tests *you +will need to update your code*. + +#### Conversion of invalid form ID values + +ID attributes on Form and Form Fields will now follow the +[HTML specification](http://www.w3.org/TR/REC-html40/types.html#type-cdata). +Generating ID attributes is now handled by the new `FormTemplateHelper` class. + +Please test each of your existing site forms to ensure that they work +correctly in particular, javascript and css styles which rely on specific ID +values. + +#### Invalid ID attributes stripped + +ID attributes will now be run through `Convert::raw2htmlid`. Invalid characters +are replaced with a single underscore character. Duplicate, leading and trailing +underscores are removed. Custom ID attributes (set through `setHTMLID`) will not +be altered. + + Before: +