Jump to content

DrQuincy

Members
  • Posts

    352
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by DrQuincy

  1. Thanks for clearing that up — you've been a huge help to me. ?
  2. Ah right, thanks for the clarification. ? Just to be clear though, simply calling $session->CSRF->validate() will not reset the token, will it? That's all I'm doing, which I guess is a custom CSRF check. Thanks!
  3. Many thanks for the detailed reply. ? Could this include simply saving a page in the CMS? How and why does this happen? It was my understanding CSRF tokens were basically just a random string tied to the session and so long as the value in the form and the value session match all is okay. If I understand what you're saying correctly this could get in the way and cause false negatives. I don't see why two legitimate requests couldn't happen concurrently. Is there any way to disable this? I always have $config->sessionFingerprint = false in config.php because one of my colleague's IP changes all the time (several times a day) and kept getting logged out of the CMS. Does the CSRF token always become invalid on a IP change? If so, again this could be problematic. If an IP change does not affect this with $config->sessionFingerprint = false then it can't be this since, as I say, I always switch it off. I do wonder if it is to do with him having the front-end and back-end open at the same time. Thanks!
  4. I use PW's in-built CSRF for forms. So I add a hidden tag: <input type="hidden" name="<?= $session()->CSRF->getTokenName() ?>" value="<?= $session()->CSRF->getTokenValue() ?>" /> And then when the form is sent I check: try { $session->CSRF->validate(); } catch (\Exception $e) { // ... } It has always worked for me. I have a client saying that they got a CSRF error when they filled a form out (payment form; quite lengthy but can't imagine it taking more than five minutes). This person is not very technical so it is hard to get information out of them but I can only conclude they either took ages filling the form out or they were signed into the CMS and logged out after loading the form (the latter has happened to me before). My questions are: What causes the $session()->CSRF token to change? Is it directly tied to session_id() or can a change occur (assuming no manual regeneration in my code) without the session ID changing? Do you think it is advisable on longer forms that use CSRF to have a background AJAX call that runs a simple PHP script every few minutes that simply has session_start() in it (rather than create a more resource-heavy PW request) to keep the session alive? Thanks.
  5. That's very useful, thanks for the code sample.
  6. Thanks for your reply. Weird, I replied last night and the reply has gone. Must not have submitted properly. ? I had a hook that was preventing pages that were pointed to via a Page Reference field from being unpublished. It worked fine but when you cloned a page it seems to flag a false positive and was regarded by the system as being linked to from every page. Anyway, I have since found a very simple fix: simply check when the page was created. If less than a few seconds old don't run the hook. This seems to work but I will test more thoroughly. $secondsAgoCreated = time() - $page->created; // Check it's older than 3 seconds; if a clone secondsAgoCreated seems to be 1 if ($secondsAgoCreated > 3) { // Run the hook }
  7. Exactly as the title says: can you know if a page was cloned in a Pages:saveReady hook? I know there is Pages::cloned but can't work out how to do something in cloned that is available in saveReady. I tried useing sessions but it didn't seem to work. So this would be a one-time thing per page. I.e. after the initial save it is no longer regarded as a cloned page. Does that make sense? Is there an easy way to do this? Thanks.
  8. I checked the docs and couldn't find an answer to this. If you define a hook such as Pages::saveReady it gets called when saving a page in the CMS. And also any validation gets checked. I notice though in your own code when you work with pages in the API, e.g. $page->save(), then validation checks don't take place. For example, if you had a number field and specified in its Input settings it has to be between 0 and 100 you could set it to 1000 via the API. Also, the Pages::saveReady doesn't get called. I have tested this with throwing an exception in a Pages::saveReady and it doesn't seem to trigger when using $page->save() but prevents saving in the CMS. I assume this is intended behaviour as when you use the API you should be able to do whatever you want and it is up to you to perform the required checks before you save. My question is: when you run $page->save() or $pages->save($page), is there any way to make it run as though you were saving it in the CMS? I.e. throw an exception if validation fails and/or run relevant hooks. I hope that makes sense, thanks.
  9. Ah, perfect, thanks! So, this is what I've got and seems to work so far but I need to test it a bit more. public static function freezePages($selector) { // Make name and template read-only \Processwire\wire('pages')->addHookAfter('ProcessPageEdit::buildForm', function(\Processwire\HookEvent $event) use($selector) { $wrapper = $event->return; $page = $event->object->getPage(); if ($wrapper->has('_pw_page_name') === false || $wrapper->has('template') === false || $wrapper->has('parent_id') === false) { return; } if ($page->matches($selector) === true) { $inputState = \Processwire\Inputfield::collapsedNoLocked; $labelPostfix = ' (page frozen, value not editable)'; $wrapper->_pw_page_name->collapsed = $inputState; $wrapper->_pw_page_name->label .= $labelPostfix; $wrapper->_pw_page_name->description = null; // Hide note about 09, etc $wrapper->template->collapsed = $inputState; $wrapper->template->label .= $labelPostfix; $wrapper->parent_id->label .= $labelPostfix; $wrapper->parent_id->contentClass = 'parent-disabled'; // Setting to Inputfield::collapsedNoLocked shows the ID so instead just disable via a CSS class from custom CSS file } }); // Remove “Move” button \Processwire\wire('pages')->addHookAfter('ProcessPageListActions::getActions', null, function(\Processwire\HookEvent $event) use($selector) { $page = $event->arguments(0); $actions = $event->return; if ($page->matches($selector) === true) { unset($actions['move']); } $event->return = $actions; }); } Usage is then like this: Hooks::freezePages('template=settings|site-map'); Any matching pages cannot be moved or have their name, parent or template changed. Note the parent part is blocked via a CSS class so in my case I have some custom CSS included from admin.php: .parent-disabled { opacity: 0.5; pointer-events: none; } I would've used an inline style but couldn't find that option.
  10. That's a much better idea, thanks @Robin S ? I have nearly got it working. My only issue is in the ProcessPageEdit::buildForm hook, how do I get the current page so I can match to my selector? I.e. how do I get to $page from here: \Processwire\wire('pages')->addHookAfter('ProcessPageEdit::buildForm', function(\Processwire\HookEvent $event) use($selector) { $form = $event->arguments(0); // ... $form is of type ProcessWire\InputfieldForm. How can I get to the $page being edited from that? Once I get this I can post the full working code. Thanks.
  11. I have the following hook but it doesn't quite work. The idea is I can specify a selector and it prevents the client from moving matched pages and changing their name and template. I know there are already options that allow you to do some of this but I wanted to cover it in a single hook. It so the client doesn't break things by mistake and by fixing the name and location of something like /settings/ it means I can do $pages->get('/settings/') instead of $pages->get(1063). So I'd set the hook like this: Hooks::freezePages('template=settings|site-map|emails|contact|downloads'); And then my function looks like this: public static function freezePages($selector) { \Processwire\wire()->addHookAfter('Pages::saveReady, ProcessPageSort::execute', function(\Processwire\HookEvent $event) use($selector) { $page = $event->arguments(0); // When sorting pages this can be null so do nothing in that case if ($page === null) { return; } // $page->created !== - filters out new pages as when you create them; only do this on pages alrady created if ($page->created !== 0) { $page->of(false); if ($page->matches($selector) === true) { // Get previous values (see https://processwire.com/talk/topic/24216-how-to-get-old-page-values-in-a-processwire-page-save-hook-and-some-other-notes-on-save-hooks/) $clone = clone($page); \Processwire\wire('pages')->uncache($clone); $origPage = \Processwire\wire('pages')->get($clone->id); // Debugging to see how any times it's called \Processwire\wire('session')->warning($origPage->template . ', ' . $page->template); if ($origPage->name != $page->name || $origPage->template != $page->template) { \Processwire\wire('session')->warning('**' . $origPage->path. '** is frozen and cannot have its name or template changed', \Processwire\Notice::allowMarkdown); $page->name = $origPage->name; $page->template = $origPage->template; } // Handles drag-and-drop if ($origPage->path != $page->path) { throw new \Processwire\WireException($page->path . ' is frozen and cannot change its path'); } } } }); } It kind of works. It works when dragging and dropping in the page tree and it seems to prevent name changes. It only seems to be problematic when changing the template. If you remove all template references above (i.e. just check the name) it works fine. When you use the code as above and change the template it only seems to work if you change it to a template not matched by the original selector. It's almost like if you do anything with the template it gets called twice and on the second call the templates are the same. I know this because if you look at the debugging line it adds two warnings to the session if you change the template to something not in the original selector. Can any of you expert PWers spot an elementary mistake in my code? Is there something special I need to do if changing the template? I hope that makes sense — do ask if you need more context or information. I've been staring at it for ages and it's driving me mad! Thanks.
  12. Sometimes you can't see the forest for the trees, thanks! ? Thanks, also for module you linked to, that looks great. You seem to crop up the minute I ask a question here; I most owe you a fair few beers by now. ?
  13. If you have a Files field in the same template as a Textarea CKEditor field when you insert a link you can link to any of the files uploaded to the aforementioned files field. The same goes for Images fields and inserting images. Is there a way to have a central Files field as part of say a document-storage template and have all files uploaded here available to all Textarea CKEditor fields regardless of what template they are on? In searching for an answer I found Media Manager, which looks interesting. But I wondered if there was a way within the Processwire core to do this. I know I could make each file a page and that could be selected from CKEditor and then I do something in my code to read the file but was more interested in linking directly to the file in this instance. Thanks.
  14. Great, thanks.
  15. Thanks, that's great. I think LoginRegisterPro does what ProcessForgotPassword does (and more). The "Email New User" module looks very interesting, thanks — and just what I was after. If any users of LoginRegisterPro are able to let me know if LoginRegisterPro offers the "Email New User" I'd be interested in knowing but good to know this module is available if not.
  16. Hi, A couple of questions on LoginRegisterPro that I couldn't see in the main page. Can you disable confirmation email and instantly confirm registered users? If you create new users within the PW admin back end can LoginRegisterPro send a welcome email to the user with either a password (not secure I know but a useful option for what I need it for) or a link to set their own password? Or do you need to do this yourself via hooks? Can you send users a “forgotten password” email from the PW admin back end? If confirmation emails are mandatory that's no big deal but for what I want it for I really need to be able to do the user management from the CMS rather than through the register part of the module. Thanks.
  17. That's confirmed what I thought, thanks.
  18. I have an Images field that is set to required, maximum file = 1 and Single item (null if empty). When I get the page in the front-end template the field if of ProcessWire\Pageimage (singular) type as I would expect but in a Pages::saveReady hook it is ProcessWire\Pageimages (plural). Is this excepted behaviour — and if so, why? Is it to do with PW applying the “Single item (null if empty)” further down the chain? Thanks. EDIT: I think I have found the answer. It says this in the back-end: I am assuming then that because the Pages::saveReady hook is called in the back-end this isn't applied. Instead, it is added some time before the template is loaded. So, fields Files and Images will always be arrays in the back-end/unless accessed via the templates. Is that right?
  19. Fantastic, thank you. You have been a huge help!
  20. @Robin S Thanks again, this has been extremely helpful. I think if I use Pages::saved and ProcessPageSort::execute per your example it should cover everything I need (I am detecting non-unique values in pages/templates mimicking a database unique index). Pages::saved can be called in the site tree (AJAX) or via a standard POST when editing a page (changing status from the site tree calls this) — or even the API. As you have demonstrated if using AJAX then throwing an Exception can do the job. However, I would rather output a warning if a standard POST request and only use Vex/Exception if AJAX as the former is more flexible. If there any way in the Pages::saved hook to know whether it is an AJAX call or not? Is there a property in the API or do I just have to check the $_SERVER['REQUEST_PATH'] or something? Thank you!
  21. Wow, thanks @Robin S, I was not expecting such detailed examples! Thanks so much for the effort you have put in. I'll have a think about it this week as to the best way to do what I want but you have given me some excellent starting points. ?
  22. I need to check some data when pages are moved or saved. It runs fine when I save a page (Pages::saved) but nothing happens when I move a page (e.g. to and from trash). It only appears to be the lack of warning that is the issue. The hook definitely runs when I move pages in the site tree as I set up a simple bit of text file logging. wire()->addHookAfter('Pages::moved, Pages::saved', function($event) { $event->warning('Warning'); }); Is it because from the site tree moving pages is done with AJAX? Is there a way to output a warning when sorting or moving pages in the site tree or is it not possible? Thanks. P.S. I have noticed on Pages::saved if you change the status from the tree view (using Unpub, Hide buttons) the hook calls but the messages are not displayed. So really I guess my question is: how can hooks send messages, errors and warnings in the tree view?
  23. For anyone who is interested I did this with the normal Page Reference field the way @Robin S suggested. I created a parent filter template and a child filter template (no template file and just the title field for both). I added a parent filter page and some child filters to the site tree. In the Input tab of the Page Reference field I checked Allow new pages to be created from field? Then under Selectable pages I set Parent to the page that uses the parent filter template and Template to the child filter template. It now works exactly as I wanted it — so thanks! ?
  24. Thanks to you both. I will just go with pages then. I will certainly try ProcessPageFieldSelectCreator, that looks really interesting!
  25. Just as the title says really: is there a field that basically acts like a normal Select Options field but instead of having to go into Fields to add options you can do it while you're editing the page — and this then propagates to any other template that uses this field. I basically want to let the client add to Select Options but don't want to give them access to edit the field. I know I could do it by page reference/repeater but I wondered if there was a way that 1. avoids the overheard of pages and 2. where options could be added while you're editing the page rather than having to go elsewhere. I do have a license for ProFields but don't think there's anything there that does this. I hope that makes sense! Thanks.
×
×
  • Create New...