-
Posts
76 -
Joined
-
Last visited
-
Days Won
1
Everything posted by Inxentas
-
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.
-
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?
-
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
-
I've tried this, and put a $this->message() call in the hooked methods for now. They are not called at all. The pages are also not "installed" but created in the API like this: $admin = $this->wire()->pages->get("title=Admin"); $webshop = new ProcessWire\Page(); $webshop->template = "admin"; $webshop->title = "Webshop"; $webshop->icon = "cogs"; $webshop->parent = $admin; $webshop->process = $this; $webshop->save(); $orders = new ProcessWire\Page(); $orders->template = "page-orders"; $orders->name = "orders"; $orders->title = "Orders"; $orders->parent = $webshop; $orders->save();
-
I'm developing a module that installs a couple of nested pages beneat the Admin page (and assigns the module as a Process to them). In the install method, I create these pages from top to bottom. In the uninstall method, I do the reverse and delete all children before deleting their parent page. However, I get the following error message: ProcessWire: ProcessModule: Can't delete Page 1563 because it has one or more children. This is strange as the script makes sure that isn't the case, as these are all deleted. Afterwards, their children are indeed nicely deleted. I've done a couple of checks. When I first delete all children, and use $message to print out $p->children()->count that returns zero, as expected. Yet $p cannot be deleted after confirming it's zero, because "it has children". Does anyone have any idea why? Extra information: Beneath the Admin page I create a new page called "Webshop" and assign my module as a Process to this page. This creates a button in the menu that leads to the primary admin interface of the webshop, for which I use a view. This all works nicely. The module installs a template named "orders" beneath this page, as I was planning to collect webshop orders beneath that one. In the uninstall method, I make sure to first delete all pages of the template "order" and "orders" before I attempt to delete the Webshop Process page. On an unintall attempt, I get above error message, but when I check whether the page it references (1563) indeed has children, these are indeed deleted in accordance with my code. Only the Webshop page remains, and it's Process is (logically) empty.
-
Thank you, that worked flawlessly! I was a bit scared of manually updating 4 production sites this way, but I managed to complete the task without any real issues.
-
Hi guys, I have a few really old sites that never received their updates. ProcessWire version 3.0.62. Now I can install the ProcessWireUpgrade module and run it, but I get some errors: Method AdminThemeDefault::getClass does not exist or is not callable in this context And that's logical considering how old these sites are. At the time we created them, Reno was the default Admin Theme. I have tried installing AdminUIKit by coping a (recent) module into /wire, which gave me another error. I've tried putting the module in /sites, same error. Error installing module - AdminThemeUikit Failed to construct module: ProcessWire\AdminThemeUikit - Method AdminThemeUikit::setClasses does not exist or is not callable in this context --- I have a feeling PW is trying to run a method on the current admin theme, which doesn't exist because it's only in the new admin theme. Can I somehow fix this and update the websites?
-
Moving the vendor folder outside of public_html folders?
Inxentas replied to Inxentas's topic in General Support
For me the SMTP thing is not really negotiable, sometimes companies use some mail setup that requires it. I've used both PHPMailer and the module, and sometimes the module even wins out. Thanks for the "post-install-cmd" tip, that might work well for my situation... being that moving the vendor folder outside of the webroot isn't mission critical. I'm in that situation where I can do whatever I want, but I do have to work with 101 different mail clients and shared hosts so I prefer to add my most used packages to a new project by default. Thanks everyone! -
Moving the vendor folder outside of public_html folders?
Inxentas replied to Inxentas's topic in General Support
I took a look at the WireMail class and what I miss is SMTP support (although I might be wrong here, I simply didn't see anything in the docs that implies SMTP is supported out of the box). I could also use the module named WireMailSMTP for example, but no matter how you cut it that would involve installing the module manually for each website. It's preferable to me to manage dependencies with Composer and reuse our existing mail scripts, then installing PW modules manually for each project. -
Moving the vendor folder outside of public_html folders?
Inxentas replied to Inxentas's topic in General Support
Thank you, I found that article already though, and I found it to be a little messy of a process as well. On top of that I would like to use autoloaded stuff in my modules so that approach isn't super suitable for me. I have decided that for now, I'll just modify the existing index.php as that file won't change too often. -
Hello guys, I've started to implement Composer in my workflow more seriously and I'm looking for the best way to include more then just ProcessWire. We often use a few extra bits and bobs (such as PHPMailer) and I'm not sure how to best treat PW as a Composer package and how to get the vendor folder outside of the public folder. Now whether I do composer require or composer create project, I get PW as a full project including it's own composer.json and .lock files with it's vendor folder sitting next to the wire and site folders. Now I've done some experiments, such as moving the folder manually and changing the require statement in index.php, but that feels messy to me. I've also created a new empty project with my own folder structure, and then copied vendor/processwire/processwire into the public folder for installation, but I still need to faff around with PW's actual code. Is there a neat way to create a Composer project (or inherit the existing one) and use Composer commands to somehow get the vendor folder to sit once directory up from it's default directory?
-
Yes, and that worked perfectly! Thank you @BitPoet. I was hesitant to change the .htaccess file but truth be told, this is kind of a legacy project, so in the end I did not do much research on what that would affect. Are there any PW related issues I should take into account? PS. I received a mail asking me to click a "mark as solution" button. I wanted to mark your reply as the solution but I'm not seeing that button. I fear I'm going blind.
-
Hello there. I have an issue with a PW installation on some crappy shared host. A few days ago the site stopped working. So I checked the error logs, which said: Option FollowSymlinks not allowed here And logically, PW's .htaccess file says: Options +FollowSymLinks So it appears that FollowSymlinks is suddenly no longer allowed on this server. I rarely have to deal with server settings, but is the impression that PW simply needs FollowSymlinks to be allowed. Is this the case? Would it be valid to state that this server isn't suitable for a PW installation anymore, because the host no longer allows SymLinks? Would that statement be correct? I've never had the pleasure of a shared host suddenly no longer allowing SymLinks out of the blue, to be fair I am not entirely sure how those work. I can run the site on my local and test servers just fine: it's just this shared host that's been giving me grief for the last few days. The homepage could be reached but every child page just gave me a serverside 404 (not the 404 you'd expect from PW itself).
-
I have a strange occurence which I have a hard time explaining/debugging. I have a module that contains a .JS file. I load this JS file using a hook, that is called when the template != admin... meaning it will only be called on the frontend. This works on my LIVE environment, but my DEV environment seems to decide to load the file somewhere. Since it contains a script that prevents scrolling, I cannot scroll in the admin. Is there a circumstance in which modules load JS files placed in their folder? Perhaps when it has the same name of the module under certain PHP versions?
-
How to configure InputfieldTextarea properly in a module.
Inxentas replied to Inxentas's topic in Module/Plugin Development
Ah, thanks a lot! I was trying to configure an InputfieldTextarea as one would do in the CMS itself and couldn't get it to render. Using the right class worked very well. -
Hello everyone, Is it possible to use a Repeater in module configuration? I can add one to getInputFields(), like so: // Source Repeater $field = wire('modules')->get("InputfieldRepeater"); $field->columnWidth = 100; $field->attr("id+name", "source_repeater"); $field->label = "Source Text"; $field->icon = "fa-align-left"; But I am at a loss as how to configure the Repeater further. For instance, I've tried making an instance of another fieldtype (FieldtypeText) and adding it to the repeater through code. I want to allow the user to add new entries in this repeater, but want to configure the Repeater field itself entirely through code. I want the admin to be able to add new Repeater items and fill them in. The module should decide what fields to use in the Repeater. When I add the Repeater code above to a wrapper it throws an error, I guess because it needs some fields or further configuration in order to be rendered. Is it absolutely required that I create fields, for instance in the install method of the module, or can I create them on the fly in getInputFields() somehow? How and where should I define the fields of this repeater? Thank you for your time!
-
Hello everyone, I'm trying to write a module configuration by extending ModuleConfig. So far so good. I have a nice wrapper going with some inputfields. One of them is a TextArea that I've added as a field to my config class: $field = wire('modules')->get("InputfieldTextarea"); $field->columnWidth = 100; $field->attr("id+name", "source_text"); $field->label = "Source Text"; $field->icon = "fa-align-left"; I'm trying to make this a field that accepts HTML instead of plain text, so I want it to use CKEditor and set some options. I couldn't find how to configure that so I made myself a custom field through the CMS so I could take a look at how it's constructed. I figured out I could change it's attributes, but this doesn't seem to work: $field->attr("inputfieldClass", "InputfieldCKEditor"); $field->attr("contentType", 1); I'm a bit at a loss as to how I configure this field, since it's part of a module. I've also tried calling getExportData() on the field I made manually, which works and shows the relevant data, but I can't call setImportData() on the $field variable above: this causes an error. I guess because that field isn't saved in the database the way a normal field is. Can I configure module fields to support HTML?
-
How to allow certain Roles to view Processes?
Inxentas replied to Inxentas's topic in General Support
Super, thank you! I couldn't find it in the Process module documentation, but that makes a lot of sense. I'm almost embarrased ? -
My goal is to add a tab at the top of the admin named "tools" under which I want to install a few custom modules. For the Tools tab, I used a page with a ProcessList (so it visually looks like the Templates page). This works perfectly for superusers, but I cannot for the life of me find out how to allow other roles then the superuser to access these pages. I've tried allowing the "admin" templates to be viewed by these Roles, but that doesn't seem to work. Is this actually possible?
-
$config->usePageClasses = true fails on Staging
Inxentas replied to Inxentas's topic in General Support
Ha ha yes, I feel so silly because this isn't the first time this exact issue has caused me to look for the culprit for about an hour, to finally post on a forum in desperation... only to find the bloody typo just 5 minutes later! I'm alwats torn between blaming Windows / MacOS for not being case sensitive and exact enough, or myself for sticking with CamelCase. Thanks for understanding ? -
$config->usePageClasses = true fails on Staging
Inxentas replied to Inxentas's topic in General Support
Typical. Just as I submit the topic, I find the solution. Turns out I just made a CamelCase boo boo with the filename: Defaultpage.php had to be DefaultPage.php. Sorry to waste your time. -
I seem to have some issues with the use of $config->usePageClasses introduced in ProcessWire 3.0.167. I've set it to true so I can use Page Classes. Works perfectly on my own machine, but not on my Staging server. I get the following error: Look out… Error: Exception: Method Page::renderHeading does not exist or is not callable in this context (in wire/core/Wire.php line 544) The method renderHeading() is part of the DefaultPage class. It seems the staging server ignores the classes entirely, as a var_dump() turns out the page is not of the DefaultPage class but the standard Page class. I have no real idea where to look for the issue, does anyone have an idea on how to debug this? The only difference between my Local machine and the Staging server is that it uses a different MySQL database to connect to. Here is the class in full: <?php namespace ProcessWire; class DefaultPage extends Page { /** * Renders a heading based on any title and an optional blurb. * * @param type $title * @param type $blurb * @return string */ public function renderHeading($title, $blurb = null) { $html = ''; $html .= '<div class="content"><div class="layout"><div class="intro"><div class="narrow">'; if ($title != null) { $html .= '<div class="big"><h2>'.$title.'</h2></div>'; } $html .= '<div class="divider"><div class="inner"></div></div>'; if ($blurb != null) { $html .= '<div class="blurb">'.$blurb.'</div>'; } $html .= '</div></div></div></div>'; return $html; } /** * Renders a Project page for the Collection Page grid. * * @param type $col * @param type $row * @param type $project * @return string */ public function renderProject($col, $row, $project) { $html = ''; $html .= '<div class="project project_'.$col.'_'.$row.'">'; $html .= '<a class="inner" href="'.$project->url.'">'; if ($project->image_header) { $html .= '<span class="background" style="background-image:url(\'' . $project->image_header->width(600)->url . '\')"></span>'; } $html .= '<span class="background" style="background-color:rgba(0,0,0,0.2);"></span>'; $html .= '<span class="inner">'; $html .= '<h2>' . $project->title . '</h2>'; $html .= '<div>' . $project->meta_description . '</div>'; $html .= '</span>'; $html .= '<a class="link" href="' . $project->url . '">View</a>'; $html .= '</a>'; $html .= '</div>'; return $html; } } When I add a specific class for the homepage and let it extend DefaultPage... <?php namespace ProcessWire; class PageHomePage extends DefaultPage { } I get the following error: Well well… Fatal Error: Class 'DefaultPage' not found (line 3 of site/classes/PageHomePage.php) It seems the DefaultPage class cannot be found/loaded.
-
No, I simply moved them to a local directory, hoping that would be sufficient. Uploading them via the admin seemed to work, thank you! I'm still curious as to what is the difference (what uploading them via the admin actually does apart from storing the file). I could not find a "translation" system template, did you perhaps mean the language_files_site system Field? That one seems to have an option to allow for overwriting files.
-
A client of mine wants his translations being done by an external agency. I've showed them the Site Translation Files and they can deliver me the required translations in the same JSON format. I went about it by first making some translation files for each language, so there's an ID and all the fields get an identifier. The intent is to give them the file, they change the content, and we place those files back. From there I've done some testing and it appears we could simply change the content of these files, as long as the JSON format is being respected. Whenever I changed something, the translation worked properly. So I sent a JSON file to the translator and had them translate the strings. There's no stuff in there that needs escaping so I figured this should work properly. At first they screwed up the values in the "file" and "textdomain" properties of the JSON object, but we fixed that. However, when I place the file in it's appropriate folder I get the following error: Fatal error: Uncaught TypeError: Argument 3 passed to ProcessWire\LanguageTranslator::textdomainTemplate() must be of the type array, null given, called in C:\wamp\www\bamboobrands.com\public_html\wire\modules\LanguageSupport\LanguageTranslator.php on line 485 and defined in C:\wamp\www\bamboobrands.com\public_html\wire\modules\LanguageSupport\LanguageTranslator.php on line 137 TypeError: Argument 3 passed to ProcessWire\LanguageTranslator::textdomainTemplate() must be of the type array, null given, called in C:\wamp\www\bamboobrands.com\public_html\wire\modules\LanguageSupport\LanguageTranslator.php on line 485 in C:\wamp\www\bamboobrands.com\public_html\wire\modules\LanguageSupport\LanguageTranslator.php on line 137 When I delete the JSON file the error goes away so I'm quite sure it's the file causing the issue. I did the following test: Deleted the language file and remade it via Find Files To Translate. Copied the file's content into a new JSON file. Overwritten the language file with this new JSON file. The same error occurs. I've confirmed with DiffChecker that the files are 100% identical. Somehow this issue only occurs when I overwrite the file. I seem to be able to copy-paste the data in, but using a different file causes above error. What could be causing this? Encoding maybe? I couldn't get the translator's file to work, but I could paste their work into the file. (I've told my client translation is an maintenance issue because his content will keep changing anyway. He is adamant that we do it this way, disregarding the fact that pasting in his translations would be less work then working around any coding issues. I know it's kind of stupid and error-prone to do it this way).
-
I have a big collection of Pages with a template called page-product. These products have a few fields describing their properties. Now my client wants to split this template into various types of products, each with their own set of properties. The most obvious solution would be to make a few new specific templates, like page-product-foo or page-product-bar. Doing this would also mean rewriting a lot of existing selector logic. So what I could also do, is allow the client to append a child to the Product that has these properties. For me that would be a good solution, but for my client this might be confusing. So I'm looking for a user-friendly way to do the same. Ideally, these Properties should be filled in when editing a Page without having to mess with it's Children. One option is to make a Page with children for each type of product and make it a dropdown. And subsequently have each "property" field be visible only when a specific product-type is chosen. However, I'm not sure if this is the best way to go about it - since at the frontend I'd have to take these fields into account, and I'm not sure whether I'll be able to deduct in code which field should be shown (I guess this could be pulled from $fields->MyFieldName). Does anyone have a better idea on how to implement such a feature?