Answers.Body: <p>Here’s a simple plugin with two tabs and two fields, one field per tab. This plugin was generated on <a href="https://pluginfactory.io/" rel="noreferrer">https://pluginfactory.io/</a>. It’s a great starting point to get the plugin scaffolding set up. After that I added the code that I’ve included below to get it to work.</p>
  
  <p>It should give you something like the following screenshot for the first tab:</p>
  
  <p><a href="https://i.sstatic.net/yBZrD.png" rel="noreferrer"><img src="https://i.sstatic.net/yBZrD.png" alt="First tab of plugin"></a></p>
  
  <p>and for the second tab it should look similar to this:</p>
  
  <p><a href="https://i.sstatic.net/FIiba.png" rel="noreferrer"><img src="https://i.sstatic.net/FIiba.png" alt="Second tab of plugin"></a></p>
  
  <h1>Folder Structure</h1>
  
  <pre><code>[-] craft
     [-] plugins
        [-] myplugin
           [-] controllers
              |-- MyPlugin_TabOneController.php
              |-- MyPlugin_TabTwoController.php
           [+] resources
           [+] services
           [-] templates
              [-] _layouts
                 |-- cp.twig
              [-] tabOne
                 |-- _index.twig
              [-] tabTwo
                 |-- _index.twig
           [+] translations
           [+] variables
           |-- MyPluginPlugin.php
  </code></pre>
  
  <hr>
  
  <h2><code>myplugin/MyPluginPlugin.php</code></h2>
  
  <p>I'm only highlighting the functions that were necessary for the plugins to show up. Add or adjust them as needed.</p>
  
  <pre class="lang-php prettyprint-override"><code>...
  
  public function registerCpRoutes()
  {
      return array(
          'myplugin' =&gt; ['action' =&gt; "myPlugin/tabOne/index"],
          'myplugin/additional-settings' =&gt; ['action' =&gt; "myPlugin/tabTwo/index"],
          /**
           * Each 'action' relates to a controller in your plugin.
           * The names of your controller and these action must match.
           */
      );
  }
  
  protected function defineSettings()
  {
      return array(
          'someSetting' =&gt; array(AttributeType::String, 'label' =&gt; 'Some Setting', 'default' =&gt; ''),
          'additionalSetting' =&gt; array(AttributeType::String, 'label' =&gt; 'Some Additional Setting', 'default' =&gt; ''),
          /*...more settings here as needed...*/
      );
  }
  
  public function getSettingsHtml()
  {
     return craft()-&gt;templates-&gt;render('myplugin/MyPlugin_Settings', array(
         'settings' =&gt; $this-&gt;getSettings()
     ));
  }
  
  public function getSettingsUrl()
  {
      return 'myplugin';
      /* This must be the same as the first segment in your cpRoutes. */
  }
  
  ...
  </code></pre>
  
  <hr>
  
  <h2><code>myplugin/controllers/MyPlugin_TabOneController.php</code></h2>
  
  <pre class="lang-php prettyprint-override"><code>namespace Craft;
  
  class MyPlugin_TabOneController extends BaseController
  {
  
      /**
       * @var    bool|array Allows anonymous access to this controller's actions.
       * @access protected
       */
      protected $allowAnonymous = array('actionIndex',
          );
  
      /**
       */
      public function actionIndex()
      {
          $settings = craft()-&gt;plugins-&gt;getPlugin('myPlugin')-&gt;getSettings();
          $this-&gt;renderTemplate('myplugin/tabOne/_index', array(
              'settings' =&gt; $settings,
          ));
          /**
           * The path above is to your templates folder in your plugin.
           */
      }
  }
  </code></pre>
  
  <hr>
  
  <h2><code>myplugin/controllers/MyPlugin_TabTwoController.php</code></h2>
  
  <pre class="lang-php prettyprint-override"><code>namespace Craft;
  
  class MyPlugin_TabTwoController extends BaseController
  {
  
      /**
       * @var    bool|array Allows anonymous access to this controller's actions.
       * @access protected
       */
      protected $allowAnonymous = array('actionIndex',
          );
  
      /**
       */
      public function actionIndex()
      {
          $settings = craft()-&gt;plugins-&gt;getPlugin('myPlugin')-&gt;getSettings();
          $this-&gt;renderTemplate('myplugin/tabTwo/_index', array(
              'settings' =&gt; $settings,
          ));
          /**
           * The path above is to your templates folder in your plugin.
           */
      }
  }
  </code></pre>
  
  <hr>
  
  <h2><code>myplugin/templates/_layouts/cp.twig</code></h2>
  
  <p>It’s not necessary to break out the templates like I have here but it does help to keep from repeating your self in the templates, especially if you have several tabs.</p>
  
  <pre class="lang-twig prettyprint-override"><code>{% extends "_layouts/cp" %}
  {% import "_includes/forms" as forms %}
  
  {% includeCssResource "myplugin/css/Myplugin_Style.css" %}
  {% includeJsResource "myplugin/js/Myplugin_Script.js" %}
  
  {% set title = 'My Plugin' %}
  
  {% set tabs = {
      settings: { label: "Settings"|t, url: url('myplugin') },
      additionalSettings: { label: "Additional Settings"|t, url: url('myplugin/additional-settings') },
  } %}
  </code></pre>
  
  <hr>
  
  <h2><code>myplugin/templates/tabOne/_index.twig</code></h2>
  
  <p>The <code>data-saveshortcut</code> attribute on the form will allow you to use the keyboard shortcut to save your plugin’s settings by using <kbd>⌘ + S</kbd> on a Mac. I’m assuming that <kbd>Ctrl + S</kbd> works on Windows although I haven’t tried it.</p>
  
  <pre class="lang-twig prettyprint-override"><code>{% extends "myplugin/_layouts/cp" %}
  {% import "_includes/forms" as forms %}
  
  {% set selectedTab = 'settings' %}
  
  {% set content %}
  
      &lt;h2&gt;Settings&lt;/h2&gt;
  
      &lt;form method="post" action="" accept-charset="UTF-8" data-saveshortcut data-confirm-unload&gt;
  
          {{ getCsrfInput() }}
  
          &lt;input type="hidden" name="action" value="plugins/savePluginSettings"&gt;
          &lt;input type="hidden" name="pluginClass" value="MyPlugin"&gt;
          &lt;input type="hidden" name="redirect" value="myplugin"&gt;
  
          {{ forms.textField({
              label: 'Some Setting',
              instructions: 'Enter some setting here.',
              id: 'settings-someSetting',
              name: 'settings[someSetting]', 
              value: settings['someSetting']})
          }}
  
          &lt;div class="buttons"&gt;
              &lt;input class="btn submit" type="submit" value="{{ 'Save'|t }}"&gt;
          &lt;/div&gt;
  
      &lt;/form&gt;
  
  {% endset %}
  </code></pre>
  
  <hr>
  
  <h2><code>myplugin/templates/tabOne/_index.twig</code></h2>
  
  <p>Notice that you’ll need to set the <code>selectedTab</code> in each template for the tabs to work correctly. This should match one of your keys that was set in the <code>tabs</code> variable in the <code>myplugin/templates/_layouts/cp.twig</code>. Also the hidden <code>redirect</code> input in the form should be set to wherever you want to it end up. I noticed that it felt the best when it went back to the original page and it showed the saved values.</p>
  
  <pre class="lang-twig prettyprint-override"><code>{% extends "myplugin/_layouts/cp" %}
  {% import "_includes/forms" as forms %}
  
  {% set selectedTab = 'additionalSettings' %}
  
  {% set content %}
  
      &lt;h2&gt;Additional Settings&lt;/h2&gt;
  
      &lt;form method="post" action="" accept-charset="UTF-8" data-saveshortcut data-confirm-unload&gt;
  
          {{ getCsrfInput() }}
  
          &lt;input type="hidden" name="action" value="plugins/savePluginSettings"&gt;
          &lt;input type="hidden" name="pluginClass" value="MyPlugin"&gt;
          &lt;input type="hidden" name="redirect" value="myplugin/additional-settings"&gt;
  
          {{ forms.textField({
              label: 'Some Additional Setting',
              instructions: 'Enter some setting here.',
              id: 'settings-additionalSetting',
              name: 'settings[additionalSetting]', 
              value: settings['additionalSetting']})
          }}
  
          &lt;div class="buttons"&gt;
              &lt;input class="btn submit" type="submit" value="{{ 'Save'|t }}"&gt;
          &lt;/div&gt;
  
      &lt;/form&gt;
  
  {% endset %}
  </code></pre>
  
  <p>That should do it!</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-01-31T20:49:25.343Z
Answers.Text: the return for getSettingsHtml() does seem to be correct. Where do I have to link here or what to return.
Answers.UserDisplayName: Simon Franzen
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2018-01-31T20:55:17.383Z
Answers.Text: Oh ok. I was on the wrong track. I still used the old URL in my browser. But what about the getSettingHTML -> I think it is not needed, because defined other routes and templates
Answers.UserDisplayName: Simon Franzen
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2017-08-11T15:11:46.080Z
Answers.LastActivityDate: 2017-11-26T14:23:15.593Z
Answers.LastEditDate: 2017-11-26T14:23:15.593Z
Answers.UserDisplayName: Cavell Blood
Body: <p>I would like to add tabs in setting page in my plugin. I have the following code in <code>/[myplugin]/templates/_setting.html</code>. However, the tabs do not show up, does anyone have any sample code to set up the tabs? Thanks</p>
  
  <pre><code>{% import "_includes/forms" as forms %}
  
  {% set tabs = {
  customer: { label: "Customer"|t, url: url('emailUser/_settings') },
  veterinarian:    { label: "Veterinarian"|t, url: url('emailUser/_vetSettings') }
  } %}
  
  {{ forms.textField({
  label: "Customer Account Creation Confirmation Subject"|t,
  instructions: "Customer Account Creation Confirmation Subject" |t,
  name: 'custAccountCreationSubject',
  value: settings.custAccountCreationSubject,
  })}}
  
  {{ forms.textareaField({
  label: "Customer Account Creation Confirmation Message"|t,
  instructions: "Customer email message when customer register" |t,
  name: 'custAccountCreation',
  value: settings.custAccountCreation,
  rows:10,
  })}}
  </code></pre>
  
  <p>In <code>myplugin.php</code></p>
  
  <pre><code>public function getSettingsHtml()
  {
      return craft()-&gt;templates-&gt;render('emailuser/_settings',array(
                  'settings'=&gt;$this-&gt;getSettings()
      ));
  }
  </code></pre>
  
Tags: <plugin-development><settings>
Title: Set tabs in settings on Plugin
ThreadText: # Set tabs in settings on Plugin
  
  (Asked by: username_0 on 2017-07-04)
  
  I would like to add tabs in setting page in my plugin. I have the following code in `/[myplugin]/templates/_setting.html`. However, the tabs do not show up, does anyone have any sample code to set up the tabs? Thanks
  
  ```
  {% import "_includes/forms" as forms %}
  
  {% set tabs = {
  customer: { label: "Customer"|t, url: url('emailUser/_settings') },
  veterinarian:    { label: "Veterinarian"|t, url: url('emailUser/_vetSettings') }
  } %}
  
  {{ forms.textField({
  label: "Customer Account Creation Confirmation Subject"|t,
  instructions: "Customer Account Creation Confirmation Subject" |t,
  name: 'custAccountCreationSubject',
  value: settings.custAccountCreationSubject,
  })}}
  
  {{ forms.textareaField({
  label: "Customer Account Creation Confirmation Message"|t,
  instructions: "Customer email message when customer register" |t,
  name: 'custAccountCreation',
  value: settings.custAccountCreation,
  rows:10,
  })}}
  
  ```
  
  In `myplugin.php`
  
  ```
  public function getSettingsHtml()
  {
      return craft()->templates->render('emailuser/_settings',array(
                  'settings'=>$this->getSettings()
      ));
  }
  
  ```
  
  **Tags:** plugin-development, settings
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2017-08-11. Score: 9 | Accepted answer
  
  Here’s a simple plugin with two tabs and two fields, one field per tab. This plugin was generated on <https://pluginfactory.io/>. It’s a great starting point to get the plugin scaffolding set up. After that I added the code that I’ve included below to get it to work.
  
  It should give you something like the following screenshot for the first tab:
  
  [![First tab of plugin](https://i.sstatic.net/yBZrD.png)](https://i.sstatic.net/yBZrD.png)
  
  and for the second tab it should look similar to this:
  
  [![Second tab of plugin](https://i.sstatic.net/FIiba.png)](https://i.sstatic.net/FIiba.png)
  
  # Folder Structure
  
  ```
  [-] craft
     [-] plugins
        [-] myplugin
           [-] controllers
              |-- MyPlugin_TabOneController.php
              |-- MyPlugin_TabTwoController.php
           [+] resources
           [+] services
           [-] templates
              [-] _layouts
                 |-- cp.twig
              [-] tabOne
                 |-- _index.twig
              [-] tabTwo
                 |-- _index.twig
           [+] translations
           [+] variables
           |-- MyPluginPlugin.php
  
  ```
  
  ---
  
  ## `myplugin/MyPluginPlugin.php`
  
  I'm only highlighting the functions that were necessary for the plugins to show up. Add or adjust them as needed.
  
  ```php
  ...
  
  public function registerCpRoutes()
  {
      return array(
          'myplugin' => ['action' => "myPlugin/tabOne/index"],
          'myplugin/additional-settings' => ['action' => "myPlugin/tabTwo/index"],
          /**
           * Each 'action' relates to a controller in your plugin.
           * The names of your controller and these action must match.
           */
      );
  }
  
  protected function defineSettings()
  {
      return array(
          'someSetting' => array(AttributeType::String, 'label' => 'Some Setting', 'default' => ''),
          'additionalSetting' => array(AttributeType::String, 'label' => 'Some Additional Setting', 'default' => ''),
          /*...more settings here as needed...*/
      );
  }
  
  public function getSettingsHtml()
  {
     return craft()->templates->render('myplugin/MyPlugin_Settings', array(
         'settings' => $this->getSettings()
     ));
  }
  
  public function getSettingsUrl()
  {
      return 'myplugin';
      /* This must be the same as the first segment in your cpRoutes. */
  }
  
  ...
  
  ```
  
  ---
  
  ## `myplugin/controllers/MyPlugin_TabOneController.php`
  
  ```php
  namespace Craft;
  
  class MyPlugin_TabOneController extends BaseController
  {
  
      /**
       * @var    bool|array Allows anonymous access to this controller's actions.
       * @access protected
       */
      protected $allowAnonymous = array('actionIndex',
          );
  
      /**
       */
      public function actionIndex()
      {
          $settings = craft()->plugins->getPlugin('myPlugin')->getSettings();
          $this->renderTemplate('myplugin/tabOne/_index', array(
              'settings' => $settings,
          ));
          /**
           * The path above is to your templates folder in your plugin.
           */
      }
  }
  
  ```
  
  ---
  
  ## `myplugin/controllers/MyPlugin_TabTwoController.php`
  
  ```php
  namespace Craft;
  
  class MyPlugin_TabTwoController extends BaseController
  {
  
      /**
       * @var    bool|array Allows anonymous access to this controller's actions.
       * @access protected
       */
      protected $allowAnonymous = array('actionIndex',
          );
  
      /**
       */
      public function actionIndex()
      {
          $settings = craft()->plugins->getPlugin('myPlugin')->getSettings();
          $this->renderTemplate('myplugin/tabTwo/_index', array(
              'settings' => $settings,
          ));
          /**
           * The path above is to your templates folder in your plugin.
           */
      }
  }
  
  ```
  
  ---
  
  ## `myplugin/templates/_layouts/cp.twig`
  
  It’s not necessary to break out the templates like I have here but it does help to keep from repeating your self in the templates, especially if you have several tabs.
  
  ```twig
  {% extends "_layouts/cp" %}
  {% import "_includes/forms" as forms %}
  
  {% includeCssResource "myplugin/css/Myplugin_Style.css" %}
  {% includeJsResource "myplugin/js/Myplugin_Script.js" %}
  
  {% set title = 'My Plugin' %}
  
  {% set tabs = {
      settings: { label: "Settings"|t, url: url('myplugin') },
      additionalSettings: { label: "Additional Settings"|t, url: url('myplugin/additional-settings') },
  } %}
  
  ```
  
  ---
  
  ## `myplugin/templates/tabOne/_index.twig`
  
  The `data-saveshortcut` attribute on the form will allow you to use the keyboard shortcut to save your plugin’s settings by using `⌘ + S` on a Mac. I’m assuming that `Ctrl + S` works on Windows although I haven’t tried it.
  
  ```twig
  {% extends "myplugin/_layouts/cp" %}
  {% import "_includes/forms" as forms %}
  
  {% set selectedTab = 'settings' %}
  
  {% set content %}
  
      <h2>Settings</h2>
  
      <form method="post" action="" accept-charset="UTF-8" data-saveshortcut data-confirm-unload>
  
          {{ getCsrfInput() }}
  
          <input type="hidden" name="action" value="plugins/savePluginSettings">
          <input type="hidden" name="pluginClass" value="MyPlugin">
          <input type="hidden" name="redirect" value="myplugin">
  
          {{ forms.textField({
              label: 'Some Setting',
              instructions: 'Enter some setting here.',
              id: 'settings-someSetting',
              name: 'settings[someSetting]', 
              value: settings['someSetting']})
          }}
  
          <div class="buttons">
              <input class="btn submit" type="submit" value="{{ 'Save'|t }}">
          </div>
  
      </form>
  
  {% endset %}
  
  ```
  
  ---
  
  ## `myplugin/templates/tabOne/_index.twig`
  
  Notice that you’ll need to set the `selectedTab` in each template for the tabs to work correctly. This should match one of your keys that was set in the `tabs` variable in the `myplugin/templates/_layouts/cp.twig`. Also the hidden `redirect` input in the form should be set to wherever you want to it end up. I noticed that it felt the best when it went back to the original page and it showed the saved values.
  
  ```twig
  {% extends "myplugin/_layouts/cp" %}
  {% import "_includes/forms" as forms %}
  
  {% set selectedTab = 'additionalSettings' %}
  
  {% set content %}
  
      <h2>Additional Settings</h2>
  
      <form method="post" action="" accept-charset="UTF-8" data-saveshortcut data-confirm-unload>
  
          {{ getCsrfInput() }}
  
          <input type="hidden" name="action" value="plugins/savePluginSettings">
          <input type="hidden" name="pluginClass" value="MyPlugin">
          <input type="hidden" name="redirect" value="myplugin/additional-settings">
  
          {{ forms.textField({
              label: 'Some Additional Setting',
              instructions: 'Enter some setting here.',
              id: 'settings-additionalSetting',
              name: 'settings[additionalSetting]', 
              value: settings['additionalSetting']})
          }}
  
          <div class="buttons">
              <input class="btn submit" type="submit" value="{{ 'Save'|t }}">
          </div>
  
      </form>
  
  {% endset %}
  
  ```
  
  That should do it!
  
  > **Comments:**
  > **username_2**: the return for getSettingsHtml() does seem to be correct. Where do I have to link here or what to return.  
  > **username_2**: Oh ok. I was on the wrong track. I still used the old URL in my browser. But what about the getSettingHTML -> I think it is not needed, because defined other routes and templates  
  
  ---
  
  