Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Posts posted by teppo

  1. On 10/26/2020 at 10:46 PM, MoritzLost said:

    Those modules are way, way overkill for 99% of sites. Just looking at the screenshot in the Seo Maestro thread, all those options would confuse most of my clients. Who really wants or needs to manually edit the change frequency of a single page? Some of those options should also be generated automatically (Locale, based on the current language) or set globally (Site Name, for example). I get that you can control which fields to show and that it's kind of a framework which you can use for all kinds of sites. But in my experience, showing five screens of SEO settings on every page is the best way to get clients/editors to be scared of them and never use them.

    To be fair I believe we're on the same page here, except for one detail: those modules — Seo Maestro and Markup Metadata — are actually two very different solutions. In fact what you're describing is exactly why we use Markup Metadata by default in our projects: there's no GUI, and a big part of the markup is based on globally defined values or values from (pre-existing) page fields. It's just a markup module for handling the repetitive task of rendering a standard set of metadata elements, correctly and consistently, from project to project.

    That being said, in my experience some people prefer a more complex approach, either because they actually need it or because they think they do — and if a client was specifically requesting feature set similar to that of Yoast, a solution such as Seo Maestro might be just what you need to convince them that they don't need to go with WordPress just for that 😉

    • Like 4

  2. On 10/26/2020 at 5:59 PM, DrQuincy said:

    As a default in PW I add a SEO fieldset with optional meta title, description and H1 (title property from PW is used for meta title and h1 if these fields are not set) with some simple guidelines in the notes on how to fill them out.

    Same. I also tend to bundle these with fields for open graph metadata, option to override page title separately, and whatnot.

    You might want to check out module solutions if you haven't yet. Seo Maestro is a neat one, and MarkupMetadata is what we use for our web projects (though latter one doesn't provide a GUI for content editors, it's just for generating proper markup).

    On 10/26/2020 at 5:59 PM, DrQuincy said:

    I understand Yoast has some additional tools that advise on the actual body content (word count, paragraph length, etc). Obviously PW doesn't have this. To be honest, I have always thought things like that are really for making poor SEO become mediocre and they can't do the job of a human and create excellent SEO. Also one of the things I love about PW is that I can let clients build pages on a modular basis (using repeater matrices) as opposed to a single body HTML field and so I do not even know that such tools could effectively evaluate such content since it is assembled dynamically.

    My experience is similar: the content analyzing features of Yoast have never been particularly useful for me, in part because I've mostly worked on non-English sites where they don't seem to work so well. Also these reports seem to — at best — provide a rough estimate of how good your content might be, and (in my opinion) there are better tools for that. If your clients are often interested in doing "hardcore SEO", I'd definitely dig into external tools and see if there are some that you can recommend instead.

    Yoast has some nice features for working around WP's shortcomings, and I've found their "helper" tools (such as breadcrumb creation) pretty handy in the past. ProcessWire, on the other hand, makes things like breadcrumbs and canonical links trivial, and the structure is often so straightforward that you don't need to do a whole lot to make your site "SEO friendly".

    One thing to note is that Yoast actually does handle "modular pages" relatively well. Last I checked it required a separate plugin and only worked if your content was all visible in the editor, though. My understanding is that it just mashes it all together and then does its magic. Crude perhaps, but in many (if not most) cases this provides decent results 🙂

    • Like 3

  3. 27 minutes ago, kongondo said:

    Just tried. These cannot find the domain. They do find the sub-domains though.

    I'm chatting with namecheap. Let's see what they have to say.

    Hope they can help. If I got this right and the name server doesn't know the domain, this definitely sounds like a DNS issue. Though I may have misunderstood what you meant here.

    Just for the record what I meant by "Linode changing something" was just whether they might've changed the IP of the server, or something along those lines. Though if your other domains point to the same domain and have identical DNS records, that'd be an unlikely scenario.

    Anyway, this is very much just guesswork. If you don't get answers from namecheap but can share the domain name with us (or via PM), I could also take a look. Sometimes two pairs of eyes see more than one, etc. 🙂

  4. Most likely a silly question, but are you certain that the DNS records point to the correct address? Linode didn't change anything regarding these when they upgraded the server?

    If you check the records for this site manually (host -t A www.yourdomain.com, or nslookup -type=A www.yourdomain.com if you're on Windows), are they correct?

    Also: if you try the domain with a service like https://isitup.org/, does it work there? Just trying to figure out if it's an issue with your environment, or a more widespread thing 🙂

    • Like 1

  5. 9 minutes ago, picarica said:

    is there some option to flush old unused pics ?

    See the API post: 

    $image->pim2Load('wm')->removePimVariations() should do it, though you'd want to make sure that you don't leave this code in place longer than you need it (probably won't do a lot of harm, but it'd be pointless). I'm not sure if pim stores the images as regular variations, but if it does, you can also remove them by hand from the admin (edit the image and you should see variations somewhere in the GUI).

    I wouldn't worry too much about those old variations, though. Most likely you haven't generated so many of them that they'd take considerable space on the disk, and anyone opening them accidentally is also quite unlikely.

    • Like 1

  6. 45 minutes ago, picarica said:

    just tried $position='center' and $position='C', it doesnt seem like updating 😕 but why no errors or logs

    It seems to me (based on a very quick glance at the code) that both 'center' and 'c' are acceptable. Could you try setting it to 'center', then removing the generated image variation, and then loading the page again? My initial guess would be that the image is not changing since you're getting the previously created image back.

    Alternatively you can provide a different prefix value for pim2Load; something like pim2Load('wm2', [...]).

  7. To my best knowledge there's no easy way around this, except for not using the MultiPHP Manager in the first place... which might not be an option for your use case 🙂

    Some (possibly awful) ideas you could try:

    • Perhaps you could disable overriding PHP settings via htaccess on your dev server by defining more restrictive AllowOverride option in your virtual host? AllowOverride also works on a per-directory basis, so I believe you could disable these for this particular site only, in case you're hosting multiple sites on this environment.
    • How 'bout manually creating matching directory (/var/cpanel/php/sessions/ea-php72) on your dev server for storing sessions for this site?
    • It sounds to me like cPanel will make these modifications automatically. If it also remembers all values somehow (in database or some other config file) and then repopulates them on first request or something, perhaps you could manage just one version and let cPanel work it's magic automatically after deployment? Not sure if that's exactly how it works, and whether that's feasible in the first place also depends on what your dev/deployment workflow looks like... 🙂

    It'd be great if the folks at cPanel provided some way to decide where specifically these rules should go, but I guess that'd be a new feature request. Based on some googling it also looks like when they introduced the automatic modification part, they broke quite a few sites in the process. Not cool 🙄

    • Like 1

  8. Hey @joshua! Would you consider making this module installable via Composer?

    Technically this just requires a) adding a composer.json file where you specify project name, type ("pw-module"), and a dependency for installer plugin (preferably wireframe-framework/processwire-composer-installer), b) adding the project to packagist.org, and c) (optional, but recommended) setting up a webhook between GitHub and Packagist so that new releases become automatically available at Packagist (or giving Packagist the permission to handle this step).

    Here's an example composer.json from ProcessRedirects: https://github.com/apeisa/ProcessRedirects/blob/master/composer.json.

    Thanks in advance for considering this 🙂

    • Like 3

  9. I've had similar issues with Chrome (probably because that's the only browser I really use) many times over, though not sure if it's ever occurred with a ProcessWire site. From your description it seems quite likely that "something" in the browser itself gets stuck — I'm really not an expert here, but again it's happened a number of times for me, and only things that seem to help are a) waiting until it goes away (could be a long wait), or b) rebooting the browser.

    Pretty much just guessing here, but I'd suspect either some sort of JavaScript issue, a problem with a browser extension, some sort of network issue, or perhaps some piece of software installed locally. Ad blockers, for an example, are known to cause browsers to freeze, but typically only under some very specific conditions. (I've also helped clients debug some pretty weird issues cause by firewalls or virus scanners.)

    Probably not very helpful, sorry; if you can see any console warnings or such please let us know, that'd make debugging much easier 🙂

  10. I've had this issue on some occasions, though for me it's been more like "sometimes it works, sometimes it doesn't", rather than not working for some pages at all. Never could really debug what it was all about since these have been very random occurrences, but your guess about some sort of JavaScript conflict seems reasonable.

    I've not had a proper look "under the hood" but I believe that front-end editing works by essentially duplicating your content behind the scenes, so the issue could also have something to do with the structure of the pages in question. Do they differ somehow from the pages where this works as expected?

    When this happens, do you see any JS errors in the dev tools console? Do you have any particular JS libraries etc. loaded — jQuery and the likes? Also, which editing option are you using and which version of ProcessWire do you have installed?

    Sorry for the load of questions — I'm really not sure what could cause this, but perhaps we can at least narrow it down a bit 🙂

  11. Hey @bernhard! Thanks for reporting this.

    The latter problem is connected to the first one. Config screen asks the main module for the paths object, but due to a recent change this wasn't available. This is fixed now in the latest version of the module (0.17.1).

    • Thanks 1

  12. 1 hour ago, DrQuincy said:

    When you say “The doc (HTML) part of a fairly complex site I recently finished takes ~600-900ms” are you referring to the PHP execution time, as I am, or is this how long it takes for your browser to get the whole of the HTML document?

    This was from browser, so some network latency was included in that number. If I check the render time from the site, it's roughly the same, average being somewhere around 1s. I'm looking at the numbers provided by Tracy for a logged in user, so the higher time is explained by the stuff that doesn't apply to visitors (admin tools, Tracy itself, etc.)

    Don't have a great testing setup for non-cached visitor execution time 🙂

    1 hour ago, DrQuincy said:

    Will ProCache automatically disable itself for POST requests (e.g. contact forms)? If you have a template that, for example, polls a third party service using cURL, do you simple just disable ProCache for that template in the admin? Is it possible to try ProCache first or do you need to buy it?

    ProCache is always disabled on POST requests, GET variables and cookies that bypass cache are configurable.

    I tend to rely on just the normal invalidation cycle — I have reasonably short cache time configured, so I don't have to clear the cache manually / programmatically very often. Depends on the use case, of course; for most regular websites it really doesn't matter if the data is 15..30 minutes old, even if it's of the "rapidly updating" sort. It's easy to flush the cache with PHP, though: see https://processwire.com/api/ref/pro-cache/ for more details.

    As for the "try before you buy" part, I don't actually know if there's such an option. You could try contacting Ryan 🙂

    • Like 1

  13. 10 hours ago, DrQuincy said:

    So the PHP execution time ranges from 180ms to 350ms when using OpCache on the exact environment I will use for the live site. [...]

    I have a somewhat blinkered view here since I don't use off-the-shelf CMSs very often but is this pretty much the execution time you would expect for a small brochure site? [...]

    I do have one old WordPress site I look after and that seems to average 110ms execution time under the same environment — but I don't think it's a fair comparison since it's a five page site with no plugins installed. From other WP sites I have seen once you add plugins — which you need to do anything half-decent — then it really starts slowing down.

    Those numbers seem pretty normal. Just for comparison:

    • The doc (HTML) part of a fairly complex site I recently finished takes ~600-900ms when I'm bypassing ProCache. This is from browser, though, so could be a bit different from what you're measuring (not sure how exactly you got these numbers). With ProCache I'm getting consistent < 100ms. ProCache serves content directly from disk (via Apache), bypassing PHP and database, so it's naturally quite a bit faster.
    • Took a cursory glance at a couple of (also relatively complex) WordPress site that I know for a fact are well built and hosted on pretty powerful hardware, and load times for these were somewhere between ~1.5-3 seconds uncached, 200-400ms cached (static cache using nginx, I believe).

    In my opinion 1-3 seconds would still be "pretty good" for just about any ProcessWire or WordPress site without proper caching. Anything below 4-5 seconds is in the "pretty normal" range, while 5-10 seconds is just plain wrong (but sadly not that rare). 5+ seconds is usually a sign of really bad hosting, or really bad implementation 🙂

    Might be worth noting that, in my experience at least, PHP is rarely the real bottleneck: if the server returns the markup in a few hundred milliseconds but then there's blocking JavaScript, CSS, or perhaps a large image that takes hundreds of milliseconds to seconds at worst to finish loading and/or executing, it would be better to focus on that. Just saying; developers (including yours truly) have a tendency to focus way too much on shaving milliseconds off one end, even if at the other end it might be possible to shave off seconds 😛

    10 hours ago, DrQuincy said:

    Template cache seems to makes little-to-no difference.

    Template cache loads the page from the disk and doesn't execute any of your markup generating code, but it still has to go through ProcessWire, so there's definitely some overhead there compared to ProCache. How beneficial template cache is (in comparison to non-cached site) depends a lot on how complex the site is and how well it's already optimized.

    10 hours ago, DrQuincy said:

    Would ProCache make a difference or is this more for high-traffic sites (this site would not be high traffic)? Is ProCache as low maintenance as OpCache or is it easy to end up with stale data in certain contexts?

    Kind of covered this already, but to reiterate: it would definitely make a difference.

    ProCache is usually low maintenance, but this depends a bit on how your content is generated — such as whether it's all from stored with/in ProcessWire, or if you have parts that are loaded (especially with PHP) from other sources. Typically ProCache gets flushed based on predefined rules when pages are saved, so if your content isn't stored on pages, that could be a potential issue. In which case you may even need to programmatically flush it (via cron or some other method).

    You can configure the preferred lifespan, so technically you can make ProCache stick to cached data almost indefinitely, and thus ProcessWire/ProCache will very rarely need to regenerate it. Though, again, in my opinion this is something that folks tend to pay too much attention to: if your typical page render (non-cached) takes 1-3 seconds at most and your cache hit ratio is 99+%, cache warming has so little actual effect that it's (in my opinion) mostly just wasted time and effort 🙂

    • Like 4

  14. 2 hours ago, adrian said:

    Can you explain why you think your way would actually be better?

    It seems to me that the only case when we'd want mb_decode_mimeheader() to run is when the header is encoded, in which case it's always base64 or quoted-printable (AFAIK), and these are always (US-)ASCII. I'm not aware of any notable issues with current approach, though; most likely the "worst case" is that you'd pass something that doesn't need decoding to mb_decode_mimeheader(), and it seems like a major coincidence if this operation actually ends up scrambling the data.

    Anyway, the gist is that if we're looking for ASCII strings, it's better to be specific 🙂

    • Like 1

  15. Moderator note: Modules/Plugins section of the support forum is reserved for dedicated third party module support threads. For the time being I'm moving this thread to the General Support area of the forum. Note that Padloper also has a separate support area on the forum, accessible for existing users with a valid license; you should post Padloper questions to said support area to make sure that they get noticed by the module author(s).

    As for your question: the Padloper website mentions PayPal and Stripe payment modules. If there are other payment methods, at least they are not listed on the site. As for shipping methods, there's not much information out there — I'd definitely recommend posting this question to the Padloper support forum.

    If you have a valid license but don't have access to said support forum, please contact the module author.

  16. There are a few problems with that code:

    1. Switching $magazine to $page doesn't actually help at all. $page is not defined here either. As I mentioned in my previous post, you're in function context, and in that context you only have access to a) function arguments ($event), b) variables you've made accessible with "use" (currently there are none), and c) global functions. $page is none of those.
    2. Another issue is that there's no savedPageOrField method for Inputfield, so Inputfield(...)::savedPageOrField is never triggered — what you're looking for is Pages::savedPageOrField.
    3. From your post I assume that you've put this code in the template file, i.e. /site/templates/your_template_name.php? If so, the hook only applies to when you're viewing a page using that template, i.e. it has no effect in Admin. I'm not sure if that's what you really intended, but I'd assume not — more likely you'd want to put this hook somewhere it gets added in the admin as well, so perhaps /site/init.php or /site/ready.php.

    ... and, finally, even if your code did work, you would've likely ran into an infinite loop: if you hook into page save and save the page, that'll trigger the page save hook, which will then save the page and trigger the page save hook again, which... you know the drill. Pages::saveReady() is often better method to hook, as you can just modify the page values, and they'll get saved soon after (you don't have to call save in your hook) 🙂

    In this case something along these lines could work:

    // hook into Pages::saveReady
    $wire->addHookafter('Pages::saveReady', function($event) {
        // get current Page object — in this case this is the first argument for the $event object
        $page = $event->arguments[0];
        // bail out early if current Page *doesn't* have the field we're interested in
        if (!$page->template->hasField('snipcart_item_image')) return;
        // ... and also bail out early if the snipcart_item_image field hasn't changed
        if (!$page->isChanged('snipcart_item_image')) return;
        // now that we know that snipcart_item_image has changed, set a custom value to another field
        $page->set('imagecolorat', 'YOUR_NEW_VALUE_HERE');
        // finally, populate our modified $page object back to the event
        $event->arguments(0, $page);

    Note: written in browser and not properly tested.

    • Like 7

  17. 1 hour ago, fruid said:

    Not sure what's the difference between…

    $this->addHookAfter(…) // is that inside my own function?

    I know you mentioned docs being confusing, but I'd still suggest taking another look at them — specifically this part, as it explains this pretty clearly: https://processwire.com/docs/modules/hooks/#defining-hooks.

    To simplify things a bit...

    • the key difference between the first two is that $wire->addHookAfter() works outside classes, while $this->addHookAfter() is how you'd usually attach the hook when you're in a method of a class (i.e. while you're working on a module of your own).
    • $page->addHookAfter() attaches the hook to the current Page object (which $page typically refers to), not all Page objects in general. Important distinction in case you're working with multiple pages and don't want your hook to run for all of them.

    Not sure if that explains it any better than the docs, but again the biggest difference is just the context in which you're defining the hook (use $wire when in template files, /site/init.php, /site/ready.php, /site/admin.php, etc. and $this when you're within a method in a class) and whether you want your hook to apply to a single object ($page, $pages, or a specific item such as $mypage where $mypage = $pages->get('some_selector')) or all items of a specific type.

    1 hour ago, fruid said:

    But creating your own hooable method? Why would you want to make a method hookable if it's your own creation? Can't you just change the method without touching the core?

    Most commonly you'll need your own hookable methods when you're, say, developing a reusable module. Instead of tweaking the module on a per-site basis, you'll likely want to keep it as-is and rather make some minor modifications by hooking into it's methods. This way you can easily update the module without having to redo or work around your modifications every single time.

    On some edge cases you might have a utility function on the site, and then depending on some other event you may want to say that "at this specific instance the method should do something else", so you'd want to make it hookable. Can't think of many cases where that'd make sense, though.

    1 hour ago, fruid said:

    Here's what I need particularly:

    $wire->addHookAfter('Inputfield(name=snipcart_item_image)::changed', function($event) { 
        $magazine->setAndSave('imagecolorat', '123456'); // just to test

    So once I figured out how hooks work I might ditch the said if-conditions and use hooks instead.

    Thanks for help!

    I'm not sure what you're getting at here. Is this is just an example of what you're doing at the moment? Either way your hook looks fine to me.

    Should've read this more carefully. You're accessing $magazine object here, but if this is your entire code, that won't work — you're not defining $magazine variable anywhere.

    Since you're in function scope, you only have access to the arguments passed to that function, variables you've passed in with "use" language construct, and global functions. Even if $magazine exists outside your function, you'd have to pass it in with function($event) use ($magazine) { ... }

    That being said, what you're probably looking for is something like Pages::savedPageOrField(). If you take a look at that docs page, there are code examples dealing with changes etc. Or, alternatively, Pages::savePageOrFieldReady(), which occurs right before the page/field is saved (which means that you can modify the values right before they are saved to the database).

    ... and please let me know if I missed your point here 🙂

    • Like 3

  18. 36 minutes ago, dragan said:

    There's also two buttons there in the bottom-right for downloading all files as CSV or ZIP. When you get the translated JSON files back, you can upload them here as well, and replace the old ones.

    Definitely this one. Unless the translator has very specific requirements (i.e. they intend to import the file into some sort of app), CSV export/import is by far the most effortless approach for all parties involved 🙂

    Edit: purely out of curiosity tried to run a conversion from ProcessWire's JSON format to other formats (such as PO, the format typically used by WP translation plugins — .wordpress is a new one for me, so not sure if they meant this, or if that's just something I haven't come across yet) using https://localise.biz/free/converter, and it seemed to work fine. Though would need to test properly to make sure that the process is also easy to reverse, and the result can be imported properly.

    Conversion done this way would be a bit of a pain, though, since you'd have to convert each file one by one. Unless you happen to have most of your translations in a single file or something like that. Seems like there are decent libraries for this available as well, so a conversion tool would be relatively simple to set up, but again for a one-off case that'd be a lot of extra work 😅

  19. On 10/8/2020 at 12:00 PM, Tyssen said:

    ... and then at the end of the process, by way of validation, Snipcart returns to your page to check that a button exists with the price and ID that you added to your order. You can either have hidden buttons in the page with the data-attributes that match the order, or you can instruct Snipcart to use a JSON file which contains all that info instead.

    This is why changing prices on the fly doesn't work, because the price that you've arrived at by selecting different combinations of options, has to exist somewhere that the Snipcart validator can access. So to answer another of your other questions, yes I do need to be able to present several thousand different combinations to the Snipcart validator, not a site visitor, otherwise the order won't validate.

    It seems that whatever route you take, the memory / performance optimization approach would eventually run off the cliff. I mean... it's just not scalable. So, trying to think outside the box for a moment:

    • When you say that "Snipcart returns to your page", what does that mean specifically? Does it perform a GET/POST request against the page?
    • If so, is there any chance that it would provide enough information for you with that request to actually generate a smaller subset of prices used in that specific order?

    This would technically shift the responsibility for the validation step from Snipcart to your site, but it also seems like something that could scale much better 🙂

    Anyway, just throwing in ideas.

  20. On 10/8/2020 at 8:32 AM, adrian said:

    Also, does anyone else have any thoughts on a better way to do this? @teppo - you must deal with these sorts of characters in email headers - any tips?

    Sorry, this is not my area of expertise either. Used to deal with that a lot, but thankfully no longer — switching all email sending features to use services like Mailgun has made my life so much better 😉

    Anyway, your solution seems reasonable. Only thing I'm wondering is whether it'd be better to specifically check for ASCII encoding with mb_detect_encoding(), instead of "not UTF-8"? It's a really small nuance, though.

    • Like 1
  • Create New...