diff --git a/application/extensions/SettingsWidget/SettingsWidget.php b/application/extensions/SettingsWidget/SettingsWidget.php new file mode 100644 index 00000000000..8e372b7225e --- /dev/null +++ b/application/extensions/SettingsWidget/SettingsWidget.php @@ -0,0 +1,325 @@ +action, $this->method, $this->formHtmlOptions); + } + + public function endForm() + { + echo CHtml::endForm(); + } + public function init() { + parent::init(); + + // Register assets. + Yii::app()->getClientScript()->registerPackage('jquery'); + Yii::app()->getClientScript()->registerCssFile(App()->getAssetManager()->publish(dirname(__FILE__) . '/assets/settingswidget.css')); + Yii::app()->getClientScript()->registerScriptFile(App()->getAssetManager()->publish(dirname(__FILE__) . '/assets/settingswidget.js')); + + // Add default form class. + $this->formHtmlOptions['class'] = isset($this->formHtmlOptions['class']) ? $this->formHtmlOptions['class'] . " settingswidget" : 'settingswidget'; + + + // Start form + $this->beginForm(); + + } + + protected function renderButton($label, $htmlOptions) + { + if (is_string($htmlOptions)) + { + $label = $htmlOptions; + $htmlOptions = array(); + } + echo CHtml::submitButton($label, $htmlOptions); + } + + protected function renderButtons() + { + foreach ($this->buttons as $label => $htmlOptions) + { + $this->renderButton($label, $htmlOptions); + } + } + + protected function renderSetting($name, $metaData, $form = null, $return = false) + { + $defaults = array( + 'class' => array(), + 'type' => 'string', + 'labelOptions' => array( + 'class' => 'control-label' + ) + ); + $metaData = array_merge($defaults, $metaData); + + if (is_string($metaData['class'])) + { + $metaData['class'] = array($metaData['class']); + } + if (isset($metaData['type'])) + { + $function = "render{$metaData['type']}"; + + // Handle localization. + if (isset($metaData['localized']) && $metaData['localized'] == true) + { + $name = "{$name}[{$metaData['language']}]"; + if (isset($metaData['current']) && is_array($metaData['current']) && isset($metaData['current'][$metaData['language']])) + { + $metaData['current'] = $metaData['current'][$metaData['language']]; + } + else + { + unset($metaData['current']); + } + } + + + $result = $this->$function($name, $metaData, $form); + + if ($return) + { + return $result; + } + else + { + echo $result; + } + } + } + + protected function renderSettings() + { + //echo '
'; var_dump($this->settings); echo ('
'); return; + foreach($this->settings as $name => $metaData) + { + $this->renderSetting($name, $metaData); + } + } + + + + public function run() { + parent::run(); + + // Render settings + $this->renderSettings(); + // Render buttons + $this->renderButtons(); + // End form + $this->endForm(); + } + + + + + /*********************************************************************** + * Settings renderers. + **********************************************************************/ + + + + public function renderBoolean($name, array $metaData, $form = null) + { + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : ''; + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id); + } + $out .= CHtml::radioButtonList($id, $value, array( + 0 => 'False', + 1 => 'True' + ), array('id' => $id, 'form' => $form, 'container'=>'div', 'separator' => '')); + + + return $out; + } + + public function renderFloat($name, array $metaData, $form = null) + { + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : ''; + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $out .= CHtml::textField($id, $value, array( + 'id' => $id, + 'form' => $form, + 'pattern' => '\d+(\.\d+)?' + )); + + return $out; + } + + public function renderHtml($name, array $metaData, $form = null) + { + // Register CKEditor library for inclusion. + App()->getClientScript()->registerCoreScript('ckeditor'); + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : ''; + $metaData['class'][] = 'htmleditor'; + $readOnly = isset($metaData['readOnly']) ? $metaData['readOnly'] : false; + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $out .= Chtml::tag('div', array('class' => implode(' ', $metaData['class'])), CHtml::textArea($id, $value, array('id' => $id, 'form' => $form, 'readonly' => $readOnly))); + return $out; + } + + public function renderInt($name, array $metaData, $form = null) + { + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : ''; + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $out .= CHtml::textField($id, $value, array( + 'id' => $id, + 'form' => $form, + 'data-type' => 'int', + 'pattern' => '\d+' + )); + + return $out; + } + + public function renderLogo($name, array $metaData) + { + return CHtml::image($metaData['path']); + } + public function renderRelevance($name, array $metaData, $form = null) + { + $out = ''; + $metaData['class'][] = 'relevance'; + $id = $name; + + + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $value = isset($metaData['current']) ? $metaData['current'] : ''; + + $out .= CHtml::textArea($name, $value, array('id' => $id, 'form' => $form, 'class' => implode(' ', $metaData['class']))); + + return $out; + } + + public function renderSelect($name, array $metaData, $form = null) + { + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : (isset($metaData['default']) ? $metaData['default'] : null); + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $out .= CHtml::dropDownList($name, $value, $metaData['options'], array('form' => $form)); + + return $out; + } + + public function renderString($name, array $metaData, $form = null) + { + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : ''; + $readOnly = isset($metaData['readOnly']) ? $metaData['readOnly'] : false; + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $out .= CHtml::textField($id, $value, array('id' => $id, 'form' => $form, 'class' => implode(' ', $metaData['class']), 'readonly' => $readOnly)); + + return $out; + } + + public function renderPassword($name, array $metaData, $form = null) + { + $out = ''; + $id = $name; + $value = isset($metaData['current']) ? $metaData['current'] : ''; + if (isset($metaData['label'])) + { + $out .= CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + $out .= CHtml::passwordField($id, $value, array('id' => $id, 'form' => $form)); + + return $out; + } + + public function renderList($name, array $metaData, $form = null) + { + + if (isset($metaData['label'])) + { + $label = CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + else + { + $label = ''; + } + + $headers = ''; + $cells = ''; + foreach ($metaData['items'] as $itemName => $itemMetaData) + { + $headers .= CHtml::tag('th', array(), $itemMetaData['label']); + unset($itemMetaData['label']); + $cells .= CHtml::tag('td', array(), $this->renderSetting($itemName . '[]', $itemMetaData, $form, true)); + } + $headers .= CHtml::tag('th'); + $cells .= CHtml::tag('td', array(), $this->widget('bootstrap.widgets.TbButtonGroup', array( + 'type' => 'link', + 'buttons' => array( + array('icon' => 'icon-minus', 'htmlOptions' => array('class' => 'remove')), + array('icon' => 'icon-plus', 'htmlOptions' => array('class' => 'add')), + ) + + ), true)); + echo CHtml::openTag('div', array('class' => 'settingslist')); + echo CHtml::openTag('table'); + // Create header row. + echo CHtml::openTag('thead'); + echo $headers; + echo CHtml::closeTag('thead'); + + // Create cells. + echo CHtml::openTag('tbody'); + echo CHtml::openTag('tr'); + echo $cells; + echo CHtml::closeTag('tr'); + echo CHtml::closeTag('tbody'); + echo CHtml::closeTag('table'); + echo CHtml::closeTag('div'); + } + } + +?> \ No newline at end of file diff --git a/application/extensions/SettingsWidget/assets/settingswidget.css b/application/extensions/SettingsWidget/assets/settingswidget.css new file mode 100644 index 00000000000..cc82f3121ff --- /dev/null +++ b/application/extensions/SettingsWidget/assets/settingswidget.css @@ -0,0 +1,19 @@ +div.row { + border: 2px solid red; +} + +/* + Special layout for list settings (inline). +*/ + +form.settingswidget .settingslist input[data-type=int]{ + width: 50px; +} + +form.settingswidget .settingslist table{ + width: 100%; +} +form.settingswidget .settingslist label{ + width: auto; + margin-right: 5px; +} \ No newline at end of file diff --git a/application/extensions/SettingsWidget/assets/settingswidget.js b/application/extensions/SettingsWidget/assets/settingswidget.js new file mode 100644 index 00000000000..0649b63dee1 --- /dev/null +++ b/application/extensions/SettingsWidget/assets/settingswidget.js @@ -0,0 +1,42 @@ +$(document).ready(function() { + var removeRow = function () + { + // Don't remove last row. + if ($(this).closest('tbody').children().length > 1) + { + $(this).closest('tr').fadeOut(400, function() { $(this).remove(); }); + } + + }; + + var addRow = function() + { + var baseRow = $(this).closest('tr'); + // Create new row, hidden and with empty inputs. + var newRow = baseRow.clone(true); + newRow.find('input').each(function() { $(this).val(''); }); + + // Check if the first element contains a number value and, if so, increase it by one. + var parts = baseRow.find('input:first').val().match(/([\d]+|[^\d]+)/g); + for (var i = parts.length - 1; i > 0; --i) + { + var num = parseInt(parts[i]); + var length = parts[i].length; + if (num === num) + { + parts[i] = (num + 1).toString(); + while (parts[i].length < length) + { + parts[i] = '0' + parts[i]; + } + } + } + newRow.find('input:first').val(parts.join('')); + baseRow.after(newRow); + newRow.fadeIn(); + + } + $('form.settingswidget .settingslist a.remove').bind('click', removeRow); + $('form.settingswidget .settingslist a.add').bind('click', addRow); + +}) diff --git a/application/helpers/PluginSettingsHelper.php b/application/helpers/PluginSettingsHelper.php index 3a5d5001532..e39191ef03e 100644 --- a/application/helpers/PluginSettingsHelper.php +++ b/application/helpers/PluginSettingsHelper.php @@ -215,7 +215,29 @@ public function renderPassword($name, array $metaData, $form = null) return $out; } - + + public function renderList($name, array $metaData, $form = null) + { + + if (isset($metaData['label'])) + { + $label = CHtml::label($metaData['label'], $id, $metaData['labelOptions']); + } + else + { + $label = ''; + } + + $out = ''; + foreach ($metaData['items'] as $itemName => $itemMetaData) + { + $out .= $this->renderSetting($itemName . '[0]', $itemMetaData, $form, true); + } + return $label . CHtml::tag('div', array('class' => 'list'), CHtml::tag('div', array( + 'class' => 'row', + 'data-index' => 0 + ), $out)); + } } diff --git a/application/views/plugins/configure.php b/application/views/plugins/configure.php index 3960da55680..b16409f4295 100644 --- a/application/views/plugins/configure.php +++ b/application/views/plugins/configure.php @@ -10,22 +10,18 @@ { echo CHtml::tag('h1', array(), sprintf(gT("Settings for plugin %s"), $plugin['name'])); } + $this->widget('ext.SettingsWidget.SettingsWidget', array( - Yii::import("application.helpers.PluginSettingsHelper"); - $PluginSettings = new PluginSettingsHelper(); - - echo CHtml::beginForm('', 'post', array('id' => "pluginsettings-{$plugin['name']}")); - echo CHtml::openTag('ol'); - foreach ($settings as $name => $setting) - { - echo CHtml::tag('li', array(), $PluginSettings->renderSetting($name, $setting, "pluginsettings-{$plugin['name']}", true)); - - } - echo CHtml::closeTag('ol'); - echo CHtml::submitButton(gT('Save plugin settings'), array('name'=>'ok')); - echo CHtml::submitButton(gT('Cancel'), array('name'=>'cancel')); - echo CHtml::endForm(); - + 'settings' => $settings, + 'formHtmlOptions' => array( + 'id' => "pluginsettings-{$plugin['name']}", + ), + 'method' => 'post', + 'buttons' => array( + gT('Save plugin settings'), + gT('Cancel') + ) + )); ?> diff --git a/plugins/Example/Example.php b/plugins/Example/Example.php index 57c12b33a48..15347ac525e 100644 --- a/plugins/Example/Example.php +++ b/plugins/Example/Example.php @@ -3,16 +3,26 @@ class Example extends PluginBase { protected $storage = 'DbStorage'; static protected $description = 'Example plugin'; - static protected $name = 'Example'; protected $settings = array( - 'logo' => array( - 'type' => 'logo', - 'path' => 'assets/logo.png' - ), - 'message' => array( + 'test' => array( 'type' => 'string', 'label' => 'Message' + ), + 'messages' => array( + 'type' => 'list', + 'label' => 'messages', + 'items' => array( + 'number' => array( + 'type' => 'int', + 'label' => 'Index' + ), + 'message' => array( + 'type' => 'string', + 'label' => 'Message' + ), + + ) ) ); @@ -24,6 +34,7 @@ public function __construct(PluginManager $manager, $id) { * Here you should handle subscribing to the events your plugin will handle */ $this->subscribe('afterPluginLoad', 'helloWorld'); + $this->subscribe('afterAdminMenuLoaded'); $this->subscribe('beforeSurveySettings'); $this->subscribe('newSurveySettings'); } @@ -32,34 +43,51 @@ public function __construct(PluginManager $manager, $id) { /* * Below are the actual methods that handle events */ - public function helloWorld() + + public function afterAdminMenuLoaded(PluginEvent $event) { - $this->pluginManager->getAPI()->setFlash($this->get('message', null, null, 'Example popup. Change this via plugin settings.')); + $menu = $event->get('menu', array()); + $menu['left'][]=array( + 'href' => "http://docs.limesurvey.org", + 'alt' => gT('LimeSurvey online manual'), + 'image' => 'showhelp.png' + ); + + $event->set('menu', $menu); } + + public function helloWorld(PluginEvent $event) + { + $count = (int) $this->get('count'); + if ($count === false) $count = 0; + $count++; + $this->pluginManager->getAPI()->setFlash($this->get('message') . $count); + $this->set('count', $count); + } + /** * This event is fired by the administration panel to gather extra settings * available for a survey. * The plugin should return setting meta data. + * @param PluginEvent $event */ - public function beforeSurveySettings() + public function beforeSurveySettings(PluginEvent $event) { - $event = $this->getEvent(); $event->set("surveysettings.{$this->id}", array( 'name' => get_class($this), 'settings' => array( 'message' => array( 'type' => 'string', - 'label' => 'Example survey specific setting (not used):', + 'label' => 'Message to show to users:', 'current' => $this->get('message', 'Survey', $event->get('survey')) ) ) )); } - public function newSurveySettings() + public function newSurveySettings(PluginEvent $event) { - $event = $this->getEvent(); foreach ($event->get('settings') as $name => $value) { diff --git a/themes/default/css/style.css b/themes/default/css/style.css new file mode 100644 index 00000000000..d533df463ea --- /dev/null +++ b/themes/default/css/style.css @@ -0,0 +1,3 @@ +div#poweredby{ + text-align: center; +} diff --git a/themes/default/images/poweredby.png b/themes/default/images/poweredby.png new file mode 100644 index 00000000000..9ac36ae8803 Binary files /dev/null and b/themes/default/images/poweredby.png differ