-
Posts
366 -
Joined
-
Last visited
-
Days Won
18
Everything posted by MoritzLost
-
@Juergen You can disable formatting from anywhere, why not use $this->of(false) inside your method? If you're worried about changing the output formatting setting unexpectedly, you can save the current setting first and then restore it after you've received the unformatted value or done whatever you want inside your method. $of = $this->of(); $this->of(false); // custom code here ... $this->of($of); Or is this still not what you intended?
-
Not sure if there is a hook that will exclude trashed pages directly, but you can very easily check status changes inside the Pages::saved hook, and abort the hook if the page is / was trashed. Use $page->statusPrevious to get the previous status, then you can compare it with the current status. See this hook in my TrelloWire module, which reacts to status changes. In this case I check both the current and previous status, because I'm only interested in the changes. Note that statusPrevious will be null if the status hasn't changed. If you only care about whether the page is currently in the trash, you can just use $page->isTrash().
-
@ThierryGD I have never used this module, so no idea. If you don't need something custom-built or super specific, I recommend Form Builder for ProcessWire: https://processwire.com/store/form-builder/ It's a commercial module, but absolutely worth it. Comes with automatic mail notification to site administrators for new form requests, as well as an auto-responders. For actual mail delivery the best option is to use a mail account and send mail through SMTP. There's the WireMail SMTP module for that.
-
ProcessWire blocks direct access to PHP files within the templates directory, see: https://github.com/processwire/processwire/blob/master/htaccess.txt#L317-L318 And with good reason, since the template files are supposed to be included by ProcessWire during page visits, so calling a template directly (bypassing ProcessWire's access checks) might leak confidential data. If you absolutely want to do it your way, move the PHP file outside the templates directory (for example, in the webroot) and it should work (there are several other rules in the htaccess blocking specific files and directories from direct access, so make sure to check the htaccess for those). But the "ProcessWire way" would be to create an API template and page and use URL segments and URL parameters to pass data to it. This way, you get access checks out of the box and can utility ProcessWire's mail modules instead of the native mail function, which will very likely send your mails directly to the spam folder of the recipients ...
-
@adrian I'll have to get back to you on monday ^^ Though when I got the error, I tried updating to the latest version and the error remained. Not 100% sure that the screenshot was taken with the current version though, sorry ? I haven't really changed much ... came back to the site after a week and the error was there, not sure when it started ? I'll try to find out more when I'm in the office ...
-
Hi @adrian, I'm getting a curious error that wasn't there before. Some images on a site using $config->pagefileSecure = true; aren't being loaded anymore (worked fine before, not sure what changed). The images are being served by ProcessWire. When I open the image URL directly, I get this exception (see attachment). The image field is inside a Repeater Matrix. When I turn off Tracy Debugger, the error goes away. Not sure if it's a bug with Tracy or if it's just surfacing a deeper problem. Any idea what might be causing this?
-
Try {testimonial.author}, the dot accessed sub-fields or properties, so this should work for most nested fields.
-
MySQL 8 compatibility and MariaDB replacements
MoritzLost replied to MoritzLost's topic in General Support
@adrian Thanks, good to know that this version works. I think the reported version number has to do with the replication compatibility as outlined on their compatibility page, to indiciate which MySQL version the MariaDB version is compatible with. Though the whole drop-in replacement thing doesn't seem to be what they're aiming for anymore. Well Hosteurope is definitely updating our managed server, can't speak to their other products, but I guess some hosts are following suit now. I've tested one of the sites running on Hosteurope with a docker image of MySQL 8 and a database dump – so far everything seems to be working! I'll report back if I find any bugs or errors ? -
Batch activate pages for Multi-lingual support
MoritzLost replied to michelangelo's topic in General Support
Yes, the "Active" checkbox under the settings tab controls whether the page is viewable in that language. When you create a page through the CMS, it is active in all languages by default. But if you create a page through the API, it is inactive in all non-default languages by default (I've stumbled upon this as well a couple of times, not sure what the resoning behind that is). You can set the active status through the API, the property is "status{$id}", where {$id} is the ID of the language you want to activate the page in. For example, this code will iterative over a set of pages and activate them in all languages: $importedPages = wire('pages')->find('template=imported-page'); // adjust the selector as needed foreach ($importedPages as $p) { foreach (wire('languages') as $lang) { if ($lang->isDefault()) continue; $p->of(false); $p->set("status{$lang->id}", 1); $p->save(); } } You can put this somewhere in a template file and reload the page once, or execute it through the Console provided by Tracy Debugger. Of course, if you are importing manually or can use a hook to modify the behaviour of your import module, you can also set this property at the time of importing. -
MySQL 8 compatibility and MariaDB replacements
MoritzLost replied to MoritzLost's topic in General Support
Thanks @adrian! Unfortunately Hosteurope is forcing the upgrade, so we'll have to either migrate those sites to a different host or wait and hope everything works ? Which version of MariaDB are you on? We're using mostly MariaDB 10.1 for development, which should be a sort-of replacement for MySQL 5.5, and this version works fine with the current master / dev branch. Can't speak for newer MariaDB versions though. -
I'm searching for some information regarding supported MySQL versions, since we need to prepare for an update to MySQL 8 (Hosteurope is updating their servers ...). All I can find is the software requirements for ProcessWire which lists MySQL 5.0.15 or newer. There's also at least one issue regarding the DateTime field, but not much beyond that. Is anyone running ProcessWire (current dev or master version) with MySQL 8? Is anything working as expected? Any errors / potential bugs I should be aware of? Also, does anyone know if ProcessWire supports newer MariaDB versions? I'm currently using MariaDB 10.1 (comes with Plesk), which mostly replaces 5.6, but seems that starting with MariaDB 10.3 those versions are not really compatible with any MySQL version. Is there even a version of MariaDB that's compatible with MySQL 8? The official compatibility table doesn't suggest there is ...
-
Prevent saving a page if it was changed in the meantime
MoritzLost replied to MoritzLost's topic in General Support
@horst @kongondo @Robin S Thanks for all your input! Sorry my description is kind of vague, this is about business processes for a client project, and I'm not sure how much of those I can disclose ... I don't need to hide the collisions; in fact, if the editor is notified and can manually merge both versions that would be fine. But I'm not sure how to merge both versions because ... Normally, the automated process fills most fields, and the editor changes some fields that are not written to automatically. But the editor may change ANY field that was changed programmatically to correct errors or add new information, so simply limiting the fields in the edit page or discarding edits for certain fields will not work. In case of a collision, the best result would be an automatic merge between both versions, with the manual changes having precedence if both versions have changed the same field. But that would be really involved, and I'm already not sure how to distinguish fields that were changed by the editor and fields that they didn't touch but ProcessWire regards as "changed" because their value was changed by the automated process in the meantime. I think this line of throught is already too complicated ? Interesting idea, this will probably reduce the amout of collisions a lot. Though it might be more work, and of course having every field AJAX loaded is not very nice from a usability perspective ... That's creative! I think this solution is the one to be. I like that it results in mostly a clean merge, and I could differentiate between "editor-owned" and "process-owned" fields to decide which version to drop in case of a collision. Hmmm, maybe I could even add an extra read-only field that get's populated with all the dropped values for restoration / copy-pasting purposes. Thanks for the suggestion! Probably confusing to the editor if fields are disappearing left and right ? But maybe I could just use JS polling to check for changed fields and warn the editor not to save the page – this way, they could open the edit page in another tab and manually copy over their edits. Not perfect from a usability perspective, but a good compromise to avoid data loss. Thanks for all the suggestions from all of you, I'll probably go with some sort of tracking timestamps of field changes to abort / warn about collisions. I'll report back once I've implemented a solution ? -
Prevent saving a page if it was changed in the meantime
MoritzLost replied to MoritzLost's topic in General Support
I'll look into it, but I'm not sure this applies, because in this case the editing conflict / collisions is not between two users, but one user and one website feature that changes values programmatically, so anything that's based on user sessions or the editing page will not work ... -
Prevent saving a page if it was changed in the meantime
MoritzLost replied to MoritzLost's topic in General Support
The User Activity module of the Pro Dev Tools does this, but as far as I can tell from the documentation this only works if both editors are human. We don't have that module, so I can't check right now. Hm, maybe the module can be hooked, or automated activity can somehow be registered as an active editor ... that would be great. Though getting this to work may be more work than a custom solution ^^ -
Prevent saving a page if it was changed in the meantime
MoritzLost posted a topic in General Support
I'm trying to find a solution to a problem with parallel editing. We have a site that contains some datasets (pages) that can be edited manually, but may be changed programmatically as part of some internal processes as well. The problem arises if a programmatic process changes some values of a page while an editor is manually editing the same page (controlling when those automated changes happen is not an option, in this case). Once the editor saves the page, the programmatic changes will be overwritten. Sounds like an edge case, but because of the way the processes are structured, this could occur regularly. I'm looking for an elegant solution to this problem. So far I have a couple of ideas: Build a custom module that saves the current timestamp when a user starts to edit a page, and once the editor saves the page prevents it if the page was changed in the meantime. Not ideal because in this case the manual changes would be lost. Use Version Control so that IF this problem occurs, the programmatic changes can at least be restored from a revision. Not ideal because in this case the editor has to (a) notice this has happened and (b) merge the two versions manually. Store the programmatic changes in a draft using the ProDraft module. I'm not sure this will work at all (we haven't bought the ProDrafts module yet), and it's not great because any programmatic change would have to be approved manually ... I'd appreciate any insights or suggestions, either on how to improve the approaches above or a better solution. If there are any modules that can handle this I'd be happy to hear about them as well. All input would be welcome. Thanks! -
I look forward to create a module about languages
MoritzLost replied to Sten's topic in Module/Plugin Development
Thanks for the insights! Static translation files are definitely a fast approach. If all your editors / translators are comfortable editing source code files directly, that's reasonable. Though they have to be careful not to overwrite each other's changes two or more editors are working on the same language (unless of course you're using distributed version control). Does that mean you have to add a constant to eight different files whenever you want to add a translatable string? I'd get fed up with that very quickly ? -
Checking whether $cache is expired OR doesn't exist
MoritzLost replied to a-ok's topic in API & Templates
Ok, I understand the problem now. I still think your issue comes from trying to fit too much information into a single cache entry. With all those conditions for initial setup, token refresh etc you are talking about multiple distinct pieces of state, at least: The initial token, set manually. The refreshed token, set programmatically. The expiration time for the currently used token. You should definitely store those separately to have the fine control you require. I also wouldn't use the expiration time of the $cache API to represent the lifetime of the token – as you said, that becomes problematic when you need to refresh a token after it's expired (in the cache). Why not store this state in regular pages? Could be hidden pages that represent tokens and their lifetimes – or a hidden field on a global settings page. Anything that's more persistent than cache entries. And this will provide you with the interface to set the initial token. But if absolutely want to use the $cache API, I'd use separate entries for the above pieces of state and set those to expireNever or better yet, expireReserved. You wouldn't want your Facebook token to disappear because someone clicked on Clear all caches, after all ? -
I look forward to create a module about languages
MoritzLost replied to Sten's topic in Module/Plugin Development
I'm also using Twig with a custom translations system for interface text / code i18n. While the built-in multilanguage support for page fields is well-done, I don't really like the system and interface for code internationalization and localization, and it doesn't work with Twig files (which is probably why you want to build your own). I have written about the approach I took in the second part of my Twig + ProcessWire tutorial (towards the end, the section about translations). It comes down to a ProFields Table field that holds all the translations (so they can be edited through the backend as well) and a function that gets a local translation for the current language based on translatable strings (msgid). By the way I've switched to a Twig filter since writing this tutorial, I find it easier to write, so now I can simply do the following in Twig: {{ 'hamburger_button_label'|t }} If you do end up writing a module for this, make sure to publish it here as well, I've been looking for a more scalable approach ? -
Well, if you look through the database, you can see that SelectOption fields are always saved by ID (i.e. the value that goes into the database for this field is the selected option's ID). The mapping of IDs to values and labels is done through the central field definition. So changing that would be a lot of work. Can't you change the API call (or whatever) to the external service to use the value instead of the ID?
-
@entschleunigung Why do you want to change the format though? SelectOptions are saved by ID, so that you can change the values and labels of individual options without changing selected options in existing pages. If you change the output format, you'll have to change the format they are saved in as well, that will require hooks also. Why not just let the module save the options as IDs and output the values / titles in your templates as you need them? Users don't see the value of the options in a radio input, after all ... just the labels. Or are the labels not rendering for you?
-
Checking whether $cache is expired OR doesn't exist
MoritzLost replied to a-ok's topic in API & Templates
I don't completely get your use-case; what use is a static token in this situation? But anyway ... Instead of relying on some flaky edge cases of the $cache API (like returning null vs an empty string, which could change at some point), I would just explicitly store the information you need in a separate place. If your instagram tokens are associated with pages or user accounts, you can have something like a has_token field in addition to the token itself. Or just save that information in the cache as well, but with no expiration date. So the first time the token is created, you could do something like this: $instagramToken = 'your-token'; $cache->save('instagramAccessToken', $instagramToken, WireCache::expireMonthly); $cache->save('instagramAccessTokenExists', '1', WireCache::expireReserved); Then you can check if instagramAccessTokenExists is set in the cache, which will tell if you the token was previously saved to the cache, even if has expired. Note the use of WireCache::expireReserved, which makes that value as persistent as you can get with the cache API. -
If you want to make sure there are no duplicates, excluding them by ID is the way to go. There shouldn't be any performance issues - excluding one ID or a hundred shouldn't make any noticable difference for the generated database query. You can save the first result to the session as a string, and then include it in your selector. // first result $randomPages = $pages->find("template=gallery-detail, sort=random, limit=100"); $session->set('current-random-pages', (string) $randomPages); // later, in an ajax call $previous = $session->get('current-random-pages'); $randomPagesWithoutPrevious = $pages->find("template=gallery-detail, sort=random, limit=100, id!={$previous}"); // add the new pages to the list so you can load more multiple times $session->set('current-random-pages', $previous . '|' . $randomPagesWithoutPrevious); Quick and untested, might need some adjustments and context, but you get the idea ? If you want to go stateless, you can also include the ID-list in the ajax call. By the way, casting a PageArray to string creates a pipe-seperated list of IDs (e.g. 2|5|28|278), this can go right into the query and is a good format to save the IDs to the session or include them in the ajax call.
-
You'll have to look through the $_COOKIE superglobal to find the cookie name that matches your format. You can do that with regular array functions. For example: // just for testing $_COOKIE['name_xyz'] = 'Test value'; $_COOKIE['name_foo'] = 'Bar'; $cookieNames = array_filter(array_keys($_COOKIE), function ($key) { // check if the cookie name starts with "name_" return preg_match('/^name_/', $key) === 1; })); print_r($cookieNames); // -> Array ( [6] => name_xyz [7] => name_foo ) print_r($_COOKIE[current($cookieNames)]); // -> Test value Adjust the preg_match pattern to match the pattern you're looking for. The result will be an array of keys that match your format. Note that this array is NOT zero-indexed, because array_filter doesn't change the keys, hence the use of current to get the first value. Alternatively, use array_values to zero-index the array, or foreach to loop through all matching cookies. If you dislike the raw use of $_COOKIE, you can also use $input->cookie()->getArray() (docs).
-
Yeah makes sense, checking if the field exists is sensible ? Should work fine. Maybe add the same check for the name field as well? If the template is set to generate the name automatically, the field may be missing as well (I think it skips this step entirely then, but it's an additional safeguard that won't hurt anyone). If you're paranoid you could use a whitelist of templates to apply the hook to as well, to make sure it will never have unintended consequences ...
-
Yes! The module uses preg_replace_callback to look for cache tokens and calls the corresponding callback for each occurance. BTW this also means that if you use the same token multiple times, the callback will be called that many times as well (for most tokens this will be the desired / expected behaviour).