Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 04/15/2020 in all areas

  1. Major new release, thanks in big part to @Robin S for ideas, testing, and feedback. 1. The Console panel now stores it's snippets on disk so they can be backed up, be under version control, and also editable via your external code editor (with direct link from the entry in the Console snippet sidebar). They are stored in either /site/templates/TracyDebugger/snippets/ or /site/assets/TracyDebugger/snippets/ Note that all your pre-existing snippets that were previously stored along with Tracy's module settings will be automatically written to disk when you upgrade to the new version of Tracy. 2. You can define a block of code to be prepended to the snippet when it is saved to disk. For example, Robin is using: <?php namespace ProcessWire; //<editor-fold desc="API variables"> /** * @var Config $config * @var Fieldgroups $fieldgroups * @var Fields $fields * @var Languages $languages * @var Modules $modules * @var Page $page * @var Pages $pages * @var Paths $urls * @var Permissions $permissions * @var ProcessWire $wire * @var Roles $roles * @var Sanitizer $sanitizer * @var Session $session * @var Templates $templates * @var User $user * @var Users $users * @var WireCache $cache * @var WireDatabasePDO $database * @var WireDateTime $datetime * @var WireFileTools $files * @var WireInput $input * @var WireLog $log * @var WireMail $mail * @var \ProCache $procache * @var FormBuilder $forms **/ //</editor-fold> 3. The SnippetRunner panel has been removed because it's no longer necessary with the new behavior of the Console panel. 4. The Console panel has a new inbuilt DB backup feature. If checked, it will backup your complete DB. The backups are stored under /site/assets/backups/database/ which is where Ryan's Database Backups module (http://modules.processwire.com/modules/process-database-backups/) stores its backups. It is recommended to install this module to make managing and restoring backups much easier. Backups can be automatically named: tracy-console-2020-04-14-18-23-38.sql or you can specify a custom name in the Console panel if you'd like to set something more meaningful before a major change. Automatically named backups are automatically pruned when there are a set number of backups (default is 25, but is configurable in module settings). 5. The user switcher panel now has the option to use a PW API selector string as a way to determine which users will be listed. 6. There is a new Quicklinks section at the top of the module settings to more quickly get to what you're looking for (code thanks to Robin). 7. Lots of other tweaks, fixes and improvements. 8. New core versions of Tracy and ACE editor. Please let me know what you think and if you have any problems. PS - please do a hard reload in your browser to ensure you get the new css and js files.
    4 points
  2. It’s actually really easy: $brands = $pages->find('template=brand'); echo $brands->sort('-numReferences')->each("<li>{title}: {numReferences}"); Whether it’s very efficient is a different story. I wouldn’t know. Be aware that you have to sort the PageArray. Putting it in the find() selector won’t work. Edit: actually, you probably want hasReferences, as per the documentation: $numReferences Total number of pages referencing this page with Page reference fields. $hasReferences Number of visible pages (to current user) referencing this page with page reference fields.
    3 points
  3. Big, big thanks @adrian for all your ongoing work on Tracy Debugger - it just gets better and better!
    3 points
  4. Just to bring another point in favor of that: The limits of precision for floats showing for bigger numbers. > new RockPrice(11111111111111.505, 10, 2) object(RockPrice)#1 (5) { ["tax"]=> float(10) ["vat"]=> float(1111111111111.1) ["net"]=> float(11111111111112) ["gross"]=> float(12222222222223) ["digits"]=> int(2) }
    2 points
  5. Btw, if you’re going to do $page->references()->count(), you will be better off telling references() that you just need the cound directly. In the end, these methods and properties all end up in FieldtypePage and provide some efficiency tricks like this. However, this will always query the database for every page in your array and every page field in your system, so if you want to get real gainz, you're probably best off just asking the database for your pages and the count somewhat like this: $query = wire('database')->prepare('select data, count(paged_id) from field_yourpagefield group by data'); $query->execute(); $results = $query->fetchAll(\PDO::FETCH_KEY_PAIR); $brands = wire('pages')->getById(array_keys($results), ['template' => wire('templates')->get('brand') , 'findTemplates' => false , 'getNumChildren' => false , 'joinSortfield' => false]); echo $brands->each(function ($brand) { return '<li>' . $brand->title . ' was referenced ' . $results[$brand->id] . ' times.'; }); At least that is what I was going to write reflexively before checking the core for something ready-made. That's the amazing thing about ProcessWire, that something like this just already exists and makes everything super easy.
    2 points
  6. [Litte OT] @Wanze Out of curiosity, has something changed in your development arsenal? ?
    2 points
  7. CKEditor for ProcessWire CKEditor is a web text editor like TinyMCE. This module can be used anywhere TinyMCE can be used in ProcessWire and it is generally considered one of the best web text editors out there. TinyMCE and CKEditor have always been the two big leaders of web text editors, and they've been equals for many years. Though lately it seems like CKEditor might be a little ahead of TinyMCE in some areas, so I thought we should give people the option of using CKEditor with ProcessWire. CKEditor has a nice inline mode that is desirable in the page editor when you may have lots of rich text inputs. The reason for this is that the page editor loads a lot faster. If you have this need, this would be one reason why you might want to use CKEditor over TinyMCE. Some also prefer CKEditor for other reasons. For instance, @apeisa prefers the table controls in CKEditor to TinyMCE's. Here are a few notes about this module: You already know CKEditor–it's what this forum is using. Though the version included with ProcessWire's module is significantly newer. It is currently beta test. You are advised to test thoroughly before using it in production. You may very well run into bugs, in which case please let me know so that I can fix. It is tested and confirmed compatible with both repeaters and multi-language support. It is now fully hooked into our link and image systems, like TinyMCE. Thanks to Antti for helping with the image plugin. If you want to use the inline mode, you'll need to install the HTML Purifier module first. The toolbar is fully configurable. It is multi-language ready and all text in the module is translatable. Like with TinyMCE, the module optionally supports definition of custom plugins and content.css file. How to install Copy all the files from this module into: /site/modules/InputfieldCKEditor/ Login to your admin and go to Modules > Check for new modules. Click install for InputfieldCKEditor. Now go to Setup > Fields and locate a textarea field that you want to use CKEditor (or create a new textarea field). When editing the settings for a textarea field, click the Details tab. Change the Inputfield Type to CKEditor and save. While still editing the field settings, click to the Input tab for CKEditor-specific settings you may optionally configure. Download ProcessWire Modules page: http://modules.processwire.com/modules/inputfield-ckeditor/ GitHub Repo: https://github.com/ryancramerdesign/InputfieldCKEditor Screenshots Screenshot of regular mode: Screenshot of inline mode, combined with multi-language fields:
    1 point
  8. Hi, Update to 1.3.3 - tweak to how the cache name/key is generated, shouldn't effect anything... Cheers, Chris
    1 point
  9. You're welcome! ?Let me know if you need any help in implementing a solution using hooks. Maybe the following examples from the tests could be useful : https://github.com/wanze/SeoMaestro/blob/master/tests/src/SitemapManagerTest.php#L177-L220 Cheers
    1 point
  10. Hi @HerTha - you're very welcome. I really do wish I had more time to work on JL2, which would solve this problem off the bat, but I just can't squeeze it in right now. With this specific issue, I might land up having to do some trickery in relation to checking if the column exists. Means I need to dig into the information_schema table, which I hate doing. MariaDB supports alter/modify if not exists, but there's obviously no point in me adding something that will crash for most users. I'm still baffled as to why it's running that schema update in the first place. The only possible thing is that the schema version somehow got 'lost' and so it re-ran it. I'm also a little puzzled as to why the _nf table had to be deleted – that doesn't seem to have anything to do with the issue at hand, which is in relation to the main table. Before you update production, could you check the module configuration in the database for me? In the moduels table, there will be an entry for ProcessJumplinks – in the data column, there will be a _schemaVersion in the JSON payload – the value for that is what I'm looking for. I'll also need the current version of JL that you're running on the production site. Based on that, I might be able to diagnose. If we're lucky, it might resort to a simple correction of the schemaVersion, in case it didn't successfully update before.
    1 point
  11. Okay, I understand there is no simple toggle/option to exclude/include hidden files, so I will have a closer look at hooks then. Thank you so much for the time you are dedicating to maintain this module and to answering (my) questions!
    1 point
  12. Hi there, first the answer depends on that if your array "children" is PageArray. If yes I believe if you want currently viewed page you must check if child->id is simalar like page->id not parent->id. Secondly if you want to check if checkbox is checked the right way will be: $child->checkbox_field == 1 Also I think you know this but else if must be put before else statement :D. Lastly maybe consider using custom menu builder for this. Menu Builder. In addition this checking can be done before building actual markup for menu. So you can consider this solution an then you won't need else if. $menuPages = $pages->find('menu_item=1'); //and add additional selectors for template etc., also you can use selector like this for method children() Cheers.
    1 point
  13. You have the following possibilities: Manually include/exclude all pages via SeoMaestro field Setup the default value on template level to not include pages in the sitemap, and then manually include them via SeoMaestro field. Or vice versa, whatever makes more sense ? If you know how to use hooks: Exclude the hidden pages by using the hook mentioned in my first answer Cheers
    1 point
  14. Just added onother fix that has been bugging me for years: https://github.com/processwire/processwire/pull/169 https://github.com/BernhardBaumrock/PwQuickFixes/commit/74c1378d0274bd6a99147bac597490d0113c2633
    1 point
  15. If you don't need to do anything with the existing page then you can avoid loading it: if($pages->count($selector)) continue;
    1 point
  16. Just in case you haven't come across this site before: https://caniuse.com/#search=webp
    1 point
  17. In this tutorial I want to write about handling special cases and change requests by clients gracefully without introducing code bloat or degrading code quality and maintainability. I'll use a site's navigation menu as an example, as it's relatable and pretty much every site has one. I'll give some examples of real situations and change requests I encountered during projects, and describe multiple approaches to handling them. However, this post is also about the general mindset I find useful for ProcessWire development, which is more about how to handle special cases and still keep your code clean by making the special case a normal one. The problem: Special cases everywhere Since ProcessWire has a hierarchical page tree by default, as a developer you'll usually write a function or loop that iterates over all children of the homepage and displays a list of titles with links. If the site is a bit more complex, maybe you additionally loop over all grandchildren and display those in drop-down menus as well, or you even use a recursive function to iterate over an arbitrary amount of nested child pages. Something like this: function buildRecursiveMenu(Page $root): string { $markup = ['<ul class="navigation">']; foreach ($root->children() as $child) { $link = '<a class="navigation__link" href="' . $child->url() . '">' . $child->title . '</a>'; $children = $child->hasChildren() ? buildRecursiveMenu($child) : ''; $markup[] = "<li class="navigation__item">{$link}{$children}</li>"; } $markup[] = '</ul>'; return implode(PHP_EOL, $markup); } But then the requests for special cases come rolling in. For example, those are some of the requests I've gotten from clients on my projects (by the way, I'm not saying the clients were wrong or unreasonable in any of those cases - it's simply something I needed to handle in a sensible way): The homepage has the company's name as it's title, but the menu link in the navigation should just say "Home". The first page in a drop-down menu should be the top-level page containing the drop-down menu. This was requested because the first click on the top-level item opens the sub-navigation instead of navigating to that page (espcially on touch devices, such as iPads, where you don't have a hover state!), so some visitors might not realize it's a page itself. Some top-level pages should be displayed in a drop-down menu of another top-level page, but the position in the page tree can't change because of the template family settings. The menu needs to contain some special links to external URLs. For one especially long drop-down menu, the items should be sorted into categories with subheadings based on a taxonomy field. In general, my solutions to those requests fall into three categories, which I'll try to elaborate on, including their respective benefits and downsides: Checking for the special case / condition in the code and changing the output accordingly (usually with hard-coded values). Separating the navigation menu from the page tree completely and building a custom solution. Utilizing the Content Management Framework by adding fields, templates and pages that represent special states or settings. Handling it in the code This is the simplest solution, and often the first thing that comes to mind. For example, the first request (listing the homepage as "Home" instead of it's title in the navigation) can be solved by simply checking the template or ID of the current page inside the menu builder function, and changing the output accordingly: // ... $title = $child->template->name === 'home' ? 'Home' : $child->title; $link = '<a class="navigation__link" href="' . $child->url() . '">' . $title . '</a>'; // ... This is definitely the fastest solution. However, there are multiple downsides. Most notably, it's harder to maintain, as each of those special cases increases the complexity of the menu builder function, and makes it harder to change. As you add more special conditions, it becomes exponentially harder to keep changing it. This is the breeding ground for bugs. And it's much harder to read, so it takes longer for another developer to pick up where you left (or, as is often cited, for yourself in six months). Also, now we have a hard-coded value inside the template, that only someone with access to and knowledge of the template files can change. If the client want's the link to say "Homepage" instead of "Home" at some point, they won't be able to change it without the developer. Also, each special case that is hidden in the code makes it harder for the client to understand what's going on in terms of template logic - thus increasing your workload in editorial support. That said, there are definitely some times where I would go with this approach. Specifically: For smaller projects that you know won't need to scale or be maintained long-term. If you are the only developer, and/or only developers will edit the site, with no "non-technical" folk involved. For rapid prototyping ("We'll change it later") Building a custom solution My initial assumption was that the main navigation is generated based on the page tree inside ProcessWire. But of course this isn't set in stone. You can just as easily forgo using the page tree hierarchy at all, and instead build a custom menu system. For example, you could add a nested repeater where you can add pages or links on a general settings page, and generate the menu based on that. There are also modules for this approach, such as the Menu Builder by @kongondo. This approach is not the quickest, but gives the most power to the editors of your site. They have full control over which pages to show and where. However, with great power comes great responsibility, as now each change to the menu must be performed manually. For example, when a new page is added, it won't be visible in the menu automatically. This is very likely to create a disconnect between the page tree and the menu (which may be what you want, after all). You may get ghost pages that are not accessible from the homepage at all, or the client may forgot to unpublish pages they don't want to have any more after they've removed them from the menu. I would only go with this approach if there are so many special cases that there hardly is a "normal case". However, even then it might not be the best solution. The direct relationship between the page tree, the menu structure and page paths are one of the strongest features of ProcessWire in my opinion. If many pages need to be placed in special locations without much structure in regards to what templates go where, maybe you only need to loosen up the template family settings. I have built one site without any template family restrictions at all - any page of any template can go anywhere. It's definitely a different mindset, but in this case it worked well, because it allowed the client to build custom sections with different page types grouped together. It's a trade-off, as it is so often, between flexibility and workload. Weigh those options carefully before you choose this solution! Utilizing the CMF This is the middle ground between the two options above. Instead of building a completely custom solution, you keep with the basic idea of generating a hierarchical menu based on the page tree, but add fields and templates that allow the editor to adjust how and where individual pages are displayed, or to add custom content to the menu. of course, you will still write some additional code, but instead of having hard-coded values or conditions in the template, you expose those to the client, thereby making the special case one of the normal cases. The resulting code is often more resilient to changing requirements, as it can not one handle that specific case that the client requested, but also every future change request of the same type. The key is to add fields that enable the client to overwrite the default behaviour, while still having sensible defaults that don't require special attention from the editor in most cases. I'll give some more examples for this one, as I think it's usually the best option. Example 1: Menu display options This is probably the first thing you thought of for the very first change request I mentioned (displaying the homepage with a different title). Instead of hard-coding the title "Home" in the template, you add a field menu_title that will overwrite the normal title, if set. This is definitely cleaner than the hard-coded value, since it allows the client to overwrite the title of any page in the menu. I'll only say this much in terms of downsides: Maybe the menu title isn't really what the client wanted - instead, perhaps they feel limited because the title is also displayed as the headline (h1) of the page. In this case, the sensible solution would be an additional headline field that will overwrite the h1, instead of the menu_title field. Which fields are really needed is an important consideration, because you don't want to end up with too many. If each page has fields for the title, a headline, a menu title and an SEO-title, it's much more complicated than it needs to be, and you will have a hard time explaining to the client what each field is used for. Another example in this category would be an option to "Hide this page in the menu". This could be accomplished by hiding the page using the inbuilt "hidden" status as well, but if it's hidden it won't show up in other listings as well, so separating the menu display from the hidden status might be a good idea if your site has lots of page listings. Example 2: "Menu link" template One solution that is quite flexible in allowing for custom links to pages or external URLs is creating a menu-link template that can be placed anywhere in the page tree. This templates can have fields for the menu title, target page and/or external target URL. This way, you can link to another top-level page or an external service inside a drop-down menu, by placing a Menu Link page at the appropriate position. This is also a clean solution, because the navigation menu will still reflect the page tree, making the custom links visible and easily editable by the editors. A minor downside is that those templates are non-semantical in the sense that they aren't pages with content of their own. You'll need to make sure not to display them in listings or in other places, as they aren't viewable. It may also require loosening up strict family rules - for example, allowing for Menu Link pages to be placed below the news index page, which normally can only hold news pages. Example 3: Drop-down menu override This one is a more radical solution to override drop-down menus. You add a repeater field to top-level pages, similar to the one mentioned as a custom solution, where you can add multiple links to internal pages or URLs. If the repeater is empty, the drop-down menu is generated normally, based on the sub-pages in the page tree. But if the repeater contains some links, it completely overrides the drop-down menu. It's similar to the fully custom solution in that as soon as you override a sub-menu for a top-level page, you have to manually manage it in case the page structure changes. But you can make that decision for each top-level page individually, so you can leave some of them as is and only have to worry about the ones that you have overwritten. Again, this offers sensible defaults with good customizability. A downside is that the mixed approach may confuse the client, if some changes to the page tree are reflected in the drop-down menu directly, while others don't seem to have any effect (especially if you have multiple editors working on a site). Finding the right solution So how do you choose between the approaches? It depends on the client, the requirements, and on what special cases you expect and want to handle. Sometimes, a special request can be turned down by explaining how it would complicate editorial workflows or have a negative impact on SEO (for example, if you risk having some pages not accessible from the homepage at all). Also, make sure you understand the actual reason behind a change request, instead of just blindly implementing the suggestion by the client. Often, clients will suggest solutions without telling you what the actual problem is they're trying to solve. For example: In one case, I implemented the drop-down override mentioned in example three. However, what the client really wanted was to have the top-level page as the first item in the drop-down menu (see the example requests mentioned above). So they ended up overwriting every single drop-down menu, making the menu harder to maintain. In this case, it would have been better to go with a more specialized solution, such as adding a checkbox option, or even handling it in the code, since it would have been consistent throughout the menu. Another example was mentioned above: If the client requests an additional "Menu title" field, maybe what they really need is a "Headline" field. I recommend reading Articulating Design Decisions by Tom Greever; it includes some chapters on listening to the client, finding out the real reason behind a change request, and responding appropriately. It's written from a design perspective, but is applicable to development as well, and since UX becomes more important by the day, the lines between the disciplines are blurred anyway. Conclusion I realize now this reads more like a podcast (or worse, a rant) than an actual tutorial, but hopefully I got my point across. ProcessWire is at is greatest if you utilize it as a Content Management Framework, creating options and interfaces that allow for customizability while retaining usability for the client / editor. I usually try to hit a sweet spot where the editors have maximum control over the relevant aspects of their site, while requiring minimal work on their part by providing sensible defaults. Above, I listed some examples of requests I've gotten and different solutions I came up with to handle those with custom fields or templates. Though in some cases the requirements call for a custom solution or a quick hack in the template code as well! What are some of the special requests you got? How did you solve them? I'd love to get some insights and examples from you. Thanks for reading!
    1 point
  18. Hi @mauricemoss, there's already a PR for this issue here: https://github.com/justb3a/processwire-imageextra/pull/30 I hope @justb3a takes it into account for her next release.
    1 point
×
×
  • Create New...