-
Posts
680 -
Joined
-
Last visited
-
Days Won
18
Everything posted by Jan Romero
-
I agree wit wbmnfktr, however, I also tend to agree with OP that the installer could be made more fool-proof. We’ve had a lot of threads over the years with people getting 500s trying to access the Admin (or anything other than the front page) right after installing PW. OP’s case may be especially niche, but .htaccess adjustments to get PW running aren’t really. It might be a good idea for the installer to ping the chosen admin URL and display a warning with some instructions if it doesn’t get a 200. Also, from a UI perspective, looking at this screenshot I just see a bunch of tick marks. We all know users don’t read anything you put in front of them, and this only encourages us to believe no further action is needed. IMHO, the “Complete & Secure Your Installation” section shouldn’t have check marks. Maybe it should even be a different color to emphasise its to-do-list nature and differentiate it from the mere success messages. “Get Started” is mostly to-do as well, albeit more optional.
-
Right, so… We have a couple of different problems here. The template label thing is just a simple programming mistake. I’m sure you can figure it out by looking at the last three lines again. Hints: 1. on $templates->get() 2. on API access in general (you can’t use $templates inside a function. This is an unfortunate ProcessWire quirk that makes things unnecessarily difficult for beginners. You just have to be aware of it OR simply use the functions API everywhere) Now the ProcessPageAdd::buildForm hook. Let’s look again at what we’re hooking and what we want to accomplish. Hooking after ProcessPageAdd::buildForm means that the hook code is being executed right after the Page Add Screen is generated. That is to say, the form that asks the user for the template they wish to instantiate under the given parent, if you will. This means that that code cannot know the user’s template selection. It can however know the parent page, i.e. the page of which the new page is going to be a child. So anything you do in the hook either happens in EVERY Page Add Screen, or you can make it depend on that parent page (which I called $parent in my example). It looks to me like you’re trying to skip the hook depending on the child template – this isn’t possible because that template has not been determined yet. If I understand the requirements right, you may want to do this: $this->addHookAfter("ProcessPageAdd::buildForm", function(HookEvent $event) { $form = $event->return; $parentField = $form->getChildByName('parent_id'); $parent = pages()->get($parentField->value); if ($parent->template->name === 'dashboard_customer') return; $nameField = $form->getChildByName('_pw_page_name'); $nameField->wrapClass = 'InputfieldHidden'; $nameField->value = 'Newpage'; }); Notice how I’m not using matching(). That function takes a selector string, so if you want to use it to test for a page’s template, you can probably do $page->matches('template=dashboard_customer'), but not $page->matches('dashboard_customer')! Now, if you must make the default page name depend on the selected template and also want to hide the name field from users, you have several options: Make the name field hidden the way we’ve been doing it and inject some Javascript to change its value whenever the selected template changes. Here you will still want to run the hooks only for some parent page where it makes sense. Adding the Javascript in ProcessPageAdd::buildForm could look like this: $form->appendMarkup .= '<script> const templateSelect = document.getElementById("template"); templateSelect.addEventListener("change", (e) => { const nameInput = document.getElementById("Inputfield__pw_page_name"); nameInput.value = e.target.options[e.target.selectedIndex].innerHTML; //set the name to the selected template’s label }); templateSelect.dispatchEvent(new Event("change")); //run the above function immediataly to capture the default value </script>'; Alternatively, hook ProcessPageAdd::processInput and determine the page name there. That’s the method that runs when you click „Save“ on the Page Add Screen, so at that point you’re committed to the selected template and parent. Still need to hook ProcessPageAdd::buildForm to make the name field disappear, though.
-
The error happens because $page is not defined, so you can’t call matches() on it. Fixing it is slightly involved, because we’re hooking ProcessPageAdd::buildForm, which means you can’t just do $event->object->page. $event is the HookEvent. It has information about the hook itself, the affected object and in this case the return value. $event->object is the object whose method we’re hooking, i.e. ProcessPageAdd. ProcessPageAdd should know the parent page to which we’re about to add a child, but I’m not sure how best to access it. $event->object->page is the page ProcessPageAdd belongs to. This will be a system page with the ID 6. It’s just “this is the Page Add Screen”, so that’s not what we need. However, the form that PW has built contains a hidden field for the parent page which we can access: $event->return->getChildByName('parent_id'). This should contain the parent’s ID, but not the page object itself. So this might work: $this->addHookAfter("ProcessPageAdd::buildForm", function(HookEvent $event) { $form = $event->return; $parentField = $form->getChildByName('parent_id'); $parent = pages()->get($parentField->value); if ($parent->matches('dashboard_servicepage_totalrenovation|dashboard_service_bathroomrenovation|dashboard_service_extension|dashboard_service_demolition|dashboard_service_recycling')) return; $nameField = $form->getChildByName('_pw_page_name'); $nameField->wrapClass = 'InputfieldHidden'; $nameField->value = 'Newpage'; });
-
If you want to have a bit of fun, I recommend the EXIF specs: 2.3 (2010) 2.3 (2012 revision, including fixes in the orientation part) 2.31 (2019) All of them say of orientation 8 “The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom,” so 90° CCW seems correct. Further down you can find such gems as “Orientation = 8 is equivalent to an arrangement that is turned Orientation = 3 90 degrees clockwise”. Skip to Orientation 3: “Orientation = 3 is equivalent to an arrangement that is turned Orientation = 6 90 degrees clockwise” … “Orientation = 6 is equivalent to an arrangement that is turned Orientation = 1 90 degrees clockwise”. Right. Thankfully, Orientation 1 is self-sufficient: “Orientation = 1 is created when 0th row of the coded image data stored in the Exif image file and the visual top of the display screen, and 0th column and visual left, will each be matched for display”. Hooray. I feel like I’m in an escape room.
-
Ah, sorry, I could have noticed that yesterday, since it’s in the same line ? You’re using $template but haven’t defined a variable with that name. $page->template->label should work. Btw, when you do this if ($page->matches('customer_project')) return; make sure to go to that page’s settings tab and check the “unique” box to prevent any other pages from getting the same name (theoretically possible in other parts of the page tree). Additionally you can probably make this faster by comparing $page->name directly. The matches() function first needs to parse the selector string and figure out what to do with it. Alternatively, test the entire path with $page->path or $page->url, that way you guarantee uniqueness.
-
Hi, I’m in bed but regarding maximum function nesting level, consider adding the noHooks option to your setAndSave call: $page->setAndSave('title', "($id) {$template->label} | $address", ['noHooks' => true]); I suspect you’re getting into an infinite loop because setAndSave triggers the hook on Pages::saved.
-
I’m still not 100% on our ultimate goal here. Do you really want to see the IDs in the tree? If it’s just about the way pages look in the tree, you can configure that in the template settings. In the Advanced tab there is a setting called “List of fields to display in the admin Page List” that you could configure to something like ”({id}) {template.label} {date}“ to get pretty much what you’ve described. Depends on the default date output format that is set up in the date field. Obviously you can also just hardcode the template’s label instead of {template.label} since this is template-specific anyay. In any case, this technique is completely independent of the page’s title. In fact, pages don’t need to have titles at all. You can uncheck “global” in the title field’s advanced settings and it will stop showing up in the Add screen. If these pages don’t need titles at all, because a combination of other values is sufficient, you can then remove the title field from the templates entirely. Or, if you want to keep it around because it can be a nice convention for all pages to have a title, you can make the field hidden for these templates, and generate pages’ titles on saving the page. The hook might look like this: $this->addHookBefore("Pages::save(template=renovation|delivery|recycling)", function(HookEvent $event) { $page = $event->arguments(0); $page->of(false); $page->set('title', $page->template->label . ', ' . date('Y-m-d', $p->date)); }); If these pages are for internal use and don’t need a name (the bit that identifies the page in the url), you will spare users the need to make something up on the Page Add screen. Unfortunately I believe this is only possible if the parent page accepts only a single template for children. In that case you could go to Family tab of the parent page’s template settings and enter a “Name format for children”, pretty much like the page list fields above. The Page Add screen is then bypassed entirely, because ProcessWire knows everything it needs to know (parent, template, name). I’m always in favour of skipping the Page Add business if possible, but in your case you need to choose a template anyway. To get rid of the name input, you might hook ProcessPageAdd::buildForm: $this->addHookAfter("ProcessPageAdd::buildForm", function(HookEvent $event) { //maybe check the parent page if you don’t want this to happen indiscriminately. //also, since you have multi-language inputs, you may have to adjust for that. //feels hackish, there may be a cleaner way to achieve this. $form = $event->return; $nameField = $form->getChildByName('_pw_page_name'); $nameField->wrapClass = 'InputfieldHidden'; //ProcessWire won’t let you proceed without a name, so generate something here, or //just set a static value. PW will automatically make it unique by adding a number. //Be aware that it will increment any number at the end of the name if it’s not unique, //so if you add two pages with the name “2020-12-31”, the second one will end up as //“2021-12-32”. Also, you’ll get a warning, so it’s probably best to generate a unique //name yourself. At this point you can’t use the ID because it doesn’t exist yet, though. $nameField->value = 'schnitzelmedpommes'; }); The ability to have default page names with multiple possible templates may actully be a worthwhile feature to add to the ProcessWire core. I’m not sure why it wouldn’t work, and philosophically it would fit PW’s ideal to be a “Content Management Framework”. Not every page needs a deliberate name if it just stores data.
-
That should be happening if you put it next to ProcessWire’s index.php unless the server refuses access or something. Look at the browser console. It probably shows an error trying to load https://www.example.com/favicon.ico. But you can also put it anywhere and add a link tag in your head, such as: <link rel="icon" type="image/x-icon" href="<?php echo $config->urls->templates?>style/favicon.ico"/>
-
Looks like you uncommented all RewriteBase lines. AFAIK you can only have one. Probably only the last one will have any effect, but you need the one in the middle. Try putting the #s back in the first and third RewriteBase. If it still doesn’t work, maybe paste your entire .htaccess here?
-
Sorry to unearth this thread again, but I must apologise. It was indeed a collation thing. I had only checked the relevant field tables, but after switching everything except field_password to mb4, the problem disappeared. The issue must have started when my hoster migrated from MySQL to MariaDB months ago, because I’m sure I originally installed PW with mb4, but apparently I didn’t notice until after switching to PHP 8 recently. Probably because earlier I didn’t run Tracy constantly. Not sure how exactly it was implicated, but it was entirely my fault anyway ? Just wish I had had some hints in the logs. Eventually I got an SQL collation exception when purposely searching for an emoji in the Admin, but never with the random logouts. Anyway, glad to have this thread!
-
Thanks for the quick response! Yeah, everything is set to utf8mb4, gotta have my emoji ??? I’m also using InnoDB, btw, but I feel like if it were a database issue I would either see corrupted data or an actual error in the logs. Anyway, I’m leaning towards this being some sort of exotic Tracy/PHP 8/mb4 bug. Guess I’ll try going back to PHP 7 to confirm that suspicion and see if I can narrow it down somehow. Unless in your case Tracy and PHP 8 weren’t involved? In the end it doesn’t trouble me too much, because non-superusers seem unaffected…
-
Hey @Zeka, did you ever solve this? I’m seeing the same problem now, after switching to PHP 8. The page is saved successfully, but PW logs me out in the next request (the redirect after POST). Only happens when emoji are present. Edit: Did you run Tracy Debugger by any chance? For me it seems to happen only when Tracy is enabled.
-
Hello @Ksenia! That’s a browser feature that’s supposed to prevent users from submitting the same form twice. I believe the quickest way to get rid of it would be to add a redirect to the same page after saving: $session->redirect('./', 303); That sends a 303 HTTP status code telling the browser to reload the page using a GET request, which MDN recommends for this purpose, as I understand it. Be aware that ProcessWire exits after a call to $session->redirect(), so no code below that line will get executed. Hope it works for you, I haven’t tested it though!
-
Another cool way to do this would be to let ProcessWire take care of the main selector string, because you don’t really want to write page status checks and all that stuff yourself. $query = $pages->getPageFinder()->find('template!=admin, has_parent!=2, include=all', [ 'returnQuery' => true ]); $statement = $query->leftjoin('field_title on field_title.pages_id = pages.id') ->where('char_length(field_title.data) > 10') ->prepare(); $database->execute($statement); $ids = $statement->fetchAll(\PDO::FETCH_COLUMN); $results = $pages->getByIDs($ids); For this purpose you could also do the join and the condition in one call using join().
-
Hi, welcome to the ProcessWire forums! The questions in your recent threads are kind of broad and it’s difficult to tell what exactly you’re struggling with and what you already know. I’m assuming you have ProcessWire installed and working, and you want to save some user inputs from the frontend into the fields you have created in PW’s admin area. Saving fields using the ProcessWire API First of all, you need a page to hold the data. Perhaps this page already exists, then you just get it using selectors, or maybe it’s the page you’re already on. Likely you want to save a new page for every time a user fills out the form, so let’s create a page: $p = new Page(); $p->template = 'form-submission'; //or whatever your template is called $p->parent = wire('pages')->get('/submissions/'); //or whatever the parent page is called Nice. Now, to get the information from the form you’re going to need ProcessWire’s input capabilities. You can find out all about it at the link and you’ll need to make some decisions about validation/sanitization depending on the data you’re dealing with, but the gist is to get stuff from the POST request sent by your user and put it into fields. Let’s say your user submits their e-mail address and you want to save it to a field called email: /* You see the name “email” three times: * 1. The first is the field you set up in the admin area. * 2. The second is the sanitizer that makes sure the thing the * user sent you actually looks like an email address. * 3. The third is the name of the input from from the form the * user filled out and submitted. */ $p->email = $input->post->email('email'); //Now you will probably want to fill out some more fields the same way: $p->comment = $input->post->textarea('comment'); $p->title = 'Form submission from ' . date('Y-m-d H:i'); Then you save the page and you’re done. You can go to the admin backend and check out the newly created page. $p->save(); However, we haven’t talked about the frontend bits yet. Creating a frontend form and sending submissions to ProcessWire Once again much depends on what you actually want to do, but for simplicity’s sake, let’s say this all happens on the same page. You have a ProcessWire template associated with a template file and all the above code is in it. Now we put the form into the same file. Basically the form is sent to the same page it’s coming from, but as you’ve seen above, you can still create the new page wherever you want. So here’s a simple HTML form that submits to the same page: <form method="POST" action="<?php echo $page->url; ?>"> <label for="email">Your e-mail address</label> <input name="email" type="email" /> <label for="comment">Your comment (no swearing!!!)</label> <textarea name="comment" rows="5"></textarea> <input type="submit" value="Send"/> </form> Note how the names of the input fields match the names we accessed earlier using $input->post(). Putting it together If you simply copy all my code blocks into your template file, you’ll probably notice that it tries to create a new page ever time the page is loaded. You’ll need to figure out if there is a form submission at all before you deal with it, and otherwise just skip that part and show the blank form. You may just check if there is anything in the comment variable. So here is your complete template file: <?php namespace ProcessWire; $thanks = ""; if ($input->post('comment')) { $p = new Page(); $p->template = 'form-submission'; //or whatever your template is called $p->parent = wire('pages')->get('/submissions/'); //or whatever the parent page is called $p->email = $input->post->email('email'); $p->comment = $input->post->textarea('comment'); $p->title = 'Form submission from ' . date('Y-m-d H:i'); $p->save(); $thanks = "Thanks a bunch, we value your feedback!"; } ?> <!doctype html> <html> <head> <title>Post a comment</title> </head> <body> <?php if ($thanks !== "") { echo "<h1>{$thanks}</h1>"; } ?> <form method="POST" action="<?php echo $page->url; ?>"> <label for="email">Your e-mail address</label> <input name="email" type="email" /> <label for="comment">Your comment (no swearing!!!)</label> <textarea name="comment" rows="5"></textarea> <input type="submit" value="Send"/> </form> </body> </html> Now I’m not saying you should put this exact code on your website, but it’s a demonstration of the most bare-bones things you’ll need to get input from users: A HTML form that generates a POST request and some PHP code to receive it. It doesn’t matter where these things are or what they’re called or how they’re generated. In this example we’ve written the form by hand because it’s easy, but you could just as well generate it from ProcessWire’s fields.
- 13 replies
-
- 10
-
Wherever you append ”id=“ to the selector string must be inside a loop. You only want to append it once after all the IDs have been collected. That is to say, I believe you probably have a foreach somewhere that either needs to be removed or closed earlier. It’s not in the lines you posted here, though.
-
Hi. Not sure how this relates to the original issue, but this selector string can never get any results, because it is asking for a page that has both no ID (”id=“) and ID 1705 at the same time!
-
Since the “I don’t get it” message is displayed, $final_output is the same whether the second-to-last line is commented out or not. So if nothing else happens to $selector_org that we’re not seeing, the results should be the same as well. Above, you called the results $matches. Can you put the following line immediately after the $pages->find() call and compare the output? var_dump($matches->getSelectors()); See the docs here: https://processwire.com/api/ref/page-array/get-selectors/ To help you debug you may also want to install the Tracy Debugger module. It can show you the selectors that were executed along with their SQL statements.
-
How get the name of a DOM element for use in hook
Jan Romero replied to abmcr's topic in General Support
Hi, I’m on my phone, but I think you might want to hook ProcessPageEdit::getSubmitActions and change the label in the returned array. It will look like this: $event->return['view']['label'] Apologies if this is not 100% correct – I’m looking through the code on a first gen iphone SE ? You should also be able to change the text using ProcessWire’s translation capabilities. -
I just ran into a PHP 8 issue with a line I contributed to this module. I opened a PR with @thetuningspoon’s original repo but I see the same error will arise in @MarkE’s fork, so @MarkE you may want add ”->name“ to the relevant line, too.
-
I would do the user agent thing. The age “verification” is pure CYA anyway, so the less it’s triggered, the less annoying it is for everyone involved. I doubt anyone will get in trouble over it being bypassable by spoofing the UA as opposed to disabling JS or just spoofing the cookie. edit: I just checked out Jack Daniels and Campari. Both use a JS modal. Jack Daniels’ can’t be completed with JS disabled, Campari’s doesn’t appear at all (however, without JS the site turns into an unusable mess anyway). Both sites load everything regardless of the modal so if you were 12 and too dumb to lie, you could still click view source and read all about the booze. I’ll pour one out for all the brain cells that died for nothing thinking about these things.
-
Hi, welcome to ProcessWire! A page reference field IS a many-to-many relationship. Say you want to model Students and Courses: each Student can enroll in many Courses, each Course can have many Students. You put a page reference field in your Student template and make multiple Courses selectable. Now you can get all Courses a Student is enrolled in: $studentPage->courses And all Students enrolled in a Course: $pages->find("template=student, courses=$coursePage") A feature that is often requested is the ability to edit or at least view this relationship from both sides. In my example, the Admin area will only show Courses while editing a Student, but when editing a Course you can’t see its Students . There are Modules that fix this, such as this one by @Robin S: https://processwire.com/modules/connect-page-fields/. It will synchronise two page reference fields so the relationship will actually be stored from both sides.
-
Usually when you use a page reference or URL field, you do so to model a specific relationship. That way it is often sufficient to have a fixed link text describing that relationship, or to generate it based on the target page. For example, links might always just say “more info” or “related: <?=$page_reference->title?>”, or the domain name extracted from a URL field. If you want arbitrary link text that is editable by users, your solution seems perfectly fine.
-
Try adding status>=hidden to your selector. I haven’t tested this with many pages, nor with an additional field selector, but it gives a much better index utilization than the query without. 17 examined rows vs. 2000 (this is on MariaDB 10.5.11). Clearly only values over 1024 (hidden) can contain the 1024-bit, so the result set should be the same.
-
So are you looking for hidden pages or unpublished pages? Because for pages that have never been published, this may be faster: $pages->find('published=, include=all'); The selector status=unpublished will turn into SQL as where pages.status & 2048. Now I don’t know what optimizations MySQL can do there, but it I suppose it will still have to look at a bunch of values and see if they match, whereas published=, include=all turns into pages.published IS NULL, so it should be a matter of returning a continuous range of rows from that index. Even better if you only want the ID, then it should never even touch the actual table.