Modifying tabs & moving fields to another tab in Page Editor

Recommended Posts

There's native `Fieldset in Tab` for creating editor tabs, but sometimes it could make more sense to put a field that's not directly related to `Content` into `Settings` or `Children` tab (such as for body class or some toggles that I see being used often). You can use the hook below to move fields between the tabs.




// site/ready.php

wire()->addHookAfter('ProcessPageEdit::buildForm', function (HookEvent $e) {
    // make sure we're editing a page and not a user
    if ($e->process != 'ProcessPageEdit') return;

    // $page = $e->object->getPage();
    // if ($page->template != 'home') return;

    // $user = $e->user;
    // if (!$user->hasRole('editor')) return;

    $form = $e->return;

    $contentTab = $form->children->get('id=ProcessPageEditContent');
    $settingsTab = $form->children->get('id=ProcessPageEditSettings');
    // $childrenTab = $form->children->get('id=ProcessPageEditChildren');

    // if page template is set noSettings = true, $settings will not exist
    if (!$settingsTab) return;

    $title = $contentTab->get('title');
    if (!$title) return;



  • Like 11

Share this post

Link to post
Share on other sites
5 minutes ago, maxf5 said:

Great tip! it is also possible to add new tabs? Like a tab called SEO where i put my seo fields.

There's Fieldset in Tab (Open) for that. Create one, add to a template and move fields that you want to show in the tab between tabField and tabfield_END fields



Which gives you a tab like this


  • Like 1
  • Thanks 1

Share this post

Link to post
Share on other sites

Cool stuff! But in the example you would definitely want that template check at the top of the hook (or check the current process with $this->process) or you will get an error when creating or editing a user (no Settings tab exists).

  • Like 2

Share this post

Link to post
Share on other sites
12 minutes ago, Robin S said:

Cool stuff! But in the example you would definitely want that template check at the top of the hook (or check the current process with $this->process) or you will get an error when creating or editing a user (no Settings tab exists).

Good point. I left page template optional (but it's really encouraged), but I didn't consider user pages at all.

Updated the OP.

  • Like 1

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By hellomoto
      I have a manufacturers page select field and a dependent models one with pages of template `model` which are allowed children of pages with template `manufacturer`. So I have this as the findPagesCode for the models page options field:
      return $page->manufacturer->children(); However this requires the page be saved in order to display options. This is not ideal. 
      I have a singular autoload module with the following:
      public function init() { $this->pages->addHookAfter('render', $this, 'filterModels'); } public function filterModels($event) { $page = $event->arguments('page'); if($page->template != 'boat_vessel') return; $this->message("models filter"); } It's doing nothing. 
      I was thinking I could work out something with this example but I would need the above test to be working first anyway...
      $this->pages->addHookAfter('changed', function(HookEvent $event) { $page = $event->object; $change = $event->arguments(0); if($page->template == 'boat_vessel' && $change == 'manufacturer') { // execute some code } }); But what? How do I refresh the models field?
      Is there a way to do this in the field settings? I would think in the custom PHP textarea that `return $page->manufacturer->children();` would work but it doesn't. 
    • By Soma
      There's a permission for allowing editor to use the translator in PW 2.73. I could need this but it doesn't work. It shows the "Language" in the menu but when opening the page it says no entries to show. Anybody has some experience using the lang-edit permission for editor? 
    • By DZuz14
      Hello everyone.
      I am having an issue with navigating the Processwire site, as well as the web to find anything useful to help me get a text editor installed in Processwire. I am not new to development whatsoever, but this is the sort of thing that makes people go to craptastic Wordpress, because its super easy to find what you need.
      Does anyone have any suggestions whatsoever on how to get a text editor field(exactly like the one I am using to write this thread, if at all possible)? I am building a site and all my beautifully written Processwire PHP hooks work great, but when it comes to something simple like setting up and using a module, nothing good is easily found.
    • By hellomoto
      For some reason when I unpublish an already-published page, even of a template other than the specified 'importer', all its fields are cleared on page render. How do I understand and remedy this? Peace & thanks
      public function init() { $this->addHookAfter('Page::render', $this, 'renderEditor'); } protected function renderEditor(HookEvent $event) { $here = $event->object; $page = $this->pages->get($this->input->get->id); //$this->log->message($page->template.$here->process); return; if ($here->process != 'ProcessPageEdit' && $page->template != 'importer') return; $css = $this->config->urls->ProcessImportAll . 'ass/ProcessPageEdit_importer.css'; $event->return = str_replace("</head>", "<link rel='stylesheet' type='text/css' href='{$css}'/>\n</head>", $event->return); $field = wire('fields')->get('name=pi_template'); if (count($field->templateTypes)>0) { $templates = $field->templateTypes; } else { $templates = []; foreach (wire('templates') as $tpl) $templates[] = $tpl->id; } sort($templates); //$tplTypes = json_encode($templates); $templatesFields = $this->getTemplatesFields($templates); $templatesFields = json_encode($templatesFields); $js = <<<EOT <script> $(document).ready(function(){ if (!document.getElementById('Inputfield_status_2048').checked) { $('#ProcessPageEditContent *').prop('disabled',true); } else { var all = $templatesFields; var sel = '#Inputfield_pi_template option[selected="selected"]'; var tpl = getTpl(); var arr = all[tpl]; var def = '<option value=""></option>'; $('#Inputfield_pi_match').prepend(def); filterFields(tpl); $('#wrap_Inputfield_pi_template .asmListItem .asmListItemRemove').click(clearFieldOptions); $('#Inputfield_pi_template').change(function() { var on = $(this).find('option[selected="selected"]')[0]; if (on !== undefined) { tpl = on.value; //console.log('clicked ' + tpl); filterFields(tpl); } else { tpl = null; clearFieldOptions(); } }); function filterFields(t) { if (t != null) { $('#Inputfield_pi_match').prop('disabled',false); arr = all[t]; //console.log(arr); $('#Inputfield_pi_match option').each(templateFieldOptions); $('#wrap_Inputfield_pi_fieldmap select option').each(templateFieldOptions); $('#_Inputfield_pi_maptab').click(function() { //$('#wrap_Inputfield_iu_fieldmap select option').each(templateFieldOptions); }); } else { clearFieldOptions(); } } function getTpl() { if ($(sel).length>0 && $(sel)[0].value) { var tpl = $(sel)[0].value; } else { var tpl = null; } return tpl; } function templateFieldOptions() { var val = parseInt(this.value); if (!arr.includes(val) && val>0) { //console.log(val + ' ' + arr); $(this).prop('selected',false); $(this).prop('disabled',true); } else { if (arr.includes(val) && val>0) { //console.log(tpl); $(this).prop('disabled',false); } } } function clearFieldOptions() { $('#Inputfield_pi_match').find('option[value=""]').first().prop('selected',true); $('#Inputfield_pi_match').find('option[value!=""]').each(function() { $(this).prop('disabled',true); }); $('#Inputfield_pi_match').prop('disabled',true); $('#wrap_Inputfield_pi_fieldmap select').find('option[value=""]').first().prop('selected',true); $('#wrap_Inputfield_pi_fieldmap select').find('option[value!=""]').each(function() { $(this).prop('disabled',true); }); $('#wrap_Inputfield_pi_fieldmap select').prop('disabled',true); } } }); </script> EOT; $event->return = str_replace("</body>", "$js</body>", $event->return); //$this->filterFieldFields($event); }
    • By hellomoto
      This is in my module's install() function:
      $f = new Field(); $f->type = wire('modules')->get('FieldtypeSelectExtOption'); $f->set("name", "iu_template")->set("label", "Template"); $f->option_table = "templates"; $f->option_value = "id"; $f->option_label = "name"; //$f->filter('id not in (2,3,4,5)'); $f->set("required",1); $f->set("tags", "-impupd")->set("columnWidth",50)->save(); $f = new Field(); // set to select from template's allowed parents $f->type = wire('modules')->get('FieldtypePage'); $f->set("name", "iu_parent")->set("label", "Parent"); $f->set("derefAsPage",1)->set("required",1); $f->set("findPagesSelector","template!=admin,hasParent!=2"); $f->set("tags", "-impupd")->set("columnWidth",50)->save(); $f = new Field(); $f->type = wire('modules')->get('FieldtypeOptions'); $f->inputfieldClass = 'InputfieldCheckboxes'; $f->set("name", "iu_actions")->set("label", "Action(s)"); $f->options = ["import" => "Import", "update" => "Update"]; $f->set("tags", "-impupd")->set("columnWidth",50)->save(); $f = new Field(); $f->type = wire('modules')->get('FieldtypeOptions'); $f->set("name", "iu_uid")->set("label", "Match Field"); $f->description = "Values must be unique."; $options = array(); foreach(wire('fields') as $opt) { $options["{$opt->name}"] = "{$opt->title}"; } $f->options = $options; $f->set("required",1); $f->set("tags", "-impupd")->set("columnWidth",50)->save(); Oddly every time I install the module the second field `iu_parent` says it still needs configuring, without fail; I have to go to the field editor and just save it as it is one time.
      Anyway the real field in question here is the last, i.e., `iu_uid`. That method of adding options is entirely ineffective, sadly; it adds none at all. What I need is for it to dynamically populate with the fields in the previously selected template's fieldgroup on saveReady. This is possible with page fields, to dynamically populate options based on another field's value.
      Alternatively I can set up `iu_uid` like so:
      $f = new Field(); $f->type = wire('modules')->get('FieldtypeSelectExtOption'); $f->set("name", "iu_uid")->set("label", "Match Field"); $f->description = "Values must be unique."; $f->option_table = "fields"; $f->option_value = "id"; $f->option_label = "name"; $f->set("required",1); $f->set("tags", "-impupd")->set("columnWidth",50)->save(); Then I would just need for a hook to filter the options to correspond with the `iu_template` value.
      Any pro tips/guidance to spare on this subject?