dragan Posted January 2, 2018 Share Posted January 2, 2018 (edited) 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 January 3, 2018 by dragan clarify "classic" vs. "default" theme... 1 Link to comment Share on other sites More sharing options...
mr-fan Posted January 3, 2018 Share Posted January 3, 2018 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 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 6 1 Link to comment Share on other sites More sharing options...
horst Posted February 2, 2018 Share Posted February 2, 2018 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? 2 Link to comment Share on other sites More sharing options...
mr-fan Posted February 3, 2018 Share Posted February 3, 2018 (edited) 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 February 3, 2018 by mr-fan missing n in fancy...;) 3 1 Link to comment Share on other sites More sharing options...
Sebastian Posted February 15, 2018 Share Posted February 15, 2018 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.. 1 Link to comment Share on other sites More sharing options...
bernhard Posted May 14, 2018 Author Share Posted May 14, 2018 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 More sharing options...
Robin S Posted May 14, 2018 Share Posted May 14, 2018 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. 2 Link to comment Share on other sites More sharing options...
elabx Posted May 15, 2018 Share Posted May 15, 2018 4 hours ago, Robin S said: You can't add a Field object to a form, but rather an Inputfield object. Field::getInputfield() would come in handy here. I've done it getting the actual Inputfield module, not the Fieldtype, I'd be very curious to see if the getInputfield method works! Get back to us on what works @bernhard :D 1 Link to comment Share on other sites More sharing options...
flydev Posted May 15, 2018 Share Posted May 15, 2018 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); 1 Link to comment Share on other sites More sharing options...
bernhard Posted May 15, 2018 Author Share Posted May 15, 2018 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())); 2 Link to comment Share on other sites More sharing options...
monollonom Posted May 31, 2018 Share Posted May 31, 2018 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)); 1 Link to comment Share on other sites More sharing options...
bernhard Posted May 31, 2018 Author Share Posted May 31, 2018 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 More sharing options...
monollonom Posted May 31, 2018 Share Posted May 31, 2018 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 1 Link to comment Share on other sites More sharing options...
Soma Posted July 9, 2018 Share Posted July 9, 2018 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? 6 Link to comment Share on other sites More sharing options...
bernhard Posted October 19, 2018 Author Share Posted October 19, 2018 If anybody might wonder. This is how to setup permissions for a sub-page of a processmodule: 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: Whereas in the menu it is still there until you logout+login: @szabesz you asked for that in the blog comments... 6 1 Link to comment Share on other sites More sharing options...
ocr_b Posted February 14, 2019 Share Posted February 14, 2019 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 More sharing options...
bernhard Posted February 14, 2019 Author Share Posted February 14, 2019 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 More sharing options...
ocr_b Posted February 14, 2019 Share Posted February 14, 2019 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 More sharing options...
bernhard Posted February 14, 2019 Author Share Posted February 14, 2019 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 More sharing options...
ocr_b Posted February 14, 2019 Share Posted February 14, 2019 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. 1 Link to comment Share on other sites More sharing options...
bernhard Posted May 7, 2019 Author Share Posted May 7, 2019 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: When you upload an invalid file it will show a warning: 5 Link to comment Share on other sites More sharing options...
Cybermano Posted April 1, 2021 Share Posted April 1, 2021 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 More sharing options...
bernhard Posted April 1, 2021 Author Share Posted April 1, 2021 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 More sharing options...
Cybermano Posted April 1, 2021 Share Posted April 1, 2021 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. . Link to comment Share on other sites More sharing options...
bernhard Posted April 1, 2021 Author Share Posted April 1, 2021 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 1 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now