-
Posts
366 -
Joined
-
Last visited
-
Days Won
18
Everything posted by MoritzLost
-
How are you overriding the selectable pages with contextual options? I have recently used a similar approach to have a different set of categories selectable depending on a page's position in the page tree. The error you mentioned will definitely occur if you aren't hooking getSelectablePages during the request that saves the page. For reference, here is my code that is working for me (this is for a multisite, so $homepage is the current multisite instance): $wire->addHookAfter('InputfieldPage::getSelectablePages', function (HookEvent $event) { if ($event->object->hasField->name === 'category') { $currentPage = $event->arguments('page'); $homepage = $currentPage->rootParent; $categories = $homepage->get('template=category-index, include=hidden')->children('template=category'); $event->return = $categories; } });
-
Another perspective: The API acts as the model, with the $page / Page class giving you access to page data through a generic interface. By default, the PHP template file for the current content type / template acts as Controller and View, though you can easily seperate Controller and View by using, for example, a frontend templating library like twig. That's close to my usual setup: View: Twig acts as a View layer, since it gets pretty privileged access to API variables, you can get anything done you'd usually do in a PHP View class. Controller: The PHP template file acts as the Controller, for example processing input and rendering the correct Twig template. Usually, I just keep the template logic procedural, because ProcessWire already does a lot of work in determining the correct template file and setting up API variables. Though you could also use a custom Controller class and just use the template file to instantiate it. Model: As mentioned above, the $page API variable is already a kind of generic model for your data, and for sites that are mostly brochure sites / presentational in nature, this is really all you need. But if you want to go further, you can create custom page classes by extending the Page class and set your template to use that, this way you can make your model as smart as it need to be. I have written two tutorials on how to integrate Twig if you're interested ? Part 1: Extendible template structures Part 2: Custom functionality and integrations
- 6 replies
-
- 10
-
Only Ryan can answer that, but I suppose checking the timestamp every time would have a performance impact (though a tiny one). Another perspective is that checking only the template file might result in unexpected results if you modify an included file, because then the cache wouldn't be cleared. For example, if you're using Twig, the PHP template file isn't modified very often, so it wouldn't work. I actually prefer it this way, that is if the system makes few assumptions. I've only used WireCache because as far as I could tell when I was looking for something similar, it's the newer and better version of MarkupCache. Also, WireCache is already integrated into ProcessWire and ready to go. You can cache any kind of data (array, PageArrays etc) with it, while MarkupCache (as far as I'm aware) can only store strings. I'm using the WireCache to cache render-intensive HTML output and queried data on pages where I can't use template cache for various reasons, and it has a large impact, reducing page generation time on a particularly heavy page from ~2 seconds down to a couple milliseconds. Have a look at the $cache API documentation. Just out of curiosity, what are your tested page generation times for cached / uncached pages? Because in my experience, a simple ProcessWire site will already be so fast out of the box that caching won't do anything, because most of the generation time comes from network overhead and hard drive speed ...
-
ProcessWire is pretty fast on it's own, so if you only have some PHP templates doing basic output there won't be much of a difference. Though you can probably see the difference by recording page load times in Chrome for requests with and without the cache, respectively. Though you need to do this in a private browser window where you aren't logged in, otherwise ProcessWire won't use the cache, as per your template cache settings. Two things that I have noticed to slow page loads down are "heavy" queries, e.g. if you query dozens of pages for data, especially if you do it in separate database queries. I'm also using Twig for output, which does come with a small time increase, especially while auto reload is active. That is correct, ProcessWire doesn't check timestamps of template files to decide when to serve from the cache. You can manually clear the entire template cache by going to Modules -> Configure -> PageRender and checking the checkbox there. Or use my new Cache Control module, which can clear the template cache alongside a couple of others in one click ?
-
module Cache Control - Clear all your caches in one place
MoritzLost replied to MoritzLost's topic in Modules/Plugins
Good thing I marked this as a beta release, because I'm still finding a lot of bugs ? I just pushed version 0.4.0. The module is now no longer autoloaded, because it's not really required anymore. This release splits the module into two seperate Modules: ProcessCacheControl and CacheControlTools. They are still installed alongside each other, and ProcessCacheControl will install CacheControlTools during install. For existing installs, you might have to reinstall the module, or install CacheControlTools manually after the upgrade. Also, the asset version management is now part of CacheControlTools, so the code has to be adjusted: // version 0.3.0 and below $CacheControlTools = $modules->get('ProcessCacheControl')->getTools(); // version 0.4.0 $CacheControlTools = $modules->get('CacheControlTools'); A bit of background for the split: I learned something new about the permission system. If a Process module defines a permission, the current user needs that permission not only to access the Process page in the admin, but also for the module to even be instantiated at all. Because of this, the asset management system didn't work for guest users. By splitting the repository into two modules, I can still have strict access control for the Process part of the module, while allowing the tools module to be instantiated even during requests from guest users. Anyway, the module is now, let's say, nearing stability. I'd like to publish a stable release soon, so let me know if you find any more bugs! -
module Cache Control - Clear all your caches in one place
MoritzLost replied to MoritzLost's topic in Modules/Plugins
Thanks everyone for the feedback! I just pushed a new version that resolves a bug that prevented installs on PHP versions <7.4. PHP 7.1, 7.2 and 7.3 should work now! Thanks @Mikie for your help! I have also found some documentation in the ProCache store page. I've added an option to clear the ProCache now as well, though I haven't included it in the new version because I can't test it out. Would you mind giving it a go? The ProCache integration is in the procache branch on Github (download link). If it's working as intended, it should add a new option to clear the ProCache in the module configuration, including a note saying whether ProCache is installed. If selected, the 'Clear all' action should use $procache->clearAll and log the result. It would also be good to know if non-superusers can use this, or if it's limited to the superuser. Thanks! The module also appears to have been approved to the module directory, so it can now be downloaded directly through the backend using the module name ProcessCacheControl. Thanks @adrian (or is Ryan in charge of the module directory? ?) -
module Cache Control - Clear all your caches in one place
MoritzLost replied to MoritzLost's topic in Modules/Plugins
Hi @adrian, I didn't know that module existed, it's not in the directory, is it? Because I looked if something like this already existed and didn't find anything. Might've saved me some trouble ? That said, I had a quick look, here's what I found in comparing both modules: ClearCacheAdmin exposes more options through the setup page, so there are available to everyone, whereas ProcessCacheControl has it's options in the module configuration, so they are only available to the developer. ClearCacheAdmin has multiple links (and multiple options) on the process page, giving the user more fine control, whereas ProcessCacheControl goes for a simpler interface and bundles all those actions into one (configurable) action with a single button. ProcessCacheControl can delete $cache entries without an expiry date, which ClearCacheAdmin doesn't as far as I can tell. ProcessCacheControl also lets you configure caches to be deleted by namespace, whereas ClearCacheAdmin offers each cache entry to be deleted individually (I think this has mostly to do with the module being a bit older, I believe $cache didn't have all those options back then). The largest difference is my concept of "cache actions", which ClearCacheAdmin doesn't have. I'm not sure how useful that will actually be yet. I think if I can expand on the actions available by default, it will be pretty handy. With ProcessCacheControl, you can add custom cache actions / profiles through the API, that may be useful depending on the use case. Adding to that, ProcessCacheControl has permission checks on a per-action basis. ProcessCacheControl can be modified and executed through the API. In particular, you can modify the default action and execute any action programmatically. -
module Cache Control - Clear all your caches in one place
MoritzLost posted a topic in Modules/Plugins
Process Cache Control This module provides a simple solution to clearing all your cache layers at once, and an extensible interface to perform various cache-related actions. The simple motivation behind this module was that I was tired of manually clearing caches in several places after deploying a change on a live site. The basic purpose of this module is a simple Clear all caches link in the Setup menu which clears out all caches, no matter where they hide. You can customize what exactly the module does through it's configuration menu: Expire or delete all cache entries in the database, or selectively clear caches by namespace ($cache API) Clear the the template render cache. Clear out specific folders inside your site's cache directory (/site/assets/cache) Clear the ProCache page render cache (if your site is using ProCache) Refresh version strings for static assets to bust client-side browser caches (this requires some setup, see the full documentation for details). This is the basic function of the module. However, you can also add different cache management action through the API and execute them through the module's interface. For this advanced usage, the module provides: An interface to see all available cache actions and execute them. A system log and logging output on the module page to see verify what the module is doing. A CacheControlTools class with utility functions to clear out different caches. An API to add cache actions, execute them programmatically and even modify the default action. Permission management, allowing you granular control over which user roles can execute which actions. The complete documentation can be found in the module's README. Plans for improvements If there is some interest in this, I plan to expand this to a more general cache management solution. I particular, I would like to add additional cache actions. Some ideas that came to mind: Warming up the template render cache for publicly accessible pages. Removing all active user sessions. Let me know if you have more suggestions! Links https://github.com/MoritzLost/ProcessCacheControl ProcessCacheControl in the Module directory CHANGELOG in the repository Screenshots -
Image count translations (1 image, 2 images, 3 images)?
MoritzLost replied to Roych's topic in Getting Started
That's actually an interesting question, because it's a flaw with ProcessWire's built-in internationalization function. A general internationalization API will support multiple plural forms, because as in your language, there's more forms than singular or plural. ProcessWire's _n function for translating singular/plural forms simply checks if the item count is exactly one or more than one and return either the singular or the plural version of the translatable string (see the source code). This works fine for English, German and many other languages, but no in languages which have more than two forms for counting items. If you still want to use ProcessWire's translation API, you'll have to handle the different cases yourself, as suggested by @dragan (though I would still use the translation API instead of hardcoding the strings in the switch statement). However, a more "general" solution that would allow you to add different language versions of your site without changing the code would be to utilize the native Gettext implementation in PHP. This will be much more work though; as far as I'm aware there's no module that provides a gettext interface for ProcessWire, so you'll either have to write one yourself or upload translation files manually. BTW I know this is probably overkill for your current situation ? But this has been bugging me for a while, it would be nice to see native support for different plural forms in the ProcessWire translation API. Might be a good candidate for a new module ... For more reading, here's a good resource for I18N in PHP. -
Can't Get Rid of <p> </p> from $page->body
MoritzLost replied to prestoav's topic in General Support
Ok, in that case you can still filter out the unnecessary paragraph with the non-breaking space inside. Your str_replace function probably doesn't work because you are trying to replace the literal string . That won't work because the non-breaking space is (most likely) stored inside the database as a Unicode character, not as the HTML entity. Matching "<p> </p>" won't work either, because that is a regular space, not a non-breaking space. To replace the non-breaking space, you can use a regular expression and match the specific NO-BREAK SPACE Unicode character. This works for me: $string = "<p> </p>"; $string_unicode = html_entity_decode($string); // $string_unicode now contains a non-breaking space unicode character echo preg_replace('/<p>\x{00A0}<\/p>/u', 'Successfully replaced!', $string_unicode); // Successfully replaced! Make sure to include the u flag to treat the string as UTF-8. You could also modify this to match multiple non-breaking spaces or whatever CK Editor throws at you ? You can use that either to remove the superfluous paragraph in your template output, or write a little script to convert the text saved in the database (which is the cleaner way to go, if you ask me). -
Can't Get Rid of <p> </p> from $page->body
MoritzLost replied to prestoav's topic in General Support
For CK Editor fields, I always use the option to force pasting text as plain text. This strips any markup and formatting when pasting, it has prevented so many headaches for me ... It does mean that you can't paste formatted text even if you know what you're doing, but this way no weird formatting from Word can make it into the source code. You can activate this option through the Custom Config Options for the CK Editor field: forcePasteAsPlainText: true I'm not sure that will remove the extra non-breaking space (the paragraphs get added by CK Editor, so you only need to get rid of the NBSP), but it's worth a try and it does deal with most problems when pasting from Word. -
The machine names of system fields are always in english, so you need to access the description like this: $description = $download->Datei->description(); By the way, there's also a utility method for the file extension: $extension = $download->Datei->ext(); Also, if your Repeater only contains the file field, you could also get rid of the Repeater altogether and just allow multiple files on the "Datei" field. Then you could iterate it directly: foreach ($page->Datei as $file) { $url = $file->url(); $ext = $file->ext(); $description = $file->description(); // output download links here ... } Look up the Pagefile API documentation for a list of all available utility methods.
-
@LostKobrakai I don't think so, it appears the old name (getModuleConfigData) is simply an alias to the getConfig function now. See the source code of the Modules class. I tested it with the current dev version of ProcessWire, and the problem described above still occurs. Also, the source of getConfig pretty much only fetches the config from the correct database row, without merging in the default config (which I believe it should do as well) ...
-
I'm digging this thread out because I just came across the same problem in my module. I fixed it for now by merging the default config as suggested by @Robin S: $options = $this->modules->getModuleConfigData($this); $defaults = (new TextformatterPageTitleLinksConfig())->getDefaults(); $options = array_merge($defaults, $options); See the complete source code here. However, I agree that this should be handled by ProcessWire. I expected getModuleConfigData to return the final merged configuration, the method doesn't suggest that it only retrieves the config that has been saved to the database. This way the method creates a temporary dead zone between installing a module and saving the module configuration page, where errors can pop up in the meantime. @horst @kongondo By the way, saving the module config during installation may not be sufficient. In my case, I got an error when I upgraded my module to version 3.0.0 which included a new setting. So even though the module was already installed and the options had been saved before, the new version was missing the new configuration option as it wasn't included in the configuration returned by getModuleConfigData. So with this approach one may also need to hook into the upgrade process to make sure the configuration is saved after every update.
-
Twig would definitely be number one on my list, I've been using it for every project for a while and it feels waaaay cleaner than native PHP or Blades. See my tutorials (part one - part two) regarding the Twig integration ? Part two has some examples of connecting Twig to the ProcessWire API, it's a natural fit.
-
It mostly doesn't matter, you get the same API variables independent of the method. That said, I would suggest the following for readability: $this: Use only inside of classes extending \ProcessWire\Wire. It appears that $this is also available in template files as a reference to the current wire instance, but as you said, this is a somewhat confusing and unconventional usage given that you're not actually inside a class. $wire: Use inside regular PHP template files (or if you want to use a different API variable like $page, $pages, $input, et c. use that one directly). wire(): Use inside functions (not class methods) so you don't have to pass $wire or $this to the closure.
-
Checkout You Might Not Need jQuery for this, it will tell you some replacements for the most common jQuery operations! Here's a rough draft: function more(url) { window.location = url; } document.querySelectorAll('a').forEach( link => link.addEventListener('click', e => { e.preventDefault(); const href = e.currentTarget.href; document.body.classList.add('fade-out'); setTimeout(() => more(href), 500); }); ); Note that the callback to fadeOut in your code is "open" while the reload function is called "more", is that a mistake? Anyway, I corrected it. Note you have to add a CSS transition for the "fade-out" class yourself, check the link above for an easy example. Also make sure that the timeout (500ms in my example) matches the duration of the CSS transition. Cheers! Sidenote, if all you're doing is delaying the page reload by a couple hundred miliseconds to have the body fadeout, you might as well get rid of that, it only gets in the way of the user without any real benefit. But I don't know anything about your use-case, so I might be way off here!
-
My videos page is incorrectly linking to the wrong video :(
MoritzLost replied to OrganizedFellow's topic in General Support
I don't understand most of what's going on there, but the problem is probably that your $videos variable will always be the same page: $videos = $pages->get("template=videos"); Since you're using $pages->get this will always match the first page of template "video" on your site, independent of the current page. I don't know your tree structure, is the video a field of the current page, or a sub-page? In those cases you probably meant to use $page->get or $page->find. @Klenkes Beat me to it ^^ But you can normal selectors inside $pages->get, though the results may be unintuitive because it will always return the first page found. Just as a sidenote, check out the new Clipboard API, it's pretty cool. I'm using it here and it works well? -
The page ID is stored in an unsigned auto-incremented int column (you can find this in ProcessWire's install.sql file) in the pages table in MySQL, so the highest the IDs can go is 4294967295 - that should be plenty ? You can manually reset an auto-increment column in MySQL, but not lower than the highest value that exists in the table. I guess you could also manually change the column to a BIGINT, I haven't checked but I don't think it would break anything in ProcessWire. That said, is it really necessary to trash and rebuild all pages every time? I don't know what job advertisements database you have, but if you can get a unique ID or identifier for every job posting from it, you could store that identifier in a hidden field in the corresponding ProcessWire page and then selectively update the fields that have changed (or at least only rebuild the pages that have been changed). This way you also retain meaningful created/modified timestamps inside ProcessWire, so it seems like a better approach.
-
In this case I wouldn't want to have the overhead of having to set up an API endpoint and rendering AJAX-loaded content in JS, but use simple lazy loading. I.e. leave the src attribute of the images that are not initially visible out, and include a data-src attribute or similar instead, then dynamically programmatically replace the src attribute once the images get close to the viewport. Or, you know, use native lazy loading which has landed in Chrome and is a progressive enhancement so you don't need any JS at all, but can be polyfilled as well if you want to. Has pretty good browser support out of the box as well.
-
Your function isn't a method on a class, but you pass $this as the second argument, so the system looks for a method "apiFunction" on $this, which doesn't exist. If you pass null instead as the second argument, it will look for a function instead, it should work then: <?php namespace ProcessWire; wire()->addHookAfter('ProcessPageEdit::processInput', null, 'apiFunction'); function apiFunction($event) { die("testing"); }
-
One simple way to do this client side with normal fields would be to add an "Active languages" field to each Repeater Matrix type, and use that as a language activation switch. For example: Create a Page Reference field "active_languages" with the template "language" as selectable pages and the input field type set to checkboxes. Then add that field to each repeater matrix type. Finally, in the code that renders the matrix item, use something like this (code untested): // assuming the matrix field name is "sections" foreach ($page->sections as $section) { $if ($section->matches("active_language={$user->language->id}") { // render the field } } Now the editor can check which languages they want the section to be displayed in. Obviously you can also invert the logic and use the checkboxes to hide the section in a specific language. Of course, having this built in would simplify things a lot ?
-
textformatter Automatically link page titles
MoritzLost replied to MoritzLost's topic in Modules/Plugins
I just pushed a big update to the textformatter with version 3.0.0! I'm not sure if anyone is actually using this module, but at least for my own projects it has been useful, and it's good training for MySQL intricacies, so I'm still updating it when I think of new stuff to add ? With the updated version, it's now possible to overwrite any option from the module configuration when calling the module manually in your template code. That way, you can automatically link to pages of different templates in different contexts on the same site, or customize the behaviour of the textformatter any way you want! Also, there's now an option to toggle case insensitive behaviour, if needed, as well as an option to force case sensitive queries for case insensitive database collations. The changelog is now included in the repository itself, I also updated the readme with instructions on how to call the module manually. Make sure to check out the full changelog for version 3.0.0, as well as the readme section on manual usage. Let me know if the module is or isn't working for you, and any suggestions you have on how to improve it! -
Handling special cases: The elusive navigation menu override
MoritzLost replied to MoritzLost's topic in Tutorials
@teppo @wbmnfktr Wow this discussion derailed a bit, but here we are ^^ Teppo, thanks for the pointers regarding target blank, I didn't really have that on my radar. Makes perfect sense though, I'll definitely reconsider my current approach! Even more off-topic, but I feel a bit smug now about having isolated that logic into a twig template in my current project, so now I can change it in a central place: {# blocks/link-start.twig #} {#- # Renders a start tag for a link. The link will automatically have target="_blank" # and rel="noopener" if the link leads to an external domain. # # @var string url The target (href) for the URL. # @var bool is_download Should the link be marked as a download (for direct file downloads)? # @var array classes Optional classes for the anchor. -#} <a href="{{ url }}" {%- if is_download is defined and is_download %} download{% endif -%} {%- if classes is defined and classes is not empty %} class="{{ classes|join(' ')|trim }}"{% endif -%} {%- if isExternalUrl(url) %} target="_blank" rel="noopener"{% endif %}> I'll just get rid of the last part ? Thanks to everyone participating in the discussion! I think it's great how different perspectives come together that provide everyone new insights & best practices ... -
I think it's great! I've already incorporated it into my current project, and it has a sizable performance impact on Chrome without any real work on my part. I always thought removing the src attribute and setting it with JavaScript was an antipattern, as anyone with JavaScript disabled wouldn't see any images at all, so it's good to see native support for lazy loading. It's progressive enhancement, so you don't need to have support from all browsers. Support doesn't look half bad though, even though only Chrome supports it at the moment (see caniuse), that's already a sizable chunk of the population. Also, caniuse currently lists Chrome on Android, which is huge (36 %) as not supporting it, I'm not sure that's correct. According to the Chrome Platform Status page, native lazyload is already supported in Chrome on Android. That would get the feature to >50 % browser support already. On current Android versions, all WebViews are provided by Chrome, so it will work in all in-app browsers on Android as well. Also, you can very easily polyfill the native lazyload with JavaScript (see article above), so everyone will profit from this without much of a downside!