Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Valery

  1. Thank you, Robin S! That was exactly what I was looking for ?
  2. Hello everybody! One of my clients seems to be using the extra page actions a lot, and he is rather that they are visible to him without clicking the "extras" icon (">") to expand. So I decided I'd throw a couple of hooks to add some extra page actions to the regular page actions, so that they become instantly accessible without the need to expand the extras. Here's a fragment of code from my /site/ready.php I was trying to use a global variable to store the extra actions array but I don't seem to be able to use it in the second hook. /* * A couple of hooks to add page action * "unpub" to the list of non-ajax page actions * alongside with "new", "edit" etc * */ wire()->addHookAfter("ProcessPageListActions::getExtraActions", function($event) { global $extraActions; $extraActions = $event->return; }); wire()->addHookAfter("ProcessPageListActions::getActions", function($event) { $actions = $event->return; global $extraActions; $actions['unpub'] = $extraActions['unpub']; $event->return = $actions; }); Somehow var_dump(extraActions) keeps giving me a NULL. I don't really think I should develop a whole module just to change the list of page actions; I just need the extras to be immediately visible without the click. Could you please help me out? Any hints or solutions are more than welcome!
  3. Hey there! While not a part of PW, you might want to check out htmLawed, an advanced HTML filter/purifier.
  4. Hi! Will "Show this field only if" condition work for you? You could specify an id of a page on which you would like your field to be present. I am attaching a screenshot to illustrate my point.
  5. Hello! I've been migrating a Joomla site to PW. As I was at it, I could not help notice that the text of the articles I was importing into PW was messy. The site owner confessed that he simply pasted his text from MS Word into Joomla's editor, which resulted in very dirty HTML full of inline CSS and custom class names. So this got me thinking of tools I could use to clean this dirty HTML. After some search I stumbled across a handy PHP tool named htmLawed developed by Santosh Patnaik. What I liked about htmLawed was its flexibility in filtering and cleaning HTML. My goal was to strip off the style attributes from <p>'s in my HTML, which was way below htmLawed's capabilities. It can do a lot more -- reading the list of features got me hooked up! The htmLawed download contains just two files: the software itself, and a script to measure performance. Including htmLawed in a PW template is a matter of one include(), and after that you are ready to go. Using any HTML formatter adds a certain memory footprint, so using htmLawed for outputting each and every piece of HTML might not be the best idea; but nevertheless it's a handy and flexible tool for sanitizing and beautifying any HTML before saving it in your PW page. htmLawed is free, well-documented and flexible. Give it a try if you feel you need more control over HTML that your users post. http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/
  6. Hi everybody! I am migrating a Joomla site and I am having troubles with Joomla's URLs. Joomla links look like this: www.example.com/index.php?option=com_content&view=article&id=80 I have set up my prependTemplateFile to detect GET variables that Joomla uses, and redirect to PW pages: // Example: index.php?option=com_content&view=article&id=80 if ($input->get('option') && $input->get('id')) { $j_article_id = $input->get->int('id'); $target = $pages->findOne("j_migration_id=$j_article_id"); // `j_migration_id` is the field where Joomla IDs are stored if ($target->id != 0) { $session->redirect($target->url()); } else { $session->redirect($target->url()); } } The problem I am having is this: the above code works fine with any template but home.php But when a Joomla URL is called from the root of the site, PW simply redirects to the root page. For instance, www.example.com/index.php?option=com_content&view=article&id=80 redirects me to www.example.com While www.example.com/some-page-with-custom-template/index.php?option=com_content&view=article&id=80 does what expected: redirects to the right page (e.g. www.example.com/some-page-with-custom-template/some-page/) The question is: does either the root page or the home template have there any exceptions for GET variables processing?
  7. Hello Michael, In response to your earlier post: I've tried storing PageTable pages directly under the parent but it didn't work (for me, at least). However, the idea behind trying to use existing pages in a PageTable field escapes me. Why not use Page fields with PageListSelect+ instead? For instance, here is a screenshot of mock-up of a color chooser for a webshop item.
  8. Hi verdeandrea, and thanks for a chance to think it over. Why not have a template for Teachers where their names and emails would go, and another template with page fields which would display teachers' names and emails on your Course page? I did a mock-up quickie. If you are curious as to how it might look, I've attached a couple of screenshots.
  9. Excuse me for interrupting, but what PW version did you find this option for a PageTable field? I am using 2.5.3 and I see nothing like an "add an existing page" toggle for a PageTable field.
  10. Hi, Just my 5 cents. Give Closure Compiler Service a try. It is good at removing "dead" code, i.e. the functions of your libraries/frameworks that are not used in your JS code.
  11. Hello, You might want to look at HAProxy. Here's a nice article on using it. https://www.digitalocean.com/community/tutorials/how-to-use-haproxy-to-set-up-mysql-load-balancing--3 And this is an article on how to set up master-master Mysql replication. https://www.digitalocean.com/community/tutorials/how-to-set-up-mysql-master-master-replication
  12. Hello, PageTable is described as a leaner way to enter large amount of repeatable data into the admin backend, possibly with different sets of fields (think different templates). The main advantages compared to the Repeater are: 1. PageTable can save new pages where you tell it to. Repeaters are saved deep down the Admin pages and are given cryptic names. 2. One PageTable field can make use of several different templates. Repeaters play according to the "One repeater = one template" rule (roughly put). PageTable is a PageArray, so relevant methods are applicable. For example, here is how to add and remove items from a PageTable field using the PW API: // create a new page // IMPORTANT: your PageTable field must be configured to use template 'basic-page' in the field Setup tab $newpage = $pages->add('basic-page', '/about/', 'address', array('title' => 'Write to us')); $page->of(false); // add the newly created page to 'pt' which is a PageTable field $page->pt->add($newpage); $page->save(); Here is how to remove the first item from a PageTable field: $page->of(false); // 'pt' is a field of type PageTable $page->pt->get(0)->delete(); $page->save(); That's a quick overview of this new field type. Give it a bit of your attention, it's quite promising. Cheers, Valery.
  13. Thanks, adrian, really appreciate your help. Yes, it started to work and I saw the 'custom' page action. The problem is, it now affects all instances of ProcessPageList, including /processwire/page/edit Thanks, though. I will see what I can do.
  14. Hello adrian, I am sorry to say but this code does not work on my environment. Here is what I have in my module: public function init () { parent::init(); wire()->addHookAfter("ProcessPageListRender::getPageActions", function($event) { // anonymous function $event->replace = true; $new_action = array ( 'cn' => 'custom', 'name' => 'Custom', 'url' => '/my/path' ); $event->return = $new_action; }); } public function ___execute () { return wire('modules')->get("ProcessPageList")->execute(); } The list of page actions that I receive in the output is not changed. I tried different browsers, did a reset & flushed cache. I sense that the "ProcessPageListRender::getPageActions" hook is somehow not working for me. If so, what could be the reason?
  15. Valery

    Fooled isit.pw

    Hey guys, Please, please read netcarver's warning before making changes to the 'admin' template settings. Changing access for non-logged in users from login prompt to http 404 may effectively block you from logging in! If you have locked yourself out (like I just did), do the following: - Log in to phpMyAdmin. - Find the 'pages' table, then Browse it and find the Id of the page named 'login'. Mine was 23 and I did not change the default settings. - Then go to the table 'templates', find the line with name=admin and edit it. Add , "redirectLogin":23 to the array (assuming that 23 is the id of the login page). Save (press "Go"). Now you will be able to access your admin login prompt. I attach a screenshot, so that should be pretty clear.
  16. Valery

    Fooled isit.pw

    Oh boy, that got me in trouble Just in case anyone's interested how to block a request containing "?it=", here's a short rule for nginx: if ($request_uri ~ .*.\?it.*) { return 404; } If is evil. Hiding ProcessWire from isit.pw is even worse. Tread the path of eeevil
  17. Valery

    Fooled isit.pw

    No problem. It's done in just two steps. Assuming you are logged in with administrative rights: 1. Go to Setup -> Templates. Click "Filters", then set "Show system templates" to Yes. Click the "admin" template in the list. 2. On the "Edit template" page click the tab "Access" and scroll down to "What to do when user attempts to view a page and has no access?". Select "Show a 404 Page" instead of "Show the login page". Save your admin template settings and you are done! This setting works for any template, not just admin templates. I attach a couple of screenshots to illustrate the above two steps. Hope it helps you. P.S.: If you want to go a little bit paranoid, I suggest playing with request handling such that GET requests with ?it= in them would be handled differently.
  18. Valery

    Fooled isit.pw

    Hey everybody, Many of us know about isit.pw, the web site that checks if you are running PW. I thought I should find out how it knows whether a web site is ProcessWire-powered. Turns out, it sends a "GET /?it=/trash/ HTTP/1.0" request, and if the response is 200, then PW it is. Next thing I did was to go to admin templates and change the default behavior such that ProcessWire would through back a 404 instead of offering to log in. Now, isit.pw does not like me for "not running" ProcessWire. However, I liked the method isit.pw uses. It can be handy if you want to run a quick check like this: http://<...>/<some_page_name/?it=/ If you get the Home page, it means you've hit a ProcessWire-powered web site. Have a good Friday and a nice week-end
  19. Thanks, adrian. I'll better just show the code of my module. I am attaching a hook which shouild modify the URL of the page action "Edit": public function init () { parent::init(); wire()->addHookAfter("ProcessPageListRender::getPageActions", function($event) { // anonymous function $custom_actions = $event->return; $custom_actions[0]['url'] = '/my/path'; $event->replace = true; $event->return = $custom_actions; } ); } public function ___execute () { parent::___execute(); $page_lister = wire('modules')->get("ProcessPageList"); return $page_lister->execute(); } Cannot really tell why but the list of page actions is not modified.
  20. Thanks. Was already browsing Nico's code. Fruitless so far
  21. Hello again, I need your help 'cause I am really stuck. I am developing a module where I would have a PageList. With kongondo's help I managed to tweak some of PageList aspects. However, I am not moving anywhere with Page Actions. My task is to build a custom list of page actions instead of the default "edit", "view", "new" etc. I added an "after" hook to ProcessPageListRender: wire()->addHookAfter('ProcessPageListRender::getPageActions', function($event) { // inside a class $actions[] = array ( 'cn' => 'Custom actions', 'name' => 'Custome name', 'url' => "/custom/path" ); $event->return = $actions; } ); Then the ProcessPageList gets executed like this: public function ___execute () { $lister = wire('modules')->get("ProcessPageList"); $lister->id = 99999; return $lister->execute(); } But it does not seem to work. ___getPageActions is a hookable method but I cannot figure out how to put a hook to modify the returned list of page actions. A little help, guys? Please?
  22. Thanks, adrian. Cannot really say for sure if this commit addresses my issue but I found that the CSRF challenge cookie lasts a whopping 30 days. // set challenge cookie to last 30 days (should be longer than any session would feasibly last) setcookie(session_name() . '_challenge', $challenge, time()+60*60*24*30, '/', null, false, true); Now that's really more than enough time to think a form submission over and over a few hundred times
  23. Hi everybody, As much as we hate the CAPTCHA, it is still used. I decided to share my solution of using ProcessWire forms with CAPTCHA. My goals were quite simple: 1. Create a form and customize it. 2. Get an Inputfield for a page field and append this Inputfield to the form. 3. Display a CAPTCHA. 4. Process the form and check its fields for correctness. 5. Check the CAPTCHA. 6. If steps 4-5 fail, display the form back with the user input intact. What I found to be not quite easy was how to push a different CAPTCHA into an already submitted form. (A spoiler: this is done quite smoothly with the power of ProcessWire's Inputfields.) Let's start, then. My Form class has several methods: appendFields() accepts an array of field names, gets their Inputfields from a template and appends these to the form displayForm() adds a CAPTCHA and returns the form's HTML processForm() checks the CSRF session ID and user input and delegates CAPTCHA verification getCAPTCHA() returns the html for the CAPTCHA checkCAPTCHA() checks the user input against the CAPTCHA and sets the verification flag to true or false. That's mostly it. Now to the code itself. class Form { public $form; public $captchaVerificationResult; // captcha verification flag (bool) public function __construct () { // form is created upon class instantiation $form = wire('modules')->get("InputfieldForm"); $form->action = wire('page')->path; $form->method = 'post'; $form->attr("id+name", 'form'); $form->attr("class", 'pure form'); // I use PureCSS class name here as an example // how form items are displayed can be customized $form->setMarkup(array ( 'list' => "\n{out}\n", 'item' => "\n\t{out}\n\t", 'item_label' => "\n\t\t<label class='pure-form' for='{for}'>{out}</label>", 'item_content' => "\n\t{out}\n\t", 'item_error' => "\n<p><span class='ui-state-error'>{out}</span></p>", 'item_description' => "\n<p class='description'>{out}</p>", 'item_head' => "\n<h2>{out}</h2>", 'item_notes' => "\n<p class='notes'>{out}</p>", ) ); // form classes can also be customized $form->setClasses(array ( 'list' => 'Inputfields', 'list_clearfix' => 'ui-helper-clearfix', 'item' => '', 'item_required' => '', 'item_error' => 'ui-state-error InputfieldStateError', 'item_collapsed' => '', 'item_column_width' => '', 'item_column_width_first' => '', ) ); $this->form = $form; } public function appendFields ($fields) { /* * $fields is an array of field names * * for each field name a corresponding Inputfield is fetched from a page * and appended to the form * */ $page = wire('pages')->get("999999999"); // this is the page that has your fields foreach ($fields as $f_name) { $field = wire('fields')->get($f_name); $if = $field->getInputfield($page); // this page must have field name $f_name among its fields! $this->form->append($if); } /* * * CAPTCHA placeholder */ /* * create an InputfieldMarkup field, * give it an ID and append to the form * * Later this Inputfield can be easily found by its ID. * * */ $inputfield = new InputfieldMarkup; // inputfield markup is appendable to form! $inputfield->id = 'captcha1'; // id of the InputfieldMarkup with CAPTCHA $this->form->append($inputfield); /** * finally, the SUBMIT button * */ $submit = wire('modules')->get('InputfieldSubmit'); $submit->skipLabel = Inputfield::skipLabelBlank; // remove the label $submit->name = 'submit_save'; $submit->value = "Save"; $this->form->append($submit); } public function get () { return $this->form; } public function displayForm ($form) { /* * Each time the form is displayed, the CAPTCHA should be there * * So, behore doing $form->render() let's get an InputFieldMarkup field by ID * and pour some fresh CAPTCHA in it * * */ $form->children->findOne("id=captcha1")->value .= $this->getCAPTCHA(); return $form->render(); } public function processForm ($form) { try { $form->processInput(wire('input')->post); wire('session')->CSRF->validate(); } catch (WireCSRFException $e) { die ($this->displayForm($form)); } $this->checkCAPTCHA(); // sets class property $captcha_verification_result to false/true if (!$this->captcha_verification_result) { $form->children->findOne("id=captcha1")->value = "Wrong captcha, try again <br />"; // this will go before the CAPTCHA code die($this->displayForm($form)); } else { // let's do some simple checks // you can and should do more of yours, of course $email = $form->get("b_email"); if (filter_var($email->value, FILTER_VALIDATE_EMAIL)) { $email->error = "Incorrect email format"; } if ($form->getErrors()) { // the form contains errors, display it with user input die($this->displayForm($form)); } else { echo "Thanks for the submission!"; // everything is just right } } } public function getCAPTCHA () { /* * * $captcha_html = your CAPTCHA code * */ return $captcha_html; // have a lovely CAPTCHA } public function checkCAPTCHA () { /* * * get some response and check it * * */ if ($response->failed) { $this->captchaVerificationResult = false; } else { $this->captchaVerificationResult = true; } } } Here's how this can be used: $form = new Form(); $formFields = array ("name", "email"); $form->appendFields($formFields); $newForm = $form->get(); if ($input->post->submit_save) { // user submitted the form, process it and check for errors $form->processForm($newForm); // the form was processed successfully because nothing die()'d. Now it's time to play with what's been submitted. } else { echo $form->displayForm($newForm); // if it's not been submitted, just display it and the CAPTCHA } With this being just a generic idea on how to do form processing with CAPTCHA, I hope portions of this code will prove helpful in your development. Your comments, corrections and suggestions are very much welcome.
  24. Hi fellow ProcessWire devs, Recently I had a revelation which was: CSRF session IDs do expire in ProcessWire with time (though I do not yet know how soon it is). So, for instance, if you leave a form unsubmitted for some time and then try to post it, ProcessWire will through a CSRF validation error. I gave this a little bit of thought and came up with some code like this: public function processForm ($form) { try { $form->processInput(wire('input')->post); wire('session')->CSRF->validate(); } catch (WireCSRFException $e) { die($this->displayForm($form)); // $this->displayForm($form) is a method which returns $form->render() } } I run a few public forms for comments and orders, and I do not want to piss off my guests and clients when they have to re-enter form data again. If their CSRF session runs out, I will send them their form back with their data filled in. What do you think of this approach? How likely is that a CSRF session expiry occurs? Should I be harsh when a CSRF validation fails, or should I give them a second try?
  25. kongondo, thank you, man! It worked like a charm! Wow, another great advance in my project. Can't thank you enough! And it was not too much of a puzzle, as it seems to me now One small note for those who would like to use this trick in a ProcessPageEdit-based module: if you construct your own form for ProcessPageEdit, then put the rendered ProcessPageList in an InputfieldMarkup (and append it to an InputfieldWrapper field if you want to): $form = $this->modules->get('InputfieldForm'); // prep the form $wrapper = new InputfieldWrapper; // a wrapper $wrapper->attr('value', 'List of available articles'); $p = $this->modules->get('ProcessPageList'); // kongondo's code goes here $p->set('id', 9999); // setting the parent page $pageTree = new InputfieldMarkup; // the placeholder $pageTree->value = $p->execute(); // fill the InputfieldMarkup form field... $wrapper->add($pageTree); // put inside the wrapper... $form->append($wrapper); // append the wrapper Because Process<something> modules do not append directly to forms, you know
  • Create New...