Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 07/11/2019 in all areas

  1. While working on the comments form of my blog, I thought to add an honeypot field in comments form to reduce spam. ? Honeypot is simply an hidden field added to a form. Honeypot field can have any name and is made invisible normally with a css directive {display: none}. Web users, being unable to see the field, will not fill it, while spam bots majority will not detect its invisibility and will populate the field. Once the form is submitted with a not-empty honeypot field it is very likely we are dealing with spam. ? In this post you can find more details about honeypot technique. While studying FieldtypeComments module and in particular CommentForm.php, to my great surprise ? I realized that PW already supports honeypot for Comments Form. ?? This feature has been introduced with PW 3.0.40. Normally this honeypot field is disabled, so it was enough to understand how to enable it! And as often is the case with PW ... it is super easy. ? If in your profile you are directly working with CommentArray, you will just have to enable honeypot passing it as an option to the renderForm() function of CommentArray class, example below: $comments->renderForm(['requireHoneypotField' => 'email2']); And .. we are done! ?? If you will look at the html of your Comment Form you will see an additional line CommentFormHP, that's the hidden honeypot field. In case you are using the Uikit 3 Site/Blog Profile, the renderForm() function is called in _uikit.php, ukCommentForm() function. If you wish that honeypot field is applied to every comment form of your site, just add the requireHoneypotField option to the list at the function start: ... 'errorMessage' => __('Your comment was not saved due to one or more errors.') . ' ' . __('Please check that you have completed all fields before submitting again.'), requireHoneypotField' => 'email2', // >>>>> ADD THIS LINE ); ... Otherwise if you wish to add honeypot in comment form on selected templates only, do not modify ukCommentForm(), but pass the option requireHoneypotField when calling the function in your template: ukCommentForm($comments, ['requireHoneypotField' => 'email2']); Now that we enabled it, let test if honeypot works. ? In the browser development section let's select the honeypot field and disable css {display:none} to show it. A new field will appear: If the spam bot is going to fill the field with a value and submit the form, an error is returned and comment will not be submitted ? That approach is great as spam comments will not be even saved inside the table field_comments. ? I hope this can be of help if somebody needs to enable this PW comments feature.
    3 points
  2. Use wire('all') Example from our production code: $factory = $modules->get('TemplateEngineFactory'); echo $factory->render('chunks/bodyCta.tpl', array_merge( (array) wire('all'), (array) [ "title" => $title, "text" => $text, "href" => $href ] ));
    3 points
  3. When a blog post article receives many comments ? your start to consider to apply pagination to comments. Looking around this forum, I found some useful hints, but I couldn't find how to take full advantage of PW built-in pagination. In particular the ukBlogPosts() function in Uikit 3 Site/Blog Profile renders a list of paginated posts. ukBlogPosts() is calling a ukPagination() function which permits to navigate the posts. ? After some unsuccessful attempts to use it with CommentArray … ? I decided to insist. ? And here is the result. The ukPagination() function in _uikit.php expects an argument $items of type PageArray. Of course a CommentArray ... is not a PageArray ... ? so we have to remove this blocking point. Going deeper inside the function you will notice that pagination is achieved through a call to a render() method of MarkupPagerNav. Let's look at its declaration: public function ___render(WirePaginatable $items, $options = array()) Interesting ? here $items are declared of type WirePaginatable. What's that? ? a class? No! It's an interface! ? Looking around further I discovered that WirePaginatable interface is used by PageArray, PaginatedArray, and CommentArray.. bingo! ?? We got what to do first, modify ukPagination() function in _uikit.php changing $items argument type from PageArray to WirePaginatable: //function ukPagination(PageArray $items, $options = array()) { function ukPagination(WirePaginatable $items, $options = array()) { //>>>>> REPLACE WITH THIS LINE Now the function accepts our CommentArray as argument, but to make it work we need to set its Limit, Start, and Total attributes as PW does automatically when dealing with pages. In order to do that we will use the WirePaginatable methods setLimit(), setStart(), setTotal(). In this process our Start attribute will have to be calculated dynamically to consider the page number when navigating the comments. To do that we will largely ? modify the function ukComments() in _uikit.php: function ukComments(CommentArray $comments, $options = array()) { if(!$comments->count) { //>>>>>NEW-start if(input()->pageNum > 1) { // redirect to first pagination if accessed at an out-of-bounds pagination session()->redirect(page()->url); } return ''; } //>>>>>NEW-end $defaults = array( 'id' => 'comments', 'paginate' => false, //>>>>>NEW 'limit' => 3, //>>>>>NEW ); //if(!count($comments)) return ''; $options = _ukMergeOptions($defaults, $options); $language = user()->language->id; //>>>>>NEW $comments = $comments->find("language=$language"); //>>>>>NEW if($options['paginate']) { //>>>>>NEW-start $limit = $options['limit']; $start = (wire()->input->pageNum - 1) * $limit; $total = $comments->count(); $comments = $comments->slice($start, $limit); $comments->setLimit($limit); $comments->setStart($start); $comments->setTotal($total); } //>>>>>NEW-end $out = "<ul id='$options[id]' class='uk-comment-list'>"; foreach($comments as $comment) { $out .= "<li class='uk-margin'>" . ukComment($comment) . "</li>"; } $out .= "</ul>"; if($options['paginate'] && $comments->getTotal() > $comments->count()) { //>>>>>NEW-start $out .= ukPagination($comments); } //>>>>>NEW-end return $out; } As you will notice we have added two options to the function [paginate => false] and [limit => 3]. In such a way the ukComments() function will operate as before. If you wish to use pagination you will have to pass [paginate => true] as argument of the function. Of course you can freely choose the limit value or by pre-setting it in the function or by passing it as an argument to the function. Please note that the modification implements the necessary changes (2 lines) to make comments language-aware as described in the tutorial: We are now just to steps away to see our paginated comments. ? First you will need to enable pagination in the templates where you want comments to be paginated. Login to PW Admin and select your template. In the tab URLs enable the "Allow Page Numbers?" option. Second you will need to modify the associated php template, in our example blog-post.php, to change the call to ukComments() as follows (sorting is not necessary, depends on your preferences): //echo ukComments($comments); echo ukComments($comments->find("sort=-created"), ['paginate' => true]); //>>>>> REPLACE WITH THIS LINE And here we are! Below a snapshot our paginated comments! ??? As you can see on the bottom of the comments you have the standard PW pagination navigator, without being forced to rewrite a new one for the purpose! ? I hope you can find something useful with this tutorial.
    3 points
  4. re: Composer It's like npm for PHP, and super-easy to use. Some scripts out there require lots of dependencies, and they also check if your system meets all requirements. Downloading all dependent libraries yourself manually can be time-consuming. You can install Composer locally, install your spreadsheet stuff, and then copy / FTP the generated vendor folder to site/wherever/. From there on, just make sure your include_once() points to the proper path, and follow the docs. tl;dr: Composer is certainly not overkill ?
    3 points
  5. Hi @iipa I am not familiar with PhpSpreadsheet, ? but more generally speaking you can upload an external library calling a require_once() in profile ready.php or in site/config.php. I load all my libraries from site/config.php calling a simple loader /** * MRCMOD03: Add custom library Argo * */ include_once(__DIR__ . '/includes/Argo.php'); which load all my functions and class libraries (same as PW does in wire/core/boot.php file) <?php namespace Argo; $preloads = array( 'argo/Functions.php', 'argo/Object.php', 'argo/Controller.php', 'argo/Widget.php', 'argo/WidgetTracker.php', 'view/ContactForm.php', 'view/CookieBanner.php', 'view/FollowButtons.php', 'view/RecentPosts.php', 'view/ShareButtons.php', 'view/LegalNotice.php', 'controller/CookieController.php', ); foreach($preloads as $file) { include_once(__DIR__ . '/' . $file); } unset($preloads); As library will have different namespace than Processwire namespace, do not forget to apply "use" directive before using a library class or function in your template ? use \Argo\ShareButtons; ... $sb = new ShareButtons(); I hope you can find some hints with that.
    3 points
  6. Hey @horst, @3fingers, I'm very sorry for the late response. I finally found some time to look into this. - @horst at the beginning, you mentioned a 401 error. I think this could be caused by a wrong audience id. But seems like this was no problem later? - Regarding 404: you get a 404 for every new user, because the "getStatus" method simply can't find a status for that user. This is absolutely correct behaviour and the check itself is suggested by the API documentation. I guess it is not a good idea to log this as a warning, as it is part of the normal process. I removed that. - @3fingers do you have double opt-in enabled or disabled? there was an extra 'if' on line 55 that has caused problems with resubscription when double opt-in was disabled. This should be fixed in 0.0.5. Thanks to jliebermann for posting this issue on github. (Note, that after a 'resubscribe', the user has to confirm his email adress again and will disappear in your "contacts" list until he does that!) - @3fingers your solution with updating line 54 can not really work, because the $status you are checking is not the HTTP_STATUS. It is supposed to be the subscription status, and it will be false, when you (correctly) get a 404 during the API call. So there should never be a 404 in $status ?. Besides that, I changed the wording in the readme and comments from 'list' to 'audience', since this is the new wording by Mailchimp. Version 0.0.5 is live in a minute. Appreciate your feedback. Thanks a lot for posting your issues and for your patience! Trying to answer you faster next time. Let me know if you still have problems.
    2 points
  7. @ttttim - have a read of this: https://processwire.com/blog/posts/pw-3.0.135/#step-4-decide-whether-to-to-enable-hsts-section-9f
    2 points
  8. @bernhard if you are using ProcessWire 3 and if useFunctionsApi enabled you can call all api functions in rendered files directly.
    1 point
  9. Hi @iipa , I think so... ? Here is an example taken from PhpSpreadsheet documentation: <?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setCellValue('A1', 'Hello World !'); $writer = new Xlsx($spreadsheet); $writer->save('hello world.xlsx'); Php "use" directive provide you an alias for the namespace, so thanks to that in the above example you can simply instantiate this class's library as: $spreadsheet = new Spreadsheet(); Alternatively you could call the class with the fully qualified class name which includes both namespace and class: $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); Of course if you have several calls... the "use" directive avoids to keep repeating the fully qualified name each time. ? Without informing PHP about the namespace of your class or function, it will search for it inside Processwire namespace where not finding it will return an error. ?
    1 point
  10. @iipa You need to use the correct namespace by either aliasing the class with the use statement, or by using a fully qualified class name. As others have said though, it's much easier to use Composer with it's autoloader, since you only need to include it once and all classes will be autoloaded. It's also better for performance, as only the classes you actually use get loaded for each request. If you need help setting up Composer and the autoloader, check out my tutorial on Composer + ProcessWire ?
    1 point
  11. Processwire API and minimalism is so much lean that it feels like you are still writing PHP compared to other CMS which are overly complex, I think Processwire is easy to use Project with so many things taken care of, especially the fields, It's so much easier to build applications for client easily than other frameworks/cms because Processwire provides so many features that are vital.
    1 point
  12. Why is composer an overkill? You are one command away from the library! The docs don't seem to offer the library as a package.
    1 point
  13. This is just a rough idea, not sure if it's going to go anywhere but I'm actually wondering if I should extend the $partials object a bit? Currently it's actually a bit silly – basically an "object oriented" way to replace <?php include 'partials/menu/top.php' ?> with <?php include $partials->menu->top ?>. If each property could be used as a function, this would allow us to pass the partial arguments: <?= $partials->menu->meta([ 'description' => 'some text' ]) ?>, etc. Or we could pass in a string, in which case a Controller method would be used to pass the data to the partial. Again, just thinking out loud here. Not sure if this makes any sense, but I'm kind of liking the idea ? Another idea I've been toying with would be subcontrollers (or child controllers, or partial controllers, or whatever terminology makes most sense). These could solve situations where you end up needing the same stuff from template to template. You can always create custom base controllers and extend them, but this might allow for easier composition.
    1 point
  14. @stanoliver I don't quite understand your situation, but if you have some data in an array, you have a couple of methods to output it. A simple foreach loop will keep the code at the same length regardless of how many columns you need: $prices = [ '100', '200', '300', '400', '500' ]; foreach ($prices as $price) { echo $price . ' '; } // output: // 100 200 300 400 500 // note there's one final space after "500" If you need to have something in between each item, for example a comma for CSV-style notation, you can use implode: $prices = [ '100', '200', '300', '400', '500' ]; echo implode(', ', $prices); // output: // 100, 200, 300, 400, 500 Finally, if you need to add something before or after each item or manipulate the items in some way, you can use array_map, which applies a callback function to each element of the array and returns a new array with the return values. For example, to wrap each element in double quotes for the CSV delimiters: $prices = [ '100', '200', '300', '400', '500' ]; echo implode(',', array_map(function ($price) { return '"' . $price . '"'; }, $prices)); // output: // "100","200","300","400","500"
    1 point
  15. Just in case someone else wants to do something like this, I just wanted to note that hooking before ProcessPageEdit::execute and foreaching through wire('breadcrumbs') works well. But for my particular needs I had problems with this approach and the Reno theme and so had to take another approach which worked: hooking after Process::breadcrumb Anyway, thought it might be useful info for others.
    1 point
×
×
  • Create New...