Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by dragan

  1. @Haagje R. I assume you have FTP-access? The error screenshot you posted says that your PW installation still has the install.php in root. That should have been removed right after the initial installation (removing that file - and others - is actually part of PW's installation routine). If that install.php still exists on the server, simply delete it, and try again.


    Checking https://www.neuro-insight.de/install.php throws an error about your 404 page not being viewable by guests (guest user = not logged in). So there might be more strange things to check...

    • Like 1
  2. 3 hours ago, bernhard said:

    Maybe there's a CSS-only way of doing that?

    here's a quick try: https://codepen.io/dragan1700/pen/bGNZBEK?editors=1100

    Whatever you do: don't ever try to do stuff like "underline only the last line of a text block": it's a major cause of migraine. Without using JS this can't be done (yet) only with CSS alone. (Well - not unless you do PDFs, of course, or don't have to make stuff responsive...). A client recently came up with BS like that (which was designed by another agency), and we had a hard time explaining why this was not a good idea.

    • Like 2
  3. quick, dirty & ready 🙂

    $this->addHookAfter('Page::render', function ($event) {
        if ($this->page->template != 'admin') {
            // just an example I use in almost every PW site: add responsive wrappers around iFrames:
            if (strpos($event->return, 'videoWrapper') === false) {
                $event->return = str_replace("<iframe", "\n<div class='videoWrapper'><iframe", $event->return);
                $event->return = str_replace("</iframe>", "</iframe></div>", $event->return);
            // another example I use in almost every PW site: add responsive wrappers around tables:
            if (strpos($event->return, 'scrollTable') === false) {
                $event->return = str_replace("<table", "\n<div class='scrollTable'><table", $event->return);
                $event->return = str_replace("</table>", "</table></div>", $event->return);
            $cleaned = preg_replace('~(<h[23]\\b.*?>(?:<[a-z][^>]*>)*)(\w+)~ism', '$1<span>$2</span>', $event->return); // BitPoet's regex magic
            $event->return = preg_replace('/<p>\\s*?(<a .*?><img.*?><\\/a>|<img.*?>)?\\s*<\\/p>/s', '\1', $cleaned); // remove default paragraphs around images in CKE


    • Like 1
    • Thanks 1
  4. @Macrura I'll try and give some more information.

    Version: it seems SF installs actually two modules. One is v. 1.0.4, the other 1.0.3. Since I just installed it 1-2 days ago, it's unlikely I accidentally picked an old version.

    I'm using the JSON kitchen sink version.

    I de-installed SF y'day and installed again today. The same issues persist.

    I attached a few screenshots.

    before installing:


    after installation:


    after installation, under modules (notice versions 1.0.3 + 1.0.4)


    Trying to update process SF:


    editing the page under /admin: the page I entered for the JSON is 100% correct:


    Thanks a lot for looking into it.


  5. I can't answer all of your questions right now, but you should never edit any file in the wire-folder. It will be overwritten the next time you upgrade PW.

    With admin themes, you can simply copy the entire admin-theme folder from wire/modules/AdminTheme/AdminThemeDefault to site/modules/AdminTheme/AdminThemeDefault. PW will notice there are two versions of the same module and ask you which one it should use. Just select the site/... version and then you can work with the copy. (I think that applies to any kind of module - not just admin themes)

    re: your first use-case / question
    There's an old module out there which does something similar: Pick an image from the page and display it in the page-tree. iirc it's a module (or hook?) by @Soma; search the forums - you'll find it eventually.


    • Like 1
  6. @Macrura First of all: Thanks for this great module. I noticed it already when you first released it, but because there was no real for it in my own projects, I never installed it till now.

    I found a confusing behavior (bug?) when I use the kitchen sink example.

    No matter what email address I try, PW always shows me an error: "InputfieldEmail: Please enter a valid e-mail address - Email Test"

    It doesn't matter if I use a real, existing email, or just a semantically correct one.

    What's even more strange is that this red error shows up on every page in the PW admin - literally everywhere.

    Am I missing something obvious? PW throwing an error if a required field is empty or doesn't match some regex rules is one thing, but that should only be visible in the respective page-edit screen, not globally? The only thing I noticed: There is no "autoload disabled" checkbox in module/edit?name=ProcessSettingsFactory

    I'm using SF 1.0.3 and
    ProcessWire: 3.0.149
    PHP: 7.3.13
    Webserver: Apache/2.4.35 (Win64) OpenSSL/1.1.1d
    MySQL: 5.7.24


    • Like 1
  7. 9 hours ago, bernhard said:

    How does that work with Tracy? 😮 

    Well, maybe "breakpoint" is not the right word, i.e. the code won't stop to process like in JS dev tools. But you can dump all kinds of stuff with d($doc) inside the template. Then you'll quickly see e.g. if a var doesn't contain what you thought it should etc. (null, 0 results, nullpage etc.). I'm sure you used it a million ways...

    What's perhaps a bit misleading in Tracy, is that addBreakpoint() is meant to only be used with the performance panel. So yeah, not exactly the same as adding JS breakpoints in frontend-land 🙂

    • Like 1
  8. On 1/19/2020 at 12:41 AM, Robin S said:

    I linked to information about it in my earlier post above

    I have of course read that thread, but that approach has a few limitations.

    If you set up categories and sub-categories the way you did, it works without a doubt.

    However, if you want to stick to the DRY principle (don't repeat yourself), and choose a maincat<-->subcat relationship that I have chosen, this won't work. In a real-life scenario you'll likely have several sub-categories that are eligible for more than just one main category. Suppose you (or your client) re-names a certain sub-category, you'll have to update all instances of that one single sub-category for each main-category as well. With a small setup this might not be an issue at all. But as the site (and products...) grows, this can turn into a chore to maintain. I'd rather have a separate, independent set of sub-cats to choose from. IRL many of those sub-cats are overlapping ("backups" or "upgrade" surely isn't only available on Win or Mac).

    From a UX/GUI perspective, when you need multiple subcats to attach to a maincat, your options are rather poor. Select Multiple, even in 2020, looks and feels... like 1999 (without the party aspect). So if you decide to use checkboxes in the admin, this approach won't work.

  9. Is your file-field configured to have just one file, or multiple? If multiple, you'd have to do $filefield->first()->url

    Since you get 403 (forbidden) errors, it's maybe more likely those file-fields are somehow access-protected. Check your access setting of that field and/or template. Do you get these errors also as a logged-in superuser or just as guest?

    Do yourself a favor and install Tracy Debugger (if you haven't already). You can then insert breakpoints in your code (similar to console.log() with Javascript), and you'll see more infos and hints about the error(s).

  10. I have no idea. I just tried it here as well - RM fields get ignored.

    Ryan stated a while back (~ 5 yrs ago) that the whole concept of this field-type was outdated, and wasn't delivering the performance advantages etc. he initially had in mind. That's also the reason it eventually got kicked out the core. I guess this field-type is much older than some newer field-types like repeater matrix, so it never got updated to accomodate them.

    It's not too hard though to re-build the same basic functionality though with a regular text field and a hook that on page-save collects all text-based fields in your template(s) and saves it to that field.

    What you could also try is using fieldtype(s) instead of fields: https://processwire.com/blog/posts/processwire-3.0.91-core-updates/

    This won't help with caching though, just keep your selector string a bit more readable...

  11. I just installed the module (though it's still at version 4, according to module info) and after trying to run it, I just get:

    The process returned no content.

    ProcessWire: 3.0.148
    PHP: 7.3.13
    Webserver: Apache/2.4.35 (Win64) OpenSSL/1.1.1b (Laragon)
    MySQL: 5.7.24

    Could this be a Windows issue? Do I have to enable special PHP extensions first?

    Nothing in Apache or PHP error logs.

    The strangest thing is: The page title says "Form Builder". (?)

    OK, after unpublishing the Form Builder page, I get a static version alright.

    I was a bit puzzled though that each parent page is actually a folder in the static version.

    Going to play around a bit more. Thanks for sharing this module!

  12. Here's an example that works without having to first save & re-load the page you're editing.

    The setup:

    In my page tree somewhere (usually under a parent I call "meta", "settings" or similar), I have main and sub-categories as pages:



    The sub category pages have template "empty" with just the title field and nothing else - not even a physical template.php file.

    The main category pages have tpl "maincat", which holds an addtl. field "main2sub", which is a page reference field. Here you map your sub-categories to each main category.

    My (simplified) product tpl has two fields:

    • maincat -> page reference field single. Parent = main cat / 1366
    • select_categories -> page reference field multiple / output as checkboxes. No configuration set in field settings "selectable pages".


    So far, so boring.

    I need JS + PHP for the auto-switch to happen.

    I created site/templates/admin/admin.js

    In order for PW to even load it, I edited site/templates/admin.php:

    $config->scripts->add($config->urls->templates . "admin/admin.js");
    require($config->paths->adminTemplates . 'controller.php'); // this must stay as the last line

    admin.js looks like this:


    document.addEventListener("DOMContentLoaded", function (event) {

        const urlParams = new URLSearchParams(window.location.search);
        const pid = urlParams.get('id');
        const select = document.getElementById("Inputfield_maincat");
        if (select) {
            getAllowedSubCats(event); // on page load
            select.addEventListener("change", getAllowedSubCats, true); // on main-cat change

        function getAllowedSubCats(event) {

            // change this: outermost inputfield wrapper ID that holds your sub-categories:
            const subcatInput = document.getElementById('wrap_Inputfield_select_categories');
            // if you're not using checkboxes, change your selector accordingly:
            const subcatItems = subcatInput.querySelectorAll("input[type=checkbox]");
            let mainCatValue = select.options[select.selectedIndex].value;

            if(event.type === 'change') {
                subcatItems.forEach(function (elem) {

            // change this to point to your PHP script URL that returns the matching sub-categories:
            const url = "http://pw.test/api/update_subcats/" + pid + "/" + mainCatValue + "/";

            fetch(url, {
                headers: {
                    'Content-Type': 'application/json',
                method: "GET",
                .then((response) => response.json())
                .then((data) => {
                    // console.log('Success:', data);
                    const subcats = data.subCats; // the response array holding our sub-category page IDs

                    // reset disabled state:
                    subcatItems.forEach(function (elem) {
                        elem.closest('li').setAttribute("style", "display: block;");

                    subcatItems.forEach(function (elem) {
                        const cbValue = elem.getAttribute("value"); // checkbox value
                        const cbValueInt = Number(cbValue); // this is necessary, because DOM attributes are strings
                        if (!subcats.includes(cbValueInt)) {
                            // disable and hide all categories that are not allowed for selected main category:
                            elem.setAttribute("disabled", "disabled");
                            elem.closest('li').setAttribute("style", "display: none;");
                .catch((error) => {
                    console.error('Error:', error);



    It's written in plain JS (ES6). With jQuery it would have been a bit shorter, no doubt, but I got into the habit of using as much vanilla JS as possible. If you need to support older browsers, you'd need to run it through something like Babel.

    In nearly every PW installation, I create a dedicated template + page that acts as an API / AJAX endpoint. Create a new template "api", enable URL-segments, create a file "api.php" in site/templates/, and finally create a hidden page somewhere that uses this template. Use the option "must have trailing slashes" (check yes).

    The api.php looks something like this:



    if (!$input->urlSegment1 ) {
        return $this->halt();

    if ($input->urlSegment1 === 'update_subcats' && $input->urlSegment2 && $input->urlSegment3) {
        header('Content-Type: application/json; charset=utf-8');

        $pid = (int) $input->urlSegment2; // page id of product
        $productPage = $pages->get($pid); // product page
        $mainCat = (int) $input->urlSegment3; // page id of main category, e.g. Windows, Mac
        $subCats = $pages->get($mainCat)->main2sub; // assigned page reference sub-categories attached to main category

        $subCatsArray = array();
        foreach($subCats as $sub) {
            $subCatsArray[] = $sub->id;

        $ar = array(
            "success" => 'yay!',
            "pid" => $pid,
            "mainCat" => $mainCat,
            "subCats" => $subCatsArray,

        $json = json_encode($ar);
        echo $json;


    Basically, what happens, is:

    On page load and on main-category-dropdown change, I send an AJAX request to my API script with the page ID I'm currently editing and the main category page id. The API script then sends back the matching sub-categories as JSON. JS again then loops through all checkboxes, and de-activates and hides all non-matching choices.

    It works, but it's not perfect*. I wish PW had an in-built way to handle such scenarios.

    As always, YMMV.

    * There's a split second on page load where you see all sub-categories. And also, when you select again the same main-category (without having saved the page), if you made selections previously, they're being erased. The first (minor) issue could probably easy be prevented if you set your subcat field to closed. You could first do your JS-syncing and at the end make them visible with Inputfields.open. Speaking of Inputfields JS API... I found that Inputfields.reload() didn't work here in my example (I tried several types of fields). Pity - would have made such a thing much easier...


    • Like 1
    • Thanks 1
  13. @MilenKo Finally managed to put together a quick setup.

    Good news is that it works (with a hook), but it's not ideal UX-wise.

    In order for the sub-categories to switch, the page must be saved first. Only then do you see the valid options.

    Perhaps this can be optimized with the new JS field API, but I haven't tried it yet.

    On the other hand - if the number of main and sub-categories won't change constantly, you could simply create four sets of page-reference fields (one for each main category), and work with field dependencies (show only if ___). But that won't scale, and if your above example is just a simplified example, you'll get into maintenance hell sooner or later.

  14. 6 hours ago, BFD Calendar said:

    The index page has 310 views while five font pages have over 1800 views, while they're not even viewed in a way.

    I don't understand that sentence...

    Anyway, there's quite a few things you can do:

    • Use caching (.htaccess)
    • Use gzip compression (.htaccess)
    • Use a Service Worker and the Cache API to store font-files in the user's cache (JS)
    • Look for an alternative on Google Fonts. Perhaps they have the exact same font there, or a font that looks very similar. Use Google's servers (CDN) instead of your own resources.
  15. I'm not sure I understand the question, but I guess by "folder" you mean "a URL structure that doesn't exist neither as physical folder on disk nor in the PW page-tree". If that's what you're after, you can enable URL segments in home (page id 1), and then go from there. There are plenty of forum threads with examples. It's really simple and straightforward (no need to mess around with .htaccess rewrite rules etc.)



    • Like 1
  16. This works for me here:

    if ( count(page()->images) ) {
        echo "yay!";

    ProcessWire: 3.0.148
    PHP: 7.3.13
    Webserver: Apache/2.4.35 (Win64) OpenSSL/1.1.1b
    MySQL: 5.7.24

    but you don't even need count... this works as well:

    if ( page()->images) {
        echo "yay!";

    and if you really need the number of images, this works too (always has):

    $imageCount = page()->images->count()


    • Like 1
    • Do you need this in the frontend or backend?
    • Do you want to detect when a logged-in user switches language in the backend?
    • Do you want to detect when a user in the frontend switches from page language A to page language version B?

    You could try and keep track of $user->language->title changes or something similar, with cookies/session.

    The code instance you linked to seems indeed to be the only instance in the entire PW core. I don't think it does anything useful right now as is, nor do I think it is really hookable.


  • Create New...