diff --git a/Plugin.php b/Plugin.php
index 18a1480..ec83915 100644
--- a/Plugin.php
+++ b/Plugin.php
@@ -65,7 +65,7 @@ public function boot() {
* If Blog plugin exists but there is no custom_repeater column, create it
* Mostly because Rainlab Blog plugin was installed after Small Extensions
*/
- if (Schema::hasTable('rainlab_blog_posts') and !Schema::hasColumn('rainlab_blog_posts', 'custom_repeater'))
+ if (Schema::hasTable('rainlab_blog_posts') and !Schema::hasColumn('rainlab_blog_posts', 'custom_repeater'))
{
Schema::table('rainlab_blog_posts', function($table)
{
@@ -145,21 +145,21 @@ public function boot() {
if(Settings::get('custom_repeater_allow', null) and Settings::get('custom_repeater_fields', null))
{
-
+
$fields = Settings::get('custom_repeater_fields', []);
-
+
$columnTypesMap = [
'number' => 'number',
'datepicker' => 'date',
];
- foreach($fields as $field)
+ foreach($fields as $field)
{
$columns = [];
$fieldType = 'sme_json_field';
- if(isset($field['custom_repeater_field_type'])
+ if(isset($field['custom_repeater_field_type'])
and $field['custom_repeater_field_type']
and $field['custom_repeater_field_type'] != 'section')
{
@@ -251,11 +251,11 @@ public function boot() {
// Check for Rainlab.User plugin
$pluginManagerUser = PluginManager::instance()->findByIdentifier('Rainlab.User');
- if ( ($pluginManager && !$pluginManager->disabled) and
+ if ( ($pluginManager && !$pluginManager->disabled) and
($pluginManagerUser && !$pluginManagerUser->disabled) ){
\RainLab\Blog\Models\Post::extend(function($model) {
-
+
$usersFormated = [];
if( Settings::get('blog_rainlab_user') ) {
@@ -266,12 +266,12 @@ public function boot() {
$usersFormated[$user->id] = ($user->surname . ' ' . $user->name);
}
- }
-
+ }
+
$model->addDynamicMethod('listRainlabUsers', function() use($usersFormated) {
return $usersFormated;
});
-
+
});
@@ -514,8 +514,8 @@ public function boot() {
* Check the Rainlab.Translate plugin is installed
*/
$pluginManager = PluginManager::instance()->findByIdentifier('Rainlab.Translate');
-
- if ($pluginManager && !$pluginManager->disabled)
+
+ if ($pluginManager && !$pluginManager->disabled)
{
$fields['custom_fields[api_code]']['type'] = 'text';
} else {
@@ -693,7 +693,7 @@ public function boot() {
'tab' => 'rainlab.blog::lang.post.tab_manage'
];
- if(empty(Settings::get('blog_featured_image_both', null)))
+ if(empty(Settings::get('blog_featured_image_both', null)))
{
$widget->removeField('featured_images');
}
@@ -703,7 +703,7 @@ public function boot() {
'custom_fields[featured_image]' => $featuredImage,
]);
- if(Settings::get('blog_featured_image_meta'))
+ if(Settings::get('blog_featured_image_meta'))
{
$widget->addSecondaryTabFields([
'custom_fields[featured_image_title]' => $featuredImageTitle,
@@ -787,14 +787,14 @@ public function boot() {
/**
* Custom repeater builder (new repeater)
*/
- if(Settings::get('custom_repeater_allow', null) and Settings::get('custom_repeater_fields', null))
+ if(Settings::get('custom_repeater_allow', null) and Settings::get('custom_repeater_fields', null))
{
$fields = [];
$counter = 0;
foreach(Settings::get('custom_repeater_fields', null) as $field) {
-
+
if(empty($field['custom_repeater_field_name'])) {
$fieldName = 'field' . $counter;
} else {
@@ -807,7 +807,7 @@ public function boot() {
{
$fieldName = 'custom_repeater['.$field['custom_repeater_field_name'].']';
}
- else
+ else
{
$fieldName = 'custom_repeater['.$counter.']';
}
@@ -822,9 +822,9 @@ public function boot() {
];
- if(!empty($field['custom_repeater_field_attributes']))
+ if(!empty($field['custom_repeater_field_attributes']))
{
- foreach($field['custom_repeater_field_attributes'] as $value)
+ foreach($field['custom_repeater_field_attributes'] as $value)
{
$fields[$fieldName][$value['attribute_name']] = $value['attribute_value'];
}
@@ -840,14 +840,14 @@ public function boot() {
$options = [];
- if(!empty($field['custom_repeater_field_options']))
+ if(!empty($field['custom_repeater_field_options']))
{
- foreach($field['custom_repeater_field_options'] as $value)
+ foreach($field['custom_repeater_field_options'] as $value)
{
$options[$value['option_key']] = $value['option_value'];
}
}
-
+
$fields[$fieldName]['options'] = $options;
}
@@ -892,7 +892,7 @@ public function boot() {
/**
* Repeater fields (old repeater)
*/
- if(Settings::get('blog_custom_fields_repeater'))
+ if(Settings::get('blog_custom_fields_repeater'))
{
$repeaterFields = [];
@@ -1016,7 +1016,7 @@ public function boot() {
/**
* Blog preview btn
*/
- if(Settings::get('blog_add_preview_btn') and $widget->model->slug)
+ if(Settings::get('blog_add_preview_btn') and $widget->model->slug)
{
$widget->addFields([
'blog_add_preview_btn' => [
@@ -1200,7 +1200,7 @@ public function boot() {
}
- if (Settings::get('allow_grouprepeater_titlefrom'))
+ if (Settings::get('allow_grouprepeater_titlefrom'))
{
// If Rainlab.Translate is not present, bypass translate filters
$pluginManager = PluginManager::instance()->findByIdentifier('Rainlab.Translate');
@@ -1210,7 +1210,7 @@ public function boot() {
$widget->addViewPath(plugins_path().'/janvince/smallextensions/formwidgets/repeater/partials');
});
- if ($pluginManager and !$pluginManager->disabled)
+ if ($pluginManager and !$pluginManager->disabled)
{
// MLRepeater has hardcoded viewPath, so this is only simple workaround
// This looks for a first input and get its value. Can't access titleFrom
@@ -1221,6 +1221,17 @@ public function boot() {
});
}
}
+
+ // RainLab.Pages Duplicating tool
+ if (Settings::get('static_pages_enable_duplicating'))
+ {
+ if (PluginManager::instance()->hasPlugin('RainLab.Pages') && !PluginManager::instance()->isDisabled('RainLab.Pages'))
+ {
+ \RainLab\Pages\Controllers\Index::extend(function ($controller) {
+ $controller->implement[] = 'JanVince.SmallExtensions.Classes.Behaviors.StaticPageCloneController';
+ });
+ }
+ }
}
public function registerSettings() {
@@ -1284,7 +1295,7 @@ public function registerMarkupTags()
$pluginManager = PluginManager::instance()->findByIdentifier('Rainlab.Translate');
if (!$pluginManager or ($pluginManager and $pluginManager->disabled)) {
-
+
$twigExtensions['filters'] = [
'_' => ['Lang', 'get'],
@@ -1306,22 +1317,22 @@ public function registerMarkupTags()
public function twigFilterTruncate($text, $length, $preserveWords = false, $separator = '...') {
- if (mb_strlen($text) <= $length)
+ if (mb_strlen($text) <= $length)
{
return $text;
}
$text = $text." ";
-
+
$text = mb_substr($text, 0, $length);
-
+
if($preserveWords)
{
$text = mb_substr($text, 0, mb_strrpos($text,' '));
}
-
+
$text = $text.$separator;
-
+
return $text;
}
@@ -1358,14 +1369,14 @@ public function registerListColumnTypes()
'sme_image_preview' => function($value) {
if($value){ return ""; }
},
- 'sme_json_field' => function($value, $column, $record)
- {
+ 'sme_json_field' => function($value, $column, $record)
+ {
$values = [];
if(is_array($record->custom_repeater) and isset($column->config['repeaterValue']))
{
foreach($record->custom_repeater as $field)
-
+
if(isset($field[$column->config['repeaterValue']]))
{
$values[] = $field[$column->config['repeaterValue']];
@@ -1378,7 +1389,7 @@ public function registerListColumnTypes()
}
public function registerFormWidgets() {
-
+
return [
'JanVince\SmallExtensions\FormWidgets\PostPreview' => 'postpreview',
];
diff --git a/README.md b/README.md
index 08908cd..f099379 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,10 @@ Rainlab Blog is a great plugin, but none of my clients is happy with MarkDown sy
* If on, new tab Notes and a field Note is added to Menu items editing popup window.
* text is then accessible from page/layout from {{item.viewBag.note}}.
+* **Enable Page duplicating**
+
+ * If on, Static Pages can be duplicated by backend user.
+
## System Extension
> *OctoberCMS > Backend > Settings > Small Extensions > System*
@@ -89,7 +93,7 @@ Rainlab Blog is a great plugin, but none of my clients is happy with MarkDown sy
* If Rainlab Translate plugin is not present, bypass trans and choice functions
* **truncate**
- * {{ text|truncate(20, true, '*') }}
+ * {{ text|truncate(20, true, '*') }}
* length, preserve words (default false), separator (default is '...')
## Report Widgets
@@ -121,9 +125,9 @@ Useful when you need to limit access to several pages or whole site to only admi
----
-> My special thanks goes to:
-> [OctoberCMS](http://www.octobercms.com) team members and supporters for this great system.
-> [Joel kyber](https://unsplash.com/@jtkyber1) for his photo I have used in the plugin banner.
+> My special thanks goes to:
+> [OctoberCMS](http://www.octobercms.com) team members and supporters for this great system.
+> [Joel kyber](https://unsplash.com/@jtkyber1) for his photo I have used in the plugin banner.
> [Font Awesome](http://www.fontawesome.io) for Universal access symbol.
diff --git a/classes/behaviors/StaticPageCloneController.php b/classes/behaviors/StaticPageCloneController.php
new file mode 100644
index 0000000..a5e0950
--- /dev/null
+++ b/classes/behaviors/StaticPageCloneController.php
@@ -0,0 +1,87 @@
+controller = $controller;
+ $this->controller->addJs('/plugins/janvince/smallextensions/classes/behaviors/staticpageclonecontroller/assets/js/staticpageclone.js');
+
+ Event::listen('backend.form.extendFields', function (Form $widget) {
+ if (!$widget->model instanceof Page) {
+ return;
+ }
+ if ($widget->isNested) {
+ return;
+ }
+ $widget->addFields(['custom_toolbar' => [
+ 'type' => 'partial',
+ 'path' => '$/janvince/smallextensions/classes/behaviors/staticpageclonecontroller/partials/_page_toolbar.htm',
+ 'cssClass' => 'collapse-visible',
+ ]]);
+ });
+
+ }
+
+ public function index_onDuplicate()
+ {
+ $type = Request::input('objectType');
+ if ($type != 'page') {
+ throw new ApplicationException(trans('rainlab.pages::lang.object.unauthorized_type', ['type' => $type]));
+ }
+
+ $objectPath = trim(Request::input('objectPath'));
+ if (($object = call_user_func([Page::class, 'load'], Theme::getEditTheme(), $objectPath)) === false) {
+ throw new ApplicationException(trans('rainlab.pages::lang.object.not_found'));
+ }
+
+ // Save it at first and remember return response
+ $response = $this->controller->onSave();
+
+ // Try to find free URL/objectPath
+ $i = 0;
+ do {
+ $newObjectPath = $objectPath . '-' . ++$i;
+ } while (call_user_func([Page::class, 'load'], Theme::getEditTheme(), $newObjectPath));
+
+ // Rewrite values to force create new copy
+ $_POST['objectPath'] = null;
+ $_POST['viewBag']['url'] .= '-' . $i;
+ $_POST['parentFileName'] = $object->getParent() ? preg_replace('#\.htm$#i', '', $object->getParent()->getFileName()) : null;
+ Request::merge(array_only($_POST, ['objectPath', 'viewBag', 'parentFileName']));
+
+ // Dtto for RainLab Translate plugin
+ if (PluginManager::instance()->hasPlugin('RainLab.Translate') && !PluginManager::instance()->isDisabled('RainLab.Translate')) {
+ if (isset($_POST['RLTranslate']) && is_array($_POST['RLTranslate'])) {
+ foreach ($_POST['RLTranslate'] as &$rlTranslate) {
+ $rlTranslate['viewBag']['url'] .= '-' . $i;
+ }
+ Request::merge(array_only($_POST, ['RLTranslate']));
+ }
+ }
+
+ // Call original save with modified request
+ $this->controller->onSave();
+
+ // Return previous response
+ return $response;
+ }
+}
\ No newline at end of file
diff --git a/classes/behaviors/staticpageclonecontroller/assets/js/staticpageclone.js b/classes/behaviors/staticpageclonecontroller/assets/js/staticpageclone.js
new file mode 100644
index 0000000..a0a4a49
--- /dev/null
+++ b/classes/behaviors/staticpageclonecontroller/assets/js/staticpageclone.js
@@ -0,0 +1,17 @@
+function onDuplicatePage(el) {
+ var $el = $(el);
+ $el.request('onDuplicate', {
+ complete: function() {
+ $.oc.pagesPage.updateObjectList('page');
+ }
+ });
+};
+
++function ($) { "use strict";
+ $(document).on('ajaxSuccess', '#pages-master-tabs form', function (event, context, data) {
+ var $form = $(event.currentTarget);
+ if (data.objectPath !== undefined) {
+ $('[data-control=duplicate-button]', $form).removeClass('oc-hide hide')
+ }
+ });
+}(window.jQuery);
\ No newline at end of file
diff --git a/classes/behaviors/staticpageclonecontroller/partials/_page_toolbar.htm b/classes/behaviors/staticpageclonecontroller/partials/_page_toolbar.htm
new file mode 100644
index 0000000..083269f
--- /dev/null
+++ b/classes/behaviors/staticpageclonecontroller/partials/_page_toolbar.htm
@@ -0,0 +1,10 @@
+