├── LICENSE.md ├── README.md ├── composer.json ├── screenshots ├── after.png ├── before.png ├── settings.png ├── table.png └── types.png └── settable ├── SetTablePlugin.php ├── fieldtypes └── SetTableFieldType.php ├── resources └── css │ └── settable.css └── templates ├── field.html ├── settings-table.html └── settings.html /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Josh Crawford 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Set Table 2 | 3 | Set Table is a Craft CMS field type to allow you to create static tables. Set Table is essentially identical to a regular Table field, apart from a few small differences. The aim of this plugin is to provide end-users with a set collection of rows for their table - defined in the field settings. 4 | 5 | **Label Type** 6 | 7 | In addition to the regular column options for a table (text, multi-line text, select, checkbox), you can set a column's type to be a Label. This will display the provided value as a read-only label. 8 | 9 | 10 | 11 | ###Use Case 12 | 13 | By now you might be a bit confused as to why Set Table even exists, being that you can simply use a regular table to do the same thing. However there are potential cases where read-only portions of a table might be helpful to your end user. Likewise for the number of rows in the table. 14 | 15 | A good use case would be a list of social media fields, containing links to your website's various social media pages. 16 | 17 | Firstly, you could simply use a collection of text fields like so: 18 | 19 | 20 | 21 | This is a little messy visually, and unnecessary amount of fields. You'd be better off with a regular table: 22 | 23 | 24 | 25 | Great! Nice and neat. We can even use it to define the icon class for templating. 26 | 27 | But what if you don't want your users to be able to edit the Name and Icon Class fields? If you're using an icon font, this will certainly break things on the site. And maybe you also don't want users to add any more social links? What if they try to add a service without an icon? 28 | 29 | Obviously there are ways around the above (provide a placeholder when no icon, and educate your editors on table fields), but here's a Set Table. 30 | 31 | 32 | 33 | There's also some small visual tweaks to the table compared to a regular one. 34 | 35 | ###Field Settings 36 | 37 | If you can edit a Table, you can edit a Set Table. 38 | 39 | 40 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "engram-design/settable", 3 | "description": "A Craft CMS field type to allow you to create static tables.", 4 | "homepage": "https://github.com/engram-design/SetTable", 5 | "type": "craft-plugin", 6 | "keywords": ["craft","plugin"], 7 | "license": "MIT", 8 | "authors": [{ 9 | "name": "S. Group", 10 | "email": "joshua@sgroup.com.au", 11 | "homepage": "http://sgroup.com.au/" 12 | }], 13 | "require": { 14 | "composer/installers": "~1.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /screenshots/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/SetTable/a3e0383077887fae65e9511838c06e17e41ae94a/screenshots/after.png -------------------------------------------------------------------------------- /screenshots/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/SetTable/a3e0383077887fae65e9511838c06e17e41ae94a/screenshots/before.png -------------------------------------------------------------------------------- /screenshots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/SetTable/a3e0383077887fae65e9511838c06e17e41ae94a/screenshots/settings.png -------------------------------------------------------------------------------- /screenshots/table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/SetTable/a3e0383077887fae65e9511838c06e17e41ae94a/screenshots/table.png -------------------------------------------------------------------------------- /screenshots/types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verbb/SetTable/a3e0383077887fae65e9511838c06e17e41ae94a/screenshots/types.png -------------------------------------------------------------------------------- /settable/SetTablePlugin.php: -------------------------------------------------------------------------------- 1 | getSettings()->columns; 19 | $tableData = $this->getSettings()->tableData; 20 | 21 | if (!$columns) 22 | { 23 | $columns = array('col1' => array('heading' => '', 'handle' => '', 'type' => 'singleline')); 24 | 25 | // Update the actual settings model for getInputHtml() 26 | $this->getSettings()->columns = $columns; 27 | } 28 | 29 | if ($tableData === null) 30 | { 31 | $tableData = array('row1' => array()); 32 | } 33 | 34 | $columnSettings = array( 35 | 'heading' => array( 36 | 'heading' => Craft::t('Column Heading'), 37 | 'type' => 'singleline', 38 | 'autopopulate' => 'handle' 39 | ), 40 | 'handle' => array( 41 | 'heading' => Craft::t('Handle'), 42 | 'class' => 'code', 43 | 'type' => 'singleline' 44 | ), 45 | 'width' => array( 46 | 'heading' => Craft::t('Width'), 47 | 'class' => 'code', 48 | 'type' => 'singleline', 49 | 'width' => 50 50 | ), 51 | 'type' => array( 52 | 'heading' => Craft::t('Type'), 53 | 'class' => 'thin', 54 | 'type' => 'select', 55 | 'options' => array( 56 | 'label' => Craft::t('Label'), 57 | 'singleline' => Craft::t('Single-line Text'), 58 | 'multiline' => Craft::t('Multi-line text'), 59 | 'number' => Craft::t('Number'), 60 | 'checkbox' => Craft::t('Checkbox'), 61 | ) 62 | ), 63 | ); 64 | 65 | craft()->templates->includeCssResource('settable/css/settable.css'); 66 | 67 | craft()->templates->includeJsResource('js/TableFieldSettings.js'); 68 | craft()->templates->includeJs('new Craft.TableFieldSettings(' . 69 | '"'.craft()->templates->namespaceInputName('columns').'", ' . 70 | '"'.craft()->templates->namespaceInputName('tableData').'", ' . 71 | JsonHelper::encode($columns).', ' . 72 | JsonHelper::encode($tableData).', ' . 73 | JsonHelper::encode($columnSettings) . 74 | ');'); 75 | 76 | $columnsField = craft()->templates->render('settable/settings', array( 77 | 'label' => Craft::t('Table Columns'), 78 | 'instructions' => Craft::t('Define the columns your table should have.'), 79 | 'id' => 'columns', 80 | 'name' => 'columns', 81 | 'cols' => $columnSettings, 82 | 'rows' => $columns, 83 | 'addRowLabel' => Craft::t('Add a column'), 84 | 'initJs' => false 85 | )); 86 | 87 | $tableDataField = craft()->templates->render('settable/settings', array( 88 | 'label' => Craft::t('Table Values'), 89 | 'instructions' => Craft::t('Define the set values for the field.'), 90 | 'id' => 'tableData', 91 | 'name' => 'tableData', 92 | 'cols' => $columns, 93 | 'rows' => $tableData, 94 | 'initJs' => false 95 | )); 96 | 97 | return $columnsField.$tableDataField; 98 | } 99 | 100 | public function getInputHtml($name, $value) 101 | { 102 | $input = ''; 103 | 104 | $tableHtml = $this->_getInputHtml($name, $value, false); 105 | 106 | if ($tableHtml) { 107 | $input .= $tableHtml; 108 | } 109 | 110 | return $input; 111 | } 112 | 113 | public function prepValueFromPost($value) 114 | { 115 | return $value; 116 | } 117 | 118 | public function prepValue($value) 119 | { 120 | if (is_array($value) && ($columns = $this->getSettings()->columns)) { 121 | // Make the values accessible from both the col IDs and the handles 122 | foreach ($value as &$row) { 123 | foreach ($columns as $colId => $col) { 124 | if ($col['handle']) { 125 | $row[$col['handle']] = (isset($row[$colId]) ? $row[$colId] : null); 126 | } 127 | } 128 | } 129 | 130 | return $value; 131 | } 132 | } 133 | 134 | protected function defineSettings() 135 | { 136 | return array( 137 | 'columns' => AttributeType::Mixed, 138 | 'tableData' => AttributeType::Mixed, 139 | ); 140 | } 141 | 142 | public function prepSettings($settings) 143 | { 144 | if (!isset($settings['tableData'])) { 145 | $settings['tableData'] = array(); 146 | } 147 | 148 | return $settings; 149 | } 150 | 151 | private function _getInputHtml($name, $value, $static) 152 | { 153 | $columns = $this->getSettings()->columns; 154 | $tableData = $this->getSettings()->tableData; 155 | 156 | if ($columns) { 157 | // Translate the column headings 158 | foreach ($columns as &$column) { 159 | if (!empty($column['heading'])) { 160 | $column['heading'] = Craft::t($column['heading']); 161 | } 162 | } 163 | 164 | // Minor fix for Backwards-compatibility - migrate old data into new key 165 | if ($value && is_array($value)) { 166 | foreach ($value as $key => $val) { 167 | if (is_numeric($key)) { 168 | $value['row'.($key+1)] = $val; 169 | unset($value[$key]); 170 | } 171 | } 172 | } 173 | 174 | if (!$value) { 175 | if (is_array($tableData)) { 176 | $value = $tableData; 177 | } 178 | } else { 179 | // Merge the saved existing values and any new rows 180 | foreach ($tableData as $key => $val) { 181 | if (!isset($value[$key])) { 182 | $value[$key] = $val; 183 | } 184 | } 185 | } 186 | 187 | $id = craft()->templates->formatInputId($name); 188 | 189 | return craft()->templates->render('settable/field', array( 190 | 'id' => $id, 191 | 'name' => $name, 192 | 'cols' => $columns, 193 | 'rows' => $value, 194 | )); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /settable/resources/css/settable.css: -------------------------------------------------------------------------------- 1 | .setTable { 2 | position: relative; 3 | margin-bottom: 10px; 4 | border-radius: 3px; 5 | background: #f9fafa; 6 | padding: 1px 0 0 0; 7 | 8 | -webkit-user-select: none; 9 | -moz-user-select: none; 10 | -ms-user-select: none; 11 | 12 | outline: none; 13 | -webkit-box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); 14 | -moz-box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); 15 | box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1); 16 | } 17 | 18 | .setTable thead { 19 | border-radius: 2px 2px 0 0; 20 | background: #eef0f1; 21 | color: #b9bfc6; 22 | } 23 | 24 | .setTable thead tr th:first-child { 25 | border-left: 1px #DFE0E0 solid; 26 | border-radius: 2px 0 0 0; 27 | } 28 | 29 | .setTable thead tr th:last-child { 30 | border-right: 1px #DFE0E0 solid; 31 | border-radius: 0 2px 0 0; 32 | } 33 | 34 | .setTable thead tr th, 35 | .setTable tbody tr td { 36 | padding-left: 14px; 37 | } 38 | 39 | .setTable thead tr th:last-child, 40 | .setTable tbody tr td:last-child { 41 | padding-right: 14px; 42 | } 43 | 44 | 45 | /* Settings */ 46 | 47 | table.editable tbody tr td.type-label { 48 | background: #f9fafa; 49 | } 50 | 51 | table.editable tbody tr td.type-label textarea { 52 | background: transparent; 53 | } 54 | -------------------------------------------------------------------------------- /settable/templates/field.html: -------------------------------------------------------------------------------- 1 | {% import "_includes/forms" as forms %} 2 | 3 | {% includeCssResource 'settable/css/settable.css' %} 4 | 5 |
6 | 7 | 8 | 9 | {% for col in cols %} 10 | 11 | {% endfor %} 12 | 13 | 14 | 15 | {% for rowId, row in rows %} 16 | 17 | {% for colId, col in cols %} 18 | {% set cellName = name~'['~rowId~']['~colId~']' %} 19 | {% set value = (row[colId] is defined ? row[colId] : null) %} 20 | 21 | 47 | {% endfor %} 48 | 49 | {% endfor %} 50 | 51 |
{{ col.heading ? col.heading : ' ' }}
22 | {% if col.type == 'select' %} 23 | {{ forms.selectField({ 24 | class: 'small', 25 | id: cellName, 26 | name: cellName, 27 | options: col.options, 28 | value: value, 29 | }) }} 30 | {% elseif col.type == 'checkbox' %} 31 | {{ forms.checkboxField({ 32 | id: cellName, 33 | name: cellName, 34 | checked: value, 35 | }) }} 36 | {% elseif col.type == 'label' %} 37 | 38 | {{ value }} 39 | {% else %} 40 | {{ forms.textField({ 41 | id: cellName, 42 | name: cellName, 43 | value: value 44 | }) }} 45 | {% endif %} 46 |
52 |
-------------------------------------------------------------------------------- /settable/templates/settings-table.html: -------------------------------------------------------------------------------- 1 | {%- set static = (static is defined ? static : false) %} 2 | {%- set cols = (cols is defined ? cols : []) %} 3 | {%- set rows = (rows is defined ? rows : []) -%} 4 | 5 | 6 | 7 | 8 | {% for col in cols %} 9 | 10 | {% endfor %} 11 | {% if not static %} 12 | 13 | {% endif %} 14 | 15 | 16 | 17 | {% for rowId, row in rows %} 18 | 19 | {% for colId, col in cols %} 20 | {% set cellName = name~'['~rowId~']['~colId~']' %} 21 | {% set value = (row[colId] is defined ? row[colId] : null) %} 22 | {% set textual = (col.type in ['singleline','multiline','number']) %} 23 | 39 | {% endfor %} 40 | {% if not static %} 41 | 42 | 43 | {% endif %} 44 | 45 | {% endfor %} 46 | 47 |
{{ col.heading ? col.heading : ' ' }}
24 | {%- if col.type == 'select' -%} 25 | {% include "_includes/forms/select" with { 26 | class: 'small', 27 | name: cellName, 28 | options: col.options, 29 | value: value, 30 | disabled: static 31 | } only %} 32 | {%- elseif col.type == 'checkbox' -%} 33 | 34 | 35 | {%- else -%} 36 | 37 | {%- endif -%} 38 |
48 | {% if not static %} 49 |
{{ addRowLabel is defined ? addRowLabel : "Add a row"|t }}
50 | {% endif %} 51 | 52 | {% if not static and (initJs is not defined or initJs) %} 53 | {% set js %} 54 | new Craft.EditableTable( 55 | "{{ id | namespaceInputId | e('js') }}", 56 | "{{ name | namespaceInputName | e('js') }}", 57 | {{ cols|json_encode|raw }}); 58 | {% endset %} 59 | 60 | {% includeJs js %} 61 | {% endif %} 62 | -------------------------------------------------------------------------------- /settable/templates/settings.html: -------------------------------------------------------------------------------- 1 | {%- set id = (id is defined ? id : null) %} 2 | {%- set label = (label is defined and label != '__blank__' ? label : null) %} 3 | {%- set locale = ((craft.isLocalized() and locale is defined) ? locale : null) %} 4 | {%- set instructions = (instructions is defined ? instructions : null) %} 5 | {%- set warning = (warning is defined ? warning : null) %} 6 | {%- set errors = (errors is defined ? errors : null) -%} 7 | 8 |
9 | {% if label or instructions %} 10 |
11 | {% if label %} 12 | 13 | {{- label|raw -}} 14 | 15 | {% if locale %} 16 | {{ locale }} 17 | {% endif %} 18 | {% endif %} 19 | {% if instructions %} 20 |
{{ instructions|md }}
21 | {% endif %} 22 |
23 | {% endif %} 24 |
25 | {% include 'settable/settings-table' %} 26 |
27 | {% if warning %} 28 |

{{ warning }}

29 | {% endif %} 30 | {% include "_includes/forms/errorList" with { errors: errors } %} 31 |
32 | --------------------------------------------------------------------------------