Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation since 07/01/2021 in all areas

  1. While the version remains at 3.0.181 this week, there have been several core updates committed to the dev branch containing a mixture of issue fixes, new features, core optimizations and upgrades. This is likely to continue for another minor version or two while we prepare for our next master branch release. The most visible improvements this week can be found in ProcessField and ProcessTemplate (aka Setup > Fields, Setup > Templates). The main list of fields (Setup > Fields) has been improved with some new supported icon indicators that now identify fields that have template overrides/context settings, among others. All of the existing icon indicators have also been updated with contextual links so that clicking them takes you to the relevant part in the template editor. My favorite update here though is that the "Type" column in the fields list now indicates the Inputfield type in cases where it matters. For instance, rather than saying "Textarea" for the "body" field, it now says "Textarea/CKEditor". Likewise, Page and Options fields indicate what kind of input is used (i.e. "Page/AsmSelect" or "Options/Checkboxes"), as do some other types. Clicking on the "Templates" quantity now takes you to the "Add/remove from templates" field, rather than just showing you a list of templates. Similar updates were made to the ProcessTemplate main list of templates (Setup > Templates) though there wasn't as much to do there. Once you are actually editing a template, the instructions on the "Basics" tab have been improved to clarify what you can do on this screen. For instance, many don't know that you can click a field name/label to edit it in context, or that you can click and drag the percent indicator to adjust the field's width in the editor, all from this tab in the template editor. Now the description outlines these features. When you select a new field to add to your template, it now reveals a note that clarifies you must save before the field becomes editable in the context of that template. These are minor updates, but combined I think they add significant clarity, especially for new users. Stepping outside of the core, I've been working quite a bit this week on the PagesSnapshots module I told you about a couple of weeks ago. It lets you save or restore snapshot versions of pages, and it doesn't have any notable limitations in terms of fields. At this stage it is working fully with repeaters, matrix repeaters, nested repeaters, Page Tables and even paginated Table fields with thousands of rows. It can restore to the original page, to a new page, or to another page of your choice. And it is very fast. Eventually ProDrafts will use its API to handle saving and publishing of draft versions. Initially I plan to release it in one or more of the existing module sets (like ProDevTools or ProDrafts or both), and longer term it may be added to the core. I've got another week or two of work to cover with it, but at this stage the API is fully functional and working well, so I'm now beginning to focus more on the Process module (user interface) side of it. Thanks for reading and have a great weekend!
    29 points
  2. This week has been a continuation of last week in terms of ProcessWire development. While a lot of work has been done, there's not much new to report relative to last week, but I like to check in and say hello either way. I enjoyed putting a lot of time into the PagesSnapshots module this week and it's coming along nicely, getting closer every day. Today I've focused primarily on resolving GitHub issue reports, and am going to keep doing that every week till our next master version, which I hope to be ready in 2-3 weeks (same for the new module). But we'll adjust as needed. The next master version is going to be really solid, so I'm getting excited about it. I will likely bump the dev branch version to 3.0.182 next week. There were a couple more minor issues I wanted to get resolved in 3.0.182 but needed to spend a little more time with before I commit to the dev branch. Thanks and have a great weekend.
    24 points
  3. ProcessWire 3.0.182 is now posted on the dev branch. For a review of what's in this version, see the "Latest core updates" section of ProcessWire Weekly #374 and #375 plus this week there have been 12 additional commits with new issue resolutions, improvements and additions which can be found in the dev branch commits log. It's possible that ProcessWire Weekly #376 will also cover some of these updates when it is released too. I'd planned on even more in this version, but ended up losing a day of work yesterday as we had no electricity all day (it happens). So I worked outside in the yard instead— 3 issues were resolved, 4 improvements were made and 1 garage was organized. Focus in the coming weeks is on our next main/master version (core, not yard). If you have a chance, please take a moment to add sites you've built with ProcessWire to our sites directory. And if it's one that's already in the directory, feel free to add it again when/if it goes through a major redesign or redo. I'm really motivated by seeing the great work that all of you do and always enjoy seeing more of it. Plus, I'm thinking @teppo also likely looks at the newly submitted sites when considering the site of the week for his ProcessWire Weekly issues. If you find the existing categories on the submission form don't quite match a site you want to add, please send me a PM to let me know and I may be able to add new categories. Thanks for reading and have a great weekend.
    20 points
  4. Inertia Adapter ProcessWire Module Hello! Long time no see. I created this module so you can use Inertia.js (https://inertiajs.com/) with ProcessWire. Description Inertia allows you to create fully client-side rendered, single-page apps, without much of the complexity that comes with modern SPAs. It does this by leveraging existing server-side frameworks. Inertia isn’t a framework, nor is it a replacement to your existing server-side or client-side frameworks. Rather, it’s designed to work with them. Think of Inertia as glue that connects the two. Inertia comes with three official client-side adapters (React, Vue, and Svelte). This is an adapter for ProcessWire. Inertia replaces PHP views altogether by returning JavaScript components from controller actions. Those components can be built with your frontend framework of choice. Links - https://github.com/joyofpw/inertia - https://github.com/joyofpw/inertia-svelte-mix-pw - https://inertiajs.com/ Screenshots
    19 points
  5. This week we have 12 new commits to the dev branch largely focused on resolving older issue reports. Since the focus right now is primarily on getting the next master version live, we'll likely keep this focus for the next week or two, making sure the next big release version strikes the best balance between new features, issue resolutions and timeliness. Thanks for being here and have a great weekend!
    18 points
  6. I developed the new Geffen Playhouse website over the course of 2018/2019 and launched it in September 2019. It has been perhaps the largest project I have been involved in. The Geffen Playhouse went through an entire re-branding done by Base (including a custom font), and I worked with Teak on the new website. Website https://www.geffenplayhouse.org/ Wikipedia https://en.wikipedia.org/wiki/Geffen_Playhouse Base write-up https://www.basedesign.com/work/geffen-playhouse-always-geffen-playhouse-always-new Teak SF write-up https://teaksf.com/work/geffen-playhouse-ticketing-ecommerce-website-design/ Another write-up: https://www.laurentakayama.com/geffen Their previous website was severely antiquated and it wasn't a responsive website (as of 2019!). Instead, it forwarded mobile users to a "mobile-friendly" website on a different subdomain, which I think was hosted by a third party service. However the data containing all the actors, shows, seasons, news and press articles were all in there. So one major aspect of this website was de-duping and importing their data into ProcessWire, along with some post-import cleaning… that's ~25 years of data. The site is built with UIkit 3 for the most part, and also uses FullCalendar for the large and small calendars. There is a custom integration with AudienceView, their ticketing system, which is used to import all the performance showtimes of their shows into ProcessWire. It's not the easiest API to work with (XML), but I eventually got it working. Repeater Matrix is being heavily used for section-based page building. Building out all the necessary matrix types took a long time as there was quite a bit of thinking what types and layouts we needed as we went along. However the end result has given the editors a lot of flexibility. ProCache is being used as well, including a CDN for all assets. This is crucial because when opening season sales are announced, the site gets slammed, but with caching turned on, it's not a problem anymore. On a deeper level, the site uses my new (well 2 years old now), universal and very opinionated base module that provides a menu builder, a standard set of fields/templates/pages, and a bunch of other tweaks that I tend to use on every site. All the fields, templates and pages are set up in a streamlined and editor friendly way. I wasn't able to access their previous CMS backend for various reasons (I only got the MySQL dump), so when developing the site and data model in ProcessWire, I was able to completely re-envision the editor experience and the data model without bias. A quote from one of the marketing directors at Geffen Playhouse: "We absolutely love ProcessWire."
    18 points
  7. ProcessWire 3.0.181 has fixes and improvements as usual, but the biggest addition is a nice pull request from @LostKobrakai, plus major updates to our Helloworld and ProcessHello demonstration modules. This post covers it all— https://processwire.com/blog/posts/pw-3.0.181-hello/
    17 points
  8. Hi all, we made a small module for sending SMS via Sms77.io. It supports sending to one and multiple users. You can download it from GitHub and follow the instructions on how to install it - it is quite easy. An API key is required for sending, get yours for free @ Sms77 and receive 0,50 €. Hope this helps somebody and we are open for improvement suggestions! Best regards André
    13 points
  9. Reputation count is now back next to post count 🙂
    13 points
  10. Pete and I have been using Postmark in some PW based projects at reasonable scale (>13k emails a month) and have found it to be an exceptionally good API-based transactional email provider with fast delivery times and great availability. It seems strange that there is no WireMail offering (as far as we know of anyway) that supports Postmark, so we thought we'd throw one together in case anyone else in the community wants to give Postmark a try. NB: This is not the code we use in our production systems, just a rainy-day project to fill a gap in the WireMail ecosystem. However, it should be sufficient to get you going with Postmark. We hope you find it useful and please let us know if you find any issues. WireMailPostmark module on Netcarver's github account. Screenshot from my test account:
    12 points
  11. You can achieve this with hooks to Page::addable and Page::editable. 1. Define access controls for the news-region and news-article templates so that users with the news-editor role can add children under news-region pages and they can edit and create news-article pages. 2. Create a news_regions Page Reference field which allows pages with template news-region. Best to make it a "multiple" AsmSelect field so it covers the potential case that a user needs to add/edit within multiple regions. Add this field to the user template. 3. Edit the news-editor users and set the regions they are allowed to add/edit within. 4. In /site/ready.php: $wire->addHookAfter('Page::addable', function(HookEvent $event) { /** @var Page $page */ $page = $event->object; $user = $event->wire()->user; // Return early if PW has already determined that the user is not allowed to add if(!$event->return) return; if($user->hasRole('news-editor') && $page->template == 'news-region') { // Addability depends on if the news_regions Page Reference field has the page $event->return = $user->news_regions->has($page); } }); $wire->addHookAfter('Page::editable', function(HookEvent $event) { /** @var Page $page */ $page = $event->object; $user = $event->wire()->user; // Return early if PW has already determined that the user is not allowed to edit if(!$event->return) return; if($user->hasRole('news-editor') && $page->template == 'news-article') { // Editability depends on if the news_regions Page Reference field has the parent page $event->return = $user->news_regions->has($page->parent); } }); The result for user "ringo": You could add all the regions to that user's page, but personally I would create a different news-supereditor role and just not make them subject to the restrictions in the hooks.
    12 points
  12. Select Images An inputfield that allows the visual selection and sorting of images, intended for use with the FieldtypeDynamicOptions module. Together these modules can be used to create a kind of "image reference" field. Integration with FieldtypeDynamicOptions InputfieldSelectImages was developed to be used together with FieldtypeDynamicOptions (v0.1.3 or newer): Create a Dynamic Options field. Choose "Select Images" as the "Inputfield type". Select Images appears in the "Multiple item selection" category but you can set "Maximum number of items" to 1 if you want to use Select Images for single image selections. Define selectable options for the field via a FieldtypeDynamicOptions::getSelectableOptions hook. See some examples below. FieldtypeDynamicOptions is recommended but is not a strict requirement for installing InputfieldSelectImages in case you want to use an alternative way to store the field data. Selection of Pageimages In this example the field allows selection of Pageimages that are in the "images" field of the home page. The field will store URLs to the Pageimages so it works as a kind of "image reference" field. You can use the "Format as Pagefile/Pageimage object(s)" option for the Dynamic Options field to have the formatted value of the field be automatically converted from the stored Pageimage URLs to Pageimage objects. $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get Pageimages within the "images" field on the home page foreach($event->wire()->pages(1)->images as $image) { // Add an option for each Pageimage // When the key is a Pageimage URL the inputfield will automatically create a thumbnail // In this example the label includes the basename and the filesize /** @var Pageimage $image */ $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}"; } $event->return = $options; } }); Selection of image files not associated with a Page When not working with Pageimages you must add a "data-thumb" attribute for each selectable option which contains a URL to a thumbnail/image. In this example the field allows selection of image files in a "/pics/" folder which is in the site root. $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get files that are in the /pics/ folder $root = $event->wire()->config->paths->root; $path = $root . 'pics/'; $files = $event->wire()->files->find($path); // Add an option for each file foreach($files as $file) { $basename = str_replace($path, '', $file); $url = str_replace($root, '/', $file); // The value must be an array with the following structure... $options[$url] = [ // The label for the image 'label' => $basename, 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => $url, ], ]; } $event->return = $options; } }); The field values don't have to be image URLs The values stored by the Dynamic Options field don't have to be image URLs. For example, you could use the images to represent different layout options for a page, or to represent widgets that will be inserted on the page. Also, you can use external URLs for the thumbnails. In the example below the options "calm" and "crazy" are represented by thumbnails from placecage.com. $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "calm_or_crazy" if($field->name === 'calm_or_crazy') { $options = []; // Add options that are illustrated with thumbnails from placecage.com $options['calm'] = [ // The label for the option 'label' => 'Nicolas Cage is a calm man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/260/260', ] ]; $options['crazy'] = [ // The label for the option 'label' => 'Nicolas Cage is a crazy man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/c/260/260', ] ]; $event->return = $options; } }); Field configuration You can define labels for the button, notices, etc, that are used within the inputfield if the defaults don't suit. https://github.com/Toutouwai/InputfieldSelectImages https://processwire.com/modules/inputfield-select-images/
    9 points
  13. A few days ago I stumbled upon this old module, which had been laying in the modules directory of one of my sites since 2017 in a half-finished state. I have no recollection why I left it like that, but figured it might be useful for someone, so here we go: https://github.com/teppokoivula/Snippets https://processwire.com/modules/snippets/ Snippets is a tool for injecting front-end code snippets (HTML/JS/CSS) into page content. The way it works is that you create a snippet — say, a Google Analytics tag — and then choose... which element it should be tied to (there are some pre-populated choices and custom regex option), whether it should be placed before/after said element or replace it entirely, and which pages the snippet should apply to. The "apply to" option also has some ready to use options (such as "all pages" and "all non-admin pages") or you can select specific pages... or use a selector. Snippets are regular markup, with the exception that you can use values from current page (behind the scenes the module makes use of wirePopulateStringTags()). Available hooks: Snippets::isApplicable, which receives the snippet object and current Page object as arguments and returns a boolean (true/false). Snippets::applySnippet, which receives the snippet object, page content (string), variables (an object derived from WireData), and an array of options for wirePopulateStringTags() as arguments and returns the modified page content string. That's just about it. It's a pretty simple module, but perhaps someone will find this useful 🙂
    8 points
  14. Hello wonderful people, I've recently started using devdocs.io as my go-to reference for various programming languages and libraries; it's a great resource. Would be nice to see PW's API documentation in there too, but I don't yet know what needs to be done for this to happen. In the meantime, enjoy the docs :)
    8 points
  15. I really must make more of an effort to add more sites to this forum - we've done some nice work really. Women / Theatre / Justice is a case in point. WTJ is the umbrella title for research and public engagement activities undertaken by academics from various universities in partnership with Clean Break theatre company. Clean Break was founded in the 1970s by two women in prison and focuses on using theatre to help create positive change in the lives of women with experience of the criminal justice system. We wanted to reflect the origins of the organisation so we created a home made 'zine' like design with typewriter fonts and adding noise to the photographs and images to get a photocopied feel: There's not too much bespoke coding going on functionally - we created the usual blog and events as well as a simple photogallery, but most of the technical effort went into working out the best way to apply textures and filters to the images so that the admins could upload new content without needing to phaff around in photoshop ( CSS 'backdrop-filter' for the win). One interesting thing got thrown up in accessibility testing; originally we'd created the design using a fixed with typewriter font and even though we'd set a pretty large font size with good contrast that passed our automated accessibility testing, we found that real world users still had difficulty reading it. So we changed that to a more modern and readable slab serif. Testing with real users is always a good move.
    8 points
  16. Update: Here's a little snippet how to use dynamic navigation items using the NavJSON feature: // in getModuleInfo() 'useNavJSON' => true, // enable JSON nav feature 'nav' => [ [ 'url' => 'contacts', 'label' => 'Contacts', 'icon' => 'users', ],[ 'url' => 'mailings', 'label' => 'Mailings', 'icon' => 'folder-open-o', 'navJSON' => 'mailings-nav', // this will call executeMailingsNav() to get items ],[ 'url' => 'groups', 'label' => 'Groups', 'icon' => 'users', ], ] // and in the module /** * JSON nav for mailings */ public function executeMailingsNav() { $options['add'] = null; // no link to add pages $options['edit'] = $this->mailer()->url."new/?channel={name}"; $options['itemLabel'] = 'label'; // use the label property as item label (default is name) $options['items'] = []; foreach($this->mailer()->channels as $channel) { $data = $this->wire(new WireData()); /** @var WireData $data */ $data->name = $channel->short; $data->label = "New ".$channel->name; $data->icon = "plus-circle"; $options['items'][] = $data; } return parent::___executeNavJSON($options); }
    7 points
  17. A while back we had to import a few hundred users into PW from a site we were porting over. I needed to generate new passwords for them that matched our PW password requirements and couldn't find anything super useful so I scraped together a word list and a quick bit of code to spit out a list of reasonable diceware type passwords. Since then it's been surprisingly useful so I've tidied things up a bit and put it on Github in case anyone else could make us of it. There's a demo and Github link at https://millipedia.com/millco-diceware/ It's pretty simple and very Anglo-centric ... and possibly not suitable if you're a secret government agency ... but hey, it might come in handy.
    7 points
  18. But this isn't a solution for touch devices which don't have a hover state, and we all know that touch devices are a big share of internet use. There's discussion in the Invision Community support forums about how to bring back reputation points into the author pane (some of which I can't see but maybe @Pete can)... https://invisioncommunity.com/forums/topic/462326-current-ranks-–-and-–-reputation/ https://invisioncommunity.com/forums/topic/462099-bring-back-reputation-in-userinfopane/ Could we implement that here? Also, it would be nice to bring back custom member titles for those who had them (e.g. Ryan's reiska title): https://invisioncommunity.com/forums/topic/462409-46-how-to-restore-custom-member-titles/
    7 points
  19. This was a site we put together for the department of Arts and Culture at Queen Mary University in London (QMUL). https://conversations.qmul.ac.uk/ QMUL invited artists to converse with academics from across the university: A dentist spoke to a sculptor. A performance artist to a historian. A mathematician to a choreographer... The site needed to showcase not only the work of the artists but also reflect the back and forwards of the conversations which toook place during lockdown and were recorded over Zoom. We ended up implementing templates which allowed the admins to upload various different types of media for each side of the conversion - mostly video clips of the Zoom sessions but also slideshows, mp3 audio files, images and documents. To do this we used a few ProFields fields, most notably Repeater Matrix. We also built a simple events management system for an online Conversation Week event. Other modules we used included ProCache and Page Field Edit Links and Login Persist. On the front end we used PhotoSwipe for the slideshows and Plyr for the embedded audio and video files (mostly to give a consistant look). Oh and the css animated menu icon is from https://jonsuh.com/hamburgers/ - which are really useful (honestly I think I've used them in our last 4 projects).
    6 points
  20. This module implements the website live chat service from tawk.to. Actually the module doesn't have to do much. It just need to inserted a few lines of JavaScript just before the closing body tag </body> on each side. However, the module offers additional options to display the widget only on certain pages. Create an account Visit https://www.tawk.to and create an account. It's free! At some point you will reach a page where you can copy the required JavaScript-code. Open the module settings and paste the JavaScript-code into the field as shown below. Click "Submit" and that's all. Open the module settings The settings for this module are located int the menu Modules=>Configure=>LiveChatTawkTo.
    6 points
  21. HTMX is quite good. $config->ajax won't detect HTMX requests though, so I recommend adding this to your /site/ready.php: // if htmx request, DO NOT use _main.php if(array_key_exists('HTTP_HX_REQUEST', $_SERVER)) { $config->appendTemplateFile = ''; $config->htmxRequest = true; } That assumes you are using the Markup Regions output strategy by the way. Use $config->htmxRequest as needed.
    6 points
  22. Here is my Caddy 2 config for a PW site: mysite.fi, www.mysite.fi { encode gzip tls my@email.com root * /var/www/mysite/pw file_server php_fastcgi unix//var/run/php-fpm/php-fpm.sock { health_timeout 10000s } @deny_hidden path_regexp /\. @deny_root path_regexp /(CONTRIBUTING|COPYRIGHT|LICENSE|README|htaccess)\.txt @deny_assets path_regexp ^/site(-[^/]+)?/assets/(.*\.php|backups|cache|config|install|logs|sessions) @deny_install path_regexp ^/site(-[^/]+)?/install @deny_config path_regexp ^/(site(-[^/]+)?|wire)/(config(-dev)?|index\.config)\.php @deny_modules path_regexp ^/((site(-[^/]+)?|wire)/modules|wire/core)/.*\.(inc|module|php|tpl) @deny_templates path_regexp ^/(site(-[^/]+)?|wire)/templates(-admin)?/.*\.(inc|html?|php|tpl) rewrite @deny_hidden /denyaccess rewrite @deny_root /denyaccess rewrite @deny_assets /denyaccess rewrite @deny_install /denyaccess rewrite @deny_config /denyaccess rewrite @deny_modules /denyaccess rewrite @deny_templates /denyaccess # global rule try_files {path} {path}/ /index.php?it={path}&{query} log { format single_field common_log output file /var/log/www/access.log { roll_size 50MiB roll_keep 5 roll_keep_for 168h } } }
    5 points
  23. I'll make a video of this in the near future but here's the extremely short version of the page builder journey I went on. There are 2 approaches that ultimately make sense: section-based and component-based Section Based: This is the "intended" way repeater matrix (and similar fieldtypes in other CMSes -- WordPress ACF has flexible content, Craft has Matrix) is supposed to be used. Each matrix-type represents a section with the necessary fields to populate that section. No depth. Pros easy for editors to understand they just need to put in the content and it will work; no need to worry about layout because it's baked in; hard to mess up Cons not flexible / requires a lot of discussion to make sure all sections, fields and their variations are covered can't move content from one section to another can lead to field bloat Component Based: Each matrix-type represents a component (headline, text, image, video, description list, etc.). Furthermore, 4 additional matrix-types for layout: section, container, row, column. Depth is enabled as well as the new "family friendly" option which I pushed Ryan to implement earlier this year and makes this approach more usable. Pros extremely flexible; closely follows YOOtheme Pro's approach but in a matrix-way can move content anywhere Cons higher learning curve compared to section-based since layout and nesting is involved structures must be repeated over and over again since each component is independent uses many more pages behind the scenes -- Which approach is better? The answer is it depends on the nature of the content of the site and the technical ability of editors. If the site has a level of consistency from page to page, the section-based approach would work better. However if there is less consistency and/or the editors need and/or are willing to put in extra effort to build truly unique pages, then the component-based approach makes sense. The thing is, you can actually use both approaches. The matrix field name for the section-based approach that I use is called 'sections' and the component-based approach field name is called 'builder'. My base module sets up these fields automatically and uses Mystique for all the settings. You can add both the 'sections' and 'builder' fields to a template and the editors can decide which one makes more sense given a page's needs.
    4 points
  24. FieldtypeDecimal uses InputfieldFloat as its inputfield, and it's the renderValue() method that determines what is shown when a field is not editable. So you could do this: $wire->addHookAfter('InputfieldFloat::renderValue', function(HookEvent $event) { /** @var InputfieldFloat $inputfield */ $inputfield = $event->object; $field = $inputfield->hasField; if($field && $field->name === 'your_field_name') { $event->return = '$' . number_format((float) $inputfield->value, 2); } });
    4 points
  25. According to the specs it should work like this (not tested) $results = $pages->find("title~=word1|word2|word3, title~=word4"); title=x, title=y is AND AND selectors: matching more than one value in the same field There will be instances where you need to say that a specific field matches more than one selector. This is simple, just specify all the conditions you need to match as separate selectors in the same string. For example: height>500, height<=1000 This AND selector matches all pages that have a "height" field greater than 500, and less than or equal to 1000. title=a|b|c, title=y should be the right combination https://processwire.com/docs/selectors/#and-selectors
    4 points
  26. In most cases I expect people to use the Select Image inputfield in conjunction with the Dynamic Options fieldtype. And regarding the value of a Dynamic Options field: The Select Images input type is a bit of a special case because it can be a "multiple" or a "single" input type: And beyond that there is a config option for Dynamic Options that can be used if the values that are saved are paths or URLs to Pagefiles or Pageimages, and this could be a good option when you're using Select Images as the inputfield: When that option is enabled you could use the Pageimage sizing methods on the field value. Note that for multiple images the value is an array of Pageimage objects and not a Pageimages object. That's because these modules are intended to be flexible enough to allow selection of images from more than one PW page but a Pageimages object only supports Pageimage objects associated with a single page (see the class constructor). The readme for Select Images says: So you can have the value for each option (thumbnail) be any string you like and output that in a template file or use it in a conditional to affect the page markup in some way. But I think my most common use case will be as an "image reference" field to select URLs to Pageimages and have the formatted value be a Pageimage or array of Pageimage objects. People have suggested different use cases for an image reference field in the GitHub request linked to at the start of the readme: https://github.com/processwire/processwire-requests/issues/207 Personally I have used image references in these scenarios: To allow an editor to select a social media "share" image from among all the uploads to Images fields on a page. In cases where an editor may only select from a collection of existing images and is not allowed to upload new images. Similar to the previous example, but where the editor needs to choose images that have been prepared in advance to fit in spaces with particular aspect ratios. So for a landscape space they can only select landscape images, square space can only select square images, etc. The allowed options for a Dynamic Options field are determined at runtime according to the FieldtypeDynamicOptions::getSelectableOptions hook you are using. These allowed options are used to validate any stored value when it is accessed. So if an image that was referenced is deleted then it will no longer be in the value you get from $page->your_dynamic_options_field
    4 points
  27. Are you using pagefileSecure? If you do, or otherwise use ProcessWire to send the file, you can change the config to force mp3s to always download using this incantation: $config->fileContentTypes = array_merge($config->fileContentTypes, ['mp3' => '+audio/mpeg']); The + sign in front of the mime type will tell PW to use the header “Content-Disposition: attachment” when sending the file type in question. Cf. https://github.com/processwire/processwire/blob/master/wire/config.php#L632
    4 points
  28. Maybe that's what you are looking for: https://processwire.com/blog/posts/pw-3.0.137/#on-demand-mirroring-of-remote-web-server-files-to-your-dev-environment
    4 points
  29. I've tweaked the default colours (using the handy tool at https://learnui.design/tools/accessible-color-generator.html ) to be as close to the current colours as possible but still hitting AA contrast requirements. Here's before and after screenshots for comparison. I don't think it's too drastic a change... The admin.less changes for that are: /************************************************************** * Primary variables * * Adjusted to hit AA accessibility. * */ @reno-text-color: #354b60; @reno-link-color: #df2b5b; // #e83561; @reno-link-hover-color: darken(@reno-link-color, 10%); @reno-dark-background: #1C2836; @reno-muted-background: #f0f3f7; @reno-muted-text-color: #6f7580; // #8d939e; @reno-muted-text-color-alternate: #577898; // #6c8dae; @reno-primary-background: #008668; // #3eb998; @reno-secondary-background: #e2e9ef; @reno-success-background: #8bccde; @reno-success-color: @reno-text-color; @reno-warning-background: #FFD15E; @reno-warning-color: @reno-text-color; @reno-danger-background: #bc283d; @reno-danger-color: #fff; @reno-alert-background: #ffd; @reno-alert-color: @reno-text-color; @reno-primary-headline-color: #1C2836; @reno-secondary-headline-color: lighten(@reno-primary-headline-color, 30%); @reno-notes-text-color: @reno-muted-text-color; @reno-notes-background: #ffd; @reno-masthead-background: @reno-dark-background; @reno-masthead-link-color: rgba(255, 255, 255, 0.7); @reno-masthead-link-hover-color: lightblue; @reno-masthead-link-current-color: #fff; @reno-masthead-icon-color: #6f7580; // #8d939e; @reno-masthead-search-text-color: #8d939e; // #008668 @reno-masthead-search-background: transparent; @reno-masthead-search-border-color: lighten(#253447, 10%); @reno-masthead-search-focus-background: lighten(#253447, 5%); @reno-masthead-search-focus-text-color: #fff; @reno-masthead-search-focus-border-color: @reno-masthead-search-focus-background; @reno-masthead-search-icon-color: @reno-masthead-icon-color; @reno-button-text-color: #fff; @reno-button-background: @reno-primary-background; @reno-button-hover-background: @global-link-color; @reno-button-secondary-background: @reno-muted-text-color-alternate; @reno-dropdown-background: #fff; @reno-dropdown-color: @reno-text-color; @reno-dropdown-hover-background: #e2e9ef; @reno-dropdown-border-color: #d9e1ea; @reno-dropdown-border: 1px solid @reno-dropdown-border-color; @reno-dropdown-nav-icon-color: #667982; //#97aab4; @reno-page-list-link-color: #7a002b; @reno-page-list-link-open-color: #bb153e; @reno-page-list-icon-color: @reno-link-color; @reno-page-list-link-active-color: @reno-link-hover-color; @reno-page-list-link-hover-color: @reno-link-hover-color; @reno-page-list-action-link-color: #fff; @reno-page-list-action-link-background-color: @reno-link-color; @reno-page-list-action-link-hover-color: #fff; @reno-page-list-action-link-hover-background-color: @reno-link-hover-color; @reno-inputfield-border: 1px solid #d9e1ea; @reno-inputfield-input-background: #f0f3f7; @reno-inputfield-input-border-color: #b1c3d4 #cbd7e3 #cbd7e3 #cbd7e3; @reno-inputfield-select-background: #f0f3f7; @reno-inputfield-select-border-color: #cbd7e3; @reno-form-radio-checked-background: @reno-muted-text-color-alternate; @reno-offcanvas-text-color: @reno-text-color; @reno-offcanvas-link-color: @reno-text-color; @reno-offcanvas-link-hover-color: @reno-link-color; @reno-offcanvas-link-open-color: @reno-link-color; @reno-offcanvas-background: @reno-secondary-background; @reno-offcanvas-search-background: #fff; @reno-language-tab-background: transparent; @reno-language-tab-color: @reno-text-color; @reno-language-tab-current-background: #d2dce6; @reno-language-tab-current-color: @reno-primary-headline-color; @reno-language-tab-hover-background: @reno-muted-background; @reno-language-tab-hover-color: @reno-text-color; @reno-language-tab-empty-color: @reno-muted-text-color; @reno-table-header-color: @reno-muted-text-color-alternate; @reno-table-header-current-color: @reno-primary-background; I think it'd be worth updating to the defaults to something like this - seems an easy step in the right direction.
    4 points
  30. Symprowire is a PHP MVC Framework based and built on Symfony using ProcessWire 3.x as DBAL and Service-Provider It acts as a Drop-In Replacement Module to handle the Request/Response outside the ProcessWire Admin. Even tough Symfony or any other mature MVC Framework could be intimidating at first, Symprowire tries to abstract Configuration and Symfony Internals away as much as possible to give you a quick start and lift the heavy work for you. The main Goal is to give an easy path to follow an MVC Approach during development with ProcessWire and open up the available eco-system. You can find the GitHub Repo and more Information here: https://github.com/Luis85/symprowire Documentation The Symprowire Wiki https://github.com/Luis85/symprowire/wiki How to create a simple Blog with Symprowire https://github.com/Luis85/symprowire/wiki/Symprowire-Blog-Tutorial Last Update 16.07.2021 // RC 1 v0.6.0 centralized ProcessWire access trough out the Application by wrapping to a Service https://github.com/Luis85/symprowire/releases/tag/v0.6.0-rc-1 Requirements PHP ^7.4 Fresh ProcessWire ^3.0.181 with a Blank Profile Composer 2 (v1 should work, not recommended) The usual Symfony Requirements Features Twig Dependency Injection Monolog for Symprowire Support for .env YAML Configuration Symfony Console and Console Commands Symfony Webprofiler Full ProcessWire access inside your Controller and Services Webpack Encore support Caveats Symfony is no small Framework and will come with a price in terms of Memory Usage and added Overhead. To give you a taste I installed Tracy Debugger alongside to compare ProcessWire profiling with the included Symfony Webprofiler So in a fresh install Symprowire would atleast add another 2MB of Memory usage and around 40ms in response time, should be less in production due to the added overhead of the Webprofiler in dev env
    4 points
  31. I can't say that I have a strong opinion here either. Also not sure if I grasp the whole context — when you say "routing" and mention URL segments, does that mean that you create routes automatically based on page structure (I assume so but...) or that there's a separate routing setup for front-end and page structure is only reflected in the data? Or do you mean something different with this? 🙂 This may or may not be related, but one thing to consider is whether you want Symprowire to be "all in", or rather something that can be used only for some parts of the site. Couple of examples from other MVC(ish) solutions: Template Engine Factory hooks before Page::render and replaces the response, but also provides hookable method (TemplateEngineFactory::shouldRenderPage) for preventing it from rendering the page, in which case the site will fall back to the regular rendering flow (whatever that happens to be). Wireframe takes an entirely different approach: instead of a hook ("enabled by default") you need to point templates that you want to render via it to the front controller file via Alternate Template Filename setting (more details in the docs). Essentially it's disabled by default. Wireframe is my pet project and something we've used for our production sites for a while now, so that's what I'm mostly familiar with. I intentionally decided not to use hooks, since I felt it was best to let the developer decide if they want to use Wireframe for everything, just a small part of the site, or something in between. In fact it's possible to skip the MVC structure entirely, and just use Wireframe for its partials and/or components 🤷‍♂️ Template Engine Factory may be a bit closer to Symprowire feature wise, although Symprowire clearly has a much larger scope (and is more opinionated). So not sure if any of what I've said here applies as-is 🙂
    4 points
  32. @Cybermano, I don't understand what you are doing with this line inside the foreach: $boat->barca_results->sort('id', $race); This doesn't seem to correspond to the correct use of either $wirearray->sort() or $pages->sort() so maybe you are getting mixed up with the methods? To go back to brass tacks (you may already know a lot of this but it might be useful for others)... The first meaning of "sort" as it relates to pages is the sort value. This is a column in the "pages" database table. Every page (including Repeater pages) has a sort value and this determines its sort position relative to its sibling pages (assuming no automatic sort has been set on the parent page or template). The sort value starts at zero, and the lower the number is the closer to the top of the sort order the page will be relative to its siblings. So in this page structure... The sort value of "Red" will be 0, the sort value of "Green" will be 1, and so on. In some circumstances you can end up with gaps in the sort values as pages are deleted or moved and that is where the $pages->sort() method can be used to "rebuild" those sort values under a parent. But in 99% of cases you don't need to worry about sort values because PW just takes care of it and you don't have to use $pages->sort() to sort your Repeater items so we can ignore that. You can set the sort value of a page the same as you would an integer field in the page's template: $page->of(false); $page->sort = 123; $page->save(); The second meaning of "sort" is the WireArray::sort() method. When it comes to pages, a PageArray is a kind of WireArray, and a RepeaterPageArray is a kind of PageArray, so this means we can use WireArray::sort() to sort a RepeaterPageArray. The method works by sorting the WireArray by one or more properties of its items. In the case of a RepeaterPageArray this could be something like the "modified" timestamp of the items or it could be a field such as "title". So you could do something like this to sort a field named "test_repeater" alphabetically by title (assuming that the Title field was used in the Repeater): $page->test_repeater->sort('title'); And you can get more advanced by adding a temporary custom property to the items in a WireArray/PageArray/RepeaterPageArray and then using WireArray::sort() on that temporary property. I'll show an example of that in a moment. If you have a page $p that contains a Repeater field "test_repeater" then you can sort the RepeaterPageArray as shown above in your template file and when you output the items in $p->test_repeater they'll be in the sort order you want. But if you wanted to save $p after you sorted test_repeater then it's not enough to just have the items in test_repeater in the right order and then save $p. And that's because PW uses the sort value of each Repeater page to determine the sorting of the Repeater field when it loads it from the database. So to make the sort order stick we need to save the sort value of each Repeater page in the RepeaterPageArray, and to do that we can use an incrementing counter that starts at zero. A couple of code examples... Sort test_repeater by title and save: // Get the page containing the Repeater items $p = $pages(1066); // Turn off output formatting for the page because we are going to save it later $p->of(false); // Sort the Repeater items alphabetically by title $p->test_repeater->sort('title'); // $i is a counter we will use to set the sort value of each Repeater item/page $i = 0; foreach($p->test_repeater as $repeater_item) { $repeater_item->of(false); $repeater_item->sort = $i; $repeater_item->save(); $i++; // Increment the counter } // Save the test_repeater field for $p $p->save('test_repeater'); More advanced sorting using a temporary property: // Get the page containing the Repeater items $p = $pages(1066); // Turn off output formatting for the page because we are going to save it later $p->of(false); // More advanced sorting: sorting by title length // We add a temporary "custom_sort" property to each Repeater item // And then sort the RepeaterPageArray by that custom_sort property foreach($p->test_repeater as $repeater_item) { $repeater_item->custom_sort = strlen($repeater_item->title); } $p->test_repeater->sort('custom_sort'); // $i is a counter we will use to set the sort value of each Repeater item/page $i = 0; foreach($p->test_repeater as $repeater_item) { $repeater_item->of(false); $repeater_item->sort = $i; $repeater_item->save(); $i++; // Increment the counter } // Save the test_repeater field for $p $p->save('test_repeater'); Hope this helps.
    4 points
  33. 10 templates is definitely not going to be a problem. There's no particular number of templates that you should never go above, but I've asked Ryan about this topic in a Pro module forum and he said: ...and in reply to my proposal of a strategy to reduce the number of templates needed in a project... On that particular project I currently have 51 templates and there are no noticeable performance issues.
    4 points
  34. I'll see what I can do about the rep count - in the meantime I've done something someone else asked for (and my older eyes needed) - increased font size on mobile. I haven't gone too far with it but it's a lot better than 13px 😄
    4 points
  35. Thank you all for the insight! This has been super helpful. This community continues to impress me with how helpful and kind everyone is. The more I learn about ProcessWire the more I like what I see! This gives me the direction I need to continue development. I appreciate it!
    4 points
  36. @gornycreative, me and @fliwire are not teaching you how to do your modals. Both Unpoly and htmx are js libraries that help you (amoung other things) to extract a portion of markup that they receive from a ajax requests and paste it where you want it (in a modal). That way you do not need to alter the rendering, but can define which part of a rendered markup you need.
    4 points
  37. I've got some core updates in progress but nothing major committed yet, so we'll save that for next week. But I wanted to briefly tell you about a module I'm working on here, which I plan to eventually put in core. I built it for some needs I've had here, but it will first be released in ProDrafts (so ProDrafts may start using its API), and in ProDevTools too, since it is also a developer tool. It's a snapshot versioning system for pages, but more of an API tool at this stage, kind of like the $pages API var, but for snapshots/versions of pages. It lets you create a snapshot of any page, including its fields and files (including core fields, ProFields, 3rd party fields, repeaters, nested repeaters, table fields with a million rows, etc.). The snapshots can be restored at any later time. Snapshots may also be restored to a different page, such as a new or existing page. In this manner, a module like ProDrafts may be able to use it to manage draft versions even better than it currently can, or at least that's the plan. Though since it's an API tool, my hope is that when/if it winds up in the core, others may be able to use it for stuff we've not thought of yet too. The module is a little different than my previous attempts (like what's in ProDrafts now) in that it does most of its work directly at the database level (and file system level, where applicable), which enables it work without needing to know much about the Fieldtype. Currently it is fully functional, but I have a few less common cases to work out before it's ready for release. Once installed, every page includes a Snapshots fieldset, which can be located in the settings tab of the page editor, or a separate tab in the page editor (configurable). When installed, every page has one, so long as the user has the appropriate permissions. There's a screenshot of what it looks like in the page editor below, but it is very simple at this stage, basically just enough for testing purposes. Every snapshot has a name that is unique on the page. You can overwrite a snapshot just by saving a snapshot with the same name on the same page. So you could for instance have a hook that creates a snapshot named "undo" right before a page is saved (in a Pages::saveReady hook), and in that way a module could add a basic undo capability to the page editor. This is just a simple example though. Thanks for reading, have a great weekend!
    4 points
  38. Process Images A basic, proof-of-concept Textformatter module for ProcessWire. When the Textformatter is applied to a rich text field it uses Simple HTML DOM to find <img> tags in the field value and passes each img node through a hookable TextformatterProcessImages::processImg() method. This is a very simple module that doesn't have any configurable settings and doesn't do anything to the field value unless you hook the TextformatterProcessImages::processImg() method. Hook example When added to /site/ready.php the hook below will replace any Pageimages in a rich text field with a 250px square variation and wrap the <img> tag in a link to the original full-size image. For help with Simple HTML DOM refer to its documentation. $wire->addHookAfter('TextformatterProcessImages::processImg', function(HookEvent $event) { // The Simple HTML DOM node for the <img> tag /** @var \simple_html_dom_node $img */ $img = $event->arguments(0); // The Pageimage in the <img> src, if any (will be null for external images) /** @var Pageimage $pageimage */ $pageimage = $event->arguments(1); // The Page object in case you need it /** @var Page $page */ $page = $event->arguments(2); // The Field object in case you need it /** @var Field $field */ $field = $event->arguments(3); // Only for images that have a src corresponding to a PW Pageimage if($pageimage) { // Set the src to a 250x250 variation $img->src = $pageimage->size(250,250)->url; // Wrap the img in a lightbox link to the original $img->outertext = "<a class='lightboxclass' href='{$pageimage->url}'>{$img->outertext}</a>"; } }); GitHub: https://github.com/Toutouwai/TextformatterProcessImages Modules directory: https://processwire.com/modules/textformatter-process-images/
    4 points
  39. Or you use the new InputfieldTextTags: https://processwire.com/blog/posts/pw-3.0.177/ 😉
    3 points
  40. Just in case... add this: https://processwire-recipes.com/recipes/logging-outgoing-emails/ to your setup and you will see each and every mail in the logs. Should only be used in DEV environments... privacy and such... you know.
    3 points
  41. Create a new php file inside template folder (or a subdirectory of it, ex. "inc") and paste your html portion you want to include in all the other templates, save it. Then, in all the templates, where you want that portion to appear just: <?php include('path/to/partial.php'); ?> // replace the path with yours.
    3 points
  42. Hi there, My company, Fixate, has been building and supporting ProcessWire sites for a number of years now. I've recently accepted an offer for a position at another company, and as the last dev on the team (the team is now primarily UI/UX), we need to find someone who can assist with client requests. We have clients in Canada, Zambia, and South Africa, so we're comfortable with any timezone you may be in. The projects predominantly have this setup: Dockerised services for quick setup Gulp to automate: builds and file revisioning SSH deployments backups / restorations Some of our client's sites: blu-line.co.za biocarbonpartners.com knowledgehub.southernafricatrust.org We're looking for someone to work directly with our clients when they need assistance. Feel free to contact me at larry U+0040 fixate.it Thanks! Larry
    3 points
  43. Wow... thanks again @teppo! https://weekly.pw/issue/374/
    3 points
  44. I did exactly the same thing a year or two ago. Here's the script I used (I've simplified the selector and a couple of other things, and added one comment). The script moves the image description and the ImageExtra field 'credit' to the new 'photo_caption' and 'photo_credit' fields. I don't know if you're familiar with running such scripts, but you simply put the script somewhere on your server and enter its URL in your browser. It might be a good idea to test with the saves commented out. <?php // Amend depending on where this script file is located include("../index.php"); $selectedRecords = $wire->pages->find("template=my-template"); echo "Pages with images: " . count($selectedRecords) . "<br/>"; $recordsProcessed = 0; foreach($selectedRecords as $thisRecord) { echo $thisRecord->title . "<br/>"; $recordsProcessed += 1; if(count($thisRecord->article_images)) { $thisRecord->of(false); foreach($thisRecord->article_images as $image) { $description = $image->description; $credit = $image->credit; $image->photo_caption = $description; $image->photo_credit = $credit; $thisRecord->save('article_images'); } $thisRecord->save(); } } echo "<p>Records processed: {$recordsProcessed}</p>"; ?> This was written for a one-off task, so I did nothing to refine it, but it worked. Note that at the time both saves were necessary, though I think this has been resolved now and, depending on which version of PW you are running, you may be able just to save the whole record - but it'd probably be easier to leave it as it is!
    3 points
  45. I loved Svelte too. For the routing part I mostly use Laravel and now ProcessWire (With the Inertia Module) with the https://inertiajs.com/ Inertia adapters. That simplify greatly the flow and you got the best of backend and frontend 🙂
    3 points
  46. I typically start with the raw HTML output design and as I work I notice elements that I use often and break those into partials. In my _main I typically have the spacing and grid scaffolding for most items with the understanding I may embed nested structure as part of my individual page template, and then I will load includes into those embedded areas as needed. I see _main as the foundation and outer walls of the house. I see the page templates with their regions as different layouts of walls/rooms within that house. I see the furniture and features of the rooms of the house as partial includes. Whether you choose for the rooms themselves to be adaptable or not is really up to you. It isn't necessary to use partials at all, but I find it cuts down a little bit on the template counts. A common tradeoff seems to be between the amount of code you put into your template file region replacements/append/prepend etc vs the number of different templates you have, and whether you want the presentation routing to be directed more by the selection of the templates or programmatic responses to data in the fields your templates use. It is possible to have very few template files that reroute and alter the display based on tons of selections made on the pages themselves, or it is possible to have individual templates for all of the different scenarios you may want to come up with - but for complicated sites I tend to prefer fewer templates, more function controls. I would say that for the end user having lots of templates and transparent choices/structure on the pw backend is safer with clients. Because you can control a lot of things about adding new content, etc. it is less likely that the user may alter some value and cause some other glitches on the site that you don't expect unless you are pretty strict with your unit testing. But if you have code content that is really never anything the client is going to have to be actively involved in, bringing stuff like that into the code side keeps down the clutter. Hanna code sits somewhere in the middle and I always feel like it is playing with fire but that's a pretty happy bridge between the two worlds if you want to try to give placement control to the client but want to maintain logic control.
    3 points
  47. I added this to my search.php template file. Hope that helps. $searchEngine->addHookAfter('Renderer::renderResult', function($event) { $p = $event->arguments[0]; $image = $p->getunFormatted('pdf_images|image|images|video_images')->first(); if($image) { $thumb = $image->size(0, 260, array('upscaling' => false)); $event->return = ' <div class="row"> <div class="small-12 medium-4 large-5 xlarge-4 xxlarge-3 columns teaser__image"> <a href="'.$p->url.'"> <img style="max-height: 300px" src = "'.$thumb->url.'" /> </a> </div> <div class="small-12 medium-8 large-7 xlarge-8 xxlarge-9 columns teaser__content"> ' . $event->return . ' </div>'; } });
    3 points
  48. u can use unpoly. Layers - Unpoly <a href="/post-page" up-layer="new" up-target='.content'>Open Content</a>
    3 points
  49. Hey @Pete... do you have an idea when we will have the reputation count back? The more I use the forum now, the more I somehow want it back as fast as possible. It's probably weird but... it was such a great indicator for a lot of things here. And no... it's not about my silly amount of reputation points.
    3 points
×
×
  • Create New...