diff --git a/LICENCE.md b/LICENCE.md deleted file mode 100644 index d68943e..0000000 --- a/LICENCE.md +++ /dev/null @@ -1,19 +0,0 @@ -# MIT license - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..72ec8ea --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,5 @@ +# License + +This plugin is an official extension of the October CMS platform and is free to use if you have a platform license. + +See End User License Agreement at https://octobercms.com/eula for more details. diff --git a/Plugin.php b/Plugin.php index e95fcda..51ad8d8 100644 --- a/Plugin.php +++ b/Plugin.php @@ -2,84 +2,99 @@ use Backend; use System\Classes\PluginBase; +use System\Classes\SettingsManager; /** - * Location Plugin Information File + * Plugin Information File */ class Plugin extends PluginBase { /** - * Returns information about this plugin. - * - * @return array + * pluginDetails returns information about this plugin. */ public function pluginDetails() { return [ - 'name' => 'rainlab.location::lang.plugin.name', - 'description' => 'rainlab.location::lang.plugin.description', - 'author' => 'Alexey Bobkov, Samuel Georges', - 'icon' => 'icon-globe', - 'homepage' => 'https://github.com/rainlab/location-plugin' + 'name' => "Location", + 'description' => "Location based features, such as Country and State.", + 'author' => 'Alexey Bobkov, Samuel Georges', + 'icon' => 'icon-globe', + 'homepage' => 'https://github.com/rainlab/location-plugin' ]; } + /** + * registerSettings + */ public function registerSettings() { return [ 'location' => [ - 'label' => 'rainlab.location::lang.locations.menu_label', - 'description' => 'rainlab.location::lang.locations.menu_description', - 'category' => 'rainlab.location::lang.plugin.name', - 'icon' => 'icon-globe', - 'url' => Backend::url('rainlab/location/locations'), - 'order' => 500, + 'label' => "Countries & States", + 'description' => "Manage available user countries and states.", + 'category' => SettingsManager::CATEGORY_USERS, + 'icon' => 'icon-globe', + 'url' => Backend::url('rainlab/location/locations'), + 'order' => 700, 'permissions' => ['rainlab.location.access_settings'], - 'keywords' => 'country, countries, state', + 'keywords' => 'country, countries, state', ], 'settings' => [ - 'label' => 'rainlab.location::lang.settings.menu_label', - 'description' => 'rainlab.location::lang.settings.menu_description', - 'category' => 'rainlab.location::lang.plugin.name', - 'icon' => 'icon-map-signs', - 'class' => 'RainLab\Location\Models\Setting', - 'order' => 600, + 'label' => "Location Settings", + 'description' => "Manage location based settings.", + 'category' => SettingsManager::CATEGORY_USERS, + 'icon' => 'icon-map-signs', + 'class' => \RainLab\Location\Models\Setting::class, + 'order' => 800, 'permissions' => ['rainlab.location.access_settings'], ] ]; } + /** + * registerPermissions + */ public function registerPermissions() { return [ - 'rainlab.location.access_settings' => ['tab' => 'rainlab.location::lang.plugin.name', 'label' => 'rainlab.location::lang.permissions.settings'], + 'rainlab.location.access_settings' => [ + 'tab' => "Location", + 'label' => "Locations management" + ], ]; } /** - * Register new Twig variables - * @return array + * registerMarkupTags registers new Twig variables */ public function registerMarkupTags() { return [ 'functions' => [ - 'form_select_country' => ['RainLab\Location\Models\Country', 'formSelect'], - 'form_select_state' => ['RainLab\Location\Models\State', 'formSelect'] + 'form_select_country' => [\RainLab\Location\Models\Country::class, 'formSelect'], + 'form_select_state' => [\RainLab\Location\Models\State::class, 'formSelect'] ] ]; } + + /** + * registerComponents + */ + public function registerComponents() + { + return [ + \RainLab\Location\Components\Location::class => 'location', + ]; + } + /** - * Registers any form widgets implemented in this plugin. + * registerFormWidgets registers any form widgets implemented in this plugin. */ public function registerFormWidgets() { return [ - 'RainLab\Location\FormWidgets\AddressFinder' => [ - 'label' => 'Address Finder', - 'code' => 'addressfinder' - ] + \RainLab\Location\FormWidgets\AddressFinder::class => 'addressfinder' ]; } } diff --git a/README.md b/README.md index aeea23d..665d593 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,23 @@ # Location plugin -This plugin adds location based features to [OctoberCMS](http://octobercms.com). +This plugin adds location based features to [October CMS](https://octobercms.com). * Easily add Country and State to any model * Form widget for address lookups (Google API) +View this plugin on the October CMS marketplace: + +- https://octobercms.com/plugin/rainlab-location + ### Extended features -To integrate locations with front-end users consider installing the [User Plus+ plugin](http://octobercms.com/plugin/rainlab-userplus) (`RainLab.UserPlus`). +To integrate locations with front-end users, consider also installing the `RainLab.UserPlus` plugin. + +- https://octobercms.com/plugin/rainlab-userplus ### Google API key requirement -As of June 22, 2016 the Google Maps service requires an API key. You may generate a key from the following link: +Using the Google Maps service requires an API key. You may generate a key from the following link: - [Get a Google API key](https://developers.google.com/maps/documentation/javascript/get-api-key) @@ -21,12 +27,16 @@ Copy the key and enter it in the **Settings > Location settings** area. If you f This plugin provides an easy way to add location fields, country and state, to any model. Simply add these columns to the database table: - $table->integer('country_id')->unsigned()->nullable()->index(); - $table->integer('state_id')->unsigned()->nullable()->index(); +```php +$table->integer('country_id')->unsigned()->nullable()->index(); +$table->integer('state_id')->unsigned()->nullable()->index(); +``` -Then implement the **RainLab.Location.Behaviors.LocationModel** behavior in the model class: +Then implement the **RainLab\Location\Traits\LocationModel** trait in the model class: - public $implement = ['RainLab.Location.Behaviors.LocationModel']; +```php +use \RainLab\Location\Traits\LocationModel; +``` This will automatically create two "belongs to" relationships: @@ -39,82 +49,69 @@ This will automatically create two "belongs to" relationships: You are free to add the following form field definitions: - country: - label: rainlab.location::lang.country.label - type: dropdown - span: left - placeholder: rainlab.location::lang.country.select - - state: - label: rainlab.location::lang.state.label - type: dropdown - span: right - dependsOn: country - placeholder: rainlab.location::lang.state.select +```yaml +country: + label: Country + type: dropdown + span: left + placeholder: -- select country -- + +state: + label: State + type: dropdown + span: right + dependsOn: country + placeholder: -- select state -- +``` #### Lists For the list column definitions, you can use the following snippet: - country: - label: rainlab.location::lang.country.label - searchable: true - relation: country - select: name - sortable: false - - state: - label: rainlab.location::lang.state.label - searchable: true - relation: state - select: name - sortable: false +```yaml +country: + label: Country + searchable: true + relation: country + select: name + sortable: false + +state: + label: State + searchable: true + relation: state + select: name + sortable: false +``` ### Front-end usage -The front-end can also use the relationships by creating a partial called **country-state** with the content: - - {% set countryId = countryId|default(form_value('country_id')) %} - {% set stateId = stateId|default(form_value('state_id')) %} - -
- - {{ form_select_country('country_id', countryId, { - id: 'accountCountry', - class: 'form-control', - emptyOption: '', - 'data-request': 'onInit', - 'data-request-update': { - 'country-state': '#partialCountryState' - } - }) }} -
- -
- - {{ form_select_state('state_id', countryId, stateId, { - id: 'accountState', - class: 'form-control', - emptyOption: '' - }) }} -
- -This partial can be rendered in a form with the following: - -
- {% partial 'country-state' countryId=user.country_id stateId=user.state_id %} -
- -### Short code accessors +The front-end can also use the relationships by rendering the `@form-select-country` and `@form-select-state` partials provided by the location component. Before proceeding, make sure you have the `location` component attached to the page or layout. + +```twig +
+ + {% partial '@form-select-country' countryId=user.country_id %} +
+ +
+ + {% partial '@form-select-state' countryId=user.country_id stateId=user.state_id %} +
+``` + +### Short Code Accessors The behavior will also add a special short code accessor and setter to the model that converts `country_code` and `state_code` to their respective identifiers. - // Softly looks up and sets the country_id and state_id - // for these Country and State relations. +```php +// Softly looks up and sets the country_id and state_id +// for these Country and State relations. - $model->country_code = "US"; - $model->state_code = "FL"; - $model->save(); +$model->country_code = "US"; +$model->state_code = "FL"; +$model->save(); +``` ### Address Finder Form Widget @@ -140,39 +137,45 @@ By default the `street` mapper places the house number before the street name. H Usage: - # =================================== - # Form Field Definitions - # =================================== - - fields: - address: - label: Address - type: addressfinder - countryRestriction: 'us,ch' - reverseStreetNumber: false - fieldMap: - latitude: latitude - longitude: longitude - city: city - zip: zip - street: street - country: country_code - state: state_code - vicinity: vicinity - - city: - label: City - zip: - label: Zip - street: - label: Street - country_code: - label: Country - state_code: - label: State - latitude: - label: Latitude - longitude: - label: Longitude - vicinity: - label: Vicinity +```yaml +# =================================== +# Form Field Definitions +# =================================== + +fields: + address: + label: Address + type: addressfinder + countryRestriction: 'us,ch' + reverseStreetNumber: false + fieldMap: + latitude: latitude + longitude: longitude + city: city + zip: zip + street: street + country: country_code + state: state_code + vicinity: vicinity + + city: + label: City + zip: + label: Zip + street: + label: Street + country_code: + label: Country + state_code: + label: State + latitude: + label: Latitude + longitude: + label: Longitude + vicinity: + label: Vicinity +``` + +### License + +This plugin is an official extension of the October CMS platform and is free to use if you have a platform license. See [EULA license](LICENSE.md) for more details. diff --git a/behaviors/LocationModel.php b/behaviors/LocationModel.php index 13c30af..f2058a7 100644 --- a/behaviors/LocationModel.php +++ b/behaviors/LocationModel.php @@ -5,55 +5,60 @@ use System\Classes\ModelBehavior; /** - * Location model extension + * LocationModel extension adds Country and State relations to a model * - * Adds Country and State relations to a model + * Usage in the model class definition: * - * Usage: - * - * In the model class definition: - * - * public $implement = ['RainLab.Location.Behaviors.LocationModel']; + * public $implement = [\RainLab\Location\Behaviors\LocationModel::class]; * */ class LocationModel extends ModelBehavior { /** - * Constructor + * __construct */ public function __construct($model) { parent::__construct($model); - $guarded = $model->getGuarded(); + $model->addFillable([ + 'country', + 'country_id', + 'country_code', + 'state', + 'state_id', + 'state_code' + ]); - if (count($guarded) === 1 && $guarded[0] === '*') { - $model->addFillable([ - 'country', - 'country_id', - 'country_code', - 'state', - 'state_id', - 'state_code' - ]); - } + $model->belongsTo['country'] = [ + Country::class, + 'replicate' => false + ]; - $model->belongsTo['country'] = ['RainLab\Location\Models\Country']; - $model->belongsTo['state'] = ['RainLab\Location\Models\State']; + $model->belongsTo['state'] = [ + State::class, + 'replicate' => false + ]; } + /** + * getCountryOptions + */ public function getCountryOptions() { return Country::getNameList(); } + /** + * getStateOptions + */ public function getStateOptions() { return State::getNameList($this->model->country_id); } /** - * Sets the "country" relation with the code specified, model lookup used. + * setCountryCodeAttribute sets the "country" relation with the code specified, model lookup used. * @param string $code */ public function setCountryCodeAttribute($code) @@ -66,7 +71,7 @@ public function setCountryCodeAttribute($code) } /** - * Sets the "state" relation with the code specified, model lookup used. + * setStateCodeAttribute sets the "state" relation with the code specified, model lookup used. * @param string $code */ public function setStateCodeAttribute($code) @@ -79,7 +84,7 @@ public function setStateCodeAttribute($code) } /** - * Mutator for "country_code" attribute. + * getCountryCodeAttribute mutator for "country_code" attribute. * @return string */ public function getCountryCodeAttribute() @@ -88,7 +93,7 @@ public function getCountryCodeAttribute() } /** - * Mutator for "state_code" attribute. + * getStateCodeAttribute mutator for "state_code" attribute. * @return string */ public function getStateCodeAttribute() @@ -97,7 +102,7 @@ public function getStateCodeAttribute() } /** - * Ensure an integer value is set, otherwise nullable. + * setCountryIdAttribute ensures an integer value is set, otherwise nullable. */ public function setCountryIdAttribute($value) { @@ -105,7 +110,7 @@ public function setCountryIdAttribute($value) } /** - * Ensure an integer value is set, otherwise nullable. + * setStateIdAttribute ensures an integer value is set, otherwise nullable. */ public function setStateIdAttribute($value) { diff --git a/components/Location.php b/components/Location.php new file mode 100644 index 0000000..4fc3556 --- /dev/null +++ b/components/Location.php @@ -0,0 +1,29 @@ + 'Location Component', + 'description' => 'No description provided yet...' + ]; + } + + /** + * availableCountries + */ + public function availableCountries() + { + return Country::getObjectList(); + } +} diff --git a/components/location/form-select-country.htm b/components/location/form-select-country.htm new file mode 100644 index 0000000..ef5de5f --- /dev/null +++ b/components/location/form-select-country.htm @@ -0,0 +1,18 @@ +{% set countryId = countryId|default(post('country_id')) %} +{% set uniqueId = post('country_unique_id', uniqueId|default('stateControlSelector')) %} +{% set countries = __SELF__.availableCountries|default([]) %} + + diff --git a/components/location/form-select-state.htm b/components/location/form-select-state.htm new file mode 100644 index 0000000..4bfef81 --- /dev/null +++ b/components/location/form-select-state.htm @@ -0,0 +1,25 @@ +{% set countryId = post('country_id', countryId|default(null)) %} +{% set stateId = post('state_id', stateId|default(null)) %} +{% set uniqueId = post('country_unique_id', uniqueId|default('stateControlSelector')) %} +{% set countries = __SELF__.availableCountries|default([]) %} +{% set country = countries.find(countryId)|default(countries.first()) %} +{% set states = country.fetchStates() %} +{% set optionsOnly = post('country_unique_id') %} + +{% if optionsOnly %} + + {% for state in states %} + + {% endfor %} +{% else %} + +{% endif %} diff --git a/components/partials/country-state.htm b/components/partials/country-state.htm deleted file mode 100644 index c680756..0000000 --- a/components/partials/country-state.htm +++ /dev/null @@ -1,24 +0,0 @@ -{% set countryId = countryId|default(form_value('country_id')) %} -{% set stateId = stateId|default(form_value('state_id')) %} - -
- - {{ form_select_country('country_id', countryId, { - id: 'accountCountry', - class: 'form-control', - emptyOption: '', - 'data-request': 'onInit', - 'data-request-update': { - (__SELF__ ~ '::country-state'): '#partialCountryState' - } - }) }} -
- -
- - {{ form_select_state('state_id', countryId, stateId, { - id: 'accountState', - class: 'form-control', - emptyOption: '' - }) }} -
diff --git a/composer.json b/composer.json index 54c28a8..ca50c5f 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "description": "Location plugin for October CMS", "homepage": "https://octobercms.com/plugin/rainlab-location", "keywords": ["october", "octobercms", "location"], - "license": "MIT", + "license": "proprietary", "authors": [ { "name": "Alexey Bobkov", @@ -18,7 +18,7 @@ } ], "require": { - "php": ">=5.5.9", + "php": "^8.0.2", "composer/installers": "~1.0" }, "minimum-stability": "dev" diff --git a/controllers/Locations.php b/controllers/Locations.php index ce02269..316689c 100644 --- a/controllers/Locations.php +++ b/controllers/Locations.php @@ -3,37 +3,49 @@ use Lang; use Flash; use Backend; -use BackendMenu; use RainLab\Location\Models\Country; use RainLab\Location\Models\State; -use Backend\Classes\Controller; -use System\Classes\SettingsManager; +use Backend\Classes\SettingsController; use Exception; /** * Locations Backend Controller */ -class Locations extends Controller +class Locations extends SettingsController { + /** + * @var array implement behaviors + */ public $implement = [ \Backend\Behaviors\FormController::class, \Backend\Behaviors\ListController::class, \Backend\Behaviors\RelationController::class ]; + /** + * @var array formConfig configuration. + */ public $formConfig = 'config_form.yaml'; + + /** + * @var array listConfig configuration. + */ public $listConfig = 'config_list.yaml'; - public $relationConfig = 'config_relation.yaml'; - public $requiredPermissions = ['rainlab.location.access_settings']; + /** + * @var array relationConfig configuration. + */ + public $relationConfig = 'config_relation.yaml'; - public function __construct() - { - parent::__construct(); + /** + * @var string settingsItemCode determines the settings code + */ + public $settingsItemCode = 'location'; - BackendMenu::setContext('October.System', 'system', 'settings'); - SettingsManager::setContext('RainLab.Location', 'location'); - } + /** + * @var array requiredPermissions to view this page. + */ + public $requiredPermissions = ['rainlab.location.access_settings']; /** * {@inheritDoc} @@ -45,6 +57,9 @@ public function listInjectRowClass($record, $definition = null) } } + /** + * relationExtendViewWidget + */ public function relationExtendViewWidget($widget) { $widget->bindEvent('list.injectRowClass', function ($record) { @@ -54,6 +69,24 @@ public function relationExtendViewWidget($widget) }); } + /** + * formExtendFields adds available permission fields to the User form. + * Mark default groups as checked for new Users. + */ + public function formExtendFields($form) + { + if ($codeField = $form->getField('iso_code')) { + $codeField->commentAbove(str_replace('%s', 'http://en.wikipedia.org/wiki/ISO_3166-1', $codeField->commentAbove)); + } + + if ($codeField = $form->getField('calling_code')) { + $codeField->commentAbove(str_replace('%s', 'https://en.wikipedia.org/wiki/List_of_country_calling_codes', $codeField->commentAbove)); + } + } + + /** + * onLoadDisableForm + */ public function onLoadDisableForm() { try { @@ -67,6 +100,9 @@ public function onLoadDisableForm() return $this->makePartial('disable_form'); } + /** + * onDisableLocations + */ public function onDisableLocations() { $enable = post('enable', false); @@ -95,15 +131,18 @@ public function onDisableLocations() } if ($enable) { - Flash::success(Lang::get('rainlab.location::lang.locations.enable_success')); + Flash::success(__("Successfully enabled those locations.")); } else { - Flash::success(Lang::get('rainlab.location::lang.locations.disable_success')); + Flash::success(__("Successfully disabled those locations.")); } return redirect()->refresh(); } + /** + * onLoadUnpinForm + */ public function onLoadUnpinForm() { try { @@ -116,6 +155,9 @@ public function onLoadUnpinForm() return $this->makePartial('unpin_form'); } + /** + * onUnpinLocations + */ public function onUnpinLocations() { $pin = post('pin', false); @@ -134,10 +176,10 @@ public function onUnpinLocations() } if ($pin) { - Flash::success(Lang::get('rainlab.location::lang.locations.pin_success')); + Flash::success(__("Successfully pinned selected locations.")); } else { - Flash::success(Lang::get('rainlab.location::lang.locations.unpin_success')); + Flash::success(__("Successfully unpinned selected locations.")); } return Backend::redirect('rainlab/location/locations'); diff --git a/controllers/locations/_disable_form.htm b/controllers/locations/_disable_form.htm deleted file mode 100644 index 6c66d19..0000000 --- a/controllers/locations/_disable_form.htm +++ /dev/null @@ -1,56 +0,0 @@ - 'disableForm']) ?> - - - - - - diff --git a/controllers/locations/_disable_form.php b/controllers/locations/_disable_form.php new file mode 100644 index 0000000..b6c1c41 --- /dev/null +++ b/controllers/locations/_disable_form.php @@ -0,0 +1,46 @@ + 'disableForm']) ?> + + + + + diff --git a/controllers/locations/_list_toolbar.htm b/controllers/locations/_list_toolbar.htm deleted file mode 100644 index 8cce6bb..0000000 --- a/controllers/locations/_list_toolbar.htm +++ /dev/null @@ -1,36 +0,0 @@ -
- - - - - -
diff --git a/controllers/locations/_list_toolbar.php b/controllers/locations/_list_toolbar.php new file mode 100644 index 0000000..481213e --- /dev/null +++ b/controllers/locations/_list_toolbar.php @@ -0,0 +1,18 @@ +
+ icon('icon-plus') + ->primary() ?> + + ajaxData(['location_type' => 'country']) + ->listCheckedTrigger() + ->listCheckedRequest() + ->icon('icon-magic') + ->secondary() ?> + + listCheckedTrigger() + ->listCheckedRequest() + ->icon('icon-map-pin') + ->secondary() ?> +
diff --git a/controllers/locations/_states_list_toolbar.htm b/controllers/locations/_states_list_toolbar.htm deleted file mode 100644 index dae8d0f..0000000 --- a/controllers/locations/_states_list_toolbar.htm +++ /dev/null @@ -1,40 +0,0 @@ -
- - trans('rainlab.location::lang.state.label')])) ?> - - - - - -
diff --git a/controllers/locations/_states_list_toolbar.php b/controllers/locations/_states_list_toolbar.php new file mode 100644 index 0000000..0a46c2a --- /dev/null +++ b/controllers/locations/_states_list_toolbar.php @@ -0,0 +1,19 @@ +
+ icon('icon-file') + ->secondary() ?> + + ajaxData(['location_type' => 'country']) + ->listCheckedTrigger() + ->listCheckedRequest() + ->icon('icon-magic') + ->secondary() ?> + + listCheckedTrigger() + ->listCheckedRequest() + ->confirmMessage(__("Do you really want to delete this location?")) + ->icon('icon-map-pin') + ->secondary() ?> +
diff --git a/controllers/locations/_unpin_form.htm b/controllers/locations/_unpin_form.php similarity index 61% rename from controllers/locations/_unpin_form.htm rename to controllers/locations/_unpin_form.php index 235a058..bfbf21c 100644 --- a/controllers/locations/_unpin_form.htm +++ b/controllers/locations/_unpin_form.php @@ -1,7 +1,7 @@ 'unpinForm']) ?> -