Leaderboard
Popular Content
Showing content with the highest reputation on 03/08/2021 in all areas
-
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 keep displaying a message when a superuser logs in until the update has been successfully executed. Technically this schema update could be handled just like the one before, behind the scenes, but since this one involves adding new indexes... well, if the custom database table (process_changelog) has a lot of data, this update could 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: the README.md file contains a simple PHP script that can be used 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, I almost accidentally noticed that some of my own 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 those 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 my 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 anyway ?♂️5 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.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.4 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->url4 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).2 points
-
The PW core comes with many great classes such as WireData, WireArray and so on. Sometimes I'm developing stuff without a running PW instance and I'd love to have the great PW api available there as well. I've mentioned that in the PW Roadmap 2021 thread and Ryan stated: https://processwire.com/talk/topic/24897-weekly-update-– 8-january-2021/?do=findComment&comment=209891 That sounded interesting, so I've tried how that works and created a repo for quick and easy testing so that everybody can try what works and share their findings so that everybody can learn. https://github.com/baumrock/PwStandalone PwStandalone The ProcessWire core has many great classes such as WireData, WireArray, WireRandom etc.; Many of them can be used without a running PW installation! This repo helps testing those features and hopefully will motivate others to share their findings (as PRs). It's as simple as creating a new file in the examples folder! I'd appreciate any help regarding the examples throwing an error! PRs for other examples that you've tried very welcome!1 point
-
From the recent discussion about the roadmap & wishlist for 2021 and some other posts by @ryan, it comes to my mind that developing and coordinating the whole project for one person is becoming harder and harder and leads nearly to the reverse of expanding the ProcessWire ecoysystem. This is not against Ryan, i think everyone here knows how engaged he is about ProcessWire, but he has only 24/7 (sometimes i think he's got far more than that...). We as the community could support the project (financially) to relieve Ryan and could take over some tasks from him. This could be, but is not limited, to: Building a Foundation/Association/Company to ensure the persistence of the project and to fund the work put in ProcessWire of Ryan (and others). Nearly every other CMSs i checked has something like this (Drupal Association, Typo3 Association, Joomla Foundation, Wordpress Foundation, Contao Association, ...). This also puts more trust in the project, if someone new will check on his engagement in ProcessWire. Assigning persons/teams to work on things: Extending the core (when necessary) Developing and maintaining major modules (e.g. page builder, admin themes, internationalization, marketing, ecommerce system, ...) Testing and inspection of modules developed by others Making translations of modules (translation of the core is mostly covered, i think) Working on PRs & issues submitted on github Working on the homepage Coordinating the community efforts I know, some resorts are already covered by others (e.g. @Pete for the forum, @horst for images, ... ), but there are many other areas where this ist not the case. By joined efforts by the ProcessWire community this hopefully will also attract new developers to the system and by a growing number of users this assists in the things above in a circular process. What do you think?1 point
-
Hi everyone, Does anyone know if the idea of a $pages->has() that doesn't return unpublished, hidden, trashed pages has been discussed anywhere? Currently, has() works like get() but I would like to see it work like findOne() - ie one result, but with the exclusion rules in place. I just don't see many uses for has() in its current form. Based on these tests, and the fact that has() and getID() have basically the same time execution time, even a findOneID() would work. Any info on this would be great, otherwise I'll add it to the Requests GH repo.1 point
-
Oh man, I must be going blind - totally overlooked that, sorry. Just went straight to the text and thought you were saying has()/getID() wasn't useful and should be changed. Thanks for opening the request - thumbs up added ?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!1 point
-
Yeah, the documentation is kind of buried: https://processwire.com/api/ref/page-render/render-page/. I don’t even know how to get there, really. The PageRender class doesn’t show up on the API frontpage at all…1 point
-
If you use $myPage->render() you can use the options array: echo $myPage->render(['lol' => 'dongs']); /////// then on $myPage’s template ///////// echo $options['lol']; Or you could just add the property to the Page object: $myPage->set('lol', 'dongs'); echo $myPage->render(); /////// then on $myPage’s template ///////// echo $page->lol;1 point
-
Hi @adrian Sorry for being too briefly ? This is what I'm talking about: My module hooks into the pagehitcounter's method that logs requests to pages. I had to find a way to get the page path that relates to a given page id efficiently and easily. Otherwise I'd just have a list of page ids shown to the user (which is not really helpful) or the other option would have been to store page paths to the DB or calculate them on runtime (also not ideal). The solution I came up with is to install the PagePath module. That module creates a list of all pages that live on the system and keeps that list in sync with pages (eg when changing a pagename etc). You get a table in the DB that holds the page ID and the corresponding page path. If you don't have that module installed, there is no way (as far as I know) to get that information from the database (well, I guess that's the reason why PagePaths module exists so I'm quite confident with that assumption ? ). RockFinder has the same limitation and I've taken another approach there, see the addPath() method: https://github.com/baumrock/rockfinder3#addpath Does that clarify things? ?1 point
-
You can do that with hasChildren() just like you proposed, but you have to get the grandchild first. If you want to get the first child of $page and then the first child of that, and then check if that has children, you can do $page->child()->child()->hasChildren(). You can also put selector strings between each of the parentheses to only get specific children. Yeah, I’m pretty sure this should work: $pages->count("has_parent=$child, template=tops|tops2, projekt_top_vermietet=1") If you already know there aren’t any other templates under $child, you can just remove the template selector entirely.1 point
-
That's what it says in the title of this request: "or maybe a findOneID()" ? I completely agree that getID works as it should. I think that perhaps has() is a confusing term given what it does - it's hard to know by its name whether it will work like a get or a find (ie with exclusions or not). But, we also clearly agree on that ? Request just posted: https://github.com/processwire/processwire-requests/issues/3941 point
-
I'm talking about the code of the migration that throws the error - not the code of your fix ?1 point
-
Actually it found me ? My code could probably be improved, but here is wahat I did (from around line 1190) // if the page is the home page then we need to avoid referring to its parent when saving if ($parent === 0) { $parent = ''; unset($data['parent']); } else { // make sure parent is a page and not a selector $parent = $this->pages->get((string)$parent); } // get page if it exists if ($parent and $parent->id) { $selector = [ 'name' => $name, 'template' => $template, 'parent' => $parent, ]; } else { $selector = [ 'name' => $name, 'template' => $template ]; } $page = $this->pages->get($selector); Hope that helps.1 point
-
getID() is just an alias of has() - I requested that alias because to my way of thinking it's a clearer name for what the method does. I think what you're asking for is a new findOneID() method ?. getID() works just as it should in my opinion - getID() is to get() as findIDs() is to find(). Once upon a time we only had find() and get(), and then findOne() was introduced to save doing find("limit=1")->first(). So it would be cool to have a similar findOneID() labour-saving method, and in the meantime you could use findIDs() with a limit of 1.1 point
-
I've done something similar on my extension module for PageHitCounter where only page IDs of page hits are stored. You need to enable PagePath module and then you can join page paths to page ids easily and efficiently. The page paths are kept in sync by the module, cached in the DB and therefore do not need to be "calculated" at runtime on every item.1 point
-
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 ?1 point
-
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.1 point
-
And it would be great if @David Karich could update Page Hit Counter to use it.1 point
-
Thanks. Great addition. It's like routes in Laravel. Much needed1 point
-
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). Screenshots1 point
-
Looks fine to me ? Hi @wwwouter, welcome to the forums. To your question about generating a dynamic CSS file via PHP, I would do it like this: 1) create a php file for example called "dyncss.php" and copy it under site/templates/ (look that it starts with a namespace signature "<?php namespace ProcessWire;" 2) in pw admin under setup > templates > addNew create a new template and select dyncss as its template file 3) in your site/config.php create a appropriate content header for css files, according to the advice "To add more content types see contentTypes in /wire/config.php (and add them in /site/config.php)." // in site/config.php merge our custom types into the existing core ones $config->contentTypes = array_merge($config->contentTypes, array( 'css' => 'text/css' ) ); 4) now open the template in backend under setup > templates > dyncss and adjust some settings: a) under family tab set "no, may not have children" and for example "can be used for new pages = one", if you only want to have one unique page of this type b) under the files tab disable the compiler, under content type select css, and if you use delayed output with prepend and append files you have to disable this here for this content type c) adjust others as you may like or need and save and close the template 5) create a page in your page tree that has this template, give the page a page name that you want to call as url for your dynamic css file for example "mycss" 6) put all your php/css magic into the php file under site/templates and add the URL of your generated page where ever you need it in your generated html If you later on want to use more than one dynamic css file, you can decide to allow more than one page with that template, or go the root with url-segments and a single page what I would prefer. But first I hope this can be a good starting point to get you going. If questions arise, come back here into this thread again and ask. ? Happy exploring and coding.1 point
-
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.mp41 point
-
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.');1 point
-
What about this? ? if(!$user->isLoggedin() AND $page->template == 'sometemplate' AND $input->get('code', 'string') == $page->page_pass) { $page->removeStatus('unpublished'); }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; } } }); }1 point
-
Don't worry, it's not a stupid question ? You can definitely build a custom search feature and get great results with that, but things tend to get a little more complicated if your site contains a larger amount of fields, and particularly if you're using Repeater, RepeaterMatrix, PageTable, or other types of fields with repeatable content — or perhaps page reference fields gluing content from different pages together. In other words content that isn't technically (directly) part of the page, but needs to be tied with the page in the context of site search. Although in many cases you could no doubt include all those fields in your search queries, this can result in pretty complex queries, and such queries also tend to become inefficient. Generally speaking the more fields you join in the query, the more complex the resulting SQL query will get, and the more time and resources it'll take to process. Not a great thing for scalability. First version of SearchEngine was really just an easy way to reuse bits and pieces of code that were developed over time to mash field values together so that they could be searched more efficiently. Soon after along came the markup generating parts (which now make up a notable portion of the module), a set of features for automatically filtering and sanitizing queries and processing the index, JSON output option (mostly for AJAX requests), indexing support for field types requiring specific handling (core ones as well as third party), etc. From the initial post in this thread: These days in my projects I install SearchEngine, set it up, and trigger the render function. The module takes care of everything else and "just works". In a nutshell SE bundles most of the stuff a typical site search will need into one package, and tries to do it efficiently and following best practices ? Hope this answered your question!1 point
-
Unfortunately I'm not totally happy with the module as it is. One thing is the decimal issues shown by Lostkobrakai, the other thing is that in my setup the need has come up to list all revenue that have 20% vat etc.; This is at the moment not possible using this module, because the data is stored in json objects. While it would be possible to query the JSON directly in the DB with current mysql versions, the problem is also that those revenues can not easily be enriched with other data (like some tax codes or the like). So I need to rethink that module soon ?1 point
-
Grüezi Daniele, you will get access to the form builder support forum once Ryan sees this. I'm not sure what you need to archive, but since the entries are stored as json you can't search for entry fields. If you want to use API to find entries you need to store them as pages. You can access the entries in DB through $forms foreach($forms->get("contact-form")->entries->find() as $e){ echo "<p>{$e['e_mail']}</p>"; }1 point