Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by MoritzLost

  1. @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 ...
  2. 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?
  3. Try {testimonial.author}, the dot accessed sub-fields or properties, so this should work for most nested fields.
  4. @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 πŸ™‚
  5. 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.
  6. 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.
  7. 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 ...
  8. @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 πŸ™‚
  9. 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 ...
  10. 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 ^^
  11. 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!
  12. 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 πŸ˜›
  13. 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 πŸ˜›
  14. 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 πŸ™‚
  15. 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?
  16. @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?
  17. 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.
  18. 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.
  19. 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).
  20. 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 ...
  21. 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).
  22. Interesting, I was thinking that ProcessWire could do with a couple of alternatives to the dated CK Editor. EditorJS is one of those 'modern' editors (I was also looking at Quill), I definitely like the block-based output in JSON format. In the end I think it would be nice to be able to change the editor implementation library for textarea fields on the fly – like having a textarea field where you can select between CK Editor, EditorJS, TinyMCE etc. and being able to easily add other editors through a hook or something. Edit: Obviously this should be changed in the field settings per site, not configurable by editors themselves!
  23. This module allows you to integrate hCaptcha bot / spam protection into ProcessWire forms. hCaptcha is a great alternative to Google ReCaptcha, especially if you are in the EU and need to comply with privacy regulations. The development of this module is sponsored by schwarzdesign. The module is built as an Inputfield, allowing you to integrate it into any ProcessWire form you want. It's primarily intended for frontend forms and can be added to Form Builder forms for automatic spam protection. There's a step-by-step guide for adding the hCaptcha widget to Form Builder forms in the README, as well as instructions for API usage. Features Inputfield that displays an hCaptcha widget in ProcessWire forms. The inputfield verifies the hCaptcha response upon submission, and adds a field error if it is invalid. All hCaptcha configuration options for the widget (theme, display size etc) can be changed through the inputfield configuration, as well as programmatically. hCaptcha script options can be changed through a hook. Error messages can be translated through ProcessWire's site translations. hCaptcha secret keys and site-keys can be set for each individual inputfield or globally in your config.php. Error codes and failures are logged to help you find configuration errors. Please check the README for setup instructions. Links Github Repository and documentation InputfieldHCaptcha in the module directory (pending approval) Screenshots (configuration) Screenshots (hCaptcha widget)
  24. You can disable autocomplete on input elements with the autocomplete attribute. You can modify built-in forms through hooks by looking up which Process module is creating the form and then hooking after it's buildForm method. In this case, it's ProcessPageAdd. This hook sets autocomplete="off" on both the title and the name input fields: // site/init.php wire()->addHookAfter('ProcessPageAdd::buildForm', function (HookEvent $e) { $form = $e->return; // disable autocomplete for the title field $title = $form->getChildByName('title'); $title->attr('autocomplete', 'off'); // disable autocomplete for the name field $pwPageName = $form->getChildByName('_pw_page_name'); $pwPageName->attr('autocomplete', 'off'); });
  25. This error indicates that you have not added your site's domain to the allowed referers for your Google Maps API key. See the error documentation here: https://developers.google.com/maps/documentation/javascript/error-messages#deverrorcodes This didn't use to be required, but Google Maps is constantly becoming more strict with their API usage limits. Go to the cloud console and make sure to add your site's domain to the allowed referers for the key you're using, then it should start working again.
  • Create New...