├── blueprint-reader ├── README.md ├── docs │ ├── bblueprint.md │ ├── bfile.md │ ├── bparse.md │ ├── bread.md │ ├── cache.md │ ├── changelog.md │ ├── compare.md │ ├── install.md │ └── options.md ├── kirby-blueprint-reader.php ├── lib │ ├── api.php │ ├── cache.php │ ├── definitions.php │ ├── language.php │ ├── method-blueprint.php │ ├── method-read.php │ ├── methods.php │ └── read.php ├── license.md └── package.json ├── blueprints └── silence.yml ├── docs ├── changelog.md ├── hub-nodes.md ├── hub.md ├── installation.md ├── license.md ├── node.md └── troubleshooting.md ├── hub ├── hooks.php ├── routes.php ├── tasks │ ├── read.php │ └── urls.php └── tests │ ├── read.php │ └── urls.php ├── kirby-sync.php ├── node ├── config.php ├── routes.php ├── tasks.php ├── tasks │ ├── blueprint.php │ ├── create.php │ ├── delete.php │ ├── filename.php │ ├── move.php │ └── update.php └── tests │ ├── delete.php │ ├── save-create.php │ ├── save-filename.php │ ├── save-move.php │ └── save-update.php ├── package.json ├── readme.md └── shared ├── config.php ├── fetch.php └── settings.php /blueprint-reader/README.md: -------------------------------------------------------------------------------- 1 | # Kirby Blueprint Reader 2 | 3 | A very easy to use blueprint reader for Kirby CMS. 4 | 5 | *Version 0.4* - ***[Changelog](docs/changelog.md)*** 6 | 7 | **Features / Supports** 8 | 9 | - [x] Global field definitions 10 | - [x] Global field extends 11 | - [x] Translated fields 12 | - [x] Structure fields 13 | - [x] Simple array walking 14 | - [x] Caching 15 | 16 | ## Basic examples 17 | 18 | If you have a project blueprint, you can get the title field type by this one-liner: 19 | 20 | ```php 21 | echo b::blueprint('project', 'fields/title/type'); 22 | ``` 23 | 24 | If you want the complete blueprint data array, you can do that like this: 25 | 26 | ```php 27 | $data = b::blueprint('project'); 28 | print_r($data); 29 | ``` 30 | 31 | ## Methods 32 | 33 | These are the methods you can use. Read more about them if you need more advanced configurations. 34 | 35 | - **[b::blueprint](docs/bblueprint.md)($name, $steps, $options = array())** 36 | *Get the blueprint data array by template.* 37 | - **[b::read](docs/bread.md)($filepath, $steps, $options = array())** 38 | *Get the blueprint data array by filepath.* 39 | - **[b::file](docs/bfile.md)($name, $options = array())** 40 | *Get the blueprint filepath by template.* 41 | - **[b::parse](docs/bparse.md)($data, $steps, $options)** 42 | *Get the parsed blueprint data array by blueprint data array.* 43 | 44 | ## Table of contents 45 | 46 | - [Cache](docs/cache.md) 47 | - [Options](options.md) 48 | - [Differences to Kirby Architect](docs/compare.md) 49 | 50 | ## Requirements 51 | 52 | - [**Kirby**](https://getkirby.com/) 2.4.1+ 53 | 54 | ## Disclaimer 55 | 56 | This plugin is provided "as is" with no guarantee. Use it at your own risk and always test it yourself before using it in a production environment. If you find any issues, please [create a new issue](https://github.com/username/kirby-blueprint-reader/issues/new). 57 | 58 | ## License 59 | 60 | [**MIT**](https://opensource.org/licenses/MIT) 61 | 62 | It is discouraged to use this plugin in any project that promotes racism, sexism, homophobia, animal abuse, violence or any other form of hate speech. 63 | 64 | ## Credits 65 | 66 | - [Jens Törnell](https://github.com/jenstornell) -------------------------------------------------------------------------------- /blueprint-reader/docs/bblueprint.md: -------------------------------------------------------------------------------- 1 | # b::blueprint() 2 | 3 | Get a blueprint data array by a blueprint name. 4 | 5 | - **$name** (string) 6 | *Path to the blueprint file* 7 | - **$steps** (string)(optional) 8 | *Step the array to get your value.* 9 | - **$options** (array)(optional) 10 | - **$format** (array) 11 | *The format can be filepath or data. Default is data.* 12 | - **$language** (string) 13 | *The language of the fields. If no language is set, it will show all languages.* 14 | - **$definitions** (bool) 15 | *Turn global field definitions off by set it to false. It's set to true by default.* 16 | - **return** (array) 17 | *It will return an array with blueprint data if found, else it will return null.* 18 | 19 | ### Basic examples 20 | 21 | We use the blueprint name to get the blueprint data as array: 22 | 23 | ```php 24 | $data = b::blueprint('project'); 25 | print_r($data); 26 | ``` 27 | 28 | We can also use the step feature as second argument, to get the field title type and print it out. 29 | 30 | ```php 31 | echo b::blueprint('project', 'fields/title/type'); 32 | ``` 33 | 34 | ### Advanced example 35 | 36 | In this case we don't use the steps feature. Instead of having steps as a second argument we can set options as second argument instead. 37 | 38 | ```php 39 | $options = array( 40 | 'language' => 'sv', 41 | 'definitions' => false 42 | ); 43 | 44 | $data = b::blueprint('project', $options); 45 | print_r($data); 46 | ``` 47 | 48 | To also use steps, we set it as the second argument and push the options to the third argument: 49 | 50 | ```php 51 | $options = array( 52 | 'language' => 'sv', 53 | 'definitions' => false 54 | ); 55 | 56 | echo b::blueprint('project', 'fields/title/type', $options); 57 | ``` -------------------------------------------------------------------------------- /blueprint-reader/docs/bfile.md: -------------------------------------------------------------------------------- 1 | # b::file() 2 | 3 | Get a filepath by a blueprint name. 4 | 5 | - **$name** (string) 6 | *Path to the blueprint file* 7 | - **return** (array) 8 | *It will return a blueprint filepath if found, else it will return null.* 9 | 10 | ### Basic example 11 | 12 | We use the blueprint name to get the blueprint filepath: 13 | 14 | ```php 15 | echo b::file('project'); 16 | ``` -------------------------------------------------------------------------------- /blueprint-reader/docs/bparse.md: -------------------------------------------------------------------------------- 1 | # b::parse() 2 | 3 | Get a parsed blueprint data array by an own blueprint data array. It will include global field defintions, extends and translations. 4 | 5 | - **$data** (array) 6 | *Path to the blueprint file* 7 | - **$steps** (string)(optional) 8 | *Step the array to get your value.* 9 | - **$options** (array)(optional) 10 | - **$language** (string) 11 | *The language of the fields. If no language is set, it will show all languages.* 12 | - **$definitions** (bool) 13 | *Turn global field definitions off by set it to false. It's set to true by default.* 14 | - **return** (array) 15 | *It will return an array with blueprint data if found, else it will return null.* 16 | 17 | ### Basic examples 18 | 19 | We use an own blueprint data array. Then we run it through the parser to include global field definitions, extends and translations. 20 | 21 | ```php 22 | $data = array( 23 | 'title' => 'My page', 24 | 'fields' => array( 25 | 'title' => array( 26 | 'label' => array( 27 | 'sv' => 'Hej världen!', 28 | 'en' => 'Hello world!', 29 | ), 30 | 'type' => 'title', 31 | ) 32 | ) 33 | ); 34 | 35 | $data = b::parse($data); 36 | print_r($data); 37 | ``` 38 | 39 | We can also use the step feature as second argument, to get the field title type and print it out. 40 | 41 | ```php 42 | echo b::parse('project', 'fields/title/type'); 43 | ``` 44 | 45 | ### Advanced example 46 | 47 | In this case we don't use the steps feature. Instead of having steps as a second argument we can set options as second argument instead. 48 | 49 | ```php 50 | $data = array( 51 | 'title' => 'My page', 52 | 'fields' => array( 53 | 'title' => array( 54 | 'label' => array( 55 | 'sv' => 'Hej världen!', 56 | 'en' => 'Hello world!', 57 | ), 58 | 'type' => 'title', 59 | ) 60 | ) 61 | ); 62 | 63 | $options = array( 64 | 'language' => 'sv', 65 | 'definitions' => false 66 | ); 67 | 68 | $data = b::parse('project', $options); 69 | print_r($data); 70 | ``` 71 | 72 | To also use steps, we set it as the second argument and push the options to the third argument: 73 | 74 | ```php 75 | $data = array( 76 | 'title' => 'My page', 77 | 'fields' => array( 78 | 'title' => array( 79 | 'label' => array( 80 | 'sv' => 'Hej världen!', 81 | 'en' => 'Hello world!', 82 | ), 83 | 'type' => 'title', 84 | ) 85 | ) 86 | ); 87 | 88 | $options = array( 89 | 'language' => 'sv', 90 | 'definitions' => false 91 | ); 92 | 93 | echo b::parse('project', 'fields/title/type', $options); 94 | ``` -------------------------------------------------------------------------------- /blueprint-reader/docs/bread.md: -------------------------------------------------------------------------------- 1 | # b::read() 2 | 3 | Get a blueprint data array by a blueprint filepath. 4 | 5 | - **$filepath** (string) 6 | *Path to the blueprint file* 7 | - **$steps** (string)(optional) 8 | *Step the array to get your value.* 9 | - **$options** (array)(optional) 10 | - **$cache_key** (string) 11 | *Name of the cache key. If no cache key name is set, it will use the filename.* 12 | - **$language** (string) 13 | *The language of the fields. If no language is set, it will show all languages.* 14 | - **$definitions** (bool) 15 | *Turn global field definitions off by set it to false. It's set to true by default.* 16 | - **return** (array) 17 | *It will return an array with blueprint data if found, else it will return null.* 18 | 19 | ### Basic examples 20 | 21 | We use the filepath to get the blueprint data as array: 22 | 23 | ```php 24 | $data = b::read('C:\xampp\htdocs\kirby\site\blueprints\project.yml'); 25 | print_r($data); 26 | ``` 27 | 28 | We can also use the step feature as second argument, to get the field title type and print it out. 29 | 30 | ```php 31 | echo b::read('C:\xampp\htdocs\kirby\site\blueprints\project.yml', 'fields/title/type'); 32 | ``` 33 | 34 | ### Advanced example 35 | 36 | In this case we don't use the steps feature. Instead of having steps as a second argument we can set options as second argument instead. 37 | 38 | ```php 39 | $options = array( 40 | 'cache_key' => 'my-cache-key', 41 | 'language' => 'sv', 42 | 'definitions' => false 43 | ); 44 | 45 | $data = b::read('C:\xampp\htdocs\kirby\site\blueprints\project.yml', $options); 46 | print_r($data); 47 | ``` 48 | 49 | To also use steps, we set it as the second argument and push the options to the third argument: 50 | 51 | ```php 52 | $options = array( 53 | 'cache_key' => 'my-cache-key', 54 | 'language' => 'sv', 55 | 'definitions' => false 56 | ); 57 | 58 | echo b::read('C:\xampp\htdocs\kirby\site\blueprints\project.yml', 'fields/title/type', $options); 59 | ``` -------------------------------------------------------------------------------- /blueprint-reader/docs/cache.md: -------------------------------------------------------------------------------- 1 | # Cache 2 | 3 | The blueprint reader has a built in memory cache. You don't need to do anything to set it up. It's always working behind the scenes and does not save any files because it's stored in the memory. 4 | 5 | ## Cached types 6 | 7 | There are three different types of data that will be cached: 8 | 9 | - [x] Blueprint data `blueprint.reader.data` 10 | - [x] Blueprint filepaths `blueprint.reader.files` 11 | - [x] Global field definitions `blueprint.reader.definitions` 12 | 13 | ### Storage 14 | 15 | The cached data is stored in the memory. After your function calls are made you can look at your cache. 16 | 17 | ```php 18 | print_r(kirby()->get('option', 'blueprint.reader.data')); 19 | print_r(kirby()->get('option', 'blueprint.reader.files')); 20 | print_r(kirby()->get('option', 'blueprint.reader.definitions')); 21 | ``` 22 | 23 | ### Blueprint data 24 | 25 | This cache stores the full blueprint arrays, if you have used the `b::blueprint()`. 26 | 27 | ### Blueprint filepaths 28 | 29 | This cache stores the blueprint filepaths, if you have used the `b::read()` method. 30 | 31 | ### Global field definitions 32 | 33 | This cache stores the blueprint global field definitions. All the methods that returns the blueprint data array will parse the global field definitions. -------------------------------------------------------------------------------- /blueprint-reader/docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | **0.4** 4 | 5 | - Support for translations. 6 | - Support for users blueprints with `users/blueprint`. 7 | - Support for multiple caches separated by return type. 8 | - Support for array step like `fields/title/type`. 9 | - Support for global options. 10 | - Support for `.yaml` and `.php` extensions. 11 | - Changed syntax from `bread::` to `b::`. 12 | - Changed arguments. Steps added and options is now an array. 13 | - Changed `b::file()` to use filename as cache-key instead of path as default. 14 | - Changed the behavior of the `$name` argument, which does no longer fallback to `default.yml`. 15 | - Docs changed. 16 | - Bugfixes and code enhancements. 17 | 18 | **0.3** 19 | 20 | - Fixed major bug with singleton class. 21 | - Fixed major bug with `bread::parse()`. 22 | - Added support for global field definitions in structure fields. 23 | - Added support for global field definitions extends in structure fields. 24 | - Added [compare](compare.md) to Kirby Architect. 25 | 26 | **0.2** 27 | 28 | - Complete rewrite 29 | 30 | **0.1** 31 | 32 | - Initial release -------------------------------------------------------------------------------- /blueprint-reader/docs/compare.md: -------------------------------------------------------------------------------- 1 | # Kirby Blueprint Reader VS Kirby Architect 2 | 3 | Because [Kirby Architect](https://github.com/AugustMiller/kirby-architect) is the only competitor, I have made a comparation table for both plugins. 4 | 5 | | Feature | Blueprint Reader | Architect 6 | | -------------------------------------------------------- | ---------------- | --------- 7 | | Support for global field definitions | Yes | - 8 | | Support for global field extends | Yes | - 9 | | Support for definitions and extends in structure fields | Yes | - 10 | | Method to get the label | - | Yes 11 | | Method to get an option label | - | Yes 12 | | Method to get options | - | Yes 13 | | Method to generate a menu | - | Yes 14 | | Method to blacklist values | - | Yes 15 | | Method to step in the array | Yes | - 16 | | Method to get the blueprint filepath by template | Yes | - 17 | | Method to get the blueprint data by a filepath | Yes | - 18 | | Method to parse a blueprint array | Yes | - 19 | 20 | ## Summery 21 | 22 | Which one you need depends on your needs. They are very different in what you can do and how things work and how the syntax looks like. 23 | 24 | **[Kirby Blueprint Reader](https://github.com/jenstornell/kirby-blueprint-reader)** 25 | It's probably better for reading and parsing as it supports global field definitions, extends, structure fields and array stepping. 26 | 27 | **[Kirby Architect](https://github.com/AugustMiller/kirby-architect)** 28 | It's probably better for help with generating html as it supports methods for menus, options and labels. -------------------------------------------------------------------------------- /blueprint-reader/docs/install.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Use one of the alternatives below. 4 | 5 | ## 1. Kirby CLI 6 | 7 | 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: 8 | 9 | ``` 10 | $ cd path/to/kirby 11 | $ kirby plugin:install jenstornell/kirby-blueprint-reader 12 | ``` 13 | 14 | ## 2. Clone or download 15 | 16 | 1. [Clone](https://github.com/jenstornell/kirby-blueprint-reader.git) or [download](https://github.com/jenstornell/kirby-blueprint-reader/archive/master.zip) this repository. 17 | 2. Unzip the archive if needed and rename the folder to `kirby-blueprint-reader`. 18 | 19 | **Make sure that the plugin folder structure looks like this:** 20 | 21 | ``` 22 | site/plugins/kirby-blueprint-reader/ 23 | ``` 24 | 25 | ## 3. Git Submodule 26 | 27 | If you know your way around Git, you can download this plugin as a submodule: 28 | 29 | ``` 30 | $ cd path/to/kirby 31 | $ git submodule add https://github.com/jenstornell/kirby-blueprint-reader site/plugins/kirby-blueprint-reader 32 | ``` -------------------------------------------------------------------------------- /blueprint-reader/docs/options.md: -------------------------------------------------------------------------------- 1 | # Options 2 | 3 | ```php 4 | c::set('blueprint.reader.language', null); 5 | c::set('blueprint.reader.definitions', true); 6 | c::set('blueprint.reader.format', 'array'); 7 | ``` 8 | 9 | ### blueprint.reader.language 10 | 11 | Blueprint fallback language if no `language` argument is sent. It defaults to `null` which will show all translated values. 12 | 13 | ### blueprint.reader.definitions 14 | 15 | You can enable or disable global field definitions by sending an argument to the function, or use this option as fallback. It defaults to `true` which means that global fields definitions will be included. 16 | 17 | ### blueprint.reader.format 18 | 19 | The blueprint format that your blueprint will be returned. It can be `array` or `filepath`. -------------------------------------------------------------------------------- /blueprint-reader/kirby-blueprint-reader.php: -------------------------------------------------------------------------------- 1 | get($options, $steps_options, $steps); 17 | return $data; 18 | } 19 | 20 | public static function read($filepath, $steps_options = array(), $options = array()) { 21 | $Read = new BlueprintReader\MethodRead(); 22 | $steps = array(); 23 | 24 | if(is_string($steps_options)) { 25 | $steps = explode('/', $steps_options); 26 | } else { 27 | $options = $steps_options; 28 | } 29 | 30 | $options['filepath'] = $filepath; 31 | return $Read->get($options, $steps_options, $steps); 32 | } 33 | 34 | public static function file($template, $options = array()) { 35 | $options['format'] = 'filepath'; 36 | return self::blueprint($template, $options); 37 | } 38 | 39 | public static function parse($data, $steps_options = array(), $options = array()) { 40 | $steps = array(); 41 | 42 | if(is_string($steps_options)) { 43 | $steps = explode('/', $steps_options); 44 | } else { 45 | $options = $steps_options; 46 | } 47 | 48 | $options = BlueprintReader\fallbackOptions($options); 49 | $data = BlueprintReader\parse($data, $options); 50 | 51 | if(is_string($steps_options) && $format != 'filepath') { 52 | $data = steps($steps, $data); 53 | } 54 | 55 | return $data; 56 | } 57 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/cache.php: -------------------------------------------------------------------------------- 1 | kirby = $kirby; 8 | } 9 | 10 | function getOption($option) { 11 | return $this->kirby->get('option', $option); 12 | } 13 | 14 | function get($template, $option) { 15 | $cache = $this->getOption($option); 16 | if(isset($cache[$template])) { 17 | return $cache[$template]; 18 | } 19 | } 20 | 21 | function set($template, $data, $option) { 22 | $cache = $this->getOption($option); 23 | $cache[$template] = $data; 24 | $this->kirby->set('option', $option, $cache); 25 | return $data; 26 | } 27 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/definitions.php: -------------------------------------------------------------------------------- 1 | kirby = $kirby; 10 | $array = $this->walkField($array); 11 | return $array; 12 | } 13 | 14 | function walkField($array) { 15 | if(array_key_exists('fields', $array)) { 16 | foreach($array['fields'] as $key => $field) { 17 | if(is_string($field)) { 18 | $array['fields'][$key] = $this->setDefinition($field, $key); 19 | } elseif(is_array($field)) { 20 | if(!empty($field['extends'])) { 21 | $array['fields'][$key] = $this->setExtends($field, $key); 22 | } 23 | $array = $this->walkStructure($field, $key, $array); 24 | } 25 | } 26 | } 27 | return $array; 28 | } 29 | 30 | function walkStructure($field, $key, $array) { 31 | if(array_key_exists('fields', $field)) { 32 | foreach($field['fields'] as $key2 => $item) { 33 | if(is_string($item)) { 34 | $array['fields'][$key]['fields'][$key2] = $this->setDefinition($item, $key2); 35 | } elseif(is_array($item)) { 36 | if(!empty($item['extends'])) { 37 | $array['fields'][$key]['fields'][$key2] = $this->setExtends($item, $key2); 38 | } 39 | } 40 | } 41 | } 42 | return $array; 43 | } 44 | 45 | function setExtends($part, $key) { 46 | $buffer = $part; unset($buffer['extends']); 47 | return array_merge($this->cache($part['extends']), $buffer); 48 | } 49 | 50 | function setDefinition($part, $key) { 51 | return $this->cache($part); 52 | } 53 | 54 | function cache($name) { 55 | $Cache = new Cache(); 56 | $cache_key = 'blueprint.reader.definitions'; 57 | 58 | if($Cache->get($name, $cache_key)) { 59 | $data = $Cache->get($name, $cache_key); 60 | 61 | } else { 62 | $data = $this->find($name); 63 | $Cache->set($name, $data, $cache_key); 64 | } 65 | return $data; 66 | } 67 | 68 | function find($field) { 69 | $definition = blueprint('fields/' . $field); 70 | if(isset($definition)) { 71 | return yaml::read($definition); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/language.php: -------------------------------------------------------------------------------- 1 | array = $array; 7 | if(isset($language)) { 8 | global $kirby; 9 | $this->kirby = $kirby; 10 | $this->language = $language; 11 | 12 | $this->walkField(); 13 | } 14 | return $this->array; 15 | } 16 | 17 | function walkField() { 18 | if(is_array($this->array) && array_key_exists('fields', $this->array)) { 19 | foreach($this->array['fields'] as $this->field_key => $field) { 20 | if(is_array($field)) { 21 | $this->depth = 'field'; 22 | $this->setFields(); 23 | $this->walkStructure($field, $this->field_key); 24 | } 25 | } 26 | } 27 | } 28 | 29 | function walkStructure($field) { 30 | if(array_key_exists('fields', $field)) { 31 | if(!empty($field['fields'])) { 32 | foreach($field['fields'] as $this->structure_key => $item) { 33 | if(is_array($item)) { 34 | $this->depth = 'structure'; 35 | $this->setFields(); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | function setFields() { 43 | foreach(array('label', 'help', 'placeholder') as $key) { 44 | $this->singular($key); 45 | } 46 | $this->headlines(); 47 | $this->options(); 48 | } 49 | 50 | function singular($type) { 51 | $part = $this->getPart(); 52 | 53 | if(isset($part[$type][$this->language])) { 54 | $part[$type] = $part[$type][$this->language]; 55 | } elseif(isset($part[$type]) && is_array($part[$type])) { 56 | $part[$type] = null; 57 | } 58 | 59 | $this->setPart($part); 60 | } 61 | 62 | function getPart() { 63 | $part = $this->array['fields'][$this->field_key]; 64 | if($this->depth == 'structure') { 65 | $part = $part['fields'][$this->structure_key]; 66 | } 67 | return $part; 68 | } 69 | 70 | function setPart($part) { 71 | if($this->depth == 'field') { 72 | $this->array['fields'][$this->field_key] = $part; 73 | } elseif($this->depth == 'structure') { 74 | $this->array['fields'][$this->field_key]['fields'][$this->structure_key] = $part; 75 | } 76 | } 77 | 78 | function options() { 79 | $part = $this->getPart(); 80 | if(isset($part['options']) && is_array($part['options'])) { 81 | if(!empty($part['options'])) { 82 | foreach($part['options'] as $key => $option) { 83 | if(array_key_exists($this->language, $option)) { 84 | $part['options'][$key] = $option[$this->language]; 85 | } else { 86 | $part['options'][$key] = null; 87 | } 88 | } 89 | } 90 | $this->setPart($part); 91 | } 92 | } 93 | 94 | function headlines() { 95 | $part = $this->getPart(); 96 | if(isset($part['type']) && $part['type'] == 'headline') { 97 | if(isset($part['text'][$this->language])) { 98 | $part['text'] = $part['text'][$this->language]; 99 | } elseif(isset($part['text']) && is_array($part['text'])) { 100 | $part['text'] = null; 101 | } 102 | $this->setPart($part); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/method-blueprint.php: -------------------------------------------------------------------------------- 1 | Cache = new Cache(); 8 | $this->Read = new Read(); 9 | } 10 | function get($options, $steps_options, $steps) { 11 | $options = $this->setArgs($options); 12 | extract($options); 13 | 14 | $cache_key = 'blueprint.reader.' . $format . 's'; 15 | 16 | $cache = $this->Cache->get($template, $cache_key); 17 | 18 | if($cache) { 19 | return $cache; 20 | } else { 21 | $filepath = blueprint($template); 22 | 23 | if($filepath) { 24 | if($format == 'filepath') { 25 | $this->Cache->set($template, $filepath, $cache_key); 26 | return $filepath; 27 | } else { 28 | $options['filepath'] = $filepath; 29 | $data = $this->Read->file($options); 30 | if(is_string($steps_options)) { 31 | $data = steps($steps, $data); 32 | } 33 | $this->Cache->set($template, $data, $cache_key); 34 | return $data; 35 | } 36 | } 37 | } 38 | } 39 | 40 | function setArgs($options) { 41 | extract($options); 42 | $fallbacks = array( 43 | 'template' => $template, 44 | 'format' => fallback($options, 'format', 'data') 45 | ); 46 | return array_merge(fallbackOptions($options), $fallbacks); 47 | } 48 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/method-read.php: -------------------------------------------------------------------------------- 1 | Cache = new Cache(); 9 | $this->Read = new Read(); 10 | } 11 | function get($options, $steps_options, $steps) { 12 | $options = $this->setArgs($options); 13 | extract($options); 14 | 15 | $cache_key = 'blueprint.reader.data'; 16 | $cache = $this->Cache->get($template, $cache_key); 17 | 18 | if($cache) { 19 | $data = $cache; 20 | } else { 21 | if(f::exists($filepath)) { 22 | $data = $this->Read->file($options); 23 | $this->Cache->set($template, $data, $cache_key); 24 | } 25 | } 26 | if(is_string($steps_options)) { 27 | $data = steps($steps, $data); 28 | } 29 | return $data; 30 | } 31 | 32 | function setArgs($options) { 33 | extract($options); 34 | $fallbacks = array( 35 | 'template' => (isset($cache_key)) ? $cache_key : pathinfo($filepath, PATHINFO_FILENAME), 36 | 'filepath' => $filepath, 37 | ); 38 | return array_merge(fallbackOptions($options), $fallbacks); 39 | } 40 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/methods.php: -------------------------------------------------------------------------------- 1 | get('blueprint', $name); 16 | $filepath = (is_string($blueprint)) ? $blueprint : null; 17 | return $filepath; 18 | } 19 | 20 | function parse($data, $options) { 21 | $Language = new Language(); 22 | $Definitions = new Definitions(); 23 | 24 | extract($options); 25 | 26 | if($definitions) { 27 | $data = $Definitions->parse($data); 28 | } 29 | $data = $Language->parse($data, $language); 30 | return $data; 31 | } 32 | 33 | function fallbackOptions($options) { 34 | return array( 35 | 'language' => fallback($options, 'language', null), 36 | 'definitions' => fallback($options, 'definitions', true), 37 | ); 38 | } 39 | 40 | function steps($steps, $data) { 41 | foreach($steps as $step) { 42 | if(!isset($data[$step])) { 43 | $data = null; 44 | break; 45 | } 46 | $data = $data[$step]; 47 | } 48 | return $data; 49 | } -------------------------------------------------------------------------------- /blueprint-reader/lib/read.php: -------------------------------------------------------------------------------- 1 | kirby = $kirby; 9 | $this->Cache = new Cache(); 10 | $this->Language = new Language(); 11 | $this->Definitions = new Definitions(); 12 | } 13 | 14 | function file($options) { 15 | $array = $this->set($options); 16 | return $array; 17 | } 18 | 19 | function set($options) { 20 | $array = yaml::read($options['filepath']); 21 | 22 | $options['language'] = fallback($options, 'language', null); 23 | $options['definitions'] = fallback($options, 'definitions', true); 24 | 25 | $array = parse($array, $options); 26 | 27 | return $array; 28 | } 29 | } -------------------------------------------------------------------------------- /blueprint-reader/license.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jens Törnell 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. -------------------------------------------------------------------------------- /blueprint-reader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kirby-blueprint-reader", 3 | "description": "Blueprint Reader for Kirby CMS", 4 | "author": "Jens Törnell ", 5 | "version": "0.4", 6 | "type": "kirby-plugin", 7 | "license": "MIT" 8 | } -------------------------------------------------------------------------------- /blueprints/silence.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirby-deprecated-plugins/kirby-sync/462ca72c99ac09dcdfbb36eca2d0b7536fda3114/blueprints/silence.yml -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | **0.3** 4 | 5 | - Major rewrite. 6 | - Added option `plugin.sync.active`. 7 | - Removed option `plugin.sync.log`. 8 | - Removed option `plugin.sync.trigger.delete`. 9 | - Removed option `plugin.sync.modified`. 10 | - Updated option `plugin.sync.token` is now required by security reasons (`null` by default). 11 | - Replaced option `plugin.sync.blueprint.prefix` and `plugin.sync.content.prefix` with `plugin.sync.prefix`. 12 | - Added tests for `panel.page.create`, `panel.page.update`, `panel.page.move`, template change, blueprint and urls. 13 | - Less hook type dependent. I will try to figure out what to do. If a `panel.page.update` is triggered and the page does not exist, it will create it. If it already exists, it will make an update instead. 14 | - **Price dropped from 50 EUR to only 9 EUR for each domain**. There is also a licenses for multiple domains and multiple plugins. 15 | 16 | **0.2** 17 | 18 | - Delete hook implemented. 19 | - Delete hook is triggered if `plugin.sync.trigger.delete` is set to `true` (false by default). 20 | - Move hook implemented. When you rename the page on the hub, it will also be renamed on the nodes. 21 | - It does only include files when they are needed. 22 | 23 | **0.1** 24 | 25 | - Initial release -------------------------------------------------------------------------------- /docs/hub-nodes.md: -------------------------------------------------------------------------------- 1 | # Hub & Nodes 2 | 3 | With this plugin we work with a hub and nodes. 4 | 5 | ## Hub 6 | 7 | The hub is the domain where your original content is stored. 8 | 9 | Also see how to [setup the hub](hub.md). 10 | 11 | ## Node 12 | 13 | The nodes are the domains where your content is copied to. 14 | 15 | ```text 16 | hub 17 | ├─ node 18 | └─ node 19 | ``` 20 | 21 | Also see how to [setup a node](node.md). 22 | 23 | # Options - Required 24 | 25 | *These options needs to be set on both the hub and the nodes in order to work.* 26 | 27 | ```php 28 | c::set('plugin.sync.token', null); 29 | ``` 30 | 31 | ### plugin.sync.token 32 | 33 | To prevent other people to use your sync API, you need to protect it by a token (string) of your choice. You need to use the same token on both the hub and the nodes in order to make the handshake. 34 | 35 | # Options - Optional 36 | 37 | All of these options are optional. 38 | 39 | In your `site/config/config.php`: 40 | 41 | ```php 42 | c::set('plugin.sync.slug', 'sync'); 43 | c::set('plugin.sync.active', true); 44 | ``` 45 | 46 | ### plugin.sync.slug 47 | 48 | The slug of the sync API. You only need to use this config if you suspect a collision with `yourdomain.com/sync`. It needs to be set to the same value on both the hub and the nodes in order to work. 49 | 50 | ### plugin.sync.active 51 | 52 | The plugin will run by default, but by setting this value to `false` the plugin will be disabled. You can disable the hub, the nodes or both. -------------------------------------------------------------------------------- /docs/hub.md: -------------------------------------------------------------------------------- 1 | # Hub 2 | 3 | For information about the hub, read [hub and nodes](hub-nodes.md). 4 | 5 | ## Options - Required 6 | 7 | These options are required in order for the plugin to work. 8 | 9 | ```php 10 | c::set('plugin.sync.domains'); 11 | ``` 12 | 13 | ### plugin.sync.domains 14 | 15 | To be able to sync the content you need to setup some domains. You also need to setup the parents that are allowed to be synced. 16 | 17 | **Example** 18 | 19 | In the example below `projects/project-a` will be sent to `example.com` when it's saved on the hub domain. 20 | 21 | ```php 22 | c::set('plugin.sync.domains', [ 23 | 'https://example.com' => [ 24 | 'projects/project-a', 25 | ], 26 | 'https://anotherdomain.com' => [ 27 | 'projects', 28 | ] 29 | ]); 30 | ``` 31 | 32 | `projects/project-a` will match `projects`, because `projects` is a parent of `projects/project-a`. -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | Use one of the alternatives below. 4 | 5 | ### 1. Kirby CLI 6 | 7 | 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: 8 | 9 | ```text 10 | $ cd path/to/kirby 11 | $ kirby plugin:install jenstornell/kirby-sync 12 | ``` 13 | 14 | ### 2. Clone or download 15 | 16 | 1. [Clone](https://github.com/jenstornell/kirby-sync.git) or [download](https://github.com/jenstornell/kirby-sync/archive/master.zip) this repository. 17 | 2. Unzip the archive if needed and rename the folder to `kirby-sync`. 18 | 19 | **Make sure that the plugin folder structure looks like this:** 20 | 21 | ```text 22 | site/plugins/kirby-sync/ 23 | ``` 24 | 25 | ### 3. Git Submodule 26 | 27 | If you know your way around Git, you can download this plugin as a submodule: 28 | 29 | ```text 30 | $ cd path/to/kirby 31 | $ git submodule add https://github.com/jenstornell/kirby-sync site/plugins/kirby-sync 32 | ``` -------------------------------------------------------------------------------- /docs/license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | ## Disclaimer 4 | 5 | This plugin is provided "as is" with no guarantee. Use it at your own risk and always test it yourself before using it in a production environment. If you find any issues, please [create a new issue](https://github.com/jenstornell/kirby-sync/issues/new). 6 | 7 | ## Try it out first 8 | 9 | Try it out on a development environment before you buy. You don't need to pay before you are happy with the plugin. 10 | 11 | ## When you need a license 12 | 13 | When you are using it for a live project, you need to pay a license for it. You need one license for each hub domain. 14 | 15 | ## When you don't need a license 16 | 17 | - Test environments for trying the plugin 18 | - Development and staging enviroments, which are not ment for public use 19 | - Backups 20 | 21 | ## Support 22 | 23 | No support are included in the license. However, I try to always be helpful. You can [create a new issue](https://github.com/jenstornell/kirby-sync/issues/new) if you get into some kind of trouble. 24 | 25 | ## Refunds 26 | 27 | Refunds are not supported. 28 | 29 | ## License code 30 | 31 | You will not get a license code for this plugin, at least not with this version. 32 | 33 | ## Purchase 34 | 35 | Be sure to try before you buy. Refunds are not supported. Read more in the [license agreement](docs/license.md). 36 | 37 | ***The purchase button is temporary disabled*** 38 | 39 | 61 | 62 | ## Donate 63 | 64 | If you want to make a donation in addition to the purchase, you can do that by sending any amount https://www.paypal.me/DevoneraAB -------------------------------------------------------------------------------- /docs/node.md: -------------------------------------------------------------------------------- 1 | # Node 2 | 3 | For information about the node, read [hub and nodes](hub-nodes.md). 4 | 5 | ## Options - Optional 6 | 7 | ```php 8 | c::set('plugin.sync.blueprint', false); 9 | c::set('plugin.sync.prefix', 'synced-'); 10 | c::set('plugin.sync.parent', 'synced-data'); 11 | c::set('plugin.sync.parent.blueprint', 'silence'); 12 | ``` 13 | 14 | ### plugin.sync.blueprint 15 | 16 | By default your blueprints are not synced. To sync your blueprints, you need to set this option to `true`. 17 | 18 | **Be aware:** Even if you sync the blueprints, this plugin does not [register/set](https://getkirby.com/docs/developer-guide/plugins/registry) them up for you. However, if you don't set a path, they will be placed in the `blueprints` folder and Kirby will find them there. 19 | 20 | ### plugin.sync.prefix 21 | 22 | Prefix is used by both the content text file and the blueprint file. That's because they should match. By default it will fallback to `synced-`. 23 | 24 | **Be aware:** If you remove the prefix there is a risk that your blueprints will be overwritten if they use the same name. 25 | 26 | ### plugin.sync.parent 27 | 28 | To prevent page collisions on your hub, you can add a parent to your data. By default `synced-data` is added as a blueprint parent. 29 | 30 | ```text 31 | example-hub.com/my/page > example-node.com/synced-data/my/page 32 | ``` 33 | 34 | ### plugin.sync.parent.blueprint 35 | 36 | By default `silence` is the name for the blueprint that is registered for the parent pages. 37 | 38 | When browsing in the Panel it will just display an empty page. 39 | 40 | # Site paths 41 | 42 | In order to change the paths you need to add `site.php` into your Kirby root. 43 | 44 | ### sync_blueprints 45 | 46 | This option is only needed if you have enabled `plugin.sync.blueprint`. 47 | 48 | By default the `site/blueprints` folder will be used. 49 | 50 | ```php 51 | $kirby = kirby(); 52 | $kirby->roots->sync_blueprints = $kirby->roots()->blueprints(); 53 | ``` -------------------------------------------------------------------------------- /docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ### Use the latest versions 4 | 5 | Make sure you are using at least PHP 7 and Kirby 2.5.5. Also make sure you have the latest version of this plugin. 6 | 7 | ### Make sure the routes work 8 | 9 | If your site prevents plugin routes to run, this plugin will not work. To test it, try the sync read API on both the hub and the nodes. 10 | 11 | **Example** 12 | 13 | ```text 14 | https://example.com/sync/read/projects/project-a?token=token 15 | ``` 16 | 17 | ### Make sure your domains are setup correctly 18 | 19 | On the hub, you need to have a config to call the nodes when updating a page. Make sure the domains are correct. Also make sure you create/update a page that exists within one of the parents listed. 20 | 21 | ```php 22 | c::set('plugin.sync.domains', [ 23 | 'https://example.com' => [ 24 | 'projects/project-a', 25 | ], 26 | 'https://anotherdomain.com' => [ 27 | 'projects', 28 | ] 29 | ]); 30 | ``` 31 | 32 | ### Make sure you save on the hub, not on the node 33 | 34 | It's easy to think the wrong way. Make sure to save the data on the hub and not on the node. -------------------------------------------------------------------------------- /hub/hooks.php: -------------------------------------------------------------------------------- 1 | hook('panel.page.*', function($page, $old_page = null) { 8 | $Urls_class = new Urls(); 9 | $urls = $Urls_class->hook($this->type(), $page, $old_page); 10 | foreach($urls as $url) { 11 | $Fetch = new Fetch(); 12 | $Fetch->visit($url); 13 | } 14 | }); -------------------------------------------------------------------------------- /hub/routes.php: -------------------------------------------------------------------------------- 1 | routes(array( 7 | [ 8 | 'pattern' => settings::slug() . '-test', 9 | 'action' => function($name = null) { 10 | $content = tpl::load(__DIR__ . DS . 'tests' . DS . 'urls.php', true); 11 | return new Response($content, 'json', 200); 12 | } 13 | ], 14 | [ 15 | 'pattern' => settings::slug() . '-test/(:any)', 16 | 'action' => function($name) { 17 | require_once __DIR__ . DS . 'tests' . DS . 'read.php'; 18 | 19 | switch($name) { 20 | case 'read': 21 | $TestRead = new TestRead(); 22 | $content = $TestRead->run(); 23 | break; 24 | case 'urls': 25 | $content = tpl::load(__DIR__ . DS . 'tests' . DS . 'urls.php', true); 26 | break; 27 | } 28 | return new Response($content, 'json', 200); 29 | } 30 | ], 31 | [ 32 | 'pattern' => settings::slug() . '/read/(:all)', 33 | 'action' => function($id) { 34 | $Read = new Read(); 35 | return $Read->data($id); 36 | } 37 | ], 38 | )); -------------------------------------------------------------------------------- /hub/tasks/read.php: -------------------------------------------------------------------------------- 1 | $page->intendedTemplate(), 19 | 'filename' => pathinfo($page->textfile(), PATHINFO_FILENAME), 20 | 'content' => $page->content()->toArray(), 21 | 'blueprint' => b::blueprint($page->intendedTemplate()) 22 | ]; 23 | } 24 | $parent_uid = $root . $page_id; 25 | } 26 | if(isset($array)) { 27 | $json = json_encode($array); 28 | return new Response($json, 'json', 200); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /hub/tasks/urls.php: -------------------------------------------------------------------------------- 1 | trigger($type, $page, $old_page); 11 | } 12 | 13 | // Trigger 14 | function trigger($type, $page, $old_page = null) { 15 | if(settings::domains()) { 16 | $urls = []; 17 | foreach(settings::domains() as $domain => $parents) { 18 | foreach($parents as $parent) { 19 | $old_page_id = ($old_page) ? $old_page->id() : null; 20 | if($page && $this->match($parent, $page->id(), $old_page_id)) { 21 | $urls[] = $this->{$type}($domain, $page->id(), $old_page_id); 22 | } 23 | } 24 | } 25 | return $urls; 26 | } 27 | } 28 | 29 | // Match 30 | function match($parent, $page_id, $old_page_id) { 31 | if(str::startsWith($page_id, $parent)) { 32 | return true; 33 | } 34 | if($old_page_id && str::startsWith($old_page_id, $parent)){ 35 | return true; 36 | } 37 | return false; 38 | } 39 | 40 | // Root 41 | function root($domain) { 42 | return $domain . '/' . settings::slug() . '/'; 43 | } 44 | 45 | // Token 46 | function token() { 47 | return '?token=' . settings::token(); 48 | } 49 | 50 | // Hub 51 | function hub() { 52 | return '&hub=' . urlencode(u()); 53 | } 54 | 55 | function newName($page_id) { 56 | $split = str::split($page_id, '/'); 57 | $new_page_slug = end($split); 58 | return ($page_id) ? '&new_name=' . $new_page_slug : ''; 59 | } 60 | 61 | // Save is update, create and move 62 | function save($domain, $page_id, $old_page_id = null) { 63 | $u = $this->root($domain); 64 | $u .= 'save/' . $page_id; 65 | $u .= $this->token(); 66 | $u .= $this->hub(); 67 | return $u; 68 | } 69 | 70 | // Update url 71 | function hookUpdate($domain, $page_id) { 72 | return $this->save($domain, $page_id); 73 | } 74 | 75 | // Create url, inherit save url 76 | function hookCreate($domain, $page_id) { 77 | return $this->save($domain, $page_id, null); 78 | } 79 | 80 | // Move url, inherit save url 81 | function hookMove($domain, $page_id, $old_page_id) { 82 | $u = $this->root($domain); 83 | $u .= 'save/' . $old_page_id; 84 | $u .= $this->token(); 85 | $u .= $this->hub(); 86 | $u .= $this->newName($page_id); 87 | return $u; 88 | } 89 | 90 | // Delete url 91 | function hookDelete($domain, $page_id) { 92 | $u = $this->root($domain); 93 | $u .= 'delete/' . $page_id; 94 | $u .= $this->token(); 95 | return $u; 96 | } 97 | } -------------------------------------------------------------------------------- /hub/tests/read.php: -------------------------------------------------------------------------------- 1 | Read = new Read(); 8 | } 9 | function run() { 10 | $this->status = ['success' => true]; 11 | $this->refresh(); 12 | $this->createBlueprint(); 13 | $this->testBlueprint(); 14 | $this->testRefresh(); 15 | $this->createPages(); 16 | $this->testRead(); 17 | 18 | return $this->status; 19 | } 20 | 21 | function createPages() { 22 | $parent_object = $this->getParentObject(); 23 | $parent_object->children()->create('level1', 'level', [ 24 | 'title' => 'Level1' 25 | ]); 26 | 27 | $parent_object = $this->getParentObject('level1'); 28 | $parent_object->children()->create('level2', 'level', [ 29 | 'title' => 'Level2' 30 | ]); 31 | 32 | $parent_object = $this->getParentObject('level1/level2'); 33 | $new_page = $parent_object->children()->create('level3', 'level', [ 34 | 'title' => 'Level3' 35 | ]); 36 | } 37 | 38 | function getParentObject($uri = '') { 39 | if(!empty($uri)) 40 | return site()->find($uri); 41 | else 42 | return site()->pages(); 43 | } 44 | 45 | function refresh() { 46 | $page = page('level1'); 47 | if($page) { 48 | $page->delete(true); 49 | } 50 | } 51 | 52 | function createBlueprint() { 53 | yaml::write(kirby()->roots()->blueprints() . DS . 'level.yml', [ 54 | 'title' => 'Level' 55 | ]); 56 | } 57 | 58 | function testBlueprint() { 59 | if(!file_exists(kirby()->roots()->blueprints() . DS . 'level.yml')) { 60 | $this->status = [ 61 | 'success' => false, 62 | 'message' => 'Blueprint does not exists' 63 | ]; 64 | } 65 | } 66 | 67 | function testRefresh() { 68 | if(page('level1')) { 69 | $this->status = [ 70 | 'success' => false, 71 | 'message' => 'Refresh did not work' 72 | ]; 73 | } 74 | } 75 | 76 | function testCreatePages() { 77 | if(!page('level1/level2/level3')) { 78 | $this->status = [ 79 | 'success' => false, 80 | 'message' => 'Pages was not created correctly' 81 | ]; 82 | } 83 | } 84 | 85 | function testRead() { 86 | $data = json_decode($this->Read->data('level1/level2'), true); 87 | 88 | if(array_key_exists('level3', $data)) { 89 | $this->status = [ 90 | 'success' => false, 91 | 'message' => 'Level3 should not exist' 92 | ]; 93 | } 94 | if(!array_key_exists('level1', $data)) { 95 | $this->status = [ 96 | 'success' => false, 97 | 'message' => 'Level1 is missing' 98 | ]; 99 | } 100 | if(!array_key_exists('level1/level2', $data)) { 101 | $this->status = [ 102 | 'success' => false, 103 | 'message' => 'Level2 is missing' 104 | ]; 105 | } 106 | if($data['level1/level2']['template'] != 'level') { 107 | $this->status = [ 108 | 'success' => false, 109 | 'message' => 'Template is missing' 110 | ]; 111 | } 112 | if($data['level1/level2']['filename'] != 'level') { 113 | $this->status = [ 114 | 'success' => false, 115 | 'message' => 'Filename is missing' 116 | ]; 117 | } 118 | if(empty($data['level1/level2']['content'])) { 119 | $this->status = [ 120 | 'success' => false, 121 | 'message' => 'Content is missing' 122 | ]; 123 | } 124 | if(empty($data['level1/level2']['blueprint'])) { 125 | $this->status = [ 126 | 'success' => false, 127 | 'message' => 'Blueprint is missing' 128 | ]; 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /hub/tests/urls.php: -------------------------------------------------------------------------------- 1 | [ 11 | 'test' => $Urls->hook('page.panel.create', page('projects/project-a')), 12 | 'match' => $domain . '/sync/save/projects/project-a?token=token&hub=' . $hub 13 | ], 14 | 'update' => [ 15 | 'test' => $Urls->hook('page.panel.update', page('projects/project-a'), page('projects/project-a')), 16 | 'match' => $domain . '/sync/save/projects/project-a?token=token&hub=' . $hub 17 | ], 18 | 'move' => [ 19 | 'test' => $Urls->hook('page.panel.move', page('projects/project-b'), page('projects/project-a')), 20 | 'match' => $domain . '/sync/save/projects/project-a?token=token&hub=' . $hub . '&new_name=project-b' 21 | ], 22 | 'delete' => [ 23 | 'test' => $Urls->hook('page.panel.delete', page('projects/project-a')), 24 | 'match' => $domain . '/sync/delete/projects/project-a?token=token' 25 | ] 26 | ]; 27 | 28 | $result = ['success' => true]; 29 | 30 | foreach($data as $type => $items) { 31 | if($items['match'] != $items['test'][0]) { 32 | $result = [ 33 | 'success' => false, 34 | 'Message' => 'Url ' . $type . ' . is wrong' 35 | ]; 36 | } 37 | } 38 | 39 | $unallowed = $Urls->hook('page.panel.update', page('unallowed'), page('unallowed')); 40 | 41 | if(!empty($unallowed)) { 42 | $result = [ 43 | 'success' => false, 44 | 'Message' => 'The page unallowed should not be available' 45 | ]; 46 | } 47 | 48 | echo json_encode($result); -------------------------------------------------------------------------------- /kirby-sync.php: -------------------------------------------------------------------------------- 1 | roots()->plugins() . DS . 'kirby-blueprint-reader')) { 19 | require_once __DIR__ . DS. 'blueprint-reader' . DS . 'kirby-blueprint-reader.php'; 20 | } 21 | 22 | if(settings::domains()) { 23 | require_once __DIR__ . DS . 'hub' . DS . 'tasks' . DS . 'read.php'; 24 | require_once __DIR__ . DS . 'hub' . DS . 'routes.php'; 25 | } else { 26 | require_once __DIR__ . DS . 'node' . DS . 'routes.php'; 27 | require_once __DIR__ . DS . 'node' . DS . 'tasks.php'; 28 | } 29 | } 30 | 31 | if(!settings::domains()) { 32 | $path = __DIR__ . DS . 'blueprints' . DS . 'silence.yml'; 33 | $kirby->set('blueprint', settings::prefix() . settings::parentBlueprint(), $path); 34 | } 35 | } -------------------------------------------------------------------------------- /node/config.php: -------------------------------------------------------------------------------- 1 | roots(); 3 | 4 | kirby()->set('option', 'plugin.sync.settings.custom', [ 5 | 'parent' => 'synced-data', 6 | 'parent.blueprint' => 'silence', 7 | 'blueprint' => false, 8 | 'blueprint.root' => ($roots->sync_blueprints()) ? $roots->sync_blueprints() : $roots->blueprints(), 9 | 'prefix' => 'synced-' 10 | ]); -------------------------------------------------------------------------------- /node/routes.php: -------------------------------------------------------------------------------- 1 | routes(array( 9 | [ 10 | 'pattern' => settings::slug() . '/save/(:all)', 11 | 'action' => function($id) { 12 | $Tasks = new Tasks(); 13 | // http://localhost/plugins/node/sync/save/level1/level2/level3?token=token&hub=http%3A%2F%2Flocalhost%2Fplugins%2Fsync&new_name=hahaha 14 | $url = urldecode(get('hub')) . '/'; 15 | $url .= settings::slug() . '/read/'; 16 | $url .= $id . '?token=' . settings::token(); 17 | 18 | $Fetch = new Fetch(); 19 | $read = json_decode($Fetch->visit($url), true); 20 | $Tasks->run($id, $read, get('new_name')); 21 | } 22 | ], 23 | [ 24 | 'pattern' => settings::slug() . '/delete/(:all)', 25 | 'action' => function($id) { 26 | $TaskDelete = new TaskDelete(); 27 | // http://localhost/plugins/node/sync/delete/projects?token=token 28 | $url = trim(settings::parent() . '/' . $id, '/'); 29 | $TaskDelete->delete($url); 30 | } 31 | ], 32 | [ 33 | 'pattern' => settings::slug() . '-test', 34 | 'action' => function() { 35 | require_once __DIR__ . DS . 'tests' . DS . 'save-create.php'; 36 | require_once __DIR__ . DS . 'tests' . DS . 'save-update.php'; 37 | require_once __DIR__ . DS . 'tests' . DS . 'save-filename.php'; 38 | 39 | $TestSaveCreate = new TestSaveCreate(); 40 | $TestSaveUpdate = new TestSaveUpdate(); 41 | $TestSaveFilename = new TestSaveFilename(); 42 | 43 | $create = json_decode($TestSaveCreate->run(), true); 44 | $update = json_decode($TestSaveUpdate->run(), true); 45 | $filename = json_decode($TestSaveFilename->run(), true); 46 | 47 | if(!$create['success']) 48 | return new Response(json_encode($create), 'json', 200); 49 | if(!$update['success']) 50 | return new Response(json_encode($update), 'json', 200); 51 | if(!$filename['success']) 52 | return new Response(json_encode($filename), 'json', 200); 53 | 54 | return new Response(json_encode(['success' => true]), 'json', 200); 55 | } 56 | ], 57 | [ 58 | 'pattern' => settings::slug() . '-test/(:any)/(:any)', 59 | 'action' => function($hook, $name) { 60 | require_once __DIR__ . DS . 'tests' . DS . 'save-create.php'; 61 | require_once __DIR__ . DS . 'tests' . DS . 'save-update.php'; 62 | require_once __DIR__ . DS . 'tests' . DS . 'save-filename.php'; 63 | require_once __DIR__ . DS . 'tests' . DS . 'save-move.php'; 64 | 65 | if($hook == 'save') { 66 | switch($name) { 67 | case 'create': 68 | $TestSaveCreate = new TestSaveCreate(); 69 | $content = $TestSaveCreate->run(); 70 | break; 71 | case 'update': 72 | $TestSaveUpdate = new TestSaveUpdate(); 73 | $content = $TestSaveUpdate->run(); 74 | break; 75 | case 'filename': 76 | $TestSaveFilename = new TestSaveFilename(); 77 | $content = $TestSaveFilename->run(); 78 | break; 79 | case 'move': 80 | $TestSaveMove = new TestSaveMove(); 81 | $content = $TestSaveMove->run(); 82 | break; 83 | } 84 | } 85 | return new Response($content, 'json', 200); 86 | } 87 | ], 88 | [ 89 | 'pattern' => settings::slug() . '-test/(:any)', 90 | 'action' => function($name) { 91 | require_once __DIR__ . DS . 'tests' . DS . 'save-create.php'; 92 | require_once __DIR__ . DS . 'tests' . DS . 'delete.php'; 93 | 94 | $TestSaveDelete = new TestSaveDelete(); 95 | $content = $TestSaveDelete->run(); 96 | 97 | return new Response($content, 'json', 200); 98 | } 99 | ] 100 | )); -------------------------------------------------------------------------------- /node/tasks.php: -------------------------------------------------------------------------------- 1 | TaskCreate = new TaskCreate(); 16 | $this->TaskUpdate = new TaskUpdate(); 17 | $this->TaskFilename = new TaskFilename(); 18 | $this->TaskBlueprint = new TaskBlueprint(); 19 | $this->TaskMove = new TaskMove(); 20 | } 21 | 22 | // Run 23 | function run($id, $data, $new_name = null) { 24 | $tasklist = $this->tasklist($id, $data); 25 | $this->execute($tasklist, $new_name); 26 | } 27 | 28 | // Tasklist 29 | function tasklist($id, $data_pages) { 30 | $uid = ''; 31 | foreach(str::split($id, '/') as $id) { 32 | $uid .= $id . '/'; 33 | $page_id = trim($uid, '/'); 34 | $page_id_full = trim(settings::parent() . '/' . $page_id); 35 | 36 | $data = $data_pages[$page_id]; 37 | $data_extra = [ 38 | 'filename_node' => $this->filenameNode($page_id_full, $data['filename']), 39 | 'current' => $this->pageUid($page_id), 40 | 'parents' => $this->parentId($page_id_full), 41 | 'is_page' => true, 42 | ]; 43 | 44 | $tasks[$page_id_full] = [ 45 | 'page_exists' => $this->pageExists($page_id_full), 46 | 'data' => array_merge($data, $data_extra), 47 | ]; 48 | } 49 | $parents = $this->parents(); 50 | $tasks = array_merge($parents, $tasks); 51 | return $tasks; 52 | } 53 | 54 | // Parents 55 | function parents() { 56 | $parent_uid = ''; 57 | $uid = ''; 58 | $parents = []; 59 | foreach(str::split(settings::parent(), '/') as $id) { 60 | $uid .= $id . '/'; 61 | $page_id = trim($uid, '/'); 62 | $data = [ 63 | 'template' => settings::parentBlueprint(), 64 | 'filename' => settings::parentBlueprint(), 65 | 'content' => [], 66 | 'blueprint' => [], 67 | 'filename_node' => $this->filenameNode($page_id, settings::parentBlueprint()), 68 | 'current' => $this->pageUid($page_id), 69 | 'parents' => $this->parentId($page_id), 70 | 'is_page' => false, 71 | ]; 72 | $parents[$page_id] = [ 73 | 'page_exists' => $this->pageExists($page_id), 74 | 'data' => $data 75 | ]; 76 | } 77 | return $parents; 78 | } 79 | 80 | // Execute tasklist 81 | function execute($tasklist, $new_name) { 82 | foreach($tasklist as $id => $task) { 83 | if(!$task['page_exists']) { 84 | $this->TaskCreate->page($id, $task['data']); 85 | } else { 86 | if($task['data']['is_page']) { 87 | if(settings::prefix() . $task['data']['filename'] == $task['data']['filename_node']) { 88 | $this->TaskUpdate->page($id, $task['data']); 89 | } else { 90 | $this->TaskFilename->rename($id, $task['data']); 91 | } 92 | } 93 | } 94 | $saved_blueprints = $this->saveBlueprint($id, $task, $saved_blueprints = []); 95 | } 96 | 97 | $last = end($tasklist); 98 | if($new_name) { 99 | $this->TaskMove->page($id, $last['data'], $new_name); 100 | } 101 | } 102 | 103 | // Save blueprint 104 | function saveBlueprint($id, $task, $saved_blueprints) { 105 | if(settings::blueprint()) { 106 | if($task['data']['is_page']) { 107 | if(!in_array($task['data']['template'], $saved_blueprints)) { 108 | $this->TaskBlueprint->write($task['data']); 109 | $saved_blueprints[] = $task['data']['template']; 110 | } 111 | } 112 | } 113 | return $saved_blueprints; 114 | } 115 | 116 | // Filename node 117 | function filenameNode($page_id, $filename) { 118 | $page = page($page_id); 119 | if($page) { 120 | $filename = pathinfo($page->textfile(), PATHINFO_FILENAME); 121 | } 122 | return $filename; 123 | } 124 | 125 | // Get page uid 126 | function pageUid($page_id) { 127 | $split = str::split($page_id, '/'); 128 | return end($split); 129 | } 130 | 131 | // Get parents 132 | function parentId($uid) { 133 | $uris = str::split($uid, '/'); 134 | array_pop($uris); 135 | return implode($uris, '/'); 136 | } 137 | 138 | // Check if page exists 139 | function pageExists($uid) { 140 | if(page($uid)) return true; 141 | return false; 142 | } 143 | } -------------------------------------------------------------------------------- /node/tasks/blueprint.php: -------------------------------------------------------------------------------- 1 | path($data['template']); 10 | $yaml = yaml::encode($data['blueprint']); 11 | return f::write($path, $yaml); 12 | } 13 | 14 | // Blueprint path 15 | function path($template) { 16 | return settings::blueprintRoot() . DS . settings::prefix() . $template . '.yml'; 17 | } 18 | } -------------------------------------------------------------------------------- /node/tasks/create.php: -------------------------------------------------------------------------------- 1 | getParentObject($data['parents']); 8 | $this->create($id, $parent_obj, $data); 9 | } 10 | 11 | function create($id, $parent_obj, $data) { 12 | if(!page($id)) { 13 | return $parent_obj->children()->create($data['current'], settings::prefix() . $data['template'], $data['content']); 14 | } 15 | return true; 16 | } 17 | 18 | function getParentObject($uri) { 19 | if(!empty($uri)) 20 | return site()->find($uri); 21 | else 22 | return site()->pages(); 23 | } 24 | } -------------------------------------------------------------------------------- /node/tasks/delete.php: -------------------------------------------------------------------------------- 1 | delete(true); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /node/tasks/filename.php: -------------------------------------------------------------------------------- 1 | kirby = kirby(); 7 | } 8 | 9 | function rename($id, $data) { 10 | $page = page($id); 11 | $root = $page->root() . DS; 12 | $extension = $this->kirby->option('content.file.extension'); 13 | 14 | $from = $root . $data['filename_node'] . '.' . $extension; 15 | $to = $root . settings::prefix() . $data['filename'] . '.' . $extension; 16 | 17 | if(file_exists($from)) { 18 | return rename($from, $to); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /node/tasks/move.php: -------------------------------------------------------------------------------- 1 | delete(true); 11 | } 12 | 13 | if($page) { 14 | $page->move($new_name); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /node/tasks/update.php: -------------------------------------------------------------------------------- 1 | kirby = kirby(); 8 | } 9 | function page($id, $data) { 10 | return page($id)->update($data['content']); 11 | } 12 | } -------------------------------------------------------------------------------- /node/tests/delete.php: -------------------------------------------------------------------------------- 1 | TaskDelete = new TaskDelete(); 8 | $this->Tasks = new Tasks(); 9 | $this->status = ['success' => true]; 10 | } 11 | 12 | function run() { 13 | $this->create(); 14 | $this->testCreate(); 15 | $this->refresh(); 16 | $this->testRefresh(); 17 | return json_encode($this->status); 18 | } 19 | 20 | function testRefresh() { 21 | if(page($this->parentRoot())) { 22 | $this->status['success'] = false; 23 | $this->status['message'] = 'Did not delete all pages.'; 24 | } 25 | } 26 | 27 | function refresh() { 28 | $this->TaskDelete->delete($this->parentRoot()); 29 | } 30 | } -------------------------------------------------------------------------------- /node/tests/save-create.php: -------------------------------------------------------------------------------- 1 | TaskDelete = new TaskDelete(); 8 | $this->Tasks = new Tasks(); 9 | $this->status = ['success' => true]; 10 | } 11 | 12 | function run() { 13 | $this->refresh(); 14 | $this->testRefresh(); 15 | $this->refreshBlueprint(); 16 | $this->testRefreshBlueprint(); 17 | $this->create(); 18 | $this->testCreate(); 19 | return json_encode($this->status); 20 | } 21 | 22 | function read() { 23 | $blueprint = [ 24 | 'title' => 'Create', 25 | 'fields' => [ 26 | 'title' => [ 27 | 'label' => 'Title', 28 | 'type' => 'text' 29 | ] 30 | ] 31 | ]; 32 | 33 | $data = [ 34 | 'about' => [ 35 | 'template' => 'about', 36 | 'filename' => 'about', 37 | 'content' => [ 38 | 'title' => 'About' 39 | ], 40 | 'blueprint' => $blueprint, 41 | ], 42 | 'create' => [ 43 | 'template' => 'create', 44 | 'filename' => 'create', 45 | 'content' => [ 46 | 'title' => 'Create' 47 | ], 48 | 'blueprint' => $blueprint, 49 | ], 50 | 'create/child' => [ 51 | 'template' => 'create', 52 | 'filename' => 'create', 53 | 'content' => [ 54 | 'title' => 'Create child' 55 | ], 56 | 'blueprint' => $blueprint, 57 | ], 58 | 'create/child/grandchild' => [ 59 | 'template' => 'create', 60 | 'filename' => 'create', 61 | 'content' => [ 62 | 'title' => 'Create grandchild' 63 | ], 64 | 'blueprint' => $blueprint, 65 | ] 66 | ]; 67 | 68 | return $data; 69 | } 70 | 71 | function testRefresh() { 72 | if(page($this->parentRoot())) { 73 | $this->status['success'] = false; 74 | $this->status['message'] = 'Did not delete all pages.'; 75 | } 76 | } 77 | 78 | function testCreate() { 79 | $split = str::split(settings::parent(), '/'); 80 | $split[] = 'create'; 81 | $split[] = 'child'; 82 | $split[] = 'grandchild'; 83 | 84 | $uid = ''; 85 | foreach($split as $item) { 86 | $uid .= $item . '/'; 87 | $page_id = trim($uid, '/'); 88 | $page = page($uid); 89 | if(!$page) { 90 | $this->status['success'] = false; 91 | $this->status['message'] = 'Could not create "' . $page_id . '".'; 92 | } 93 | $this->testBlueprint($page->intendedTemplate()); 94 | } 95 | } 96 | 97 | function testBlueprint($template) { 98 | $path = settings::blueprintRoot() . DS . $template . '.yml'; 99 | $match = settings::blueprintRoot() . DS . settings::prefix() . settings::parentBlueprint() . '.yml'; 100 | 101 | if($path != $match) { 102 | if(!file_exists($path)) { 103 | $this->status['success'] = false; 104 | $this->status['message'] = 'Could not create blueprint "' . $path . '".'; 105 | } 106 | } 107 | } 108 | 109 | function parentRoot() { 110 | $split = str::split(settings::parent(), '/'); 111 | return $split[0]; 112 | } 113 | 114 | function create() { 115 | $this->Tasks->run('create/child/grandchild', $this->read()); 116 | } 117 | 118 | function refresh() { 119 | $this->TaskDelete->delete($this->parentRoot()); 120 | } 121 | 122 | function refreshBlueprint() { 123 | $query = settings::blueprintRoot() . DS . '*'; 124 | foreach(glob($query) as $file) { 125 | unlink($file); 126 | } 127 | } 128 | 129 | function testRefreshBlueprint() { 130 | $query = settings::blueprintRoot() . DS . '*'; 131 | if(!empty(glob($query))) { 132 | $this->status['success'] = false; 133 | $this->status['message'] = 'Could not refresh blueprint.'; 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /node/tests/save-filename.php: -------------------------------------------------------------------------------- 1 | kirby = kirby(); 8 | $this->TaskDelete = new TaskDelete(); 9 | $this->Tasks = new Tasks(); 10 | $this->status = ['success' => true]; 11 | } 12 | 13 | function run() { 14 | $this->refresh(); 15 | $this->testRefresh(); 16 | $this->create(); 17 | $this->testCreate(); 18 | $this->update(); 19 | $this->testUpdate(); 20 | return json_encode($this->status); 21 | } 22 | 23 | function readUpdate() { 24 | $blueprint = [ 25 | 'title' => 'Create', 26 | 'fields' => [ 27 | 'title' => [ 28 | 'label' => 'Title', 29 | 'type' => 'text' 30 | ] 31 | ] 32 | ]; 33 | 34 | $data = [ 35 | 'about' => [ 36 | 'template' => 'about', 37 | 'filename' => 'about', 38 | 'content' => [ 39 | 'title' => 'About' 40 | ], 41 | 'blueprint' => $blueprint, 42 | ], 43 | 'create' => [ 44 | 'template' => 'filename', 45 | 'filename' => 'filename', 46 | 'content' => [ 47 | 'title' => 'Update' 48 | ], 49 | 'blueprint' => $blueprint, 50 | ], 51 | 'create/child' => [ 52 | 'template' => 'filename', 53 | 'filename' => 'filename', 54 | 'content' => [ 55 | 'title' => 'Update child' 56 | ], 57 | 'blueprint' => $blueprint, 58 | ], 59 | 'create/child/grandchild' => [ 60 | 'template' => 'filename', 61 | 'filename' => 'filename', 62 | 'content' => [ 63 | 'title' => 'Update grandchild' 64 | ], 65 | 'blueprint' => $blueprint, 66 | ] 67 | ]; 68 | 69 | return $data; 70 | } 71 | 72 | function testUpdate() { 73 | $root = kirby()->roots()->content() . DS . str_replace('/', DS, settings::parent()) . DS; 74 | $extension = $this->kirby->option('content.file.extension'); 75 | $filename = settings::prefix() . 'filename.' . $extension; 76 | $create = $root . 'create'; 77 | $child = $create . DS . 'child'; 78 | $grandchild = $child . DS . 'grandchild'; 79 | 80 | if(!file_exists($create) || !file_exists($child) || !file_exists($grandchild)) { 81 | $this->status['success'] = false; 82 | } 83 | 84 | if(!file_exists($create)) { 85 | $this->status['message'] = 'Could not change template to ' . $create . DS . $filename; 86 | } 87 | 88 | if(!file_exists($child)) { 89 | $this->status['message'] = 'Could not change template to ' . $child . DS . $filename; 90 | } 91 | 92 | if(!file_exists($grandchild)) { 93 | $this->status['message'] = 'Could not change template to ' . $grandchild . DS . $filename; 94 | } 95 | } 96 | 97 | function update() { 98 | $this->Tasks->run('create/child/grandchild', $this->readUpdate()); 99 | } 100 | } -------------------------------------------------------------------------------- /node/tests/save-move.php: -------------------------------------------------------------------------------- 1 | TaskDelete = new TaskDelete(); 8 | $this->Tasks = new Tasks(); 9 | $this->status = ['success' => true]; 10 | } 11 | 12 | function run() { 13 | $this->refresh(); 14 | $this->testRefresh(); 15 | $this->create(); 16 | $this->testCreate(); 17 | $this->update(); 18 | $this->testUpdate(); 19 | return json_encode($this->status); 20 | } 21 | 22 | function readUpdate() { 23 | $blueprint = [ 24 | 'title' => 'Create', 25 | 'fields' => [ 26 | 'title' => [ 27 | 'label' => 'Title', 28 | 'type' => 'text' 29 | ] 30 | ] 31 | ]; 32 | 33 | $data = [ 34 | 'about' => [ 35 | 'template' => 'about', 36 | 'filename' => 'about', 37 | 'content' => [ 38 | 'title' => 'About' 39 | ], 40 | 'blueprint' => $blueprint, 41 | ], 42 | 'create' => [ 43 | 'template' => 'create', 44 | 'filename' => 'create', 45 | 'content' => [ 46 | 'title' => 'Update' 47 | ], 48 | 'blueprint' => $blueprint, 49 | ], 50 | 'create/child' => [ 51 | 'template' => 'create', 52 | 'filename' => 'create', 53 | 'content' => [ 54 | 'title' => 'Update child' 55 | ], 56 | 'blueprint' => $blueprint, 57 | ], 58 | 'create/child/grandchild' => [ 59 | 'template' => 'create', 60 | 'filename' => 'create', 61 | 'content' => [ 62 | 'title' => 'Update grandchild' 63 | ], 64 | 'blueprint' => $blueprint, 65 | ] 66 | ]; 67 | 68 | return $data; 69 | } 70 | 71 | function testUpdate() { 72 | $old_id = settings::parent() . '/create/child/grandchild'; 73 | $new_id = settings::parent() . '/create/child/renamed'; 74 | 75 | if(page($old_id)) { 76 | $this->status['success'] = false; 77 | $this->status['message'] = 'Old page ' . $old_id . ' did not get moved'; 78 | } elseif(!page($new_id)) { 79 | $this->status['success'] = false; 80 | $this->status['message'] = 'New page ' . $new_id . ' missing'; 81 | } 82 | } 83 | 84 | function update() { 85 | $this->Tasks->run('create/child/grandchild', $this->readUpdate(), 'renamed'); 86 | } 87 | } -------------------------------------------------------------------------------- /node/tests/save-update.php: -------------------------------------------------------------------------------- 1 | TaskDelete = new TaskDelete(); 8 | $this->Tasks = new Tasks(); 9 | $this->status = ['success' => true]; 10 | } 11 | 12 | function run() { 13 | $this->refresh(); 14 | $this->testRefresh(); 15 | $this->create(); 16 | $this->testCreate(); 17 | $this->update(); 18 | $this->testUpdate(); 19 | return json_encode($this->status); 20 | } 21 | 22 | function readUpdate() { 23 | $blueprint = [ 24 | 'title' => 'Create', 25 | 'fields' => [ 26 | 'title' => [ 27 | 'label' => 'Title', 28 | 'type' => 'text' 29 | ] 30 | ] 31 | ]; 32 | 33 | $data = [ 34 | 'about' => [ 35 | 'template' => 'about', 36 | 'filename' => 'about', 37 | 'content' => [ 38 | 'title' => 'About' 39 | ], 40 | 'blueprint' => $blueprint, 41 | ], 42 | 'create' => [ 43 | 'template' => 'create', 44 | 'filename' => 'create', 45 | 'content' => [ 46 | 'title' => 'Update' 47 | ], 48 | 'blueprint' => $blueprint, 49 | ], 50 | 'create/child' => [ 51 | 'template' => 'create', 52 | 'filename' => 'create', 53 | 'content' => [ 54 | 'title' => 'Update child' 55 | ], 56 | 'blueprint' => $blueprint, 57 | ], 58 | 'create/child/grandchild' => [ 59 | 'template' => 'create', 60 | 'filename' => 'create', 61 | 'content' => [ 62 | 'title' => 'Update grandchild' 63 | ], 64 | 'blueprint' => $blueprint, 65 | ] 66 | ]; 67 | 68 | return $data; 69 | } 70 | 71 | function testUpdate() { 72 | $split = str::split(settings::parent(), '/'); 73 | $split[] = 'create'; 74 | $split[] = 'child'; 75 | $split[] = 'grandchild'; 76 | 77 | $uid = ''; 78 | foreach($split as $item) { 79 | $uid .= $item . '/'; 80 | $page_id = trim($uid, '/'); 81 | $page = page($uid); 82 | if($page->slug() == 'create' && $page->title() != 'Update') { 83 | $this->status['success'] = false; 84 | $this->status['message'] = 'Could not update "' . $page_id . '".'; 85 | } elseif($page->slug() == 'child' && $page->title() != 'Update child') { 86 | $this->status['success'] = false; 87 | $this->status['message'] = 'Could not update "' . $page_id . '".'; 88 | } elseif($page->slug() == 'grandchild' && $page->title() != 'Update grandchild') { 89 | $this->status['success'] = false; 90 | $this->status['message'] = 'Could not update "' . $page_id . '".'; 91 | } 92 | } 93 | } 94 | 95 | function update() { 96 | $this->Tasks->run('create/child/grandchild', $this->readUpdate()); 97 | } 98 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kirby-sync", 3 | "description": "Kirby Sync for Kirby CMS.", 4 | "author": "Jens Törnell ", 5 | "version": "0.3", 6 | "type": "kirby-plugin", 7 | "license": "Commercial" 8 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Kirby Sync 2 | 3 | [![Version 0.3](https://img.shields.io/badge/version-0.3-blue.svg)](https://github.com/jenstornell/kirby-sync/blob/master/docs/changelog.md) [![Commercial license](https://img.shields.io/badge/license-commercial-red.svg)](https://github.com/jenstornell/kirby-sync/blob/master/docs/license.md) [![Commercial license](https://img.shields.io/badge/price-€9-yellow.svg)](https://github.com/jenstornell/kirby-sync/blob/master/docs/license.md) 4 | 5 | Copy (called sync) your page on when saving the page in the panel, from a domain (called hub) > to other domains (called nodes). 6 | 7 | ```text 8 | hub (domain) 9 | ├─ node (domain) 10 | └─ node (domain) 11 | ``` 12 | 13 | ## Simple setup 14 | 15 | ### 1. Install the plugin 16 | 17 | [Install the plugin](docs/installation.md) on both the hub and the nodes. 18 | 19 | ### 2. Add domains 20 | 21 | In the hub `config.php`, add node domains and page parents that should be synced to the nodes. 22 | 23 | **Example** 24 | 25 | ```php 26 | c::set('plugin.sync.domains', [ 27 | 'https://example.com' => [ 28 | 'projects/project-a', 29 | ], 30 | 'https://anotherdomain.com' => [ 31 | 'projects' 32 | ] 33 | ]); 34 | ``` 35 | 36 | ### 3. Add a token on both hub and nodes 37 | 38 | A token is **required**. Use the same string on both the hub and the nodes. 39 | 40 | ```php 41 | c::set('plugin.sync.token', null); 42 | ``` 43 | 44 | There are much more options for the [hub and nodes](docs/hub-nodes.md) and for [nodes](docs/node.md). 45 | 46 | ## Usage 47 | 48 | 1. Login to the hub domain panel and create/update a page. 49 | 1. Login to the node domain to see if the change has been synced. 50 | 51 | ### Table of contents 52 | 53 | - [Installation](docs/installation.md) 54 | - [Hub & nodes](docs/hub-nodes.md) 55 | - [Hub](docs/hub.md) 56 | - [Node](docs/node.md) 57 | - [Troubleshooting](docs/troubleshooting.md) 58 | - [Changelog](docs/changelog.md) 59 | 60 | ## Good to know 61 | 62 | - This plugin is not yet tested on multi language sites. 63 | - You can sync sorted pages, but the sort number will not be synced because of [this issue](https://github.com/getkirby/panel/issues/827). 64 | 65 | ## Requirements 66 | 67 | - [**Kirby**](https://getkirby.com/) 2.5.5+ 68 | - PHP 7+ 69 | 70 | ## Disclaimer 71 | 72 | This plugin is provided "as is" with no guarantee. Use it at your own risk and always test it yourself before using it in a production environment. If you find any issues, please [create a new issue](https://github.com/username/plugin-name/issues/new). 73 | 74 | ## Purchase 75 | 76 | Be sure to try before you buy. Refunds are not supported. Read more in the [license agreement](docs/license.md). 77 | 78 | ***The purchase button is temporary disabled*** 79 | 80 | 102 | 103 | ## Donate 104 | 105 | If you want to make a donation in addition to the purchase, you can do that by sending any amount https://www.paypal.me/DevoneraAB 106 | 107 | ## Credits 108 | 109 | - [Jens Törnell](https://github.com/jenstornell) -------------------------------------------------------------------------------- /shared/config.php: -------------------------------------------------------------------------------- 1 | set('option', 'plugin.sync.settings', [ 3 | 'slug' => 'sync', 4 | 'token' => null, 5 | 'domains' => null 6 | ]); -------------------------------------------------------------------------------- /shared/fetch.php: -------------------------------------------------------------------------------- 1 | getContent($url); 21 | } 22 | } -------------------------------------------------------------------------------- /shared/settings.php: -------------------------------------------------------------------------------- 1 | get('option', $prefix . 'settings'); 11 | $settings_custom = kirby()->get('option', $prefix . 'settings.custom'); 12 | 13 | if(isset($settings_custom)) { 14 | $settings = array_merge($settings, $settings_custom); 15 | } 16 | 17 | if(isset($settings) && array_key_exists($name, $settings)) { 18 | return c::get($prefix . $name, $settings[$name]); 19 | } 20 | } 21 | } --------------------------------------------------------------------------------