Leaderboard
Popular Content
Showing content with the highest reputation on 11/28/2023 in all areas
-
I have a json based migrations module that I released some time ago (ProcessDbMigrate). It needed further work, so I have not publicised it further after the initial proof of concept. In the meantime I have improved it enormously and tested it quite extensively with multiple field types including RepeaterMatrix. It is almost ready for re-release. It is a completely different concept to RockMigrations, which is an excellent and well-established module. My module will automatically track database changes as you make them in the back end UI and then export json for installation in the target. It also provides for roll-back, database comparisons and much more. Hopefully out before Christmas. PS I found during the course of development that there are quite a few flaws in the native export functions and had to re-write quite a bit.6 points
-
Will do. Please note that the code on GitHub may be behind the latest version and may well contain bugs. I will post on the Forum when I am happy with it. I haven't tried this. It might work if you have not changed any pages at all. I would be afraid of breaking relationships and, personally, I sometimes use pages to hold settings that are not user-updatable and which I might need to migrate, so that would not be a solution for me. Fair point. Particularly re any special field types introduced in modules. ProcessDbMigrate does cater for most of them (I am just updating it for FieldtypePageTable) including some which store attributes as objects, but would need updating if there are major changes or additions to the core (my guess, though, is that these are very unlikely to be breaking changes). It is clear to me from previous discusssions that @ryanis not convinced of the need for this in the core and, indeed, does not feel the need for it personally. I consider that a shame as the lack of this functionality is in my view the only major missing feature in ProcessWire. The topic has been debated here numerous times with no clear conclusion. As a consequence, those of us who feel the need have had to turn to modules - either our own or others'. RockMigrations has been released for a while and is well-supported. Both that and ProcessDbMigrate are open-source so, unless @ryanchanges his mind, it will be up to the community to contribute to module development or, indeed, take it over should the original author no longer maintain it. That will depend on how important the community thinks it is. I think I reported some issues without any action being taken. Also, I recall that the export functions were left as work in progress some time ago. But I may mis-remember...4 points
-
@MarkE Sounds interesting! Keep us posted about new releases and let us know if you need beta testers.3 points
-
Dear ProcessWire folks! First of all, thanks for all your work on this amazing CMS @ryan and contributors! ❤️ I'm a WordPress guy with 10+ years experience. I'm well aware of the shortcomings of WP (chaos API, no built-in multilingual support, to name a few), but was always able to find a way to work around these. In 2023, due to the never-ending praise from some of my developer peers, I finally gave ProcessWire (and also Kirby CMS) a try for two small projects. If nothing else, these excursions gave me a more senior perspective on the advantages and shortcomings of these three content management systems (none of them is perfect). I was impressed by many thoughtful decisions in ProcessWire's setup, data structure and API. But I am missing one last thing that both WordPress and Kirby have solved in a very clean an comfortable way: A clean solution for deploying fields between environments and committing them to version control. ✅ In WordPress, I am using Advanced Custom Field's local JSON feature for this ✅ In Kirby, all fields and templates are being defined using YAML blueprints, so they are also easy to deploy and version control (although YAML configs come with their own set of shortcomings ?) ❌ In ProcessWire, I couldn't work out a reliable way to define (/autosave/sync) the fields in my code base. So, my question is: Is there any chance the core team will give first-class support for programmatic deployment of fields another thought? If not, what are the considerations that lead to this decision? Syncing fields manually between environments is too cumbersome and error-prone for my taste. I know my request is not new, but the information available felt a bit outdated and not very convincing to me. I'd like to start a discussion here based on facts, not opinions.2 points
-
One thing I should mention (and would appreciate some feedback on) is that my migrations module (and I guess any that uses json files) is declarative. Even if you create a migration automatically while making your database changes, what happens is the module logs what objects have changed and exports the final state. It does not operate as a 'macro' logging each change separately. The disadvantage of this is that problems might arise where there is a 'dependency cycle' - e.g. a new field (say a page ref type) depends on a new template which includes the field. With my module, if this happens while you are logging changes it will (should ?) warn you. If you create the migration manually and then 'sort on save' to get the items in dependency order, it will give you an error message (see video at end). I think there are 3 ways of dealing with this: Do more than one migration, so that all required objects are present in the database. Include an item twice in the migration - firstly without the dependency, then with the dependency after the other object has been added. Install the migration twice and hope it will sort itself out (i.e. the misssing objects will be there on the second installation). My module encourages (1) but permits (3) - as you will see. It does not attempt (2) - this might be feasible by detecting the cycle, and breaking it for the first item, but a bit complex to implement. I'd be interested in any thoughts people have on this issue (and indeed on how RockMigrations deals with it - @bernhard?). Meanwhile, to amuse you (??) here is a video of a big complex migration I did as part of the testing process. As you will see, there are lots of cycles, but it installed with just two clicks of the 'install' button ?. cycle errors.mp42 points
-
Sorry to catch up this older topic again, but there seems to be no changes in sight so far. I also still would find it useful to have webp as input format. The need- or the wish- to use it, is from my experience originated in Google requesting (or recommending) webp format in order to reduce loading times when doing speed tests with lighthouse. So people start to use and store images in webp format. Then to convert them back to jpg or png just to output these as webp again makes no sense at all.2 points
-
I have more and more customers complaining that they can't upload webp images. I understand that webp is not the ideal master image format, but neither is jpeg. The webp browser support is also so good now that it would not be necessary to generate jpeg/png versions from webp.2 points
-
This week in the blog we'll take a detailed look at the newest addition to the ProFields set of modules: the Date Range Fieldtype and Inputfield. This post serves both as an introduction to, and documentation for this new module. In addition, the v1 (beta) release of the module is now available for download in the ProFields board. https://processwire.com/blog/posts/date-range-fields/1 point
-
WebP to JPG Converts WebP images to JPG format on upload. This allows the converted image to be used in ProcessWire image fields, seeing as WebP is not supported as a source image format. Config You can set the quality (0 – 100) to be used for the JPG conversion in the module config. Usage You must set your image fields to allow the webp file extension, otherwise the upload of WebP images will be blocked before this module has a chance to convert the images to JPG format. https://github.com/Toutouwai/WebpToJpg https://processwire.com/modules/webp-to-jpg/1 point
-
@MarkE I rephrased your post above a little bit and fed GTP-4 with it. This is what I got: Circular dependencies can indeed present a challenge when dealing with database migrations. Your proposed solutions are viable and could work depending on the specific situation. Let's delve deeper into each approach and discuss their potential benefits and drawbacks: 1. Performing the database migration in multiple steps: This approach is generally safe and effective, as it ensures that all required objects are present in the database in the end. However, it may require careful planning and sequencing of migrations to ensure that each step has all its dependencies satisfied. This approach is encouraged by your module and is also recommended in Django's migration documentation [Source 2](https://django.readthedocs.io/en/stable/topics/migrations.html). 2. Including an item twice in the migration: This approach involves initially adding the class or field without the dependency, then adding the dependency after the referenced object has been added. This method can work, but it may be complex to implement and maintain, especially for large databases with many interdependencies. Your module currently doesn't support this approach. 3. Running the migration process twice: This approach relies on the hope that the second run will resolve any missing dependencies from the first run. While this may work in some cases, it's not a guaranteed solution and could potentially lead to inconsistent results. Therefore, it's typically not recommended unless you have a strong understanding of the specific dependencies and migration behavior in your database. Another approach worth considering is the use of **"swappable" dependencies**, as mentioned in Django's migration documentation [Source 2](https://django.readthedocs.io/en/stable/topics/migrations.html). This allows you to declare dependencies that can be swapped out with different implementations, which can be useful when dealing with circular dependencies. Hmmm....1 point
-
I can confirm all this... and I also in favor of a solution that works the way @MarkE designs it. As soon as it is ready for testing and I have the time, I will surely try it out as well.1 point
-
Welcome to our lovely community, Rasso. I hope you are enjoying the benefits of ProcessWire as much as I do. We had the discussion about replicating / syncing fields on the server many times. I use RockMigrations as a reliable and working module for this. You write code with migrations and they get executed as soon as you save the file and refresh the admin. A migration can be inside the site/migrate.php file, or in a migrate method inside of your custom module, or somewhere else. Here is a possible example of a migration: $rm->migrate([ 'fields' => [ 'body' => [ 'label' => 'Content', 'type' => 'FieldtypeTextareaLanguage', 'inputfieldClass' => 'InputfieldTinyMCE', 'tags' => 'generic', 'contentType' => 1, 'langBlankInherit' => 1, 'inlineMode' => 0, 'height' => 500, 'lazyMode' => 1, 'features' => [ 0 => 'toolbar', 1 => 'menubar', 2 => 'stickybars', 3 => 'spellcheck', 4 => 'purifier', 7 => 'pasteFilter', ], 'toolbar' => 'styles bold italic pwlink pwimage blockquote hr bullist numlist anchor code', 'rows' => 15, ], 'home_intro' => [ 'label' => 'Home Intro', 'type' => 'textarealanguage', 'tags' => 'generic', 'inputfieldClass' => 'InputfieldTinyMCE', 'rows' => 5, 'inlineMode' => 1, ], 'subheadline' => [ 'type' => 'text', 'inputfieldClass' => 'InputfieldTextLanguage', 'maxlength' => 100, 'tags' => 'generic', ], ], 'templates' => [ 'basic-page' => [ 'label' => 'Standardseite', 'fields' => [ 'title', 'navtitle', 'seo', ], ], 'imprint' => [ 'label' => 'Impressum', 'fields' => [ 'title', 'body', 'seo', ], ], ] ]); With the migration you can also create pages and even fill them with content (if you want that) $rm->createPage(title: 'About us', name: 'about-us', template: 'basic-page', parent: '/'); There is no sync between fields or templates you create in the admin and RockMigrations (there was a YAML option once, but I don't know if it still exists), but you get an easy to copy code in each fields edit section, that you can copy directly over to your migration. I am also in favor that migrations should be a core feature of ProcessWire, but Ryan does not approve. So we have to stick with what we have, and RockMigrations is a great solution.1 point
-
@cwsoft, yes got Option 2 working. Also including my custom variables. Regarding Option 1: I tried your proposals to but didn´t get it to work. LATTE is properly installed, otherwise Option 2 would also not work I guess. But anyway. I am happy with Option 2 for my purposes. Thanks1 point
-
Hey. Interesting topic. First, and not to lecture you, Pages::find doesn't have a getFromCache parameter. The find method decides to fetch from cache based on what is queried. I did a little further digging and code reading. What I can come up with is that the cache gets invalidated every time a save is performed on the page itself or on it's parent. Move operations are handled by the ProcessPageSort module (part of core) and before the sorted hook is called, the functions in this module save the page and after, it's parent. Save operations seem to invalidate the cache from the page down across its children. This makes sense as the database doesn't necessarily hold the same data as the cached versions. To illustrate this, I have the following hooks with child ID 1017 and parent ID 1015 and performing the same move operation as in your example: <?php $wire->addHook(['Pages::saveReady(1017)', 'Pages::save(1017)', 'Pages::sorted(1017)'], function (HookEvent $event) { $eventPage = $event->arguments(0); $wire = $event->wire; $pages = $wire->pages; be($event->method . ' - ' . $event->when); $findPage = $pages->find(1017)[0]; $getPage = $pages->get(1017); be('$eventPage ' . spl_object_id($eventPage)); be('$findPage ' . spl_object_id($findPage)); be('$getPage ' . spl_object_id($getPage)); }, ['before' => true, 'after' => true]); $wire->addHook(['Pages::save(1015)'], function (HookEvent $event) { $wire = $event->wire; $pages = $wire->pages; be('parent' . ' / ' . $event->method . ' - ' . $event->when); $findPage = $pages->find(1017)[0]; $getPage = $pages->get(1017); be('$findPage ' . spl_object_id($findPage)); be('$getPage ' . spl_object_id($getPage)); }, ['before' => true, 'after' => true]); This gives me the following event log (the number is the object instance id): save - before $eventPage 243 $findPage 243 $getPage 243 saveReady - before $eventPage 243 $findPage 243 $getPage 243 saveReady - after $eventPage 243 $findPage 243 $getPage 243 save - after $eventPage 243 $findPage 187 $getPage 187 parent / save - before $findPage 187 $getPage 187 parent / save - after $findPage 196 $getPage 196 sorted - before $eventPage 243 $findPage 196 $getPage 196 sorted - after $eventPage 243 $findPage 196 $getPage 196 You can see that after "save - after", the find call loads a new version into cache. The instance IDs change a second time after "parent / save - after" which must be doing the same. In between these, the IDs stay the same. This should explain why you do not get your situation on saveReady, but then afterwards in sorted, you do get it since the sort functions save the pages in question before calling the hook.1 point
-
Hi all, Here is one of the latest website we created for a french company renting construction machines and trucks with specialized drivers, based in Le Mans. It’s a rather simple showcase/informational website, but we aimed at making clear (and visually attractive) what is available, what is the skillset and various ways to quickly get in touch. We produced everything, from the design, pictures, illustrations down to the development (ofc). There wasn’t any website before except for social media presence and this task was a follow-up to the update of the visual identity we did. Behind the scene, on the front-end, everything is custom made: we don’t use any frameworks. On the back-end we used the usual suspects and some: TracyDebugger ❤️ FormBuilder ProCache ProFields (RepeaterMatrix, Table) Dynamic options / Select images Our own flavor of “components” Thanks for having a look!1 point
-
Looking really great ? Really? ? @ryan is that a limitation or is that something coming later? I'd also need a field like this for events, eg MyEvent 1.12.2023 19:00 - 2.12.2023 03:001 point
-
This is great! Just a tiny nitpick: I'd swap around the logic for "Don't allow selection in both directions" and get rid of the "Don't", or maybe change it to "Disallow". It's a very cultural and regional thing whether questions with a negation get answered with a yes or no to confirm then, and it's bitten me in the backside myself when I rolled out an app with such a toggle to our international employees.1 point
-
It seems this got updated in the last few versions: Check this thread: https://github.com/processwire/processwire-issues/issues/1791 And also this commit which has new $config options: https://github.com/processwire/processwire/commit/013231acdabd7b42640c1a9975c9e54ecf366b451 point
-
For those who just want to let users upload WEBP format and are looking for a workaround, here is a simple module that converts WEBP to JPG on upload: I wonder though... how is it that the user acquired the WEBP image in the first place? Surely they didn't just pinch it off somebody else's website... ?1 point
-
There's no simple way to do this because when a page is moved the page list is updated via AJAX, whereas the core message() / warning() / error() methods require a normal page load in order to appear. But I had fun exploring some workarounds. Maybe one of these will suit or maybe not. In these examples a message is shown whenever a page is moved but of course you would add your own logic show your message more selectively. Option 1: queue a warning() to appear at the next page load via $session->warning(), and use some JS to automatically reload ProcessPageList whenever a page is sorted or moved. In some custom admin JS (you can add this with a hook or by using AdminOnSteroids): // When a page is moved in ProcessPageList $(document).on('pageMoved', '.PageListItem', function() { // Reload location.reload(); }); In /site/ready.php // When a page is moved $wire->addHookAfter('Pages::moved', function(HookEvent $event) { $page = $event->arguments(0); // Show a warning $event->wire()->session->warning("Page '{$page->title}' was moved to parent '{$page->parent->title}'."); }); Result: Option 2: Make use of the fact that when an exception is thrown ProcessPageList will show this as an alert. The below assumes that you only want to show a warning when a page is moved to a new parent and not when it is sorted within its existing parent. If that distinction doesn't matter then you could simplify this by only hooking after ProcessPageSort::execute(). In /site/ready.php // Optional: use Vex for nicer alerts in ProcessPageList $wire->addHookBefore('ProcessPageList::execute', function(HookEvent $event) { $event->wire()->modules->get('JqueryUI')->use('vex'); }); // Before ProcessPageSort::execute $wire->addHookBefore('ProcessPageSort::execute', function(HookEvent $event) { $pps = $event->object; $move_id = (int) $event->wire()->input->post->id; $moved = $event->wire()->pages->get($move_id); // Store original parent ID on the ProcessPageSort object $pps->original_parent_id = $moved->parent->id; }); // After ProcessPageSort::execute $wire->addHookAfter('ProcessPageSort::execute', function(HookEvent $event) { $pps = $event->object; $input = $event->wire()->input; $pages = $event->wire()->pages; $parent_id = (int) $input->post->parent_id; $move_id = (int) $input->post->id; $parent = $pages->get($parent_id); $moved = $pages->get($move_id); // Check if the parent ID has changed from the original (i.e. the page has moved) if($parent->id !== $pps->original_parent_id) { // Show an alert by making use of how exceptions are handled in ProcessPageList throw new WireException("Page '{$moved->title}' was moved to parent '{$parent->title}'."); } }); Result: Option 3: do it all in JavaScript. Whether this is viable depends on what sort of logic you need. With a bit of extra faffing around (not shown here) you can get things like the page IDs and template names from the class names of the page list items which might help. Optionally add the Vex library as in option 2. And then in some custom admin JS: // When a page is moved in ProcessPageList $(document).on('pageMoved', '.PageListItem', function() { var moved_title = $(this).find('.label_title').text(); var $parent = $(this).parent('.PageList').prev('.PageListItem'); var parent_title = $parent.find('.label_title').text(); var $from = $('#PageListMoveFrom').prev('.PageListItem'); if($from.length) { // Page was moved to a different parent var from_title = $from.find('.label_title').text(); ProcessWire.alert('Page "' + moved_title + '" was moved to parent "' + parent_title + '" from parent "' + from_title + '"'); } else { // Page was sorted within its existing parent ProcessWire.alert('Page "' + moved_title + '" was sorted within parent "' + parent_title + '"'); } }); Result:1 point
-
Thanks to bernhard and Autofahrn i have come up with this example code and run it in TracyDebugger on a test page with a image field and it works beautifully. <?PHP /* get and save a new image to image field Pageimages array */ $page->of(false); $pageImages = $page->images->add('https://www.somesite.com/image_of_tree.jpg'); /* save the page (perhaps not needed but there for comfort.) */ $page->save(); /* get the last added image */ $lastImage = $page->images->last(); /* debug before changes */ d($lastImage, '$lastImage before changes'); /* add tags to the image and description */ $lastImage->addTag('test'); $lastImage->addTag('Tree'); $lastImage->addTag('Syren'); $lastImage->addTag('Sun'); $lastImage->addTag('Sunny'); $lastImage->description = 'This is a beautiful tree.'; /* debug info */ d($page->images, '$page->images'); d($lastImage, '$lastImage'); /* save the page */ $page->save(); ?> I used the following API docs and mentioned forums users help to accomplish this: https://processwire.com/api/ref/pageimage/ https://processwire.com/api/ref/pageimages/ https://processwire.com/api/ref/pagefile/ https://processwire.com/api/ref/pagefiles/ Just wanted to post this at the end so others who wonder about this could get a starting point.1 point
-
I fixed the issue referred to in Nik's GitHub issue report (thanks Nik). As for sorting the items in the repeater, I think the situation you are trying to accommodate here is a little unusual. Wouldn't the date field be associated with the 'event' rather than for some repeatable field within the repeater? It can't sort on eventdetails.date because it's not referring to any single date, but rather any number of potential dates. What I'd suggest is adding an 'date' field to your 'event' template that represents the event starting date. But if you want it to work with what you've already got, then I suppose you could query the repeaters rather than the events (though I've not yet tried this): $events = new PageArray(); $items = $pages->find("template=repeater_eventdetails, date>0, sort=date, include=all"); foreach($items as $item) { $event = $item->getForPage(); if($item->isPublic() && !$events->has($event)) $events->add($event); }1 point