Jump to content

Leaderboard

Popular Content

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

  1. It's possible using the native permission system by creating page-edit-lang-[language] permissions, i.e. both page-edit-lang-default and page-edit-lang-de (or however German language is named in this PW instance) and only giving page-edit-lang-de to your translators (your regular editors roles gets both). To prevent translators from changing non-multilanguage fields, you also need to create a page-edit-lang-none permission and assign that to the regular editor roles, but not the translator role. This blog post gives a good introduction to the underlying concept.
    4 points
  2. I often had the need for an overview of all used fields and their contents for a specific page/template while developing new websites without switching to the backend, so I made a small module which lists all the needed information in a readable manner (at least for me): Debug Page Fields https://github.com/robertweiss/ProcessDebugPageFields It adds two new properties to all pages: $page->debugFieldValues – returns an object with all (sub-)fields, their labels, fieldtypes and values $page->debugFieldTypes – returns an object with all fieldtypes and their corresponding fields // List all values of a pages $page->debugFieldValues // List a specific field $page->debugFieldValues->fieldname // List all used fieldtypes of a page $page->debugFieldTypes I recommend using it in combination with Tracy Debugger, Ray, Xdebug etc. as it returns an object and is only meant for developing/debugging uses. For now, the fieldtype support includes mostly fieldtypes I use in my projects, but can easily be extended by adding a new FieldtypeFIELDNAME method to the module. I use it with five different client installations (all PW 3.0.*), but of course there might be some (or more) field configurations which are not covered correctly yet. Supported fieldtypes Button Checkbox Color Combo Datetime Email FieldsetPage * File FontIconPicker Functional Image ImageReference MapMarker Multiplier Mystique Options Page PageIDs PageTitle Radio Repeater * RepeaterMatrix * RockAwesome SeoMaestro Table Text Textarea Textareas Toggle URL * The fields with complete subfield-support also list their corresponding subfields. Installation Download the zip file at Github or clone the repo into your site/modules directory. If you downloaded the zip file, extract it in your sites/modules directory. In your admin, go to Modules > Refresh, then Modules > New, then click on the Install button for this module. As this is my first ›public‹ module, I hope I did not miss any important things to mention here.
    3 points
  3. Hi @adrian, first of all thanks a lot for your fast and nice reply, this community is always so helpful and motivating! 1) Good idea, I never really used it that way, but I definitely will check it out and change the autoload setting. 2 & 3) Thanks for the hints, I changed the code as you suggested. 4) You are right – in combination with Tracy and usual field configurations, the difference is mostly a formatting preference. As I like using Ray in an external window for debugging, the module helped me to always get identical informations in different environments. Another thing which was important to me is the support for ›subfields‹, so that I can immediately see the content of repeater matrix items inside repeaters inside fieldset page fields etc. etc. (you see the point … ?). The third thing is – as you already mentioned – the support for all kinds of fieldtypes in a more or less consistent and simplified way.
    3 points
  4. @Hector Nguyen getcsv() does return an array, but it is an array representing and consuming the memory of just one line from a CSV file (i.e. the columns from 1 row). fgetcsv() is just a layer on top of the fgets() function, which reads one line at a time from a file, which is what makes it memory friendly. On the other hand, PHP functions like file() or file_get_contents() do read the entire file in memory, so they are not memory friendly, even if they are fast. @fedeb I think the best route to take for your groupID+start+end+sequence would be a custom Fieldtype. This would give you all of the benefits of having a repeater, and without any of the overhead. Custom fieldtype may sound complicated, but it's not at all. I've developed a module that can very easily be adapted for this need. See FieldtypeEvents which was created as an example to build exactly this sort of thing from. If you are interested in that route and have any questions, I'm happy to walk you through it.
    2 points
  5. Thanks a lot @BitPoet. I didn't know the "user access > permission page" on Processwire Doc. I just applied the permission and it works nicely ?
    2 points
  6. Finally I got my local setup switched today. PHP 8 which needed a new apache version too, and then SSL needs to be updated, etc., etc., ... Now I need to upgrade my IDE for PHP 8 support too. ? After all I could debug and correct the WireMail-SMTP module: https://github.com/horst-n/WireMailSmtp
    2 points
  7. @AndZyk Alright, I had some fun with it. Here's an improved script for the asset export, which can handle nested repeater and matrix repeater fields: /** * Get a flat array of all images on the given page, even in nested repeater / repeater matrix fields. * * @var Page $page The page to get the images from. * @return Pageimage[] Array of Pageimage objects. */ function getImages(Page $page): WireArray { $images = new WireArray(); $fields = $page->fields->each('name'); foreach ($fields as $field) { $value = $page->get($field); $type = $page->fields->{$field}->getFieldType(); if ($value instanceof Pageimage) { $images->add($value); } elseif ($value instanceof Pageimages) { $images->import($value->getArray()); } elseif ($value instanceof RepeaterMatrixPageArray || $value instanceof RepeaterPageArray) { foreach ($value as $repeaterPage) { $images->import(getImages($repeaterPage)); } } elseif ($value instanceof RepeaterMatrixPage || $value instanceof RepeaterPage) { $images->import(getImages($value)); } } return $images; } $images = getImages($page); // create target folder for the page assets $targetDir = $config->paths->assets . 'export/' . $page->name . '/'; $files->mkdir($targetDir, true); // move all images to the target folder foreach ($images as $image) { $name = $image->basename(); $target = $targetDir . $name; $src = $image->filename(); $files->copy($src, $target); } This could be extended in any number of ways: Handle file fields as well as images. Handle any other fields and dump them in the target folder as JSON. Handle native Page properties. At some point you got a complete page export module. Might want to look into the pages export module if you don't want to write all that stuff yourself ?
    2 points
  8. @fedeb Very interesting, thx for sharing! ? @ryan Wouldn't it be great to abstract all that findings/knowledge into the files API? Importing data from CSV is a quite common need and thinking about all the pieces (like utf8 encoding, wrong delimiters etc) can be tedious and does not need to be ? $file = "/my/large/file.csv"; $tpl = $templates->get('foo'); $parent = $pages->get(123); $options = [ 'delimiter' => ',', 'length' => 100, 'firstLineArrayKeys' => true, ]; while($line = $files->readCSV($file, $options)) { $p = $this->wire(new Page()); $p->template = $tpl; $p->parent = $parent; $p->title = $line['Foo Column']; $p->body = $line['Bar Column']; $p->save(); } Support this request on github: https://github.com/processwire/processwire-requests/issues/400
    2 points
  9. 101aandd.com New site mostly designed by the client and implemented in Processwire. Mostly concentrating on small subtle animations.
    1 point
  10. In a blog, I have a page field that references a library of writers (cbpage2) so their info doesn't have to be reentered for each post. At the bottom of the post I wanted to list, " the last 2 posts by THIS AUTHOR, but obviously not include the CURRENT PAGE POST" EDITING TO REMOVE ALL BUT THE apparent answer When I used title to match the variable $activepost it seemed to break - title too long? But when I matched it to the ID it seemed to work... $author = $page ->cbpage2->id; $activepost = $page->id; $articles = $pages->find("template=post, limit=50, cbpage2=$author, id!=$activepost, sort=-cbdate");
    1 point
  11. After a second look, apparently the offending HannaCodeHelper module was installed, although I don't recall installing it, was it part of the previous version by default? Anyway, disabling it, no more errors, problem solved.
    1 point
  12. Hi @robert - thanks very much for your work on this. I few initial observations for you ? 1) I think it would be useful if it worked in the admin (ie change the autoload to allow this) because Tracy's Console panel sets $page to the object of the page being edited. 2) If you are running recent versions of PHP, you get an error on line 180. A simple fix is to replace count with wireCount 3) On line 185 you need to replace get_class($val) with is_object($val) && get_class($val) to prevent a "on bool" type error. Again, probable related to recent PHP versions. 4) I am not yet sure what extra info this provides over the Page Field List & Values section of Tracy's RequestInfo panel. Is it just a formatting preference or is it the support for fields like Mystique - not sure if Tracy would handle this properly or not - never tried it. Thanks again!
    1 point
  13. Thank you very much @MoritzLost. This scripts works perfect. ? I have put it in a shell script to run it for all pages and modified it a little bit: Added a foreach-loop for all pages Only create folder if there are images Added timestamps to see what the shell script is doing I looked first in this module, but it only exports the pages as JSON without assets. The assets only get downloaded when importing. So this use case wasn't covered yet, as far as I know. ? I love this community. ? Regards, Andreas
    1 point
  14. Yes, that is easier haha, i have this currently setup. But i want to have the SEO benefits of having it on the same domain.
    1 point
  15. If using subdomains is an option I think it would be easier to serve the blog from blog.example.com
    1 point
  16. Thanks @MoritzLost now I got it, you've clarified my doubts. So many things to learn ? I'll bookmark this thread for reference in the future ?
    1 point
  17. @3fingers No that's actually correct, I'm checking the class of the formatted values there, not the class of the fieldtype. For example, for an image field, the $value will be an instance of Pageimage or Pageimages depending on the formatting settings, but the $type will be an instance of InputfieldImage. In fact, the $type variable is not needed at all. In the first version I started by checking the $type, but turned out checking the $value directly was more efficient. Though you could do it both ways. That would only work if you're working with field objects, not formatted values. The formatted value of a text field will be a string, so accessing $value->type will result in an error. The code you linked is slightly different since it iterates Field objects, not field names: // iterate over Field objects // @see https://processwire.com/api/ref/field/ foreach($this->wire('fields') as $field) {} // iterate over field names (strings) of fields on a page // @see https://processwire.com/api/ref/wire-array/each/ $fields = $page->fields->each('name'); foreach ($fields as $field) {}
    1 point
  18. @Hector Nguyen Functions can't be autoloaded in PHP. Two options to work around this: Put the QFramework\Function function in a class as a static method, then the class can be autoloaded. Add all files containing functions in the autoload files list in your composer.json. This way those files will be included on every request.
    1 point
  19. @ryan I'm afraid not, fget() in PHP will return one string (a line) but fgetcsv() will return an indexed array. An array in PHP is not very friendly with big dataset because it's still saved into the memory! The main difference is in memory usage. In the first case all the data are in memory when you return the array. Because of no matter what, if you "returns" values, that will be save in memory. In the second case you get a lazy approach, and you can iterate the values without keeping all of them in memory. This would be a great benefit when memory is a constraint compared to the size of the sum of all your data. I've managed to import 4GB CSV file on the Shared VPS 1GB mem in seconds, the bottleneck of "yield" method now is your MySQL.
    1 point
  20. @fedeb Glad that moving the $parent outside the loop helped there. The reason it helps is because after a $pages->save() is the automatic $pages->uncacheAll(), so the auto-assigned parent from the template is having to be re-loaded on every iteration. By keeping your own copy loaded and assigning it yourself, you are able to avoid that extra overhead in this case. Avoid getting repeaters involved. I wouldn't even experiment with it here. That will at minimum triple the number of pages (assuming every protein page could have a repeater). Repeaters would be just fine if you were working in the thousands-of-pages territory, but in the millions-of-pages territory, it's not going to be worth even attempting. Using a ProFields table field would be the best alternative if you needed it to be queryable data. If you didn't need it to be queryable data (groupID, start, end, sequence), I would leave them as they are, space-separated in a plain textarea field — they can easily be parsed out at runtime so you can access them as as properties of the page. (If that suits your need, let me know and I'll get into how that can be done). When working at large scale, it's also always good to consider custom building a Fieldtype module for the purpose too (that's another topic, but we can get into it too). For your groupID, if the same groupID is referenced by multiple proteins, and there is more information about each "group" (other than just an ID) then I think it would make sense for it to be a Page reference field. What is the max number of groupID+start+end+sequence rows that a protein can have? If there is a natural limit and it's not large, then that would open up some new storage possibilities too. Another optimization you can make in your loop: $page->sort = $i; This prevents it from having to detect and auto-assign a sort value based on the quantity of children the parent page has. For the $page->name, if each page will have a unique "protein-name" then you might also consider using that rather than the ("protein" . $i), as it will be more reflective of the page than a generic index number.
    1 point
  21. Hi, Thanks a lot for all the feedback. I did some additional tests based on all of the suggestions you gave me and results are already amazing!! Figure 1 shows @ryan suggestions tested independently: 1. I created the $template variable outside the loop. 2. I created the $parent variable outside the loop. The boost in performance is surprising! Defining the $parent outside the loop made a huge difference (before I didn't assigned the parent explicitly, it was already defined in the template thus the assignment was automatic) 4. I also tried this suggestion ($page->name = "protein" . $i;) and although it seems to boost a bit performance I didn't include the plot because results were not conclusive. Still I will include this in my code. Figure 2 is based on @horst suggestion. I tested the impact of calling gc_collect_cycles() and $pages->uncacheAll() after every $database->commit(). I didn't do a test for $pages->uncache($page) because I thought $pages->uncacheAll() was basically the same. Maybe this is not true (?). Results don't show any well defined boost in performance (I guess ryan's recent reply predicted this). I still need to try @BitPoet suggestion because I am sure this is something that will boost performance. I am now doing this tests on my personal computer. I will do this test when running on the dedicated server. I will also would like to try generators (first time a hear about them ) ______________________________________________________________________________________________________________________________________________________________________________________________ One last thing regarding the fields in the protein template and the data structure in general (the pseudo code I posted initially was just as an example). Proteins are classified into groups. Each protein can belong to more than one groups (max. 5). My original idea was to use repeaters because for each protein I have the following information repeated: GroupID [integer], start [integer], end [integer], sequence [text] The idea is that from GroupID you can go to the particular group page (I have around 50k groups) but I don't necessarily need a page reference for this. The csv is structured as follow. Note that some protein entries are repeated which means that I shouldn't create a new page but add an entry to the repeater field. Protein-name groupID start end sequence A0A151DJ30 41 3 94 CPFES[...]]VRQVEK A0A151DJ30 55 119 140 PWSGD[...]NWPTYKD A0A0L0D2B9 872 74 326 MPPRV[...]TTKWSKK V8NIV9 919 547 648 SFKYL[...]LEAKEC A0A1D2MNM4 927 13 109 GTRVW[...]IYTYCG A0A1D2MNM4 999 119 437 PWSGDN[...]]RQDTVT A0A167EE16 1085 167 236 KTYLS[...]YELLTT A0A0A0M635 1104 189 269 KADQE[...]INLVIV Since I know repeaters also creates additional overhead I am doing all my benchmarks without them. I can always build the websites without them. In the next days I will do some benchmarks including repeaters just to see how it goes. Once again, thanks for all the replies!
    1 point
  22. @Hector Nguyen This is cool to see generators in action. Though as far as I know, PHP's fgetcsv() never loads the whole file in memory at the same time, regardless of which method is used to call it. I think it just loads one line at a time (?), but this reminds me that an optimization to fgetcsv() is to tell it what the longest possible line might be (as 2nd argument), so that it doesn't have to figure it out. Fedeb's example has 0 as the 2nd argument to fgetcsv(), which means "let PHP figure it out", so some overhead could be reduced here by giving it a number like 1024 or whatever the largest line length (in bytes) might be. There may be other benefits to using generators here though? I haven't experimented with them much yet so am curious.
    1 point
  23. I think you could try the generators here while you are playing with CSV. For .eg <?php function getRows($file) { $handle = fopen($file, 'rb'); if ($handle === false) { throw new Exception('open file '.$file.' error'); } while (feof($handle) === false) { yield fgetcsv($handle); } fclose($handle); } // allocate memory for only a single line in the csv file // do not need the entire csv file is read into memory $generator = getRows('../data/20_mil_data.csv'); // foreach ($it as $row) {print_r($row);} while ($generator->valid()) { print_r($generator->current()); //$generator->current() is your $row // playing with ProcessWire here $generator->next(); } $generator->rewind(); // http://php.net/manual/en/class.generator.php That's always my #1 choice while working with big datasets in PHP.
    1 point
  24. Unless I'm forgetting something, the $pages->uncache($page); won't help here because $page is a newly created Page that wasn't loaded from the database. So it's not going to be cached either. Uncaching pages is potentially useful when iterating through large groups of existing pages. For instance, if you are rendering or exporting something large from the contents of existing pages, you might like to $pages->uncacheAll() after getting through a thousand of them to clear room for another paginated batch. Though nowadays we have $pages->findMany() and $pages->findRaw(), so there are fewer instances were you would even need to use uncache or uncacheAll, if ever. ProcessWire actually does an uncacheAll() internally after saving a page already. This is necessary because changes to a page or additions/deletions to the page tree may affect other pages, and we don't want any potential for old cached data to appear in future $pages->find() or other operations. Just one example is if we called $parent->children() before a save, and then after the save called it again, we'd want our new page to be in the children rather than having it return the previously cached value. There are a lot of similar cases, so the safest bet is for PW to uncache the results of future page get/find operations after a save as the default behavior. So that's the way it's always done it. As far as I can tell from fedeb's example (and often other with import operations), it may be better to tell PW to skip this "uncacheAll-after-save" behavior. That's because imports often involve Page reference fields, and you don't want PW to have to reload referenced pages after every save. So you could potentially reduce overhead by telling it not to uncache after save, i.e. $pages->save($page, [ 'uncacheAll' => false ]); I'm not sure if fedeb's import involves loading of any other pages, whether for page reference fields, or anything else. So it may not matter one way or the other here, but wanted to mention it just in case. I know about ProcessWire tuning, but not about MySQL server tuning. When dealing with 20 million rows that seems like getting into the territory where optimizations to the DB configuration deserve a lot of focus, so I would bet that BitPoet's suggestions are going to make the most difference.
    1 point
  25. Just to add, if the points from @ryan and @horst aren't enough (they should boost import times quite noticeably) you could try dropping the FULLTEXT keys on the relevant fields' tables before the import and recreating them afterwards (ALTER TABLE `field_fieldname` DROP KEY `data` / ALTER TABLE `field_fieldname` ADD FULLTEXT KEY `data` (`data`)). Finally, a big part of MySQL performance depends on server tuning. The default size for the InnoDB buffer pool (the part of RAM where MySQL holds data and indexes) is rather small at 128MB. If you have a dedicated database server, you can up that to 80% of physical memory to avoid unnecessary disk access.
    1 point
  26. Whats about a #5: $pages->uncache($page) after the $database->commit(); to additionally free some memory.
    1 point
  27. @fedeb That's the largest quantity of pages I've heard of anyone creating in ProcessWire, by a pretty large margin. So you are in somewhat uncharted territory. But that's really cool you are doing that. I would be curious how different the graph would be if you split it up into batches so that you aren't creating more than a certain quantity per execution/runtime. For instance, maybe you create 10k in one execution and another 10k in the next, etc., or something like that. Would the same slowdown still occur? If so, I would start to think it might be the database index and increased overhead in maintaining that index as the quantity increases. On the flip side, if restarting the process to create each set in batches solves the slowdown, then I would think it might be memory or resource related. A couple things you can do to potentially (?) improve your page creation time: 1. At the top of your code (before the loop) put: $template = $templates->get('protein'); Then within the loop set: $page->template = $template; 2. I don't see a parent page assignment. How are you doing that? Double check that you aren't asking PW to load the parent page every time in the loop and instead handle it like with the template in #1 above. 3. What kind of fields are on your "protein" template? Depending on their type, there may be potential optimizations. Especially if any are Page references. Can you paste in a line or two from the CSV? 4. If you can assign a $page->name = "protein" . $i; rather than having PW auto-generate a name from the title, that will save some resources too.
    1 point
  28. Hi everyone. This is my Russian language pack for version PW 3.0.166. It contains 197 translation files. I did the translation for my site, but I decided to share it with everyone. ) I will update it periodically. https://github.com/TikhonovEduard/pw-wire-ru
    1 point
  29. I know that a runtime field is tempting because it abstracts complexity but such things can quite easily be done via hooks as well. That means you don't need a module and you get even more possibilities: <?php $wire->addHookAfter("ProcessPageEdit::buildForm", function($event) { $page = $event->object->getPage(); if($page->template != "mytemplate") return; $form = $event->return; if($f = $form->get("title")) { $next = $page->next; if($next) $f->notes = "Title of next page: ".$next->title; } });
    1 point
  30. The HTML5 number input should handle that already. For formatting for output I'd suggest looking at NumberFormatter of php / the intl extension. Having worked with a cldr based library elsewhere extensively I can suggest everyone dealing with locales to use a library for all those differences instead of building one-of solutions.
    1 point
  31. Additionally Russian translations of several modules: ColorPicker, IconPicker, Page Hit Counter, Range Slider, Hanna Code, Hanna Code Dialog, Login/Register/Profile, Markup Cookie Consent, Google reCAPTCHA, Pollino, Sitemap, SearchEngine, Site Profile Exporter, ProcessWire Upgrade, WireMail: SMTP https://github.com/TikhonovEduard/pw-site-ru
    1 point
  32. Hi @Eduard, Thanks for an excellent first post and welcome to the forums ?.
    1 point
  33. Hey @Eduard, Thanks for working on this! It would be great if you could also submit this to the modules directory (https://modules.processwire.com/add/). There's currently one existing Russian translation, but it was last updated 8 years ago, so your version should probably replace that one... ?
    1 point
  34. Hi, here is Rating module what I developed for project but at the end didn't use it (p.s.). and if you want you can use/test it. It use Ajax POST on front-end and cookie to prevent multiple votes . Inside module global settings you have option to set desired max number of stars for votes (up to 10). For usage and instructions please read README.md file (and do not skip step 5.). Here are some screenshots Field in admin backend: Front-end (echo $page->my_rating_field) Clean cookie (for testing) This is not polished module version, and only what is "complex" is how to include javascript files, but there are instructions how to do that, and if that is a problem I will do some changes or feel free to contact me for free support/assistance. Regards. P.S. I developed this module for project (2018.) where one task was and comments component, and that's was ok but my problem was that I first developed this module, and after that installed Ryan Comments module. I didn't know that Comments module has rating part ? !@#$%...". FieldtypeRating.zip
    1 point
  35. While working on optimizing Comments field for my blog, I noticed it could have been useful to have a GDPR checkbox in the form, to get privacy acceptance before a comment is submitted. At the beginning I though to inject the checkbox processing the rendered comment form, but as I already modified FieldtypeComments to create a Language field, I decided to continue on that road to add the GDPR checkbox. We will not modify the original FieldtypeComments in wire/modules/Fieldtype/FieldtypeComments, but copy it to site/modules/FieldtypeComments. Please refer to the initial part of this tutorial for the detailed steps to duplicate the module and make PW aware of which module version to use. Now that FieldtypeComments is duplicated, we can proceed with the necessary modifications. Inside the module there are 14 files, but do not worry ... only ContactForm.php and (optionally) comments.css will have to be modified. First we will modify the $options class property of ContactForm.php to add a new 'gdpr' label. Later we will use this option to pass the label's text associated with the checkbox. protected $options = array( … 'labels' => array( 'cite' => '', // Your Name 'email' => '', // Your E-Mail 'website' => '',// Website 'stars' => '', // Your Rating 'text' => '', // Comments 'submit' => '', // Submit 'starsRequired' => '', // Please select a star rating 'gdpr' => '', // >>>>> ADD THIS LINE ), As a second step it will be necessary to create the markup of the checkbox and of its label. We will do that by modifying function renderFormNormal() in ContactForm.php. Uikit 3 Site/Blog Profile is indirectly calling this function, so for my purpose it was enough. In case your application is using threaded comments, it will be necessary to modify also renderFormThread(). "\n\t\t<textarea name='text' class='required' required='required' id='{$id}_text' rows='$attrs[rows]' cols='$attrs[cols]'>$inputValues[text]</textarea>" . ... "\n\t</p>" . "\n\t<p class='CommentFormGdpr {$id}_gdpr'>" . //>>>>> ADD THIS BLOCK - START "\n\t\t<input class='uk-checkbox' type='checkbox' name='gdpr' value='checked' required='required'>" . "\n\t\t<label for='{$id}_gdpr'>$labels[gdpr]</label>" . "\n\t</p>" . //>>>>> ADD THIS BLOCK - END $this->renderNotifyOptions() . ... The last ContactForm.php modification will include our checkbox in processInput() to block comments submissions if GDPR checkbox is not filled. Please note this will operate in case you do not place "required" in the <input> directive. ... $errors = array(); foreach(array('cite', 'email', 'website', 'stars', 'text', 'gdpr') as $key) { //>>>>> ADD 'gdpr' in the array ... Now let's see how to call the modified form. If you are using Uikit 3 Site/Blog Profile you will have simply to modify the template file where ukCommentForm() is called (example: blog-post.php template). There we will prepare our checkbox message and pass it to ukCommentForm() as an argument option. echo ukHeading3(__('Join the discussion'), "icon=comment"); $gdpr = __('I agree with the processing of my personal data. I have read and accept the Privacy Policy.'); echo ukCommentForm($comments, ['labels' => ['gdpr' => $gdpr]]); However, if you are using comments in multiple template files, it makes more sense to directly modify ukCommentForm() presetting the new options inside the function body: $defaults = array( 'headline' => '', 'successMessage' => __('Thank you, your comment has been posted.'), 'pendingMessage' => __('Your comment has been submitted and will appear once approved by the moderator.'), 'errorMessage' => __('Your comment was not saved due to one or more errors.') . ' ' . __('Please check that you have completed all fields before submitting again.'), // >>>>> SEE THE HONEYPOT TUTORIAL 'requireHoneypotField' => 'email2', //>>>> ADD THESE FOUR LINES 'labels' => array( 'gdpr' => __('I agree with the processing of my personal data. I have read and accept the Privacy Policy.'), ), ); Before testing, we will modify the file comments.css adding these two directives (that's purely optional): .CommentFormGdpr input[type=checkbox] { background-color: white; } .CommentFormGdpr label { padding-left: 10px } Now let's test our form. Once it is filled with our data and comment it will look like this: If the user is pressing the submit button without accepting the GDPR checkbox, the comment will not be submitted and an error is displayed (in case you have used "required" in <input> you get this tooltip box, otherwise you will get an alert message): Now we accept the box After acceptance and submission, the comment form will be successfully sent. The standard success message is shown and the form is again displayed empty (with just cite and email pre-filled) ready for a next comment submission. Again I hope you will find some useful hint with that.
    1 point
×
×
  • Create New...