-
Posts
131 -
Joined
-
Last visited
-
Days Won
1
Everything posted by AAD Web Team
-
If I have a Datetime field on my page and the field is not empty then the getUnformatted method returns the integer for the Unix timestamp (as expected), but if my field is empty then getUnformatted returns an empty string. I would have expected it to return the integer 0 as that's the raw value for an uninitialised timestamp. I think this is unintuitive, and inconsistent with the way the system datetime fields work. With two non-empty dates (using the current timestamp as an example), you get compatible results: $page->getUnformatted('my_date') === 1750221096; $page->getUnformatted('published') === 1750221096; $page->getUnformatted('my_date|published') === 1750221096; With two empty dates (e.g. if the page hadn't been published yet) you get incompatible results: $page->getUnformatted('my_date') === ''; $page->getUnformatted('published') === 0; $page->getUnformatted('my_date|published') === null; I think the getUnformatted method should return an integer 0 for an uninitialised date. Does that sound reasonable, or is there a particular need for it returning a string instead? I can see that returning a string could be handy if you want to distinguish between an empty date and a date that's actually set to 1 January 1970 at 00:00. If that's the logic to it then I'd submit a pull request for the documentation to be changed so that it's clear what getUnformatted returns with Datetime fields.
- 1 reply
-
- 1
-
-
Thanks for the suggestion @Robin S, hooking ProcessLogin::loginFailed() unfortunately does not allow us to amend the username before the login is attempted. If an email address is detected, we want to look up that email address in Active Directory and swap the login request to using the user's username (samaccountname). The following hooks do NOT trigger when an email address is used as the username, regardless of email logins being enabled/disabled: ProcessLogin::loginAttempted ProcessLogin::loginAttemptReady ProcessLogin::login ProcessLogin::loginFormProcessReady ProcessLogin::loginFormProcessed All the above hooks trigger for a username, and allow the username to be caught and changed before the login is attempted. A hook to ProcessLogin::execute will trigger with an email address login, but we haven't been able to figure out how to catch the username/email argument and change it before the login is attempted.
-
We're in the process of our organisation switching the naming convention of usernames to be email addresses. As part of this change we are looking to support users logging in with their old username (of which the format will still be supported for a while) or their email address. We have a custom module which hooks a login attempt. If the user is not found in Processwire, then our Active Directory is queried and if the user is found there (and the password matches) then the user is created within Processwire. This functionality needs to be retained for email logins. In our testing, we have set the ProcessLogin module to allow emails or username logins. If we try to login using an email that is not currently associated with a user, then we get an error message but the login hook does not trigger, and the session log does not get updated with any entries of a failed login attempt. If we try to login using the email address of an existing user, but using an incorrect password, then this does trigger the hook and logs a failed login attempt (using the found username) to the session log. Is there a reason the behaviour is not consistent? It would be better security to log a failed email login attempt, even if a user with that email address does not exist. In our case, we would like a failed email login to still trigger the hook in our module that should then find that user by their email address in Active Directory. Can we achieve this?
-
Constructors with custom page classes?
AAD Web Team replied to AAD Web Team's topic in API & Templates
Great, thanks for that! I’ve implemented a constructor and it seems fine. I initially made a mistake by including a reference to a page that has no view of its own, but contains some general website settings, like this: public function __construct(Template $tpl = null) { parent::__construct($tpl); $this->siteSettings = pages('/site-settings/'); } This caused an infinite loop because the site-settings page itself uses the DefaultPage class. To get around this I created an empty class for the site-settings template, like this: class SiteSettingsPage extends Page { } I’m not sure if this was the smartest solution, or if the idea of having so much code in DefaultPage is a good idea. I wonder if there are a heap of system pages and other things that I’m not thinking of, which are inheriting from DefaultPage? -
If we’re using custom page classes, is it acceptable to have a constructor, or does that cause problems by overriding the constructor that should be running in the parent Page class? Should we call the parent class constructor? For example in /site/classes/DefaultPage.php, would this be correct? class DefaultPage extends Page { function __construct() { parent::__construct(); // … set some class properties for later use … } }
-
Error handling when using WireDatabasePDO?
AAD Web Team replied to AAD Web Team's topic in API & Templates
Great, thanks! I put the try/catch around the $db->query() method rather new WireDatabasePDO constructor and it worked well. -
Is it possible to connect to a non-ProcessWire database using WireDatabasePDO, and have error handling, so if the database is unavailable then the page can recover gracefully? I tried: try { $db = new WireDatabasePDO([ /* dsn, user, pass */ ]); } catch (\Exception $e) { /* Code to handle the problem gracefully */ } This doesn’t work – it seems that the exception is dealt with by ProcessWire before my code sees it. Is it better just to use PHP’s native PDO object directly? Something such as: try { $dbh = new \PDO($dsn, $user, $password); } catch (\PDOException $e) { /* Code to handle the problem */ }
-
I was a bit worried about the 191 number, given ProcessWire uses indexes up to 250 characters. However, the maximum length of a utf8mb4 index would be 191 only if the InnoDB table uses compact or redundant row format. If your InnoDB table uses compressed or dynamic row format then you can have up to 768 characters (3,072 bytes) in a utf8mb4 index. I’d never paid attention to row format before, but I checked all our databases, which are up to 5 years old, and they all use dynamic row format. This SQL command gives the row format information, among other things, for a database: SHOW TABLE STATUS IN my_database;
-
Hello all. We've notived a potential bug with redirects that go to a restricted page (a page that guests cannot view, and the user is currently not logged in). We have a page that contains a form that people can use to post news items to a section of our site (like a noticeboard). This page template requires to used to be logged in so we can atrribute the post to the user. We've noticed that if the link to this page is a redirect (due to the page moving and the PagePathHistory module being installed), then rather than direct the user to login, it presents a 404 page instead. Normal behaviour is clicking a link to the this page will send to user to login, once a successful login is made it continue them onto the restricted page they were trying to access. Is this a potential bug or could we be doing something different to prevent this?
-
From time-to-time we end up with an infinite loop in the PagePathHistory module. The effect of this is that someone clicking on "What other URLs redirect to this page?" in the admin interface justs get the spinning icon. In the error log we get an entry about the allowed memory size being exhausted in PagePathHistory.module. Inside the module, the getVirtualHistory method is being endlessly called, alternating between two different page ids. This seems to happen when we have two pages like this: Page 1 URL: /one/two/three/ Old URL: /one/four/ Page 2 URL: /one/two/ Old URL: /one/four/five/ The only way to fix this seems to be to log into MySQL and delete rows from the page_path_history table. Ideally we'd like ProcessWire to either stop users adding new redirect URLs that cause this problem, or to somehow handle and escape from the infinite loop that results. Is there a modification to the code that might achieve this?
-
I'm looking to add an icon next to the page title in the page tree, similar to how pages in draft state (with ProDrafts) can be identified by the little paperclip icon. What is the best way to go about this? I tried diving through the source code of a few modules and I suspect I need to hook into 'ProcessPageListActions::getExtraActions'?
- 1 reply
-
- 1
-
-
this request was aborted because it appears to be forged
AAD Web Team replied to joshuag's topic in General Support
We often get this error ("This request was aborted because it appears to be forged."), but for us it's due to people pressing the "Login" button more than once (or pressing the enter key followed by the Login button). Someone in our group of web authors does this about once per day on average. For us it'd be a useful system change if the Login button was immediately disabled via JavaScript after clicking (or pressing enter) so that it couldn't be activated twice. (I wonder if there would be any downside to this change?) -
Current major server outage - advice please!
AAD Web Team replied to AAD Web Team's topic in General Support
Thanks @Craigand @flydevwe have now resolved the issue. Clearing the cache was good for temporarily unlocking space in blocks to get through. Turns out the issue was a recent code change where the Pageimages class was used for handling a group of Pageimage's from across many pages (where we'd previously use an array) - we had not realised at the time that this leads to each image getting instantiated again (and therefore duplicated)! -
Hello, Brodie from the Australian Antarctic Division here. We've got some kind of suspected cache issue casuing runaway disk usage, it's currently so bad that our websites are down (unless logged in) and I cannot SSH into the servers. A restart of both the database and hosting server have not helped either. Pages load fine when logged in (since the cache is skipped) or when disabling the cache. Below are some of various error messages we've started seeing, I suspect due to running out of disk space: Unable to write lock file: /site/assets/cache/LazyCronLock.cache Error: Exception: Unable to copy: /site/assets/files/26153/keon-anzac.jpg => /site/assets/files/26151/keon-anzac-7.jpg (in wire/core/Pagefile.php line 236) unlink: Unable to unlink file: /site/assets/cache/Page/45511/4df366e0700b7c24883b744b6cb250ee+https.cache Has anyone had a similar issue before and knows something we could try to resolve it?
-
In our page template cache settings we have, "Clear cache for saved page and parents", which works as expected when using ProcessWire (3.0.200) interactively. When we manipulate pages with the same template from the API though we get this: $page->save(); // Cache clear happens as expected $page->save('my_field'); // Cache clear is not triggered – it's as if the page has not been saved (though the field has been saved correctly). $page->setAndSave('my_field', $value); // Cache clear is not triggered Is this the expected behaviour? If so, could the $page->save() documentation be updated to mention this difference?
- 1 reply
-
- 2
-
-
Hi everyone, we've experienced an issue where FormBuilder is rejecting the client side validation of image uploads for .jpeg images, even though they are allowed as a file extension. We did also submit a Github issue: https://github.com/processwire/processwire-issues/issues/1656 Expected behavior Both forms have the same form and file fields settings. Therefore both should accept a .jpeg image. Actual behavior One form accepts a .jpeg image happily, the other pops up with an error message saying the image does not match an expected file type. This is using the same image for both forms. Optional: Screenshots/Links that demonstrate the issue Image I'm using to test: Steps to reproduce the issue I'll include both forms export codes: Working form import code; { "required": false, "columnWidth": 0, "roles": { "form-submit": [ "guest" ], "form-list": [], "form-edit": [], "form-delete": [], "entries-list": [], "entries-edit": [], "entries-delete": [], "entries-page": [], "entries-resend": [] }, "flags": 0, "pluginActions": [], "framework": "Bootstrap5", "submitText": "Submit news item", "successMessage": "Thanks for your submission. Refresh the page to see your item at the top of the list.", "errorMessage": "One or more errors prevented submission of the form. Please correct and try again.", "frBasic_cssURL": "/site/templates/css/form-builder.css", "emailSubject": "Staff news submission [SEC=OFFICIAL]", "responderSubject": "Staff news submission [SEC=OFFICIAL]", "saveFlags": 75, "savePageTemplate": 64, "savePageParent": 8349, "savePageStatus": 1, "savePageFields": { "name": "headline", "1": "headline", "150": "body", "102": "link_url", "44": "image" }, "listFields": [ "headline", "email" ], "entryDays": 366, "emailTo": "webteam@aad.gov.au", "emailFrom": "email", "responderFromName": "AAD Web Team", "responderReplyTo": "webteam@aad.gov.au", "responderTo": "email", "emailFrom2": "webteam@aad.gov.au", "mobilePx": "55em", "partialEntryDays": 14, "spamEntryDays": 7, "frBasic_itemContent": [ "description", "out", "error", "notes" ], "frBootstrap5_noLoad": [ "framework", "jquery" ], "frBootstrap5_bootURL": "/site/modules/FormBuilder/frameworks/bootstrap5/", "allowPreset": 0, "skipSessionKey": 0, "useCookies": 0, "frBasic_noLoad": [], "spamFlags": 0, "spamWords": [], "children": { "headline": { "type": "Text", "label": "Headline", "required": 1, "columnWidth": 0, "requiredAttr": 1, "maxlength": 300 }, "body": { "type": "Textarea", "label": "Body", "notes": "There’s a limit of about 650 words (4000 characters) in this field, but 150 words or less would be ideal.", "required": 1, "columnWidth": 0, "rows": 5, "maxlength": 4000, "requiredAttr": 1, "collapsed": "0", "minlength": 0, "showCount": "2" }, "email": { "type": "Email", "label": "Email address", "description": "Please provide your email address. It will *not* be displayed with the news item, but will enable the web team to get in touch if there's an issue with your submission.", "required": 1, "columnWidth": 0, "rows": 5, "requiredAttr": 1, "maxlength": 512 }, "link_url": { "type": "URL", "label": "Optional hyperlink to more information", "required": false, "columnWidth": 100, "maxlength": 1024, "requiredIf": "link_text!=''" }, "image": { "type": "FormBuilderFile", "label": "Optional image", "notes": "Allowed file types: jpg, jpeg, png\nMaximum file size: 5mb", "required": false, "columnWidth": 0, "extensions": "jpg jpeg png", "maxFiles": 1, "maxFileSize": 5242880 } } } Not working form import code: { "required": false, "columnWidth": 0, "roles": { "form-submit": [ "guest" ], "form-list": [], "form-edit": [], "form-delete": [], "entries-list": [], "entries-edit": [], "entries-delete": [], "entries-page": [], "entries-resend": [] }, "flags": 0, "pluginActions": [], "framework": "Bootstrap5", "submitText": "Submit advertisement", "successMessage": "Thanks for your submission. Refresh the page to see your item at the top of the list.", "errorMessage": "One or more errors prevented submission of the form. Please correct and try again.", "frBasic_cssURL": "/site/templates/css/form-builder.css", "emailSubject": "Staff classifieds submission [SEC=OFFICIAL]", "saveFlags": 11, "savePageTemplate": 67, "savePageParent": 8377, "savePageStatus": 1024, "savePageFields": { "name": "item_name", "137": "category", "1": "item_name", "150": "body", "102": "link_url", "44": "image", "122": "document", "141": "contact_person", "92": "email", "138": "phone_x", "139": "phone_m", "140": "phone_o" }, "listFields": [ "category", "item_name", "contact_person" ], "entryDays": 60, "emailTo": "webteam@aad.gov.au", "emailFrom2": "webteam@aad.gov.au", "responderSubject": "Auto-Response", "mobilePx": "55em", "allowPreset": 0, "skipSessionKey": 0, "useCookies": 0, "partialEntryDays": 14, "spamEntryDays": 7, "frBasic_noLoad": [], "frBasic_itemContent": [ "description", "out", "error", "notes" ], "spamFlags": 0, "spamWords": [], "frBootstrap5_noLoad": [ "framework", "jquery" ], "frBootstrap5_bootURL": "/site/modules/FormBuilder/frameworks/bootstrap5/", "children": { "item_details": { "type": "Fieldset", "label": "Item details", "required": false, "columnWidth": 100, "children": { "category": { "type": "Select", "label": "Choose a category", "required": 1, "columnWidth": 30, "defaultValue": "For sale", "options": "=\nFor sale\nFor rent\nGiveaway\nWanted\nMiscellaneous" }, "item_name": { "type": "Text", "label": "Item name", "required": 1, "columnWidth": 70, "requiredAttr": 1, "maxlength": 300, "stripTags": 1 }, "body": { "type": "Textarea", "label": "Description", "notes": "**Do not include website addresses in the description.** They will not be clickable links and may break the visual layout of the advertisement. You can either use the website field provided, or attach a document if you need to list multiple website addresses.", "required": false, "columnWidth": 0, "rows": 5, "maxlength": 1200, "requiredAttr": 1, "stripTags": 1 }, "link_url": { "type": "URL", "label": "Website (optional)", "required": false, "columnWidth": 100, "maxlength": 1024, "requiredIf": "link_text!=''" }, "attachments": { "type": "Fieldset", "label": "Attachments (optional)", "required": false, "columnWidth": 0, "children": { "image": { "type": "FormBuilderFile", "label": "Image", "notes": "Allowed file types: jpg, jpeg, png\nMaximum file size: 5MB", "required": false, "columnWidth": 50, "extensions": "jpg jpeg png", "maxFiles": 1, "maxFileSize": 5242880, "collapsed": "0", "descRows": 0, "descLength": 2048, "hideInputs": 0, "usePreview": 0, "useHeader": 0 }, "document": { "type": "FormBuilderFile", "label": "Document", "notes": "Allowed file types: pdf, docx, doc, rtf\nMaximum file size: 5MB", "required": false, "columnWidth": 50, "extensions": "pdf docx doc rtf", "maxFiles": 1, "maxFileSize": 5242880 } } } } }, "contact_details": { "type": "Fieldset", "label": "Contact details", "description": "Your name is required. Please provide at least one method of contact.", "required": false, "columnWidth": 100, "children": { "contact_person": { "type": "Text", "label": "Contact person", "notes": "***Must* be a member of staff.**", "required": 1, "columnWidth": 0, "requiredAttr": 1, "maxlength": 2048, "stripTags": 1 }, "email": { "type": "Email", "label": "Email address", "required": false, "columnWidth": 0, "rows": 5, "requiredAttr": 1, "maxlength": 512 }, "phone_numbers": { "type": "Fieldset", "label": "Phone numbers", "notes": "**Numbers only – no spaces or punctuation please**", "required": false, "columnWidth": 0, "children": { "phone_x": { "type": "Text", "label": "Extension", "required": false, "columnWidth": 0, "maxlength": 4, "stripTags": 1 }, "phone_m": { "type": "Text", "label": "Mobile", "required": false, "columnWidth": 0, "maxlength": 10, "stripTags": 1 }, "phone_o": { "type": "Text", "label": "Other", "required": false, "columnWidth": 0, "maxlength": 10, "stripTags": 1 } } } } }, "classifieds_policy": { "type": "Fieldset", "label": "Classifieds policy", "required": false, "columnWidth": 0, "children": { "agreement": { "type": "Checkbox", "label": "Advertisements that do not adhere to this policy will be removed.", "description": "", "required": 1, "columnWidth": 0, "checkedValue": "1", "checkboxLabel": "I have read and agree to these terms" } } } } } Setup/Environment ProcessWire version: 3.0.200 PHP version: 7.4.3 FormBuilder: 0.5.3
-
Thanks @bernhard and @Jan Romero. I didn't think of how status works as a bit field, and I've never used the bitwise 'or' operator in a selector. The code snippet with Page::statusHidden is very useful, thank you. The part of the documentation I found confusing was in the selectors documentation where it says, "Pages with hidden or unpublished status will not appear in the results from database-querying selectors that can return multiple pages (i.e. functions that return the PageArray type). For instance $pages->find(), $page->children(), etc." However, I understand it now. Thanks @Robin S for explaining the underlying logic to why $page->children() and $page->parents() work differently with hidden pages. I can see from your linked post how this has been discussed previously. Unfortunately, in my forum searching before asking this question I didn't come across your post – so thanks for pointing me to it.
-
We're trying to get all the parent pages of a page in order to display a breadcrumbs trail on the web page. We're using: $page->parents('template!=home') … to get all the non-home parents ready to display. We didn't want to include hidden pages in the parents list, and based on the selector documentation I thought they would be excluded, but the hidden pages are returned in the list. Is this how it's supposed to be? In order to not retrieve the hidden pages we also tried: $page->parents('template!=home,status!=hidden') … but this still returned the hidden parent pages. Can anyone help with this? Is the documentation wrong, or am I misunderstanding it? We're using ProcessWire 3.0.184.
-
Hello, we've had some selectors no longer work after updating from ProcessWire 3.0.165 to 3.0.184. This particular selector will find the 3 most recent news items created within the last 6 months to display as a summary on the home page. This selector worked on 3.0.165: template=staff-news-item, created>=6 months ago, created<=now, sort=-created, limit=3, include=hidden But on 3.0.184 it returns 0 results. It doesn’t crash or log any warnings. It just doesn’t return anything. After changing to this, it now works again: template=staff-news-item, page.created>=-6 months, page.created<=now, sort=-created, limit=3, include=hidden This also works, with inconsistent use of page.created: template=staff-news-item, created>=-6 months, page.created<=now, sort=-created, limit=3, include=hidden Is anyone aware of selector changes in recent updates that would cause the selector to break?
-
Implementing search statistics in PW
AAD Web Team replied to heldercervantes's topic in General Support
You could also use Matomo analytics (open source), which keeps track of site search keywords and searches with no results, as well as the most common pages visited as a result of a site search. I reckon it’s ok to log searches to a file in ProcessWire as well. I was previously logging searches with no results to the messages log with: wire('log')->message("No results for $searchTerms"); Or to log all results to a separate search log with: wire('log')->save('search', $searchTerms); Because the log files are saved in a consistent space-separated format they'd be possible to parse with a script (maybe reporting the results through a ProcessWire page), or import into a spreadsheet program. We use the paid Form Builder module to allow users to answer a 'was this page helpful' question at the bottom of any page, including search. Another way of recording the success of the search could be to put the results through an intermediate PHP script that logs the search position of the chosen search result, before redirecting users on to the actual result. So you could see if the first match is mostly chosen, or something further down the list.