diff --git a/layouts/joomla/form/field/subform/repeatable-table.php b/layouts/joomla/form/field/subform/repeatable-table.php index 10c09330f9b2c..1bee0c6bbdd3d 100644 --- a/layouts/joomla/form/field/subform/repeatable-table.php +++ b/layouts/joomla/form/field/subform/repeatable-table.php @@ -67,42 +67,67 @@ ); } ?> -
-
+
+ + + + + + + + + + + $form): + echo $this->sublayout( + $sublayout, + array( + 'form' => $form, + 'basegroup' => $fieldname, + 'group' => $fieldname . $k, + 'buttons' => $buttons, + 'unique_subform_id' => $unique_subform_id, + ) + ); + endforeach; ?> + +
+ +
+ + + +
+ +
- - - - - - - - - - - $form) : - echo $this->sublayout($sublayout, array('form' => $form, 'basegroup' => $fieldname, 'group' => $fieldname . $k, 'buttons' => $buttons)); - endforeach; - ?> - -
- -
- -
- -
- - - + + +
diff --git a/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php b/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php index 717e7c88556e8..6a27082c228f3 100644 --- a/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php +++ b/layouts/joomla/form/field/subform/repeatable-table/section-byfieldsets.php @@ -18,10 +18,13 @@ * @var array $buttons Array of the buttons that will be rendered */ extract($displayData); - ?> - + getFieldsets() as $fieldset) : ?> getFieldset($fieldset->name) as $field) : ?> @@ -32,9 +35,21 @@
- - - + + + + + + + + + + + + + + +
diff --git a/layouts/joomla/form/field/subform/repeatable-table/section.php b/layouts/joomla/form/field/subform/repeatable-table/section.php index 2b0a3a0290244..f4cf9a4a36821 100644 --- a/layouts/joomla/form/field/subform/repeatable-table/section.php +++ b/layouts/joomla/form/field/subform/repeatable-table/section.php @@ -21,7 +21,11 @@ ?> - + getGroup('') as $field) : ?> renderField(array('hiddenLabel' => true)); ?> @@ -30,9 +34,21 @@
- - - + + + + + + + + + + + + + + +
diff --git a/layouts/joomla/form/field/subform/repeatable.php b/layouts/joomla/form/field/subform/repeatable.php index 72f5344f56b88..00af8264a2603 100644 --- a/layouts/joomla/form/field/subform/repeatable.php +++ b/layouts/joomla/form/field/subform/repeatable.php @@ -39,24 +39,49 @@
+ data-bt-add="a.group-add-" + data-bt-remove="a.group-remove-" + data-bt-move="a.group-move-" + data-repeatable-element="div.subform-repeatable-group-" + data-minimum="" data-maximum="" + > + $form) : - echo $this->sublayout($sublayout, array('form' => $form, 'basegroup' => $fieldname, 'group' => $fieldname . $k, 'buttons' => $buttons)); + echo $this->sublayout( + $sublayout, + array( + 'form' => $form, + 'basegroup' => $fieldname, + 'group' => $fieldname . $k, + 'buttons' => $buttons, + 'unique_subform_id' => $unique_subform_id, + ) + ); endforeach; ?> - +
diff --git a/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php b/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php index 01b5d93e22d49..64bbbdbedb1d9 100644 --- a/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php +++ b/layouts/joomla/form/field/subform/repeatable/section-byfieldsets.php @@ -20,13 +20,29 @@ extract($displayData); ?> -
+
- - - + + + + + + + + + + + + + + +
diff --git a/layouts/joomla/form/field/subform/repeatable/section.php b/layouts/joomla/form/field/subform/repeatable/section.php index 2fb6e67b1fb6b..e36742280e0da 100644 --- a/layouts/joomla/form/field/subform/repeatable/section.php +++ b/layouts/joomla/form/field/subform/repeatable/section.php @@ -21,13 +21,29 @@ ?> -
+
- - - + + + + + + + + + + + + + + +
diff --git a/libraries/joomla/form/fields/subform.php b/libraries/joomla/form/fields/subform.php index 988fb6eb9dd8b..5204ed02de29a 100644 --- a/libraries/joomla/form/fields/subform.php +++ b/libraries/joomla/form/fields/subform.php @@ -232,7 +232,7 @@ protected function getInput() try { // Prepare the form template - $formname = 'subform' . ($this->group ? $this->group . '.' : '.') . $this->fieldname; + $formname = 'subform.' . str_replace(array('jform[', '[', ']'), array('', '.', ''), $control); $tmplcontrol = !$this->multiple ? $control : $control . '[' . $this->fieldname . 'X]'; $tmpl = JForm::getInstance($formname, $this->formsource, array('control' => $tmplcontrol)); @@ -274,6 +274,14 @@ protected function getInput() $data['fieldname'] = $this->fieldname; $data['groupByFieldset'] = $this->groupByFieldset; + /** + * For each rendering process of a subform element, we want to have a + * separate unique subform id present to could distinguish the eventhandlers + * regarding adding/moving/removing rows from nested subforms from their parents. + */ + static $unique_subform_id = 0; + $data['unique_subform_id'] = ('sr-' . ($unique_subform_id++)); + // Prepare renderer $renderer = $this->getRenderer($this->layout); diff --git a/media/system/js/subform-repeatable-uncompressed.js b/media/system/js/subform-repeatable-uncompressed.js index fb7d5dd0d8bfa..16f5f8070248f 100644 --- a/media/system/js/subform-repeatable-uncompressed.js +++ b/media/system/js/subform-repeatable-uncompressed.js @@ -29,7 +29,7 @@ this.$containerRows = this.options.rowsContainer ? this.$container.find(this.options.rowsContainer) : this.$container; // last row number, help to avoid the name duplications - this.lastRowNum = this.$containerRows.find(this.options.repeatableElement).length; + this.lastRowNum = this.$containerRows.find(this.options.repeatableElement).length; // To avoid scope issues, var self = this; @@ -67,9 +67,9 @@ // prepare a template that we will use repeating $.subformRepeatable.prototype.prepareTemplate = function(){ // create from template - if(this.options.rowTemplateSelector){ - var tmplElement = this.$container.find(this.options.rowTemplateSelector)[0] || {}; - this.template = $.trim(tmplElement.text || tmplElement.textContent); //(text || textContent) is IE8 fix + if (this.options.rowTemplateSelector) { + // Find the template element and get its HTML content, this is our template. + this.template = $.trim(this.$container.find(this.options.rowTemplateSelector).last().html()) || ''; } // create from existing rows else { @@ -142,13 +142,18 @@ $row.remove(); }; - // fix names ind id`s for field that in $row - $.subformRepeatable.prototype.fixUniqueAttributes = function($row, count){ - var group = $row.attr('data-group'),// current group name - basename = $row.attr('data-base-name'), // group base name, without count - count = count || 0, + // fix names and id`s for fields in $row + $.subformRepeatable.prototype.fixUniqueAttributes = function( + $row, // the jQuery object to do fixes in + _count, // existing count of rows + _group, // current group name, e.g. 'optionsX' + _basename // group base name, without count, e.g. 'options' + ) { + var group = (typeof _group === 'undefined' ? $row.attr('data-group') : _group), + basename = (typeof _basename === 'undefined' ? $row.attr('data-base-name') : _basename), + count = (typeof _count === 'undefined' ? 0 : _count), countnew = Math.max(this.lastRowNum, count), - groupnew = basename + countnew; // new group name + groupnew = basename + countnew; this.lastRowNum = countnew + 1; $row.attr('data-group', groupnew); @@ -203,6 +208,22 @@ // Guess there a label for this input $row.find('label[for="' + forOldAttr + '"]').attr('for', idNew).attr('id', idNew + '-lbl'); } + + /** + * Recursively replace our basename + old group with basename + new group + * inside of nested subform template elements. First we try to find such + * template elements, then we iterate through them and do the same replacements + * that we have made here inside of them. + */ + var nestedTemplates = $row.find(this.options.rowTemplateSelector); + // If we found it, iterate over the found ones (might be more than one!) + for (var j = 0; j < nestedTemplates.length; j++) { + // Get the nested templates content (as DocumentFragment) and cast it + // to a jQuery object + var nestedTemplate = $($(nestedTemplates[j]).prop('content')); + // Fix the attributes for this nested template. + this.fixUniqueAttributes(nestedTemplate, count, group, basename); + } }; // remove scripts attached to fields @@ -247,18 +268,35 @@ if(window.SqueezeBox && window.SqueezeBox.assign){ SqueezeBox.assign($row.find('a.modal').get(), {parse: 'rel'}); } + + // @TODO We need to do a lot more here. See e.g. administrator/templates/isis/js/template.js + // and all that it does with e.g. turning radios into btn groups with disabled/active/btn-danger classes. + // See also related issues #16695 and #16676, which could get fixed by this method being better. + + // subforms in subforms + $row.find('div.subform-repeatable').subformRepeatable(); }; // defaults $.subformRepeatable.defaults = { - btAdd: ".group-add", // button selector for "add" action - btRemove: ".group-remove",// button selector for "remove" action - btMove: ".group-move",// button selector for "move" action - minimum: 0, // minimum repeating - maximum: 10, // maximum repeating + // button selector for "add" action, must be unique per nested subform! + btAdd: ".group-add", + // button selector for "remove" action, must be unique per nested subform! + btRemove: ".group-remove", + // button selector for "move" action, must be unique per nested subform! + btMove: ".group-move", + // minimum repeating + minimum: 0, + // maximum repeating + maximum: 10, + // selector for the repeatable element inside the main container, + // must be unique per nested subform! repeatableElement: ".subform-repeatable-group", - rowTemplateSelector: 'script.subform-repeatable-template-section', // selector for the row template +