Leaderboard
Popular Content
Showing content with the highest reputation on 05/11/2022 in all areas
-
With respect to templates, from the frontend, there is no way to tell. You might get a clue from the URL but there is no guarantee that the developer who worked on the site previously did not implement some sort of URL mapping/ rewriting. In addition, the page name (this is what is used to build the URL on the fly) is not guaranteed to match the page title since the previous developer might have amended those as well. Instead, I'd suggest using Lister to show you the information for various aspects of a page. If you don't have Lister Pro, use the core module Lister. Head to you menu Pages > Find. The select the Tab 'Columns' and add Http URL or URL depending on your needs. Templates should already be selected. That should give you a good start. There are filters there as well that you can play with to narrow things down, e.g. filter by URL (URL = /some/url/. Welcome to the ProcessWire forums ?.2 points
-
Hello @bernhard, you can add multiple httpHosts in your config file as array: $config->httpHosts = array( 'example.dev', 'localhost:8080' ); Source: https://processwire.com/docs/start/variables/config/ Also you could have a separate config file "config-dev.php" for your development environment: For the HTTPS issue, you could try to this: $config->noHTTPS = 'localhost:8080'; Source: https://processwire.com/blog/posts/pw-3.0.99/ I have never tried the noHTTPS option, but maybe it could work. Regards, Andreas2 points
-
@Matzn that not.how obj inheritennce work { .clsses inherit code not runs time valus } class A{public $foo;} class B extends A{} $a=new A(); $a->foo='Foo'; $b=new B(); echo $a->foo; // 'Foo' echo $b->foo; // emptys.. nothing modules work.same { .mabe.u want this in you TestModuleChild ? } >>> public function __construct(){ $this->setArray($this->modules->getConfig('TestModule')); }2 points
-
Hello friends! I have another module for you, which will make your daily work as a Processwire developer easier. Introducing: AppApi This module helps you to create api-endpoints, to which an app or an external service can connect to. Features Simple routing definition Authentication - Three different authentication-mechanisms are ready to use. Access-management via UI Multiple different applications with unique access-rights and authentication-mechanisms can be defined The documentation has become quite extensive, so have a look at the Github repository for details: Installation Defining Applications Api-Keys PHP-Session (Recommended for on-site usage) Single JWT (Recommended for external server-calls) Double JWT (Recommended for apps) Creating Endpoints Output Formatting Error Handling Example: Listing Users Example: Universal Twack Api Routes Page Handlers File Handlers A special thanks goes to Thomas Aull , whose module RestApi was the starting point to this project. This module is not meant to replace this module because it does a great job. But if you want to connect and manage multiple apps or need other authentication methods, this module might help you. I am already very curious about your feedback and would be glad if the module helps you a little bit.1 point
-
I've just posted a Fieldtype and Inputfield module combination that support the use of MySQL time fields in ProcessWire. Ryan's Datetime module is great but I needed something that dealt specifically with times for a scheduling system and this is one of the results. For 24 hour clock format ('HH24MM') you now get a clock-picker pop-up by default... ...but you can inhibit it if you don't want it... Although the input time format is selectable, as this is stored as a MySQL time field, you currently need to specify values in selectors in full "H:MM:SS" format. So if you wanted to get all events starting on or after 9am you'd do something like this... $events = $pages->find("template=events, starts>=9:00:00")->sort("starts"); This is definitely a beta module as I've not tested every single input format yet. Currently does not support negative time-periods or fractions of a second. FieldtypeTime on Github FieldtypeTime in the Module Repository Releases Version 0.2.0: Adds support for the use of this input field in repeaters and repeater matrix types. Version 0.1.0: Adds clock picker.1 point
-
I'm really in love with FormBuilder, but the one thing missing to match all my end users' expectations were repeatable field groups. Think repeaters, in ProcessWire terms. Our primary application of PW is our corporate intranet, so "lines" of fields are quite common in the forms I build. We have all kinds of request forms where the information for a varying number of colleagues needs to be entered (from meal order to flight booking request) and where it is simply impractical to send a form for each, and I don't want to clutter my forms with multiple instances of fields that may only get used ten percent of the time. That's why I started to build FormBuilderMultiplier (link to GitHub). What it does: Adds an option to make a regular Fieldgroup repeatable Lets you limit the number of instances of a Fieldgroup on the form Adds an "Add row" button the form that adds another instance of the Fieldgroup's fields Adds a counter suffix at the end of every affected field's label Stores the entered values just like regular fields Makes the entered values available in preview and email notifications Supports most text based fields, textareas and selects (really, I haven't had enough time to test all the available choices yet) What it doesn't do (yet): Support saving to ProcessWire pages (i.e. real Repeaters) I haven't tested all the validation stuff, Date/Time inputs etc. yet, but since I'm utterly swamped with other stuff at work, I didn't want to wait until I have it polished. Any feedback is welcome. There might also be some issues with different output frameworks that I haven't encountered yet. The forms I work with mostly use UIKit. Status: Still alpha, so test well before using it in the field. Known issues: When rows are added, the form's iframe needs to be resized, which isn't completely clean yet. How it works: The Fieldgroup settings are added through regular hooks, as is the logic that adds the necessary field copies for processing the form and displaying previews. "Multiplied" field instances are suffixed with _NUM, where NUM is an incremental integer starting from 1. So if you have add two fields named "surname" and "givenname" to a fieldgroup and check the "multiply" checkbox, the form will initially have "surname_1" and "givenname_1" field (I'm still considering changing that to make the risk to shoot oneself into the foot by having a regular "surname_1" field somewhere else in the form less likely). When a "row" is added, the first row is cloned through JS and the counter in the fields' IDs, names and "for" attributes as well as the counter in the label are incremented before appending the copies to the Fieldset container in the form. To keep backend and frontend in sync, a hidden field named [name of the fieldset]__multiplier_rows is added to the form. Both the backend and the frontend script use this to store and retrieve the number of "rows". ToDo: Naturally, add the option to store the data in real repeaters when saving to pages. Do a lot of testing (and likely fixing). Make a few things (like the "Add row" button label etc.) configurable in field(set) context. Add a smooth API to retrieve the multiplied values as WireArrays. The mandatory moving screenshot:1 point
-
Thank you! I was able to find the page I was looking for by using search and filtering by name. However I was trying to filter by path/url, but when I type anything into that filter, it shows the error: Operator '%=' is not supported for path or url unless: 1) non-multi-language; 2) you install the PagePaths module. Underneath this error it shows what looks to be the search: has_parent!=2, path%=/, limit=25, sort=url, include=unpublished Where I can see path%=/ (I just put "/" into the search). Am I doing something wrong? For some more context here's a picture of my search After playing with it some more I found out changing "Contains Text" to "Equals" fixes the problem, can you just not use that option with URL's?1 point
-
In the template code for your website footer you can add something like this: <?php if($page->editable()): ?> <a target="_blank" href="<?= $page->editURL ?>">Edit this page</a> <?php endif; ?> This will give logged-in users who have permission to edit the page a link to open the page in Page Edit, and you can work out its location, template, etc, from there. Or if you have Tracy Debugger installed you can run the following in the Console panel while viewing the page on the frontend and the edit link will appear in the console output: <a target="_blank" href="<?= $page->editURL ?>">Edit this page</a>1 point
-
1 point
-
1 point
-
Thanks @wbmnfktr I will definitely do. I was also looking into email relay services such as Mailgun or SendGrid but their privacy policies were not acceptable for them. ? Anyway, thanks for everyone for the ideas. We are going off topic though so it will be the WireMailSMTP module for now.1 point
-
Try instead: template=faq-category, parent=page.parent.parent, sort=sort1 point
-
Well, once you have the submitted data, you can do with it whatever you want. It really depends on what the data is and where you want it. In my comment above the example is one email address and one comment. So if you want to store those things in your admin page, its template would need a way to store multiple email-comment pairs. That could be a Repeater or a ProFieldsTable (?) or a custom fieldtype. You could also store one new page per submission as a child of your admin page (that’s essentially what a repeater would do behind the scenes anyway, it just looks different in the page tree and page editor – pretty much a matter of preference). Having a page per submission takes care of some stuff you probably want anyway automatically, such as storing the creation date. It also gives you a url for each submission that you could put into an email notification for yourself, or something. My comment gives a pretty complete example of how to get the data from the user’s browser into ProcessWire’s database. Are you struggling with anything specific?1 point
-
I think the error comes from trying to use PHP variables? I have approached this using the getSelectablePages hook and a couple others to enable dynamically adding them to different parents depending on their position within the tree,. (Bear with me since I am basing this example this from a very old project I have that "cascades" options into children page fields, it might not work exactly in a copy/paste fashion) Ok so this hook gets the pages only under the closest faq-parent of the current page with the reference field: $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { $page = $event->arguments('page'); $event->return = $page->closest('template=faq-parent')->children('template=faq-category'); }); Now, the tricky part when doing this dynamically and adding new pages is selecting the right parent, which from some old code I have lying around has to be done sort of like this: $wire->addHookBefore('InputfieldPage::processInputAddPages', function($event) { $field = $event->object; $page = $field->hasPage; $parent_id = $page->closest('template=faq-parent'); $field->set("parent_id", $parent_id); }); And this last step I am not super sure, but I think it is needed to enable the Add New pages part. $wire->addHookBefore('InputfieldPage::renderAddable', function($event) { $field = $event->object; $page = $field->hasPage; $parent_id = $page->closest('template=faq-parent'); $field->set("parent_id", $parent_id); })1 point
-
I can confirm I use this a lot too!! It also merges updates on fields and templates. I think the only caveat I have is that it doesn't work 100% with FieldtypeOptions. And the pages import/export is crazy good, works with absolutely every native field I've tested with, even complex Repeater Matrix/Repeaters. I just recently used it to migrate some old sites, into a new "template field schema" and I cannot even estimate how much work it saved lol1 point
-
@Matzn Your TestModuleChild.module is overriding the getModuleConfigInputfields() of the TestModule.module and replacing it. In PHP, if you want the parent method to run, then you need to either omit the method completely in your child class, or call the method parent::method() so that it will run (like what you are doing with your __construct method). What you are doing currently is replacing the method with one that has no code in it. What I think you are wanting is this in your TestModuleChild.module: public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { parent::getModuleConfigInputfields($inputfields); } After doing the above, now it will inherit the code of the parent class. And after calling that parent::method() maybe you want to add another configuration setting to it, i.e. public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { parent::getModuleConfigInputfields($inputfields); $f = $this->wire()->modules->get('InputfieldText'); $f->attr('name', 'test_field'); $f->label = 'Test field'; $f->val($this->test_field); $inputfields->add($f); } But if you don't want to add anything new to the getModuleConfigInputfields() then just leave the method out of your TestModuleChild.module completely, and it will inherit automatically. Also, module configuration data should be defined with runtime calls, so I recommend you remove this: public $field_1 = "parent module property 1"; and replace it with this in your __construct(): $this->set('field_1', 'parent module property 1'); I see you are using static getModuleInfo() methods in your classes. In cases where you've got one module extending another, unless the child module literally does an include() at the top of the parent module's file, then you should define your child module's info in a ModuleName.info.php file like I described in my previous message (including the 'requires' statement), and omit the getModuleInfo() static method. Otherwise, if PHP/PW happens to include() the child module before the parent module, you'll get a fatal class error. By using a separate ModuleName.info.php, PW can learn about the module without having to load the class file, and it can see from your 'requires' statement that it needs to load the parent module before your child module, ensuring no fatal errors. If you can't do this, or don't want to, then make sure your child module does an include() or require() of the parent module's file, somewhere before your "class TestModuleChild ...".1 point
-
@DrQuincy Yes, sorry, it should be "return $this->halt();" rather than just $this->halt(), I have updated my example. If you don't do a return then it'll still do the same thing (see below), but instead it'll continue executing your code in that file after the halt() call. What $this->halt(); does is tell the current request not to render any more files. So if you call $this->halt(); from your template file, then it's not going to include any further files specified to render afterwards. For instance, it will skip rendering your _main.php file (if you are using one). But everything else about the request will continue normally, so it does a normal shutdown, etc.1 point
-
Here is the blog post about it: https://processwire.com/blog/posts/processwire-2.6.8-brings-new-version-of-reno-admin-theme-and-more/#new-this-gt-halt-method-for-use-in-template-files1 point
-
@Matzn Are you wanting a parent/child class relationship, or are parent and child modules separate modules where you want the child module to pull a value from the parent module? If you just want to pull a configuration value from "ParentModule" to use somewhere in "ChildModule" (or anywhere else) then you don't need to have a parent/child relationship between the modules and can instead just pull the value you need: $api_user = $modules->getConfig('ParentModule', 'api_user'); On the other hand, if you really do want a parent/child relationship between the module classes, and they will be two separate installed modules, then it is possible. Many Inputfield and Fieldtype modules use this approach. For instance, InputfieldCKEditor extends InputfieldTextarea, and InputfieldTextarea extends InputfieldText. The reason is that they all have similar configuration needs, as they are all inputs for text. InputfieldCKEditor builds upon the configuration of InputfieldTextarea and InputfieldTextarea builds upon the configuration of InputfieldText. When it's possible that someone might attempt to install the ChildModule without having first installed the ParentModule then it's recommended to keep your module-info in a separate php (or json) file like ModuleName.info.php, at least for the modules that require another (i.e. ChildModule requires ParentModule). This ensures that if ChildModule is on the file system, but ParentModule isn't, PW can identify the requirements for the ChildModule without having to load its class file. (If it loaded its class file, then it would result in an error since ChildModule extends ParentModule, and ParentModule isn't present, so that's why we want to avoid that). Let's say we've got this ParentModule: // ParentModule.module.php class ParentModule extends WireData implements Module, ConfigurableModule { public function __construct() { $this->set('api_user', ''); // establish default value parent::__construct(); } public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { $f = $this->wire()->modules->get('InputfieldText'); $f->attr('name', 'api_user'); $f->label = 'API User'; $f->required = true; $f->val($this->get('api_user')); $inputfields->add($f); } } And it also has this ParentModule.info.php file to define module info. Since ParentModule doesn't extend another module, it could also be in a static getModuleInfo() method if you preferred it, but we'll stick to the file for this example: // ParentModule.info.php <?php namespace ProcessWire; $info = array( 'title' => 'Parent module', 'version' => 1, 'summary' => 'Parent module description', ); Here's a child module that extends ParentModule and also adds an "api_key" to the configuration: // ChildModule.module.php class ChildModule extends ParentModule implements Module, ConfigurableModule { public function __construct() { $this->set('api_key', ''); // default value parent::__construct(); } public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { parent::getModuleConfigInputfields($inputfields); $f = $this->wire()->modules->get('InputfieldText'); $f->attr('name', 'api_key'); $f->label = 'API Key'; $f->required = true; $f->val($this->get('api_key')); $inputfields->add($f); } public function test() { $this->message("API user is $this->api_user and key is $this->api_key"); } } …and the ChildModule's info file is below. Note the "requires" that says it requires ParentModule: // ChildModule.info.php <?php namespace ProcessWire; $info = array( 'title' => 'Child module', 'version' => 1, 'summary' => 'Child module description', 'requires' => 'ParentModule', ); Zeka's example above is also a good one, but just adding my example as another variation.1 point
-
Hi @Matzn <?php namespace ProcessWire; class ParentModule extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return [ 'title' => 'Parent Module', 'version' => 1, ]; } const defaultValue = '12345'; public function __construct() { $this->set('api_user', self::defaultValue); // set default value in construct } public function getApiUser() { return $this->api_user; } public static function getModuleConfigInputfields(array $data) { if(!isset($data['api_user'])) $data['api_user'] = self::defaultValue; $form = new InputfieldWrapper(); $f = wire('modules')->get('InputfieldText'); $f->name = 'api_user'; $f->label = 'API USER'; $f->value = $data['api_user']; $form->add($f); return $form; } } <?php namespace ProcessWire; class ChildModule extends ParentModule { public static function getModuleInfo() { return [ 'title' => 'ChildModule', 'version' => 1 ]; } public function __construct() { parent::__construct(); bd($this->getApiUser()); bd($this->wire()->modules->getModuleConfigData('ParentModule')); } } Take a look at the construct method of ChildModule. Without calling parent::__construct you will not be able to get what you want. Also you can use $this->wire()->modules->getModuleConfigData('ParentModule') to get config data of module.1 point
-
So... maybe ask them what they would like to use or recommend in first place and go from there. Depending on the setup you could also look into environment variables which sit in places far away from any web root. Another thing could be a mail account which is only used for that website. Or you use something like Mailgun, Mailjet or similar which is a 3rd party service and therefore all details sit on yet another server. I personally use Mailjet which is configured in a way that only my website can send mails through that account, still everything via SMTP. So... even if someone can get a copy of my credentials they still would need access to the server or the Mailjet account to do anything with it.1 point
-
@horst the password could be encrypted using the $config->userAuthSalt. For sending an email the module could load the encrypted password from the DB, decrypt it using the config salt and log into the mail account. That way an attacker would have to have access both to the DB and to your config.php file. https://stackoverflow.com/questions/9262109/simplest-two-way-encryption-using-php Though I don't know if it's really worth having that extra...1 point
-
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.1 point
-
I know this topic is old, but I came across the exact same issue in my first ML site. I am surprised that what @maba was looking to do is not more common or talked about. It seems like the only logical option in my opinion. Anyway, soma's solution did the trick - it's all about that getUnformatted method so that getLanguageValue has an object to work with. I did want to clarify though that it is in fact getLanguageValue rather than getLanguagevalue - note the case of the "V". It doesn't prevent it from working because methods in PHP are not case sensitive. Also can of course just use the $language object instead of the id. $language->getUnformatted('title')->getLanguageValue($language); It does seem a little unnecessarily complicated though.1 point
-
1 point