├── README.md ├── fields └── snippetfield │ ├── assets │ ├── css │ │ └── structure.css │ └── js │ │ └── structure.js │ ├── controller.php │ ├── forms │ ├── add.php │ ├── delete.php │ └── update.php │ ├── snippetfield.php │ ├── styles │ ├── items.php │ └── table.php │ ├── template.php │ └── views │ ├── add.php │ ├── delete.php │ └── update.php ├── kirby-snippetfield.php └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # Kirby Snippetfield 2 | 3 | *Version 0.2* 4 | 5 | This field work exactly like the [structure field](https://getkirby.com/docs/cheatsheet/panel-fields/structure). In fact it's a copy of it with some important changes. 6 | 7 | ***The Snippetfield does not use entry. Instead it uses snippets.*** 8 | 9 | ## Installation 10 | 11 | Use one of the alternatives below. 12 | 13 | ### 1. Kirby CLI 14 | 15 | If you are using the [Kirby CLI](https://github.com/getkirby/cli) you can install this plugin by running the following commands in your shell: 16 | 17 | ```text 18 | $ cd path/to/kirby 19 | $ kirby plugin:install jenstornell/kirby-snippetfield 20 | ``` 21 | 22 | ### 2. Clone or download 23 | 24 | 1. [Clone](https://github.com/jenstornell/kirby-snippetfield.git) or [download](https://github.com/jenstornell/kirby-snippetfield/archive/master.zip) this repository. 25 | 2. Unzip the archive if needed and rename the folder to `kirby-snippetfield`. 26 | 27 | **Make sure that the plugin folder structure looks like this:** 28 | 29 | ```text 30 | site/plugins/kirby-snippetfield/ 31 | ``` 32 | 33 | ### 3. Git Submodule 34 | 35 | If you know your way around Git, you can download this plugin as a submodule: 36 | 37 | ```text 38 | $ cd path/to/kirby 39 | $ git submodule add https://github.com/jenstornell/kirby-snippetfield site/plugins/kirby-snippetfield 40 | ``` 41 | 42 | ## 1. Blueprint with `style: items` (default) 43 | 44 | I used the example from the docs. I have replaced `entry` with `snippet` and added `style: items` (just to make it clear). 45 | 46 | ```yaml 47 | fields: 48 | addresses: 49 | label: Addresses 50 | type: snippetfield 51 | snippet: mydir/snippet 52 | style: items 53 | fields: 54 | street: 55 | label: Street 56 | type: text 57 | zip: 58 | label: ZIP 59 | type: text 60 | city: 61 | label: City 62 | type: text 63 | ``` 64 | 65 | The example above will use the snippet `site/snippets/mydir/snippet.php`. You can change the path in the `config.php`. 66 | 67 | ## 1. Blueprint with `style: table` 68 | 69 | For the table style you need a snippet for every field. 70 | 71 | ```yaml 72 | fields: 73 | addresses: 74 | label: Addresses 75 | type: snippetfield 76 | style: table 77 | fields: 78 | street: 79 | label: Street 80 | type: text 81 | snippet: mydir/snippet1 82 | zip: 83 | label: ZIP 84 | type: text 85 | snippet: mydir/snippet2 86 | city: 87 | label: City 88 | type: text 89 | snippet: mydir/snippet2 90 | ``` 91 | 92 | ### Config 93 | 94 | If you don't like the root path, you can change it in your `config.php`: 95 | 96 | ```php 97 | c::set( 'snippetfield.path', kirby()->roots()->snippets() ); 98 | ``` 99 | 100 | ## 2. Snippet 101 | 102 | ### `$page` object 103 | 104 | All the page variables are available through the `$page` object and you can use them like this: 105 | 106 | ```php 107 | echo $page->title(); 108 | ``` 109 | 110 | ### `$field` object 111 | 112 | To give you a hint of what it contains, do this (might be slow or crash): 113 | 114 | ```php 115 | print_r( $field ); 116 | ``` 117 | 118 | ### `$style` string 119 | 120 | If for some reason you need to have the style value you can use that. 121 | 122 | ## 3. Snippet with `style: items` (default) 123 | 124 | If you use `style: items` you also have access to the `$values` object. 125 | 126 | ### `$values` object 127 | 128 | Because the output contains more than one field, all the field keys and values are in the `$values` object. 129 | 130 | ```php 131 | print_r( $values ); 132 | ``` 133 | 134 | ## 3. Snippets with `style: table` 135 | 136 | If you use `style: table` you also have access to the `$value` string. 137 | 138 | ### `$value` string 139 | 140 | Because the output only contain one field, you only need a string and that is the `$value` string. 141 | 142 | ## Snippet with `style: table` example 143 | 144 | In this case I have an `image` field and the `$value` will be `filename.jpg`. It is actually using the thumbnail cache to generate and store a thumbnail of an image. 145 | 146 | ```php 147 | 148 | ``` 149 | 150 | You now also have access to `$key` which is the column slug. 151 | 152 | ## What is wrong with entry? 153 | 154 | - It does not support any logic. 155 | - Images can not be viewed as images, only as file names. 156 | 157 | ## What can be done with Snippetfield? 158 | 159 | - **Use calculations and logic** 160 | - **Format values** 161 | - **Image galleries** can be created quite easily. 162 | - **If statements**. Maybe you want to display a pink elephant every time a value is true. Now you can. 163 | - **Advanced stuff** could be made, like take the value, run it trough Google Analytics, get some data back and present that. 164 | 165 | ## Changelog 166 | 167 | **0.2** 168 | 169 | - Made it a plugin instead of a field. Will probably require Kirby 2.4.1 from now on. 170 | - Added `$key` to `style: table` which is the column slug. 171 | - Added `package.json` which means Kirby CLI support. 172 | 173 | **0.1** 174 | 175 | - Initial release 176 | -------------------------------------------------------------------------------- /fields/snippetfield/assets/css/structure.css: -------------------------------------------------------------------------------- 1 | .structure { 2 | padding-bottom: .5em; 3 | } 4 | .structure-entry { 5 | background: #fff; 6 | border: 2px solid #ddd; 7 | margin-bottom: .5em; 8 | } 9 | .structure-readonly .structure-entry { 10 | background: #efefef; 11 | color: #777; 12 | } 13 | .structure-entry:last-child { 14 | margin-bottom: 0; 15 | } 16 | .structure-entry-content { 17 | padding: 1em 1.5em; 18 | border-bottom: 1px solid #efefef; 19 | } 20 | .structure[data-sortable=true] .structure-entry-content { 21 | cursor: move; 22 | } 23 | .structure-entry-options .btn { 24 | padding: .75em 1.5em; 25 | width: 50%; 26 | float: left; 27 | border-right: 1px solid #efefef; 28 | } 29 | .structure-empty { 30 | padding: 1.5em; 31 | background: #ddd; 32 | } 33 | .fileview-sidebar .structure-empty { 34 | background: none; 35 | border-radius: 5px; 36 | border: 1px dashed #ddd; 37 | padding: 1rem 1.5rem 1.25rem; 38 | } 39 | .structure-empty a { 40 | border-bottom: 2px solid #aaa; 41 | margin-left: .5em; 42 | } 43 | .fileview-sidebar .structure-empty a { 44 | display: inline-block; 45 | margin-left: 0; 46 | } 47 | .structure-empty a:hover { 48 | border-color: #000; 49 | } 50 | .structure-add-button { 51 | cursor: pointer; 52 | } 53 | 54 | 55 | /* Table */ 56 | .structure-table { 57 | width: 100%; 58 | border-spacing: 0; 59 | border: 2px solid #ddd; 60 | border-bottom: 1px solid #ddd; 61 | border-right: 1px solid #ddd; 62 | table-layout: fixed; 63 | } 64 | .structure-table td, .structure-table th { 65 | background: #fff; 66 | border-bottom: 1px solid #ddd; 67 | border-right: 1px solid #ddd; 68 | text-align: left; 69 | vertical-align: top; 70 | } 71 | .structure-table th { 72 | padding: .5em; 73 | font-weight: 400; 74 | color: #777; 75 | font-style: italic; 76 | } 77 | .structure-table td a { 78 | display: block; 79 | padding: .5em; 80 | overflow: hidden; 81 | width: 100%; 82 | text-overflow: ellipsis; 83 | cursor: move; 84 | } 85 | .structure-table-options { 86 | width: 3rem; 87 | text-align: center; 88 | } 89 | .structure-table .structure-table-options a { 90 | text-align: center; 91 | cursor: pointer; 92 | } 93 | 94 | .structure-sortable-helper { 95 | border-top: 1px solid #ddd; 96 | border-left: 1px solid #ddd; 97 | } 98 | -------------------------------------------------------------------------------- /fields/snippetfield/assets/js/structure.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | var Structure = function(el) { 4 | 5 | var element = $(el); 6 | var style = element.data('style'); 7 | var api = element.data('api'); 8 | var sortable = element.data('sortable'); 9 | var entries = style == 'table' ? element.find('.structure-table tbody') : element.find('.structure-entries'); 10 | 11 | if(sortable === false) return false; 12 | 13 | entries.sortable({ 14 | helper: function(e, ui) { 15 | ui.children().each(function() { 16 | $(this).width($(this).width()); 17 | }); 18 | return ui.addClass('structure-sortable-helper'); 19 | }, 20 | update: function() { 21 | 22 | var ids = []; 23 | 24 | $.each($(this).sortable('toArray'), function(i, id) { 25 | ids.push(id.replace('structure-entry-', '')); 26 | }); 27 | 28 | $.post(api, {ids: ids}, function() { 29 | app.content.reload(); 30 | }); 31 | 32 | } 33 | }); 34 | 35 | }; 36 | 37 | $.fn.structure = function() { 38 | 39 | return this.each(function() { 40 | 41 | if($(this).data('structure')) { 42 | return $(this); 43 | } else { 44 | var structure = new Structure(this); 45 | $(this).data('structure', structure); 46 | return $(this); 47 | } 48 | 49 | }); 50 | 51 | }; 52 | 53 | })(jQuery); -------------------------------------------------------------------------------- /fields/snippetfield/controller.php: -------------------------------------------------------------------------------- 1 | model(); 9 | $structure = $this->structure($model); 10 | $modalsize = $this->field()->modalsize(); 11 | $form = $this->form('add', array($model, $structure), function($form) use($model, $structure, $self) { 12 | 13 | $form->validate(); 14 | 15 | if(!$form->isValid()) { 16 | return false; 17 | } 18 | 19 | $structure->add($form->serialize()); 20 | $self->redirect($model); 21 | 22 | }); 23 | 24 | return $this->modal('add', compact('form', 'modalsize')); 25 | 26 | } 27 | 28 | public function update($entryId) { 29 | 30 | $self = $this; 31 | $model = $this->model(); 32 | $structure = $this->structure($model); 33 | $entry = $structure->find($entryId); 34 | 35 | if(!$entry) { 36 | return $this->modal('error', array( 37 | 'text' => l('fields.structure.entry.error') 38 | )); 39 | } 40 | 41 | $modalsize = $this->field()->modalsize(); 42 | $form = $this->form('update', array($model, $structure, $entry), function($form) use($model, $structure, $self, $entryId) { 43 | 44 | // run the form validator 45 | $form->validate(); 46 | 47 | if(!$form->isValid()) { 48 | return false; 49 | } 50 | 51 | $structure->update($entryId, $form->serialize()); 52 | $self->redirect($model); 53 | 54 | }); 55 | 56 | return $this->modal('update', compact('form', 'modalsize')); 57 | 58 | } 59 | 60 | public function delete($entryId) { 61 | 62 | $self = $this; 63 | $model = $this->model(); 64 | $structure = $this->structure($model); 65 | $entry = $structure->find($entryId); 66 | 67 | if(!$entry) { 68 | return $this->modal('error', array( 69 | 'text' => l('fields.structure.entry.error') 70 | )); 71 | } 72 | 73 | $form = $this->form('delete', $model, function() use($self, $model, $structure, $entryId) { 74 | $structure->delete($entryId); 75 | $self->redirect($model); 76 | }); 77 | 78 | return $this->modal('delete', compact('form')); 79 | 80 | } 81 | 82 | public function sort() { 83 | $model = $this->model(); 84 | $structure = $this->structure($model); 85 | $structure->sort(get('ids')); 86 | $this->redirect($model); 87 | } 88 | 89 | protected function structure($model) { 90 | return $model->structure()->forField($this->fieldname()); 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /fields/snippetfield/forms/add.php: -------------------------------------------------------------------------------- 1 | fields(), array(), $store->field()); 6 | $form->cancel($model); 7 | $form->buttons->submit->value = l('add'); 8 | 9 | return $form; 10 | 11 | }; -------------------------------------------------------------------------------- /fields/snippetfield/forms/delete.php: -------------------------------------------------------------------------------- 1 | array( 7 | 'label' => 'fields.structure.delete.label', 8 | 'type' => 'info', 9 | ) 10 | )); 11 | 12 | $form->style('delete'); 13 | $form->cancel($model); 14 | 15 | return $form; 16 | 17 | }; -------------------------------------------------------------------------------- /fields/snippetfield/forms/update.php: -------------------------------------------------------------------------------- 1 | fields(), $entry->toArray(), $store->field()); 6 | 7 | $form->cancel($model); 8 | $form->buttons->submit->value = l('ok'); 9 | 10 | return $form; 11 | 12 | }; -------------------------------------------------------------------------------- /fields/snippetfield/snippetfield.php: -------------------------------------------------------------------------------- 1 | array( 7 | 'structure.js' 8 | ), 9 | 'css' => array( 10 | 'structure.css' 11 | ) 12 | ); 13 | 14 | public $fields = array(); 15 | public $entry = null; 16 | public $structure = null; 17 | public $style = 'items'; 18 | public $modalsize = 'medium'; 19 | 20 | public function routes() { 21 | 22 | return array( 23 | array( 24 | 'pattern' => 'add', 25 | 'method' => 'get|post', 26 | 'action' => 'add' 27 | ), 28 | array( 29 | 'pattern' => 'sort', 30 | 'method' => 'post', 31 | 'action' => 'sort', 32 | ), 33 | array( 34 | 'pattern' => '(:any)/update', 35 | 'method' => 'get|post', 36 | 'action' => 'update' 37 | ), 38 | array( 39 | 'pattern' => '(:any)/delete', 40 | 'method' => 'get|post', 41 | 'action' => 'delete', 42 | ) 43 | ); 44 | } 45 | 46 | public function modalsize() { 47 | $sizes = array('small', 'medium', 'large'); 48 | return in_array($this->modalsize, $sizes) ? $this->modalsize : 'medium'; 49 | } 50 | 51 | public function style() { 52 | $styles = array('table', 'items'); 53 | return in_array($this->style, $styles) ? $this->style : 'items'; 54 | } 55 | 56 | public function structure() { 57 | if(!is_null($this->structure)) { 58 | return $this->structure; 59 | } else { 60 | return $this->structure = $this->model->structure()->forField($this->name); 61 | } 62 | } 63 | 64 | public function fields() { 65 | 66 | $output = array(); 67 | 68 | foreach($this->structure->fields() as $k => $v) { 69 | $v['name'] = $k; 70 | $v['value'] = '{{' . $k . '}}'; 71 | $output[] = $v; 72 | } 73 | 74 | return $output; 75 | 76 | } 77 | 78 | public function entries() { 79 | return $this->structure()->data(); 80 | } 81 | 82 | public function result() { 83 | return $this->structure()->toYaml(); 84 | } 85 | 86 | public function entry($data) { 87 | return tpl::load(c::get( 'snippetfield.path', kirby()->roots()->snippets() ) . DS . $this->snippet . '.php', array( 88 | 'page' => $this->page(), 89 | 'field' => $this, 90 | 'values' => $data, 91 | 'style' => $this->style, 92 | )); 93 | } 94 | 95 | public function label() { 96 | return null; 97 | } 98 | 99 | public function headline() { 100 | 101 | if(!$this->readonly) { 102 | 103 | $add = new Brick('a'); 104 | $add->html('' . l('fields.structure.add')); 105 | $add->addClass('structure-add-button label-option'); 106 | $add->data('modal', true); 107 | $add->attr('href', purl($this->model, 'field/' . $this->name . '/snippetfield/add')); 108 | 109 | } else { 110 | $add = null; 111 | } 112 | 113 | // make sure there's at least an empty label 114 | if(!$this->label) { 115 | $this->label = ' '; 116 | } 117 | 118 | $label = parent::label(); 119 | $label->addClass('structure-label'); 120 | $label->append($add); 121 | 122 | return $label; 123 | 124 | } 125 | 126 | public function content() { 127 | return tpl::load(__DIR__ . DS . 'template.php', array( 128 | 'page' => $this->page(), 129 | 'field' => $this, 130 | 'style' => $this->style, 131 | )); 132 | } 133 | 134 | public function url($action) { 135 | return purl($this->model(), 'field/' . $this->name() . '/snippetfield/' . $action); 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /fields/snippetfield/styles/items.php: -------------------------------------------------------------------------------- 1 | entries() as $entry): ?> 2 |
3 |
4 | entry($entry) ?> 5 |
6 | readonly()): ?> 7 | 16 | 17 |
18 | -------------------------------------------------------------------------------- /fields/snippetfield/styles/table.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fields() as $f): ?> 5 | 8 | 9 | 12 | 13 | 14 | 15 | entries() as $entry): ?> 16 | 17 | fields() as $f): ?> 18 | 39 | 40 | 45 | 46 | 47 | 48 |
6 | i18n($f['label']), false) ?> 7 | 10 |   11 |
19 | 20 | {$f['name']})): ?> 21 | roots()->snippets() ) . DS . $f['snippet'] . '.php', array( 24 | 'page' => $page, 25 | 'field' => $field, 26 | 'key' => $f['name'], 27 | 'value' => @$entry->{$f['name']}, 28 | 'style' => $style, 29 | ), true); 30 | } else { 31 | echo html(@$entry->{$f['name']}, false); 32 | } 33 | ?> 34 | 35 |   36 | 37 | 38 | 41 | 42 | 43 | 44 |
-------------------------------------------------------------------------------- /fields/snippetfield/template.php: -------------------------------------------------------------------------------- 1 |
6 | 7 | headline() ?> 8 | 9 |
10 | 11 | entries()->count()): ?> 12 |
13 | 14 |
15 | 16 | style() . '.php') ?> 17 | 18 |
19 | 20 |
-------------------------------------------------------------------------------- /fields/snippetfield/views/add.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fields/snippetfield/views/delete.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fields/snippetfield/views/update.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /kirby-snippetfield.php: -------------------------------------------------------------------------------- 1 | set('field', 'snippetfield', __DIR__ . DS . 'fields' . DS . 'snippetfield'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kirby-snippetfield", 3 | "description": "Snippets in a Kirby CMS structure field.", 4 | "author": "Jens Törnell ", 5 | "version": "0.2", 6 | "type": "kirby-plugin", 7 | "license": "MIT" 8 | } --------------------------------------------------------------------------------