Leaderboard
Popular Content
Showing content with the highest reputation on 01/27/2023 in all areas
-
This week we've got a few minor issue fixes and a couple of pull request additions on the dev branch. Pull request #251 thanks to @Jan Romero added a download button to the thumbnail images in InputfieldImage. I wasn't sure we really needed that, but really liked his thinking behind it, which was envisioning the ability to add more custom buttons/actions for images. So while I didn't specifically add the download button, I added the proposed system for adding any custom buttons, and then applied that same thinking to some other parts of InputfieldImage. And we'll talk about how to add that Download button here. ? First, let's look at how you might add your own download button, and note we're using this as just an example, as you might add any kind of button this way. A new hookable getImageThumbnailActions() method was added for this purpose. So here's how you might hook it (in /site/ready.php) to add a download button: $wire->addHookAfter('InputfieldImage::getImageThumbnailActions', function(HookEvent $event) { $image = $event->arguments(0); // Pageimage $class = $event->arguments(3); // class to use on all returned actions $a = $event->return; // array $icon = wireIconMarkup('download'); $a['download'] = "<a class='$class' href='$image->url' download>$icon</a>"; $event->return = $a; }); With that hook in place, here's what it looks like when you hover a thumbnail image. And if you click that Download icon, it downloads the file to your computer: or in list mode (download icon appears in right corner next to trash): I was thinking it would be useful to also be able to add custom actions after you click the thumbnail, and it shows the image edit features. So let's add a Download button there instead, by hooking the new getImageEditButtons() method: $wire->addHookAfter('InputfieldImage::getImageEditButtons', function(HookEvent $event) { $image = $event->arguments(0); // Pageimage $class = $event->arguments(3); // class(es) to use on all returned actions $buttons = $event->return; // array, indexed by action name $icon = wireIconMarkup('download'); $buttons['download'] = "<button class='$class'><a download href='$image->url'>$icon Download</a></button>"; $event->return = $buttons; }); And the result looks like this (see new Download button after Variations button): We also have that Actions dropdown that you see in the screenshot above. This is already hookable but we've not had any good examples of it. In this case, you need two hooks: one to add the action to the <select> and another to handle the processing of the action when the page is saved. So in our next example, we'll demonstrate how to display verbose EXIF information about whatever image(s) the action was selected for. In this first hook, we'll add the action to the Actions <select>: // Example of adding an “Get EXIF data” action to the <select> $wire->addHookAfter('InputfieldImage::getFileActions', function(HookEvent $event) { $image = $event->arguments(0); // Pageimage if($image->ext == 'jpg' || $image->ext == 'jpeg') { $actions = $event->return; // array $actions['exif'] = 'Get EXIF data'; $event->return = $actions; } }); And in this next hook, we'll handle the action, which gets called when the page editor form is submitted: // Example of handling an “Get EXIF data” action $wire->addHookAfter('InputfieldImage::processUnknownFileAction', function(HookEvent $event) { $image = $event->arguments(0); $action = $event->arguments(1); if($action === 'exif' && file_exists($image->filename)) { $exif = exif_read_data($image->filename); $event->warning([ "EXIF data for $image->name" => $exif ], 'icon-photo nogroup'); $event->return = true; } }); And here's what it shows after you hit save (for any images that had the action selected): The screenshot above is truncated because it was about twice is big as what's above. All the above code examples are also included in the phpdoc for each of the relevant hookable methods in the InputfieldImage module. For another recent useful addition, be sure to check out ProcessWire Weekly #454 (last week) which covered some new options available for the language translation functions like __text('hello'); where you can now tell it what kind of input type (and how many rows) to use in the admin translation interface, via inline PHP comments. Thanks for reading and I hope you have a great weekend!9 points
-
TagsToFolders Github: https://github.com/eprcstudio/TagsToFolders Modules directory: https://processwire.com/modules/tags-to-folders/ This is a small helper tool to visually organise fields/templates into folders in the menu when they are tagged. This is a simple module that can help you declutter your templates/fields menu by organising them in folders using tags. I initially envisionned this module to be used for cases where fields and/or templates were created by modules and thus not polute the ones created by the user.2 points
-
My new module PAGEGRID can be a good alternative to website builders like Pinegrow or Webflow. This way you don't have to convert your design to ProcessWire templates and can design and edit pages directly in the backend.2 points
-
PAGEGRID – A visual page builder for ProcessWire. Design fully responsive websites (or parts of them) without writing any code. Use ProcessWire's native templates (and fields) to create your own blocks. Rearrange and resize items in a visual way and use inline or modal editing to quickly edit the content of your website. Try PAGEGRID for free PAGEGRID is not free software. However, you can try PAGEGRID on your local machine or on a test server as long as you need to make sure it is the right tool for your next project. … and when you’re convinced, buy your license. Get it here Download from GitHub Download from Module Directory Requirements ProcessWire 3.0.210 or greater Installation Go to “Modules > Site > Add New“ in your admin Paste the Module Class Name "FieldtypePageGrid" into the field “Add Module From Directory“ Click “Get Module Info“ On the overview, click “Download And Install“ On the following screen, click “Install Now“ More install options Module install guide Site profile install guide Get up and running Quick start Create your own blocks or install the PageGridBlocks Module (installs premade templates and fields for PAGEGRID blocks). What's PAGEGRID? page-grid.com – Get to know PAGEGRID. Documentation – Read the official documentation. Issues – Report bugs and other problems. Forum – Whenever you get stuck, don't hesitate to reach out for questions and support. Why I build it ProcessWire is super flexible in itself and lets me build whatever I want. But building a custom website can be a lot of work. For some projects, I've ended up using a lot of templates and fields. To make my pages more flexible, I sometimes build my own little page builder based on the RepeaterMatrix or PageTable module. While these page builders were great for the specific site I was building them for, they were never flexible enough to be used for new projects, so I ended up customizing them frequently. The more complex they became, the harder it became to use them for my clients. After playing around with some WYSIWYG page builder tools, I realized that while they can save me a lot of time, they can also be very limiting or have expensive subscriptions and somehow tie you to their ecosystem. So I decided to build my own page builder based on the most flexible CMS I knew. Concept This fieldtype Renders block templates and adds drag and drop functionality in admin, as well as enable inline editing for text, and file fields. It also let's you manipulate CSS in a visual way to design fully responsive websites (or parts of them) without writing code. The fieldtype comes with an optional style panel to manipulate CSS properties directly on the page. You can customize the panel or disable it completely from the module settings (and just use a CSS file that you include in your template). The data to style the items is stored directly on the item using PW's meta data (no extra fields are created). Don't want to give your client all that power? Use ProcessWire’s powerful permission system to control what your clients can edit. You can then also grant access individually to the style panel, resize or drag functionality using ProcessWire's build in permission system. Features Blocks are just pages Blocks are defined by native PW templates and fields Manipulate CSS grid or flexbox based layouts in a visual way to design fully responsive websites (or parts of them) Encapsulated frontend code (PAGEGRID renders the template of your frontend inside an iframe in the backend) Design and editing features can be disabled for certain roles (using ProcessWire's build in permission system) Inline editing of text, textarea, TinyMCE (supports latest version), ckeditor and file fields Simply drag and resize to manipulate grid items directly inside the backend Manipulate grid columns and rows directly on the page (use any number of columns you want) All style manipulations are saved as JSON and used to generate dynamic styles that you render in your main template (no inline styles) Nested groups/grids (child pages of nested blocks are created under group parent) The style panel supports adding custom classes and assigning styles to them. These classes can be used globally on all pages (a css class is also a page) The style panel supports selecting html tags to style tags globally across the whole site Global blocks work with page reference field (changes on one page, changes all blocks on all pages) Manual and auto placement of grid items blocks and nested blocks can be cloned Redo/undo and copy/paste shortcuts Editing block items in modal sidebar immediately updates frontend (Ajax Save). Define custom icons for your blocks via native template settings (template -> advanced -> icon) Automatic page save (Changes are getting saved via ajax, no need to click the save button) NEW: Option to automatically load lazysizes lazyloader (V 0.1.0) Changelog V 0.1.0: Feature: Option to automatically load lazysizes lazyloader (Module Settings > Plugins). V 0.1.5: Fixed bug: Tabs not working when editing items via modal panel. V 0.1.6: Fixed bug: Setting height in VH unit was not working. V 0.1.7: Feature: Option to hide save button (and use automatic ajax save ) if there are no other fields than PAGEGRID on the content tab (Module Settings > Interface). V 0.2.0: Fixed bug: Custom block wrapper element <p> was not working with inline editor. V 0.2.0: Fixed bug: Inline editor would sometimes not save after clicking cancel and then edit item again. V 0.2.0: Feature: Now it's possible to add classes to elements inside richt text fields via style panel. V 0.2.0: Fixed bug: Inline editor was not working after first item was added to the page (needed reloading the page). V 0.2.1: Feature: Updated PageGridBlocks Module: Using TinyMCE as the default editor. V 0.2.1: Feature: Updated PageGridBlocks Module: Group/container wrapper element can now be changed to <div>, <section>, <article>, <header>, <footer>, <nav>. Thanks to everyone who helped me improve my coding skills and for the support of this great community! Special thanks to @diogo for the valuable feedback and @ryan for this great CMS and his support for the PageFrontEdit module!1 point
-
1 point
-
That's why this module is already on my TODO list to give it a try. Your website, the module, and docs look awesome so far and I can guarantee you I'll give it an in-depth look.1 point
-
@Macrura sorry I meant $wire->files... I did end up finding a way to do what I needed to although I came across a few interesting issues that I can't really explain. The functions were from a module context, so I had to pull them abstract them away from $this and just tried to manipulate them directly, since it seemed like a $wire object should still exist. When I ran a db($wire->files) the bar dump said that this did represent a WireFileTools object, but the find() method was faulting, saying that the object was null when I tried to use $wire->files->find($something, $somewhere). Are the API helpers only available in certain contexts? What I ended up doing was the following: First, I brought my functions into a separate _theme_fn.php file. If I included them in the main Inputfields generating file, I got a cannot redeclare error, perhaps the settings factory config file is iterated in some way. I put both my settings factory config file and the external functions file in the PW namespace. Second, in the external functions file I brought in a $wf = wire('files') and $wc = $wire('config') to the functions which allowed me to grab default paths, run the file searches and create the appropriate select options arrays. Third, in the main settings factory file, did an include_once on the functions file and then I just set variables to call those functions and then inserted that into options. This lets me generate select options arrays dynamically. If you want a concrete example, let me know. It is hard for me to determine sometimes whether I am able to use an API variable or have to actually call functions to create my own object on the customization side. Front end side and modules seem much clearer.1 point
-
I get errors like this that I cannot reproduce more often than I'd like and, because I can't reproduce them, haven't been able to track down how or why they occur. I think it's a race condition based on PHP code running faster than the filesystem (ex: the code says to create a folder, the OS tells PHP OK, code continues, but the filesystem hadn't actually completed creating the folder). When I refresh the same page, I either don't get any errors, or I get a different file/folder that throws an error. Eventually refreshing enough times clears all errors. This is all just a hunch, so changing PHP versions might cause a large refresh of many files and folders of the cache all at once, which could have a higher probability of hitting a race condition like this - if it is indeed what causes the error.1 point
-
Since I had to update more and more projects lately and also the PHP version of some of these projects was set to >=8 in the meantime I created a fork of the module and updated mPDF to version 8.1.3. With only some small changes in the WirePDF module I can now continue to use Pages2PDF without any restrictions. Maybe this is helpful for someone. https://github.com/markusthomas/Pages2Pdf1 point
-
My new module PAGEGRID can be a good alternative to website builders like Pinegrow or Webflow. This way you don't have to convert your design to ProcessWire templates and can design and edit pages directly in the backend.1 point
-
My new module PAGEGRID can be a good alternative to website builders like Pinegrow or Webflow. This way you don't have to convert your design to ProcessWire templates and can design and edit pages directly in the backend.1 point
-
// site/migrate.php $rm->installModule('YourModule', [ 'fooSetting' => 'fooValue', 'barSetting' => 'barValue', ]); See https://github.com/baumrock/RockMigrations/blob/main/profiles/default.php for inspiration. Please let me know if anything is unclear and can be improved.1 point
-
You get this error because you are using $cache inside the hook function. There you need to use wire('cache') or $this->wire->cache, depending on the context.1 point
-
Something like the ImportPagesCSV Module?1 point
-
Yes, it is used for many things in the admin panel. On the frontend you are free to choose whatever you like. I think you are talking about the frontend. IMHO you should avoid using a CDN to integrate jQuery into your site, because of GDPR regulations in the EU (if you reside in the EU) and also I experienced times, where the CDNs failed or took a long time, so the whole site was blocked because it could not load jQuery. Nowadays I don't use jQuery anymore and try to write everything as vanilla JavaScript. jQuery has a simpler syntax sometimes, but adds additional Kilobytes to the load and execution time of your website. It was great some years ago, to overcome browser inconsistencies, but that is not needed any more. You might read You Might Not Need jQuery To avoid discussion: This is MY opinion and everyone can decide for themselves what to use.1 point
-
Good morning @gebeer, that looks promising – so far I didn't make the step to use the findRaw() but I will – looks like a huge potential to get my search run better. Thank you so much for your detailed examples. I will definitely dive into that and try to use it further up in my data collecting process. Btw. its about 650 documents and 430 tags. For now I just tried your 1-liner… Result: 1:4! The nested if takes avg 0.28s, your version 0.07s – now found 423 matches takes 30s instead of 120s for building the cache file (on my slow local machine). Looks like a good start for today – thank you @gebeer!1 point
-
Instead of the nested foreach you could do foreach ($tags as $tag) { $usage = $matches->find("tax_tag={$tag}")->count; $label = "{$tag->get('title')} {$usage}"; … } As for optimizing speed, how many pages are there potentially in $matches and in $tags, hundreds or thousands? To speed up queries, you can use $pages->findRaw() and only get the properties that you need for output and then work with those arrays in memory. Example: // find all pages with template document that have a tax_tag assigned // returns associative array indexed by page id with fields title, body and tax_tag where tax_tag is an associative array indexed by tax tag id with fields id and title // [ // 1234 => [ // 'title' => 'Page title', // 'body' => 'Page body content', // 'tax_tag' => [ // 4567 => [ // 'id' => 4567, // 'title' => 'Tax tag title', // ], // ... // ], // ], // ... // ] $matches = $pages->findRaw("template=document, tax_tag!=''", ['title', 'body', 'tax_tag' => ['id', 'title']]); Now you can use the resulting associative array $matches and do operations on it in memory without further DB calls. If you want to get a unique array of tags with usage count that are inside $matches, you could do something like this: // loop through matches to construct available tags array $tagsAvailable = array(); foreach($matches as $m) { // loop through tax_tags of each item foreach($m['tax_tag'] as $key => $tag) { // if key found in $tagsAvailable, tag is already there and we continue if(array_key_exists($key, $tagsAvailable)) continue; // get count of occurences of tax_tag id inside $matches items // and assign count as field 'usage' to the tag $tag['usage'] = count(array_filter(array_column($matches, 'tax_tag'), function($arr) use($key) { return in_array($key, array_keys($arr)); })); // add tag to available $tags $tagsAvailable[$key] = $tag; } } $tagsAvailable will look somewhat like this [ 1137 => [ 'id' => 1137, 'title' => 'Tag Title 1', 'usage' => 4, ], 1140 => [ 'id' => 1140, 'title' => 'Tag Title 2', 'usage' => 7, ], ] Now you can use $tagsAvailable to render your tags and $matches to render your result list. EDIT: I did this with $matches only containing 22 items and $tagsAvilable resulting in 5 items and the whole operation took 0.01ms and used 2.6kB of memory. So this should scale to hundreds or even thousands of matches. Though if you have that many matches you might consider chunk processing and paginating them.1 point
-
Hi, you can completely customize the comment form and comments list. Have a look at https://github.com/processwire/processwire/blob/master/wire/modules/Fieldtype/FieldtypeComments/CommentFormCustom.php and https://github.com/processwire/processwire/blob/master/wire/modules/Fieldtype/FieldtypeComments/CommentListCustom.php The classes are well documented. This should get you started.1 point
-
The way MarkupCache works simply does not allow for this nested use, IIRC. My advice is to just use the database cache (class WireCache, available as $cache or cache()), because it’s much easier to use and understand and probably just as fast. Obviously the speed depends on your setup, but if the database is on the same system and fits into memory it’s probably even faster than the filesystem. Also I would only cache the individual items, since renderResultList() doesn’t add anything interesting. It’s just going to complicate invalidation and bloat the cache table. With $cache you can preload all items in one roundtrip to the database, that should suffice. For more speed gainz you can always look into ProCache. As a sidenote, you’re overriding the WireCache $cache variable with MarkupCache, which may be a source of mistakes to watch out for.1 point
-
The computer mostly did a good job, but I wouldn’t put it quite like this. One major thing to note about ImageMagick is that it is generally used without a graphical user interface and in fact without immediate user input. If the server on which you run ProcessWire has ImageMagick installed, ProcessWire can use it to create versions of your images in different sizes such as thumbnails. Shared hosting providers sometimes offer this (some don’t, but ProcessWire can also use a different library called “GD”). Basically you wouldn’t manually use ImageMagick before uploading images to your site. FileZilla can be used to install ProcessWire and transfer your own PHP code, but for uploading images and managing your webcomic, you would use ProcessWire’s admin area. That is, you would upload images over HTTP instead of FTP. ProcessWire then automatically creates thumbnails and puts everything where it needs to be (on the file system and in the database). To do this as a user you don’t need to know what any of these things mean. You just click upload, choose your images and hit save.1 point
-
This week we've got ProcessWire 3.0.211 on the dev branch. Relative to the current main/master version (3.0.210), this version is 20 commits ahead. Many of the additions are user-submitted pull requests, and there are also several minor issue fixes too. Full details can be found in the dev branch commit log. I'd been planning on merging InputfieldTinyMCE into the core on the dev branch almost right away, but with so many minor fixes and improvements being added (that don't need much testing) we may put out another main/master version first in the short term, and then merge in InputfieldTinyMCE. Thanks for reading and have a great weekend!1 point
-
Thank you! Will give it a try again. Update: Sorry, I was in hurry. I just saw you made a video for me. This is like VIP support ?1 point
-
Upate: I've created a video for you: Why do you want to do that? You don't need to ? Correct. That's why I didn't build it that way. // content of migrate.php @ 2023-01-01 // create the foo field and add it to the foo template $rm->createField('foo', [...]); $rm->addFieldToTemplate('foo', 'footpl'); // content of migrate.php @ 2023-01-10 // remove foo field and create bar field instead // "true" makes it "quiet" - if not set it will write a log on every // following migration that says "Field foo not found" $rm->deleteField('foo', true); $rm->createField('bar'); $rm->addFieldToTemplate('bar', 'footpl'); // content of migrate.php @ 2023-01-16 // refactor to array syntax // that's nicer because you can instantly sort fields on the template // in the example we add 'bar' on top of 'title' $rm->deleteField('foo', true); $rm->migrate([ 'fields' => [ 'bar', ], 'templates' => [ 'footpl' => [ 'bar', 'title', ], ], ]); https://github.com/baumrock/RockMigrations/wiki/Ship-your-Module-with-Custom-Page-Classes This is an advanced example.1 point
-
When we've needed to import images from an old CMS (looking at you WordPress) to a new PW installation then I've just used the API to add an image to a field by passing the URL of the image. I normally just run a PHP script that looks something like this <?php namespace ProcessWire; /** * * Import data from a CSV * */ // Boostrap PW. // this example lives in an 'import' folder include("../index.php"); echo 'in import <br><br>'; // read in data from your CSV file in the same folder while ($row = $files->getCSV('your_export.csv')) { // in this case we already had pages created in PW which we mapped to the old system // using a field 'article_key' so we're using that here to find the right page. // You can do something similar or you could create a new page here. $article_key=$row['article_key']; $p = $pages->get("template=article,article_key={$article_key}"); if ($p->id) { // check we've got a page echo '<br> Got page ID: ' . $p->id; // Check if this CSV row has a field containing our image path if ($row['article_pic'] !== '') { // in this case it was the path to a url on the old site (which obvs // still needs to be accessible online). $post_image = 'https://our-old-site.co.uk/uploads/' . $row['article_pic']; $p->setOutputFormatting(false); $p->featured_image->removeAll(); // remove any old version of the image if you want $p->featured_image->add($post_image); // add our new image $p->save(); echo ' Added: ' . $row['article_pic'] . PHP_EOL; } else { echo ' No image'; } } } This code is just cobbled together from things I've done in the past so you'll need to fix it your needs. If you have 4,500 images then you'll want to run the import in batches rather than trying to do them all at once.1 point
-
1 point
-
The PAGEGRID beta will be released next week! If you want to help testing this module and try it yourself for free, please apply for the beta test.1 point
-
Version 1.1.0 is here. - added template edit link - added link for editing field in template context - using modals - style update: changed link icons, field name, tooltips, ...1 point
-
You can always reset your password just by pasting this temporarily into any one of your templates, and then viewing a page that uses the template: $u = $users->get('admin'); // or whatever your username is $u->of(false); $u->pass = 'your-new-password'; $u->save();1 point