├── 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 |
6 | i18n($f['label']), false) ?> 7 | | 8 | 9 |10 | 11 | | 12 |
---|---|
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 | | 39 | 40 |41 | 42 | 43 | 44 | | 45 |