-
Posts
5,039 -
Joined
-
Days Won
340
Everything posted by Robin S
-
Hook for when a module's configuration is updated
Robin S replied to Marc's topic in Module/Plugin Development
I've always found that the easiest way to store site settings is with a dedicated template/page. I'm a bit confused by your code because it looks like the module doesn't have any config fields, but generally speaking you can get your module config data after it is saved by hooking after Modules::saveConfig (or Modules::saveModuleConfigData if you need to support older versions of PW). You hook will run after the config is saved for any module, but you can use the first argument to compare against your module classname and return early if it doesn't match. -
Oh right, I never looked into how the module does its work. My idea isn't practical then. Just curious: does setting the page name via JS have an advantage over hooking saveReady and setting it via the API? Do you do it that way to distinguish between title changes made in Page Edit versus the API (in case a user wants to exclude title changes made via the API)?
-
InputfieldPageName phonetic support to create slugs
Robin S replied to Sten's topic in Multi-Language Support
@Sten, I was thinking that it would be good to have some configurable options in the module to determine when a page's name is set via EasySlugger. That is what the Page Rename Options module provides, so to avoid recreating all of that I've asked in the support thread if @adrian would consider adding a hookable method to his module. I think it might be better to bundle EasySlugger in with your module because it will have a wider audience that way - a lot of people are on shared hosting that doesn't include Composer. You could use Composer locally to pull in EasySlugger updates (it actually hasn't received any updates in the last 3 years) and then push them out to your module repo. -
Hi Adrian, @Sten is working on a module that uses EasySlugger to set better page names from titles that use non-latin alphabets. I was thinking that it would be cool if that module (and potentially others) could build on the features that Page Rename Options provides to select what pages are automatically renamed. What do you think about separating off the line that determines the new page name into a hookable method? Then other modules could hook into that to set a custom page name.
-
There are a number of screenshots in the blog post that introduced the inline editing. You can see multi-language fields in those. https://processwire.com/blog/posts/inline-ajax-page-editing-comes-to-listerpro-processwire-2.6.6/
-
InputfieldPageName phonetic support to create slugs
Robin S replied to Sten's topic in Multi-Language Support
@Sten, your module looks like it is missing the required getModuleInfo() method. Check the Module docs for what is required in a module. The Hello World module is a good one to learn from too. And Tracy Debugger is a must-have for debugging modules or anything else (the Tracy bd() method is all you need to learn to begin with). Also, when you want to make changes to a Page in a Pages::added hook you need to do a $page->save() in order to save the changes. Not wanting to hijack your module or anything but when you first posted this topic I was interested in EasySlugger and put together a simple module to test it out. I've added a couple of comments to the code that might help you. <?php namespace ProcessWire; class Slugger extends WireData implements Module { /** * Module information */ public static function getModuleInfo() { return array( 'title' => 'Slugger', 'version' => '0.1.0', 'autoload' => true, ); } /** * Ready */ public function ready() { // You would generate the slug/name after saveReady or added, but not both $this->addHookAfter('Pages::added', $this, 'afterAdded'); // $this->addHookAfter('Pages::saveReady', $this, 'afterSaveReady'); } /** * After Pages::added * Set the page name using EasySlugger only once when the page is first added * * @param HookEvent $event */ protected function afterAdded(HookEvent $event) { require_once __DIR__ . '/EasySlugger/SluggerInterface.php'; require_once __DIR__ . '/EasySlugger/Utf8Slugger.php'; $page = $event->arguments(0); $page->of(false); $page->name = \EasySlugger\Utf8Slugger::slugify($page->title); $page->save(); } /** * After Pages::saveReady * Set the page name using EasySlugger every time the page is saved, in case the Title was changed * You could add a condition to set the name only if the Title changed if you like * * @param HookEvent $event */ protected function afterSaveReady(HookEvent $event) { require_once __DIR__ . '/EasySlugger/SluggerInterface.php'; require_once __DIR__ . '/EasySlugger/Utf8Slugger.php'; $page = $event->arguments(0); $page->name = \EasySlugger\Utf8Slugger::slugify($page->title); } } Slugger.zip Note that there is a minor core issue with warning notices from ProcessPageAdd that will hopefully be fixed soon: https://github.com/processwire/processwire-issues/issues/648 -
The PW issues repo is a bit of a cause for concern I think. Issues are added much more frequently than they are solved, so the collection of open issues is just going to grow and grow over time unless something changes to accelerate the rate at which they are addressed. At the end of the day, it seems the volume of issues is too great to deal with by just a single person with limited time. And a comment Ryan made in a blog post a while ago left me a bit puzzled: Maybe I'm reading that wrong, but it sounds like Ryan believes that almost all significant issues are already solved. I wouldn't agree with that as I think the majority of the open issues are valid ones that affect multiple people. Maybe the fact that there are quite a few issues that have been solved but are still left open is obscuring the fact that a significant number of unsolved issues exist, some dating back a long while. So I would support the idea of closing issues marked solved if it also meant taking a fresh look at the ones not yet solved.
-
Update... The above works okay but it seems that the match callback only fires when CKEditor loads, so to be sure that any disallowed content resulting from the current page edit is removed you have to save the page twice. After a bit more hunting I think I've found a better approach that uses CKEditor's DTD object. It's not quite as straightforward as you'd expect at first because element objects within the DTD object are not fully independent (e.g. CKEDITOR.dtd['h2'] seems to refer to the same object as CKEDITOR.dtd['p']). This SO post helped me find a solution. In /site/modules/InputfieldCKEditor/config.js: // For numbers 1 to 6 for(var i = 1; i <= 6; i++) { // Create the tag name from 'h' plus the number var tag = 'h' + i; // Create clone of DTD heading object so it can be modified individually CKEDITOR.dtd[tag] = Object.assign({}, CKEDITOR['dtd'][tag]); // Disallow strong element from being contained within heading element CKEDITOR.dtd[tag]['strong'] = 0; }
- 2 replies
-
- 5
-
-
- ckeditor
- disallowed content
-
(and 1 more)
Tagged with:
-
A pet hate of mine is when an editor uses a paragraph of bold text for what ought to be a heading. When I need to tidy up poorly formatted content like this I will quickly change such lines of text into the heading of the appropriate level, but that still results in markup like... <h2><strong>Some heading text</strong></h2> The <strong> has no business being there, but it's a bit of a hassle to remove it because you have to drag a selection around the exact text as opposed to just placing your cursor within the line. That gets tedious if you have a lot content to process. I figured there has to be an easier way so started looking into the ACF (Advanced Content Filter) features of CKEditor. What I wanted is a rule that says "strong tags are disallowed specifically when they are within a heading tag". (I guess there could occasionally be a use case where it would be reasonable to have a strong tag within a heading tag, but it's so rare that I'm not bothered about it). With the typical string format for allowedContent and disallowedContent there is no ability to disallow a specific tag only when it is within another specific tag - a tag is allowed everywhere or not at all. But I found there is an alternative object format for these rules that supports a callback function in the "match" property. So I was able to achieve my goal with the following in /site/modules/InputfieldCKEditor/config.js: CKEDITOR.editorConfig = function(config) { config.disallowedContent = { // Rule for the <strong> element strong: { // Use "match" callback to determine if the element should be disallowed or not match: function(element) { // Heading tag names var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; // The parent of the element (if any) var parent = element.parent; if(typeof parent !== 'undefined') { // If there is a parent, return true if its name is in the heading names array return headings.indexOf(parent.name.toLowerCase()) !== -1; } else { // There is no parent so the element is allowed return false; } } } } }; Another tip: if you want to debug your allowedContent or disallowedContent rules to make sure they are being parsed and applied successfully you can log the filter rules to the console. For convenience I used /site/modules/InputfieldCKEditor/config.js again. // Get the CKEditor instance you want to debug var editor = CKEDITOR.instances.Inputfield_body; editor.on('instanceReady', function() { // Log the disallowed content rules console.log(editor.filter.disallowedContent); });
- 2 replies
-
- 6
-
-
- ckeditor
- disallowed content
-
(and 1 more)
Tagged with:
-
Inline styles getting auto added in ckeditor
Robin S replied to cleanboy3000's topic in General Support
Discussed recently here: -
Hi @netcarver, Right now the module can't do that. It's on my to-do list to update this module so that it has a hookable method that would allow the sort of thing you're wanting to do, but not sure when I'll get around to that. For now you could use a hook to hide/disable the trash icon, similar to what I described in this topic: In /site/ready.php: $wire->addHookAfter('InputfieldFieldset::render', function(HookEvent $event) { /* @var $fieldset InputfieldFieldset */ $fieldset = $event->object; $attr = $fieldset->wrapAttr(); // Fieldsets in a Repeater inputfield have a data-page attribute if(isset($attr['data-page'])) { // Get the Repeater item $p = $this->pages((int) $attr['data-page']); if($p->hidden_field != '') $fieldset->addClass('no-trash'); } }); In some custom admin CSS: /* Hide and disable the trash icon while keeping its space in the layout */ .no-trash .InputfieldRepeaterTrash { visibility:hidden; pointer-events:none; } You could use display:none instead if you're not worried about the icon alignment.
-
I did consider that but decided that show on click is preferrable and I'll stick with that. The original request that inspired this module references the Windows Explorer behaviour that shows dropdowns on click, and I wouldn't want to see menus flying out if my mouse happens to pass over the breadcrumb on the way to the main menu.
- 79 replies
-
- 2
-
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
Good spotting, thanks. That's explained in the readme: Thanks, should be fixed in v0.1.1. The Home page is a special case that the module should leave alone because the Home page has no siblings to appear in a dropdown. I'm thinking that this is the error that @matjazp mentioned earlier, which actually isn't related to AdminThemeBoss. Although the modules are probably still incompatible if the "extended breadcrumb" option is activated in AdminThemeBoss.
- 79 replies
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
Ah, yes, that does work. Thanks, I didn't realise your earlier comment was a suggestion to change to the string syntax. I will raise an issue for Ryan to take a look at as the array syntax is the one that's shown in the documentation example.
- 79 replies
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
Yes, it is weird. I think it must be an issue with the modules directory as the "requires" condition looks legit to me. I remember @Macrura struck something like this before with his Selectize modules - maybe he knows more about it? For now I've removed the entire "requires" condition so the module is at least installable by classname from the directory. Will raise a GitHub issue on the PW repo if it's looking like it is a definite bug with the modules directory.
- 79 replies
-
- 2
-
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
There's no AJAX involved - unless you had a crazy number of parents the number of pages rendered in the menus isn't that many and it's better to have the dropdowns appear instantly I think.
- 79 replies
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
@adrian, it seems that the $page->siblings() method used in the module already sorts by the sort field defined on the parent page or template. Do you have that set for your blog items parent?
- 79 replies
-
- 1
-
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
Hmm, it's installing as expected here. The "requires" setting I've used looks like it conforms to the example given in the Module documentation. Any ideas why it might not be working in your case? I use the "AddNewChildFirst" option in AdminOnSteroids for blog/news items and the dropdowns work well with that. But I get your point and will look at implementing something that honours the "sort settings for children" on the parent page/template. And add a note to the readme about the 25 item limit. Yes, very likely will not be compatible with that module as AdminThemeBoss already modifies the breadcrumbs.
- 79 replies
-
- 2
-
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
-
Breadcrumb Dropdowns Adds dropdown menus of page edit links to the breadcrumbs in Page Edit. The module also adds dropdowns in Edit Template, Edit Field, Edit User, Edit Role, Edit Permission, Edit Language, and when viewing a log file at Setup > Logs. Configuration options Features/details The module adds an additional breadcrumb item at the end for the currently edited page. That's because I think it's more intuitive for the dropdown under each breadcrumb item to show the item's sibling pages rather than the item's child pages. In the dropdown menus the current page and the current page's parents are highlighted in a crimson colour to make it easier to quickly locate them in case you want to edit the next or previous sibling page. Unpublished and hidden pages are indicated in the dropdowns with similar styling to that used in Page List. If the option to include uneditable pages is selected then those pages are indicated by italics with a reduced text opacity and the "not-allowed" cursor is shown on hover. There is a limit of 25 pages per dropdown for performance reasons and to avoid the dropdown becoming unwieldy. If the current user is allowed to add new pages under the parent page an "Add New" link is shown at the bottom of the breadcrumb dropdown. If the currently edited page has children or the user may add children, a caret at the end of the breadcrumbs reveals a dropdown of up to the first 25 children and/or an "Add New" link. Overriding the listed siblings for a page If you want to override the siblings that are listed in the Page Edit dropdowns you can hook the BreadcrumbDropdowns::getSiblings method and change the returned PageArray. For most use cases this won't be necessary. Incompatibilities This module replaces the AdminThemeUikit::renderBreadcrumbs method so will potentially be incompatible with other modules that hook the same method. https://modules.processwire.com/modules/breadcrumb-dropdowns/ https://github.com/Toutouwai/BreadcrumbDropdowns
- 79 replies
-
- 13
-
-
- breadcrumbs
- admin
-
(and 2 more)
Tagged with:
-
Designme - Visually Layout Your Edit Screens - Preview
Robin S replied to joshuag's topic in Modules/Plugins
When removing a field from Page Edit, is the user removing that field from the associated template? Does that happen on the fly or Is there some sort of "Apply" step that happens at the end with the normal warnings about the loss of data that will occur? If it happens on the fly is there an undo feature? I'd be a bit nervous about accidentally clicking on the field remove button otherwise. -
Welcome @torf! Profields Table would be ideal, but Repeater, PageTable and Page Reference (with modal editing via AsmSelect) are also candidates, or even a custom repeating fieldtype in the style of the demo Events module. Oh, and one more: child pages together with Batch Child Editor. And another: pages from anywhere (not just children) with inline editing via Lister Pro. So many options... ?
-
Thanks for the new search features @ryan! I wonder if the keyword to bring up the search help should be something less likely to be typed in by a site editor, e.g. "~help". Because it's not that unlikely that an editor might want to find a page with the word "help" in it. To test I created a page titled "Help" and that was not included in the results when I searched "help". The option to include search results from a module sounds interesting but I'm struggling to think of an existing module that might want to give search results other than pages. Maybe @adrian's Admin Actions module? What sorts of modules do you think would use that feature?
-
It's never overkill to create the fields you need to achieve the functionality you want. For efficiency PW sorts pages when it gets them in the SQL query. So when you get pages from the database whatever you want to sort on must exist as a column in the database. You can sort a PageArray independently of this but it's less efficient and will be problematic when you have a large number of pages or want to add pagination to your results. Also, think about the sorting of names like "Anthony van Diemen" if you were to try to sort without an explicit "last name" field. There is no need to enter anything twice. There are a few different ways you could go about it - here is the one that I prefer because it's simple and intuitive even if it does involve some minor redundancy of data... This assumes you have a template team_members that allows children of template team_member. 1. Install the core PagePathHistory module if you haven't already (a must-have for every site). 2. Install the Page Rename Options module. 3. Add text fields first_name and last_name to template team_member, right after the title field. Make them required fields, and a width of 50% would make sense. 4. In template team_member re-label the title field to "Full name" and set the field visibility to "Open when populated + Closed when blank + Locked (not editable)". 5. In the Family tab of template team_members set "Name format for children" to "Y/m/d H:i:s". This allows new pages added under this template to skip the first step of the "Add New" process. New pages will get a name according to the date/time they were added but this is only temporary until they are published or saved for the first time. 6. Add the following hook to /site/ready.php $pages->addHookAfter('saveReady', function(HookEvent $event) { $page = $event->arguments(0); // Only for the team_member template if($page->template != 'team_member') return; // Skip if this page is brand new and doesn't have any data yet if(!$page->id) return; // Derive the title (Full name) from the first_name and last_name fields // The Page Rename Options module will take care of updating the page name to match $page->title = $page->first_name . ' ' . $page->last_name; }); Now in your template files you can get and sort on the first_name and last_name fields independently. If you want the full name you could output those fields together, but why bother when you also have the full name in the title field. Having the full name in the title field is nice because things in the PW admin (Page List, Page Edit headings, admin search) continue to work without needing any special settings or additional hooks.
-
These two spring to mind: $user->isLoggedin() $user->isSuperuser()