├── src ├── FormWidgets │ ├── Tagbox │ │ ├── screenshots │ │ │ └── screenshot.png │ │ ├── widget │ │ │ ├── partials │ │ │ │ ├── _tag.htm │ │ │ │ └── _widget.htm │ │ │ └── assets │ │ │ │ ├── css │ │ │ │ └── tagbox.css │ │ │ │ └── js │ │ │ │ └── tagbox.js │ │ ├── composer.json │ │ ├── README.md │ │ └── Widget.php │ ├── Hasmany │ │ ├── screenshots │ │ │ ├── screenshot-1.png │ │ │ └── screenshot-2.png │ │ ├── widget │ │ │ ├── partials │ │ │ │ ├── _input.htm │ │ │ │ ├── _add.htm │ │ │ │ ├── _list.htm │ │ │ │ ├── _popup.htm │ │ │ │ ├── _item.htm │ │ │ │ └── _widget.htm │ │ │ └── assets │ │ │ │ ├── js │ │ │ │ ├── jquery-binding.js │ │ │ │ ├── sortable.min.js │ │ │ │ └── hasmany.js │ │ │ │ └── css │ │ │ │ └── hasmany.css │ │ ├── composer.json │ │ ├── README.md │ │ └── Widget.php │ ├── Password │ │ ├── widget │ │ │ ├── assets │ │ │ │ ├── css │ │ │ │ │ └── owl-password.css │ │ │ │ └── js │ │ │ │ │ └── password.min.js │ │ │ └── partials │ │ │ │ └── _widget.htm │ │ ├── composer.json │ │ ├── README.md │ │ └── Widget.php │ ├── Knob │ │ ├── widget │ │ │ ├── assets │ │ │ │ ├── css │ │ │ │ │ └── widget.css │ │ │ │ └── js │ │ │ │ │ └── widget.js │ │ │ └── partials │ │ │ │ └── _widget.htm │ │ ├── composer.json │ │ ├── README.md │ │ └── Widget.php │ ├── Preview │ │ ├── widget │ │ │ ├── partials │ │ │ │ └── _widget.htm │ │ │ └── assets │ │ │ │ └── js │ │ │ │ └── widget.js │ │ ├── composer.json │ │ ├── classes │ │ │ └── TagProcessor.php │ │ └── Widget.php │ ├── Comment │ │ ├── Widget.php │ │ ├── composer.json │ │ └── README.md │ ├── Money │ │ ├── composer.json │ │ ├── widget │ │ │ ├── partials │ │ │ │ └── _widget.htm │ │ │ └── assets │ │ │ │ └── js │ │ │ │ └── jquery.maskMoney.js │ │ ├── README.md │ │ └── Widget.php │ └── Address │ │ ├── widget │ │ ├── partials │ │ │ └── _widget.htm │ │ └── assets │ │ │ └── js │ │ │ └── location-autocomplete.js │ │ ├── composer.json │ │ ├── Widget.php │ │ └── README.md ├── Widgets │ └── TreeSort │ │ ├── widget │ │ ├── assets │ │ │ ├── css │ │ │ │ └── treesort.css │ │ │ ├── less │ │ │ │ └── treesort.less │ │ │ └── js │ │ │ │ └── treesort.js │ │ └── partials │ │ │ ├── _tree_records.htm │ │ │ └── _popup.htm │ │ ├── composer.json │ │ └── Widget.php └── Behaviors │ └── ListDelete │ ├── composer.json │ ├── Behavior.php │ └── README.md ├── composer.json └── README.md /src/FormWidgets/Tagbox/screenshots/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/october-widgets/library/HEAD/src/FormWidgets/Tagbox/screenshots/screenshot.png -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/screenshots/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/october-widgets/library/HEAD/src/FormWidgets/Hasmany/screenshots/screenshot-1.png -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/screenshots/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/october-widgets/library/HEAD/src/FormWidgets/Hasmany/screenshots/screenshot-2.png -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/partials/_input.htm: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/FormWidgets/Password/widget/assets/css/owl-password.css: -------------------------------------------------------------------------------- 1 | .owl-password, .owl-password.hideShowPassword-hidden { 2 | font-size: 16px; 3 | } 4 | 5 | .owl-password.hideShowPassword-shown { 6 | font-size: 12px; 7 | } 8 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/partials/_add.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/partials/_list.htm: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
  1. 4 | 5 |
  2. 6 | 7 |
8 | -------------------------------------------------------------------------------- /src/FormWidgets/Knob/widget/assets/css/widget.css: -------------------------------------------------------------------------------- 1 | .owl-knob { 2 | display: table; 3 | } 4 | 5 | .owl-knob .knob-row { 6 | display: table-row; 7 | } 8 | 9 | .owl-knob .knob { 10 | display: table-cell; 11 | } 12 | 13 | .owl-knob .knob-field { 14 | display: table-cell; 15 | vertical-align: middle; 16 | padding-left: 20px; 17 | } 18 | -------------------------------------------------------------------------------- /src/FormWidgets/Preview/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | 6 |
7 |
8 | 9 |
-------------------------------------------------------------------------------- /src/FormWidgets/Comment/Widget.php: -------------------------------------------------------------------------------- 1 | 2 |
  • 4 | > 5 | 7 | > 8 | 9 | 10 | 11 | 12 | 13 | 14 |
  • 15 | -------------------------------------------------------------------------------- /src/FormWidgets/Password/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /src/FormWidgets/Knob/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/knob", 3 | "description": "Knob widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "knob"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\FormWidgets\\Knob\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /src/Behaviors/ListDelete/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/list-delete", 3 | "description": "List delete behavior for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["octobercms", "owl"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\Behaviors\\ListDelete\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /src/FormWidgets/Money/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/money", 3 | "description": "Tag form widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "money"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\FormWidgets\\Money\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /src/FormWidgets/Tagbox/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/tagbox", 3 | "description": "Tag form widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "tagbox"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\FormWidgets\\Tagbox\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } -------------------------------------------------------------------------------- /src/Widgets/TreeSort/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/treesort", 3 | "description": "Tree sort widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["octobercms", "october", "owl", "treesort"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\Widgets\\TreeSort\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /src/FormWidgets/Comment/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/comment", 3 | "description": "A simple placeholder widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "comment"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\FormWidgets\\Comment\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } -------------------------------------------------------------------------------- /src/FormWidgets/Password/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/password", 3 | "description": "Password widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "password"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\FormWidgets\\Password\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/hasmany", 3 | "description": "Has-many / belongs-to relation subforms.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "hasmany"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Scott Bedard", 10 | "email": "scottbedard90@gmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=5.4.0" 15 | }, 16 | "autoload": { 17 | "psr-4": { 18 | "Owl\\FormWidgets\\HasMany\\": "" 19 | } 20 | }, 21 | "extra": { 22 | "branch-alias": { 23 | "dev-master": "1.0-dev" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /src/FormWidgets/Address/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 |
    2 | 3 | previewMode): ?> 4 |
    5 | 6 |
    9 | getAttributes() ?> 16 | getFieldMapAttributes() ?> 17 | /> 18 |
    19 | 20 | 21 |
    22 | -------------------------------------------------------------------------------- /src/Widgets/TreeSort/widget/partials/_tree_records.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 |
  • 4 |
    5 | 6 | name ?> 7 |
    8 | 9 |
      10 | getChildren()): ?> 11 | getChildren(); 13 | if ($this->index) { 14 | $children->sortBy($this->index); 15 | } 16 | ?> 17 | makePartial('tree_records', ['records' => $children]) ?> 18 | 19 |
    20 | 21 |
  • 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/FormWidgets/Preview/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/preview", 3 | "description": "A preview widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "comment"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Alexey Bobkov", 10 | "email": "aleksey.bobkov@gmail.com" 11 | }, 12 | { 13 | "name": "Samuel Georges", 14 | "email": "daftspunky@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.4.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Owl\\FormWidgets\\Comment\\": "" 23 | } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.0-dev" 28 | } 29 | }, 30 | "minimum-stability": "dev" 31 | } -------------------------------------------------------------------------------- /src/FormWidgets/Address/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "owl/address", 3 | "description": "Address widget for OctoberCMS.", 4 | "homepage": "http://github.com/october-widgets", 5 | "keywords": ["october cms", "october", "owl", "address"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Alexey Bobkov", 10 | "email": "aleksey.bobkov@gmail.com" 11 | }, 12 | { 13 | "name": "Samuel Georges", 14 | "email": "daftspunky@gmail.com" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.4.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Owl\\FormWidgets\\Address\\": "" 23 | } 24 | }, 25 | "extra": { 26 | "branch-alias": { 27 | "dev-master": "1.0-dev" 28 | } 29 | }, 30 | "minimum-stability": "dev" 31 | } 32 | -------------------------------------------------------------------------------- /src/FormWidgets/Money/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 |
    4 | 9 | 10 | 21 | 22 |
    23 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/partials/_popup.htm: -------------------------------------------------------------------------------- 1 |
    2 | 6 | 11 | 26 |
    27 | -------------------------------------------------------------------------------- /src/FormWidgets/Password/README.md: -------------------------------------------------------------------------------- 1 | # Password 2 | Uses [hideShowPassword](https://github.com/cloudfour/hideShowPassword) to mask input values on element blur. 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/password.svg) 5 | 6 | ### Installation 7 | To install the Password widget with your plugin, add the following to your plugin's ```composer.json``` file. 8 | 9 | ```json 10 | "require": { 11 | "owl/password": "~1.0@dev" 12 | } 13 | ``` 14 | Next, register the widget in your plugin's ```Plugin.php``` file. 15 | ```php 16 | public function registerFormWidgets() 17 | { 18 | return [ 19 | 'Owl\FormWidgets\Password\Widget' => [ 20 | 'label' => 'Password', 21 | 'code' => 'owl-password' 22 | ], 23 | ]; 24 | } 25 | ``` 26 | 27 | ### Usage 28 | To use the Password widget, simply declare a field type as ```owl-password``` 29 | ```yaml 30 | password: 31 | label: Password 32 | type: owl-password 33 | ``` 34 | -------------------------------------------------------------------------------- /src/FormWidgets/Tagbox/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 |
    4 | 5 | 6 | 17 | 18 | 19 | 22 | 23 | 24 | 29 | 30 |
    31 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/partials/_item.htm: -------------------------------------------------------------------------------- 1 |
    2 | 3 | config->default['icon'])): ?> 4 |
    5 | 6 | 7 |
    8 | config->default['label'])): ?> 9 |
    config->default['label'] ?>
    10 | 11 | 12 | config->default['comment'])): ?> 13 |
    config->default['comment'] ?>
    14 | 15 |
    16 | 17 |
    18 | config->sortColumn)): ?> 19 | 20 | 21 | 22 | 23 | 24 | 25 |
    26 | 27 |
    28 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 |
    5 | > 6 | 7 | 8 | 11 | 12 | 13 |
    14 | makePartial('list', ['items' => $items]) ?> 15 |
    16 | 17 | 18 | makePartial('add') ?> 19 | 20 | 21 | 24 | 25 | 26 | 31 | 32 |
    33 | -------------------------------------------------------------------------------- /src/FormWidgets/Comment/README.md: -------------------------------------------------------------------------------- 1 | # Comment 2 | A simple placeholder widget for [OctoberCMS](http://octobercms.com). 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/comment.svg) 5 | 6 | ### Installation 7 | To install the Comment widget, add the following to your plugin's ```composer.json``` file. 8 | ```json 9 | "require": { 10 | "owl/comment": "~1.0@dev" 11 | } 12 | ``` 13 | Next, register the widget in your plugin's ```Plugin.php``` file. 14 | ```php 15 | public function registerFormWidgets() 16 | { 17 | return [ 18 | 'Owl\FormWidgets\Comment\Widget' => [ 19 | 'label' => 'Comment', 20 | 'code' => 'owl-comment' 21 | ], 22 | ]; 23 | } 24 | ``` 25 | 26 | ### Usage 27 | The comment widget serves no form function, it exists solely to place extra text in your form. 28 | ```yaml 29 | comment: 30 | type: owl-comment 31 | label: Hello world 32 | comment: > 33 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla porttitor 34 | imperdiet elit, sed pellentesque eros. Morbi blandit elit a turpis 35 | pellentesque tincidunt. 36 | ``` 37 | -------------------------------------------------------------------------------- /src/FormWidgets/Knob/widget/partials/_widget.htm: -------------------------------------------------------------------------------- 1 | previewMode) 3 | $knobSettings['readOnly'] = true; 4 | ?> 5 | 6 |
    7 |
    8 |
    9 | $settingValue): ?> 15 | data-="" 16 | 17 | > 18 |
    19 |
    20 | 21 |

    22 |
    23 |
    24 | 33 |
    34 | -------------------------------------------------------------------------------- /src/FormWidgets/Preview/classes/TagProcessor.php: -------------------------------------------------------------------------------- 1 | callbacks[] = $callback; 26 | } 27 | public function processTags($markup, $preview) 28 | { 29 | foreach ($this->callbacks as $callback) 30 | $markup = $callback($markup, $preview); 31 | return $markup; 32 | } 33 | } -------------------------------------------------------------------------------- /src/FormWidgets/Password/Widget.php: -------------------------------------------------------------------------------- 1 | prepareVars(); 29 | return $this->makePartial('widget'); 30 | } 31 | 32 | /** 33 | * Prepares the form widget view data 34 | */ 35 | public function prepareVars() 36 | { 37 | $this->vars['name'] = $this->formField->getName(); 38 | $this->vars['value'] = $this->getLoadValue(); 39 | $this->vars['model'] = $this->model; 40 | } 41 | 42 | /** 43 | * {@inheritDoc} 44 | */ 45 | public function loadAssets() 46 | { 47 | $this->addJs('js/password.min.js'); 48 | $this->addCss('css/owl-password.css'); 49 | } 50 | 51 | /** 52 | * {@inheritDoc} 53 | */ 54 | public function getSaveValue($value) 55 | { 56 | return $value; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # October Widget Library 2 | A collection of form widgets for [October CMS](http://octobercms.com). 3 | 4 | ### Installing widgets 5 | For installation instructions, see the individual widget repositories. 6 | 7 | | Widget | Used for... | 8 | | :------------ | :-------------| 9 | | [Address](https://github.com/october-widgets/address) | Google maps auto-complete for address inputs. | 10 | | [Comment](https://github.com/october-widgets/comment) | Adding information to forms, without creating extra widgets. | 11 | | [Has Many](https://github.com/october-widgets/hasmany) | Managing related models through a has-many/belongs-to relationship. | 12 | | [Knob](https://github.com/october-widgets/knob) | A touch friendly widget to handle numeric fields. | 13 | | [Money](https://github.com/october-widgets/money) | Accepting currency input. | 14 | | [Password](https://github.com/october-widgets/password) | Masking input values on blur. | 15 | | [Tagbox](https://github.com/october-widgets/tagbox) | Accepting an array of strings. | 16 | 17 | ### Installing the complete library 18 | To install the complete library, add the following to your plugin's ```composer.json``` file. 19 | ```json 20 | "require": { 21 | "owl/library": "~1.0" 22 | } 23 | ``` 24 | Next, register the widgets in your ```Plugin.php``` file. Examples demonstrating how to do this can be found in the individual repositories. 25 | 26 | ### Contributing 27 | Have a widget that others might find useful? [Get in touch](http://octobercms.com/forum/post/october-widget-library)! 28 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/widget/assets/js/jquery-binding.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery plugin for Sortable 3 | * @author RubaXa 4 | * @license MIT 5 | */ 6 | (function (factory) { 7 | "use strict"; 8 | 9 | if (typeof define === "function" && define.amd) { 10 | define(["jquery"], factory); 11 | } 12 | else { 13 | /* jshint sub:true */ 14 | factory(jQuery); 15 | } 16 | })(function ($) { 17 | "use strict"; 18 | 19 | 20 | /* CODE */ 21 | 22 | 23 | /** 24 | * jQuery plugin for Sortable 25 | * @param {Object|String} options 26 | * @param {..*} [args] 27 | * @returns {jQuery|*} 28 | */ 29 | $.fn.sortable = function (options) { 30 | var retVal; 31 | 32 | this.each(function () { 33 | var $el = $(this), 34 | sortable = $el.data('sortable'); 35 | 36 | if (!sortable && (options instanceof Object || !options)) { 37 | sortable = new Sortable(this, options); 38 | $el.data('sortable', sortable); 39 | } 40 | 41 | if (sortable) { 42 | if (options === 'widget') { 43 | return sortable; 44 | } 45 | else if (options === 'destroy') { 46 | sortable.destroy(); 47 | $el.removeData('sortable'); 48 | } 49 | else if (options in sortable) { 50 | retVal = sortable[sortable].apply(sortable, [].slice.call(arguments, 1)); 51 | } 52 | } 53 | }); 54 | 55 | return (retVal === void 0) ? this : retVal; 56 | }; 57 | }); 58 | -------------------------------------------------------------------------------- /src/Widgets/TreeSort/Widget.php: -------------------------------------------------------------------------------- 1 | 'Tree Sort', 34 | 'description' => 'Controller list sorting.' 35 | ]; 36 | } 37 | 38 | /** 39 | * Inject CSS and JS assets 40 | */ 41 | public function loadAssets() 42 | { 43 | $this->addCss('css/treesort.css'); 44 | $this->addJs('js/treesort.js'); 45 | } 46 | 47 | /** 48 | * Load the re-order popup 49 | */ 50 | public function onLoadPopup() 51 | { 52 | $model = $this->controller->widget->list->model; 53 | 54 | $records = $model::make()->getAllRoot(); 55 | if ($this->index) { 56 | $records->sortBy($this->index); 57 | } 58 | 59 | return $this->makePartial('popup', [ 60 | 'records' => $records, 61 | 'header' => $this->header, 62 | 'empty' => $this->empty, 63 | ]); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/FormWidgets/Address/Widget.php: -------------------------------------------------------------------------------- 1 | fieldMap = $this->getConfig('fieldMap', []); 21 | } 22 | 23 | /** 24 | * {@inheritDoc} 25 | */ 26 | public function render() 27 | { 28 | $this->prepareVars(); 29 | return $this->makePartial('widget'); 30 | } 31 | 32 | /** 33 | * Prepares the list data 34 | */ 35 | public function prepareVars() 36 | { 37 | $this->vars['name'] = $this->formField->getName(); 38 | $this->vars['value'] = $this->getLoadValue(); 39 | $this->vars['field'] = $this->formField; 40 | } 41 | 42 | public function getFieldMapAttributes() 43 | { 44 | $widget = $this->controller->formGetWidget(); 45 | $fields = $widget->getFields(); 46 | $result = []; 47 | foreach ($this->fieldMap as $map => $fieldName) { 48 | 49 | if (!$field = array_get($fields, $fieldName)) 50 | continue; 51 | 52 | $result['data-input-'.$map] = '#'.$field->getId(); 53 | } 54 | 55 | return HTML::attributes($result); 56 | } 57 | 58 | /** 59 | * {@inheritDoc} 60 | */ 61 | public function loadAssets() 62 | { 63 | $this->addJs('http://maps.googleapis.com/maps/api/js?libraries=places&sensor=false'); 64 | $this->addJs('js/location-autocomplete.js', 'core'); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/FormWidgets/Money/README.md: -------------------------------------------------------------------------------- 1 | # Money 2 | Money form widget for OctoberCMS. 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/money.svg) 5 | 6 | ### Installation 7 | To install the Money widget with your plugin, add the following to your plugin's ```composer.json``` file. 8 | 9 | ```json 10 | "require": { 11 | "owl/money": "~1.0@dev" 12 | } 13 | ``` 14 | Next, register the widget in your plugin's ```Plugin.php``` file. 15 | ```php 16 | public function registerFormWidgets() 17 | { 18 | return [ 19 | 'Owl\FormWidgets\Money\Widget' => [ 20 | 'label' => 'Money', 21 | 'code' => 'owl-money' 22 | ], 23 | ]; 24 | } 25 | ``` 26 | 27 | ### Usage 28 | To use the Money widget, simply declare a field type as ```owl-money``` 29 | ```yaml 30 | price: 31 | label: Price 32 | type: owl-money 33 | ``` 34 | There are several parameters that can be used to customize the money widget. Defining a ```thousands``` or ```decimal``` will change the thousands and decimal characters. Defining a ```prefix``` or ```suffix``` string will allow you to add currency symbols before or after the input. Lastly, setting ```allowNegative``` to ```true``` will allow negative values to be submitted. 35 | 36 | The below example will accept negative inputs with a USD prefix (```$ -1,234.56```) 37 | ```yaml 38 | price: 39 | label: Price 40 | type: owl-money 41 | prefix: "$ " 42 | allowNegative: true 43 | ``` 44 | 45 | You may need to define the widget configuration dynamically. For example, your plugin might have a variable currency symbol. This can be achieved like so... 46 | ```php 47 | Config::set('owl.formwidgets::money', [ 48 | 'prefix' => '$ ', 49 | 'placeholder' => '$ 0.00', 50 | ]); 51 | ``` 52 | -------------------------------------------------------------------------------- /src/Behaviors/ListDelete/Behavior.php: -------------------------------------------------------------------------------- 1 | controller = $controller; 24 | 25 | $this->setConfig($controller->listConfig, ['modelClass']); 26 | } 27 | 28 | /** 29 | * Delete the selected rows 30 | * 31 | * @return array 32 | */ 33 | public function index_onDelete() 34 | { 35 | $model = $this->config->modelClass; 36 | 37 | if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) { 38 | foreach ($checkedIds as $id) { 39 | if ($record = $model::find($id)) { 40 | if (method_exists($this->controller, 'overrideListDelete')) { 41 | $this->controller->overrideListDelete($record); 42 | } else { 43 | $record->delete(); 44 | } 45 | } 46 | } 47 | 48 | if (method_exists($this->controller, 'afterListDelete')) { 49 | $this->controller->afterListDelete(); 50 | } else { 51 | Flash::success(Lang::get('backend::lang.list.delete_selected_success')); 52 | } 53 | } 54 | 55 | return method_exists($this->controller, 'overrideListRefresh') 56 | ? $this->controller->overrideListRefresh() 57 | : $this->controller->listRefresh(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Widgets/TreeSort/widget/partials/_popup.htm: -------------------------------------------------------------------------------- 1 |
    2 | 6 | 7 | 22 | 23 | 49 |
    50 | -------------------------------------------------------------------------------- /src/FormWidgets/Tagbox/README.md: -------------------------------------------------------------------------------- 1 | # Tagbox 2 | Tag form widget for OctoberCMS. 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/tagbox.svg) 5 | 6 | ### Installation 7 | To install the Tagbox widget with your plugin, add the following to your plugin's ```composer.json``` file. 8 | 9 | ```json 10 | "require": { 11 | "owl/tagbox": "~1.0@dev" 12 | } 13 | ``` 14 | Next, register the widget in your plugin's ```Plugin.php``` file. 15 | ```php 16 | public function registerFormWidgets() 17 | { 18 | return [ 19 | 'Owl\FormWidgets\Tagbox\Widget' => [ 20 | 'label' => 'Tagbox', 21 | 'code' => 'owl-tagbox' 22 | ], 23 | ]; 24 | } 25 | ``` 26 | 27 | ### Usage 28 | To use the Tagbox widget, simply declare a field type as ```owl-tagbox``` 29 | ```yaml 30 | tags: 31 | label: Tags 32 | type: owl-tagbox 33 | ``` 34 | If tags *are not* being stored through a related model, the model attribute must be [jsonable](http://octobercms.com/docs/database/model#attribute-modifiers). If tags *are* being stored through a related model, the ```getTagsAttribute``` and ```setTagsAttribute``` methods must be declared to process the relationship. These methods should return / accept an array of strings. 35 | 36 | Validation can be performed on tags by defining a ```validation``` regular expression parameter. A ```validationMessage``` can also be defined for custom error messages. For example, if you are accepting an array of emails, something like this could be used to validate the tagbox values... 37 | 38 | ```yaml 39 | emails: 40 | label: Email Addresses 41 | type: owl-tagbox 42 | validation: ^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$ 43 | validationMessage: Please enter a valid email address. 44 | ``` 45 | 46 | A ```filter``` expression may also be used to block unwanted characters. In the following example, we'll allow alpha-numeric characters, dashes, and underscores. The default css may be customized or removed by specifying a ```cssPath``` attribute. 47 | 48 | ```yaml 49 | tags: 50 | label: Tags 51 | type: owl-tagbox 52 | filter: "[^a-zA-Z0-9_.-]+" 53 | cssPath: /plugins/vendor/plugin/assets/css/custom-tagbox.css 54 | ``` 55 | 56 | Lastly, a tag may be "slugified" upon entry by setting the ```slugify``` parameter to ```true```. 57 | -------------------------------------------------------------------------------- /src/Behaviors/ListDelete/README.md: -------------------------------------------------------------------------------- 1 | # List Delete Behavior 2 | List delete behavior for [OctoberCMS](http://octobercms.com). 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/list-delete.svg) 5 | 6 | ### Installation 7 | To install the List Delete behavior, add the following to your plugin's ```composer.json``` file. 8 | ```json 9 | "require": { 10 | "owl/list-delete": "~1.0@dev" 11 | } 12 | ``` 13 | 14 | Next, implement the behavior in your controllers... 15 | ```php 16 | public $implement = [ 17 | 'Backend.Behaviors.FormController', 18 | 'Backend.Behaviors.ListController', 19 | 'Owl.Behaviors.ListDelete.Behavior', // <-- add this line 20 | ]; 21 | ``` 22 | 23 | Then enable checkboxes in your ```config_list.yaml``` file... 24 | ```yaml 25 | showCheckboxes: true # <-- uncomment this line 26 | ``` 27 | And lastly, add a button to your `_list_toolbar.htm` file... 28 | ```html 29 | 44 | ``` 45 | 46 | ### Overriding default actions 47 | If you need to perform additional delete logic, simply add the following method to your controller. 48 | ```php 49 | public function overrideListDelete($record) 50 | { 51 | $record->delete(); 52 | // do whatever else you need to do 53 | } 54 | ``` 55 | 56 | To override what should happen after your records are deleted, add the following method to your controller. 57 | ```php 58 | public function afterListDelete() 59 | { 60 | Flash::success('Things were deleted!'); 61 | } 62 | ``` 63 | 64 | By default, the list will be refreshed after a delete has occured. If you'd like to override this behavior, add the following method to your controller. 65 | ```php 66 | public function overrideListRefresh() 67 | { 68 | // do stuff here 69 | } 70 | ``` 71 | -------------------------------------------------------------------------------- /src/FormWidgets/Address/README.md: -------------------------------------------------------------------------------- 1 | # Address 2 | Address widget for [OctoberCMS](http://octobercms.com). 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/address.svg) 5 | 6 | ### Installation 7 | To install the Address widget, add the following to your plugin's ```composer.json``` file. 8 | ```json 9 | "require": { 10 | "owl/address": "~1.0@dev" 11 | } 12 | ``` 13 | Next, register the widget in your plugin's ```Plugin.php``` file. 14 | ```php 15 | public function registerFormWidgets() 16 | { 17 | return [ 18 | 'Owl\FormWidgets\Address\Widget' => [ 19 | 'label' => 'Address', 20 | 'code' => 'owl-address' 21 | ], 22 | ]; 23 | } 24 | ``` 25 | 26 | ### Usage 27 | You can add this widget to your forms simply by decalring the field type as `owl-address`. 28 | 29 | ``` 30 | address: 31 | label: Address 32 | type: owl-address 33 | ``` 34 | This allows the form field to query the google maps public API and predict the location using autocomplete. 35 | 36 | You can customize it further by using field maps, so that it automatically fills other fields with values based on the selected place i.e. such as latitude, longitude, etc. To do this first declare the form field with the field maps. 37 | ``` 38 | address: 39 | label: Address 40 | type: owl-address 41 | fieldMap: 42 | latitude: latitude 43 | longitude: longitude 44 | city: city 45 | zip: zip 46 | country: country_code 47 | state: state_code 48 | name: place_name 49 | address: place_address 50 | formataddress: fmt_address 51 | ``` 52 | 53 | Now define fields which should get filled up with the result values. The field name should be same as the value specified in the fieldMap fields. 54 | ``` 55 | city: 56 | label: City 57 | zip: 58 | label: Zip 59 | country_code: 60 | label: Country 61 | state_code: 62 | label: State 63 | latitude: 64 | label: Latitude 65 | longitude: 66 | label: Longitude 67 | place_name: 68 | label: Place Name 69 | place_address: 70 | label: Place Address 71 | fmt_address: 72 | label: Formatted Address 73 | ``` 74 | The widget automatically detects the presence of the fields and applies the correct values whenever a place is selected using the autocomplete. 75 | 76 | Available mappings: 77 | 78 | * street 79 | * city 80 | * zip 81 | * state 82 | * country 83 | * country-long 84 | * latitude 85 | * longitude 86 | * name 87 | * address 88 | * formataddress -------------------------------------------------------------------------------- /src/FormWidgets/Tagbox/widget/assets/css/tagbox.css: -------------------------------------------------------------------------------- 1 | /* The input form element */ 2 | .owl-tagbox input[data-control="tagbox-input"] { 3 | border: 0; 4 | font-family: inherit; 5 | font-size: inherit; 6 | outline: none; 7 | padding: 5px; 8 | font-size: 13px; 9 | } 10 | .owl-tagbox input[data-control="tagbox-input"]::-webkit-input-placeholder { color: #ccc; } 11 | .owl-tagbox input[data-control="tagbox-input"]:-moz-placeholder { color: #ccc; } 12 | .owl-tagbox input[data-control="tagbox-input"]::-moz-placeholder { color: #ccc; } 13 | .owl-tagbox input[data-control="tagbox-input"]:-ms-input-placeholder { color: #ccc; } 14 | 15 | /* The list containing our tags */ 16 | .owl-tagbox ul { 17 | background-color: #ffffff; 18 | background-image: none; 19 | border: 1px solid #E0E0E0; 20 | border-radius: 2px; 21 | -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 22 | box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); 23 | color: #555555; 24 | cursor: text; 25 | display: block; 26 | list-style: none; 27 | margin: 0; 28 | overflow: hidden; 29 | padding: 0 6px 6px; 30 | width: 100%; 31 | } 32 | 33 | .owl-tagbox ul.focused { 34 | border-color: #808C8D; 35 | } 36 | 37 | /* Tags are contained within
  • items */ 38 | .owl-tagbox li { 39 | display: inline-block; 40 | float: left; 41 | font-size: 14px; 42 | line-height: 1.428571429; 43 | margin: 6px 6px 0 0; 44 | } 45 | 46 | /* These are the actual tags */ 47 | .owl-tagbox li[data-control="tag"] { 48 | background-color: #405D7A; 49 | border-radius: 2px; 50 | border: 1px solid #000; 51 | color: #dedede; 52 | cursor: default; 53 | padding: 3px 8px; 54 | } 55 | .owl-tagbox .tagbox-tag span.value { float: left; } 56 | .owl-tagbox .tagbox-tag span.remove-span { float: right; } 57 | 58 | /* Link for removing a tag */ 59 | .owl-tagbox li[data-control="tag"] [data-control="remove"] { 60 | color: inherit; 61 | cursor: pointer; 62 | margin-left: 10px; 63 | text-decoration: none; 64 | } 65 | .owl-tagbox li[data-control="tag"] [data-control="remove"]:hover { 66 | color: #fff; 67 | text-decoration: none; 68 | } 69 | 70 | /* This class will add to pending backspace deletes */ 71 | .owl-tagbox li[data-control="tag"].pre-delete { 72 | background-color: #E0444B; 73 | } 74 | 75 | /* This class will be flashed to display duplicates */ 76 | .owl-tagbox li[data-control="tag"].flash { 77 | background-color: #457A40 !important; 78 | } -------------------------------------------------------------------------------- /src/FormWidgets/Money/Widget.php: -------------------------------------------------------------------------------- 1 | prepareVars(); 16 | return $this->makePartial('widget'); 17 | } 18 | 19 | /** 20 | * Load widget assets 21 | */ 22 | public function loadAssets() 23 | { 24 | $this->addJs('js/jquery.maskMoney.js'); 25 | } 26 | 27 | /** 28 | * Prepare widget variables 29 | */ 30 | public function prepareVars() 31 | { 32 | $this->vars['loadValue'] = $this->getLoadValue(); 33 | 34 | $this->vars['config']['placeholder'] = isset($this->config->placeholder) 35 | ? $this->config->placeholder 36 | : '0.00'; 37 | 38 | $this->vars['config']['thousands'] = isset($this->config->thousands) 39 | ? $this->config->thousands 40 | : ','; 41 | 42 | $this->vars['config']['decimal'] = isset($this->config->decimal) 43 | ? $this->config->decimal 44 | : '.'; 45 | 46 | $this->vars['config']['suffix'] = isset($this->config->suffix) 47 | ? $this->config->suffix 48 | : ''; 49 | 50 | $this->vars['config']['prefix'] = isset($this->config->prefix) 51 | ? $this->config->prefix 52 | : ''; 53 | 54 | $this->vars['config']['allowNegative'] = isset($this->config->allowNegative) && $this->config->allowNegative 55 | ? 'true' 56 | : 'false'; 57 | 58 | // Allow plugins to override the configuration 59 | if ($config = Config::get('owl.formwidgets::money')) { 60 | if (is_array($config) && count($config) >= 1) { 61 | foreach ($config as $key => $value) 62 | $this->vars['config'][$key] = $value; 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Return save value 69 | * 70 | * @return float 71 | */ 72 | public function getSaveValue($value) 73 | { 74 | if (!$input = input($this->fieldName)) 75 | return 0; 76 | 77 | $input = preg_replace("/[^0-9]/", '', $input); 78 | $input = substr($input, 0, -2) . '.' . substr($input, -2); 79 | $input = floatval($input); 80 | 81 | return $input; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/FormWidgets/Knob/README.md: -------------------------------------------------------------------------------- 1 | # Knob 2 | A touch friendly widget to handle numeric fields. 3 | 4 | [jQuery Knob](https://github.com/aterrien/jQuery-Knob) is © 2012 Anthony Terrien under the MIT license. OctoberCMS widget created by [Keios Solutions](http://keios.eu). 5 | 6 | ![Packagist](https://img.shields.io/packagist/dt/owl/knob.svg) 7 | 8 | ### Installation 9 | To install the Knob widget, add the following to your plugin's ```composer.json``` file. 10 | 11 | ```json 12 | "require": { 13 | "owl/knob": "~1.0@dev" 14 | } 15 | ``` 16 | Next, register the widget in your plugin's ```Plugin.php``` file. 17 | ```php 18 | public function registerFormWidgets() 19 | { 20 | return [ 21 | 'Owl\FormWidgets\Knob\Widget' => [ 22 | 'label' => 'Knob', 23 | 'code' => 'owl-knob' 24 | ], 25 | ]; 26 | } 27 | ``` 28 | 29 | ### Usage 30 | To use the Knob widget, simply declare a field type as ```owl-knob``` 31 | ```yaml 32 | knob: 33 | label: Knob 34 | type: owl-knob 35 | ``` 36 | The following options are supported : 37 | 38 | **Behaviors:** 39 | ``` 40 | min : min value | default=0. 41 | max : max value | default=100. 42 | step : step size | default=1. 43 | angleOffset : starting angle in degrees | default=0. 44 | angleArc : arc size in degrees | default=360. 45 | stopper : stop at min & max on keydown/mousewheel | default=true. 46 | readOnly : disable input and events | default=false. 47 | rotation : direction of progression | default=clockwise. 48 | ``` 49 | 50 | **UI:** 51 | ``` 52 | cursor : display mode "cursor", cursor size could be changed passing a numeric value to the option, default width is used when passing boolean value "true" | default=gauge. 53 | thickness : gauge thickness. 54 | lineCap : gauge stroke endings. | default=butt, round=rounded line endings 55 | width : dial width. 56 | displayInput : default=true | false=hide input. 57 | displayPrevious : default=false | true=displays the previous value with transparency. 58 | fgColor : foreground color. 59 | inputColor : input value (number) color. 60 | font : font family. 61 | fontWeight : font weight. 62 | bgColor : background color. 63 | ``` 64 | 65 | **October UI related:** 66 | ``` 67 | knobLabel: label to display on right side of the knob, can be translation string 68 | knobComment: comment to display on right side of the knob, can be translation string 69 | ``` 70 | 71 | **Form configuration example** 72 | 73 | ```yaml 74 | fields: 75 | age: 76 | knobLabel: Your current age 77 | knobComment: We need to know, really. 78 | type: owl-knob 79 | span: right 80 | min: 1 81 | default: 23 82 | max: 100 83 | angleOffset: -125 84 | angleArc: 250 85 | ``` 86 | -------------------------------------------------------------------------------- /src/Widgets/TreeSort/widget/assets/js/treesort.js: -------------------------------------------------------------------------------- 1 | +function ($) { "use strict"; 2 | 3 | // 4 | // TreeSort 5 | // 6 | var TreeSort = function ($el) { 7 | var self = this; 8 | 9 | this.$el = $el; 10 | 11 | this.$el.unbind().on('click', function() { 12 | self.popup(); 13 | }); 14 | } 15 | 16 | // 17 | // Popup form 18 | // 19 | TreeSort.prototype.popup = function() { 20 | 21 | this.$el.on('show.oc.popup', function(e) { 22 | var $popup = $(e.relatedTarget), 23 | $list = $popup.find('ol[data-control="records"]').first(); 24 | 25 | // Inline the list height to prevent the modal from 26 | // shrinking while categories are being re-ordered 27 | $list.css('height', $list.height()); 28 | 29 | // Submit the request on apply button clicks 30 | $popup.find('button[data-control="apply-btn"]').on('click', function() { 31 | var $loadingIndicator = $popup.find('div.loading-indicator'), 32 | treeData = [], 33 | i = 0; 34 | 35 | // Cycle through the list items and create the array of 36 | // data to send to the ajax handler. 37 | $list.find('li').each(function() { 38 | treeData[i] = { 39 | id: parseInt($(this).data('record-id')) || null, 40 | parent_id: parseInt($(this).parent().data('parent-id')) || null 41 | }; 42 | i++; 43 | }); 44 | 45 | $loadingIndicator.show(); 46 | $.request('onUpdateTree', { 47 | data: { 48 | treeData: treeData, 49 | }, 50 | success: function(data) { 51 | this.success(data).done(function() { 52 | $popup.trigger('close.oc.popup'); 53 | $(document).trigger('render'); 54 | }); 55 | }, 56 | complete: function(data) { 57 | $loadingIndicator.hide(); 58 | } 59 | }); 60 | }); 61 | }); 62 | 63 | // Trigger the popup 64 | this.$el.popup({ 65 | handler: 'treesort::onLoadPopup' 66 | }); 67 | } 68 | 69 | // 70 | // Attach TreeSort to the element 71 | // 72 | $.fn.TreeSort = function () { 73 | return new TreeSort(this); 74 | } 75 | 76 | $(document).on('render', function() { 77 | $('[data-control="treesort"]').TreeSort(); 78 | }); 79 | 80 | }(window.jQuery); 81 | -------------------------------------------------------------------------------- /src/FormWidgets/Hasmany/README.md: -------------------------------------------------------------------------------- 1 | # Hasmany 2 | Has-many/belongs-to popup widget for [OctoberCMS](http://octobercms.com). 3 | 4 | ![Packagist](https://img.shields.io/packagist/dt/owl/hasmany.svg) 5 | 6 | ### Installation 7 | To install the Hasmany widget, add the following to your plugin's ```composer.json``` file. 8 | ```json 9 | "require": { 10 | "owl/hasmany": "~1.0@dev" 11 | } 12 | ``` 13 | Next, register the widget in your ```Plugin.php``` file. 14 | ```php 15 | public function registerFormWidgets() 16 | { 17 | return [ 18 | 'Owl\FormWidgets\HasMany\Widget' => [ 19 | 'label' => 'Hasmany', 20 | 'code' => 'owl-hasmany' 21 | ], 22 | ]; 23 | } 24 | ``` 25 | 26 | ### Usage 27 | First things first, you'll need to have a pair of models related via a has-many / belongs-to relationship. From there, in your parent model's fields.yaml file use the relationship name as the field name, and ```owl-hasmany``` as the type. 28 | ```yaml 29 | relationship: 30 | type: owl-hasmany 31 | ``` 32 | 33 | Next, you'll need to define the default parameters, or a custom partial. The default parameters will create a list that is very similar to the Sitemap plugin's UI. You may use basic twig markup inside these fields, variable names will reference model attributes. The ```icon``` option should be a valid [Font-Autumn](http://daftspunk.github.io/Font-Autumn/) icon class, or ```false```. 34 | ```yaml 35 | relationship: 36 | type: owl-hasmany 37 | default: 38 | icon: icon-file-o 39 | label: "{{ name }}" 40 | comment: "{{ description }}" 41 | ``` 42 | To customize the widget appearance, you may also define a custom partial instead of the default. 43 | ```yaml 44 | relationship: 45 | type: owl-hasmany 46 | partial: @/plugins/author/plugin/models/relationship/_partial.htm 47 | ``` 48 | There are a few additional parameters available to customize the widget's appearance. Defining a ```sortColumn``` enables drag-and-drop re-ordering. This value should reference the model's "order by" column name. Defining a ```formHeader``` will change the default header of popup windows. Defining an ```addLabel``` or ```addIcon``` will customize the appearance of the add button. ```addLabel```. 49 | 50 | Javascript events will be triggered when a popup window is opened or closed. Listening for these events can be useful when you wish to show / hide fields based on form values. 51 | ```javascript 52 | $(document).bind("owl.hasmany.opened", function(e, data) { 53 | // popup was opened 54 | }); 55 | $(document).bind("owl.hasmany.closed", function(e, data) { 56 | // popup was closed 57 | }); 58 | ``` 59 | The ```data``` array will contain 3 keys. ```data.alias``` refers to the widget alias, ```data.item``` refers to the popup's cooresponding li element, and ```data.form``` references the popup form element. 60 | -------------------------------------------------------------------------------- /src/FormWidgets/Knob/Widget.php: -------------------------------------------------------------------------------- 1 | prepareVars(); 29 | return $this->makePartial('widget'); 30 | } 31 | 32 | /** 33 | * Prepares the form widget view data 34 | */ 35 | public function prepareVars() 36 | { 37 | $this->vars['name'] = $this->formField->getName(); 38 | $this->vars['default'] = $default = $this->getConfig('default', 0); 39 | $this->vars['label'] = $this->getConfig('knobLabel'); 40 | $this->vars['comment'] = $this->getConfig('knobComment'); 41 | $this->vars['knobSettings'] = [ 42 | 'angleArc' => $this->getConfig('angleArc', 360), 43 | 'angleOffset' => $this->getConfig('angleOffset', 0), 44 | 'bgColor' => '#'.$this->getConfig('bgColor', 'EEEEEE'), 45 | 'cursor' => $this->getConfig('cursor', 'false'), 46 | 'displayInput' => $this->getConfig('displayInput', 'true'), 47 | 'displayPrevious' => $this->getConfig('displayPrevious', 'false'), 48 | 'fgColor' => '#'.$this->getConfig('fgColor', '87CEEB'), 49 | 'font' => $this->getConfig('font', 'Open Sans'), 50 | 'fontWeight' => $this->getConfig('fontWeight', 'normal'), 51 | 'height' => $this->getConfig('width', 100), 52 | 'inputColor' => '#'.$this->getConfig('inputColor', '87CEEB'), 53 | 'lineCap' => $this->getConfig('linecap', 'default'), 54 | 'max' => $this->getConfig('max', 100), 55 | 'min' => $this->getConfig('min', 0), 56 | 'readOnly' => $this->getConfig('disabled', 'false'), 57 | 'rotation' => $this->getConfig('rotation', 'clockwise'), 58 | 'step' => $this->getConfig('step', 1), 59 | 'stopper' => $this->getConfig('stopper', 'true'), 60 | 'thickness' => $this->getConfig('thickness', 0.3), 61 | 'width' => $this->getConfig('width', 100), 62 | ]; 63 | 64 | $this->vars['value'] = $this->getLoadValue() ?: $default; 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | public function loadAssets() 71 | { 72 | $this->addCss('css/widget.css'); 73 | $this->addJs('js/widget.js'); 74 | } 75 | 76 | /** 77 | * {@inheritDoc} 78 | */ 79 | public function getSaveValue($value) 80 | { 81 | return $value; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/FormWidgets/Preview/Widget.php: -------------------------------------------------------------------------------- 1 | ', '
    ', $result);
     30 |         $result = TagProcessor::instance()->processTags($result, $preview);
     31 |         return $result;
     32 |     }
     33 | 
     34 |     /**
     35 |      * {@inheritDoc}
     36 |      */
     37 |     public function render()
     38 |     {
     39 |         $this->vars['preview_html'] = $this->formatHtml($this->model->content, true);
     40 |         return $this->makePartial('widget');
     41 |     }
     42 | 
     43 |     /**
     44 |      * {@inheritDoc}
     45 |      */
     46 |     public function loadAssets()
     47 |     {
     48 |         $this->addJs('js/widget.js');
     49 |     }
     50 | 
     51 |     public function init()
     52 |     {
     53 |         $this->checkUploadPostback();
     54 |     }
     55 |     protected function checkUploadPostback()
     56 |     {
     57 |         if (!post('X_BLOG_IMAGE_UPLOAD'))
     58 |             return;
     59 |         $uploadedFileName = null;
     60 |         try {
     61 |             $uploadedFile = Input::file('file');
     62 |             if ($uploadedFile)
     63 |                 $uploadedFileName = $uploadedFile->getClientOriginalName();
     64 |             $validationRules = ['max:'.File::getMaxFilesize()];
     65 |             $validationRules[] = 'mimes:jpg,jpeg,bmp,png,gif';
     66 |             $validation = Validator::make(
     67 |                 ['file_data' => $uploadedFile],
     68 |                 ['file_data' => $validationRules]
     69 |             );
     70 |             if ($validation->fails())
     71 |                 throw new ValidationException($validation);
     72 |             if (!$uploadedFile->isValid())
     73 |                 throw new SystemException(Lang::get('cms::lang.asset.file_not_valid'));
     74 |             $fileRelation = $this->model->content_images();
     75 |             $file = new File();
     76 |             $file->data = $uploadedFile;
     77 |             $file->is_public = true;
     78 |             $file->save();
     79 |             $fileRelation->add($file, $this->sessionKey);
     80 |             $result = [
     81 |                 'file' => $uploadedFileName,
     82 |                 'path' => $file->getPath()
     83 |             ];
     84 |             $response = Response::make()->setContent($result);
     85 |             $response->send();
     86 |             die();
     87 |         } catch (Exception $ex) {
     88 |             $message = $uploadedFileName
     89 |                 ? Lang::get('cms::lang.asset.error_uploading_file', ['name' => $uploadedFileName, 'error' => $ex->getMessage()])
     90 |                 : $ex->getMessage();
     91 |             $result = [
     92 |                 'error' => $message,
     93 |                 'file' => $uploadedFileName
     94 |             ];
     95 |             $response = Response::make()->setContent($result);
     96 |             $response->send();
     97 |             die();
     98 |         }
     99 |     }
    100 | }
    
    
    --------------------------------------------------------------------------------
    /src/FormWidgets/Hasmany/widget/assets/css/hasmany.css:
    --------------------------------------------------------------------------------
      1 | /* Modal */
      2 | div[data-owl="hasmany"] .modal-footer.in-progress button {
      3 |     visibility: hidden;
      4 | }
      5 | div[data-owl="hasmany"] .modal-footer [data-control="loading-indicator"] {
      6 |     bottom: 5px;
      7 |     position: absolute;
      8 |     right: 0;
      9 | }
     10 | 
     11 | /* Add button */
     12 | div[data-owl="hasmany"].default a[data-control="add-model"] {
     13 |     display: block;
     14 |     margin-top: 20px;
     15 |     padding: 13px 15px;
     16 |     border: dotted 2px #ebebeb;
     17 |     color: #bdc3c7;
     18 |     font-size: 12px;
     19 |     font-weight: 600;
     20 |     text-transform: uppercase;
     21 |     -webkit-border-radius: 5px;
     22 |     -moz-border-radius: 5px;
     23 |     border-radius: 5px;
     24 |     vertical-align: middle;
     25 | }
     26 | .compact-container div[data-owl="hasmany"].default a[data-control="add-model"] {
     27 |     margin-left: 20px;
     28 |     margin-right: 20px;
     29 | }
     30 | div[data-owl="hasmany"].default a[data-control="add-model"] i {
     31 |     margin-right: 10px;
     32 |     font-size: 14px;
     33 | }
     34 | div[data-owl="hasmany"].default a[data-control="add-model"]:hover {
     35 |     text-decoration: none;
     36 |     background-color: #58b6f7 !important;
     37 |     color: #ffffff !important;
     38 |     border: none;
     39 |     padding: 15px 17px;
     40 | }
     41 | 
     42 | /* List */
     43 | div[data-owl="hasmany"].default ol {
     44 |     list-style-type: none;
     45 |     margin: 0;
     46 |     padding: 0;
     47 |     font-size: 13px;
     48 | }
     49 | div[data-owl="hasmany"].default ol li {
     50 |     position: relative;
     51 |     height: 60px;
     52 |     color: #666;
     53 | }
     54 | div[data-owl="hasmany"].default ol li.delete-highlight {
     55 |     background: #C30;
     56 |     color: #fefefe;
     57 | }
     58 | div[data-owl="hasmany"].default ol li:hover {
     59 |     background: #58B6F7;
     60 |     color: #fefefe;
     61 |     cursor: pointer;
     62 | }
     63 | div[data-owl="hasmany"].default ol li .item {
     64 |     display: table;
     65 |     height: 60px;
     66 |     width: 100%;
     67 |     position: absolute;
     68 | }
     69 | div[data-owl="hasmany"].default ol li .icon {
     70 |     display: table-cell;
     71 |     vertical-align: middle;
     72 |     font-size: 22px;
     73 | }
     74 | div[data-owl="hasmany"].default ol li .icon i {
     75 |     display: block;
     76 |     width: 60px;
     77 |     text-align: center;
     78 | }
     79 | div[data-owl="hasmany"].default ol li .content {
     80 |     display: table-cell;
     81 |     vertical-align: middle;
     82 |     width: 50%;
     83 | }
     84 | div[data-owl="hasmany"].default ol li .content.no-icon {
     85 |     padding-left: 15px;
     86 | }
     87 | 
     88 | /* List sorting */
     89 | div[data-owl="hasmany"].default ol.sorting li {
     90 |     background: #F9F9F9 !important;
     91 |     color: #666 !important;
     92 | }
     93 | div[data-owl="hasmany"].default ol.sorting li .actions {
     94 |     visibility: hidden !important;
     95 | }
     96 | div[data-owl="hasmany"].default ol li.sortable-ghost {
     97 |     background: #58B6F7 !important;
     98 |     color: #fefefe !important;
     99 |     z-index: 999;
    100 | }
    101 | 
    102 | /* List actions */
    103 | div[data-owl="hasmany"].default ol li .actions {
    104 |     display: table-cell;
    105 |     vertical-align: middle;
    106 |     text-align: right;
    107 |     width: 50%;
    108 | }
    109 | div[data-owl="hasmany"].default ol li .actions a {
    110 |     font-size: 22px;
    111 |     float: right;
    112 |     margin-left: 1px;
    113 |     width: 60px;
    114 |     height: 60px;
    115 |     line-height: 60px;
    116 |     display: none;
    117 | }
    118 | div[data-owl="hasmany"].default ol li:hover .actions a, ol .sortable-placeholder .actions a {
    119 |     background: #2581B8;
    120 |     text-align: center;
    121 |     border-right: 1px solid #328ec8;
    122 |     text-decoration: none;
    123 |     color: #fefefe;
    124 |     display: inline-block;
    125 | }
    126 | div[data-owl="hasmany"].default ol li .actions [data-control="sort-handle"] {
    127 |     cursor: move;
    128 | }
    129 | 
    
    
    --------------------------------------------------------------------------------
    /src/FormWidgets/Tagbox/Widget.php:
    --------------------------------------------------------------------------------
      1 | prepareVars();
     15 |         return $this->makePartial('widget');
     16 |     }
     17 | 
     18 |     /**
     19 |      * Prepare widget variables
     20 |      */
     21 |     public function prepareVars()
     22 |     {
     23 |         // Break key codes
     24 |         if (isset($this->config->breakCodes)) {
     25 |             $config['breakCodes'] = is_array($this->config->breakCodes)
     26 |                 ? $this->config->breakCodes
     27 |                 : [$this->config->breakCodes];
     28 |         } else {
     29 |             $config['breakCodes'] = [13, 9];
     30 |         }
     31 | 
     32 |         // Slugify
     33 |         $config['slugify'] = isset($this->config->slugify) &&
     34 |             filter_var($this->config->slugify, FILTER_VALIDATE_BOOLEAN);
     35 | 
     36 |         // Accepted characters
     37 |         $config['filter'] = isset($this->config->filter)
     38 |             ? $this->config->filter
     39 |             : false;
     40 | 
     41 |         // Validation rules
     42 |         $config['validation'] = isset($this->config->validation)
     43 |             ? $this->config->validation
     44 |             : false;
     45 | 
     46 |         // Validation message
     47 |         $config['validationMessage'] = isset($this->config->validationMessage)
     48 |             ? $this->config->validationMessage
     49 |             : 'The tag format is invalid.';
     50 | 
     51 |         // Disable auto-focus
     52 |         $config['autofocus'] = isset($this->config->autofocus)
     53 |             ? (bool) $this->config->autofocus
     54 |             : true;
     55 | 
     56 |         // Javascript configuration
     57 |         $config['fieldName'] = $this->fieldName;
     58 |         $this->vars['config'] = htmlspecialchars(json_encode($config));
     59 | 
     60 |         // Pre-populated tags
     61 |         $fieldName = $this->fieldName;
     62 |         $this->vars['tags'] = is_array($this->model->$fieldName)
     63 |             ? implode(',', $this->model->$fieldName)
     64 |             : false;
     65 | 
     66 |         // Placeholder
     67 |         $this->vars['placeholder'] = isset($this->config->placeholder)
     68 |             ? htmlspecialchars($this->config->placeholder)
     69 |             : "Enter tags...";
     70 | 
     71 |         // Prepopulated tags
     72 |         $tags = [];
     73 |         if (is_array($this->model->$fieldName) && count($this->model->$fieldName)) {
     74 |             foreach ($this->model->$fieldName as $tag)
     75 |                 $tags[] = htmlspecialchars($tag);
     76 |         } else if ($loadValue = $this->getLoadValue()) {
     77 |             $loadValue = json_decode($loadValue, true);
     78 |             if ($loadValue && is_array($loadValue)) {
     79 |                 foreach ($loadValue as $tag) {
     80 |                     if (empty($tag))
     81 |                         continue;
     82 |                     $tags[] = htmlspecialchars($tag);
     83 |                 }
     84 |             }
     85 |         }
     86 | 
     87 |         $this->vars['tags'] = $tags;
     88 |     }
     89 | 
     90 |     /**
     91 |      * Load widget assets
     92 |      */
     93 |     public function loadAssets()
     94 |     {
     95 |         $this->addJs('js/tagbox.js');
     96 | 
     97 |         if (isset($this->config->cssPath) && $this->config->cssPath)
     98 |             $this->addCss($this->config->cssPath);
     99 | 
    100 |         elseif (!isset($this->config->cssPath))
    101 |             $this->addCss('css/tagbox.css');
    102 |     }
    103 | 
    104 |     /**
    105 |      * Return save value
    106 |      * @return  array
    107 |      */
    108 |     public function getSaveValue($value)
    109 |     {
    110 |         $data   = [];
    111 |         foreach (post($this->fieldName) as $field) {
    112 |             if (empty($field))
    113 |                 continue;
    114 |             $data[] = $field;
    115 |         }
    116 | 
    117 |         return $data;
    118 |     }
    119 | 
    120 | }
    121 | 
    
    
    --------------------------------------------------------------------------------
    /src/FormWidgets/Tagbox/widget/assets/js/tagbox.js:
    --------------------------------------------------------------------------------
      1 | +function ($) { "use strict";
      2 | 
      3 |     /**
      4 |      * Constructor
      5 |      */
      6 |     var tagbox = function (el, config) {
      7 |         this.$el        = $(el)
      8 | 
      9 |         this.alias      = this.$el.data('alias')
     10 |         this.config     = this.$el.data('config')
     11 |         this.$tags      = this.$el.find('[data-control="tags"]')
     12 |         this.$list      = this.$el.find('[data-control="list"]')
     13 |         this.$last      = this.$el.find('[data-control="last"]')
     14 |         this.$input     = this.$el.find('[data-control="tagbox-input"]')
     15 |         this.$template  = this.$el.find('[data-control="template"]')
     16 | 
     17 |         this.init()
     18 |     }
     19 | 
     20 |     /**
     21 |      * Listen for editor events
     22 |      */
     23 |     tagbox.prototype.init = function () {
     24 |         var self = this
     25 | 
     26 |         // Listen for break keys
     27 |         this.$input.unbind().on('keydown', function(e) {
     28 |             var code = e.keyCode || e.which
     29 |             if ($.inArray(code, self.config.breakCodes) !== -1) {
     30 |                 e.preventDefault()
     31 |                 self.addTag($(this).val())
     32 |             }
     33 | 
     34 |             // Listen for backspace removals
     35 |             if (code == 8 && $(this).val() === '') {
     36 |                 self.backspaceRemove()
     37 |                 return false
     38 |             }
     39 | 
     40 |             // Remove any pre-delete classes
     41 |             self.$list.find('.pre-delete').removeClass('pre-delete')
     42 |         })
     43 | 
     44 |         // Filter input
     45 |         this.$input.on('change keydown keyup paste', function() {
     46 |             self.filterInput()
     47 |         })
     48 | 
     49 |         // Listen for tag removals through X button
     50 |         this.$list.on('click', '[data-control="remove"]', function() {
     51 |             $(this).closest('li').remove()
     52 |         })
     53 | 
     54 |         // Focus the cursor in the input box
     55 |         if (this.config.autofocus) {
     56 |             this.$list.on('click', function() {
     57 |                 self.$input.focus()
     58 |             })
     59 |             this.$input.on('focus', function() {
     60 |                 self.$list.addClass('focused')
     61 |             })
     62 |             this.$input.on('blur', function() {
     63 |                 self.$list.removeClass('focused')
     64 |             })
     65 |         }
     66 |     }
     67 | 
     68 |     /**
     69 |      * Removes invalid characters from input
     70 |      */
     71 |     tagbox.prototype.filterInput = function() {
     72 |         var filter = new RegExp(this.config.filter, 'g'),
     73 |             original = this.$input.val()
     74 | 
     75 |         this.$input.val(original.replace(filter, ''))
     76 |     }
     77 | 
     78 |     /**
     79 |      * Add tag to list
     80 |      */
     81 |     tagbox.prototype.addTag = function(tag) {
     82 |         this.filterInput()
     83 | 
     84 |         if (typeof tag != 'undefined' && !tag.length) {
     85 |             return false
     86 |         }
     87 | 
     88 |         // Validate the tag
     89 |         if (this.config.validation && this.validation(tag) == false) {
     90 |             $.oc.flashMsg({
     91 |                 text: this.config.validationMessage,
     92 |                 'class': 'error',
     93 |                 'interval': 3
     94 |             })
     95 |             this.$input.focus()
     96 |             return false
     97 |         }
     98 | 
     99 |         // Sluggify the tag
    100 |         if (this.config.slugify) {
    101 |             tag = tag.toLowerCase()
    102 |                 .replace(/\s+/g, '-')           // Replace spaces with hyphens
    103 |                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
    104 |                 .replace(/\-\-+/g, '-')         // Replace multiple hyphens
    105 |                 .replace(/^-+/, '')             // Trim hyphens from start of text
    106 |                 .replace(/-+$/, '')             // Trim hyphens from end of text
    107 |         }
    108 | 
    109 |         var cleanTag = tag
    110 |                 .replace(/&/g, "&")
    111 |                 .replace(//g, ">")
    113 |                 .replace(/"/g, """)
    114 |                 .replace(/'/g, "'"),
    115 |             $exists = this.$list.find('[data-tag="' + cleanTag + '"]'),
    116 |             $newTag = $(this.$template.html())
    117 | 
    118 |         // Make sure the tag doesn't already exist
    119 |         if ($exists.length) {
    120 |             this.flash($exists)
    121 |             return false
    122 |         }
    123 | 
    124 |         // Add the new tag and clear the input box
    125 |         $newTag.attr('data-tag', cleanTag)
    126 |         $newTag.find('input').val(tag)
    127 |         $newTag.find('[data-control="display"]').html(cleanTag)
    128 | 
    129 |         this.$last.before($newTag)
    130 |         this.$input.val('')
    131 |     }
    132 | 
    133 |     /**
    134 |      * Removes a tag with the backspace key
    135 |      */
    136 |     tagbox.prototype.backspaceRemove = function() {
    137 |         var $target = this.$list.find('li:nth-last-child(2)')
    138 | 
    139 |         if ($target.hasClass('pre-delete')) {
    140 |             $target.remove()
    141 |         } else {
    142 |             $target.addClass('pre-delete')
    143 |         }
    144 |     }
    145 | 
    146 |     /**
    147 |      * Temporarily adds the flash class to a tag
    148 |      * @param  element  $tag
    149 |      */
    150 |     tagbox.prototype.flash = function($tag) {
    151 |         $tag.addClass('flash');
    152 |         setTimeout(function() {
    153 |             $tag.removeClass('flash');
    154 |         }, 300)
    155 |     }
    156 | 
    157 |     /**
    158 |      * Validates a tag
    159 |      * @return  boolean
    160 |      */
    161 |     tagbox.prototype.validation = function(tag) {
    162 |         var expression = new RegExp(this.config.validation)
    163 |         return expression.test(tag)
    164 |     }
    165 | 
    166 |     /*
    167 |      * Bind and construct non-conflicting tagbox
    168 |      */
    169 |     var old = $.fn.tagbox
    170 | 
    171 |     $.fn.tagbox = function (config) {
    172 |         return new tagbox($(this), config)
    173 |     }
    174 | 
    175 |     $.fn.tagbox.Constructor = tagbox
    176 | 
    177 |     $.fn.tagbox.noConflict = function () {
    178 |         $.fn.tagbox = old
    179 |         return this
    180 |     }
    181 | 
    182 | }(window.jQuery);
    183 | 
    
    
    --------------------------------------------------------------------------------
    /src/FormWidgets/Password/widget/assets/js/password.min.js:
    --------------------------------------------------------------------------------
    1 | (function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],factory)}else if(typeof exports==="object"){factory(require("jquery"))}else{factory(jQuery)}})(function($,undef){var dataKey="plugin_hideShowPassword",shorthandArgs=["show","innerToggle"],SPACE=32,ENTER=13;var canSetInputAttribute=function(){var body=document.body,input=document.createElement("input"),result=true;if(!body){body=document.createElement("body")}input=body.appendChild(input);try{input.setAttribute("type","text")}catch(e){result=false}body.removeChild(input);return result}();var defaults={show:"infer",innerToggle:false,enable:canSetInputAttribute,className:"hideShowPassword-field",initEvent:"hideShowPasswordInit",changeEvent:"passwordVisibilityChange",props:{autocapitalize:"off",autocomplete:"off",autocorrect:"off",spellcheck:"false"},toggle:{element:'