Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Inxentas

  1. You installed it manually... so I would check first if you can also uninstall it manually before you move or delete the files. If you instructed PW to use the /wire version beforehand, that should be safe.
  2. Leaving the field empty IS the choice here. It's just that the choice is binary, but you're also opting to use 3 templates, making me think you just need a "pick between multiple" mechanic. Why not add a template that represents just that bit of text (let's call it a Sentence page for now), make three Sentence pages with it (each with a different bit of text), and then in your original template make a Page Reference field that filters out only Sentence pages: now you can pick which sentence you want to use every time you make a new page with your original template. If you ever need a 4rth Sentence you just make a new Sentence page.
  3. I have used ProcessWireUpgrade for years over a decade without any issues. I do have to admit that I can work at least annually on most of the sites I run. Only once did I have any problems with it, when UIKit became part of the core and I had to update a website that I built in 2012. That's over a decade ago! That was the only instance where I had to upgrade PW manually, for which I followed this process which wasn't all too difficult. Basically came down to renaming some files so I could revert back in case I'd screw up. Aside from that it basically came down to uploading a new wire folder and refreshing the backend. Quite simple.
  4. I too like to organize everything as if templates are classes and pages are instances, and do things as OOP / DRY as possible. The ArtistProfile (template) could have page reference field in which one Artist (page instance) can be selected. The profile then encapsulates the artist. In order to make grabbing the image easier later down the line, I like to use Page Classes (these are "on" by default these days, I believe). I would add methods to the PageArtistProfile (page class) that returns field values from the Artist it encapsulates. In the ArtistProfile template, I could then do stuff like: $page->getArtistPortrait(); You can off course add arguments to these methods should you so desire. Same with the partners. I would just make a big "box" of Partner pages. I would probably make a Partners (plural) page under which to collect them all, so I have "one source of truth". In order to subsequently use them on other pages, I would just use a Repeater field with a Page Reference field that filters out only Partners. That way an admin can put them in any order they like as Partners are now also encapsulated in the ArtistProfile, just in a plural form. In my opinion Media Managers tend to be a bit archaic, I prefer to treat media as if they were member values of class instances.
  5. I;m sorry, this has been so long I don't remember which project this was exactly. Probably not the same hosting provider, as all the hosts our clients use are local Dutch companies. Most of them are fine but some make changes to their server structure and inform only the client... who them often neglects to inform us. Unfortunately we're not a full service internet bureau so I often find myself dealing with (surmountable) server differences. In that regard PW has been the right choice for me.
  6. Can I ask why that is not an option? Perhaps that issue is solved more easily.
  7. While this is a very unrefined script and I could always offload that line count to a method, I still think I'm abusing page class constructors a bit too much. Essentially treating them as MVC controllers. So when I got back this morning and had my What The Hell Was I Thinking experience I fully agree with that. However, I would like to handle incoming $input on a per-template basis so I'll move this code into some file and prepend it to the template. What I learned from this is that it's a hassle to get scope on API variables from these constructors. The intent here is to treat the actual template file as a "do whatever you want with it" file after install, so that's why I wanted to move parsing forms on the frontend anywhere but in the actual template file.
  8. I would recommend you keep your iframe seperate from your other content. Make two fields. Check the Settings for each. For the iframe, use a Textarea without any Text Formatters. You can echo this on the frontend and get the exact HTML code you put in that field. For content with HTML markup, use a Textarea with the HTML Entity Encoder Text Formatter. I use this approach all the time for any kind of embed codes. You can treat iFrames in a similar manner.
  9. I've taken a look at the actual line in PagesEditor.php that throws this error. It's in the isDeleteable() method, the first line of this snippet: } else if($page->id === $this->wire()->page->id && $this->wire()->config->installedAfter('2019-04-04')) { $error = "it is the current page being viewed, try \$pages->trash() instead"; }
  10. I had a very similar issue not too long ago, which pointed to some issue with the AdminUIKit module while upgrading an old site. You can find the topic here. Perhaps going through these steps will help figuring out what tweaks you might have forgotten. This assumes you've got the ProcessWireUpgrade module installed.
  11. I'm having a little trouble deleting pages from a page class context. My approach might be screwy as well, but I'm open for critique. I am very much aware I'm doing no validaton yet. My page class PageAccountPage has an extended constructor that runs through a switch in order to centrally parse forms from it's template. Afterwards it sets a couple of variables, such as $this->addresses (which will store all addresses associated with the users account). This all works pretty well and ensures all form data is parsed before the page is rendered. However, I run into trouble when I delete a page in the switch under the deleteAddress case. I get the following error: Notice: Trying to get property 'id' of non-object in C:\wamp\www\mydomain.com\public_html\wire\core\PagesEditor.php on line 317 This implies the PagesEditor class is trying to grab a page by id that no longer existst due to it's deletion in the switch case. I assign $this->addresses after the switch, at the very end when everything should be parsed. I've also removed it to check whether it was causing the issue, but a little var_dump-ing points out that the error is thrown after the $a->delete line within the switch. A small note: the template has a prepend file set under Files named _webshop.php. Here is the full code: <?php namespace ProcessWire; /** * This is the class tied to the page-account template. */ class PageAccountPage extends WebshopPage { public $addresses; public function __construct(Template $tpl = null) { if($this->input->post->action) { $id = (int)$this->input->post->id; $title = (string)$this->sanitizer->text($this->input->post->title); $street = (string)$this->sanitizer->text($this->input->post->street); $house_nr = (string)$this->sanitizer->text($this->input->post->house_nr); $house_nr_suffix = (string)$this->sanitizer->text($this->input->post->house_nr_suffix); $zipcode = (string)$this->sanitizer->text($this->input->post->zipcode); $city = (string)$this->sanitizer->text($this->input->post->city); $phone = (string)$this->sanitizer->text($this->input->post->phone); $email = (string)$this->sanitizer->email($this->input->post->email); // switch actions switch($this->input->post->action) { case 'updatePassword': $password = (string)$page->input->post->password; $this->user->pass = $password; $this->user->save(); break; case 'loginAccount': $email = (string)$page->input->post->email; $password = (string)$page->input->post->password; $existingUser = $page->users->get("email=$email"); if($existingUser){ $page->session->login($existingUser->name, $password); } break; case 'createAccount': $name = (string)$page->input->post->email . '-' . time(); $email = (string)$page->input->post->email; $password = (string)$page->input->post->password; $existingUser = $page->users->find("name=$name"); if(!$existingUser){ $new = new User(); $new->of(false); $new->name = $name; $new->email = $email; $new->pass = $password; $new->save(); // TODO: Implement email validation and login through link? $page->session->login($new->name, $password); } else { // User exists, cannot remake the account. // TODO: consider a safe implementation of this case. } break; case 'updateAddress': // find page $a = $this->pages->get($id); $a->of(false); $a->title = $title; // populate fields $a->webshop_address_street = $street; $a->webshop_address_house_nr = $house_nr; $a->webshop_address_house_nr_suffix = $house_nr_suffix; $a->webshop_address_zipcode = $zipcode; $a->webshop_address_city = $city; $a->webshop_address_phone = $phone; $a->webshop_address_email = $email; // save page $a->save(); break; case 'deleteAddress': $a = $this->pages->get($id); $a->of(false); $a->delete(); break; case 'addAddress': // create page $new = new Page(); $new->of(false); $new->name = $title; $new->parent = $this->pages->find("template=page-addresses"); $new->template = $this->templates->get('page-address'); $new->name = $title; $new->title = $title; $new->save(); // populate fields $new->webshop_address_street = $street; $new->webshop_address_house_nr = $house_nr; $new->webshop_address_house_nr_suffix = $house_nr_suffix; $new->webshop_address_zipcode = $zipcode; $new->webshop_address_city = $city; $new->webshop_address_phone = $phone; $new->webshop_address_email = $email; // save page $new->save(); break; } } $parent = $this->pages->get("template=page-addresses"); $selector = "template=page-address,created_users_id=" . $this->user->id . ",parent=" . $parent->id; $this->addresses = $this->pages->find($selector); parent::__construct($tpl); } }
  12. @OllieM Not long at all. Following the steps above (which is basically just doing the smart thing and backup up old files) it took me a couple of minutes at most.
  13. Ah, got the bugger! I was setting the Page template using a string rather then grabbing a full instance of the template. I think this used to work in a previous version or something, but this is way cleaner anyway: $new->template = $this->templates->get('page-address');
  14. $_POST is an associative array of variables passed to the current script via the HTTP POST method when using application/x-www-form-urlencoded or multipart/form-data as the HTTP Content-Type in the request. Not when using application/json. For json, you'd typically do something like this: $data = json_decode(file_get_contents('php://input'), true); AJAX is Javascript, and Javascript libraries often use the JSON method. I'm not sure if jQuery does, but I had the same issue when working with AngularJS in the past.
  15. I'm writing a class file for an Account page that parses a form whenever a certain 'action' is set in $input->post. The template contains forms to save an existing address, and one to add a new address. Saving the address works great, but my code bugs out the moment I attempt to create a new one. Here is the code I am using in the class file: <?php namespace ProcessWire; /** * This is the class tied to the page_account template. */ class PageAccountPage extends WebshopPage { public $addresses; public function __construct($page) { if($page->input->post->action) { $id = (int)$page->input->post->id; $title = (string)$page->input->post->title; $street = (string)$page->input->post->street; $zipcode = (string)$page->input->post->zipcode; $city = (string)$page->input->post->city; $phone = (string)$page->input->post->phone; $email = (string)$page->input->post->email; switch($page->input->post->action) { case 'save': // find page $a = $this->pages->get($id); $a->of(false); $a->title = $title; // populate fields $a->webshop_address_street = $street; $a->webshop_address_zipcode = $zipcode; $a->webshop_address_city = $city; $a->webshop_address_phone = $phone; $a->webshop_address_email = $email; // save page $a->save(); break; case 'add': // create page $new = new Page(); $new->name = $title; $new->parent = $this->user; $new->template = 'page-address'; $new->save(); // populate fields $new->title = $title; $new->webshop_address_street = $street; $new->webshop_address_zipcode = $zipcode; $new->webshop_address_city = $city; $new->webshop_address_phone = $phone; $new->webshop_address_email = $email; // save page $new->save(); break; } } $this->addresses = $this->user->children("template=page-address"); parent::__construct($page); } } The problem sits in the add case of the switch. I get the following error: Trying to get property 'fieldgroup' of non-object in C:\wamp\www\fransvanberendonk.com\vendor\processwire\processwire\wire\core\PagesEditor.php on line 370 To be clear, the template 'page-address' is one that gets created on module install through code. That code also modifies the User template to allow children in the form of 'page-address' pages. 'page-address' can have a 'user' as a parent. I'm not sure if it's relevant (since it all seems fine from the backend), but here is the code that does that: // set relationships for page_address $page_address->parentTemplates([$page_order, $user]); $page_address->noChildren = 1; $page_address->save(); // set relationships for user $user->noChildren = 0; $children = $user->childTemplates; $children[] = $page_address; $user->childTemplates($children); $user->save(); I also get a message in the CMS after the page bugs out: PagesLoader: You must assign a template to the page before setting field values (title__data) [pageClass=ProcessWire\Page, template=] Which a weird message because the pageClass of the page I'm trying to save is PageAccountPage, which inherits from WebshopPage, which interits from Page. I tried not setting the title before the first save() and filling the name value instead, but to no avail. I'm not sure what goes wrong here. DISCLAIMER: I am aware I should sanitize data first. I wanted to get the thing working first, this code is WIP off course.
  16. Thank you Ryan! I have been dilligently but blindly turning Output Formatting on and off in my code for years now, it's nice to know how it all works below the hood. ?
  17. Yeah I'm still debating where it belongs, I just thought it nice to encapsulate them into the page classes. Why did I not think of using it as a static property? That seems a good idea. That way I can still have the constructors be responsible for it's auto-instantiation on every page that extends WebshopPage ? but be sure there is only one instance.
  18. I have a weird issue regarding page classes. For my module, I'm using a couple of those. For clarity, class names are in bold while template names are in italic. The WebshopPage class extends the Page class and is the class from which the others extend. PageCheckOutPage is the class for the CheckoutPage template and extends WebShopPage. Now I've added the following constructor to WebshopPage, in order to make sure every page that extends it has scope on an instance of the (custom) Cart class: public function __construct($page) { $this->cart = new Cart($this->session); parent::__construct($page); var_dump( get_class($this) ); } Now what happens is that the constructor of the Cart class is called twice when I view a page with the CheckoutPage template. Yet the PageCheckoutPage class doesn't have a constructor of it's own, so I would have expected it to fire it's base class constructor once. Yet with the var_dump above, I get the following result: C:\wamp\www\webshop.com\public_html\site\classes\Cart.php:14:string 'Cart constructor' (length=16) C:\wamp\www\webshop.com\public_html\site\classes\WebshopPage.php:16:string 'ProcessWire\PageCheckoutPage' (length=28) C:\wamp\www\webshop.com\public_html\site\classes\Cart.php:14:string 'Cart constructor' (length=16) C:\wamp\www\webshop.com\public_html\site\classes\WebshopPage.php:16:string 'ProcessWire\PageWebshopPage' (length=27) Instead of the extended class simply using the base class constructor as I would have expected, both the base class as the extended class fire a constructor seperately. Now I have a hunch that this happens because each page's parent is also instantiated, and thus it's constructor is ran. I can tell from nesting pages and then using var_dump($page->name) instead. Is this assumption correct? Perhaps there is a better way to ensure that all pages that extend WebshopPage have scope on an instance of the Cart class? I'd rather not have this constructor run as many times as a given page has parents.
  19. Ah, my bad. But that does mean this solution should work. The second-last post in that topic tells you exactly what to do. As other people pointed out, it's probably a good idea to make a backup of the files and the database before you do. I do agree though, PW is a fantastic piece of software that rarely runs into these kind of issues, especially if you keep upgrading your website like... once a year. This is also a reason why I vastly prefer it over Wordpress, which is likely to whine about one of it's many plugins needing an upgrade every few weeks. PW has most of that "plugin" stuff in the core already. I can serve most of my clients without using any custom modules: I only install the ProcessUpgrade module for my own convenience. The AdminThemeUikit issue on some old sites is the only instance of PW ever making me manually update anything on the backend. I regularly say no thanks to well-paying development jobs on LinkedIn because I want to keep using ProcessWire. That says something.
  20. Heyyyy https://www.orchardheightsdental.com/ is back up! I dunno what you did, but it worked! Nice! ?
  21. Thanks for your replies, dear people! I am going to take a look at the suggested JS libraries / frameworks and see what's most suitable for the application. Your descriptions of Alpine and htmx's features were very helpful. It sounds like Alpine would be something I could learn quite easily, but htmx's SEO advantage might be something I end up liking very much. One last question though: do module developers usually supply the JS code with the module, or do module devs tend to lean on a CDN? Performance and footprint is less important to me, but I like the idea of having the module work "out of the box" without requiring code from a CDN, and offering alternate solutions for client who want custom designs for everything.
  22. I get this error all the time on my development server. 9 out of 10 times I had simply forgotten to change config.php, and my site would try to access the database using my local config! Could it be that you've accidentally overwritten that file with your local version? I ended up implementing a switch in config.php that checks what domain we're on, and sets the config variables according to the current domain. Should it be an upgrade issue, you can get into some trouble when upgrading through the ProcessUpdate module because the JS files for AdminThemeUikit are missing! I had this issue a while back with a bunch of sites that were suddenly running on PHP8 due to a decision of the hosting provider, and some friendly people on this forum helped me update ProcessWire manually to get around this issue. You can find the topic here.
  23. I have a pretty broad question about module development. At the moment I have a module (a webshop) which contains some template and class files, which the module will move into the relevant folders (site/templates and site/classes) on install. It will also create a few pages to create the initial page structure, as well as a Process that I place beneath the Admin page. Now imagine that a page with the "product" template contains a form that posts the product ID and a quantity to itself, or another page which then redirects the request. So far so good, that would work. I would like to make it so the module works "out of the box" with some bare-bones templates the developer can modify / style after install. What I am not so sure about is how I'd typically create inline interaction that works "out of the box". On most webshops, a user might click the "add to cart" button and then some Javascript fires, adding the product to the cart through an AJAX call. Now, I could make it so some file (let's call it _webshop.php) is prefixed to a template instead of _main.php, that ensures any Javascript I supply gets loaded on pages that have such interactions. But I am unsure what kind of Javascript to use for AJAX related stuff in the frontend. I would have no issues having the module copy a .JS file into the /template/scripts folder, but I'm unsure on how to structure the JS itself. I've developed apps with Vue3 and (in the olden days) AngularJS or jQuery, but I also see some AJAX interaction in ProcessWire's page tree. Whenever you click on a page that has children, I can see an URL gets called in the Network tab of Chrome's inspector. That means there must already be a methodology the backend uses. Looking at the <head> section of a backend page, I suspect this was done using JQuery. Here is an example url I plucked from the inspector. It's clearly a GET, whereas I would be using POST, but that's just a detail. /page/list/?id=1110&render=JSON&start=0&lang=0&open=undefined&mode=actions Now I am hesitant to use JQuery (as anyone used to Vue would, I reckon). I was wondering if anyone else has ideas on how to best implement frontend AJAX interactions for inline actions, when working in the context of a module? I've considered to write a Vue3 app and simpy have _webshop.php load Vue3 from a CDN, but this feels "messy" to me and not very self-contained. I would also want to prevent introducing a whole Javascript framework, especially when the existing codebase already includes a methodology for AJAX calls. I know this "question" is pretty vague, but I had hoped the community could push me in the right direction. Thank you for reading my post.
  24. Yes! Well, not immediately. When I do this to the Process it still gave me an error, but when I used the argument in the lines that delete pages by their template (orders and order pages are deleted first) then it works. I still explicitly delete the individual orders by template before I delete their parent, but I'll experiment a bit with this technique. Again PW surprises me with it's simplicity. One last question: notifications are going to ask me to mark your post as the best solution. But I don't see any options for that. How can I do this?
  25. I've tried adding the hooks to the init method. I also used my own module name. This works. public function init() { $this->addHookBefore('Webshop::uninstall', function($event){ $this->deleteProcessChildren(); }); $this->addHookAfter('Webshop::uninstall', function($event){ $this->deleteProcesses(); }); } But alas, even when I explicitly set a BEFORE hook to delete all children... and an AFTER hook to delete the process itself... I still get the same message. Because I get that error, the module isn't truly uninstalled, but it DOES delete the children of the Process page. If I then attempt to uninstall it again, it suddenly works because the previous attempt deleted all children already. But whenever I try to do this in the same request, I get the message that the Process Page still has children, so somehow it's being called too early or something. I've added a $message to the loop that runs through the Process Pages children. As you can see it deletes the "orders" page (second line) before it tries to delete the Process page (last line). Webshop: deleteProcessChildren Webshop: process child deleted: orders Webshop: Deleted 0 Pages. Webshop: Deleted 3 Templates. Webshop: Deleted 5 Fields. Webshop: Permission 'shop-owner' deleted. Webshop: deleteProcesses
  • Create New...