Jump to content

How to create custom admin pages (aka ProcessModules) - yes, it's that simple!


bernhard

Recommended Posts

OK, I think I'm really confused right now.

For some reason I was working with the default  classic or Reno admin theme. h1 isn't used (or shown) there. With the latest UIKit admin theme, everything's OK.

Edited by dragan
clarify "classic" vs. "default" theme...
  • Like 1
Link to comment
Share on other sites

Bernhard i wrote it and i repeat it you are really my hero - the admin/backend was always a ? to me since i was flashed about all the options on the frontendside with the API, creating some little modules or changing some or using small hooks to get some results are the level i was before i read your tutorials and examples...:)

Wanna share a really cool example im playing with this responsive calendar plugin:

https://tympanus.net/Development/Calendario/

in an easy process module i get some results like this (with some CSS tweaks):

Spoiler

calendario.thumb.gif.d14d555ae3735417835bafb045664d56.gif

This is with the example data from but it works like a charm and is seemless responsive within the PW admin.

Code i used on the PW side is really easy:

Spoiler

 


  public function ___execute() {
    $out = '';
    //load css and js files
    $this->config->styles->add($this->config->urls->siteModules . $this->className() . '/calendario/calendar.css');   
    $this->config->styles->add($this->config->urls->siteModules . $this->className() . '/calendario/custom_1.css');      
    $this->config->scripts->add($this->config->urls->siteModules . $this->className() . '/calendario/jquery.calendario.js');
    $this->config->scripts->add($this->config->urls->siteModules . $this->className() . '/calendario/data.js');   
    $this->config->scripts->add($this->config->urls->siteModules . $this->className() . '/calendario/script.js'); 
    //container for the calendar
    $out  = '<div class="container uk-card-default"><div class="custom-calendar-wrap custom-calendar-full">';
    //render navigation buttons
    $out .= '<div class="custom-header clearfix">
                    <h2>
                        <span id="custom-month" class="custom-month"></span>
						<span id="custom-year" class="custom-year"></span>
                    </h2>
                    <nav>
                        <span id="custom-prev" class="custom-prev"></span>
                        <span id="custom-next" class="custom-next"></span>
                        <span id="custom-current" class="custom-current" title="Got to current date"></span>
                    </nav>
				</div>';
    //render calender
    $out .= '<div id="calendar" class="fc-calendar-container"></div>';
    //end container
    $out .= '</div></div>';
    $out .= '<hr class="uk-divider-icon">';
    return $out;
  }

now i'm getting hands dirty on trigger some booking data and some actionbuttons...

Thank you for sharing your knowlegde!

I wish you a healthy and successfull year - best regards mr-fan

  • Like 6
  • Thanks 1
Link to comment
Share on other sites

  • 5 weeks later...
On 3.1.2018 at 11:59 PM, mr-fan said:

in an easy process module i get some results like this (with some CSS tweaks):

Hi @mr-fan, thanks for sharing this calendario demo. Would you mind to post the css tweaks you show in the example?

  • Like 2
Link to comment
Share on other sites

nothing fancy i just drop the extrem use of shadows and converted colors....so it fits a little bit normal in the PW:

calendar.css

Spoiler

.fc-calendar-container {
	position: relative;
	height: 400px;
	width: 400px;
}

.fc-calendar {
	width: 100%;
	height: 100%;
}

.fc-calendar .fc-head {
	height: 30px;
	line-height: 30px;
	background: #ccc;
	color: #fff;
}

.fc-calendar .fc-body {
	position: relative;
	width: 100%;
	height: 100%;
	height: -moz-calc(100% - 30px);
	height: -webkit-calc(100% - 30px);
	height: calc(100% - 30px);
	border: 1px solid #ddd;
}

.fc-calendar .fc-row {
	width: 100%;
	border-bottom: 1px solid #ddd;
}

.fc-four-rows .fc-row  {
	height: 25%;
}

.fc-five-rows .fc-row  {
	height: 20%;
}

.fc-six-rows .fc-row {
	height: 16.66%;
	height: -moz-calc(100%/6);
	height: -webkit-calc(100%/6);
	height: calc(100%/6);
}

.fc-calendar .fc-row > div,
.fc-calendar .fc-head > div {
	float: left;
	height: 100%;
	width:  14.28%; /* 100% / 7 */
	width: -moz-calc(100%/7);
	width: -webkit-calc(100%/7);
	width: calc(100%/7);
	position: relative;
}

/* IE 9 is rounding up the calc it seems */
.ie9 .fc-calendar .fc-row > div,
.ie9 .fc-calendar .fc-head > div {
	width:  14.2%;
}

.fc-calendar .fc-row > div {
	border-right: 1px solid #ddd;
	padding: 4px;
	overflow: hidden;
	position: relative;
}

.fc-calendar .fc-head > div {
	text-align: center;
}

.fc-calendar .fc-row > div > span.fc-date {
	position: absolute;
	width: 30px;
	height: 20px;
	font-size: 20px;
	line-height: 20px;
	font-weight: 700;
	color: #ddd;
	text-shadow: 0 -1px 0 rgba(255,255,255,0.8);
	bottom: 5px;
	right: 5px;
	text-align: right;
}

.fc-calendar .fc-row > div > span.fc-weekday {
	padding-left: 5px;
	display: none;
}

.fc-calendar .fc-row > div.fc-today {
	background: #fff4c3;
}

.fc-calendar .fc-row > div.fc-out {
	opacity: 0.6;
}

.fc-calendar .fc-row > div:last-child,
.fc-calendar .fc-head > div:last-child {
	border-right: none;
}

.fc-calendar .fc-row:last-child {
	border-bottom: none;
}

 

custom_1.css

Spoiler

/*dirty hack since i don't figure out why this responsive calendar overwrap the footer...#todo
#pw-footer {display:none!important;}

.container {
	width: 100%;
	height: 600px;
	position: relative;
}

.container > header,
.main {
	padding: 0 30px 50px 30px;
	width: 100%;
	max-width: 600px;
	margin: 0 auto;
}

.container > header {
	padding: 30px;
}

.container > header h1 {
	font-size: 34px;
	line-height: 38px;
	margin: 0;
	font-weight: 700;
	color: #fff;
	float: left;
/*	text-shadow: 0 1px 1px rgba(0,0,0,0.3);*/
}

.container > header h1 span {
	font-size: 18px;
	font-weight: 300;
	display: block;
}


/* Demo Buttons Style */
.codrops-demos {
	float: right;
}

.codrops-demos a {
    display: inline-block;
    margin: 10px;
    color: #fff;
    font-weight: 700;
    line-height: 30px;
    border-bottom: 4px solid transparent;
}

.codrops-demos a:hover {
	color: #000;
	border-color: #000;
}

.codrops-demos a.current-demo,
.codrops-demos a.current-demo:hover {
	color: rgba(0,0,0,0.5);
	border-color: rgba(0,0,0,0.5);
}

.custom-calendar-full {
	position: absolute;
	top: 24px;
	bottom: 0px;
	left: 0px;
	width: 100%;
	height: auto;
}

.fc-calendar-container {
	height: auto;
	bottom: 0px;
	width: 100%;
	top: 50px;
	position: absolute;
}

.custom-header {
	padding: 20px 20px 10px 30px;
	height: 50px;
	position: relative;
}

.custom-header h2,
.custom-header h3 {
	float: left;
	font-weight: 300;
	text-transform: uppercase;
	letter-spacing: 4px;
/*	text-shadow: 1px 1px 0 rgba(0,0,0,0.1);*/
}

.custom-header h2 {
	color: rgba(0,0,0,0.9);
	width: 60%;
}


.custom-header nav {
	position: absolute;
	right: 20px;
	top: 20px;
	-webkit-touch-callout: none;
	-webkit-user-select: none;
	-khtml-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
}

.custom-header nav span {
	float: left;
	width: 30px;
	height: 30px;
	position: relative;
	color: transparent;
	cursor: pointer;
	background: rgba(0,0,0,0.3);
	margin: 0 1px;
	font-size: 20px;
	/*border-radius: 0 3px 3px 0;*/
/*	box-shadow: inset 0 1px rgba(0,0,0,0.2);*/
}

/*.custom-header nav span:first-child {
	border-radius: 3px 0 0 3px;
}*/

.custom-header nav span:hover {
	background: rgba(0,0,0,0.5);
}

.custom-header span:before {
	font-family: 'fontawesome-selected';
	color: #fff;
	display: inline-block;
	text-align: center;
	width: 100%;
	text-indent: 4px;
}

.custom-header nav span.custom-prev:before {
	content: '\25c2';
}

.custom-header nav span.custom-next:before {
	content: '\25b8';
}

.custom-header nav span:last-child {
	margin-left: 20px;
	border-radius: 3px;
}

.custom-header nav span.custom-current:before {
	content: '\27a6';
}


.fc-calendar {
	background: rgba(0,0,0,0.1);
	width: auto;
	top: 10px;
	bottom: 20px;
	left: 20px;
	right: 20px;
	height: auto;
/*	border-radius: 20px;*/
	position: absolute;
}

.fc-calendar .fc-head { 
	background: #1c2836;
	color: #fff;
	/*box-shadow: inset 0 1px 0 rgba(0,0,0,0.2);*/
/*	border-radius: 20px 20px 0 0;*/
	height: 40px;
	line-height: 40px;
	padding: 0 20px;
}

.fc-calendar .fc-head > div {
	font-weight: 300;
	text-transform: uppercase;
	font-size: 14px;
	letter-spacing: 3px;
/*	text-shadow: 0 1px 1px rgba(0,0,0,0.4);*/
}

.fc-calendar .fc-row > div > span.fc-date {
	color: rgba(0,0,0,0.9);
	text-shadow: none;
	font-size: 26px;
	font-weight: 300;
	bottom: auto;
	right: auto;
	top: 10px;
	left: 10px;
	text-align: left;
/*	text-shadow: 0 1px 1px rgba(0,0,0,0.3);*/
}

.fc-calendar .fc-body {
	border: none;
	padding: 20px;
}

.fc-calendar .fc-row {
	box-shadow: inset 0 -1px 0 rgba(0,0,0,0.2);
	border: none;
}

.fc-calendar .fc-row:last-child {
	box-shadow: none;
}

.fc-calendar .fc-row:first-child > div:first-child {
	border-radius: 10px 0 0 0;
}

.fc-calendar .fc-row:first-child > div:last-child {
	border-radius: 0 10px 0 0;
}

.fc-calendar .fc-row:last-child > div:first-child {
	border-radius: 0 0 0 10px;
}

.fc-calendar .fc-row:last-child > div:last-child {
	border-radius: 0 0 10px 0;
}

.fc-calendar .fc-row > div {
	box-shadow: -1px 0 0 rgba(0, 0, 0, 0.2);
	border: none;
	padding: 10px;
	cursor: pointer;
}

.fc-calendar .fc-row > div:first-child{
	box-shadow: none;
}

.fc-calendar .fc-row > div.fc-today {
	background: transparent;
	box-shadow: inset 0 0 100px rgba(0,0,0,0.1);
}

.fc-calendar .fc-row > div.fc-today:after { 
	content: ''; 
	display: block;
	position: absolute;
	top: 0; 
	left: 0;
	width: 100%;
	height: 100%;
	opacity: 0.2;
	background: transparent;
}

.fc-calendar .fc-row > div > div {
	margin-top: 35px;
}

.fc-calendar .fc-row > div > div a,
.fc-calendar .fc-row > div > div span {
	color: rgba(0,0,0,0.7);
	font-size: 12px;
	text-transform: uppercase;
	display: inline-block;
	padding: 3px 5px;
	border-radius: 3px;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 100%;
	margin-bottom: 1px;
	background: rgba(0,0,0,0.1);
}

.no-touch .fc-calendar .fc-row > div > div a:hover {
	background: rgba(0,0,0,0.3);
}

@media screen and (max-width: 880px) , screen and (max-height: 450px) {
	html, body, .container {
		height: auto;
	}

	.custom-header,
	.custom-header nav,
	.custom-calendar-full,
	.fc-calendar-container, 
	.fc-calendar,
	.fc-calendar .fc-head,
	.fc-calendar .fc-row > div > span.fc-date {
		position: relative;
		top: auto;
		left: auto;
		bottom: auto;
		right: auto;
		height: auto;
		width: auto;
        padding-bottom: 1.2em;
	}

	.fc-calendar {
		margin: 0 20px 20px;
	}

	.custom-header h2,
	.custom-header h3 {
		float: none;
		width: auto;
		text-align: left;
		padding-right: 100px;
	}

	.fc-calendar .fc-row,
	.ie9 .fc-calendar .fc-row > div,
	.fc-calendar .fc-row > div {
		height: auto;
		width: 100%;
		border: none;
	}

	.fc-calendar .fc-row > div {
		float: none;
		min-height: 50px;
		box-shadow: inset 0 -1px rgba(0,0,0,0.2) !important;
		border-radius: 0px !important;
	}

	.fc-calendar .fc-row > div:empty{
		min-height: 0;
		height: 0;
		box-shadow: none !important;
		padding: 0;
	}

	.fc-calendar .fc-row {
		box-shadow: none;
	}

	.fc-calendar .fc-head {
		display: none;
	}

	.fc-calendar .fc-row > div > div {
		margin-top: 0px;
		padding-left: 10px;
		max-width: 70%;
		display: inline-block;
	}

	.fc-calendar .fc-row > div.fc-today {
		background: rgba(255, 255, 255, 0.2);
	}

	.fc-calendar .fc-row > div.fc-today:after { 
		display: none;
	}

	.fc-calendar .fc-row > div > span.fc-date {
		width: 30px;
		display: inline-block;
		text-align: right;
	}

	.fc-calendar .fc-row > div > span.fc-weekday {
		display: inline-block;
		width: 40px;
		color: #fff;
		color: rgba(0,0,0,0.7);
		font-size: 10px;
		text-transform: uppercase;
	}
}

 

but notice it is just a proof of concept - not browsertested - just some fast playing.

Best regard mr-fan

Edited by mr-fan
missing n in fancy...;)
  • Like 3
  • Thanks 1
Link to comment
Share on other sites

  • 2 weeks later...

Thanks bernhard for a very informative post.

I do have a follow-up question though: How would i make the new panel / custom admin page accessible to a non-admin role? Currently it works great as superuser / admin using another role, logged in as a non-admin user there's only the "pages" tab, nothing else.
The way I understand it, since it uses the "admin" template, I can't really manage access through template settings (?).

I might overthink this though, anyone got an idea?
 

Edit: Actually, i'm sorry, this is a duplicate of

Which of course i found 2 minutes after posting the question, but not during the hour of research before..

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

Does anybody know how I can add existing fields to a form?

$form = $this->modules->get('InputfieldForm');
$form->add($this->fields->get('myfieldname'));

Whatever I try I get an error

Quote

Method Field::setParent does not exist or is not callable in this context

 

Link to comment
Share on other sites

4 hours ago, bernhard said:

Does anybody know how I can add existing fields to a form?


$form = $this->modules->get('InputfieldForm');
$form->add($this->fields->get('myfieldname'));

You can't add a Field object to a form, but rather an Inputfield object. Field::getInputfield() would come in handy here.

  • Like 2
Link to comment
Share on other sites

It would be easy to give an example using a Page object, but from a module I am curious how to achieve it. Another idea could be creating a page/template with all the fields which will be potentially injected in a form then

$test = $fields->get('test_field');
$field = $test->getInputfield($injectPage); // $injectPage is a Page object
$form->add($field);

 

  • Like 1
Link to comment
Share on other sites

thx @Robin S @elabx @flydev

This works:

$p = new Page();
$p->template = 'project';
$field = $this->fields->get('title');
$form->add($field->getInputfield($p));

I've had an error in my field's config (requesting dynamic data, getting a page via $this->wire->process->getPage(); and that threw an error. I thought I was doing something wrong but with the title field it worked ?

Thank you!

Edit: This also works:

$form->add($this->fields->get('recipients')->getInputfield(new NullPage()));
  • Like 2
Link to comment
Share on other sites

  • 3 weeks later...

Not a question but just a thank you @bernhard for your great tutorial.

I was already amazed by Processwire's capabilities (at my beginner level) but this new tool is coming very handy for a current project I have but also future ones.

Actually I may have a question: is there a way to execute a function before you exit a page ? I have a cancel button which brings me back to the previous screen, along with a notification warning that the adding process has been canceled, but would it be possible to do the same when clicking in a tree link for example ?

Thanks !

Edit: I'm using this for the inputfield, works as well: 

$form->add($this->fields->get('field')->getInputfield($this->page));

 

  • Like 1
Link to comment
Share on other sites

glad it helped you @monollonom

not sure what would be the best approach here - I think it depends on your situation. PW uses the class "InputfieldStateChanged" for inputfields that where changed. You could either check on the client side (by intercepting click events) or if you want to do some checks on the server you could use sessions (set a session flag on beginning of the creation and check for that flag on every page other than your process module). 1000 possibilities here...

Link to comment
Share on other sites

25 minutes ago, bernhard said:

not sure what would be the best approach here - I think it depends on your situation. PW uses the class "InputfieldStateChanged" for inputfields that where changed. You could either check on the client side (by intercepting click events) or if you want to do some checks on the server you could use sessions (set a session flag on beginning of the creation and check for that flag on every page other than your process module). 1000 possibilities here...

I just need a notification to be shown telling the current action has been cancelled, a very small detail... I will find something ?

Thank you

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

It's all there.

When I was starting with PW years ago, I very soon looked at modules and all the Process modules that make the admin.

So I never really needed a tutorial, just look and learn copy and try. It was so simple and since it's all in the same fashion using the already familiar API and "normal" modules. So you have a ton of modules Ryan has already built that make ProcessWIre backend you can use as examples/learning. Isn't it nice?

  • Like 6
Link to comment
Share on other sites

  • 3 months later...

If anybody might wonder. This is how to setup permissions for a sub-page of a processmodule:

lfcU441.png

As easy as adding the permission to the nav item! If you want the permission to be created/deleted on module install/uninstall you also have to add it in the "permissions" array:


$info = [
  'title' => 'ProcessProjects',
  'summary' => 'ProcessModule to manage all Projects',
  'version' => 1,
  'author' => 'Bernhard Baumrock, baumrock.com',
  'icon' => 'thumbs-up',
  'permission' => 'projects',
  'permissions' => [
    'projects' => 'Run the Projects Management Module',
    'aggregate' => 'Create Aggregated Reports',
  ],

  'page' => [
    'name' => 'projects',
    'title' => __('Projekte'),
  ],
  'nav' => [
    [
      'url' => '', 
      'label' => __('Projekte'),
    ],[
      'url' => 'mails',
      'label' => __('E-Mails verwalten'),
    ],[
      'url' => 'reports',
      'label' => __('Berichte verwalten'),
    ],[
      'url' => 'aggregate',
      'label' => __('Aggregierten Bericht erstellen'),
      'permission' => 'aggregate',
    ],
  ],
];

Make sure to logout/login, otherwise you won't see the changes in the menu! If you call the ProcessModule's page directly you will instantly get the result of the changed permissions:

xW8G8eA.png

Whereas in the menu it is still there until you logout+login:

qNsWp2K.png

 

@szabesz you asked for that in the blog comments...

  • Like 6
  • Thanks 1
Link to comment
Share on other sites

  • 3 months later...

Hi,

which rights does a user role needs to have to access those pages?


i have multiple user-roles, which can login to the backend and should work with custom pages.
but if they try to access such a custom page, they get a 404 error, because they dont have access to it — it works if they have the superuser role though.
but i dont want them to have superuser rights.

did i overlook someting?

Link to comment
Share on other sites

11 minutes ago, ocr_b said:

which rights does a user role needs to have to access those pages?

They need to have the permission of the module and the permission of the page. If you don't assign any permissions the pages should be visible for all users.

Link to comment
Share on other sites

3 minutes ago, bernhard said:

They need to have the permission of the module and the permission of the page. If you don't assign any permissions the pages should be visible for all users.

thats what i thought, but it didnt work.
i had several custom pages and all been not accessible.
then i added a permission to one(!) of them and reinstalled the module to make pw install the permission.
i don't fully understand what happend, but now every page is in default visible like you said, even if i remove the permission again.
(solved)
 

Link to comment
Share on other sites

On 10/19/2018 at 4:17 PM, bernhard said:

Make sure to logout/login, otherwise you won't see the changes in the menu! If you call the ProcessModule's page directly you will instantly get the result of the changed permissions:

Did you do that?

Link to comment
Share on other sites

20 minutes ago, bernhard said:

Did you do that?

yes.

to clear it up:
for me it works only if the custom page module gets a dedicated permission, otherwise if the user has no superuser role, he wont have access.
but with the permission it works good.
you want to reinstall the module to install a permission and you want to refresh the modules cache after you add a permission to another module.
it does not install a permission with a simple module cache refresh.

thanks for the quick replies bernhard, much appreciate.

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

Here a short tutorial of how to use file fields in ProcessModules because I needed that today. File fields are somewhat special as they are usually connected to an existing page with an existing ID and folder in /site/assets/files. But it is also possible to use the Inputfield alone to provide a GUI for uploading files (just like it is done in ProcessDatabaseBackups or the Modules GUI):

  /**
   * Import CSV file
   *
   * @return string
   */
  public function executeImportcsv() {
    $form = $this->modules->get('InputfieldForm'); /** @var InputfieldForm $form */
    $tmpDir = $this->files->tempDir('upload_csv'); // get path of temp directory

    $f = $this->modules->get('InputfieldFile');
    $f->extensions = 'csv';
    $f->maxFiles = 1; 
    $f->descriptionRows = 0;
    $f->attr('id+name', 'upload_csv');
    $f->label = 'Upload CSV';
    $f->icon = 'download';
    $f->destinationPath = $tmpDir; // here we set a custom destination path
    $form->add($f);

    $form->add([
      'type' => 'submit',
      'name' => 'submit',
      'icon' => 'download',
      'value' => 'Import',
    ]);

    if($this->input->post->submit) {
      $form->processInput($this->input->post);

      // if there where any errors we exit early and render the form
      // we also exit here if no files where uploaded
      if($form->getErrors() OR !count($f->value)) return $form->render();

      // loop all uploaded files
      // here we have only one file maximum, but still it is an array
      foreach($f->value as $pagefile) {
        // the regular pagefile's filename is incorrect because it links to
        // /site/assets/files and not the tempDir, so we get the correct path
        $file = $tmpDir . $pagefile->basename;
        
        // #######################################
        // now do whatever you want with that file
        // #######################################

      }

      // redirect to somewhere
      $this->session->redirect("/admin/somewhere/");
    }

    // form was not submitted, so render it
    return $form->render();
  }

The result:

yQq0XaQ.png

When you upload an invalid file it will show a warning:

lvsCHc7.png

  • Like 5
Link to comment
Share on other sites

  • 1 year later...

Hi,

I'm setting a custom page via this module ( many thanls to @bernhard for this,  to @Soma @bernhard @adrian for their posts, and to all others that raise my level every day ).

Awesome stuff!

My scenario is to prepare a sort of little vademecum for safe-editors: a simply list of suggestion that they could open while edidting a page, without exit their page and read the custom admin one added via this module, accessible with a specific permission.

I've setted all and it works perfectly, but I'm looking for how dispaly this page in panel mode: I suppose (surely I'm wrong... ? ) to have to set a class "pw-panel" in the html link.

Well, I've serched along the forum and on the web but I've not find anything useful.

I've also tryed (pure guess) to set "link" and "attrs" into the declaration of the page array (at the beginnin of the module), like this way (but it doesn't work):

'page' => [
	'name' => 'allergens', // You guessed! It's the list of the food allergens for a restaurant site :)
	'parent' => 'page', 
	'title' => 'Allergen List',

	// Could this be correct?
	// 'link' => $config->urls->admin . "page/allergens/",
	// 'attrs' => 'class="pw-panel" '
],

Anybody has a suggestion how set panel mode behaviour?

Many thanks in advance

 

 

 

Link to comment
Share on other sites

1 hour ago, Cybermano said:

I've setted all and it works perfectly, but I'm looking for how dispaly this page in panel mode: I suppose (surely I'm wrong... ? ) to have to set a class "pw-panel" in the html link.

I don't get your question. Where do you want to display your link that should open in a panel? Is it a menu item? Or is it a link in a processModule?

Link to comment
Share on other sites

6 hours ago, bernhard said:

I don't get your question. Where do you want to display your link that should open in a panel? Is it a menu item? Or is it a link in a processModule?

Hi and thanks,

From the admin menu (img 01), selecting the relative list-item...

Currently it opens as a normal page, but I'm trying to obtain as img 02.

.

01.jpg

02.jpg

Link to comment
Share on other sites

Interesting idea. I've had a look. There is no way of doing this via PHP, but it's quite simple to do via JS:

$(document).ready(function() {
  pwPanels.addPanel($("#pw-masthead a[href=/your/admin/link/url]"));
});

Note that this will not work for json loaded links. You'd need to intercept the click event and init the panel on the fly, see https://github.com/processwire/processwire-requests/issues/176

  • Like 1
Link to comment
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.
×
×
  • Create New...