Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation since 03/02/2021 in all areas

  1. 28 points
    ProcessWire 3.0.173 adds several new requested features and this post focuses on one of my favorites: the ability to hook into and handle ProcessWire URLs, independent of pages— https://processwire.com/blog/posts/pw-3.0.173/
  2. 13 points
    Long time no module 🙂 Github: https://github.com/baumrock/RockIframe Modules Directory: https://processwire.com/modules/rock-iframe/ Problem Sometimes the editor wants to see a preview of a file while editing a page. For example this can be helpful when digitizing invoices. Regular ProcessWire panels can be used for preview but overlap the page editor so the user has to open and close the panel repeatedly while working. RockIframe shows a preview of any content that can be rendered in an iframe and keeps the page editor 100% visible to the user. Usage Simply call $modules->get('RockIframe')->show('path/to/your/file.pdf') anywhere in the admin to show your data in a sidebar iframe on page load. You can also define a Pagefile(s) object as source of the iframe. Example This example shows the first file of the field myfilesfield on the page edit screen of pages having template mypagetemplate: $wire->addHookAfter("ProcessPageEdit::buildForm", function($event) { $page = $event->process->getPage(); if($page->template !== 'mypagetemplate') return; /** @var RockIframe $iframe */ $iframe = $this->wire->modules->get('RockIframe'); if($iframe) $iframe->show($page->get('myfilesfield')); }); Ideas & Notes PDF preview relies on the browser's capability to preview PDFs. Also at the moment there are no checks which content is thrown to the iframe as source. For images for example we could use https://leafletjs.com/examples/crs-simple/crs-simple.html to enable panning & zooming on the image. For better browsersupport of PDF we could use https://mozilla.github.io/pdf.js/web/viewer.html;
  3. 12 points
    RockCalculator Add a calculator to any Inputfield in the ProcessWire backend. Setup At the moment there is no UI for defining fields that should support the calculator. You have multiple options: Tracy Console // show rockcalculator and round result to .00 $field = $fields->get('yourfieldname'); $field->set('rockcalculator', 2); // 2 digit precision $field->save(); RockMigrations $rm->setFieldData('yourfield', ['rockcalculator' => 2]); Hook buildForm $wire->addHookAfter("ProcessPageEdit::buildForm", function($event) { $form = $event->return; $page = $event->process->getPage(); // edited page if($page->template !== 'yourpagetemplate') return; if($f = $form->get('yourfield1')) $f->rockcalculator = 2; if($f = $form->get('yourfield2')) $f->rockcalculator = 2; if($f = $form->get('yourfield3')) $f->rockcalculator = 2; }); Github: https://github.com/baumrock/RockCalculator Modules directory: https://processwire.com/modules/rock-calculator/
  4. 10 points
    I'm getting a look at this thread linked from the PW Weekly, where it looks like this topic has been discussed. BitPoet has an example where named arguments are in the format like "{user}" (if I understood it correctly) and I really like that. It would provide for an option to have named arguments without having to specify what would be in it... just "any valid PW page name characters". That sounds useful. I'll add support for it, in addition to the named arguments support mentioned in the blog post. We can support that without interfering with other regular expression features by having it convert that to a PCRE capture group.
  5. 8 points
    Another showcase 😎 $this->addHook('/rockgrid2/(.*)', function($event) { $name = trim($event->arguments(1),"/"); $grid = $this->getGrid($name); if(!$grid) throw new Wire404Exception("Grid not found"); if($event->config->ajax) $grid->json(); // send json and die() return $grid->debug(); }); When requested via AJAX you get gzipped json (as source for a tabulator grid), when requested in the browser you get a sortable+filterable grid of your data 🙂 And tracy is always waiting for bd() calls for quick and easy debugging 🙂
  6. 8 points
    Herzzentrum Bonn The Herzzentrum Bonn (Heart Center Bonn) is part of the University Hospital Bonn (UKB) and consists of the cardiology and heart chirurgy clinics. Goals for the website were a clear content structure, friendly and personal communication tailored to the different visitor groups and easier maintenance and content updates. Concept, design and implementation by schwarzdesign. www.herzzentrum-bonn.de You can read more about the challenges, concept and implementation of this project in the case study on our website (German only). Notable modules used ProFields (especially RepeaterMatrix) FormBuilder ProCache UniqueImageVariations WireMailSmtp ProcessCacheControl ProcessRedirects TracyDebugger Development insights One thing to note is the central management of staff members. To represent staff members as well as the hiarchies and different departments, we created a layered template structure: person – Represents a single person and has fields for name, title, position, portrait, contact info etc. hierarchy – Represents a hiarchy level or department in the organisational structure. Has person pages as children. people – This is a singleton template which displays all team members sorted by hierarchy level. Has hierarchy pages as children. The central staff database is also used to display people throughout the site – for example, the experts on angiology on the angiology page. Most of the editorial content is created through a RepeaterMatrix field with different section types. The section type for people contains a simple page reference field, allowing the editor to select the people to display in a given context while still being able to edit their contact info in a central place. Another interesting content type are the track records on the homepage. Those are implemented using CountUp.js and a custom IntersectionObserver script to trigger the animation as soon as the section comes into view. Finally, we built a very flexible grid section which uses CSS grid to display grids with variable contents and row/column spans (see an example here). Screenshots
  7. 7 points
    @Robin S That's correct, an existing page has precedence. I'd like to make it so you can optionally override that too, but still working to identify the most efficient way. I'm trying to avoid a solution that adds the overhead of checking hooks and regular expressions for every request before identifying the page. Currently it only does that if the request doesn't end up matching a page, which adds no overhead to page rendering requests. Once the technical details are worked out, likely the solution will involve using an $wire->addHookBefore('/path/', ...) (rather than just addHook) which will receive a matched page (if there is one) and then it can decide whether to let it proceed as-is, change the Page object, or do something else. That's also a technical still to work out. We could easily enforce trailing vs non-trailing slash but didn't want to do it without someone dictating that's what they want. Otherwise someone could very easily end up having every request getting 301'd without realizing it. So seemed safest just to allow either for the moment. I do plan to add a way to let you dictate what's required so that it can perform necessary redirects for you. I'm currently thinking that if the match pattern ends with a slash, it'll enforce the slash; if it doesn't, it'll enforce no-slash; and if it ends with a /? it'll allow for either. @eelkenet It doesn't at present since the feature was just added, but I do think it will be possible for ProCache to cache them. I've already started looking into it here. @StanLindsey The entire matched URL is always in $event->arguments(0). If it would be helpful I can also add a named argument to it, like 'url' or something, so you could do $event->arguments('url'); or just $event->url
  8. 6 points
    Hi everyone, I'm not an expert, but I regularly make small to medium-sized pages either in static html or for wordpress (_s). I now use Processwire instead of creating static pages, because I can then maintain the pages more easily and I am also faster. I created a site profile with, which is the basis for my work. Since I like to work with sass, I use the sassify module and have created a small boiler for it. html-bones is my html base. And for this I have built in the osano cookie consent. Maybe someone can still use something of it, or there are suggestions for improvement. In fact, i am certainly not using every possibilities of processwire in the best effectivly way, but this basis is sufficient for the type of internet pages that i usually create. I usually don't accept more complex jobs at all, so as not to overwhelm myself. You can find the link to this profile here: https://github.com/eversthomas/processwire/tree/master/site-html5blank-sass And an example: https://processwire.end-linkage.de/ Greetz from germany Tom.
  9. 6 points
    What we need is really easy ways to contribute and build motivation towards contributions. - slack/discord for real-time Comms - merging PRs in the Processwire repo instead of the weird closing the PR and copy pasting it in. I raised a PR years ago, got no feedback and was told it may have fine in via copy and paste. I closed the PR myself to reduce noise and haven't entertained the idea of contributing since. Unless this has changed, start treating it like a modern open source project - roadmap on the GitHub board - indentified maintainers on the GitHub repo - a backlog / causal kanban style agile setup - monthly maintainer video calls to go over backlog etc. Maybe anyone can join. Maybe video recorded. - a patron or way to donate to the PW project. This pot can be used to pay maintainers to work on critical features etc. E.g. through donations with have enough money to fund 1 week of work, what should Ryan / another maintainer take time off their normal work to work on. - key goals established for the core maintainer team. We should as a whole be marching the same direction. (Though obviously people can work on what they want, but has to pass review) - a plan that @ryan is comfortable with to slowly distribute the reigns. We don't want to move too fast and over burden the core. - some sort of backlog voting system, instead of posts on the forum. - Id really like to modernise the process around Processwire. That scares anybody larger than a sole freelancer away from the project. Bigger users mean more support. More pro modules being sold etc. - on that note. Can we have the shop not just be Ryan's modules but extended to other pais for modules. I don't like that they are strewn across the web. (And Ryan charge a fee.)
  10. 5 points
    Just released a new version (1.11.0) of Changelog. This version includes a schema update that requires action from a superuser, so the module will display a message when a superuser logs in until the update is executed. Technically the schema update could be handled just like the earlier schema update, behind the scenes, but since it involves adding new indexes... well, if the custom database table (process_changelog) has a lot of data, this can take quite a while, and might even result in a timeout. As such, I think it's best to let a superuser handle it. Just in case README.md contains a simple PHP script that can be used instead if the update refuses to go through in the browser 🙂 --- I recently migrated some sites to a new server, and ran into a serious performance issue right off the bat. After the usual process of blaming everything from the host to the MySQL version and even most recent ProcessWire updates for the problem, I almost accidentally noticed that queries for the process_changelog table were taking a very long time. Turns out there were ~2.5 million rows stored for the page in question, and no indexes whatsoever to help with the queries. "Whoops." With some new indexes — and one loosely related query optimization — in place, the heaviest page requests now take a few hundred milliseconds on this low-budget server. Still not blazing fast, but quite an improvement over the ~10-15 seconds they used to take... not to mention that having millions of changelog entries for one page is probably a bit of a border case 🤷‍♂️
  11. 5 points
    Oh my, this is excellent! A first use case I thought of was archives (eg. blogs) filtered by date: YYYY/MM/DD /** * This example merely displays back the selected date as a string, but it could be used to show date-based archives. * The following URLs are valid: * /YYYY * /YYYY/MM * /YYYY/MM/DD */ // Build basic date regex to eliminate very silly things (e.g. 2021/15/92) $dateRegexY = '(year:\d{4})'; // any four digits (could be narrowed, e.g. '(19|20)\d{2}' $dateRegexM = '(month:(0[1-9]|1[0-2]))'; // 01-09 or 10-12 $dateRegexD = '(day:(0[1-9]|[1-2][0-9]|3[0-1]))'; // 01-09, 10-29, 30-31 (will need further validation!) // Put it together $dateRegex = '/' . $dateRegexY . '(/' . $dateRegexM . '(/' . $dateRegexD . ')?)?'; $wire->addHook($dateRegex, function($event) { $date = $event->year . ($event->month ? '/' . $event->month : '') . ($event->day ? '/' . $event->day : ''); return $date; }); The possibilities are endless.
  12. 5 points
    Love the new URL hooks! The post doesn't mention what happens when a hooked URL or regex matches an existing page URL, but on testing it looks like the page URL takes precedence. That makes sense. In the blog post there are examples where trailing slashes are present and absent, and tests seem to show that the hooked URLs work regardless of whether a trailing slash is present or absent in the requested URL. But what if you want to enforce a trailing slash or no trailing slash and redirect accordingly as per real page URLs?
  13. 4 points
    @adrian It's like an index for page URLs/paths. Usually PW has to join every page in a query in order to know its URL. So the PagePaths module provides a more direct and theoretically faster route. But in my experience, it's not really faster until the URLs get long (like in cases where PW would have to do lots of joins to determine the URL otherwise). The other thing it does is that it lets you perform $pages->find() partial text matching operations on the url/path, which you can't do otherwise. The only place where it adds overhead is if you change the name of a parent page, it then has to re-index all the URLs for everything below the parent. It's not installed automatically because most people don't need it, and it doesn't support multi-language URLs. But it's handy to have when it crosses over with your needs. I'm good to provide an option for findRaw to return an array of basic objects if one requests it in the $options. But for most I would suggest sticking to the array because it's good for it to be clearly different in syntax from a regular page. That's because it's all raw and unformatted data, so it's not going to be safe to swap between find() and findRaw() in most cases and good to maintain clear differentiation. For instance, when using something from findRaw() for output, you've got to be sure to entity encode anything you output, etc. Plus, one reason for findRaw() is to provide the lowest level path to the raw data, and I think a PHP array is probably the lowest level, least overhead way of doing that. But having an option/alternative for a std object seems fine to me.
  14. 4 points
    @adrian @bernhard I've added support for getting 'url' and 'path' from $pages->findRaw() in the latest commit, but it requires the PagePaths module be installed. Now just need multi-language URL support for that module.
  15. 4 points
    Thanks. Great addition. It's like routes in Laravel. Much needed
  16. 4 points
    Great addition!! Kudos to @bernhard for bringing this on board!
  17. 4 points
    Long time no post. Here's my latest: https://maisliberdade.pt/ Mais Liberdade is a liberal think-tank in Portugal, promoting the values of liberal-democracy, individual freedom and free market economy. It's a non profit that gathers collaboration from people from multiple portuguese political parties, members of the european parliament, economists, professors, etc. During development, an announcement page was set up with a registration form for founding members. In that period of about a month, around 200 subscriptions were expected, but in the end we got over 6000 subscribers. This website features essays, events, videos and a free library that, at the time of this post is counting 400 books. The frontend was built using web components (Stencil js). Basic pages are built with a modular approach, I'm attaching a video of how they are created. The approach is a simple repeater with a custom block type selector interface. That selector is basically a modified version of FieldtypeSelectFile. I've hacked that module to expect a PNG to be available for each PHP file in the blocks folder, and modified the input field to generate a button grid after the select. This is lovely to use from the editor's perspective, but it's something to improve for the developer experience, because in the end this is a repeater, that has to contain all fields that each type of block needs, and for each of those fields I have to set it to show only if the blockType field equals X, Y or Z. With a lot of different block types this takes some planning and easily becomes hard to manage, but it's the best approach I found yet and the benefit for the editor's experience is well worth it. covered-compressed.mp4
  18. 3 points
    @ryan - I just discovered that findRaw() won't return the "url" field. I suppose that is to be expected given that it's not a DB stored field, but rather calculated at runtime, but it does still somewhat limit the functionality of findRaw in LOTS of useful situations. Do you think it would make sense to support this, or would it just slows things down too much? The other thing is I am wondering if you'd consider an option to return a standard PHP object rather than an array? It would make it so much easier to switch between find() and findRaw() Thanks.
  19. 3 points
    I never did like October 😄 ! I've fixed the code.
  20. 3 points
    And it would be great if @David Karich could update Page Hit Counter to use it.
  21. 3 points
    Just a tip - I replaced the "Page fields to search" setting in the PW core Page Search (ProcessPageSearch) module to use the search_index field. It seems like a great alternative when you want to have the admin live search find pages based on the content of lots of fields (not just title etc).
  22. 3 points
    😍 😍 😍 😍 😍 What a great addition!! Thank you Ryan!! 🙂
  23. 2 points
    Use the type "markup", which will accept HTML eg: 'name' => 'docs', 'label' => 'Docs', 'type' => 'markup', 'value' => '<strong>TEST</strong>'
  24. 2 points
    Thanks for this terrific feature, I will be able to use this directly on one of the next projects as a short link for QR codes on products. This will save me so much work.
  25. 2 points
    @ryan great work, I love this! This can totally form part of a great API endpoint generator. Or auto allowing Ajax endpoints for getting resources. Is it possible to get the whole request URL in the hook handler? Incase you want to handle any number of sub routes? Or do you have to explicitly name each path segment? (Oh I guess the regex option enables this as per your JSON example. )
  26. 2 points
  27. 2 points
    ProcessCacheControl version 1.1.0 released I just released an updated version of this module which fixes some issues with the asset version management feature. In particular: When using CacheControlTools::getAssetVersion while no asset version existed (for the specified category), the implicitly generated version incorrectly expired after one day. This is now fixed, so asset versions will not expirate anymore until manually cleared. Asset versions are no stored using WireCache::expireNever instead of WireCache::expireReserved. This means they will be deleted when using $cache->deleteAll(), when previously you had to explicity specify them by namespace to clear the cached versions. Links: Full changelog for version 1.1.0 ProcessCacheControl in the modules directory
  28. 2 points
    Thx @dotnetic for the idea about the getExtraMarkup hook! I slightly changed your version: $this->addHookBefore('AdminTheme::getExtraMarkup', function (HookEvent $event) { $config = $this->wire->config; $url = $config->urls($this); $config->scripts->add($url."lib/moment.min.js"); $config->scripts->add($url."tabulator/js/tabulator.min.js"); $config->scripts->add($url."Grid.js"); $config->scripts->add($url."RockGrid2.js"); $config->styles->add($url."tabulator/css/tabulator.min.css"); }); using the "before" hook makes it possible to use the config->scripts|styles->add syntax 🙂 I'd love to get a $config->scripts->addAfter("/my/script.js", "/my/other/script.js") feature - but acutually I've always found a way to get the correct order by using proper hooks...
  29. 2 points
    Seeing that example it might make more sense to add different options to pw-panel: One showing the panel on top of the background and one pushing the main page to the side while keeping everything visible. That would also make it possible to support mobile devices... If anybody has time, feel free to improve the module 😉 🙂 Also if we have any JavaScript experts - I'd love to have the Iframe configurable in its width via mouse drag... no idea how that could be done. At the moment it is set to 40vw.
  30. 2 points
    Glad it helps! That's already possible - I've added an example to the readme: $wire->addHookAfter("ProcessPageEdit::buildForm", function($event) { $page = $event->process->getPage(); if($page->template !== 'mypagetemplate') return; /** @var RockIframe $iframe */ $iframe = $this->wire->modules->get('RockIframe'); if($iframe) $iframe->show("http://www.example.com"); });
  31. 2 points
    Uninstalling and reinstalling LazyCron fixed it for me.
  32. 2 points
    Hi @titanium, I don't have had time to install PHP 8 and test out all my sites and modules under it. I will do so with priority for WireMailSmtp. But it may take 1 or 2 weeks, sorry.
  33. 2 points
    You aren't saving the redirect_last field. Also, I don't think it is necessary to test the counter prior to redirecting. Try this and see if it fits your needs. if ($input->urlSegment(1) === 'redirect') { $page->of(false); $page->redirect_last = time(); $page->redirect_counter += 1; $page->save(); $session->redirect($page->website_url, 302); }
  34. 2 points
    PHP 8 was released a few months ago. I can't get WireMailSMTP to work with PHP 8. The error message is: "Error in hnsmtp::send : cannot connect to smtp-server!" With PHP 7.4, on the other hand, it works fine. Was anyone already successful with PHP 8? Thanks in advance.
  35. 2 points
    There might be a better approach, but this snippet in site/ready.php works for me: <?php namespace ProcessWire; $wire->addHookBefore('ProcessPageLister::execute', function(HookEvent $event) { if($event->page->process != "ProcessUser") return; # Put all fields that should be searchable by default here: $userSearchFields = ['email']; # We create a comma separated field selector string from the array above for use by ProcessPageLister $searchFieldsString = implode('', array_map(function($f) { return ", $f="; }, $userSearchFields)); $lister = $event->object; # ProcessUser appends the ", roles=" selector before our hook happens, so we inject our fields # before that part: $lister->defaultSelector = preg_replace( '/(?=, roles=)/', $searchFieldsString, $lister->defaultSelector ); }); ProcessPageLister builds its field list from the selector style string its $defaultSelector property. So you just need to put the names of your fields into the $userSearchFields array and ProcessPageLister will take care of the rest. If you want to completely replace the search field list, it's even simpler. <?php namespace ProcessWire; if($event->page->process != "ProcessUser") return; # Put all fields that should be searchable by default here: $userSearchFields = ['lastname', 'firstname']; # We create a comma separated field selector string from the array above for use by ProcessPageLister $searchFieldsString = implode(', ', array_map(function($f) { return "$f="; }, $userSearchFields)); $lister = $event->object; $lister->defaultSelector = $searchFieldsString; }
  36. 2 points
    Another quick and easy one 🙂 Place this in /site/templates/admin.php before the require(...) statement: $this->warning('Attention: We have planned maintenance starting 2021/02/11 from 10:00 AM - log out before that date to prevent data loss.');
  37. 2 points
    Edit: Because of the great response to this topic I wrote a guest blogpost: https://processwire.com/blog/posts/building-custom-admin-pages-with-process-modules/ One of the hidden treasures of processwire seems to be the creation of custom admin pages. Technically speaking those pages are ProcessModules - but i guess that's the reason why so many people out there seem to be afraid of building them... it sounds so hard! You've never created a module for ProcessWire? You have never created a plugin for any other CMS? You have no clue about OOP with all its classes, methods and properties? No problem! I'll show you how simple you can start: <?php class CustomAdminPage extends Process { public static function getModuleinfo() { return [ 'title' => 'Custom Admin Page Example', 'summary' => 'Minimalistic ProcessModule to show that nobody has to be afraid of building custom admin pages.', 'href' => 'https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/', 'author' => 'Bernhard Baumrock, baumrock.com', 'version' => 1, // page that you want created to execute this module 'page' => [ 'name' => 'customadmin', // your page will be online at /youradmin/setup/customadmin/ 'parent' => 'setup', 'title' => 'Custom Admin Page Example' ], ]; } public function ___execute() { return 'This is the most simple Admin-Page you have ever seen :)'; } } Now save this file as CustomAdminPage.module and place it in your /site/modules folder. After a refresh it will show your module in the modules manager of your site where you can install it: After installation you already have your first very own admin page! Congratulations! Was not too hard, was it? It's as simple as that! Now lets add some more custom HTML. And to show you another nice feature we will add this code to a separate method called executeDemo(). And because everything is so simple we will also add some javascript to this page public function ___executeDemo() { $out = ''; $out .= '<h1>H1 has some special css styling in the admin, thats why it seems to have no effect</h1>'; $out .= '<h2>H2 looks different ;)</h2>'; $out .= '<h3>...and so does H3</h3>'; $out .= '<button onclick="myFunction()">Click me</button>'; $out .= '<script>function myFunction() { alert("this is a demo javascript"); }</script>'; return $out; return ''; } Now thanks to ProcessWire-magic your page will already have its own URL: Just append /demo to your url and see what you get: And of course don't forget to click the button Ok, now that code looks a bit hacky, right? Inputfields and especially InputfieldMarkup for the win! We add another method with some advanced code. To use inputfields we need a form that holds all those inputfields and that makes it possible to handle user input lateron. See somas great tutorial about forms here for a quickstart and more details: public function ___executeAdvanced() { $out = '<h2>A more complex Example</h2>'; $form = wire()->modules->get('InputfieldForm'); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Markup Test 1'; $field->value = '<h1>h1</h1><h2>h2</h2><h3>h3</h3><h4>h4</h4>'; $form->add($field); $out .= $form->render(); return $out; } Ok, it get's boring Let's do something more fun and add a chart in a second field and change the fields to 50% screen width (I'm sure you know that already from the GUI template editor)! public function ___executeAdvanced() { $out = '<h2>A more complex Example</h2>'; $form = wire()->modules->get('InputfieldForm'); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Markup Test 1'; $field->value = '<h1>h1</h1><h2>h2</h2><h3>h3</h3><h4>h4</h4>'; $field->columnWidth = 50; $form->add($field); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Chart Sample'; $field->value = '$chart'; //$field->notes = 'Example code taken from here: http://www.chartjs.org/docs/latest/getting-started/usage.html'; $field->columnWidth = 50; $form->add($field); $out .= $form->render(); return $out; } OK, we are almost there... we only need to add the chart library! To keep everything clean we will put the code for the chart in another method. We will make that method PRIVATE to add some security. Our new Method: private function renderChart() { // prepare chart code wire()->config->scripts->add('https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js'); ob_start(); ?> <canvas id="myChart"></canvas> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], datasets: [{ label: '# of Votes', data: [12, 19, 3, 5, 2, 3], backgroundColor: [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)' ], borderColor: [ 'rgba(255,99,132,1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero:true } }] } } }); </script> <?php return ob_get_clean(); } Now we just need to call $this->renderChart() in the right place! Here is the complete Module: <?php class CustomAdminPage extends Process { public static function getModuleinfo() { return [ 'title' => 'Custom Admin Page Example', 'summary' => 'Minimalistic ProcessModule to show that nobody has to be afraid of building custom admin pages.', 'href' => 'https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/', 'author' => 'Bernhard Baumrock, baumrock.com', 'version' => 1, // page that you want created to execute this module 'page' => [ 'name' => 'customadmin', // your page will be online at /youradmin/setup/customadmin/ 'parent' => 'setup', 'title' => 'Custom Admin Page Example' ], ]; } public function ___execute() { return 'This is the most simple Admin-Page you have ever seen :)'; } public function ___executeDemo() { $out = ''; $out .= '<h1>H1 has some special css styling in the admin, thats why it seems to have no effect</h1>'; $out .= '<h2>H2 looks different ;)</h2>'; $out .= '<h3>...and so does H3</h3>'; $out .= '<button onclick="myFunction()">Click me</button>'; $out .= '<script>function myFunction() { alert("this is a demo javascript"); }</script>'; return $out; return ''; } public function ___executeAdvanced() { $out = '<h2>A more complex Example</h2>'; $form = wire()->modules->get('InputfieldForm'); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Markup Test 1'; $field->value = '<h1>h1</h1><h2>h2</h2><h3>h3</h3><h4>h4</h4>'; $field->columnWidth = 50; $form->add($field); $field = wire()->modules->get('InputfieldMarkup'); $field->label = 'Chart Sample'; $field->value = $this->renderChart(); $field->notes = 'Example code taken from here: http://www.chartjs.org/docs/latest/getting-started/usage.html'; $field->columnWidth = 50; $form->add($field); $out .= $form->render(); return $out; } private function renderChart() { // prepare chart code wire()->config->scripts->add('https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.min.js'); ob_start(); ?> <canvas id="myChart"></canvas> <script> var ctx = document.getElementById("myChart"); var myChart = new Chart(ctx, { type: 'bar', data: { labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"], datasets: [{ label: '# of Votes', data: [12, 19, 3, 5, 2, 3], backgroundColor: [ 'rgba(255, 99, 132, 0.2)', 'rgba(54, 162, 235, 0.2)', 'rgba(255, 206, 86, 0.2)', 'rgba(75, 192, 192, 0.2)', 'rgba(153, 102, 255, 0.2)', 'rgba(255, 159, 64, 0.2)' ], borderColor: [ 'rgba(255,99,132,1)', 'rgba(54, 162, 235, 1)', 'rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)', 'rgba(255, 159, 64, 1)' ], borderWidth: 1 }] }, options: { scales: { yAxes: [{ ticks: { beginAtZero:true } }] } } }); </script> <?php return ob_get_clean(); } } I hope you enjoyed reading this and it will open up many new possibilities for you! Updates: Little update on permissions: https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/?do=findComment&comment=174746 Little tutorial on file uploads: https://processwire.com/talk/topic/17709-how-to-create-custom-admin-pages-aka-processmodules-yes-its-that-simple/?do=findComment&comment=185261
  38. 1 point
    Awesome @ryan - thank you! Can you please explain: 1) How PagePaths actually improves / changes searching by path in selectors 2) Is there any significant overhead having it installed, ie why isn't it installed by default? Thanks!
  39. 1 point
    Great addition @ryan! Quick question: will ProCache be able to cache (the result from) these URL hooks?
  40. 1 point
    Excited to be learning REACT/NextJS. It's so 'on-trend'. I can double my hourly rate, take 3x as long to deliver a website with 2/3 of the Google Lighthouse results and who doesn't love the spinner while the component content loads? Doing a well optimised PW site with minimal JS, fast page loads, accessibility, SEO and more built in is so old hat. And even better, with JS frontends, the code maintenance goes on and on. No end to revenue possibilities. FML
  41. 1 point
    Hi @MoritzLost - I assume you are using PHP8? This used to be OK on PHP7, but 8 seems to have become more strict about blank vs zero. It should be fixed in the latest version.
  42. 1 point
    Thx @snobjorn for creating the issue https://github.com/processwire/processwire-issues/issues/1339 Everybody having this problem please give it a thumbs up! I'm locked to v171 due to this issue 😞
  43. 1 point
    Hi @fruid, I've never seen an id attribute on the <title> tag before! Can you please edit line 258 of MarkupContentSecurityPolicy.module to remove the '>'? <?php // Line 258 $node = '<title'; I think that'll fix it. If so I'll push that change to the repo. Cheers, Chris
  44. 1 point
    @jon9china - thanks for the explanation. I think I sort of understand "They were looking for the page that identified the restricted user that was logged in", but if you could provide the selector code you are using it would be helpful - perhaps I can implement something into the ARB module so that you can still use the selector the way you want, rather than changing to the email field. So if you could post the old and new versions, I'll try to replicate and see if I can fix it somehow.
  45. 1 point
    @fruid I'd still test your mail with the mail-tester tool mentioned above to make sure your email doesn't appear "spammy". Depending on your settings and whether you're sending through the mail server of your hosting your mail may appear to be unauthenticated. Anyway, it's curious that WireMailSmtp isn't working but WireMailPHPMailer is, are you using the same settings for both? By the way, I'd be slightly wary when the provider tells you to use port 587. In general, TLS-encrypted mail should use port 465. Port 587 is for STARTTLS, which starts SSL-encryption only after a connection has been established, which means you're susceptible to a man-in-the-middle attack. Your hoster only supports port 25 (plain text) and 587 (STARTTLS), that's a red flag. Anyway, if you're using the suggested settings for SMTP connections by your hoster and they're not working, I'd just talk to them to see what might be the problem.
  46. 1 point
    What about this? 🙂 if(!$user->isLoggedin() AND $page->template == 'sometemplate' AND $input->get('code', 'string') == $page->page_pass) { $page->removeStatus('unpublished'); }
  47. 1 point
    Nice tip!! To elaborate on other examples, I do something like this to have this same "code to view" per page (in the sense of one code per page) like this on ready.php: if(!$user->isLoggedIn()){ wire()->addHookAfter('Page(template=sometemplate)::viewable', function($event){ $page = $event->object; $pass = wire('input')->get->text('pass'); if($page->page_pass && $pass){ if($page->isUnpublished() && $pass == $page->page_pass){ $event->return = true; } } }); }
  48. 1 point
    I know some things I have struggled with in the past is exporting fields from one website to another to jump start a project, and option fields can't be imported in. I have to go to each of, say 20 fields, and manually copy and paste all options in for all of them. When I am working in a page backend in one tab, and have another tab open with the front end to see my changes, I can save the backend tab, and instead of staying where it is at, it shoots me to the top of the page. This isn't great. Would prefer it to stay there so I can tweak, save, tweak, save. Most other backend system I don't even need to hit save as changes I am making are live, but this is a step in the right direction! Also would be nice to have a way to not have to switch back and forth between tabs. Getting live editing would be even better! I even purchased the plugin for this and wasn't a huge fan of it.
  49. 1 point
    Browsing to the already selected item gets to be a pain, but thankfully the AdminOnSteroids module has an enhancement that lets you clear the selection more easily.
  50. 1 point
    I haven't had much (enough) time to work on my mailer module and haven't looked at how WireMailSMTP handles these particular things, but in general I'd have to agree with Pete. For things that are commonly used, it'd be best if there was a "standard" way to do that. One that doesn't depend on which module extending WireMail is installed at the time. I believe that we're talking about interfaces here, though that's not technically what it's going to be.. or what it is at the moment, at least. Then again, if @horst has implemented this feature already, I'll probably take a look at his implementation anyway and use similar public methods if possible to provide some consistency
×
×
  • Create New...