Jump to content
novalex

Adding a custom tab on page edit screen

Recommended Posts

Hi,

New to PW, please forgive my ignorance if this has been answered before, I have scoured the docs and forums but couldn't find anything.

I'm trying to add a custom tab to the Page Edit screen (or process), which will contain fields for a page hero. Ideally, I would have a "Hero" tab before "Content", which would contain fields for images, text, a CTA button, etc. What I have so far is this:

class HeroTab extends WireData implements Module {

	public static function getModuleInfo() {
		return array(
			'title'    => 'Page Hero',
			'version'  => 1,
			'summary'  => 'Header fields for pages.',
			'singular' => true,
			'autoload' => true,
		);
	}

	public function ready() {
		if(wire('page')->process != 'ProcessPageEdit') return;

		$this->addHookAfter('ProcessPageEdit::buildForm', $this, 'addTab');
	}

	public function addTab(HookEvent $event) {
		$form = $event->return;

		// create the tab
		$hero = new InputfieldWrapper();
		$hero->attr('id+name', $this->className() . 'hero');
		$hero->attr('title', $this->_('Hero'));

		// Images
		$fimages = $this->modules->get("InputfieldImage");
		$fimages->attr('id+name', 'hero_images');
		$fimages->label = $this->_('Images');
		$fimages->extensions = 'gif jpg jpeg png svg';
		$hero->append($fimages);

		// Text
		$ftext = $this->modules->get("InputfieldCKEditor");
		$ftext->attr('id+name', 'hero_text');
		$ftext->label = $this->_('Text');
		$hero->append($ftext);

		$form->prepend($hero);
	}
}

This adds the tab and the fields, but after the "View" tab. However, the bigger problem is that the fields do not save. When I click Save, the page refreshes, with the "Saved Page" notice, but the fields are empty.

I got inspiration from https://github.com/adrianbj/ProcessRedirectIds/blob/master/ProcessRedirectIds.module, but I think it is for the older 2.x version because it originally used $form->append which put the bottom save button between the tabs and tab content.

Any help would be appreciated, I am thoroughly lost and the documentation doesn't give any examples for what I'm trying to do.

Cheers

Screenshot from 2017-05-08 18:21:34.jpeg

Share this post


Link to post
Share on other sites

Thank you for the prompt reply!

I followed the instructions there and got the tab in the position I wanted. The fields are still not saving though, I'm really scratching my head now.

Share this post


Link to post
Share on other sites

Can't you just make a regular fieldset tab and put them in there, i have sometimes 2-3 content tabs, and there is no reason to make a module.

also on that example posted, the solution provided is only for outputting markup, like some instructions, or like a training video etc. AFAIK you cannot add a tab to page edit and expect those fields to automagically save to the database b/c the fields need to exist in the database to save to.

  • Like 1

Share this post


Link to post
Share on other sites
1 minute ago, Macrura said:

Can't you just make a regular fieldset tab and put them in there, i have sometimes 2-3 content tabs, and there is no reason to make a module.

I tried that as well, but these fields need to appear on every page and there's no way to set a fieldset as global, and I don't want them appearing on the fields list either, as I will eventually have many of these custom fields and it will be a mess.

Share this post


Link to post
Share on other sites

this has been discussed in the context of a repeater field with a limit of 1 item; afaik there is no way to do what you are describing

  • Like 1

Share this post


Link to post
Share on other sites
7 hours ago, novalex said:

I tried that as well, but these fields need to appear on every page and there's no way to set a fieldset as global, and I don't want them appearing on the fields list either, as I will eventually have many of these custom fields and it will be a mess.

All fields in PW are custom fields. And you can't save content to a page without an actual field added to the page's template to hold that content (in your module you are only dealing with inputfields which themselves do not save their content to the DB).

If you like you can add tags to your fields to create groups of fields within the fields list and keep things tidy. As @Macrura said, currently the only way to add a group of fields to a template in the form of a single unit is to create a repeater with those fields and limit it to a single repeater item. Or if you prefer, a PageTable or Profields Table in conjunction with Limit PageTable or Limit Table.

Another approach that I sometimes take (if the project is well planned out in advance) is first create a template with the fields/fieldsets that will be used on all templates, and then I duplicate that template as the starting point for each additional template.

  • Like 4

Share this post


Link to post
Share on other sites

yep. you can also read this post (the whole thread) to see one option:

 

  • Like 1

Share this post


Link to post
Share on other sites

Thanks for all the help guys, appreciated. I ended up just creating repeater fields, nested as needed, and it works well enough. Coming from WordPress with ACF or CMB2, I'm surprised how flexible the PW system is and how many options it offers for the fields. I just wish the documentation was a little more comprehensive and offered some examples as well, sort of like the WordPress codex, since I'm having a hard time understanding/following the source code.

I look forward to working with PW some more, I'm sure I'll get the hang of it soon and then it's bye-bye WordPress :)

  • Like 4

Share this post


Link to post
Share on other sites

hi novalex!

great that you found a way :) just take care if you have a more complex setup and need some advanced selectors the repeater setup COULD be a problem (or a little harder). if you have 2 regular fields forename and surname a selector would be:

$pages->find('forename=john, surname=doe');

if you have a repeater setup, it would be:

$pages->find('yourgroup.forename=john, yourgroup.surname=doe');

as long as it is simple like this it will work, but i can remember that it can lead to problems but don't remember exactly... just keep in mind that it could be easier to have "duplicate" fields and simple selectors than the other way round ;)

  • Like 1

Share this post


Link to post
Share on other sites

Yep, already came across this "issue", where I have to use something like "$page->home_tabs->home_tabs_content" in my template.

I'm not sure if I'm doing this right, I'm used to ACF where field IDs inside repeaters couldn't collide with other fields, so I could do "$home_tabs['content']", but since in PW each field has to have a unique ID, my selectors for nested fields are getting pretty long and hard to read because of the repetition. Any suggestions for that? I can't simplify too much, the site I'm working on now has some complex sections which require heavy repeater use.

Share this post


Link to post
Share on other sites
6 hours ago, novalex said:

Yep, already came across this "issue", where I have to use something like "$page->home_tabs->home_tabs_content" in my template.

You can avoid some repetition and shorten the way you get field content in your templates like this:

// Get the repeater item (the item is a page)
$r_item = $page->repeater_field->first();

// Get field content from the item
echo $r_item->foo;
echo $r_item->bar;
echo $r_item->baz;

 

  • Like 3

Share this post


Link to post
Share on other sites
Quote

I can't simplify too much, the site I'm working on now has some complex sections which require heavy repeater use.

you would not imagine how often i have thought that myself and then i found out how to do it the processwire-way and it just seemed too simple with something like a 3-liner ;)

"heavy use" of repeater sounds like you could maybe improve/change how you structured your content. in PW thats a very important part of your work. if you structure your project/data well, most of the time you end up with very simple and clean selector-calls like

$pages->find('template=product');
// or
$page->children('category=car');

of course that's just wild guesses, but if you want to share your setup i'm sure you'll get valuable feedback from lots of knowledable guys (and girls) here :)

  • Like 3

Share this post


Link to post
Share on other sites

@Robin S There's still repetition because the repeater field properties are also prefixed with the pertinent section ID, such as "home_tabs" to avoid field ID collision. Perhaps I could make a wrapper function to grab fields from a repeater, something like "get_section_field('content', 'home_tabs')", do you think it's a good idea? It seems easier to read to me.

@bernhard Agree on all points! Trouble is, this is a website for a client, the design is already approved and I cannot restructure it. I could have fields for each section, which would simplify things, but the client must be able to edit, add and remove sections as needed, hence the heavy use of repeaters.

I came across another issue, and I have no idea what the problem is. I need a few admin pages to keep various site options such as API keys, common content displayed across pages, etc. I have created two templates, set their icon, and added pages under the admin page tree using these templates. However, in the menu only the parent page appears, and its icon is the default sheet of paper, not the cog I have selected. Screenshot of the page tree and admin menu attached. Any idea what I'm doing wrong?

Cheers, and thanks for all the help so far!

Alex

Screenshot from 2017-05-17 11:47:36.png

Share this post


Link to post
Share on other sites
2 hours ago, novalex said:

There's still repetition because the repeater field properties are also prefixed with the pertinent section ID, such as "home_tabs" to avoid field ID collision.

I'm not sure what you mean here. There is no prefix to field names that are used in a repeater. For example, if you have the "title" field in your containing page template, and also the title field in your repeater template then the field name "title" applies to both without any prefix.

echo $page->title; // the containing page title
echo $page->my_repeater->first->title; // the first repeater item's title

Or do you mean that in Page Edit the inputfields inside a repeater have a suffix to their name, e.g. "title_repeater1147"? Because you don't need to use that when you get the field in your template.

  • Like 2

Share this post


Link to post
Share on other sites

@Robin S I mean when I create fields for a repeater, let's say I need an image, an icon, and text, I assign them IDs prefixed with the repeater ID, so I create 3 fields named "home_tabs_image", "home_tabs_icon", and "home_tabs_text" and add them to the "home_tabs" repeater. Your reply made me realize I've been going at it like a neanderthal, and I don't actually need to create unique fields for a repeater, I just need to create each type of field I'll need once and I can assign them multiple times to different templates and repeaters. Boy, do I feel dumb right now :lol:.

Share this post


Link to post
Share on other sites
2 hours ago, novalex said:

Boy, do I feel dumb right now :lol:

never mind, the simplicity of processwire was a big hurdle for many of us in the beginning ;):P

...and i'm still curious what setup would need lots of repeaters for structuring everything. usually you can keep everything very clean just by using different templates, different parents in the tree and doing the relations via pagefields. don't get me wrong. it's just my experience that whenever something felt complex/complicated it was most of the times a problem of my (or other forum users) setup :)

i wish you lots of happy aha-moments ;)

  • Like 3

Share this post


Link to post
Share on other sites

Yep, if I'd learned PW from the start I'd probably be better off, but I'm used to the WP + ACF way of doing things, so they creep into this PW project as well.

As for the setup, for example, I have a group of tabs on the homepage, and each tab has sections with an image, icon and text. I figured the best way to do this would be to have a repeater for the tabs themselves and a nested repeater for each tab's sections. I have a pretty short deadline, so I had to just jump in head-first and get the homepage done to show the client. I'll definitely need some time to learn the ropes properly, and then I'll be able to better think every field and template out from the start and make things simpler.

Thanks a lot, once again, to all of you who took the time to help a newbie out, I hope to return the favor sometimes.

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By EyeDentify
      I have been experimenting with the new $page->meta() method and find it useful.

      Once i figured out that the data i "save" with it is tied to the page where i called the method from.

      So this is not obvious at least not for me in the documentation:
      https://processwire.com/api/ref/page/meta/
       
      So i just wanted to share that revelation with the community so you don´t get as confused as i was.

      Happy Coding Everyone.
    • By stanoliver
      In the new page-meta-method (https://processwire.com/blog/posts/pw-3.0.133/#comments) there is an example how to ouput "world". 
      Could may someone give me a snippet of code so that the output is one of the colors (red, green, blue).
    • By VeiJari
      Hello forum. I'm trying to figure out how to detect if current form has errors after saving it in pw admin page. This is so that our event location isn't added in database if we detect errors. But I can't seem to find a correct way to detect errors? I only found the wire()->errors() and it always triggers even if I don't have any errors. 
      How can I detect errors in a form?
      By error I mean $page->error('this is the red error');
    • By VeiJari
      Hello forum!
      I've yet again stumbled on a head-scratching situation. We have enabled the option on our articles template and events template that it skips the title adding part and goes straight to the form. This is what our customer wants. So when you add a new article or event it automatically names it temporary to "article-0000000" and same with event. Now the problem is that obviously after saving the form we want to change to page url or "name" to the title, like it's normally. 
      Now here's the code for the hook:
      wire()->addHookBefore("Pages::saved(template=tapahtuma|artikkeli)", function($hook) {
        $page = $hook->arguments(0);
        $newUrl = wire()->sanitizer->pageName($page->title); // give it a name used in the url for the page
        wire()->log->message($page->name);
        $page->setAndSave('name', $newUrl);
      });
      I get the correct page and the name and path changes when I log them, but when I try to save it. It just loads and then I get: 
      Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) This happens in sanitizer.php
      and then another error: Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 262144 bytes) in Unknown on line 0
       
      What is happening? Am I not suppose to use sanitizer in this way? When we made a temporary page object in out other hook, the sanitizer worked perfectly.
      Thanks for the help!
    • By VeiJari
      Hello, our customer doesn't like the fact that they have to first add a title and then add the other info in the form. Therefore, we've enabled the way to skip this by making a temporary file before saving.
      But the problem is that by using module schedulepages in this piece of code in the module:  
      if (!$page->isNew() && $page->publishable() && $page->isChanged('status') && $page->is(Page::statusUnpublished) && $page->publish_from) {
                  $this->session->error($this->_("“Publish From Date” field was cleared to prevent the page from being unintentionally re-published on the next Lazy Cron run."));
                  $page->publish_from = null;
                  $page->save('publish_from');
              }
      it fires even with the temporary file and therefore after first save it resets the publish_from field. Is there a way to check if template is temporary, or something along those lines?
      One solution is just to ask the customer to first save the form after adding a title, but I don't see that as a good solution.
×
×
  • Create New...