Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/31/2022 in all areas

  1. Find Merge Adds a Pages::findMerge() method that allows multiple PageFinder selectors to be merged into an efficient paginated set of results. This can be useful when you need more sophisticated sorting of results than what would be possible using only the sort value in a single $pages->find(). Details $results = $pages->findMerge($selectors, $options); $selectors is required and must be an array of selectors. Each selector can be in string format or array format. The findMerge() method will loop over the selectors in the order supplied, adding matching pages to the final results. $options is an optional associative array of options. limit (int) Limit for pagination. start (int) Manually override the start value rather than have it be automatically calculated from the current page number. excludeExisting (bool) Whether or not to exclude pages in each selector that have already been matched by a previous selector. Default is true. keepFirst (bool) When excludeExisting is false then a page might match more than one selector in the supplied array. But each page can only appear once in the results and if keepFirst is true then the page will appear in its earliest position in the results, whereas if keepFirst is false it will appear in its latest position in the results. Default is true. As a shortcut you can supply an integer as the second argument and it will be treated as the limit for pagination. Basic usage For most use cases only a limit will be needed for the $options argument. $selectors = [ 'title%=yellow', // title contains "yellow" 'title^=z', // title starts with "z" 'title=elephant', // title equals "elephant" 'template=colour, sort=-title, limit=3', // 3 colours in reverse alphabetical order 'template=country, sort=title, limit=40', // 40 countries in alphabetical order ]; $results = $pages->findMerge($selectors, 10); if($results->count) { echo "<p>Showing results {$results->getPaginationString()}</p>"; echo "<ul>"; foreach($results as $result) { echo "<li><a href='$result->url'>$result->title</a></li>"; } echo "</ul>"; echo $results->renderPager(); } Advanced usage The following notes are only relevant to rare cases and most users can safely skip this section. In the demo example the colour page Yellow will potentially match both the 1st selector and the 4th selector. Because of this the excludeExisting and keepFirst options will have an effect on the results. excludeExisting option false Note that the 4th selector asks for 3 colour pages (limit=3). By default excludeExisting is true, which means that when the 4th selector is processed it is interpreted as saying "find 3 colour pages in reverse alphabetical order that have not already been matched in an earlier selector". We can see that there are 3 pages in the results from that selector: Violet, Red, Orange. But if excludeExisting is set to false then the results are different. The matches of the 1st selector (Yellow, Yellow Warbler) are not excluded from consideration by the 4th selector (the 4th selector matches will be Yellow, Violet, Red), and because each page can only appear once in the results this means that the 4th selector ends up only adding 2 more pages to the results. $selectors = [ 'title%=yellow', // title contains "yellow" 'title^=z', // title starts with "z" 'title=elephant', // title equals "elephant" 'template=colour, sort=-title, limit=3', // 3 colours in reverse alphabetical order 'template=country, sort=title, limit=40', // 40 countries in alphabetical order ]; $options = [ 'limit' => 10, 'excludeExisting' => false, ]; $results = $pages->findMerge($selectors, $options); keepFirst option false As described above, the Yellow page potentially matches both the 1st and 4th selector. By default Yellow will appear in its earliest position within the results, i.e. the position resulting from it being matched by the 1st selector. But if keepFirst is set to false (and excludeExisting is false) then it will appear in its latest position within the results, i.e. the position resulting from it being matched by the 4th selector. $selectors = [ 'title%=yellow', // title contains "yellow" 'title^=z', // title starts with "z" 'title=elephant', // title equals "elephant" 'template=colour, sort=-title, limit=3', // 3 colours in reverse alphabetical order 'template=country, sort=title, limit=40', // 40 countries in alphabetical order ]; $options = [ 'limit' => 10, 'excludeExisting' => false, 'keepFirst' => false, ]; $results = $pages->findMerge($selectors, $options); keepFirst has no effect when excludeExisting is true. https://github.com/Toutouwai/FindMerge https://processwire.com/modules/find-merge/
    10 points
  2. To use a namespaced (or non-namespaced) class inside another namespaced file, you need to add a slash ('\') before the included classes' namespace. Otherwise, it is looking for the class relative to the file's namespace. While it works -- and in this instance doesn't appear to cause issues -- removing a file's namespace negates the reason for using namespaces in the first place. Here is an ammended version of your code: <?php namespace ProcessWire; class ProcessSocial extends WireData implements Module, ConfigurableModule { public function init() { $file = __DIR__ . '/vendor/autoload.php'; if (file_exists($file)) { require_once $file; } } protected function testFeed($user){ // Note the '\' before 'GetStream' $client = new \GetStream\Stream\Client($api, $key); $feed = $client->feed('User', $user); return $feed->getActivities(); } } If you find you are calling a class a lot, then you can use a 'use' statement at the top of the file, like this example: <?php namespace ProcessWire; use \GetStream\Stream\Client; // Namespace path to the classname (inc. the classname too) class ProcessSocial extends WireData implements Module, ConfigurableModule { public function init() { $file = __DIR__ . '/vendor/autoload.php'; if (file_exists($file)) { require_once $file; } } protected function testFeed($user){ // Now you don't need to add the namespace at all here $client = new Client($api, $key); $feed = $client->feed('User', $user); return $feed->getActivities(); } } Hope that helps.
    2 points
  3. Thought that I was the only one trying to avoid composer. Thanks for posting this working solution (added to my snippets)
    2 points
  4. So I got it...just removing "namespace Processwire" on top of the module
    1 point
  5. In addition to site development work and ProcessWire core development, this week I upgraded my OS X version (from Mojave to Monterey), my PhpStorm version (from 2018 to 2022), and my Apache/PHP/MySQL environment versions. I tend to wait a bit too long between upgrades on my computer and so I ended up with a lot of things to resolve, and a still few remaining. While waiting for upgrades to install, I randomly came across one of the first sites I ever designed/developed out of college, working for Grafik in the year 2000: https://americanhistory.si.edu/subs/ ... I can't believe it's still there. It looks really dated now (it's 22 years old), but reminded me of how much things have changed in web design/development. While I'd been developing sites for a few years before this, it was just a hobby, and it wasn't until this time that it was my job. Back in 2000 there weren't a lot of people that knew how to create websites and it always felt like you were breaking new ground. Internet Explorer was king (and nobody liked it then either), Google was just a small startup, but AltaVista, InfoSeek and Yahoo were big. Sites were developed in a way that would make you cringe now. I don't think we used CSS hardly at all, but we did use tables for everything layout related. There was no such thing as "mobile" (the iPhone didn't come till 7 years later). There was no such thing as "responsive" layout and accessibility was pretty much an unknown. Most of the time we used images for a lot more than was appropriate (headlines and much more) because HTML and HTML fonts were so limited. It all seems so primitive now but what's the same is that it was fun then and it's fun now, actually it's more fun now. I don't have any point here, just that it's funny to look back at how much as changed. Last week I mentioned that we're likely to upgrade CKEditor 4 to CKEditor 5 sometime in the next year. There were several comments about that and so just wanted to talk a little more about it. First off, I really like CKEditor 4, it's been a great fit for ProcessWire. If the company behind it was going to continue building and supporting version 4 after 2023 then we'd likely stick with it. But the CKEditor 4 end-of-life is sometime in 2023 and I don't think it's an option to stick with it (in the core) after the developer of it is no longer updating or supporting it... at least not long term. While CKEditor 5 is a different animal than CKEditor 4, it's also still the closest upgrade path from CKEditor 4, and I'm hopeful it will be able to serve as a near drop-in replacement from the perspective of users (only). My hope is that by the time we've completed the upgrade to CKE 5, our clients won't necessarily know the difference or have to re-learn anything, unless they want to take advantage of something new exclusive to CKE 5. From my perspective as a developer integrating CKEditor 5 into ProcessWire, the development side is not a drop in replacement for CKE 4 (not even close), as all supporting code will have to be redeveloped. By supporting code, I mean things like the code in the InputfieldCKEditor.module file, the code for our custom CKE plugins (pwimage and pwlink), as well as anything else development related that is referring to CKEditor. There's no doubt it'll be a lot of work. But the rich text editor is one of the most important input types in the ProcessWire admin, so it's fine, it's worth putting a lot of time into. As for CKEditor 5 being bloated relative to CKEditor 4, I very much doubt that's the case. It was a complete rewrite, the folks at CKEditor know what they are doing, and it's safe to assume it's even more optimized and streamlined than CKE 4. In terms of size, the download for CKE 4 and CKE 5 are both 1.7 megabytes. As I understand it, they started with a new codebase for CKEditor 5 in part to start fresh and avoid legacy bloat. So I see this upgrade as being the opposite of bloat. So what happens with CKEditor 4 in ProcessWire when it likely is replaced with CKEditor 5? So long as CKE 5 can be a near drop in replacement for our users, and for the markup it generates, then the CKE 4 module will move out of the core and into an optional module you can install from the modules directory, when/if someone wants it. On the other hand, if the transition is not completely clean between versions then we may end up supporting both in the core for a short period of time. Though I'm hopeful this won't be necessary. There are some other interesting looking editors out there that have been mentioned, and it'd be nice to have more input options available. I see something like Imperavi's Article as a good option for some but not a replacement for our current rich text editor. At least I know my clients would not be happy to have that big of a change occur from a PW version upgrade. Likewise, something like the Easy Markdown Editor is a great option for some, and I'd like to be able to install a module for that and use it in some cases. But folks used to using CKEditor 4 in their work would not be happy to have that big of a change either. CKEditor 4 works really well for what it does, and I think the goal has to be that clients using CKEditor 4 now should be able to continue doing what they are doing with as few changes to their workflow as possible. I'm hopeful we'll be able to get there with CKEditor 5, while also gaining some benefits in the process. Where other input options make a lot of sense is when building a new site where there aren't already users depending on one input method or another. And it may be that at some point (sooner or later) it will make sense for ProcessWire to have another textarea input option that's different enough from CKE to make it worthwhile. But in my opinion, that would be a potential additional option, not a replacement, as CKE is pretty well established and expected in PW.
    1 point
  6. @Kiwi Chris Let's say you've got a FormBuilder form named "review" and it has these fields, all of which are required: reviewed_page (Page) where user selects what page they are reviewing reviewer_name (Text) reviewer_email (Email) review_text (Textarea) star_rating (Integer) recommend (Toggle Yes/No) Your comments field is named "comments". We have a template named "form-reviews" where we output this form using FormBuilder embed method C. Above the embed C output code, in that file we have a hook which takes the submitted review and converts it to a pending comment: $forms->addHookAfter('FormBuilderProcessor::saveForm', function(HookEvent $event) { $processor = $event->object; /** @var FormBuilderProcessor $processor */ $form = $event->arguments(0); /** @var InputfieldForm $form */ $field = $event->wire()->fields->get('comments'); /** @var CommentField $field */ $page = $form->getValueByName('reviewed_page'); /** @var Page $page */ $errors = $processor->getErrors(); if(count($errors)) return; // if there were errors, do not create a comment yet $comment = new Comment(); $comment->status = Comment::statusPending; $comment->cite = $form->getValueByName('reviewer_name'); $comment->email = $form->getValueByName('reviewer_email'); $comment->text = $form->getValueByName('review_text'); $comment->stars = $form->getValueByName('star_rating'); // example of using meta data $comment->setMeta('recommended', $form->getValueByName('recommended')); if($field->addComment($page, $comment, true)) { // pending comment added successfully } else { // failed to add comment $processor->addError("Error adding review/comment"); } }); That's basically it. In my case, I have a lot more "meta" fields, so my hook is longer than this. This hook could also be to FormBuilderProcessor::formSubmitSuccess but I choose the saveForm instead because at this point I can still add my own error messages that will be displayed to the user. This gives you the opportunity to do additional validation, should you want to. By the time the formSubmitSuccess method is reached, it's already been determined there were no errors, so at that point you couldn't refuse the form submission if you can't convert it to a Comment for one reason or another. Let's say your Comments field is named "comments", then $page->comments->stars() will return the average of all star ratings on $page (floating point number between 0 and 5.0). For more details, arguments, options see: https://processwire.com/api/ref/comment-array/stars/ https://processwire.com/api/ref/comment-array/render-stars/ https://processwire.com/api/ref/comment-stars/ For rendering stars it'll use ★ but if you want it to use a different icon you can use CommentStars::setDefault() i.e. CommentStars::setDefault('star', '<i class="fa fa-star"></i>')); There are lots of other things you can specify to setDefault() so it's worth looking directly in the file if you decide to use it.
    1 point
  7. Thanks @Pete - I've been running that module on PHP 8 - surprised I hadn't come across the error before. Should be fixed now!
    1 point
  8. Changelog Version Description 8.0.0 count() will now throw TypeError on invalid countable types passed to the value parameter. 7.2.0 count() will now yield a warning on invalid countable types passed to the value parameter. https://www.php.net/manual/en/function.count.php?
    1 point
  9. @ryan - I just tried working with this for the first time and the issue I am having is that I don't want to prevent guests from viewing the page, but I want to prevent them from being able to view the PDF associated with the page. Of course, the first level of protection is to simply not show them the link to the file, but if they were given (or guessed) the direct url to the file, I don't think I can actually use pagefileSecure and this new approach to block access, or am I missing something? Perhaps pagefileSecure needs to have an optional permission, eg 'files-view' or something like that - I think that would allow much more flexibility in controlling access to files without relying on access to the page they are stored on. Does that make sense?
    1 point
  10. @teppo The only reason I've started using wire()->pages->find() rather than wire('pages')->find() is just because the IDE (PhpStorm in my case) recognizes what wire()->files is, but doesn't recognize what wire('files') is... well it does, but it only knows that it could be any one of a bunch of API vars, rather than knowing it's the $pages API var. Substitute any API var for "pages" here, I'm just using pages as an example. Phpdoc data is able to traverse through variable and method names like wire()->pages or pages(), but not through strings like wire('pages'). It makes zero difference technically which you use, but at least as far as IDEs like PhpStorm goes, it's the difference between the IDE knowing what you are doing and not really knowing what you are doing. I like it knowing what I'm doing because it helps to find potential issues more easily, and it knows arguments, types and return values on methods I'm going to call, etc. You can also do the same by using phpdoc to tell it what type a particular variable is. For instance, the two bits of code below would have the same effect in terms of the IDE knowing what $pages is: // this: /** @var Pages $pages */ $pages = $this->wire('pages'); // or this: $pages = $this->wire()->pages;
    1 point
×
×
  • Create New...