builder settings

This commit is contained in:
Kerim 2023-06-18 17:28:15 +05:00
parent 37498c7054
commit 402951cbe0
318 changed files with 9747 additions and 16624 deletions

View File

@ -10,7 +10,7 @@
"laravel/framework": "^9.0",
"october/all": "^3.3",
"rainlab/pages-plugin": "^1.5",
"rainlab/builder-plugin": "^2.0",
"rainlab/builder-plugin": "^1.2.5",
"blakejones/magicforms-plugin": "^1.6",
"rainlab/translate-plugin": "^1.0"
},

View File

@ -1,3 +1,19 @@
# License
# MIT license
See End User License Agreement at https://octobercms.com/eula
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.

View File

@ -1,66 +1,51 @@
<?php namespace RainLab\Builder;
use Lang;
use Event;
use Lang;
use Backend;
use System\Classes\PluginBase;
use System\Classes\CombineAssets;
use RainLab\Builder\Classes\StandardControlsRegistry;
use RainLab\Builder\Classes\StandardBehaviorsRegistry;
use RainLab\Builder\Classes\StandardBlueprintsRegistry;
use RainLab\Builder\Rules\Reserved;
use Doctrine\DBAL\Types\Type as DoctrineType;
use Validator;
/**
* Plugin registration file
*/
class Plugin extends PluginBase
{
/**
* pluginDetails
*/
public function pluginDetails()
{
return [
'name' => "Builder",
'description' => "Provides visual tools for building October plugins.",
'name' => 'rainlab.builder::lang.plugin.name',
'description' => 'rainlab.builder::lang.plugin.description',
'author' => 'Alexey Bobkov, Samuel Georges',
'icon' => 'icon-wrench',
'homepage' => 'https://github.com/rainlab/builder-plugin'
];
}
/**
* registerComponents
*/
public function registerComponents()
{
return [
\RainLab\Builder\Components\RecordList::class => 'builderList',
\RainLab\Builder\Components\RecordDetails::class => 'builderDetails'
'RainLab\Builder\Components\RecordList' => 'builderList',
'RainLab\Builder\Components\RecordDetails' => 'builderDetails'
];
}
/**
* registerPermissions
*/
public function registerPermissions()
{
return [
'rainlab.builder.manage_plugins' => [
'tab' => "Builder",
'label' => 'rainlab.builder::lang.plugin.manage_plugins'
]
'tab' => 'rainlab.builder::lang.plugin.name',
'label' => 'rainlab.builder::lang.plugin.manage_plugins']
];
}
/**
* registerNavigation
*/
public function registerNavigation()
{
return [
'builder' => [
'label' => "Builder",
'label' => 'rainlab.builder::lang.plugin.name',
'url' => Backend::url('rainlab/builder'),
'icon' => 'icon-wrench',
'iconSvg' => 'plugins/rainlab/builder/assets/images/builder-icon.svg',
@ -73,63 +58,49 @@ class Plugin extends PluginBase
'label' => 'rainlab.builder::lang.database.menu_label',
'icon' => 'icon-hdd-o',
'url' => 'javascript:;',
'attributes' => ['data-menu-item' => 'database'],
'attributes' => ['data-menu-item'=>'database'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'models' => [
'label' => 'rainlab.builder::lang.model.menu_label',
'icon' => 'icon-random',
'url' => 'javascript:;',
'attributes' => ['data-menu-item' => 'models'],
'attributes' => ['data-menu-item'=>'models'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'permissions' => [
'label' => 'rainlab.builder::lang.permission.menu_label',
'icon' => 'icon-unlock-alt',
'url' => 'javascript:;',
'attributes' => ['data-no-side-panel' => 'true', 'data-builder-command' => 'permission:cmdOpenPermissions', 'data-menu-item' => 'permissions'],
'url' => '#',
'attributes' => ['data-no-side-panel'=>'true', 'data-builder-command'=>'permission:cmdOpenPermissions', 'data-menu-item'=>'permissions'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'menus' => [
'label' => 'rainlab.builder::lang.menu.menu_label',
'icon' => 'icon-location-arrow',
'url' => 'javascript:;',
'attributes' => ['data-no-side-panel' => 'true', 'data-builder-command' => 'menus:cmdOpenMenus', 'data-menu-item' => 'menus'],
'attributes' => ['data-no-side-panel'=>'true', 'data-builder-command'=>'menus:cmdOpenMenus', 'data-menu-item'=>'menus'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'controllers' => [
'label' => 'rainlab.builder::lang.controller.menu_label',
'icon' => 'icon-asterisk',
'url' => 'javascript:;',
'attributes' => ['data-menu-item' => 'controllers'],
'attributes' => ['data-menu-item'=>'controllers'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'versions' => [
'label' => 'rainlab.builder::lang.version.menu_label',
'icon' => 'icon-code-fork',
'url' => 'javascript:;',
'attributes' => ['data-menu-item' => 'version'],
'attributes' => ['data-menu-item'=>'version'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'localization' => [
'label' => 'rainlab.builder::lang.localization.menu_label',
'icon' => 'icon-globe',
'url' => 'javascript:;',
'attributes' => ['data-menu-item' => 'localization'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'code' => [
'label' => 'Code',
'icon' => 'icon-file-code-o',
'url' => 'javascript:;',
'attributes' => ['data-menu-item' => 'code'],
'permissions' => ['rainlab.builder.manage_plugins']
],
'imports' => [
'label' => 'Import',
'icon' => 'icon-arrow-circle-down',
'url' => 'javascript:;',
'attributes' => ['data-no-side-panel' => 'true', 'data-builder-command' => 'imports:cmdOpenImports', 'data-menu-item' => 'imports'],
'attributes' => ['data-menu-item'=>'localization'],
'permissions' => ['rainlab.builder.manage_plugins']
]
]
@ -138,9 +109,6 @@ class Plugin extends PluginBase
];
}
/**
* registerSettings
*/
public function registerSettings()
{
return [
@ -168,10 +136,6 @@ class Plugin extends PluginBase
new StandardBehaviorsRegistry($behaviorLibrary);
});
Event::listen('pages.builder.registerTailorBlueprints', function ($blueprintLibrary) {
new StandardBlueprintsRegistry($blueprintLibrary);
});
// Register reserved keyword validation
Event::listen('translator.beforeResolve', function ($key, $replaces, $locale) {
if ($key === 'validation.reserved') {
@ -179,6 +143,15 @@ class Plugin extends PluginBase
}
});
// Compatibility with v1 legacy
if (!class_exists('System')) {
Validator::extend('reserved', Reserved::class);
Validator::replacer('reserved', function ($message, $attribute, $rule, $parameters) {
// Fixes lowercase attribute names in the new plugin modal form
return ucfirst($message);
});
}
else {
$this->callAfterResolving('validator', function ($validator) {
$validator->extend('reserved', Reserved::class);
$validator->replacer('reserved', function ($message, $attribute, $rule, $parameters) {
@ -186,10 +159,24 @@ class Plugin extends PluginBase
return ucfirst($message);
});
});
}
// Register doctrine types
if (!DoctrineType::hasType('timestamp')) {
DoctrineType::addType('timestamp', \RainLab\Builder\Classes\Doctrine\TimestampType::class);
}
}
/**
* register
*/
public function register()
{
/*
* Register asset bundles
*/
CombineAssets::registerCallback(function ($combiner) {
$combiner->registerBundle('$/rainlab/builder/assets/js/build.js');
});
}
}

View File

@ -1,76 +1,48 @@
Builder is a visual development tool. It shortens plugin development time by automating common development tasks and makes programming fun again. With Builder you can create a fully functional plugin scaffold in a matter of minutes.
Builder makes the learning curve less steep by providing a visual interface that naturally incorporates October's design patterns and documentation. Heres an example, instead of looking into the documentation for a list of supported form controls and their features, you can just open the Form Builder, find a suitable control in the Control Palette, add the control to the form and explore its properties with the visual inspector.
Builder makes the learning curve less steep by providing a visual interface that naturally incorporates Octobers design patterns and documentation. Heres an example, instead of looking into the documentation for a list of supported form controls and their features, you can just open the Form Builder, find a suitable control in the Control Palette, add the control to the form and explore its properties with the visual inspector.
Builder implements a Rapid Application Development process that automates the boring activities without sacrificing complete control. With this tool you can spend more time implementing the plugin's business logic in your favorite code editor rather than dealing with the more mundane tasks, like building forms or managing plugin versions.
Plugins created with the help of Builder are no different to plugins that you would usually create by hand. That means that you can continue to use your usual “hands on” workflow for updating your servers, managing the code versions and sharing work with your teammates.
**What's New!** Builder has been revamped to support October CMS v3, including dark mode, updated specifications, a Tailor import tool, and a new inline code editor.
## Requirements
- October CMS 3.3 or above
### Installation
Run the following to install this plugin:
```bash
php artisan plugin:install RainLab.Builder
```
To uninstall this plugin:
```bash
php artisan plugin:remove RainLab.Builder
```
If you are using October CMS v1 or v2, install v1.2 with the following commands:
```bash
composer require rainlab/builder-plugin "^1.2"
```
## Video Tutorial
## Video tutorial
We also recorded a video tutorial showing how to use the plugin to build a simple library plugin: [watch the video](https://vimeo.com/154415433).
## What You Can Do with Builder
## What you can do with Builder
This tool includes multiple features that cover almost all aspects of creating a plugin.
- Initializing a new plugin - this creates the plugin directory along with any necessary files.
- Creating and editing plugin database tables. All schema changes are saved as regular migration files, so you can easily update the plugin on other servers using your regular workflow.
- Creating model classes.
- Creating backend forms with the visual Form Builder.
- Creating backend lists.
- Managing a list of user permissions provided by the plugin.
- Creating plugin backend navigation - in the form of main menu items and sidebar items.
- Creating backend controllers and configuring their behaviors with a visual tool.
- Managing plugin versions and updates.
- Managing plugin localization files.
- A set of universal components - used for displaying data from the plugin on the front-end in form of lists and single record details.
- Managing raw code files directly in the backend (**New in v2**).
- Converting [Tailor blueprints](https://docs.octobercms.com/3.x/cms/tailor/blueprints.html) to plugin files (**New in v2**).
* Initializing a new plugin - this creates the plugin directory along with any necessary files.
* Creating and editing plugin database tables. All schema changes are saved as regular migration files, so you can easily update the plugin on other servers using your regular workflow.
* Creating model classes.
* Creating back-end forms with the visual Form Builder.
* Creating back-end lists.
* Managing a list of user permissions provided by the plugin.
* Creating plugin back-end navigation - in the form of main menu items and sidebar items.
* Creating back-end controllers and configuring their behaviors with a visual tool.
* Managing plugin versions and updates.
* Managing plugin localization files.
* A set of universal components - used for displaying data from the plugin on the front-end in form of lists and single record details.
Put simply, you can create a multilingual plugin, that installs database tables, has backend lists and forms protected with user permissions, and adds CMS pages for displaying data managed with the plugin. After learning how Builder works, this process takes just a few minutes.
Put simply, you can create a multilingual plugin, that installs database tables, has back-end lists and forms protected with user permissions, and adds CMS pages for displaying data managed with the plugin. After learning how Builder works, this process takes just a few minutes.
Builder is a productivity tool, it doesn't completely replace coding by hand and doesn't include a code editor for editing PHP files (the only exception is the version management interface). Builder never overwrites or deletes plugin PHP files, so you can rest assured knowing that the code you write never gets touched by Builder. However, Builder can create new PHP files, like models and controllers.
Most of the visual editors in Builder work with YAML configuration files, which are a native concept in October CMS. For example, after creating a model class in Builder, you can choose to add a form to the model. This operation creates a YAML file in the model's directory.
Most of the visual editors in Builder work with YAML configuration files, which are a native concept in OctoberCMS. For example, after creating a model class in Builder, you can choose to add a form to the model. This operation creates a YAML file in the model's directory.
There are currently some limitations when using Builder. Some of them are missing features which will be added later. Others are ideas intentionally omitted to keep the things simple. As mentioned above, Builder doesn't want to replace coding, while at the same time, it doesn't go too far with visual programming either. The limitations are explained and described in the corresponding sections of the plugin documentation. Those limitations don't mean you can't create any plugin you want - the good old approach to writing the code manually is always applicable for plugins developed with Builder. Builders aim is to be a modest, yet powerful tool that is used to accelerate your development cycle.
## Getting Started
## Getting started
Before you create your first plugin with Builder you should configure it. Open the Settings page in October CMS backend and find Builder in the side menu. Enter your author name and namespace. The author name and namespace are required fields and should not change if you wish to publish your plugins on October CMS Marketplace.
Before you create your first plugin with Builder you should configure it. Open the Settings page in OctoberCMS back-end and find Builder in the side menu. Enter your author name and namespace. The author name and namespace are required fields and should not change if you wish to publish your plugins on OctoberCMS Marketplace.
If you already have a Marketplace account, use your existing author name and namespace.
## Initializing a New Plugin
## Initializing a new plugin
On the Builder page in October CMS backend click the small arrow icon in the sidebar to expose the plugin list. After clicking the "Create plugin" button, enter the plugin name and namespace. The default author name and namespace can be pre-filled from the plugin settings. Select the plugin icon, enter the description text and plugin homepage URL (optional).
On the Builder page in OctoberCMS back-end click the small arrow icon in the sidebar to expose the plugin list. After clicking the "Create plugin" button, enter the plugin name and namespace. The default author name and namespace can be pre-filled from the plugin settings. Select the plugin icon, enter the description text and plugin homepage URL (optional).
Please note that you cannot change the namespaces after you create the plugin.
@ -89,13 +61,13 @@ authornamespace
plugin.yaml
```
The file **plugin.yaml** contains the basic plugin information - name, description, permissions and backend navigation. This file is managed by the Builder user interface.
The file **plugin.yaml** contains the basic plugin information - name, description, permissions and back-end navigation. This file is managed by the Builder user interface.
The initial contents of the **lang.php** localization file is the plugin name and description. The localization file is created in the default locale of your October installation.
When a new plugin is created, it's automatically selected as the current plugin that Builder works with. You can select another plugin in the plugin list if you need.
## Managing Plugin Database Tables
## Managing plugin database tables
Tables are managed on the Database tab of Builder. You can create tables, update their structure and delete tables with the visual interface.
@ -103,13 +75,13 @@ Click Add button to open the Create Table tab. Builder automatically generates p
Every time when you save changes in a table, Builder shows a popup window with the automatically generated migration PHP code. You can't edit the code in the popup, but you can inspect it or copy to the clipboard. After reviewing the migration, click Save & Apply button. Builder executes the migration immediately and saves the migration file to the plugin's **updates** directory. Afterwards you can find all plugin migrations on the Versions tab of Builder.
> **Important**: Although Builder generates migration files automatically, it can't prevent data loss in some cases when you significantly change the table structure. In some cases it's possible - for example, when you alter length of a string column. Always check the migration PHP code generated by Builder before applying the migration and consider possible consequences of running the migration in a production database.
> **Note:** Although Builder generates migration files automatically, it can't prevent the data loss in some cases when you significantly change the table structure. In some cases it's possible - for example, when you alter length of a string column. Always check the migration PHP code generated by Builder before applying the migration and consider possible consequences of running the migration in a production database.
Currently Builder doesn't allow to manage table indexes with the visual user interface. Unique column management is not supported yet as well. Please use the Version Management feature to manually create migration files.
Please note that the `enum` data type is not currently supported by the Builder due to limitations in the underlying Doctrine classes.
## Managing Models
## Managing models
You can edit models on the Models tab of Builder. Click the Add button, enter the model class name and select a database table from the drop-down list.
@ -117,15 +89,15 @@ The model class name should not contain the namespace. Some examples: Post, Prod
Please note that you cannot delete model files with builder because it would contradict the idea of not deleting or overwriting PHP files with the visual tool. If you need to delete a model, remove its files manually.
## Managing Backend Forms
## Managing back-end forms
In October CMS forms belong to models. For every model you can create as many backend forms as you need, but in most cases there is a single form per model.
In OctoberCMS forms belong to models. For every model you can create as many back-end forms as you need, but in most cases there is a single form per model.
> **Note**: When you create a form, it's not displayed in October CMS backend until you create a backend controller which uses the form. Read more about controllers below.
> **Note:** when you create a form, it's not displayed in OctoberCMS back-end until you create a back-end controller which uses the form. Read about controllers below.
On the Models tab in Builder find a model you want to create a form for. Expand the model if needed, hover the **Forms** section and click the plus sign.
Forms in October CMS are defined with YAML files. The default form file name is **fields.yaml**. In the Form Builder, click a placeholder and select a control from the popup list. After that you can click the control and edit it parameters in Inspector.
Forms in OctoberCMS are defined with YAML files. The default form file name is **fields.yaml**. In the Form Builder, click a placeholder and select a control from the popup list. After that you can click the control and edit it parameters in Inspector.
Almost all form controls have these common properties:
@ -134,63 +106,66 @@ Almost all form controls have these common properties:
* Comment - the comment text - fixed text or localization string key.
* Span - position of the control on the form - left, right, full or automatic placement.
Most of the properties have descriptive names or have a description in Inspector. If you need more information about control properties please refer to the [Documentation](https://docs.octobercms.com/3.x/element/form-fields.html).
Most of the properties have descriptive names or have a description in Inspector. If you need more information about control properties please refer to the [Documentation](http://octobercms.com/docs/backend/forms).
You can drag controls in the Form Builder to rearrange them or to move them to/from form tabs. A form tab should have at least one control, otherwise it will be ignored when Form Builder saves the YAML file.
You can drag controls in the Form Builder to rearrange them or to move them to/from form tabs.
Some form controls, for example the file upload control, require a relation to be created in the model class manually. The relation name should be entered in the Field name property. Please read the [Forms Documentation](https://docs.octobercms.com/3.x/element/form-fields.html) for details about specific form controls.
> **Note:** a form tab should have at least one control, otherwise it will be ignored when Form Builder saves the YAML file.
## Managing Backend Lists
Similarly to forms, backend lists in October CMS belong to models.
> **Note:** some form controls, for example the file upload control, require a relation to be created in the model class manually. The relation name should be entered in the Field name property. Please read the [Forms Documentation](http://octobercms.com/docs/backend/forms) for details about specific form controls.
> **Note**: When you create a list, it's not displayed in October CMS backend until you create a backend controller which uses the list. Read about controllers below.
## Managing back-end lists
Similarly to forms, back-end lists in OctoberCMS belong to models.
> **Note:** when you create a list, it's not displayed in OctoberCMS back-end until you create a back-end controller which uses the list. Read about controllers below.
On the Models tab in Builder find a model you want to create a list for. Expand the model if needed, hover the **Lists** section and click the plus sign.
Lists in October CMS are defined with YAML files. The default list file name is **columns.yaml**. The grid in the list editor contains list column definitions. Column property names are self descriptive, although some of them require some explanations. Refer to the [Lists documentation](https://docs.octobercms.com/3.x/element/list-columns.html) for details about each property.
Lists in OctoberCMS are defined with YAML files. The default list file name is **columns.yaml**. The grid in the list editor contains list column definitions. Column property names are self descriptive, although some of them require some explanations. Refer to the [Lists documentation](http://octobercms.com/docs/backend/lists#column-options) for details about each property.
For the Label property you can either enter a static string or create a new localization string.
The Field property column has an autocompletion feature attached. It allows you to select columns from the database table that is bound to the model. At the moment it doesn't show relation properties, but you can still type them in manually.
## Managing Plugin Permissions
## Managing plugin permissions
[Plugin permissions](https://docs.octobercms.com/3.x/extend/backend/users.html) define the features and backend plugin pages a user can access. You can manage permissions on the Permissions tab in Builder. For each permission you should specify a unique permission code, permission tab title and permission label. The tab title and label are displayed in the user management interface on the System page in October backend.
[Plugin permissions](http://octobercms.com/docs/backend/users) define what features and back-end plugin pages a user can access. You can manage permissions on the Permissions tab in Builder. For each permission you should specify a unique permission code, permission tab title and permission label. The tab title and label are displayed in the user management interface on the System page in October back-end.
For the tab title and label you can either enter a static string or create a new localization string.
Later, when you create controllers and menu items, you can select what permissions users should have in order to access or see those objects.
## Managing Backend Menus
## Managing back-end menus
The [plugin navigation](https://docs.octobercms.com/3.x/extend/backend/navigation.html) is managed on the Backend Menus tab of the Builder. The user interface allows to create top level menu items and sidebar items.
The [plugin navigation](http://octobercms.com/docs/plugin/registration#navigation-menus) is managed on the Backend Menus tab of the Builder. The user interface allows to create top level menu items and sidebar items.
To create a menu item click the placeholder rectangle and then click the new item to open Inspector. In the inspector you can enter the item label, select icon and assign user permissions. The **code** property is required for referring menu items from the controllers code (for marking menu items active).
> **Note**: When you create menu items for backend pages which don't exist yet in the plugin, it makes sense to leave the **URL** property empty until you create the plugin controllers. This property supports autocompletion, so you can just select your controller URLs from the drop-down list.
> **Note:** when you create menu items for back-end pages which don't exist yet in the plugin, it makes sense to leave the **URL** property empty until you create the plugin controllers. This property supports autocompletion, so you can just select your controller URLs from the drop-down list.
## Managing Backend Controllers, Forms and Lists
## Managing back-end controllers, forms and lists
Back-end pages in October CMS are provided with backend controllers. Usually backend pages contain lists and forms for managing plugin records, although you can create any custom controller.
Back-end pages in OctoberCMS are provided with back-end controllers. Usually back-end pages contain lists and forms for managing plugin records, although you can create any custom controller.
Please refer to the [backend forms](https://docs.octobercms.com/3.x/extend/forms/form-controller.html) and [lists](https://docs.octobercms.com/3.x/extend/lists/list-controller.html) documentation pages for more information about controller behaviors. Currently only List and Form behaviors can be configured with the Builder. If your controller contains other behaviors they won't be removed by the Builder, you just won't be able to edit them with the visual interface.
Please refer to the [back-end forms](http://octobercms.com/docs/backend/forms), [lists](http://octobercms.com/docs/backend/lists) and [reorder controller](http://octobercms.com/docs/backend/reorder) documentation pages for more information about controller behaviors. Currently only List, Form and Reorder Controller behaviors can be configured with the Builder. If your controller contains other behaviors they won't be removed by the Builder, you just won't be able to edit them with the visual interface.
Builder also allows you to create empty controller classes which don't implement any behaviors and customize them manually.
> **Note**: Some behaviors require specific model features to be implemented. For example, the Reorder Controller behavior requires the model to implement Sortable or NestedTree traits. Always refer to the specific behavior documentation for the implementation details.
> **Note:** Some behaviors require specific model features to be implemented. For example, the Reorder Controller behavior requires the model to implement Sortable or NestedTree traits. Always refer to the specific behavior documentation for the implementation details.
To create a controller, click the Add button list on the Controllers tab. Enter the controller class name, for example Posts.
If the controller is going to provide backend lists or forms, select a base model in the drop-down list and select behaviors you want to add. You can also select a top and sidebar menu items that should be active on the controller pages. If needed, choose permissions that users must have to access the controller pages.
If the controller is going to provide back-end lists or forms, select a base model in the drop-down list and select behaviors you want to add. You can also select a top and sidebar menu items that should be active on the controller pages. If needed, choose permissions that users must have to access the controller pages.
> Please note that the settings you enter in the Create Controller popup cannot be changed with Builder. However you can update them manually by editing controller classes.
After creating a controller you can configure its behaviors. Click the controller in list and then click a behavior you want to configure. When Builder creates a controller it tries to apply default configuration to the behaviors, however you might want to change it. Inspector lists displays all supported behavior properties. URL properties (like the list records URLs) are autocomplete fields and populated with URLs of the existing plugin controllers.
## Managing Plugin Versions
## Managing plugin versions
Please read the [Version History](https://docs.octobercms.com/3.x/extend/system/plugins.html#version-history) documentation page to understand how versioning works in October CMS.
Please read the [Version History](http://octobercms.com/docs/plugin/updates) documentation page to understand how versioning works in OctoberCMS.
Basically there are 3 types of version updates:
@ -206,19 +181,19 @@ For every version you should specify the new version number and description. Bui
When a version file is saved, Builder doesn't apply it immediately. You should click the "Apply version" button in the toolbar in order to apply the version and execute the update code (if applicable). You can also rollback already applied version updates, change their code and apply again. This allows you to edit database schema updates generated by Builder if you don't like the default code.
> **Note**: Your migration files should provide correct rollback code in the `down` method in order to use the rollback feature.
> Note that your migration files should provide correct rollback code in the `down` method in order to use the rollback feature.
When you rollback a version, it automatically rolls back all newer versions. When you apply a version, it automatically applies all pending older versions. Please remember that when a user logs into the backend, October automatically applies all pending updates. Never edit versions on a production server or on a server with multiple backend users - it could cause unpredictable consequences.
When you rollback a version, it automatically rolls back all newer versions. When you apply a version, it automatically applies all pending older versions. Please remember that when a user logs into the back-end, October automatically applies all pending updates. Never edit versions on a production server or on a server with multiple back-end users - it could cause unpredictable consequences.
Migrations that contain multiple scripts are not supported. They can't be created or edited with Builder.
## Managing Localization
## Managing localization
Localization files are managed on the Localization tab of the Builder. When a new plugin is initialized, a single language file is created. This file is created in the default system locale specified in October CMS configuration scripts.
Localization files are managed on the Localization tab of the Builder. When a new plugin is initialized, a single language file is created. This file is created in the default system locale specified in OctoberCMS configuration scripts.
You can create as many language files as you want. Builder UI always displays strings in the October CMS locale, so you might want to update your configuration files to see your plugin in another language.
You can create as many language files as you want. Builder UI always displays strings in the OctoberCMS locale, so you might want to update your configuration files to see your plugin in another language.
Please note that although [localization files in October CMS](https://docs.octobercms.com/3.x/extend/system/localization.html) are PHP scripts, they are translated to YAML to simplify the editing in the Builder user interface. When language files are saved, they are translated back to PHP again.
Please note that although [localization files in OctoberCMS](http://octobercms.com/docs/plugin/localization) are PHP scripts, they are translated to YAML to simplify the editing in the Builder user interface. When language files are saved, they are translated back to PHP again.
Builder tries to keep the user interface synchronized with your default language file. This means that when you save the language file, Builder automatically updates all localized strings in all editors. In some cases you might need to close and open Inspector in order to re-initialize the autocomplete fields.
@ -232,45 +207,17 @@ plugin:
If you create a new localization string from the Inspector or other editor while you have the default language file tab open in the Builder, it will try to update the tab contents or merge the updated file contents from the server. It's a good idea to keep the default localization file always saved in the Builder to avoid possible content conflicts when you edit localization from another place.
> **Tip**: In YAML a single quote is escaped with two single quotes (http://yaml.org/spec/current.html#id2534365).
> Protip: In YAML a single quote is escaped with two single quotes (http://yaml.org/spec/current.html#id2534365).
## Editing Code Files (New in v2)
Raw code and other files can be managed directly in the Code tab of the Builder. You can navigate to any file within the context of the selected plugin.
Use this to make code adjustments without the need for a code editor, which includes creating, moving, renaming and deleting files.
## Importing Tailor Blueprints (New in v2)
The Import tab of the Builder can generate scaffold files using [Tailor blueprints](https://docs.octobercms.com/3.x/cms/tailor/blueprints.html) as a source. In combination, Tailor and Builder work together to create a super-scaffolding tool since it can generate multiple files in one process. First, design your fields and preview them using Tailor, and then when you are ready, import them in to Builder to start working directly with the files.
It important to note that importing blueprints is a one-way function, and in some cases it is better to leave content within Tailor for the benefits that come with its dynamic models, especially for content. This design decision is up to you.
When visiting the Import tab, use the **Add Blueprint** button to select the Tailor blueprints you wish to import. You can select multiple blueprints and it is recommended to include related blueprints so the relationships between blueprints are preserved.
Once added, each blueprint can be customized, which includes the Controller Class, Model Class, Table Name, Permission Code and Menu Code names. When these fields are modified, they will adjust the filenames of the generated files.
Clicking the Import button will begin the conversion process. Some import options are shown to control how the import should proceed.
- **Migrate Database** performs a database migration after the import is finished. This optional and you can migrate the database later.
- **Disable Blueprints** will rename the blueprint files to use a backup extension (.bak) to disable them.
- **Delete Blueprint Data** will delete any existing data and tables for the selected blueprints found within Tailor.
Before clicking **Import**, be sure to double check the selected blueprints. The import process creates multiple scaffold files for the selected plugin. This process can be difficult to undo, so it is a good idea to practice on a test plugin first, without migrating the database or disabling the blueprints.
When everything is done, you should see the controller, model and migration files generated for your selected plugin.
## Displaying Plugin Records on CMS Pages
## Displaying plugin records on the front-end pages
Builder provides universal CMS components that you can use for displaying records from your plugins on the front-end website pages. The components provide only basic functionality, for example they don't support a record search feature.
Please read the [CMS documentation](https://docs.octobercms.com/3.x/cms/themes/components.html) to learn more about the CMS components concept.
Please read the [CMS documentation](http://octobercms.com/docs/cms/components) to learn more about the CMS components concept.
### Record List Component
### Record list component
The Record list component outputs a list of records provided by a plugin's model. The component supports the following optional features: pagination, links to the record details page, using a [model scope](https://docs.octobercms.com/3.x/extend/database/model.html#query-scopes) for the list filtering. The list can be sorted by any column, but the sorting cannot be changed by website visitors - it's set in the component configuration.
The Record list component outputs a list of records provided by a plugin's model. The component supports the following optional features: pagination, links to the record details page, using a [model scope](https://octobercms.com/docs/database/model#query-scopes) for the list filtering. The list can be sorted by any column, but the sorting cannot be changed by website visitors - it's set in the component configuration.
Add this component to a CMS page by dragging it to the page code from the component list and click it to configure its properties:
@ -286,11 +233,9 @@ Add this component to a CMS page by dragging it to the page code from the compon
* `Sorting` - select a database column name to use for sorting the list.
* `Direction` - select whether the sorting should be ascending or descending.
After configuring the component save and preview the page. Most likely you will want to customize the [default component markup](https://docs.octobercms.com/3.x/cms/themes/components.html#customizing-default-markup) to output more details about each record.
After configuring the component save and preview the page. Most likely you will want to customize the [default component markup](https://octobercms.com/docs/cms/components#customizing-default-markup) to output more details about each record.
> **Tip**: In the CMS editor, you can right-click on the `{% component %}` tag and select "Expand Markup".
## Record Details Component
## Record details component
The Record details component loads a model from the database and outputs its details on a page. If the requested record cannot be found, the component outputs the "record not found" message.
@ -302,27 +247,26 @@ Add this component to a CMS page by dragging it to the page code from the compon
* `Display column` - enter a name of the database table column to display on the details page. The value is used in the default component partial, you can customize the component by providing custom markup instead of the default partial.
* `Not found message` - a message to display if the record is not found. Used in the default partial.
After configuring the component save and preview the page. You will likely want to customize the [default component markup](https://docs.octobercms.com/3.x/cms/themes/components.html#customizing-default-markup) to output more details from the loaded model.
After configuring the component save and preview the page. You will likely want to customize the [default component markup](http://octobercms.com/docs/cms/components#customizing-default-markup) to output more details from the loaded model.
## Notes About Autocompletion
## Notes about the autocompletion
Builder updates the Inspector autocompletion fields every time when the underlying data is updated. For example, the “Field name” property of the Form Builder controls is populated with the database table column names. If you update the table structure with Builder, the autocompletion cache updates automatically. However you may need to reopen Inspector so that it can update its editors.
If you edit your plugin files or database structure with an external editor, Builder wont be able to pick up those changes automatically. You might want to reload the Builder page after you add a database column with an external tool in order to refresh the autocompletion features.
## Editing Other Plugins
## Editing other plugins
Although Builder allows you to edit plugins created by other authors, remember that you do it at your own risk. Plugins could be updated by their authors, which will eliminate your changes or break the plugin. In many cases, if you make updates to plugins developed by another author, you lose any technical support provided by the author.
## Adding Support for a Custom Form Widget
## Adding support for a custom FormWidget
To add a custom widget to the Builder plugin, you must first [register a backend form widget](https://docs.octobercms.com/3.x/extend/forms/form-widgets.html#form-widget-registration) for your plugin.
To add a custom widget to the Builder plugin, you must first [register a backend form widget](https://octobercms.com/docs/backend/widgets#form-widget-registration) for your plugin.
Once it is registered, define a list of [inspector properties](https://docs.octobercms.com/3.x/element/inspector-types.html) within your plugin in the Plugin registration class `boot()` method and register the custom control. For example:
Once it is registered, define a list of properties within your plugin in the Plugin registration class `boot()` method and register the custom control. For example:
```php
public function boot()
{
public function boot() {
$properties = [
'max_value' => [
'title' => 'The maximum allowed',
@ -358,12 +302,12 @@ public function boot()
}
```
> **Note**: See the `getStandardProperties()` method in the `rainlab/builder/classes/ControlLibrary.php` file for more examples.
> Note: See the `getStandardProperties()` method in the `rainlab/builder/classes/ControlLibrary.php` file for more examples.
Now, we need the `ControlDesignTimeProvider` class referenced above. Save the following as `classes/ControlDesignTimeProvider.php` within your plugin's directory (replacing `'yourwidgetname'` with what you used in your Plugin registration class `boot()` method).
```php
namespace Acme\Blog\Classes;
<?php namespace Acme\Blog\Classes;
use RainLab\Builder\Widgets\DefaultControlDesignTimeProvider;
@ -385,7 +329,3 @@ Then save the following as `class/controldesigntimeprovider/_control-yourwidgetn
```
You should now be able to add and configure your custom widget within the Builder plugin just like any other plugin.
### 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.

View File

@ -0,0 +1,14 @@
/*
* Legacy styles for October v1.0
*/
/* Custom buttons for the toolbar */
div.control-table .toolbar a.builder-custom-table-button:before {
line-height: 17px;
font-size: 21px;
color: #323e50;
margin-right: 5px;
top: 3px;
opacity: 1;
}

File diff suppressed because one or more lines are too long

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="41px" height="41px" viewBox="134 73 41 41" enable-background="new 134 73 41 41" xml:space="preserve">
<defs>
</defs>
<rect x="0.5" y="0.5" display="none" fill="#B8CC44" stroke="#FFFFFF" width="315" height="291"/>
<path id="bg_1_" opacity="0" fill="#FFFFFF" d="M135.015,93.554c0.002-10.854,8.8-19.653,19.654-19.655
c10.856,0.002,19.653,8.802,19.655,19.657c-0.001,6.679-3.331,12.577-8.422,16.129c-3.184,2.223-7.056,3.525-11.232,3.526
C143.815,113.209,135.016,104.41,135.015,93.554z"/>
<path opacity="0" fill="none" stroke="#2A98DB" stroke-width="6" d="M137.644,93.554c0.001-9.402,7.623-17.024,17.025-17.026
c9.404,0.002,17.025,7.625,17.026,17.028c0,5.785-2.886,10.896-7.295,13.972c-2.758,1.925-6.112,3.054-9.73,3.055
C145.267,110.58,137.645,102.959,137.644,93.554z"/>
<path fill="none" stroke="#5FB6F5" stroke-width="5.28" stroke-linecap="round" stroke-linejoin="round" d="M142.114,105.059
c-4.002-4.376-5.601-10.721-3.64-16.765c1.318-4.052,4.011-7.271,7.39-9.312"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,20 @@
/*
=require builder.dataregistry.js
=require builder.index.entity.base.js
=require builder.index.entity.plugin.js
=require builder.index.entity.databasetable.js
=require builder.index.entity.model.js
=require builder.index.entity.modelform.js
=require builder.index.entity.modellist.js
=require builder.index.entity.permission.js
=require builder.index.entity.menus.js
=require builder.index.entity.version.js
=require builder.index.entity.localization.js
=require builder.index.entity.controller.js
=require builder.index.js
=require builder.localizationinput.js
=require builder.inspector.editor.localization.js
=require builder.table.processor.localization.js
*/

View File

@ -1,197 +0,0 @@
/*
* Code List
*/
+function ($) { "use strict";
var CodeList = function (form, alias) {
this.$form = $(form);
this.alias = alias;
this.$form.on('ajaxSuccess', $.proxy(this.onAjaxSuccess, this));
this.$form.on('click', 'ul.list > li.directory > a', $.proxy(this.onDirectoryClick, this));
this.$form.on('click', 'ul.list > li.file > a', $.proxy(this.onFileClick, this));
this.$form.on('click', 'p.parent > a', $.proxy(this.onDirectoryClick, this));
this.$form.on('click', 'a[data-control=delete-asset]', $.proxy(this.onDeleteClick, this));
this.$form.on('oc.list.setActiveItem', $.proxy(this.onSetActiveItem, this));
this.setupUploader();
}
// Event handlers
// =================
CodeList.prototype.onDirectoryClick = function(e) {
this.gotoDirectory(
$(e.currentTarget).data('path'),
$(e.currentTarget).parent().hasClass('parent')
);
return false;
}
CodeList.prototype.gotoDirectory = function(path, gotoParent) {
var $container = $('div.list-container', this.$form),
self = this;
if (gotoParent !== undefined && gotoParent) {
$container.addClass('goBackward');
}
else {
$container.addClass('goForward');
}
$.oc.stripeLoadIndicator.show();
this.$form.request(this.alias+'::onOpenDirectory', {
data: {
path: path,
d: 0.2
},
complete: function() {
self.updateUi()
$container.trigger('oc.scrollbar.gotoStart')
},
error: function(jqXHR, textStatus, errorThrown) {
$container.removeClass('goForward goBackward')
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
}
}).always(function(){
$.oc.stripeLoadIndicator.hide()
})
}
CodeList.prototype.onDeleteClick = function(e) {
var $el = $(e.currentTarget),
self = this;
if (!confirm($el.data('confirmation'))) {
return false;
}
this.$form.request(this.alias+'::onDeleteFiles', {
success: function(data) {
if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length) {
$.oc.flashMsg({text: data.error, 'class': 'error'});
}
},
complete: function() {
self.refresh();
}
});
return false;
}
CodeList.prototype.onAjaxSuccess = function() {
this.updateUi();
}
CodeList.prototype.onUploadFail = function(file, message) {
if (file.xhr.status === 413) {
message = 'Server rejected the file because it was too large, try increasing post_max_size';
}
if (!message) {
message = 'Error uploading file';
}
$.oc.alert(message);
this.refresh();
}
CodeList.prototype.onUploadSuccess = function(file, data) {
if (data !== 'success') {
$.oc.alert(data);
}
}
CodeList.prototype.onUploadComplete = function(file, data) {
$.oc.stripeLoadIndicator.hide();
this.refresh();
}
CodeList.prototype.onUploadStart = function() {
$.oc.stripeLoadIndicator.show();
}
CodeList.prototype.onFileClick = function(event) {
var $link = $(event.currentTarget),
$li = $link.parent();
var e = $.Event('open.oc.list', {relatedTarget: $li.get(0), clickEvent: event});
this.$form.trigger(e, this);
if (e.isDefaultPrevented()) {
return false;
}
}
CodeList.prototype.onSetActiveItem = function(event, dataId) {
$('ul li.file', this.$form).removeClass('active');
if (dataId) {
$('ul li.file[data-id="'+dataId+'"]', this.$form).addClass('active');
}
}
// Service functions
// =================
CodeList.prototype.updateUi = function() {
$('button[data-control=asset-tools]', self.$form).trigger('oc.triggerOn.update');
}
CodeList.prototype.refresh = function() {
var self = this;
this.$form.request(this.alias+'::onRefresh', {
complete: function() {
self.updateUi();
}
});
}
CodeList.prototype.setupUploader = function() {
var self = this,
$link = $('[data-control="upload-assets"]', this.$form),
uploaderOptions = {
method: 'POST',
url: window.location,
paramName: 'file_data',
previewsContainer: $('<div />').get(0),
clickable: $link.get(0),
timeout: 0,
headers: {}
};
// Add CSRF token to headers
var token = $('meta[name="csrf-token"]').attr('content');
if (token) {
uploaderOptions.headers['X-CSRF-TOKEN'] = token;
}
var dropzone = new Dropzone($('<div />').get(0), uploaderOptions);
dropzone.on('error', $.proxy(self.onUploadFail, self));
dropzone.on('success', $.proxy(self.onUploadSuccess, self));
dropzone.on('complete', $.proxy(self.onUploadComplete, self));
dropzone.on('sending', function(file, xhr, formData) {
$.each(self.$form.serializeArray(), function (index, field) {
formData.append(field.name, field.value);
});
xhr.setRequestHeader('X-OCTOBER-REQUEST-HANDLER', self.alias + '::onUpload');
self.onUploadStart();
});
}
$(document).on('render', function() {
var $container = $('#code-list-container');
if ($container.data('oc.codeListAttached') === true) {
return;
}
$container.data('oc.codeListAttached', true);
new CodeList(
$container.closest('form'),
$container.data('alias')
);
});
}(window.jQuery);

View File

@ -1,124 +0,0 @@
/*
* Builder Index controller Code entity controller
*/
+function ($) { "use strict";
if ($.oc.builder === undefined) {
$.oc.builder = {};
}
if ($.oc.builder.entityControllers === undefined) {
$.oc.builder.entityControllers = {};
}
var Base = $.oc.builder.entityControllers.base,
BaseProto = Base.prototype;
var Code = function(indexController) {
Base.call(this, 'code', indexController);
}
Code.prototype = Object.create(BaseProto);
Code.prototype.constructor = Code;
// PUBLIC METHODS
// ============================
Code.prototype.registerHandlers = function() {
}
Code.prototype.cmdCreateCode = function(ev) {
this.indexController.openOrLoadMasterTab($(ev.target), 'onCodeOpen', this.newTabId());
}
Code.prototype.cmdOpenCode = function(ev) {
var path = $(ev.currentTarget).data('path'),
pluginCode = $(ev.currentTarget).data('pluginCode');
var result = this.indexController.openOrLoadMasterTab($(ev.target), 'onCodeOpen', this.makeTabId(pluginCode+'-'+path), {
fileName: path
});
if (result !== false) {
result.done(this.proxy(this.updateFormEditorMode, this));
}
}
Code.prototype.cmdSaveCode = function(ev) {
var $target = $(ev.currentTarget),
$form = $target.closest('form'),
$inspectorContainer = $form.find('.inspector-container')
if (!$.oc.inspector.manager.applyValuesFromContainer($inspectorContainer)) {
return
}
$target.request('onCodeSave').done(
this.proxy(this.saveCodeDone)
)
}
Code.prototype.saveCodeDone = function(data) {
if (data['builderResponseData'] === undefined) {
throw new Error('Invalid response data');
}
var $masterTabPane = this.getMasterTabsActivePane();
this.getIndexController().unchangeTab($masterTabPane);
this.updateFormEditorMode();
}
Code.prototype.getCodeList = function() {
return $('#layout-side-panel form[data-content-id=code] .control-codelist')
}
Code.prototype.updateFormEditorMode = function() {
var $masterTabPane = this.getMasterTabsActivePane();
var modes = {
css: "css",
htm: "html",
html: "html",
js: "javascript",
json: "json",
less: "less",
md: "markdown",
sass: "sass",
scss: "scss",
txt: "plain_text",
yaml: "yaml",
xml: "xml",
php: "php"
};
var fileName = $('input[name=fileName]', $masterTabPane).val(),
parts = fileName.split('.'),
extension = 'txt',
mode = 'plain_text',
editor = $('[data-control=codeeditor]', $masterTabPane);
if (parts.length >= 2) {
extension = parts.pop().toLowerCase();
}
if (modes[extension] !== undefined) {
mode = modes[extension];
}
var setEditorMode = function() {
window.setTimeout(function() {
editor.data('oc.codeEditor').editor.getSession().setMode({path: 'ace/mode/'+mode})
}, 200);
};
setEditorMode();
}
// REGISTRATION
// ============================
$.oc.builder.entityControllers.code = Code;
}(window.jQuery);

View File

@ -13,11 +13,11 @@
BaseProto = Base.prototype
var Controller = function(indexController) {
Base.call(this, 'controller', indexController);
Base.call(this, 'controller', indexController)
}
Controller.prototype = Object.create(BaseProto);
Controller.prototype.constructor = Controller;
Controller.prototype = Object.create(BaseProto)
Controller.prototype.constructor = Controller
// PUBLIC METHODS
// ============================
@ -27,7 +27,7 @@
self = this,
pluginCode = $form.data('pluginCode'),
behaviorsSelected = $form.find('input[name="behaviors[]"]:checked').length,
promise = null;
promise = null
// If behaviors were selected, open a new tab after the
// controller is saved. Otherwise just update the controller
@ -38,39 +38,39 @@
'onControllerCreate',
this.makeTabId(pluginCode+'-new-controller'),
{}
);
)
}
else {
promise = $form.request('onControllerCreate');
promise = $form.request('onControllerCreate')
}
promise.done(function(data){
$form.trigger('close.oc.popup')
self.updateDataRegistry(data)
}).always($.oc.builder.indexController.hideStripeIndicatorProxy);
}).always($.oc.builder.indexController.hideStripeIndicatorProxy)
}
Controller.prototype.cmdOpenController = function(ev) {
var controller = $(ev.currentTarget).data('id'),
pluginCode = $(ev.currentTarget).data('pluginCode');
pluginCode = $(ev.currentTarget).data('pluginCode')
this.indexController.openOrLoadMasterTab($(ev.target), 'onControllerOpen', this.makeTabId(pluginCode+'-'+controller), {
controller: controller
});
})
}
Controller.prototype.cmdSaveController = function(ev) {
var $target = $(ev.currentTarget),
$form = $target.closest('form'),
$inspectorContainer = $form.find('.inspector-container');
$inspectorContainer = $form.find('.inspector-container')
if (!$.oc.inspector.manager.applyValuesFromContainer($inspectorContainer)) {
return;
return
}
$target.request('onControllerSave').done(
this.proxy(this.saveControllerDone)
);
)
}
// EVENT HANDLERS

View File

@ -186,20 +186,20 @@
// ============================
DatabaseTable.prototype.registerHandlers = function() {
this.indexController.$masterTabs.on('oc.tableCellChanged', this.proxy(this.onTableCellChanged));
this.indexController.$masterTabs.on('oc.tableCellChanged', this.proxy(this.onTableCellChanged))
}
DatabaseTable.prototype.validateTable = function($target) {
var tableObj = this.getTableControlObject($target);
var tableObj = this.getTableControlObject($target)
tableObj.unfocusTable();
return tableObj.validate();
tableObj.unfocusTable()
return tableObj.validate()
}
DatabaseTable.prototype.getTableData = function($target) {
var tableObj = this.getTableControlObject($target);
var tableObj = this.getTableControlObject($target)
return tableObj.dataSource.getAllData();
return tableObj.dataSource.getAllData()
}
DatabaseTable.prototype.getTableControlObject = function($target) {

View File

@ -1,112 +0,0 @@
/*
* Builder Index controller Imports entity controller
*/
+function ($) { "use strict";
if ($.oc.builder === undefined) {
$.oc.builder = {};
}
if ($.oc.builder.entityControllers === undefined) {
$.oc.builder.entityControllers = {};
}
var Base = $.oc.builder.entityControllers.base,
BaseProto = Base.prototype;
var Imports = function(indexController) {
Base.call(this, 'imports', indexController);
}
Imports.prototype = Object.create(BaseProto);
Imports.prototype.constructor = Imports;
// PUBLIC METHODS
// ============================
Imports.prototype.cmdOpenImports = function(ev) {
var currentPlugin = this.getSelectedPlugin();
if (!currentPlugin) {
alert('Please select a plugin first');
return;
}
this.indexController.openOrLoadMasterTab($(ev.target), 'onImportsOpen', this.makeTabId(currentPlugin));
}
Imports.prototype.cmdConfirmImports = function(ev) {
var $target = $(ev.currentTarget);
$target.popup({
handler: 'onImportsShowConfirmPopup'
});
}
Imports.prototype.cmdSaveImports = function(ev) {
var $masterTabPane = this.getMasterTabsActivePane(),
$form = $masterTabPane.find('form'),
$popup = $(ev.currentTarget).closest('.control-popup');
$popup.removeClass('show').popup('setLoading', true);
$form.request('onImportsSave', {
data: oc.serializeJSON($popup.get(0))
})
.done((data) => {
$popup.trigger('close.oc.popup');
this.saveImportsDone(data);
})
.fail(() => {
$popup.addClass('show').popup('setLoading', false).popup('setShake');
});
}
Imports.prototype.cmdMigrateDatabase = function(ev) {
var $target = $(ev.currentTarget);
$target.request('onMigrateDatabase');
}
Imports.prototype.cmdAddBlueprintItem = function(ev) {
// $.oc.builder.blueprintbuilder.controller.addBlueprintItem(ev)
}
Imports.prototype.cmdRemoveBlueprintItem = function(ev) {
// $.oc.builder.blueprintbuilder.controller.removeBlueprint(ev)
}
// INTERNAL METHODS
// ============================
Imports.prototype.saveImportsDone = function(data) {
this.hideInspector();
$('#blueprintList').html('');
if ($.oc.mainMenu && data && data.mainMenu && data.mainMenuLeft) {
$.oc.mainMenu.reload(data.mainMenu, data.mainMenuLeft);
}
var $masterTabPane = this.getMasterTabsActivePane();
this.getIndexController().unchangeTab($masterTabPane);
}
Imports.prototype.hideInspector = function() {
var $container = $('.blueprint-container.inspector-open:first');
if ($container.length) {
var $inspectorContainer = this.findInspectorContainer($container);
$.oc.foundation.controlUtils.disposeControls($inspectorContainer.get(0));
}
}
Imports.prototype.findInspectorContainer = function($element) {
var $containerRoot = $element.closest('[data-inspector-container]')
return $containerRoot.find('.inspector-container')
}
// REGISTRATION
// ============================
$.oc.builder.entityControllers.imports = Imports;
}(window.jQuery);

View File

@ -70,16 +70,12 @@
Menus.prototype.saveMenusDone = function(data) {
if (data['builderResponseData'] === undefined) {
throw new Error('Invalid response data');
throw new Error('Invalid response data')
}
var $masterTabPane = this.getMasterTabsActivePane();
var $masterTabPane = this.getMasterTabsActivePane()
if ($.oc.mainMenu && data.mainMenu && data.mainMenuLeft) {
$.oc.mainMenu.reload(data.mainMenu, data.mainMenuLeft);
}
this.getIndexController().unchangeTab($masterTabPane);
this.getIndexController().unchangeTab($masterTabPane)
}
// REGISTRATION

View File

@ -67,14 +67,14 @@
var $target = $(ev.currentTarget)
// Always use the first placeholder to add controls
var $placeholder = this.getMasterTabsActivePane().find('.builder-control-list .control.oc-placeholder:first')[0]
var $placeholder = this.getMasterTabsActivePane().find('.builder-control-list .control.placeholder:first')[0]
// Filter all fields from the DataTable that have the "add" checkbox checked.
var fields = $target.find('.control-table').data('oc.table').dataSource.data.filter(function (column) {
return column.add
}).reverse()
// Hide the popup and initialize the load indicator.
// Hide the poup and initialize the load indicator.
$target.closest('.control-popup').data('oc.popup').hide()
$.oc.stripeLoadIndicator.show()
@ -83,7 +83,7 @@
// addControlToPlaceholder requires a proper reflow of the whole form layout before
// a new field can be added. This addField helper function makes sure that all
// Promises are run in sequence to achieve this.
function addField(field) {
function addField (field) {
return function () {
var defer = $.Deferred()
$.oc.builder.formbuilder.controller.addControlToPlaceholder(

View File

@ -3,22 +3,21 @@
*/
+function ($) { "use strict";
if ($.oc.builder === undefined) {
$.oc.builder = {};
}
if ($.oc.builder === undefined)
$.oc.builder = {}
var Base = $.oc.foundation.base,
BaseProto = Base.prototype;
BaseProto = Base.prototype
var Builder = function() {
Base.call(this);
Base.call(this)
this.$masterTabs = null;
this.masterTabsObj = null;
this.hideStripeIndicatorProxy = null;
this.entityControllers = {};
this.$masterTabs = null
this.masterTabsObj = null
this.hideStripeIndicatorProxy = null
this.entityControllers = {}
this.init();
this.init()
}
Builder.prototype = Object.create(BaseProto)
@ -35,30 +34,30 @@
// ============================
Builder.prototype.openOrLoadMasterTab = function($form, serverHandlerName, tabId, data) {
if (this.masterTabsObj.goTo(tabId)) {
return false;
}
if (this.masterTabsObj.goTo(tabId))
return false
var requestData = data === undefined ? {} : data;
var requestData = data === undefined ? {} : data
$.oc.stripeLoadIndicator.show();
var promise = $form
.request(serverHandlerName, {
data: requestData
})
$.oc.stripeLoadIndicator.show()
var promise = $form.request(
serverHandlerName,
{ data: requestData }
)
.done(this.proxy(this.addMasterTab))
.always(this.hideStripeIndicatorProxy);
.always(
this.hideStripeIndicatorProxy
)
return promise;
return promise
}
Builder.prototype.getMasterTabActivePane = function() {
return this.$masterTabs.find('> .tab-content > .tab-pane.active');
return this.$masterTabs.find('> .tab-content > .tab-pane.active')
}
Builder.prototype.unchangeTab = function($pane) {
$pane.find('form').trigger('unchange.oc.changeMonitor');
$pane.find('form').trigger('unchange.oc.changeMonitor')
}
Builder.prototype.triggerCommand = function(command, ev) {
@ -94,46 +93,44 @@
Builder.prototype.createEntityControllers = function() {
for (var controller in $.oc.builder.entityControllers) {
if (controller == "base") {
continue;
continue
}
this.entityControllers[controller] = new $.oc.builder.entityControllers[controller](this);
this.entityControllers[controller] = new $.oc.builder.entityControllers[controller](this)
}
}
Builder.prototype.registerHandlers = function() {
$(document).on('click', '[data-builder-command]', this.proxy(this.onCommand));
$(document).on('submit', '[data-builder-command]', this.proxy(this.onCommand));
$(document).on('click', '[data-builder-command]', this.proxy(this.onCommand))
$(document).on('submit', '[data-builder-command]', this.proxy(this.onCommand))
this.$masterTabs.on('changed.oc.changeMonitor', this.proxy(this.onFormChanged));
this.$masterTabs.on('unchanged.oc.changeMonitor', this.proxy(this.onFormUnchanged));
this.$masterTabs.on('shown.bs.tab', this.proxy(this.onTabShown));
this.$masterTabs.on('afterAllClosed.oc.tab', this.proxy(this.onAllTabsClosed));
this.$masterTabs.on('closed.oc.tab', this.proxy(this.onTabClosed));
this.$masterTabs.on('autocompleteitems.oc.inspector', this.proxy(this.onDataRegistryItems));
this.$masterTabs.on('dropdownoptions.oc.inspector', this.proxy(this.onDataRegistryItems));
this.$masterTabs.on('changed.oc.changeMonitor', this.proxy(this.onFormChanged))
this.$masterTabs.on('unchanged.oc.changeMonitor', this.proxy(this.onFormUnchanged))
this.$masterTabs.on('shown.bs.tab', this.proxy(this.onTabShown))
this.$masterTabs.on('afterAllClosed.oc.tab', this.proxy(this.onAllTabsClosed))
this.$masterTabs.on('closed.oc.tab', this.proxy(this.onTabClosed))
this.$masterTabs.on('autocompleteitems.oc.inspector', this.proxy(this.onDataRegistryItems))
this.$masterTabs.on('dropdownoptions.oc.inspector', this.proxy(this.onDataRegistryItems))
for (var controller in this.entityControllers) {
if (this.entityControllers[controller].registerHandlers !== undefined) {
this.entityControllers[controller].registerHandlers();
this.entityControllers[controller].registerHandlers()
}
}
}
Builder.prototype.hideStripeIndicator = function() {
$.oc.stripeLoadIndicator.hide();
$.oc.stripeLoadIndicator.hide()
}
Builder.prototype.addMasterTab = function(data) {
this.masterTabsObj.addTab(data.tabTitle, data.tab, data.tabId, 'oc-' + data.tabIcon)
var $masterTabPane = this.getMasterTabActivePane();
if (data.isNewRecord) {
$masterTabPane.find('form').one('ready.oc.changeMonitor', this.proxy(this.onChangeMonitorReady));
}
var $masterTabPane = this.getMasterTabActivePane()
$('[data-builder-tabs]', $masterTabPane).dragScroll();
$masterTabPane.find('form').one('ready.oc.changeMonitor', this.proxy(this.onChangeMonitorReady))
}
}
Builder.prototype.updateModifiedCounter = function() {
@ -142,11 +139,9 @@
models: { menu: 'models', count: 0 },
permissions: { menu: 'permissions', count: 0 },
menus: { menu: 'menus', count: 0 },
imports: { menu: 'imports', count: 0 },
versions: { menu: 'versions', count: 0 },
localization: { menu: 'localization', count: 0 },
controller: { menu: 'controllers', count: 0 },
code: { menu: 'code', count: 0 }
controller: { menu: 'controllers', count: 0 }
}
$('> div.tab-content > div.tab-pane[data-modified] > form', this.$masterTabs).each(function(){
@ -200,21 +195,22 @@
if (ev.currentTarget.tagName == 'FORM' && ev.type == 'click') {
// The form elements could have data-builder-command attribute,
// but for them we only handle the submit event and ignore clicks.
return;
return
}
var command = $(ev.currentTarget).data('builderCommand');
this.triggerCommand(command, ev);
var command = $(ev.currentTarget).data('builderCommand')
this.triggerCommand(command, ev)
// Prevent default for everything except drop-down menu items
//
var $target = $(ev.currentTarget);
var $target = $(ev.currentTarget)
if (ev.currentTarget.tagName === 'A' && $target.attr('role') == 'menuitem' && $target.attr('href') == 'javascript:;') {
return;
return
}
ev.preventDefault();
return false;
ev.preventDefault()
return false
}
Builder.prototype.onFormChanged = function(ev) {
@ -270,8 +266,7 @@
data.propertyDefinition.fillFrom == 'controller-urls' ||
data.propertyDefinition.fillFrom == 'model-columns' ||
data.propertyDefinition.fillFrom == 'plugin-lists' ||
data.propertyDefinition.fillFrom == 'permissions'
) {
data.propertyDefinition.fillFrom == 'permissions') {
ev.preventDefault()
var subtype = null,

View File

@ -1,5 +1,5 @@
.builder-controllers-builder-area {
background: var(--bs-body-bg, white);
background: white;
ul.controller-behavior-list {
.clearfix();
@ -16,10 +16,10 @@
span {
display: inline-block;
background: @builder-control-tooltip-color;
color: white;
margin: 0 auto;
border-radius: 8px;
.border-radius(8px);
background: @builder-control-border-color;
padding: 7px 10px;
font-size: 13px;
line-height: 100%;
@ -34,8 +34,8 @@
.clearfix();
cursor: pointer;
.list-behavior, .import-export-behavior {
border-radius: 4px;
.list-behavior, .reorder-behavior {
.border-radius(4px);
border: 2px solid @builder-control-border-color;
padding: 25px 10px 25px 10px;
@ -53,8 +53,8 @@
}
}
.oc-placeholder {
background: var(--oc-secondary-bg, #EEF2F4);
.placeholder {
background: #EEF2F4;
height: 25px;
}
@ -64,9 +64,9 @@
}
}
.import-export-behavior {
.reorder-behavior {
table {
i.icon-bars, .oc-placeholder {
i.icon-bars, .placeholder {
float: left;
}
@ -103,7 +103,7 @@
}
div.label {
background: var(--oc-secondary-bg, #EEF2F4);
background: #EEF2F4;
height: 25px;
margin-bottom: 10px;
@ -121,14 +121,14 @@
}
div.control {
background: var(--oc-secondary-bg, #EEF2F4);
background: #EEF2F4;
height: 35px;
margin-bottom: 25px;
}
}
div.button {
background: var(--oc-secondary-bg, #EEF2F4);
background: #EEF2F4;
height: 35px;
margin-right: 20px;
.border-radius(4px);

View File

@ -1,26 +1,15 @@
@import "../../../../../modules/backend/assets/less/core/boot.less";
@builder-control-border-color: var(--oc-document-ruler-tick, #bdc3c7);
@builder-control-tooltip-color: #72809d;
@builder-control-border-color: #bdc3c7;
@builder-control-text-color: #95a5a6;
@builder-hover-color: var(--oc-selection);
:root, [data-bs-theme="light"] {
--oc-builder-control-color: #555;
}
[data-bs-theme="dark"] {
--oc-builder-control-color: #888;
}
@builder-hover-color: #2581b8;
@import "buildingarea.less";
@import "controlblueprint.less";
@import "behaviors.less";
@import "tabs.less";
@import "menus.less";
@import "imports.less";
@import "localization.less";
@import "codelist.less";
.control-filelist ul li.group.model > h4 a:after {
content: @random;
@ -68,7 +57,7 @@ html.gecko .control-filelist ul li.group {
.builder-inspector-container {
width: 350px;
border-left: 1px solid var(--bs-border-color, #d9d9d9);
border-left: 1px solid #d9d9d9;
&:empty {
display: none!important;

View File

@ -1,5 +1,5 @@
.builder-building-area {
background: var(--bs-body-bg, white);
background: white;
ul.builder-control-list {
.clearfix();
@ -11,30 +11,26 @@
position: relative;
margin-bottom: 20px;
cursor: pointer;
user-select: none;
.user-select(none);
&[data-unknown] {
cursor: default;
}
&.oc-placeholder, &.loading-control {
&.placeholder, &.loading-control {
padding: 10px 12px;
position: relative;
text-align: center;
border: 2px dotted var(--bs-border-color, #dae0e0);
border: 2px dotted #dde0e2;
margin-top: 20px;
border-radius: 4px;
color: var(--bs-emphasis-color, #dae0e0);
.border-radius(4px);
color: #dae0e0;
i {
margin-right: 8px;
}
}
&.loading-control {
background: var(--oc-secondary-bg);
}
&.clear-row {
display: none;
margin-bottom: 0;
@ -47,7 +43,7 @@
&.updating-control:after,
&.loading-control:before {
background-image:url(../images/loader-transparent.svg);
background-image:url(../../../../../modules/system/assets/ui/images/loader-transparent.svg);
background-size: 15px 15px;
background-position: 50% 50%;
display: inline-block;
@ -57,7 +53,7 @@
margin-right: 13px;
position: relative;
top: 2px;
animation: spin 1s linear infinite;
.animation(spin 1s linear infinite);
}
&.loading-control:after {
@ -79,7 +75,7 @@
width: 25px;
height: 25px;
background: rgba(127, 127, 127, 0.1);
border-radius: 4px;
.border-radius(4px);
}
&.drag-over {
@ -113,7 +109,7 @@
display: none;
}
&:not(.oc-placeholder):not(.loading-control):not(.updating-control):hover > {
&:not(.placeholder):not(.loading-control):not(.updating-control):hover > {
> div.remove-control {
font-family: sans-serif;
display: block;
@ -127,9 +123,9 @@
font-size: 16px;
font-weight: bold;
line-height: 21px;
border-radius: 20px;
background: var(--oc-toolbar-border, #ecf0f1);
color: var(--oc-toolbar-color, #95a5a6) !important;
.border-radius(20px);
background: #ecf0f1;
color: #95a5a6 !important;
&:hover {
color: white !important;
@ -162,26 +158,25 @@
> .control-wrapper,
> .control-static-contents {
position: relative;
transition: margin 0.1s;
.transition(margin 0.1s);
}
}
> li.oc-placeholder:hover,
> li.oc-placeholder.popover-highlight,
> li.oc-placeholder.control-palette-open {
> li.placeholder:hover,
> li.placeholder.popover-highlight,
> li.placeholder.control-palette-open {
background-color: @builder-hover-color!important;
color: white!important;
border-style: solid;
border-color: @builder-hover-color;
opacity: 1;
}
> li.control:not(.oc-placeholder):not(.loading-control):not([data-unknown]):hover > .control-wrapper *,
> li.control.inspector-open:not(.oc-placeholder):not(.loading-control) > .control-wrapper * {
> li.control:not(.placeholder):not(.loading-control):not([data-unknown]):hover > .control-wrapper *,
> li.control.inspector-open:not(.placeholder):not(.loading-control) > .control-wrapper * {
color: @builder-hover-color!important;
}
> li.control.drag-over:not(.oc-placeholder) {
> li.control.drag-over:not(.placeholder) {
&:before {
position: absolute;
content: '';
@ -189,7 +184,7 @@
left: 0;
width: 10px;
height: 100%;
border-radius: 5px;
.border-radius(5px);
background-color: @builder-hover-color;
}
@ -204,13 +199,13 @@
.control-body {
&.field-disabled,
&.field-hidden {
opacity: 0.5;
.opacity(0.5);
}
}
.builder-control-label {
margin-bottom: 10px;
color: var(--oc-builder-control-color);
color: #555555;
font-size: 14px;
font-weight: 600;
@ -254,9 +249,3 @@ html.gecko.mac {
}
}
}
[data-bs-theme="dark"] {
.builder-building-area {
background: @body-bg;
}
}

View File

@ -1,249 +0,0 @@
@color-text-title: @text-color;
@color-text-description: @primary-color;
[data-entity="code"] .secondary-content-tabs .nav-tabs {
display: none;
}
.control-codelist {
p.no-data {
padding: 22px;
margin: 0;
color: @text-muted;
font-size: 14px;
text-align: center;
font-weight: 400;
border-radius: @border-radius-base;
}
p.parent, ul li {
font-weight: 300;
line-height: 150%;
margin-bottom: 0;
&.active a {
background: @color-list-active;
position: relative;
&:after {
position: absolute;
height: 100%;
width: 4px;
left: 0;
top: 0;
background: @color-list-active-border;
display: block;
content: ' ';
}
}
a.link {
display: block;
position: relative;
word-wrap: break-word;
padding: 10px 50px 10px 20px;
outline: none;
font-weight: 400;
color: @color-text-title;
font-size: 14px;
&:hover, &:focus, &:active {
text-decoration: none;
}
span {
display: block;
&.description {
color: @color-text-description;
font-size: 12px;
font-weight: 400;
word-wrap: break-word;
strong {
color: @color-text-title;
font-weight: 400;
}
}
}
}
&.directory, &.parent {
a.link {
padding-left: 40px;
&:after {
display: block;
position: absolute;
width: 10px;
height: 10px;
top: 10px;
left: 20px;
.icon(@folder);
color: @color-list-icon;
font-size: 14px;
}
}
}
&.parent {
a.link {
padding-left: 41px;
background-color: @primary-bg;
color: @primary-color;
word-wrap: break-word;
&:before {
content: '';
height: 1px;
display: block;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
background: @primary-border;
}
&:after {
font-size: 13px;
color: @primary-color;
width: 18px;
height: 18px;
top: 11px;
left: 22px;
opacity: 0.5;
.icon(@chevron-left);
}
}
}
}
p.parent a.link:hover {
background: @editor-section-bg !important;
color: @editor-section-color !important;
&:after {
opacity: 1;
}
&:before {
display: none;
}
}
ul {
padding: 0;
margin: 0;
li {
font-weight: 300;
line-height: 150%;
position: relative;
list-style: none;
&.active a.link, a.link:hover {
background: @editor-section-bg;
color: @editor-section-color;
}
&.active a.link {
position: relative;
&:after {
position: absolute;
height: 100%;
width: 4px;
left: 0;
top: 0;
background: @primary-border;
display: block;
content: ' ';
}
}
div.controls {
position: absolute;
right: 45px;
top: 10px;
.dropdown {
width: 14px;
height: 21px;
&.open a.control {
display: block!important;
&:before {
visibility: visible;
display: block;
}
}
}
a.control {
color: @color-text-title;
font-size: 14px;
visibility: hidden;
overflow: hidden;
width: 14px;
height: 21px;
display: none;
text-decoration: none;
cursor: pointer;
opacity: 0.5;
&:before {
visibility: visible;
display: block;
margin-right: 0;
}
&:hover {
opacity: 1;
}
}
}
&:hover {
background: @editor-section-bg;
color: @editor-section-color;
div.controls, a.control {
display: block !important;
> a.control {
display: block !important;
}
}
}
.form-check {
position: absolute;
top: 10px;
right: 5px;
label {
margin-right: 0;
}
}
}
}
div.list-container {
position: relative;
.translate(0, 0);
&.animate ul {
.transition(all 0.2s ease);
}
&.goForward ul {
.translate(-350px, 0);
}
&.goBackward ul {
.translate(350px, 0);
}
}
}

View File

@ -1,186 +0,0 @@
.builder-tailor-builder-area {
background: var(--bs-body-bg, white);
ul.tailor-blueprint-list {
.clearfix();
cursor: pointer;
padding: 20px;
margin-bottom: 0;
list-style: none;
li {
position: relative;
h4 {
text-align: center;
border-bottom: 1px dotted @builder-control-border-color;
margin: 0 -20px 30px;
span {
display: inline-block;
color: white;
margin: 0 auto;
border-radius: 8px;
background: @builder-control-tooltip-color;
padding: 7px 10px;
font-size: 13px;
line-height: 100%;
position: relative;
top: 14px;
}
}
table.table {
margin: 0;
td {
font-size: 0.875em;
> span {
font-family: var(--bs-font-monospace);
color: @secondary-color;
word-wrap: break-word;
word-break: break-word;
}
}
th {
font-size: 0.875em;
text-align: right;
}
th:not(.table-danger) {
color: @text-color;
}
tr:last-child {
td, th {
border-bottom: none;
}
}
}
div.remove-blueprint {
font-family: sans-serif;
display: none;
position: absolute;
right: 0;
top: 20px;
cursor: pointer;
width: 21px;
height: 21px;
padding-left: 6px;
font-size: 16px;
font-weight: bold;
line-height: 21px;
border-radius: 20px;
background: var(--oc-toolbar-border, #ecf0f1);
color: var(--oc-toolbar-color, #95a5a6) !important;
&:hover {
color: white !important;
background: #c03f31;
}
}
&:hover {
div.remove-blueprint {
display: block;
}
}
&.updating-blueprint:after {
background-image:url(../images/loader-transparent.svg);
background-size: 15px 15px;
background-position: 50% 50%;
display: inline-block;
width: 15px;
height: 15px;
content: ' ';
margin-right: 13px;
position: relative;
top: 2px;
animation: spin 1s linear infinite;
}
&.updating-blueprint:after {
position: absolute;
right: -8px;
top: 35px;
}
&.updating-blueprint:before {
content: '';
position: absolute;
right: 0;
top: 30px;
width: 25px;
height: 25px;
background: rgba(127, 127, 127, 0.1);
border-radius: 4px;
}
}
.blueprint-container {
.clearfix();
.tailor-blueprint {
div.form {
.clearfix();
border: 2px solid @builder-control-border-color;
margin-bottom: 20px;
border-radius: 4px;
}
}
&:hover, &.inspector-open {
* {
border-color: @builder-hover-color!important;
}
}
}
}
.add-blueprint-button {
font-size: 16px;
text-align: center;
border: 2px dotted var(--oc-dropdown-trigger-border, #dde0e2);
height: 64px;
margin: 0 20px 40px;
a {
padding: 20px 15px;
height: 60px;
display: block;
text-decoration: none;
color: var(--oc-dropdown-trigger-color, #bdc3c7);
}
i {
margin-right: 5px;
}
span {
position: relative;
top: -1px;
}
span.title {
font-size: 14px;
}
&:hover {
border: 2px dotted @builder-hover-color;
background: @builder-hover-color!important;
a {
color: white;
}
}
}
}
// Fix for the Mac firefox
html.gecko.mac {
.builder-tailor-builder-area {
ul.tailor-blueprint-list {
padding-right: 40px;
}
}
}

View File

@ -1,5 +1,5 @@
.builder-menu-editor {
background: var(--bs-body-bg, white);
background: white;
.builder-menu-editor-workspace {
padding: 30px;
@ -11,7 +11,7 @@
cursor: pointer;
> li {
border-radius: 4px;
.border-radius(4px);
div.item-container:hover, &.inspector-open > div.item-container {
background: @builder-hover-color!important;
@ -41,10 +41,10 @@
&:hover .close-btn {
display: block;
text-decoration: none;
opacity: 0.5;
.opacity(0.5);
&:hover {
opacity: 1;
.opacity(1);
}
}
}
@ -52,11 +52,11 @@
&.add {
font-size: 16px;
text-align: center;
border: 2px dotted var(--oc-dropdown-trigger-border, #dde0e2);
border: 2px dotted #dde0e2;
a {
text-decoration: none;
color: var(--oc-dropdown-trigger-color, #bdc3c7);
color: #bdc3c7;
}
span.title {
@ -90,8 +90,8 @@
}
> div.item-container {
background: var(--bs-secondary-bg, #ecf0f1);
color: var(--bs-secondary-color, #708080);
background: #ecf0f1;
color: #708080;
padding: 20px 25px;
height: 64px;
white-space: nowrap;
@ -152,9 +152,8 @@
}
> div.item-container {
background: var(--bs-tertiary-bg, #f3f5f5);
color: var(--bs-tertiary-color, #94a5a6);
background: #f3f5f5;
color: #94a5a6;
padding: 18px 13px;
text-align: center;

View File

@ -32,31 +32,13 @@
&.global {
top: 5px;
right: 0;
padding-right: 10px;
background: @body-bg;
z-index: 110;
border-radius: 3px;
> div {
width: 24px;
height: 24px;
border-radius: 3px;
background: @toolbar-bg;
padding-left: 10px;
padding-top: 5px;
&:active {
background: @toolbar-focus-bg;
}
}
right: 15px;
}
}
}
> ul.tabs {
margin: 0;
padding-right: 50px;
list-style: none;
font-size: 0;
white-space: nowrap;
@ -64,7 +46,7 @@
position: relative;
> li {
user-select: none;
.user-select(none);
display: inline-block;
font-size: 13px;
white-space: nowrap;
@ -73,16 +55,16 @@
> div.tab-container {
position: relative;
color: @tab-color !important;
color: #bdc3c7!important;
> div {
transition: padding .1s;
.transition(padding .1s);
position: relative;
}
}
&:hover > div {
color: @tab-active-color !important;
color: #95a5a6!important;
}
.tab-control {
@ -100,7 +82,7 @@
color: #95a5a6;
&:hover {
color: @link-color !important;
color: @link-color!important;
}
}
@ -112,7 +94,7 @@
&.active {
> div.tab-container {
color: @tab-active-color !important;
color: #95a5a6!important;
}
.tab-control {
@ -139,7 +121,7 @@
&.primary {
> .tabs {
> ul.tabs {
padding: 0 40px 0 40px;
padding: 0 20px 0 40px;
height: 31px;
&:after {
@ -150,8 +132,7 @@
left: 0;
bottom: 0;
width: 100%;
// background: #bdc3c7;
background: transparent linear-gradient(90deg, #bdc3c7 90%, transparent 100%);
background: #bdc3c7;
z-index: 106;
}
@ -166,21 +147,14 @@
> div {
padding: 5px 5px 0 5px;
background: white;
border-top: 2px solid #e5e5e5;
> span {
position: relative;
top: -4px;
transition: top .1s;
top: -2px;
.transition(top .1s);
}
}
}
&.active {
z-index: 107;
color: var(--oc-tab-active-color);
> div.tab-container {
&:before, &:after {
content: '';
@ -194,11 +168,25 @@
&:before {
left: 0;
background-position: 0 0;
background-position: 0 -27px;
}
&:after {
right: 0;
background-position: -75px -27px;
}
}
&.active {
z-index: 107;
> div.tab-container {
&:before {
background-position: 0 0;
}
&:after {
background-position: -75px 0;
}
@ -296,7 +284,7 @@
> div.tab-container {
> div {
color: var(--oc-builder-control-color);
color: #555555;
padding-right: 30px;
}
}
@ -316,16 +304,3 @@
// }
// }
// }
[data-bs-theme="dark"] {
.builder-tabs.primary > .tabs > ul.tabs > li.active:before,
.builder-tabs.primary > .tabs > ul.tabs > li > div.tab-container > div {
background: #202124;
}
.builder-tabs.primary > .tabs > ul.tabs > li.active > div.tab-container:before,
.builder-tabs.primary > .tabs > ul.tabs > li.active > div.tab-container:after {
background-image: url(../images/tab-dark.png);
}
}

View File

@ -1,135 +0,0 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\CodeFileModel;
use RainLab\Builder\Classes\PluginCode;
use Request;
use Flash;
use Input;
use Lang;
/**
* IndexCodeOperations is plugin code management functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class IndexCodeOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/codefilemodel/fields.yaml';
/**
* onCodeOpen
*/
public function onCodeOpen()
{
$fileName = Input::get('fileName');
$pluginCodeObj = $this->getPluginCode();
$options = [
'pluginCode' => $pluginCodeObj->toCode()
];
$widget = $this->makeBaseFormWidget($fileName, $options);
$this->vars['fileName'] = $fileName;
$result = [
'tabTitle' => $this->getTabName($widget->model),
'tabIcon' => 'icon-file-code-o',
'tabId' => $this->getTabId($pluginCodeObj->toCode(), $fileName),
'tab' => $this->makePartial('tab', [
'form' => $widget,
'pluginCode' => $pluginCodeObj->toCode()
])
];
return $result;
}
/**
* onCodeSave
*/
public function onCodeSave()
{
$pluginCodeObj = new PluginCode(post('plugin_code'));
$pluginCode = $pluginCodeObj->toCode();
$fileName = post('fileName');
$data = array_only(post(), ['fileName', 'content']);
$model = $this->loadModelFromPost();
$model->fill($data);
$model->save();
Flash::success(Lang::get('rainlab.builder::lang.controller.saved'));
$result = $this->controller->widget->codeList->onRefresh();
$result['builderResponseData'] = [
'tabId' => $this->getTabId($pluginCode, $fileName),
'tabTitle' => $this->getTabName($model),
];
return $result;
}
/**
* getTabName
*/
protected function getTabName($model)
{
$pluginName = Lang::get($model->getModelPluginName());
return $pluginName.'/'.$model->fileName;
}
/**
* getTabId
*/
protected function getTabId($pluginCode, $fileName)
{
return 'code-'.$pluginCode.'-'.$fileName;
}
/**
* loadModelFromPost
*/
protected function loadModelFromPost()
{
$pluginCodeObj = new PluginCode(Request::input('plugin_code'));
$options = [
'pluginCode' => $pluginCodeObj->toCode()
];
$fileName = Input::get('fileName');
return $this->loadOrCreateBaseModel($fileName, $options);
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($fileName, $options = [])
{
$model = new CodeFileModel();
if (isset($options['pluginCode'])) {
$model->setPluginCode($options['pluginCode']);
}
if (!$fileName) {
if ($currentPath = $this->controller->widget->codeList->getCurrentRelativePath()) {
$model->fileName = $currentPath . '/';
}
return $model;
}
$model->load($fileName);
return $model;
}
}

View File

@ -1,29 +1,25 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\ControllerModel;
use RainLab\Builder\Classes\ControllerModel;
use RainLab\Builder\Classes\PluginCode;
use ApplicationException;
use Exception;
use Request;
use Flash;
use Input;
use Lang;
/**
* IndexControllerOperations is plugin controller management functionality for the Builder index controller
* Plugin controller management functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class IndexControllerOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/controllermodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/controllermodel/fields.yaml';
/**
* onControllerOpen
*/
public function onControllerOpen()
{
$controller = Input::get('controller');
@ -49,9 +45,6 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onControllerCreate
*/
public function onControllerCreate()
{
$pluginCodeObj = new PluginCode(Request::input('plugin_code'));
@ -69,7 +62,9 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
$result = $this->controller->widget->controllerList->updateList();
if ($model->behaviors) {
// Create a new tab only for controllers with behaviors.
// Create a new tab only for controllers
// with behaviors.
$widget = $this->makeBaseFormWidget($model->controller, $options);
$tab = [
@ -90,9 +85,6 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onControllerSave
*/
public function onControllerSave()
{
$controller = Input::get('controller');
@ -108,9 +100,6 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onControllerShowCreatePopup
*/
public function onControllerShowCreatePopup()
{
$pluginCodeObj = $this->getPluginCode();
@ -119,18 +108,15 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
'pluginCode' => $pluginCodeObj->toCode()
];
$this->baseFormConfigFile = '~/plugins/rainlab/builder/models/controllermodel/fields_new_controller.yaml';
$this->baseFormConfigFile = '~/plugins/rainlab/builder/classes/controllermodel/new-controller-fields.yaml';
$widget = $this->makeBaseFormWidget(null, $options);
return $this->makePartial('create-controller-popup-form', [
'form' => $widget,
'form'=>$widget,
'pluginCode' => $pluginCodeObj->toCode()
]);
}
/**
* getTabName
*/
protected function getTabName($model)
{
$pluginName = Lang::get($model->getModelPluginName());
@ -138,17 +124,11 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
return $pluginName.'/'.$model->controller;
}
/**
* getTabId
*/
protected function getTabId($pluginCode, $controller)
{
return 'controller-'.$pluginCode.'-'.$controller;
}
/**
* loadModelFromPost
*/
protected function loadModelFromPost()
{
$pluginCodeObj = new PluginCode(Request::input('plugin_code'));
@ -161,9 +141,6 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
return $this->loadOrCreateBaseModel($controller, $options);
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($controller, $options = [])
{
$model = new ControllerModel();
@ -180,9 +157,6 @@ class IndexControllerOperations extends IndexOperationsBehaviorBase
return $model;
}
/**
* mergeRegistryDataIntoResult
*/
protected function mergeRegistryDataIntoResult(&$result, $pluginCodeObj)
{
if (!array_key_exists('builderResponseData', $result)) {

View File

@ -1,17 +1,23 @@
<?php namespace RainLab\Builder\Behaviors;
use Backend\Classes\ControllerBehavior;
use RainLab\Builder\Models\LocalizationModel;
use RainLab\Builder\Models\ModelModel;
use RainLab\Builder\Models\ModelFormModel;
use RainLab\Builder\Models\ModelListModel;
use RainLab\Builder\Models\ControllerModel;
use RainLab\Builder\Models\PermissionsModel;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\Classes\LocalizationModel;
use RainLab\Builder\Classes\ModelModel;
use RainLab\Builder\Classes\ModelFormModel;
use RainLab\Builder\Classes\ModelListModel;
use RainLab\Builder\Classes\ControllerModel;
use RainLab\Builder\Classes\PermissionsModel;
use ApplicationException;
use SystemException;
use Exception;
use Request;
use Flash;
use Input;
use Lang;
/**
* IndexDataRegistry is plugin data registry functionality for the Builder index controller
* Plugin data registry functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges

View File

@ -1,8 +1,8 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\DatabaseTableModel;
use RainLab\Builder\Models\MigrationModel;
use RainLab\Builder\Classes\DatabaseTableModel;
use RainLab\Builder\Classes\MigrationModel;
use RainLab\Builder\Classes\TableMigrationCodeGenerator;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\Models\Settings as PluginSettings;
@ -24,12 +24,12 @@ class IndexDatabaseTableOperations extends IndexOperationsBehaviorBase
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/databasetablemodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/databasetablemodel/fields.yaml';
/**
* @var string migrationFormConfigFile
*/
protected $migrationFormConfigFile = '~/plugins/rainlab/builder/models/migrationmodel/fields.yaml';
protected $migrationFormConfigFile = '~/plugins/rainlab/builder/classes/migrationmodel/fields.yaml';
/**
* extendBaseFormWidgetConfig
@ -135,7 +135,7 @@ class IndexDatabaseTableOperations extends IndexOperationsBehaviorBase
throw new ApplicationException($ex->getMessage());
}
$result = $this->controller->widget->databaseTableList->updateList();
$result = $this->controller->widget->databaseTabelList->updateList();
$result = array_merge(
$result,

View File

@ -1,192 +0,0 @@
<?php namespace RainLab\Builder\Behaviors;
use BackendMenu;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\ImportsModel;
use RainLab\Builder\Classes\PluginCode;
use System\Classes\PluginManager;
use System\Helpers\Cache as CacheHelper;
use System\Classes\VersionManager;
use ApplicationException;
use Throwable;
use Flash;
use Lang;
/**
* IndexImportsOperations functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class IndexImportsOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/importsmodel/fields.yaml';
/**
* @var string selectFormConfigFile
*/
protected $selectFormConfigFile = '~/plugins/rainlab/builder/models/importsmodel/fields.yaml';
/**
* onImportsOpen
*/
public function onImportsOpen()
{
$pluginCodeObj = $this->getPluginCode();
$pluginCode = $pluginCodeObj->toCode();
$widget = $this->makeSelectionFormWidget($pluginCode);
$result = [
'tabTitle' => $widget->model->getPluginName().'/'.__("Import"),
'tabIcon' => 'icon-arrow-circle-down',
'tabId' => $this->getTabId($pluginCode),
'tab' => $this->makePartial('tab', [
'form' => $widget,
'pluginCode' => $pluginCodeObj->toCode()
])
];
return $result;
}
/**
* onImportsSave
*/
public function onImportsShowConfirmPopup()
{
if (!post('blueprints')) {
throw new ApplicationException(__("There are no blueprints to import, please select a blueprint and try again."));
}
$pluginCodeObj = $this->getPluginCode();
$options = [
'pluginCode' => $pluginCodeObj->toCode()
];
$this->baseFormConfigFile = '~/plugins/rainlab/builder/models/importsmodel/fields_import.yaml';
$widget = $this->makeBaseFormWidget(null, $options);
return $this->makePartial('import-blueprints-popup-form', [
'form' => $widget,
'pluginCode' => $pluginCodeObj->toCode()
]);
}
/**
* onImportsSave
*/
public function onImportsSave()
{
if (post('delete_blueprint_data')) {
$confirmText = trim(strtolower(post('delete_blueprint_data_confirm')));
if ($confirmText !== 'ok') {
throw new ApplicationException(__("Type OK in the field to confirm you want to destroy the existing blueprint data."));
}
}
$pluginCodeObj = new PluginCode(post('plugin_code'));
$pluginCode = $pluginCodeObj->toCode();
// Validate plugin code matches
$vectorCode = $this->controller->getBuilderActivePluginVector()->pluginCodeObj->toCode();
if ($pluginCode !== $vectorCode) {
throw new ApplicationException(Lang::get('rainlab.builder::lang.common.not_match'));
}
$model = $this->loadOrCreateBaseModel($pluginCodeObj->toCode());
// Disable blueprints when finished
if (post('disable_blueprints')) {
$model->disableBlueprints = true;
}
// Disable blueprints when finished
if (post('delete_blueprint_data')) {
$model->deleteBlueprintData = true;
}
// Perform import
$model->setPluginCodeObj($pluginCodeObj);
$model->fill(post());
$model->import();
// Migrate database
if (post('migrate_database')) {
VersionManager::instance()->updatePlugin($pluginCode);
}
CacheHelper::instance()->clearBlueprintCache();
Flash::success(__("Import Complete"));
$builderResponseData = [
'tabId' => $this->getTabId($pluginCode),
'tabTitle' => $model->getPluginName().'/'.__("Import"),
];
// Refresh everything
$result = $this->controller->setBuilderActivePlugin($pluginCode);
$result['builderResponseData'] = $builderResponseData;
// Feature is nice to have, only supported in >3.3.9
try {
PluginManager::instance()->reloadPlugins();
BackendMenu::resetCache();
$result['mainMenu'] = $this->controller->makeLayoutPartial('mainmenu');
$result['mainMenuLeft'] = $this->controller->makeLayoutPartial('mainmenu', ['isVerticalMenu'=>true]);
}
catch (Throwable $ex) {}
return $result;
}
/**
* onMigrateDatabase
*/
public function onMigrateDatabase()
{
$pluginCodeObj = new PluginCode(post('plugin_code'));
VersionManager::instance()->updatePlugin($pluginCodeObj->toCode());
Flash::success(__("Migration Complete"));
}
/**
* getTabId
*/
protected function getTabId($pluginCode)
{
return 'imports-'.$pluginCode;
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($pluginCode, $options = [])
{
$model = new ImportsModel;
$model->loadPlugin($pluginCode);
return $model;
}
/**
* makeBaseFormWidget
*/
protected function makeSelectionFormWidget($modelCode, $options = [])
{
if (!strlen($this->selectFormConfigFile)) {
throw new ApplicationException(sprintf('Base form configuration file is not specified for %s behavior', get_class($this)));
}
$widgetConfig = $this->makeConfig($this->selectFormConfigFile);
$widgetConfig->model = $this->loadOrCreateBaseModel($modelCode, $options);
return $this->makeWidget(\Backend\Widgets\Form::class, $widgetConfig);
}
}

View File

@ -1,7 +1,7 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\LocalizationModel;
use RainLab\Builder\Classes\LocalizationModel;
use RainLab\Builder\Classes\PluginCode;
use ApplicationException;
use Exception;
@ -18,7 +18,7 @@ use Lang;
*/
class IndexLocalizationOperations extends IndexOperationsBehaviorBase
{
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/localizationmodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/localizationmodel/fields.yaml';
public function onLanguageCreateOrOpen()
{

View File

@ -1,31 +1,25 @@
<?php namespace RainLab\Builder\Behaviors;
use BackendMenu;
use System\Classes\PluginManager;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\MenusModel;
use RainLab\Builder\Classes\MenusModel;
use RainLab\Builder\Classes\PluginCode;
use Throwable;
use ApplicationException;
use Exception;
use Request;
use Flash;
use Input;
use Lang;
/**
* IndexMenusOperations is plugin backend menu management functionality for the Builder index controller
* Plugin back-end menu management functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class IndexMenusOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/menusmodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/menusmodel/fields.yaml';
/**
* onMenusOpen
*/
public function onMenusOpen()
{
$pluginCodeObj = $this->getPluginCode();
@ -46,9 +40,6 @@ class IndexMenusOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onMenusSave
*/
public function onMenusSave()
{
$pluginCodeObj = new PluginCode(Request::input('plugin_code'));
@ -66,36 +57,19 @@ class IndexMenusOperations extends IndexOperationsBehaviorBase
'tabTitle' => $model->getPluginName().'/'.Lang::get('rainlab.builder::lang.menu.tab'),
];
// Feature is nice to have, only supported in >3.3.9
try {
PluginManager::instance()->reloadPlugins();
BackendMenu::resetCache();
$result['mainMenu'] = $this->controller->makeLayoutPartial('mainmenu');
$result['mainMenuLeft'] = $this->controller->makeLayoutPartial('mainmenu', ['isVerticalMenu'=>true]);
}
catch (Throwable $ex) {}
return $result;
}
/**
* getTabId
*/
protected function getTabId($pluginCode)
{
return 'menus-'.$pluginCode;
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($pluginCode, $options = [])
{
$model = new MenusModel();
$model->loadPlugin($pluginCode);
return $model;
}
}

View File

@ -1,12 +1,15 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\ModelFormModel;
use RainLab\Builder\Classes\ModelFormModel;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\FormWidgets\FormBuilder;
use RainLab\Builder\Models\ModelModel;
use RainLab\Builder\Classes\ModelModel;
use RainLab\Builder\Classes\ControlLibrary;
use Backend\Classes\FormField;
use Backend\FormWidgets\DataTable;
use ApplicationException;
use Exception;
use Request;
use Flash;
use Input;
@ -20,14 +23,8 @@ use Lang;
*/
class IndexModelFormOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/modelformmodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/modelformmodel/fields.yaml';
/**
* __construct
*/
public function __construct($controller)
{
parent::__construct($controller);
@ -35,14 +32,11 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
// Create the form builder instance to handle AJAX
// requests.
$defaultBuilderField = new FormField('default', 'default');
$formBuilder = new FormBuilder($controller, $defaultBuilderField);
$formBuilder->alias = 'defaultFormBuilder';
$formBuilder->bindToController();
$formBulder = new FormBuilder($controller, $defaultBuilderField);
$formBulder->alias = 'defaultFormBuilder';
$formBulder->bindToController();
}
/**
* onModelFormCreateOrOpen
*/
public function onModelFormCreateOrOpen()
{
$fileName = Input::get('file_name');
@ -73,9 +67,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onModelFormSave
*/
public function onModelFormSave()
{
$model = $this->loadOrCreateFormFromPost();
@ -99,9 +90,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onModelFormDelete
*/
public function onModelFormDelete()
{
$model = $this->loadOrCreateFormFromPost();
@ -116,9 +104,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onModelFormGetModelFields
*/
public function onModelFormGetModelFields()
{
$columnNames = ModelModel::getModelFields($this->getPluginCode(), Input::get('model_class'));
@ -144,9 +129,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
];
}
/**
* onModelShowAddDatabaseFieldsPopup
*/
public function onModelShowAddDatabaseFieldsPopup()
{
$columns = ModelModel::getModelColumnsAndTypes($this->getPluginCode(), Input::get('model_class'));
@ -165,9 +147,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
]);
}
/**
* loadOrCreateFormFromPost
*/
protected function loadOrCreateFormFromPost()
{
$pluginCode = Request::input('plugin_code');
@ -182,9 +161,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
return $this->loadOrCreateBaseModel($fileName, $options);
}
/**
* getTabId
*/
protected function getTabId($modelClass, $fileName)
{
if (!strlen($fileName)) {
@ -194,9 +170,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
return 'modelForm-'.$modelClass.'-'.$fileName;
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($fileName, $options = [])
{
$model = new ModelFormModel();
@ -216,9 +189,6 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
return $model;
}
/**
* mergeRegistryDataIntoResult
*/
protected function mergeRegistryDataIntoResult(&$result, $model, $modelClass)
{
if (!array_key_exists('builderResponseData', $result)) {
@ -235,8 +205,8 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
}
/**
* getAddDatabaseFieldsDataTableConfig returns the configuration for the DataTable widget
* that is used in the "add database fields" popup.
* Returns the configuration for the DataTable widget that
* is used in the "add database fields" popup.
*
* @return array
*/
@ -272,10 +242,11 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
}
/**
* getAddDatabaseFieldsDataTableValue returns the initial value for the DataTable widget that
* Returns the initial value for the DataTable widget that
* is used in the "add database columns" popup.
*
* @param array $columns
*
* @return array
*/
protected function getAddDatabaseFieldsDataTableValue(array $columns)
@ -297,7 +268,7 @@ class IndexModelFormOperations extends IndexOperationsBehaviorBase
'double' => 'number',
];
return array_map(function($column) use ($typeMap) {
return array_map(function ($column) use ($typeMap) {
return [
'column' => $column['name'],
'label' => str_replace('_', ' ', ucfirst($column['name'])),

View File

@ -1,49 +1,26 @@
<?php namespace RainLab\Builder\Behaviors;
use Str;
use Lang;
use Input;
use Flash;
use Request;
use System\Classes\PluginManager;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\ModelListModel;
use RainLab\Builder\Models\ModelModel;
use RainLab\Builder\Classes\ModelListModel;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\Classes\ModelModel;
use ApplicationException;
use Exception;
use Request;
use Flash;
use Input;
use Lang;
/**
* IndexModelListOperations provides model list management functionality for the Builder index controller
* Model list management functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class IndexModelListOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/modellistmodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/modellistmodel/fields.yaml';
/**
* extendBaseFormWidgetConfig
*/
protected function extendBaseFormWidgetConfig($config)
{
$typeOptions = array_get($config->tabs, 'fields.columns.columns.type.options');
$pluginColumns = PluginManager::instance()->getRegistrationMethodValues('registerListColumnTypes');
foreach ($pluginColumns as $customColumns) {
foreach (array_keys($customColumns) as $customColumn) {
$typeOptions[$customColumn] = __(Str::studly($customColumn));
}
}
array_set($config->tabs, 'fields.columns.columns.type.options', $typeOptions);
return $config;
}
/**
* onModelListCreateOrOpen
*/
public function onModelListCreateOrOpen()
{
$fileName = Input::get('file_name');
@ -74,9 +51,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onModelListSave
*/
public function onModelListSave()
{
$model = $this->loadOrCreateListFromPost();
@ -99,9 +73,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onModelListDelete
*/
public function onModelListDelete()
{
$model = $this->loadOrCreateListFromPost();
@ -116,9 +87,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* onModelListGetModelFields
*/
public function onModelListGetModelFields()
{
$columnNames = ModelModel::getModelFields($this->getPluginCode(), Input::get('model_class'));
@ -138,9 +106,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
];
}
/**
* onModelListLoadDatabaseColumns
*/
public function onModelListLoadDatabaseColumns()
{
$columns = ModelModel::getModelColumnsAndTypes($this->getPluginCode(), Input::get('model_class'));
@ -152,9 +117,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
];
}
/**
* loadOrCreateListFromPost
*/
protected function loadOrCreateListFromPost()
{
$pluginCode = Request::input('plugin_code');
@ -169,9 +131,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
return $this->loadOrCreateBaseModel($fileName, $options);
}
/**
* getTabId
*/
protected function getTabId($modelClass, $fileName)
{
if (!strlen($fileName)) {
@ -181,9 +140,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
return 'modelList-'.$modelClass.'-'.$fileName;
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($fileName, $options = [])
{
$model = new ModelListModel();
@ -203,9 +159,6 @@ class IndexModelListOperations extends IndexOperationsBehaviorBase
return $model;
}
/**
* mergeRegistryDataIntoResult
*/
protected function mergeRegistryDataIntoResult(&$result, $model, $modelClass)
{
if (!array_key_exists('builderResponseData', $result)) {

View File

@ -1,26 +1,23 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\ModelModel;
use RainLab\Builder\Classes\ModelModel;
use Backend\Behaviors\FormController;
use ApplicationException;
use Exception;
use Request;
use Input;
/**
* IndexModelOperations is model management functionality for the Builder index controller
* Model management functionality for the Builder index controller
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class IndexModelOperations extends IndexOperationsBehaviorBase
{
/**
* @var string baseFormConfigFile
*/
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/modelmodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/modelmodel/fields.yaml';
/**
* onModelLoadPopup
*/
public function onModelLoadPopup()
{
$pluginCodeObj = $this->getPluginCode();
@ -38,9 +35,6 @@ class IndexModelOperations extends IndexOperationsBehaviorBase
return $this->makePartial('model-popup-form');
}
/**
* onModelSave
*/
public function onModelSave()
{
$pluginCode = Request::input('plugin_code');
@ -65,9 +59,6 @@ class IndexModelOperations extends IndexOperationsBehaviorBase
return $result;
}
/**
* loadOrCreateBaseModel
*/
protected function loadOrCreateBaseModel($className, $options = [])
{
// Editing model is not supported, always return

View File

@ -1,7 +1,7 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\PermissionsModel;
use RainLab\Builder\Classes\PermissionsModel;
use RainLab\Builder\Classes\PluginCode;
use ApplicationException;
use Exception;
@ -18,7 +18,7 @@ use Lang;
*/
class IndexPermissionsOperations extends IndexOperationsBehaviorBase
{
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/permissionsmodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/permissionsmodel/fields.yaml';
public function onPermissionsOpen()
{

View File

@ -1,8 +1,10 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\PluginBaseModel;
use RainLab\Builder\Classes\PluginBaseModel;
use Backend\Behaviors\FormController;
use ApplicationException;
use Exception;
use Input;
/**
@ -13,7 +15,7 @@ use Input;
*/
class IndexPluginOperations extends IndexOperationsBehaviorBase
{
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/pluginbasemodel/fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/pluginbasemodel/fields.yaml';
public function onPluginLoadPopup()
{
@ -76,7 +78,7 @@ class IndexPluginOperations extends IndexOperationsBehaviorBase
protected function loadOrCreateBaseModel($pluginCode, $options = [])
{
$model = new PluginBaseModel;
$model = new PluginBaseModel();
if (!$pluginCode) {
$model->initDefaults();

View File

@ -1,7 +1,7 @@
<?php namespace RainLab\Builder\Behaviors;
use RainLab\Builder\Classes\IndexOperationsBehaviorBase;
use RainLab\Builder\Models\MigrationModel;
use RainLab\Builder\Classes\MigrationModel;
use RainLab\Builder\Classes\PluginCode;
use ApplicationException;
use Exception;
@ -18,7 +18,7 @@ use Lang;
*/
class IndexVersionsOperations extends IndexOperationsBehaviorBase
{
protected $baseFormConfigFile = '~/plugins/rainlab/builder/models/migrationmodel/management-fields.yaml';
protected $baseFormConfigFile = '~/plugins/rainlab/builder/classes/migrationmodel/management-fields.yaml';
public function onVersionCreateOrOpen()
{

View File

@ -1,10 +0,0 @@
<?= Form::open([
'class' => 'layout',
'data-change-monitor' => 'true',
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
'data-entity' => 'code',
'onsubmit' => 'return false'
]) ?>
<?= $form->render() ?>
<input type="hidden" name="plugin_code" value="<?= e($pluginCode) ?>">
<?= Form::close() ?>

View File

@ -1,10 +0,0 @@
<div class="form-buttons loading-indicator-container">
<a
href="javascript:;"
class="btn btn-primary oc-icon-check save"
data-builder-command="code:cmdSaveCode"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
data-hotkey="ctrl+s, cmd+s">
<?= e(trans('backend::lang.form.save')) ?>
</a>
</div>

View File

@ -2,9 +2,9 @@
'data-builder-command'=>'controller:cmdCreateController',
'data-plugin-code' => $pluginCode
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.controller.new_controller')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">
<?= $form->render() ?>

View File

@ -2,9 +2,9 @@
'data-builder-command'=>'databaseTable:cmdSaveMigration',
'id'=>'builderTableMigrationPopup'
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.migration.entity_name')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<?php if (!isset($noChanges)): ?>

View File

@ -1,26 +0,0 @@
<?= Form::open([
'data-builder-command'=>'imports:cmdSaveImports',
'data-plugin-code' => $pluginCode
]) ?>
<div class="modal-header">
<h4 class="modal-title"><?= __("Import Options") ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">
<?= $form->render() ?>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary">
<?= __("Import") ?>
</button>
<button
type="button"
class="btn btn-default"
data-dismiss="popup">
<?= e(trans('backend::lang.form.cancel')) ?>
</button>
</div>
<input type="hidden" name="plugin_code" value="<?= e($pluginCode) ?>">
<?= Form::close() ?>

View File

@ -1,15 +0,0 @@
<?= Form::open([
'class' => 'layout',
'data-change-monitor' => 'true',
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
'data-entity' => 'imports',
'onsubmit' => 'return false'
]) ?>
<?= $form->render() ?>
<input type="hidden" name="operationClass" value="IndexImportsOperations" />
<input type="hidden" name="formWidgetAlias" value="<?= e($form->alias) ?>" />
<input type="hidden" name="plugin_code" value="<?= e($pluginCode) ?>" />
<?= Form::close() ?>

View File

@ -1,17 +0,0 @@
<div class="form-buttons loading-indicator-container">
<a
href="javascript:;"
class="btn btn-primary oc-icon-check save"
data-builder-command="imports:cmdConfirmImports"
data-load-indicator="<?= __("Importing") ?>"
data-hotkey="ctrl+s, cmd+s">
<?= __("Import") ?>
</a>
<a
href="javascript:;"
class="btn btn-default oc-icon-database"
data-builder-command="imports:cmdMigrateDatabase"
data-load-indicator="<?= __("Migrating Database") ?>">
<?= __("Migrate Database") ?>
</a>
</div>

View File

@ -1,9 +1,9 @@
<?= Form::open([
'data-builder-command'=>'localization:cmdCopyMissingStrings'
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.localization.add_missing_strings')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">

View File

@ -1,9 +1,9 @@
<?= Form::open([
'onSubmit'=>'return false'
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.localization.create_string')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">

View File

@ -2,9 +2,9 @@
'data-builder-command'=>'modelForm:cmdAddDatabaseFields'
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.form.btn_add_database_fields')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">

View File

@ -1,9 +1,9 @@
<?= Form::open([
'data-builder-command'=>'model:cmdApplyModelSettings'
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.model.entity_name')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">
<?php if (!isset($errorMessage)): ?>

View File

@ -1,9 +1,9 @@
<?= Form::open([
'data-builder-command'=>'plugin:cmdApplyPluginSettings'
]) ?>
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('rainlab.builder::lang.plugin.entity_name')) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<div class="modal-body">
<?php if (!isset($errorMessage)): ?>

View File

@ -14,7 +14,7 @@
<input type="hidden" data-hint-hidden="builder-version-rollback" value="<?= $this->isBackendHintHidden('builder-version-rollback') ? 'true' : 'false' ?>">
<script type="text/template" data-version-hint-template="builder-version-save-unapplied">
<?= $this->makePartial('$/rainlab/builder/behaviors/indexversionsoperations/partials/_version-hint-block.php', [
<?= $this->makePartial('$/rainlab/builder/behaviors/indexversionsoperations/partials/_version-hint-block.htm', [
'title'=>trans('rainlab.builder::lang.version.save_unapplied_version'),
'text'=>trans('rainlab.builder::lang.version.hint_save_unapplied'),
'okOnlyButton' => true,
@ -23,7 +23,7 @@
</script>
<script type="text/template" data-version-hint-template="builder-version-rollback">
<?= $this->makePartial('$/rainlab/builder/behaviors/indexversionsoperations/partials/_version-hint-block.php', [
<?= $this->makePartial('$/rainlab/builder/behaviors/indexversionsoperations/partials/_version-hint-block.htm', [
'title'=>trans('rainlab.builder::lang.version.rollback_version'),
'text'=>trans('rainlab.builder::lang.version.hint_rollback'),
'code' => 'builder-version-rollback'
@ -31,10 +31,12 @@
</script>
<script type="text/template" data-version-hint-template="builder-version-apply">
<?= $this->makePartial('$/rainlab/builder/behaviors/indexversionsoperations/partials/_version-hint-block.php', [
<?= $this->makePartial('$/rainlab/builder/behaviors/indexversionsoperations/partials/_version-hint-block.htm', [
'title'=>trans('rainlab.builder::lang.version.apply_version'),
'text'=>trans('rainlab.builder::lang.version.hint_apply'),
'code' => 'builder-version-apply'
]) ?>
</script>
<?= Form::close() ?>

View File

@ -1,6 +1,6 @@
<div class="modal-header">
<div class="modal-header flex-row-reverse">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e($title) ?></h4>
<button type="button" class="btn-close" data-dismiss="popup"></button>
</div>
<form autocomplete="off">
<div class="modal-body">

View File

@ -1,7 +1,5 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\Classes\PluginVector;
use ValidationException;
use SystemException;
use Validator;
@ -22,24 +20,12 @@ abstract class BaseModel
*/
public $exists = false;
/**
* @var array validationRules
*/
protected $validationRules = [];
/**
* @var array validationMessages
*/
protected $validationMessages = [];
/**
* @var array fillable
*/
protected static $fillable = [];
/**
* @var array updatedData
*/
protected $updatedData = [];
/**
@ -47,9 +33,6 @@ abstract class BaseModel
*/
protected $pluginCodeObj = null;
/**
* fill
*/
public function fill(array $attributes)
{
$this->updatedData = [];
@ -75,9 +58,6 @@ abstract class BaseModel
}
}
/**
* validate
*/
public function validate()
{
$existingData = [];
@ -100,9 +80,6 @@ abstract class BaseModel
}
}
/**
* isNewModel
*/
public function isNewModel()
{
return $this->exists === false;
@ -126,16 +103,10 @@ abstract class BaseModel
$this->pluginCodeObj = $obj;
}
/**
* validateBeforeCreate
*/
protected function validateBeforeCreate()
{
}
/**
* getModelPluginName
*/
public function getModelPluginName()
{
$pluginCodeObj = $this->getPluginCodeObj();
@ -149,9 +120,6 @@ abstract class BaseModel
return null;
}
/**
* getPluginCodeObj
*/
public function getPluginCodeObj()
{
if (!$this->pluginCodeObj) {

View File

@ -5,7 +5,7 @@ use Backend\Classes\WidgetBase;
abstract class BehaviorDesignTimeProviderBase extends WidgetBase
{
/**
* Renders behavior body.
* Renders behaivor body.
* @param string $class Specifies the behavior class to render.
* @param array $properties Behavior property values.
* @param \RainLab\Builder\FormWidgets\ControllerBuilder $controllerBuilder ControllerBuilder widget instance.

View File

@ -1,30 +0,0 @@
<?php namespace RainLab\Builder\Classes;
use Backend\Classes\WidgetBase;
/**
* BlueprintDesignTimeProviderBase
*/
abstract class BlueprintDesignTimeProviderBase extends WidgetBase
{
/**
* renderBlueprintBody
* @param string $class Specifies the behavior class to render.
* @param array $properties Blueprint property values.
* @param object $blueprintObj
* @return string
*/
abstract public function renderBlueprintBody($class, $properties, $blueprintObj);
/**
* getPropertyValue
*/
protected function getPropertyValue($properties, $property)
{
if (array_key_exists($property, $properties)) {
return $properties[$property];
}
return null;
}
}

View File

@ -1,323 +0,0 @@
<?php namespace RainLab\Builder\Classes;
use Lang;
use File;
use Twig;
use Schema;
use RainLab\Builder\Classes\TailorBlueprintLibrary;
use ApplicationException;
use ValidationException;
use Throwable;
/**
* BlueprintGenerator is a helper class for generating controller class files and associated files.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class BlueprintGenerator
{
use \RainLab\Builder\Classes\BlueprintGenerator\HasMigrations;
use \RainLab\Builder\Classes\BlueprintGenerator\HasVersionFile;
use \RainLab\Builder\Classes\BlueprintGenerator\HasControllers;
use \RainLab\Builder\Classes\BlueprintGenerator\HasPermissions;
use \RainLab\Builder\Classes\BlueprintGenerator\HasNavigation;
use \RainLab\Builder\Classes\BlueprintGenerator\HasExpandoModels;
use \RainLab\Builder\Classes\BlueprintGenerator\HasModels;
/**
* @var object sourceModel is the imports model
*/
protected $sourceModel;
/**
* @var array sourceBlueprints are blueprint objects that can be saved to disk
*/
protected $sourceBlueprints = [];
/**
* @var array templateVars are used when rendering templates
*/
protected $templateVars;
/**
* @var array filesGenerated by this process
*/
protected $filesGenerated;
/**
* @var array filesValidated by this process
*/
protected $filesValidated;
/**
* @var array blueprintFiles to decommission
*/
protected $blueprintFiles = [];
/**
* @var array migrationScripts that have been generated
*/
protected $migrationScripts = [];
/**
* __construct
*/
public function __construct($source)
{
$this->sourceModel = $source;
}
/**
* generate
*/
public function inspect($blueprint)
{
$this->setBlueprintContext($blueprint);
$result = [
'controllerFile' => null,
'modelFiles' => [],
'migrationFiles' => [],
'errorMessage' => null
];
try {
if ($model = $this->makeControllerModel()) {
$result['controllerFile'] = $model->getControllerFilePath();
}
if ($model = $this->makeModelModel()) {
$result['modelFiles'][] = $model->getModelFilePath();
}
foreach ($this->makeExpandoModels(true) as $model) {
$result['modelFiles'][] = $model->getModelFilePath();
}
$result['migrationFiles'] = array_keys($this->inspectMigrations());
}
catch (Throwable $ex) {
$result['errorMessage'] = $ex->getMessage();
}
return $result;
}
/**
* generate
*/
public function generate()
{
$this->templateVars = [];
$this->filesGenerated = [];
$this->filesValidated = [];
$this->blueprintFiles = [];
$this->migrationScripts = [];
$this->sourceBlueprints = [];
$this->loadSourceBlueprints();
$this->validateNavigation();
// Validate
foreach ($this->sourceBlueprints as $blueprint) {
$this->setBlueprintContext($blueprint);
$this->validateModel();
$this->validateExpandoModels();
$this->validateController();
$this->validatePermission();
}
// Generate
try {
foreach ($this->sourceBlueprints as $blueprint) {
$this->setBlueprintContext($blueprint);
$this->generateMigrations();
$this->generateModel();
$this->generateExpandoModels();
$this->generateController();
$this->generatePermission();
$this->blueprintFiles[] = $blueprint->getFilePath();
}
}
catch (Throwable $ex) {
$this->rollback();
throw $ex;
}
$this->generateNavigation();
$this->generateVersionUpdate();
if ($this->sourceModel->deleteBlueprintData) {
$this->deleteGeneratedBlueprintData();
}
if ($this->sourceModel->disableBlueprints) {
$this->disableGeneratedBlueprints();
}
}
/**
* loadSourceBlueprints
*/
protected function loadSourceBlueprints()
{
$blueprintLib = TailorBlueprintLibrary::instance();
foreach ($this->sourceModel->blueprints as $uuid => $config) {
$blueprint = $blueprintLib->getBlueprintObject($uuid);
if ($blueprint) {
$this->sourceBlueprints[$uuid] = $blueprint;
}
}
}
/**
* setBlueprintContext
*/
protected function setBlueprintContext($blueprint)
{
$config = $this->sourceModel->blueprints[$blueprint->uuid] ?? [];
$this->sourceModel->setBlueprintContext($blueprint, $config);
$this->setTemplateVars();
}
/**
* disableGeneratedBlueprints
*/
protected function disableGeneratedBlueprints()
{
foreach ($this->blueprintFiles as $filePath) {
File::move(
$filePath,
str_replace('.yaml', '.yaml.bak', $filePath)
);
}
}
/**
* deleteGeneratedBlueprintData
*/
protected function deleteGeneratedBlueprintData()
{
foreach ($this->sourceBlueprints as $blueprint) {
$contentTable = $blueprint->getContentTableName();
Schema::dropIfExists($contentTable);
$joinTable = $blueprint->getJoinTableName();
Schema::dropIfExists($joinTable);
$repeaterTable = $blueprint->getRepeaterTableName();
Schema::dropIfExists($repeaterTable);
}
}
/**
* setTemplateVars
*/
protected function setTemplateVars()
{
$pluginCodeObj = $this->sourceModel->getPluginCodeObj();
$this->templateVars = $this->getConfig();
$this->templateVars['pluginNamespace'] = $pluginCodeObj->toPluginNamespace();
$this->templateVars['pluginCode'] = $pluginCodeObj->toCode();
}
/**
* getTemplatePath
*/
protected function getTemplatePath($template)
{
return __DIR__.'/blueprintgenerator/templates/'.$template;
}
/**
* parseTemplate
*/
protected function parseTemplate($templatePath, $vars = [])
{
$template = File::get($templatePath);
$vars = array_merge($this->templateVars, $vars);
$code = Twig::parse($template, $vars);
return $code;
}
/**
* writeFile
*/
protected function writeFile($path, $data)
{
$fileDirectory = dirname($path);
if (!File::isDirectory($fileDirectory)) {
if (!File::makeDirectory($fileDirectory, 0777, true, true)) {
throw new ApplicationException(Lang::get('rainlab.builder::lang.common.error_make_dir', [
'name' => $fileDirectory
]));
}
}
if (@File::put($path, $data) === false) {
throw new ApplicationException(Lang::get('rainlab.builder::lang.controller.error_save_file', [
'file' => basename($path)
]));
}
@File::chmod($path);
$this->filesGenerated[] = $path;
}
/**
* rollback
*/
protected function rollback()
{
foreach ($this->filesGenerated as $path) {
@unlink($path);
}
}
/**
* makeTabs
*/
protected function makeTabs($str)
{
return str_replace('\t', ' ', $str);
}
/**
* getConfig
*/
protected function getConfig($key = null, $default = null)
{
return $this->sourceModel->getBlueprintConfig($key, $default);
}
/**
* validateUniqueFiles
*/
protected function validateUniqueFiles(array $files)
{
if (!is_array($this->filesValidated)) {
$this->filesValidated = [];
}
foreach ($files as $path) {
if (File::isFile($path) || in_array($path, $this->filesValidated)) {
throw new ValidationException([
'modelClass' => __("File [:file] already exists when trying to import [:blueprint]", [
'file' => basename(dirname($path)) . '/' . basename($path),
'blueprint' => $this->sourceModel->getBlueprintObject()->handle ?? 'unknown'
])
]);
}
}
$this->filesValidated = array_merge($this->filesValidated, $files);
}
}

View File

@ -2,10 +2,9 @@
use Cache;
use Input;
use RainLab\Builder\Models\PluginBaseModel;
use RainLab\Builder\Models\ModelModel;
use ApplicationException;
use Exception;
use October\Rain\Support\Traits\Singleton;
use ApplicationException;
/**
* Provides helper methods for Builder CMS components.
@ -15,16 +14,10 @@ use Exception;
*/
class ComponentHelper
{
use \October\Rain\Support\Traits\Singleton;
use Singleton;
/**
* @var array|null modelListCache
*/
protected $modelListCache = null;
/**
* listGlobalModels
*/
public function listGlobalModels()
{
if ($this->modelListCache !== null) {
@ -66,9 +59,6 @@ class ComponentHelper
return $this->modelListCache = $result;
}
/**
* getModelClassDesignTime
*/
public function getModelClassDesignTime()
{
$modelClass = trim(Input::get('modelClass'));
@ -89,9 +79,6 @@ class ComponentHelper
return $modelClass;
}
/**
* listModelColumnNames
*/
public function listModelColumnNames()
{
$modelClass = $this->getModelClassDesignTime();

View File

@ -4,7 +4,7 @@ use Event;
use Lang;
/**
* ControlLibrary manages Builder form control library.
* Manages Builder form control library.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
@ -15,7 +15,6 @@ class ControlLibrary
const GROUP_STANDARD = 0;
const GROUP_WIDGETS = 1;
const GROUP_UI = 2;
const DEFAULT_DESIGN_TIME_PROVIDER = 'RainLab\Builder\Widgets\DefaultControlDesignTimeProvider';
protected $controls = [];
@ -23,7 +22,7 @@ class ControlLibrary
protected $groupedControls = null;
/**
* listControls returns a list of all known form controls grouped by control groups.
* Returns a list of all known form controls grouped by control groups.
* @param boolean $returnGrouped Indicates whether controls should be grouped in the result.
* @return array
*/
@ -35,8 +34,7 @@ class ControlLibrary
$this->groupedControls = [
$this->resolveControlGroupName(self::GROUP_STANDARD) => [],
$this->resolveControlGroupName(self::GROUP_WIDGETS) => [],
$this->resolveControlGroupName(self::GROUP_UI) => []
$this->resolveControlGroupName(self::GROUP_WIDGETS) => []
];
Event::fire('pages.builder.registerControls', [$this]);
@ -55,7 +53,7 @@ class ControlLibrary
}
/**
* getControlInfo returns information about a control by its code.
* Returns information about a control by its code.
* @param string $code Specifies the control code.
* @return array Returns an associative array or null if the control is not registered.
*/
@ -77,7 +75,7 @@ class ControlLibrary
}
/**
* registerControl registers a control.
* Registers a control.
* @param string $code Specifies the control code, for example "codeeditor".
* @param string $name Specifies the control name, for example "Code editor".
* @param string $description Specifies the control descritpion, can be empty.
@ -110,15 +108,18 @@ class ControlLibrary
];
}
/**
* getStandardProperties
*/
public function getStandardProperties($excludeProperties = [], $addProperties = [])
{
$result = [
'label' => [
'title' => Lang::get('rainlab.builder::lang.form.property_label_title'),
'type' => 'builderLocalization',
'validation' => [
// Label is technically not a required field -sg
// 'required' => [
// 'message' => Lang::get('rainlab.builder::lang.form.property_label_required')
// ]
]
],
'oc.comment' => [
'title' => Lang::get('rainlab.builder::lang.form.property_comment_title'),
@ -301,9 +302,6 @@ class ControlLibrary
return $result;
}
/**
* resolveControlGroupName
*/
protected function resolveControlGroupName($group)
{
if ($group === self::GROUP_STANDARD) {
@ -314,10 +312,6 @@ class ControlLibrary
return Lang::get('rainlab.builder::lang.form.control_group_widgets');
}
if ($group === self::GROUP_UI) {
return Lang::get('rainlab.builder::lang.form.control_group_ui');
}
return Lang::get($group);
}
}

View File

@ -17,9 +17,6 @@ class ControllerBehaviorLibrary
protected $behaviors = null;
/**
* getBehaviorInfo
*/
public function getBehaviorInfo($behaviorClassName)
{
$behaviors = $this->listBehaviors();
@ -66,9 +63,6 @@ class ControllerBehaviorLibrary
];
}
/**
* listBehaviors
*/
public function listBehaviors()
{
if ($this->behaviors !== null) {

View File

@ -1,29 +1,20 @@
<?php namespace RainLab\Builder\Classes;
/**
* ControllerFileParser parses controller source files.
* Parses controller source files.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class ControllerFileParser
{
/**
* @var object stream
*/
protected $stream;
/**
* @var __construct
*/
public function __construct($fileContents)
{
$this->stream = new PhpSourceStream($fileContents);
}
/**
* listBehaviors
*/
public function listBehaviors()
{
$this->stream->reset();
@ -40,9 +31,6 @@ class ControllerFileParser
}
}
/**
* getStringPropertyValue
*/
public function getStringPropertyValue($property)
{
$this->stream->reset();
@ -59,9 +47,6 @@ class ControllerFileParser
}
}
/**
* extractBehaviors
*/
protected function extractBehaviors()
{
if ($this->stream->getNextExpected(T_WHITESPACE) === null) {
@ -92,8 +77,8 @@ class ControllerFileParser
}
$result = [];
while ($line = $this->stream->getNextExpectedTerminated([T_CONSTANT_ENCAPSED_STRING, T_NAME_FULLY_QUALIFIED, T_WHITESPACE], [',', ']', ')'], [T_DOUBLE_COLON, T_CLASS])) {
$line = $this->stream->unquotePhpString(trim($line), $line);
while ($line = $this->stream->getNextExpectedTerminated([T_CONSTANT_ENCAPSED_STRING, T_WHITESPACE], [',', ']', ')'])) {
$line = $this->stream->unquotePhpString(trim($line));
if (!strlen($line)) {
continue;
}
@ -104,9 +89,6 @@ class ControllerFileParser
return $result;
}
/**
* extractPropertyValue
*/
protected function extractPropertyValue($property)
{
if ($this->stream->getNextExpected(T_WHITESPACE) === null) {
@ -135,9 +117,6 @@ class ControllerFileParser
return $value;
}
/**
* normalizeBehaviorClassName
*/
protected function normalizeBehaviorClassName($className)
{
$className = str_replace('.', '\\', trim($className));

View File

@ -9,54 +9,30 @@ use File;
use Twig;
/**
* ControllerGenerator is a helper class for generating controller class files and associated files.
* Helper class for generating controller class files and associated files.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class ControllerGenerator
{
/**
* @var BaseModel sourceModel
*/
protected $sourceModel;
/**
* @var array templateVars
*/
protected $templateVars;
/**
* @var array configTemplateProperties
*/
protected $configTemplateProperties = [];
/**
* @var array templateFiles
*/
protected $templateFiles = [];
/**
* @var array filesGenerated
*/
protected $filesGenerated;
/**
* @var array designTimeProviders
*/
protected $designTimeProviders = [];
/**
* __construct
*/
public function __construct($source)
{
$this->sourceModel = $source;
}
/**
* generate
*/
public function generate()
{
$this->filesGenerated = [];
@ -79,17 +55,11 @@ class ControllerGenerator
}
}
/**
* setTemplateVariable
*/
public function setTemplateVariable($var, $value)
{
$this->templateVars[$var] = $value;
}
/**
* validateBehaviorViewTemplates
*/
protected function validateBehaviorViewTemplates()
{
if (!$this->sourceModel->behaviors) {
@ -102,7 +72,7 @@ class ControllerGenerator
$behaviorLibrary = ControllerBehaviorLibrary::instance();
$knownTemplates = [];
foreach ($this->sourceModel->behaviors as $behaviorClass => $behaviorConfig) {
foreach ($this->sourceModel->behaviors as $behaviorClass) {
$behaviorInfo = $behaviorLibrary->getBehaviorInfo($behaviorClass);
if (!$behaviorInfo) {
throw new ValidationException([
@ -122,6 +92,8 @@ class ControllerGenerator
'view' => $templateBaseName
])
]);
throw new ApplicationException();
}
$knownTemplates[] = $templateFileName;
@ -150,9 +122,6 @@ class ControllerGenerator
}
}
/**
* validateBehaviorConfigSettings
*/
protected function validateBehaviorConfigSettings()
{
if (!$this->sourceModel->behaviors) {
@ -164,8 +133,8 @@ class ControllerGenerator
$controllerPath = $this->sourceModel->getControllerFilePath(true);
$behaviorLibrary = ControllerBehaviorLibrary::instance();
$knownConfigFiles = [];
foreach ($this->sourceModel->behaviors as $behaviorClass => $behaviorConfig) {
$knownConfgFiles = [];
foreach ($this->sourceModel->behaviors as $behaviorClass) {
$behaviorInfo = $behaviorLibrary->getBehaviorInfo($behaviorClass);
$configFileName = $behaviorInfo['configFileName'];
@ -173,15 +142,17 @@ class ControllerGenerator
continue;
}
if (in_array($configFileName, $knownConfigFiles)) {
if (in_array($configFileName, $knownConfgFiles)) {
throw new ValidationException([
'behaviors' => Lang::get('rainlab.builder::lang.controller.error_behavior_config_conflict', [
'file' => $configFileName
])
]);
throw new ApplicationException();
}
$knownConfigFiles[] = $configFileName;
$knownConfgFiles[] = $configFileName;
$destFilePath = $controllerPath.'/'.$configFileName;
if (File::isFile($destFilePath)) {
@ -197,25 +168,19 @@ class ControllerGenerator
}
}
/**
* validateControllerUnique
*/
protected function validateControllerUnique()
{
$controllerFilePath = $this->sourceModel->getControllerFilePath();
$controlerFilePath = $this->sourceModel->getControllerFilePath();
if (File::isFile($controllerFilePath)) {
if (File::isFile($controlerFilePath)) {
throw new ValidationException([
'controller' => Lang::get('rainlab.builder::lang.controller.error_controller_exists', [
'file' => basename($controllerFilePath)
'file' => basename($controlerFilePath)
])
]);
}
}
/**
* setTemplateVars
*/
protected function setTemplateVars()
{
$pluginCodeObj = $this->sourceModel->getPluginCodeObj();
@ -224,7 +189,6 @@ class ControllerGenerator
$this->templateVars['pluginCode'] = $pluginCodeObj->toCode();
$this->templateVars['permissions'] = $this->sourceModel->permissions;
$this->templateVars['controller'] = $this->sourceModel->controller;
$this->templateVars['controllerName'] = $this->sourceModel->controllerName;
$this->templateVars['baseModelClassName'] = $this->sourceModel->baseModelClassName;
$this->templateVars['controllerUrl'] = $pluginCodeObj->toUrl().'/'.strtolower($this->sourceModel->controller);
@ -240,7 +204,7 @@ class ControllerGenerator
}
if ($this->sourceModel->behaviors) {
$this->templateVars['behaviors'] = array_keys($this->sourceModel->behaviors);
$this->templateVars['behaviors'] = $this->sourceModel->behaviors;
}
else {
$this->templateVars['behaviors'] = [];
@ -249,17 +213,11 @@ class ControllerGenerator
$this->templateVars['behaviorConfigVars'] = $this->configTemplateProperties;
}
/**
* getTemplatePath
*/
protected function getTemplatePath($template)
{
return __DIR__.'/controllergenerator/templates/'.$template;
}
/**
* parseTemplate
*/
protected function parseTemplate($templatePath, $vars = [])
{
$template = File::get($templatePath);
@ -270,9 +228,6 @@ class ControllerGenerator
return $code;
}
/**
* writeFile
*/
protected function writeFile($path, $data)
{
$fileDirectory = dirname($path);
@ -294,9 +249,6 @@ class ControllerGenerator
$this->filesGenerated[] = $path;
}
/**
* rollback
*/
protected function rollback()
{
foreach ($this->filesGenerated as $path) {
@ -304,9 +256,6 @@ class ControllerGenerator
}
}
/**
* generateControllerFile
*/
protected function generateControllerFile()
{
$templateParts = [];
@ -327,27 +276,15 @@ class ControllerGenerator
$templateParts = "";
}
$noListTemplate = "";
if (
!array_key_exists(\Backend\Behaviors\ListController::class, $this->sourceModel->behaviors) &&
array_key_exists(\Backend\Behaviors\FormController::class, $this->sourceModel->behaviors)
) {
$noListTemplate = $this->parseTemplate($this->getTemplatePath('controller-no-list.php.tpl'));
}
$code = $this->parseTemplate($this->getTemplatePath('controller.php.tpl'), [
'templateParts' => $templateParts,
'noListTemplate' => $noListTemplate,
'templateParts' => $templateParts
]);
$controllerFilePath = $this->sourceModel->getControllerFilePath();
$controlerFilePath = $this->sourceModel->getControllerFilePath();
$this->writeFile($controllerFilePath, $code);
$this->writeFile($controlerFilePath, $code);
}
/**
* getBehaviorDesignTimeProvider
*/
protected function getBehaviorDesignTimeProvider($providerClass)
{
if (array_key_exists($providerClass, $this->designTimeProviders)) {
@ -357,9 +294,6 @@ class ControllerGenerator
return $this->designTimeProviders[$providerClass] = new $providerClass(null, []);
}
/**
* generateConfigFiles
*/
protected function generateConfigFiles()
{
if (!$this->sourceModel->behaviors) {
@ -370,7 +304,7 @@ class ControllerGenerator
$behaviorLibrary = ControllerBehaviorLibrary::instance();
$dumper = new YamlDumper();
foreach ($this->sourceModel->behaviors as $behaviorClass => $behaviorConfig) {
foreach ($this->sourceModel->behaviors as $behaviorClass) {
$behaviorInfo = $behaviorLibrary->getBehaviorInfo($behaviorClass);
$configFileName = $behaviorInfo['configFileName'];
@ -389,17 +323,12 @@ class ControllerGenerator
throw new ValidationException(['baseModelClassName' => $ex->getMessage()]);
}
$configArray = array_merge($configArray, $behaviorConfig);
$code = $dumper->dump($configArray, 20, 0, false, true);
$this->writeFile($destFilePath, $code);
}
}
/**
* generateViews
*/
protected function generateViews()
{
foreach ($this->templateFiles as $templatePath => $destPath) {

View File

@ -1,83 +1,48 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use Arr;
use ApplicationException;
use Symfony\Component\Yaml\Dumper as YamlDumper;
use SystemException;
use DirectoryIterator;
use Yaml;
use Exception;
use Lang;
use File;
use RainLab\Builder\Classes\ControllerBehaviorLibrary;
use RainLab\Builder\Classes\ControllerFileParser;
use RainLab\Builder\Classes\ControllerGenerator;
use RainLab\Builder\Models\ModelModel;
use RainLab\Builder\Classes\PluginCode;
use ApplicationException;
use DirectoryIterator;
use SystemException;
use Exception;
/**
* ControllerModel represents and manages plugin controllers.
* Represents and manages plugin controllers.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class ControllerModel extends BaseModel
{
/**
* @var string controller
*/
public $controller;
/**
* @var string controllerName
*/
public $controllerName;
/**
* @var array behaviors
*/
public $behaviors = [];
/**
* @var string baseModelClassName
*/
public $baseModelClassName;
/**
* @var array permissions
*/
public $permissions = [];
/**
* @var object menuItem
*/
public $menuItem;
/**
* @var array fillable
*/
protected static $fillable = [
'controller',
'controllerName',
'behaviors',
'baseModelClassName',
'permissions',
'menuItem'
];
/**
* @var array validationRules
*/
protected $validationRules = [
'controller' => ['regex:/^[A-Z]+[a-zA-Z0-9_]+$/']
];
/**
* load
*/
public function load($controller)
{
if (!$this->validateFileName($controller)) {
throw new SystemException("Invalid controller file name: {$controller}");
throw new SystemException('Invalid controller file name: '.$language);
}
$this->controller = $this->trimExtension($controller);
@ -85,15 +50,8 @@ class ControllerModel extends BaseModel
$this->exists = true;
}
/**
* save
*/
public function save()
{
if (!$this->controllerName) {
$this->controllerName = $this->controller;
}
if ($this->isNewModel()) {
$this->generateController();
}
@ -102,19 +60,11 @@ class ControllerModel extends BaseModel
}
}
/**
* fill
*/
public function fill(array $attributes)
{
parent::fill($attributes);
if (is_array($this->behaviors)) {
// Convert [1,2,3] to [1=>[], 2=>[], 3=>[]]
if (Arr::isList($this->behaviors)) {
$this->behaviors = array_combine($this->behaviors, array_fill(0, count($this->behaviors), []));
}
if (!$this->isNewModel() && is_array($this->behaviors)) {
foreach ($this->behaviors as $class => &$configuration) {
if (is_scalar($configuration)) {
$configuration = json_decode($configuration, true);
@ -123,9 +73,6 @@ class ControllerModel extends BaseModel
}
}
/**
* listPluginControllers
*/
public static function listPluginControllers($pluginCodeObj)
{
$controllersDirectoryPath = $pluginCodeObj->toPluginDirectoryPath().'/controllers';
@ -152,9 +99,6 @@ class ControllerModel extends BaseModel
return $result;
}
/**
* getBaseModelClassNameOptions
*/
public function getBaseModelClassNameOptions()
{
$models = ModelModel::listPluginModels($this->getPluginCodeObj());
@ -167,9 +111,6 @@ class ControllerModel extends BaseModel
return $result;
}
/**
* getBehaviorsOptions
*/
public function getBehaviorsOptions()
{
$library = ControllerBehaviorLibrary::instance();
@ -183,15 +124,9 @@ class ControllerModel extends BaseModel
];
}
// Support for this is added via import tool
unset($result[\Backend\Behaviors\ImportExportController::class]);
return $result;
}
/**
* getPermissionsOptions
*/
public function getPermissionsOptions()
{
$model = new PermissionsModel();
@ -211,9 +146,6 @@ class ControllerModel extends BaseModel
return $result;
}
/**
* getMenuItemOptions
*/
public function getMenuItemOptions()
{
$model = new MenusModel();
@ -248,9 +180,6 @@ class ControllerModel extends BaseModel
return $result;
}
/**
* getControllerFilePath
*/
public function getControllerFilePath($controllerFilesDirectory = false)
{
$pluginCodeObj = $this->getPluginCodeObj();
@ -263,9 +192,6 @@ class ControllerModel extends BaseModel
return $controllersDirectoryPath.'/'.strtolower($this->controller);
}
/**
* getPluginRegistryData
*/
public static function getPluginRegistryData($pluginCode, $subtype)
{
$pluginCodeObj = new PluginCode($pluginCode);
@ -274,8 +200,8 @@ class ControllerModel extends BaseModel
$controllers = self::listPluginControllers($pluginCodeObj);
$result = [];
foreach ($controllers as $controller) {
$controllerPath = strtolower(basename($controller));
foreach ($controllers as $controler) {
$controllerPath = strtolower(basename($controler));
$url = $urlBase.$controllerPath;
@ -285,9 +211,6 @@ class ControllerModel extends BaseModel
return $result;
}
/**
* saveController
*/
protected function saveController()
{
$this->validate();
@ -330,9 +253,6 @@ class ControllerModel extends BaseModel
}
}
/**
* generateController
*/
protected function generateController()
{
$this->validationMessages = [
@ -347,9 +267,6 @@ class ControllerModel extends BaseModel
$generator->generate();
}
/**
* loadControllerBehaviors
*/
protected function loadControllerBehaviors()
{
$filePath = $this->getControllerFilePath();
@ -370,6 +287,7 @@ class ControllerModel extends BaseModel
$this->behaviors = [];
foreach ($behaviors as $behaviorClass) {
$behaviorInfo = $library->getBehaviorInfo($behaviorClass);
if (!$behaviorInfo) {
continue;
}
@ -389,9 +307,6 @@ class ControllerModel extends BaseModel
}
}
/**
* loadBehaviorConfiguration
*/
protected function loadBehaviorConfiguration($fileName, $behaviorClass)
{
if (!preg_match('/^[a-z0-9\.\-_]+$/i', $fileName)) {
@ -411,20 +326,13 @@ class ControllerModel extends BaseModel
}
try {
$configuration = Yaml::parse(File::get($filePath));
if ($behaviorClass === \Backend\Behaviors\ImportExportController::class) {
$this->processImportExportBehaviorConfig($configuration, true);
}
return $configuration;
return Yaml::parse(File::get($filePath));
}
catch (Exception $ex) {
throw new ApplicationException(Lang::get('rainlab.builder::lang.controller.error_invalid_yaml_configuration', ['file'=>$fileName]));
}
}
/**
* saveBehaviorConfiguration
*/
protected function saveBehaviorConfiguration($fileName, $configuration, $behaviorClass)
{
if (!preg_match('/^[a-z0-9\.\-_]+$/i', $fileName)) {
@ -446,15 +354,9 @@ class ControllerModel extends BaseModel
}
}
if ($behaviorClass === \Backend\Behaviors\ImportExportController::class) {
$this->processImportExportBehaviorConfig($configuration);
}
elseif ($behaviorClass === \Backend\Behaviors\ListController::class) {
$this->processListBehaviorConfig($configuration);
}
$dumper = new YamlDumper();
if ($configuration !== null) {
$yamlData = Yaml::render($configuration);
$yamlData = $dumper->dump($configuration, 20, 0, false, true);
}
else {
$yamlData = '';
@ -467,9 +369,6 @@ class ControllerModel extends BaseModel
@File::chmod($filePath);
}
/**
* trimExtension
*/
protected function trimExtension($fileName)
{
if (substr($fileName, -4) == '.php') {
@ -479,9 +378,6 @@ class ControllerModel extends BaseModel
return $fileName;
}
/**
* validateFileName
*/
protected function validateFileName($fileName)
{
if (!preg_match('/^[a-z0-9\.\-_]+$/i', $fileName)) {
@ -495,78 +391,4 @@ class ControllerModel extends BaseModel
return true;
}
/**
* processListBehaviorConfig converts booleans
*/
protected function processListBehaviorConfig(array &$configuration, $isLoad = false)
{
if (!isset($configuration['structure'])) {
return;
}
$booleanFields = [
'showTree',
'treeExpanded',
'showReorder',
'showSorting',
'dragRow',
];
foreach ($booleanFields as $booleanField) {
if (!array_key_exists($booleanField, $configuration['structure'])) {
continue;
}
$value = $configuration['structure'][$booleanField];
if ($value == '1' || $value == 'true') {
$value = true;
}
else {
$value = false;
}
$configuration['structure'][$booleanField] = $value;
}
$numericFields = [
'maxDepth'
];
foreach ($numericFields as $numericField) {
if (!array_key_exists($numericField, $configuration['structure'])) {
continue;
}
$configuration['structure'][$numericField] = +$configuration['structure'][$numericField];
}
}
/**
* processImportExportBehaviorConfig converts import. and export. keys to and from their config
*/
protected function processImportExportBehaviorConfig(array &$configuration, $isLoad = false)
{
if ($isLoad) {
foreach ($configuration as $key => $value) {
if (!is_array($value) || !in_array($key, ['import', 'export'])) {
continue;
}
foreach ($value as $k => $v) {
$configuration[$key.'.'.$k] = $v;
}
unset($configuration[$key]);
}
}
else {
foreach ($configuration as $key => $value) {
if (starts_with($key, ['import.', 'export.'])) {
array_set($configuration, $key, array_pull($configuration, $key));
}
}
}
}
}

View File

@ -1,10 +1,5 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Classes\DatabaseTableSchemaCreator;
use RainLab\Builder\Classes\EnumDbType;
use RainLab\Builder\Classes\MigrationColumnType;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\Classes\TableMigrationCodeGenerator;
use Doctrine\DBAL\Types\Type;
use ApplicationException;
use ValidationException;
@ -17,16 +12,13 @@ use Str;
use Db;
/**
* DatabaseTableModel manages plugin database tables.
* Manages plugin database tables.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class DatabaseTableModel extends BaseModel
{
/**
* @var array columns
*/
public $columns = [];
/**
@ -165,9 +157,6 @@ class DatabaseTableModel extends BaseModel
return parent::validate();
}
/**
* generateCreateOrUpdateMigration
*/
public function generateCreateOrUpdateMigration()
{
$schemaCreator = new DatabaseTableSchemaCreator();
@ -187,9 +176,6 @@ class DatabaseTableModel extends BaseModel
return $this->createMigrationObject($migrationCode, sprintf($description, $tableName));
}
/**
* generateDropMigration
*/
public function generateDropMigration()
{
$existingSchema = $this->tableInfo;
@ -199,9 +185,6 @@ class DatabaseTableModel extends BaseModel
return $this->createMigrationObject($migrationCode, sprintf('Drop table %s', $this->name));
}
/**
* getSchema
*/
public static function getSchema()
{
if (!self::$schema) {
@ -211,9 +194,6 @@ class DatabaseTableModel extends BaseModel
return self::$schema;
}
/**
* validateColumns
*/
protected function validateColumns()
{
$this->validateColumnNameLengths();
@ -225,9 +205,6 @@ class DatabaseTableModel extends BaseModel
$this->validateDefaultValues();
}
/**
* validateColumnNameLengths
*/
protected function validateColumnNameLengths()
{
foreach ($this->columns as $column) {
@ -244,9 +221,6 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* validateDuplicateColumns
*/
protected function validateDuplicateColumns()
{
foreach ($this->columns as $outerIndex => $outerColumn) {
@ -263,9 +237,6 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* validateDuplicatePrimaryKeys
*/
protected function validateDuplicatePrimaryKeys()
{
$keysFound = 0;
@ -287,9 +258,6 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* validateAutoIncrementColumns
*/
protected function validateAutoIncrementColumns()
{
$autoIncrement = null;
@ -318,9 +286,6 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* validateUnsignedColumns
*/
protected function validateUnsignedColumns()
{
foreach ($this->columns as $column) {
@ -336,16 +301,12 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* validateColumnsLengthParameter
*/
protected function validateColumnsLengthParameter()
{
foreach ($this->columns as $column) {
try {
MigrationColumnType::validateLength($column['type'], $column['length']);
}
catch (Exception $ex) {
} catch (Exception $ex) {
throw new ValidationException([
'columns' => $ex->getMessage()
]);
@ -353,9 +314,6 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* validateDefaultValues
*/
protected function validateDefaultValues()
{
foreach ($this->columns as $column) {
@ -401,15 +359,12 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* getSchemaManager
*/
protected static function getSchemaManager()
{
if (!self::$schemaManager) {
self::$schemaManager = Schema::getConnection()->getDoctrineSchemaManager();
Type::addType('enumdbtype', \RainLab\Builder\Classes\EnumDbType::class);
Type::addType('enumdbtype', 'RainLab\Builder\Classes\EnumDbType');
// Fixes the problem with enum column type not supported
// by Doctrine (https://github.com/laravel/framework/issues/1346)
@ -421,9 +376,6 @@ class DatabaseTableModel extends BaseModel
return self::$schemaManager;
}
/**
* loadColumnsFromTableInfo
*/
protected function loadColumnsFromTableInfo()
{
$this->columns = [];
@ -460,9 +412,6 @@ class DatabaseTableModel extends BaseModel
}
}
/**
* createMigrationObject
*/
protected function createMigrationObject($code, $description)
{
$migration = new MigrationModel();

View File

@ -1,7 +1,6 @@
<?php namespace RainLab\Builder\Classes;
use Doctrine\DBAL\Schema\Table;
use RainLab\Builder\Models\BaseModel;
/**
* Creates Doctrine table schema basing on the column information array.

View File

@ -1,6 +1,7 @@
<?php namespace RainLab\Builder\Classes;
use Backend\Classes\ControllerBehavior;
use Backend\Behaviors\FormController;
use ApplicationException;
/**
@ -16,18 +17,6 @@ abstract class IndexOperationsBehaviorBase extends ControllerBehavior
*/
protected $baseFormConfigFile = null;
/**
* bindFormWidgetToController used for AJAX requests
*/
public function bindFormWidgetToController($alias = null)
{
$pluginCodeObj = $this->getPluginCode();
$pluginCode = $pluginCodeObj->toCode();
$widget = $this->makeBaseFormWidget($pluginCode, ['alias' => $alias]);
$widget->bindToController();
return $widget;
}
/**
* makeBaseFormWidget
*/
@ -39,7 +28,7 @@ abstract class IndexOperationsBehaviorBase extends ControllerBehavior
$widgetConfig = $this->makeConfig($this->baseFormConfigFile);
$widgetConfig->model = $this->loadOrCreateBaseModel($modelCode, $options);
$widgetConfig->alias = $options['alias'] ?? 'form_'.md5(get_class($this)).uniqid();
$widgetConfig->alias = 'form_'.md5(get_class($this)).uniqid();
$widgetConfig = $this->extendBaseFormWidgetConfig($widgetConfig);

View File

@ -1,7 +1,5 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Classes\LanguageMixer;
use RainLab\Builder\Classes\PhpSourceStream;
use ApplicationException;
use Symfony\Component\Yaml\Dumper as YamlDumper;
use SystemException;
@ -133,7 +131,7 @@ class LocalizationModel extends BaseModel
public function initContent()
{
$templatePath = '$/rainlab/builder/models/localizationmodel/templates/lang.php';
$templatePath = '$/rainlab/builder/classes/localizationmodel/templates/lang.php';
$templatePath = File::symbolizePath($templatePath);
$strings = include($templatePath);

View File

@ -1,4 +1,4 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use ApplicationException;
use SystemException;
@ -13,24 +13,12 @@ use Lang;
*/
class MenusModel extends PluginYamlModel
{
/**
* @var array menus
*/
public $menus = [];
/**
* @var string yamlSection
*/
protected $yamlSection = 'navigation';
/**
* @var object pluginCodeObj
*/
protected $pluginCodeObj;
/**
* @var array fillable
*/
protected static $fillable = [
'menus'
];
@ -41,7 +29,7 @@ class MenusModel extends PluginYamlModel
protected $preserveOriginal = false;
/**
* modelToYamlArray converts the model's data to an array before it's saved to a YAML file.
* Converts the model's data to an array before it's saved to a YAML file.
* @return array
*/
protected function modelToYamlArray()
@ -83,19 +71,13 @@ class MenusModel extends PluginYamlModel
return $fileMenus;
}
/**
* validate
*/
public function validate()
{
parent::validate();
$this->validateDuplicateMenus();
$this->validateDupicateMenus();
}
/**
* fill
*/
public function fill(array $attributes)
{
if (!is_array($attributes['menus'])) {
@ -109,16 +91,13 @@ class MenusModel extends PluginYamlModel
return parent::fill($attributes);
}
/**
* setPluginCodeObj
*/
public function setPluginCodeObj($pluginCodeObj)
{
$this->pluginCodeObj = $pluginCodeObj;
}
/**
* yamlArrayToModel loads the model's data from an array.
* Load the model's data from an array.
* @param array $array An array to load the model fields from.
*/
protected function yamlArrayToModel($array)
@ -147,9 +126,6 @@ class MenusModel extends PluginYamlModel
$this->menus = $menus;
}
/**
* trimMenuProperties
*/
protected function trimMenuProperties($menu)
{
array_walk($menu, function ($value, $key) {
@ -164,7 +140,7 @@ class MenusModel extends PluginYamlModel
}
/**
* getFilePath returns a file path to save the model to.
* Returns a file path to save the model to.
* @return string Returns a path.
*/
protected function getFilePath()
@ -176,10 +152,7 @@ class MenusModel extends PluginYamlModel
return $this->pluginCodeObj->toPluginFilePath();
}
/**
* validateDuplicateMenus
*/
protected function validateDuplicateMenus()
protected function validateDupicateMenus()
{
foreach ($this->menus as $outerIndex => $mainMenuItem) {
$mainMenuItem = $this->trimMenuProperties($mainMenuItem);
@ -218,9 +191,6 @@ class MenusModel extends PluginYamlModel
}
}
/**
* codeExistsInList
*/
protected function codeExistsInList($codeIndex, $code, $list)
{
foreach ($list as $index => $item) {

View File

@ -2,12 +2,11 @@
use SystemException;
use ApplicationException;
use Doctrine\DBAL\Types\Types as DoctrineType;
use RainLab\Builder\Models\BaseModel;
use Doctrine\DBAL\Types\Type as DoctrineType;
use Lang;
/**
* MigrationColumnType represents a database column type used in migrations.
* Represents a database column type used in migrations.
*
* Important: some Doctrine types map to multiple migration types, for example -
* Doctrine boolean could be boolean and tinyInteger in migrations.
@ -47,9 +46,6 @@ class MigrationColumnType extends BaseModel
const REGEX_LENGTH_SINGLE = '/^([0-9]+)$/';
const REGEX_LENGTH_DOUBLE = '/^([0-9]+)\,([0-9]+)$/';
/**
* getIntegerTypes
*/
public static function getIntegerTypes()
{
return [
@ -59,9 +55,6 @@ class MigrationColumnType extends BaseModel
];
}
/**
* getDecimalTypes
*/
public static function getDecimalTypes()
{
return [
@ -70,19 +63,16 @@ class MigrationColumnType extends BaseModel
];
}
/**
* getDoctrineTypeMap
*/
public static function getDoctrineTypeMap()
{
return [
self::TYPE_INTEGER => DoctrineType::INTEGER,
self::TYPE_SMALLINTEGER => DoctrineType::SMALLINT,
self::TYPE_BIGINTEGER => DoctrineType::BIGINT,
self::TYPE_DATE => DoctrineType::DATE_MUTABLE,
self::TYPE_TIME => DoctrineType::TIME_MUTABLE,
self::TYPE_DATETIME => DoctrineType::DATETIME_MUTABLE,
self::TYPE_TIMESTAMP => DoctrineType::DATETIME_MUTABLE,
self::TYPE_DATE => DoctrineType::DATE,
self::TYPE_TIME => DoctrineType::TIME,
self::TYPE_DATETIME => DoctrineType::DATETIME,
self::TYPE_TIMESTAMP => DoctrineType::DATETIME,
self::TYPE_STRING => DoctrineType::STRING,
self::TYPE_TEXT => DoctrineType::TEXT,
self::TYPE_BINARY => DoctrineType::BLOB,
@ -122,7 +112,7 @@ class MigrationColumnType extends BaseModel
// Some guessing could be required in this method. The method is not
// 100% reliable.
if ($type == DoctrineType::DATETIME_MUTABLE) {
if ($type == DoctrineType::DATETIME) {
// The datetime type maps to datetime and timestamp. Use the name
// guessing as the only possible solution.
@ -213,7 +203,7 @@ class MigrationColumnType extends BaseModel
}
/**
* doctrineLengthToMigrationLength converts Doctrine length, precision and scale to migration-compatible length string
* Converts Doctrine length, precision and scale to migration-compatible length string
* @return string
*/
public static function doctrineLengthToMigrationLength($column)

View File

@ -1,12 +1,10 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use Str;
use Lang;
use File;
use Yaml;
use Validator;
use RainLab\Builder\Classes\MigrationFileParser;
use RainLab\Builder\Classes\PluginVersion;
use System\Classes\VersionManager;
use October\Rain\Parse\Bracket as TextParser;
use ApplicationException;
@ -38,9 +36,6 @@ class MigrationModel extends BaseModel
*/
public $code;
/**
* @var string originalVersion
*/
protected $originalVersion;
/**
@ -50,32 +45,20 @@ class MigrationModel extends BaseModel
*/
public $scriptFileName;
/**
* @var string originalScriptFileName
*/
public $originalScriptFileName;
/**
* @var array fillable
*/
protected static $fillable = [
'version',
'description',
'code'
];
/**
* @var array validationRules
*/
protected $validationRules = [
'version' => ['required', 'regex:/^[0-9]+\.[0-9]+\.[0-9]+$/', 'uniqueVersion'],
'description' => ['required'],
'scriptFileName' => ['regex:/^[a-z]+[a-z0-9_]+$/']
];
/**
* validate
*/
public function validate()
{
$isNewModel = $this->isNewModel();
@ -104,9 +87,6 @@ class MigrationModel extends BaseModel
return parent::validate();
}
/**
* getNextVersion
*/
public function getNextVersion()
{
$versionInformation = $this->getPluginVersionInformation();
@ -127,7 +107,7 @@ class MigrationModel extends BaseModel
}
/**
* save the migration and applies all outstanding migrations for the plugin.
* Saves the migration and applies all outstanding migrations for the plugin.
*/
public function save($executeOnSave = true)
{
@ -141,8 +121,7 @@ class MigrationModel extends BaseModel
try {
$originalVersionData = $this->insertOrUpdateVersion();
}
catch (Exception $ex) {
} catch (Exception $ex) {
// Remove the script file, but don't rollback
// the version.yaml.
$this->rollbackSaving(null, $originalFileContents);
@ -167,9 +146,6 @@ class MigrationModel extends BaseModel
$this->exists = true;
}
/**
* load
*/
public function load($versionNumber)
{
$versionNumber = trim($versionNumber);
@ -211,9 +187,6 @@ class MigrationModel extends BaseModel
$this->originalScriptFileName = $this->scriptFileName;
}
/**
* initVersion
*/
public function initVersion($versionType)
{
$versionTypes = ['migration', 'seeder', 'custom'];
@ -234,7 +207,7 @@ class MigrationModel extends BaseModel
'seeder' => 'seeder.php.tpl'
];
$templatePath = '$/rainlab/builder/models/migrationmodel/templates/'.$templateFiles[$versionType];
$templatePath = '$/rainlab/builder/classes/migrationmodel/templates/'.$templateFiles[$versionType];
$templatePath = File::symbolizePath($templatePath);
$fileContents = File::get($templatePath);
@ -250,9 +223,6 @@ class MigrationModel extends BaseModel
$this->scriptFileName = $scriptFileName;
}
/**
* makeScriptFileNameUnique
*/
public function makeScriptFileNameUnique()
{
$updatesPath = $this->getPluginUpdatesPath();
@ -267,9 +237,6 @@ class MigrationModel extends BaseModel
return $this->scriptFileName = $fileName;
}
/**
* deleteModel
*/
public function deleteModel()
{
if ($this->isApplied()) {
@ -280,9 +247,6 @@ class MigrationModel extends BaseModel
$this->removeScriptFile();
}
/**
* isApplied
*/
public function isApplied()
{
if ($this->isNewModel()) {
@ -295,9 +259,6 @@ class MigrationModel extends BaseModel
return !array_key_exists($this->originalVersion, $unappliedVersions);
}
/**
* apply
*/
public function apply()
{
if ($this->isApplied()) {
@ -308,9 +269,6 @@ class MigrationModel extends BaseModel
$versionManager->updatePlugin($this->pluginCodeObj->toCode(), $this->version);
}
/**
* rollback
*/
public function rollback()
{
if (!$this->isApplied()) {
@ -321,9 +279,6 @@ class MigrationModel extends BaseModel
$versionManager->removePlugin($this->pluginCodeObj->toCode(), $this->version);
}
/**
* assignFileName
*/
protected function assignFileName()
{
$code = trim($this->code);
@ -377,9 +332,6 @@ class MigrationModel extends BaseModel
}
}
/**
* saveScriptFile
*/
protected function saveScriptFile()
{
$originalFileContents = $this->getOriginalFileContents();
@ -404,9 +356,6 @@ class MigrationModel extends BaseModel
return $originalFileContents;
}
/**
* getOriginalFileContents
*/
protected function getOriginalFileContents()
{
if (!strlen($this->originalScriptFileName)) {
@ -419,9 +368,6 @@ class MigrationModel extends BaseModel
}
}
/**
* loadScriptFile
*/
protected function loadScriptFile()
{
$scriptFilePath = $this->getPluginUpdatesPath($this->scriptFileName.'.php');
@ -433,9 +379,6 @@ class MigrationModel extends BaseModel
return File::get($scriptFilePath);
}
/**
* removeScriptFile
*/
protected function removeScriptFile()
{
$scriptFilePath = $this->getPluginUpdatesPath($this->scriptFileName.'.php');
@ -444,9 +387,6 @@ class MigrationModel extends BaseModel
@unlink($scriptFilePath);
}
/**
* rollbackScriptFile
*/
protected function rollbackScriptFile($fileContents)
{
$scriptFilePath = $this->getPluginUpdatesPath($this->originalScriptFileName.'.php');
@ -459,9 +399,6 @@ class MigrationModel extends BaseModel
}
}
/**
* rollbackSaving
*/
protected function rollbackSaving($originalVersionData, $originalScriptFileContents)
{
if ($originalVersionData) {
@ -476,9 +413,6 @@ class MigrationModel extends BaseModel
}
}
/**
* insertOrUpdateVersion
*/
protected function insertOrUpdateVersion()
{
$versionFilePath = $this->getPluginUpdatesPath('version.yaml');
@ -507,9 +441,6 @@ class MigrationModel extends BaseModel
}
}
// Add "v" to the version information
$versionInformation = $this->normalizeVersions((array) $versionInformation);
$yamlData = Yaml::render($versionInformation);
if (!File::put($versionFilePath, $yamlData)) {
@ -521,9 +452,6 @@ class MigrationModel extends BaseModel
return $originalFileContents;
}
/**
* deleteVersion
*/
protected function deleteVersion()
{
$versionInformation = $this->getPluginVersionInformation();
@ -545,18 +473,12 @@ class MigrationModel extends BaseModel
@File::chmod($versionFilePath);
}
/**
* rollbackVersionFile
*/
protected function rollbackVersionFile($fileData)
{
$versionFilePath = $this->getPluginUpdatesPath('version.yaml');
File::put($versionFilePath, $fileData);
}
/**
* getPluginUpdatesPath
*/
protected function getPluginUpdatesPath($fileName = null)
{
$pluginCodeObj = $this->getPluginCodeObj();
@ -571,9 +493,6 @@ class MigrationModel extends BaseModel
return $filePath;
}
/**
* getPluginVersionInformation
*/
protected function getPluginVersionInformation()
{
$versionObj = new PluginVersion;
@ -598,17 +517,4 @@ class MigrationModel extends BaseModel
return $value;
}
/**
* normalizeVersions checks some versions start with v and others not
*/
protected function normalizeVersions(array $versions): array
{
$result = [];
foreach ($versions as $key => $value) {
$version = rtrim(ltrim((string) $key, 'v'), '.');
$result['v'.$version] = $value;
}
return $result;
}
}

View File

@ -1,44 +1,27 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use ValidationException;
use SystemException;
use ValidationException;
/**
* ModelFormModel represents and manages model forms.
* Represents and manages model forms.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class ModelFormModel extends ModelYamlModel
{
/**
* @var array controls
*/
public $controls;
/**
* @var array originals (attributes)
*/
public $originals;
/**
* @var array fillable
*/
protected static $fillable = [
'fileName',
'controls'
];
/**
* @var array validationRules
*/
protected $validationRules = [
'fileName' => ['required', 'regex:/^[a-z0-9\.\-_]+$/i']
];
/**
* loadForm
*/
public function loadForm($path)
{
$this->fileName = $path;
@ -46,9 +29,6 @@ class ModelFormModel extends ModelYamlModel
return parent::load($this->getFilePath());
}
/**
* fill
*/
public function fill(array $attributes)
{
if (!is_array($attributes['controls'])) {
@ -62,9 +42,6 @@ class ModelFormModel extends ModelYamlModel
return parent::fill($attributes);
}
/**
* validateFileIsModelType
*/
public static function validateFileIsModelType($fileContentsArray)
{
$modelRootNodes = [
@ -82,9 +59,6 @@ class ModelFormModel extends ModelYamlModel
return false;
}
/**
* validate
*/
public function validate()
{
parent::validate();
@ -94,31 +68,26 @@ class ModelFormModel extends ModelYamlModel
}
}
/**
* initDefaults
*/
public function initDefaults()
{
$this->fileName = 'fields.yaml';
}
/**
* modelToYamlArray converts the model's data to an array before it's saved to a YAML file.
* Converts the model's data to an array before it's saved to a YAML file.
* @return array
*/
protected function modelToYamlArray()
{
return array_merge((array) $this->originals, $this->controls);
return $this->controls;
}
/**
* yamlArrayToModel loads the model's data from an array.
* Load the model's data from an array.
* @param array $array An array to load the model fields from.
*/
protected function yamlArrayToModel($array)
{
$this->originals = array_except($array, 'fields');
$this->controls = $array;
}
}

View File

@ -1,4 +1,4 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use ApplicationException;
use SystemException;
@ -6,36 +6,24 @@ use ValidationException;
use Lang;
/**
* ModelListModel represents and manages model lists.
* Represents and manages model lists.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class ModelListModel extends ModelYamlModel
{
/**
* @var array columns
*/
public $columns;
/**
* @var array fillable
*/
protected static $fillable = [
'fileName',
'columns'
];
/**
* @var array validationRules
*/
protected $validationRules = [
'fileName' => ['required', 'regex:/^[a-z0-9\.\-_]+$/i']
];
/**
* loadForm
*/
public function loadForm($path)
{
$this->fileName = $path;
@ -43,9 +31,6 @@ class ModelListModel extends ModelYamlModel
return parent::load($this->getFilePath());
}
/**
* fill
*/
public function fill(array $attributes)
{
if (!is_array($attributes['columns'])) {
@ -59,9 +44,6 @@ class ModelListModel extends ModelYamlModel
return parent::fill($attributes);
}
/**
* validateFileIsModelType
*/
public static function validateFileIsModelType($fileContentsArray)
{
$modelRootNodes = [
@ -77,32 +59,23 @@ class ModelListModel extends ModelYamlModel
return false;
}
/**
* validate
*/
public function validate()
{
parent::validate();
$this->validateDuplicateColumns();
$this->validateDupicateColumns();
if (!$this->columns) {
throw new ValidationException(['columns' => 'Please create at least one column.']);
}
}
/**
* initDefaults
*/
public function initDefaults()
{
$this->fileName = 'columns.yaml';
}
/**
* validateDuplicateColumns
*/
protected function validateDuplicateColumns()
protected function validateDupicateColumns()
{
foreach ($this->columns as $outerIndex => $outerColumn) {
foreach ($this->columns as $innerIndex => $innerColumn) {
@ -178,23 +151,18 @@ class ModelListModel extends ModelYamlModel
$this->columns = $columns;
}
/**
* preprocessColumnDataBeforeSave
*/
protected function preprocessColumnDataBeforeSave($column)
{
// Filter empty values, if not array
$column = array_filter($column, function ($value) {
return !is_array($value) && strlen($value) > 0;
});
// Cast booleans
$booleanFields = [
'searchable',
'invisible',
'sortable'
];
$column = array_filter($column, function ($value) {
return strlen($value) > 0;
});
foreach ($booleanFields as $booleanField) {
if (!array_key_exists($booleanField, $column)) {
continue;

View File

@ -1,22 +1,17 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use Str;
use Twig;
use Lang;
use File;
use Schema;
use Validator;
use RainLab\Builder\Classes\EnumDbType;
use RainLab\Builder\Classes\FilesystemGenerator;
use RainLab\Builder\Classes\MigrationColumnType;
use RainLab\Builder\Classes\ModelFileParser;
use RainLab\Builder\Classes\PluginCode;
use DirectoryIterator;
use ApplicationException;
use SystemException;
use Validator;
use Lang;
use File;
use Schema;
use Str;
use Db;
/**
* ModelModel manages plugin models.
* Manages plugin models.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
@ -25,78 +20,17 @@ class ModelModel extends BaseModel
{
const UNQUALIFIED_CLASS_NAME_PATTERN = '/^[A-Z]+[a-zA-Z0-9_]+$/';
/**
* @var string className
*/
public $className;
/**
* @var string baseClassName
*/
public $baseClassName = \Model::class;
/**
* @var string databaseTable
*/
public $databaseTable;
/**
* @var array traits
*/
public $traits = [
\October\Rain\Database\Traits\Validation:: class
];
/**
* @var array relationDefinitions (belongsTo, belongsToMany, etc.)
*/
public $relationDefinitions;
/**
* @var array validationDefinitions (rules, attributeNames, customMessages)
*/
public $validationDefinitions;
/**
* @var array multisiteDefinition (fields, sync)
*/
public $multisiteDefinition;
/**
* @var bool addSoftDeleting
*/
public $addTimestamps = false;
/**
* @var bool addSoftDeleting
*/
public $addSoftDeleting = false;
/**
* @var bool skipDbValidation
*/
public $skipDbValidation = false;
/**
* @var array injectedRawContents
*/
protected $injectedRawContents = [];
/**
* @var array fillable
*/
protected static $fillable = [
'className',
'baseClassName',
'databaseTable',
'relationDefinitions',
'addTimestamps',
'addSoftDeleting',
'addSoftDeleting'
];
/**
* @var array validationRules
*/
protected $validationRules = [
'className' => ['required', 'regex:' . self::UNQUALIFIED_CLASS_NAME_PATTERN, 'uniqModelName'],
'databaseTable' => ['required'],
@ -104,9 +38,6 @@ class ModelModel extends BaseModel
'addSoftDeleting' => ['deletedAtColumnMustExist']
];
/**
* listPluginModels
*/
public static function listPluginModels($pluginCodeObj)
{
$modelsDirectoryPath = $pluginCodeObj->toPluginDirectoryPath().'/models';
@ -150,50 +81,28 @@ class ModelModel extends BaseModel
return $result;
}
/**
* save
*/
public function save()
{
$this->validate();
$modelFilePath = $this->getFilePath();
$namespace = $this->getPluginCodeObj()->toPluginNamespace().'\\Models';
$templateFile = $this->baseClassName === \System\Models\SettingModel::class
? 'settingmodel.php.tpl'
: 'model.php.tpl';
$structure = [
$modelFilePath => $templateFile
$modelFilePath => 'model.php.tpl'
];
$variables = [
'namespace' => $namespace,
'classname' => $this->className,
'baseclass' => $this->baseClassName,
'baseclassname' => class_basename($this->baseClassName),
'table' => $this->databaseTable
];
$generator = new FilesystemGenerator('$', $structure, '$/rainlab/builder/models/modelmodel/templates');
$generator->setVariables($variables);
// Trait contents
if ($this->addSoftDeleting) {
$this->traits[] = \October\Rain\Database\Traits\SoftDelete::class;
}
usort($this->traits, function($a, $b) { return strlen($a) > strlen($b); });
$traitContents = [];
foreach ($this->traits as $trait) {
$traitContents[] = " use \\{$trait};";
}
$generator->setVariable('traitContents', implode(PHP_EOL, $traitContents));
// Dynamic contents
$dynamicContents = [];
$generator = new FilesystemGenerator('$', $structure, '$/rainlab/builder/classes/modelmodel/templates');
$generator->setVariables($variables);
if ($this->addSoftDeleting) {
$dynamicContents[] = $generator->getTemplateContents('soft-delete.php.tpl');
}
@ -202,75 +111,11 @@ class ModelModel extends BaseModel
$dynamicContents[] = $generator->getTemplateContents('no-timestamps.php.tpl');
}
$dynamicContents = array_merge($dynamicContents, (array) $this->injectedRawContents);
$generator->setVariable('dynamicContents', implode('', $dynamicContents));
// Validation contents
$validationDefinitions = (array) $this->validationDefinitions;
foreach ($validationDefinitions as $type => &$definitions) {
foreach ($definitions as $field => &$rule) {
// Cannot process anything other than string at this time
if (!is_string($rule)) {
unset($definitions[$field]);
}
}
}
$validationTemplate = File::get(__DIR__.'/modelmodel/templates/validation-definitions.php.tpl');
$validationContents = Twig::parse($validationTemplate, ['validation' => $validationDefinitions]);
$generator->setVariable('validationContents', $validationContents);
// Relation contents
$relationContents = [];
$relationTemplate = File::get(__DIR__.'/modelmodel/templates/relation-definitions.php.tpl');
foreach ((array) $this->relationDefinitions as $relationType => $definitions) {
if (!$definitions) {
continue;
}
$relationVars = [
'relationType' => $relationType,
'relations' => [],
];
foreach ($definitions as $relationName => $definition) {
$definition = (array) $definition;
$modelClass = array_shift($definition);
$props = $definition;
foreach ($props as &$prop) {
$prop = var_export($prop, true);
}
$relationVars['relations'][$relationName] = [
'class' => $modelClass,
'props' => $props
];
}
$relationContents[] = Twig::parse($relationTemplate, $relationVars);
}
$generator->setVariable('relationContents', implode(PHP_EOL, $relationContents));
// Multisite contents
$multisiteTemplate = File::get(__DIR__.'/modelmodel/templates/multisite-definitions.php.tpl');
$multisiteContents = Twig::parse($multisiteTemplate, ['multisite' => $this->multisiteDefinition]);
$generator->setVariable('multisiteContents', $multisiteContents);
$generator->generate();
}
/**
* validate
*/
public function validate()
{
$path = File::symbolizePath('$/'.$this->getFilePath());
@ -302,27 +147,9 @@ class ModelModel extends BaseModel
return $this->validateColumnsExist($value, $columns, ['deleted_at']);
});
if ($this->skipDbValidation) {
unset(
$this->validationRules['addTimestamps'],
$this->validationRules['addSoftDeleting']
);
}
parent::validate();
}
/**
* addRawContentToModel
*/
public function addRawContentToModel($content)
{
$this->injectedRawContents[] = $content;
}
/**
* getDatabaseTableOptions
*/
public function getDatabaseTableOptions()
{
$pluginCode = $this->getPluginCodeObj()->toCode();
@ -331,9 +158,6 @@ class ModelModel extends BaseModel
return array_combine($tables, $tables);
}
/**
* getTableNameFromModelClass
*/
private static function getTableNameFromModelClass($pluginCodeObj, $modelClassName)
{
if (!self::validateModelClassName($modelClassName)) {
@ -359,9 +183,6 @@ class ModelModel extends BaseModel
return $modelInfo['table'];
}
/**
* getModelFields
*/
public static function getModelFields($pluginCodeObj, $modelClassName)
{
$tableName = self::getTableNameFromModelClass($pluginCodeObj, $modelClassName);
@ -372,9 +193,6 @@ class ModelModel extends BaseModel
return Schema::getColumnListing($tableName);
}
/**
* getModelColumnsAndTypes
*/
public static function getModelColumnsAndTypes($pluginCodeObj, $modelClassName)
{
$tableName = self::getTableNameFromModelClass($pluginCodeObj, $modelClassName);
@ -407,9 +225,6 @@ class ModelModel extends BaseModel
return $result;
}
/**
* getPluginRegistryData
*/
public static function getPluginRegistryData($pluginCode, $subtype)
{
$pluginCodeObj = new PluginCode($pluginCode);
@ -425,9 +240,6 @@ class ModelModel extends BaseModel
return $result;
}
/**
* getPluginRegistryDataColumns
*/
public static function getPluginRegistryDataColumns($pluginCode, $modelClassName)
{
$classParts = explode('\\', $modelClassName);
@ -452,33 +264,16 @@ class ModelModel extends BaseModel
return $result;
}
/**
* validateModelClassName
*/
public static function validateModelClassName($modelClassName)
{
return class_exists($modelClassName) || !!preg_match(self::UNQUALIFIED_CLASS_NAME_PATTERN, $modelClassName);
}
/**
* getModelFilePath
*/
public function getModelFilePath()
{
return File::symbolizePath($this->getPluginCodeObj()->toPluginDirectoryPath().'/models/'.$this->className.'.php');
}
/**
* getFilePath
*/
protected function getFilePath()
{
return $this->getPluginCodeObj()->toFilesystemPath().'/models/'.$this->className.'.php';
}
/**
* validateColumnsExist
*/
protected function validateColumnsExist($value, $columns, $columnsToCheck)
{
if (!strlen(trim($this->databaseTable))) {

View File

@ -1,7 +1,5 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Models\ModelModel;
use RainLab\Builder\Classes\PluginCode;
use DirectoryIterator;
use SystemException;
use Exception;
@ -10,26 +8,17 @@ use File;
use Yaml;
/**
* ModelYamlModel is a base class for models belonging to database models (forms, lists, etc.).
* A base class for models belonging to databse models (forms, lists, etc.).
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
abstract class ModelYamlModel extends YamlModel
{
/**
* @var string fileName
*/
public $fileName;
/**
* @var string modelClassName
*/
protected $modelClassName;
/**
* fill
*/
public function fill(array $attributes)
{
parent::fill($attributes);
@ -39,9 +28,6 @@ abstract class ModelYamlModel extends YamlModel
}
}
/**
* setModelClassName
*/
public function setModelClassName($className)
{
if (!preg_match('/^[a-zA-Z]+[0-9a-z\_]*$/', $className)) {
@ -51,9 +37,6 @@ abstract class ModelYamlModel extends YamlModel
$this->modelClassName = $className;
}
/**
* validate
*/
public function validate()
{
$this->validationMessages = [
@ -65,7 +48,7 @@ abstract class ModelYamlModel extends YamlModel
}
/**
* getDisplayName returns a string suitable for displaying in the Builder UI tabs.
* Returns a string suitable for displaying in the Builder UI tabs.
*/
public function getDisplayName($nameFallback)
{
@ -82,9 +65,6 @@ abstract class ModelYamlModel extends YamlModel
return $this->getModelClassName().'/'.$fileName;
}
/**
* listModelFiles
*/
public static function listModelFiles($pluginCodeObj, $modelClassName)
{
if (!self::validateModelClassName($modelClassName)) {
@ -126,9 +106,6 @@ abstract class ModelYamlModel extends YamlModel
return $result;
}
/**
* getPluginRegistryData
*/
public static function getPluginRegistryData($pluginCode, $modelClassName)
{
$pluginCodeObj = new PluginCode($pluginCode);
@ -157,9 +134,6 @@ abstract class ModelYamlModel extends YamlModel
return $result;
}
/**
* getPluginRegistryDataAllRecords
*/
public static function getPluginRegistryDataAllRecords($pluginCode)
{
$pluginCodeObj = new PluginCode($pluginCode);
@ -182,25 +156,16 @@ abstract class ModelYamlModel extends YamlModel
return $result;
}
/**
* validateFileIsModelType
*/
public static function validateFileIsModelType($fileContentsArray)
{
return false;
}
/**
* validateModelClassName
*/
protected static function validateModelClassName($modelClassName)
{
return preg_match('/^[A-Z]+[a-zA-Z0-9_]+$/i', $modelClassName);
}
/**
* getModelClassName
*/
protected function getModelClassName()
{
if ($this->modelClassName === null) {
@ -210,18 +175,9 @@ abstract class ModelYamlModel extends YamlModel
return $this->modelClassName;
}
/**
* getYamlFilePath
*/
public function getYamlFilePath()
{
$fileName = $this->addExtension(trim($this->fileName));
return File::symbolizePath($this->getPluginCodeObj()->toPluginDirectoryPath().'/models/'.strtolower($this->getModelClassName()).'/'.$fileName);
}
/**
* getFilePath returns a file path to save the model to.
* Returns a file path to save the model to.
* @return string Returns a path.
*/
protected function getFilePath()
@ -236,9 +192,6 @@ abstract class ModelYamlModel extends YamlModel
return $this->getPluginCodeObj()->toPluginDirectoryPath().'/models/'.strtolower($this->getModelClassName()).'/'.$fileName;
}
/**
* addExtension
*/
protected function addExtension($fileName)
{
if (substr($fileName, -5) !== '.yaml') {

View File

@ -1,4 +1,4 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use ApplicationException;
use SystemException;
@ -13,24 +13,12 @@ use Lang;
*/
class PermissionsModel extends PluginYamlModel
{
/**
* @var array permissions
*/
public $permissions = [];
/**
* @var string yamlSection
*/
protected $yamlSection = 'permissions';
/**
* @var object pluginCodeObj
*/
protected $pluginCodeObj;
/**
* @var array fillable
*/
protected static $fillable = [
'permissions'
];
@ -49,7 +37,7 @@ class PermissionsModel extends PluginYamlModel
}
/**
* modelToYamlArray converts the model's data to an array before it's saved to a YAML file.
* Converts the model's data to an array before it's saved to a YAML file.
* @return array
*/
protected function modelToYamlArray()
@ -80,20 +68,14 @@ class PermissionsModel extends PluginYamlModel
return $filePermissions;
}
/**
* validate
*/
public function validate()
{
parent::validate();
$this->validateDuplicatePermissions();
$this->validateDupicatePermissions();
$this->validateRequiredProperties();
}
/**
* getPluginRegistryData
*/
public static function getPluginRegistryData($pluginCode)
{
$model = new PermissionsModel();
@ -114,10 +96,7 @@ class PermissionsModel extends PluginYamlModel
return $result;
}
/**
* validateDuplicatePermissions
*/
protected function validateDuplicatePermissions()
protected function validateDupicatePermissions()
{
foreach ($this->permissions as $outerIndex => $outerPermission) {
if (!isset($outerPermission['permission'])) {
@ -144,9 +123,6 @@ class PermissionsModel extends PluginYamlModel
}
}
/**
* validateRequiredProperties
*/
protected function validateRequiredProperties()
{
foreach ($this->permissions as $permission) {
@ -180,9 +156,6 @@ class PermissionsModel extends PluginYamlModel
}
}
/**
* trimPermissionProperties
*/
protected function trimPermissionProperties($permission)
{
array_walk($permission, function ($value, $key) {
@ -192,16 +165,13 @@ class PermissionsModel extends PluginYamlModel
return $permission;
}
/**
* isEmptyRow
*/
protected function isEmptyRow($permission)
{
return !isset($permission['tab']) || !isset($permission['permission']) || !isset($permission['label']);
}
/**
* yamlArrayToModel loads the model's data from an array.
* Load the model's data from an array.
* @param array $array An array to load the model fields from.
*/
protected function yamlArrayToModel($array)
@ -220,7 +190,7 @@ class PermissionsModel extends PluginYamlModel
}
/**
* getFilePath returns a file path to save the model to.
* Returns a file path to save the model to.
* @return string Returns a path.
*/
protected function getFilePath()

View File

@ -3,26 +3,17 @@
use SystemException;
/**
* PhpSourceStream represents a PHP source code token stream.
* Represents a PHP source code token stream.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class PhpSourceStream
{
/**
* @var array tokens
*/
protected $tokens;
/**
* @var int head
*/
protected $head = 0;
/**
* @var array headBookmarks
*/
protected $headBookmarks = [];
public function __construct($fileContents)
@ -166,14 +157,10 @@ class PhpSourceStream
* The termination tokens could be specified as array.
* @return string|null Returns the tokens text or null
*/
public function getNextExpectedTerminated($expectedCodesOrValues, $terminationToken, $skipToken = [])
public function getNextExpectedTerminated($expectedCodesOrValues, $terminationToken)
{
$buffer = null;
if (!is_array($skipToken)) {
$skipToken = [$skipToken];
}
if (!is_array($terminationToken)) {
$terminationToken = [$terminationToken];
}
@ -187,12 +174,12 @@ class PhpSourceStream
continue;
}
if (in_array($code, $terminationToken) || in_array($text, $terminationToken)) {
if (in_array($code, $terminationToken)) {
return $buffer;
}
if (in_array($code, $skipToken) || in_array($text, $skipToken)) {
continue;
if (in_array($text, $terminationToken)) {
return $buffer;
}
// The token should be either expected or termination.
@ -223,10 +210,10 @@ class PhpSourceStream
return $this->setHead($this->getHead()-1);
}
/**
* getTextToSemicolon returns the stream text from the head position to the next
* semicolon and updates the head. If the method succeeds, the head is positioned
* on the semicolon.
* Returns the stream text from the head position to the next semicolon and updates the head.
* If the method succeeds, the head is positioned on the semicolon.
*/
public function getTextToSemicolon()
{
@ -244,16 +231,13 @@ class PhpSourceStream
return null;
}
/**
* unquotePhpString
*/
public function unquotePhpString($string, $default = false)
public function unquotePhpString($string)
{
if ((substr($string, 0, 1) === '\'' && substr($string, -1) === '\'') ||
(substr($string, 0, 1) === '"' && substr($string, -1) === '"')) {
return substr($string, 1, -1);
}
return $default;
return false;
}
}

View File

@ -1,8 +1,5 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Classes\FilesystemGenerator;
use RainLab\Builder\Classes\IconList;
use RainLab\Builder\Classes\PluginCode;
use RainLab\Builder\Models\Settings as PluginSettings;
use System\Classes\UpdateManager;
use System\Classes\PluginManager;
@ -196,7 +193,7 @@ class PluginBaseModel extends PluginYamlModel
'pluginDescriptionSanitized' => $this->sanitizePHPString($this->localizedDescription),
];
$generator = new FilesystemGenerator('$', $structure, '$/rainlab/builder/models/pluginbasemodel/templates');
$generator = new FilesystemGenerator('$', $structure, '$/rainlab/builder/classes/pluginbasemodel/templates');
$generator->setVariables($variables);
$generator->generate();
}

View File

@ -4,26 +4,17 @@ use Db;
use ApplicationException;
/**
* PluginCode represents a plugin code and provides basic code operations.
* Represents a plugin code and provides basic code operations.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class PluginCode
{
/**
* @var string authorCode
*/
protected $authorCode;
private $authorCode;
/**
* @var string pluginCode
*/
protected $pluginCode;
private $pluginCode;
/**
* __construct
*/
public function __construct($pluginCodeStr)
{
$codeParts = explode('.', $pluginCodeStr);
@ -41,9 +32,6 @@ class PluginCode
$this->pluginCode = trim($pluginCode);
}
/**
* createFromNamespace
*/
public static function createFromNamespace($namespace)
{
$namespaceParts = explode('\\', $namespace);
@ -57,65 +45,41 @@ class PluginCode
return new self($authorCode.'.'.$pluginCode);
}
/**
* toPluginNamespace
*/
public function toPluginNamespace()
{
return $this->authorCode.'\\'.$this->pluginCode;
}
/**
* toUrl
*/
public function toUrl()
{
return strtolower($this->authorCode).'/'.strtolower($this->pluginCode);
}
/**
* toUpdatesNamespace
*/
public function toUpdatesNamespace()
{
return $this->toPluginNamespace().'\\Updates';
}
/**
* toFilesystemPath
*/
public function toFilesystemPath()
{
return strtolower($this->authorCode.'/'.$this->pluginCode);
}
/**
* toCode
*/
public function toCode()
{
return $this->authorCode.'.'.$this->pluginCode;
}
/**
* toPluginFilePath
*/
public function toPluginFilePath()
{
return '$/'.$this->toFilesystemPath().'/plugin.yaml';
}
/**
* toPluginInformationFilePath
*/
public function toPluginInformationFilePath()
{
return '$/'.$this->toFilesystemPath().'/Plugin.php';
}
/**
* toPluginDirectoryPath
*/
public function toPluginDirectoryPath()
{
return '$/'.$this->toFilesystemPath();
@ -135,34 +99,17 @@ class PluginCode
return $builderPrefix;
}
/**
* toPermissionPrefix
*/
public function toPermissionPrefix()
{
return strtolower($this->authorCode.'.'.$this->pluginCode);
}
/**
* getAuthorCode
*/
public function getAuthorCode()
{
return $this->authorCode;
}
/**
* getPluginCode
*/
public function getPluginCode()
{
return $this->pluginCode;
}
/**
* validateCodeWord
*/
protected function validateCodeWord($str)
private function validateCodeWord($str)
{
$str = trim($str);
return strlen($str) && preg_match('/^[a-z]+[a-z0-9]+$/i', $str);

View File

@ -4,7 +4,7 @@ use System\Classes\PluginBase;
use System\Classes\PluginManager;
/**
* PluginVector holds a plugin code object and a plugin information class instance.
* Holds a plugin code object and a plugin information class instancd.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
@ -21,18 +21,12 @@ class PluginVector
*/
public $pluginCodeObj;
/**
* __construct
*/
public function __construct(PluginBase $plugin, PluginCode $pluginCodeObj)
{
$this->plugin = $plugin;
$this->pluginCodeObj = $pluginCodeObj;
}
/**
* createFromPluginCode
*/
public static function createFromPluginCode($pluginCode)
{
$pluginCodeObj = new PluginCode($pluginCode);
@ -48,9 +42,6 @@ class PluginVector
return null;
}
/**
* getPluginName
*/
public function getPluginName()
{
if (!$this->plugin) {

View File

@ -1,6 +1,5 @@
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Models\BaseModel;
use SystemException;
use File;
use Yaml;

View File

@ -1,26 +1,19 @@
<?php namespace RainLab\Builder\Models;
<?php namespace RainLab\Builder\Classes;
use RainLab\Builder\Classes\PluginCode;
use ApplicationException;
use Lang;
use File;
/**
* PluginYamlModel is a base class for models that keep data in the plugin.yaml file.
* A base class for models that keep data in the plugin.yaml file.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
abstract class PluginYamlModel extends YamlModel
{
/**
* @var string pluginName
*/
protected $pluginName;
/**
* loadPlugin
*/
public function loadPlugin($pluginCode)
{
$pluginCodeObj = new PluginCode($pluginCode);
@ -39,17 +32,11 @@ abstract class PluginYamlModel extends YamlModel
return $result;
}
/**
* getPluginName
*/
public function getPluginName()
{
return Lang::get($this->pluginName);
}
/**
* loadCommonProperties
*/
protected function loadCommonProperties()
{
if (!array_key_exists('plugin', $this->originalFileData)) {
@ -63,16 +50,10 @@ abstract class PluginYamlModel extends YamlModel
}
}
/**
* initPropertiesFromPluginCodeObject
*/
protected function initPropertiesFromPluginCodeObject($pluginCodeObj)
{
}
/**
* pluginSettingsFileExists
*/
protected static function pluginSettingsFileExists($pluginCodeObj)
{
$filePath = File::symbolizePath($pluginCodeObj->toPluginFilePath());

View File

@ -3,21 +3,15 @@
use Lang;
/**
* StandardBehaviorsRegistry is a utility class for registering standard controller behaviors.
* Utility class for registering standard controller behaviors.
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class StandardBehaviorsRegistry
{
/**
* @var object behaviorLibrary
*/
protected $behaviorLibrary;
/**
* __construct
*/
public function __construct($behaviorLibrary)
{
$this->behaviorLibrary = $behaviorLibrary;
@ -25,19 +19,13 @@ class StandardBehaviorsRegistry
$this->registerBehaviors();
}
/**
* registerBehaviors
*/
protected function registerBehaviors()
{
$this->registerFormBehavior();
$this->registerListBehavior();
$this->registerImportExportBehavior();
$this->registerFormBehavior();
$this->registerReorderBehavior();
}
/**
* registerFormBehavior
*/
protected function registerFormBehavior()
{
$properties = [
@ -179,13 +167,13 @@ class StandardBehaviorsRegistry
];
$templates = [
'$/rainlab/builder/classes/standardbehaviorsregistry/formcontroller/templates/create.php.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/formcontroller/templates/update.php.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/formcontroller/templates/preview.php.tpl'
'$/rainlab/builder/classes/standardbehaviorsregistry/formcontroller/templates/create.htm.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/formcontroller/templates/update.htm.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/formcontroller/templates/preview.htm.tpl'
];
$this->behaviorLibrary->registerBehavior(
\Backend\Behaviors\FormController::class,
'Backend\Behaviors\FormController',
'rainlab.builder::lang.controller.behavior_form_controller',
'rainlab.builder::lang.controller.behavior_form_controller_description',
$properties,
@ -196,9 +184,6 @@ class StandardBehaviorsRegistry
);
}
/**
* registerListBehavior
*/
protected function registerListBehavior()
{
$properties = [
@ -272,63 +257,13 @@ class StandardBehaviorsRegistry
'type' => 'checkbox',
'ignoreIfEmpty' => true,
],
'structure' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_structure'),
'ignoreIfEmpty' => true,
'type' => 'object',
'ignoreIfPropertyEmpty' => 'maxDepth',
'properties' => [
[
'property' => 'maxDepth',
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_max_depth'),
'type' => 'string',
'ignoreIfEmpty' => true,
'validation' => [
'regex' => [
'pattern' => '^[0-9]+$',
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_max_depth_regex')
]
],
],
[
'property' => 'showTree',
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_show_tree'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_show_tree_description'),
'type' => 'checkbox',
'default' => true,
'ignoreIfDefault' => true,
],
[
'property' => 'treeExpanded',
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_tree_expanded'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_tree_expanded_description'),
'type' => 'checkbox',
'default' => true,
'ignoreIfDefault' => true,
],
[
'property' => 'showReorder',
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_show_reorder'),
'type' => 'checkbox',
'default' => true,
'ignoreIfDefault' => true,
],
[
'property' => 'showSorting',
'showSorting' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_show_sorting'),
'type' => 'checkbox',
'ignoreIfEmpty' => false,
'default' => true,
'ignoreIfDefault' => true,
],
[
'property' => 'dragRow',
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_drag_row'),
'type' => 'checkbox',
'default' => true,
'ignoreIfDefault' => true,
],
],
],
'defaultSort' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_default_sort'),
'ignoreIfEmpty' => true,
@ -386,6 +321,18 @@ class StandardBehaviorsRegistry
'ignoreIfEmpty' => true,
'type' => 'string'
],
'showTree' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_show_tree'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_show_tree_description'),
'type' => 'checkbox',
'ignoreIfEmpty' => true
],
'treeExpanded' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_tree_expanded'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_tree_expanded_description'),
'type' => 'checkbox',
'ignoreIfEmpty' => true
],
'filter' => [
'type' => 'string', // Should be configurable in place later
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_filter'),
@ -394,12 +341,12 @@ class StandardBehaviorsRegistry
];
$templates = [
'$/rainlab/builder/classes/standardbehaviorsregistry/listcontroller/templates/index.php.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/listcontroller/templates/_list_toolbar.php.tpl'
'$/rainlab/builder/classes/standardbehaviorsregistry/listcontroller/templates/index.htm.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/listcontroller/templates/_list_toolbar.htm.tpl'
];
$this->behaviorLibrary->registerBehavior(
\Backend\Behaviors\ListController::class,
'Backend\Behaviors\ListController',
'rainlab.builder::lang.controller.behavior_list_controller',
'rainlab.builder::lang.controller.behavior_list_controller_description',
$properties,
@ -410,119 +357,72 @@ class StandardBehaviorsRegistry
);
}
/**
* registerImportExportBehavior
*/
protected function registerImportExportBehavior()
protected function registerReorderBehavior()
{
$properties = [
'import.title' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_title'),
'title' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_title'),
'type' => 'builderLocalization',
'validation' => [
'required' => [
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_title_required')
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_title_required')
]
],
'group' => Lang::get('rainlab.builder::lang.controller.property_group_import'),
],
'import.modelClass' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_model_class'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_model_class_description'),
'placeholder' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_model_class_placeholder'),
'modelClass' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_model_class'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_model_class_description'),
'placeholder' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_model_placeholder'),
'type' => 'dropdown',
'fillFrom' => 'model-classes',
'validation' => [
'required' => [
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_model_class_required')
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_model_class_required')
]
],
'group' => Lang::get('rainlab.builder::lang.controller.property_group_import'),
],
'import.list' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_file'),
'placeholder' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_placeholder'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_file_description'),
'nameFrom' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_name_from'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_name_from_description'),
'type' => 'autocomplete',
'fillFrom' => 'model-lists',
'subtypeFrom' => 'import.modelClass',
'depends' => ['import.modelClass'],
'fillFrom' => 'model-columns',
'subtypeFrom' => 'modelClass',
'depends' => ['modelClass'],
'validation' => [
'required' => [
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_file_required')
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_name_from_required')
]
],
'group' => Lang::get('rainlab.builder::lang.controller.property_group_import'),
],
'import.redirect' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_redirect'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_redirect_description'),
'type' => 'autocomplete',
'fillFrom' => 'controller-urls',
'toolbar' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_toolbar'),
'type' => 'object',
'ignoreIfEmpty' => true,
'group' => Lang::get('rainlab.builder::lang.controller.property_group_import'),
],
'export.title' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_export_title'),
'type' => 'builderLocalization',
'validation' => [
'required' => [
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_title_required')
]
],
'group' => Lang::get('rainlab.builder::lang.controller.property_group_export'),
],
'export.modelClass' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_export_model_class'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_export_model_class_description'),
'placeholder' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_model_class_placeholder'),
'type' => 'dropdown',
'fillFrom' => 'model-classes',
'validation' => [
'required' => [
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_model_class_required')
]
],
'group' => Lang::get('rainlab.builder::lang.controller.property_group_export'),
],
'export.list' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_file'),
'placeholder' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_placeholder'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_file_description'),
'type' => 'autocomplete',
'fillFrom' => 'model-lists',
'subtypeFrom' => 'export.modelClass',
'depends' => ['export.modelClass'],
'validation' => [
'required' => [
'message' => Lang::get('rainlab.builder::lang.controller.property_behavior_list_file_required')
]
],
'group' => Lang::get('rainlab.builder::lang.controller.property_group_export'),
],
'export.redirect' => [
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_redirect'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_import_redirect_description'),
'type' => 'autocomplete',
'fillFrom' => 'controller-urls',
'properties' => [
[
'property' => 'buttons',
'type' => 'string',
'ignoreIfEmpty' => true,
'group' => Lang::get('rainlab.builder::lang.controller.property_group_export'),
'title' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_toolbar_buttons'),
'description' => Lang::get('rainlab.builder::lang.controller.property_behavior_reorder_toolbar_buttons_description'),
]
]
],
];
$templates = [
'$/rainlab/builder/classes/standardbehaviorsregistry/importexportcontroller/templates/import.php.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/importexportcontroller/templates/export.php.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/reordercontroller/templates/reorder.htm.tpl',
'$/rainlab/builder/classes/standardbehaviorsregistry/reordercontroller/templates/_reorder_toolbar.htm.tpl'
];
$this->behaviorLibrary->registerBehavior(
\Backend\Behaviors\ImportExportController::class,
'rainlab.builder::lang.controller.behavior_import_export_controller',
'rainlab.builder::lang.controller.behavior_import_export_controller_description',
'Backend\Behaviors\ReorderController',
'rainlab.builder::lang.controller.behavior_reorder_controller',
'rainlab.builder::lang.controller.behavior_reorder_controller_description',
$properties,
'importExportConfig',
'reorderConfig',
null,
'config_import_export.yaml',
'config_reorder.yaml',
$templates
);
}

View File

@ -1,204 +0,0 @@
<?php namespace RainLab\Builder\Classes;
/**
* StandardBlueprintsRegistry
*
* @package rainlab\builder
* @author Alexey Bobkov, Samuel Georges
*/
class StandardBlueprintsRegistry
{
/**
* @var TailorBlueprintLibrary blueprintLibrary
*/
protected $blueprintLibrary;
/**
* __construct
*/
public function __construct($blueprintLibrary)
{
$this->blueprintLibrary = $blueprintLibrary;
$this->registerBlueprints();
}
/**
* registerBlueprints
*/
protected function registerBlueprints()
{
$this->registerEntryBlueprint();
$this->registerGlobalBlueprint();
}
/**
* registerEntryBlueprint
*/
protected function registerEntryBlueprint()
{
$properties = [
'name' => [
'title' => "Name",
'description' => "The name to use for this blueprint in the user interface",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A name is required"
]
],
],
'controllerClass' => [
'title' => "Controller Class",
'description' => "Controller name defines the class name and URL of the controller's back-end pages. Standard PHP variable naming conventions apply. The first symbol should be a capital Latin letter. Examples: Categories, Posts, Products.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A controller name is required"
]
],
],
'modelClass' => [
'title' => "Model Class",
'description' => "Model name defines the class name of the model. Standard PHP variable naming conventions apply. The first symbol should be a capital Latin letter. Examples: Category, Post, Product.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A model name is required"
]
],
],
'tableName' => [
'title' => "Table Name",
'description' => "Table name defines the table name in the database.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A table name is required"
]
],
],
'permissionCode' => [
'title' => "Permission Code",
'description' => "Permission code used to manage this item.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A permission code is required"
]
],
],
'menuCode' => [
'title' => "Menu Code",
'description' => "Menu code used to include navigation for this item.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A menu code is required"
]
],
]
];
$this->blueprintLibrary->registerBlueprint(
\Tailor\Classes\Blueprint\EntryBlueprint::class,
'Entry Blueprint',
'The standard content structure that supports drafts.',
$properties,
);
$this->blueprintLibrary->registerBlueprint(
\Tailor\Classes\Blueprint\StreamBlueprint::class,
'Stream Blueprint',
'A stream of time stamped entries.',
$properties,
);
$this->blueprintLibrary->registerBlueprint(
\Tailor\Classes\Blueprint\SingleBlueprint::class,
'Single Blueprint',
'A single entry with dedicated fields.',
$properties,
);
$this->blueprintLibrary->registerBlueprint(
\Tailor\Classes\Blueprint\StructureBlueprint::class,
'Structure Blueprint',
'A defined structure of entries.',
$properties,
);
}
protected function registerGlobalBlueprint()
{
$properties = [
'name' => [
'title' => "Name",
'description' => "The name to use for this blueprint in the user interface",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A name is required"
]
],
],
'controllerClass' => [
'title' => "Controller Class",
'description' => "Controller name defines the class name and URL of the controller's back-end pages. Standard PHP variable naming conventions apply. The first symbol should be a capital Latin letter. Examples: Categories, Posts, Products.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A controller name is required"
]
],
],
'modelClass' => [
'title' => "Model Class",
'description' => "Model name defines the class name of the model. Standard PHP variable naming conventions apply. The first symbol should be a capital Latin letter. Examples: Category, Post, Product.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A model name is required"
]
],
],
'tableName' => [
'title' => "Table Name",
'description' => "Table name defines the table name in the database.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A table name is required"
]
],
],
'permissionCode' => [
'title' => "Permission Code",
'description' => "Permission code used to manage this item.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A permission code is required"
]
],
],
'menuCode' => [
'title' => "Menu Code",
'description' => "Menu code used to include navigation for this item.",
'type' => 'string',
'validation' => [
'required' => [
'message' => "A menu code is required"
]
],
]
];
$this->blueprintLibrary->registerBlueprint(
\Tailor\Classes\Blueprint\GlobalBlueprint::class,
'Global Blueprint',
'A single record in the database and is often used for settings and configuration.',
$properties,
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,6 @@ use File;
use Doctrine\DBAL\Schema\Comparator;
use Doctrine\DBAL\Schema\TableDiff;
use October\Rain\Parse\Bracket as TextParser;
use RainLab\Builder\Models\BaseModel;
/**
* Generates migration code for creating, updates and deleting tables.
@ -20,15 +19,9 @@ class TableMigrationCodeGenerator extends BaseModel
const COLUMN_MODE_CHANGE = 'change';
const COLUMN_MODE_REVERT = 'revert';
/**
* @var string indent characters
*/
protected $indent = ' ';
/**
* @var string eol character
*/
protected $eol = "\n";
protected $eol = PHP_EOL;
/**
* Generates code for creating or updating a database table.
@ -91,7 +84,7 @@ class TableMigrationCodeGenerator extends BaseModel
*/
public function wrapMigrationCode($scriptFilename, $code, $pluginCodeObj)
{
$templatePath = '$/rainlab/builder/models/databasetablemodel/templates/full-migration-code.php.tpl';
$templatePath = '$/rainlab/builder/classes/databasetablemodel/templates/full-migration-code.php.tpl';
$templatePath = File::symbolizePath($templatePath);
$fileContents = File::get($templatePath);
@ -132,7 +125,7 @@ class TableMigrationCodeGenerator extends BaseModel
protected function generateMigrationCode($upCode, $downCode)
{
$templatePath = '$/rainlab/builder/models/databasetablemodel/templates/migration-code.php.tpl';
$templatePath = '$/rainlab/builder/classes/databasetablemodel/templates/migration-code.php.tpl';
$templatePath = File::symbolizePath($templatePath);
$fileContents = File::get($templatePath);
@ -328,6 +321,11 @@ class TableMigrationCodeGenerator extends BaseModel
$tableFunction = $isNewTable ? 'create' : 'table';
$result = sprintf('\tSchema::%s(\'%s\', function($table)', $tableFunction, $tableName).$this->eol;
$result .= '\t{'.$this->eol;
if ($isNewTable) {
$result .= '\t\t$table->engine = \'InnoDB\';'.$this->eol;
}
return $result;
}

Some files were not shown because too many files have changed in this diff Show More