Leaderboard
Popular Content
Showing content with the highest reputation on 12/11/2020 in all areas
-
Last week’s post was about the new ProFields Combo field and it seemed like there was a lot of interest. Thank you for that, it really got me motivated to finish it and release it sooner rather than later. I am very excited about the Combo field and worked on it most of this week, hoping that I may be able to have the Beta version ready for release in the ProFields board by this time next week. This is a little more break than I like to take from the core, but the focused development time means it gets done a lot sooner, and likewise in a stable release state a lot sooner. Since I don’t have core updates to walk through this week, I thought I’d focus on some Q&A about the Combo fields. Below are questions that were asked either here in the forums or in the blog post. Please feel free to reply with any other questions that come up too. There are definitely cases where this type of arrangement Combo provides would provide some nice performance gains. And there are other cases where they might reduce it. Consider that fields on a page in ProcessWire are dynamically loaded when you access them. So when you access anything from a Combo field, all of the data in the Combo field loads at once (1 query). So if you’ve got 10 fields in a Combo field, and you are going to be using much of that data in the request, then you’ve just made some nice performance gains, at least relative to having those 10 fields split into dedicated/separate ProcessWire fields. On the other hand, perhaps you are rendering a list of pages, and you only need one or two fields of data from the Combo field. In that case, it’s less efficient than regular ProcessWire fields because you’ve loaded a bunch of data in memory that isn’t going to get used. So I would say Combo fields are great for data that you use when rendering a full Page, but not as great for cases where you are rendering lists or summaries of pages (where you might not be using much of that data). In the blog post I mentioned that Combo fields store data in individual DB columns, but also keep a separate index so that all of the combo data can be searched together. If that’s what you are referring to, I would say the primary downside of this storage method is that it takes up more space. It adds convenience and efficiency for searching, but at the cost of consuming more disk space [in the database]. This is something that may be beneficial to some and less so to others, and it may end up being an optional thing too. The index is similar to a JSON encoded version of all the fields. But it’s something that I think is worthwhile for another reason: backups. Like the Table field, the Combo field maintains a unique DB schema consistent with your selection of fields. Every time you save a Field, it checks the schema and determines whether to add columns, drop columns, or modify existing columns (and likewise for indexes). The JSON encoded version of the data is immune from data loss as a result of schema changes, so I see it as potentially useful as a way to backtrack, should it ever be needed. As for your question about other fields in ProcessWire, Combo stores it exactly the same way as other fields, with the only difference being that it keeps a separate combined index of data; and is able to because the core Fieldtype architecture enables it to do so. Why not use the same duplication approach with other fields? I can't think of any core fields where this type of storage duplication would be beneficial in the way that it is with Combo. Outside of the core, the approach might be worthwhile in ProFields Table though. Yes! Combo fields work in Repeater and RepeaterMatrix fields. Since Combo fields are not a repeatable type, they play nicely with repeatable types. They might look similar but they are also completely different. A page fieldset (FieldtypeFieldsetPage) is technically a single repeater item, but presented in a non-repeatable way. It’s a convenient way to make a group of fields that you could reuse across any number of templates. And in that respect it’s quite similar to Combo, at least in appearance. But that appearance is where the similarities end. Fields in a FieldsetPage are still individual ProcessWire fields defined on a template, and the Fieldset itself is an independent Page (an individual repeater item behind the scenes). So if you’ve got a FieldsetPage with 10 fields in it, then it’s still consuming the resources of 10 ProcessWire fields, plus 1-2 additional pages for structure. The goal of ProFields is to reduce the number of fields necessary to answer a particular need, and that is not the goal of FieldsetPage. If a Combo field can answer your need, it can do so a lot more efficiently than a FieldsetPage can. On the flip side, since FieldsetPage uses regular ProcessWire fields, it can support more types than Combo can. Nevertheless, once Combo is available, I would most often choose it over a FieldsetPage except in cases where I needed a type of field that only FieldsetPage could support. Yes, any field within a Combo field can be queried individually in selectors using the field.subfield syntax. For example: $pages->find(“combo.first_name=Ralph”); It’s just like querying a Page reference field on pages in ProcessWire. So far, my experience is that it can do all of the same things, and supports all of the same operators; whether querying text, numbers, Page references, selectable options, etc. There are technical differences behind the scenes though. All the data in a Combo field for a page is represented by a row in a database table, so matching some types of values (like multi-Page references) has to use indexes rather than lookup tables. This is similar to the approach taken in the Table field. Per last week's post, Combo fields can do one thing that other fields in ProcessWire can’t though. Because it keeps an index of combined data, you can perform text matching on all of the subfields in a combo field together, just by omitting the “subfield” from the selector. An example is that $pages->find(“combo~=Ralph”); would find the word “Ralph” in any of the fields in the Combo, rather than just the first_name field. So not only is it easy to do, but it can do that more quickly and efficiently than other fields in PW. Thanks for reading and have a great weekend!7 points
-
Cloudflare announced the general availability of their new Web Analytics solution today (even for non-Cloudflare-powered sites): https://blog.cloudflare.com/privacy-first-web-analytics/ I'm quite excited about this, as I've been looking for a solution that's both privacy-friendly, free for personal sites and GDPR compliance. In particular, being able to collect visitor statistics without tracking any personal data is an attractive proposition, since it doesn't require consent even under the GDRP. I looked at various alternatives, but all have their caveats: Google Analytics is out for obvious reasons. Matomo is a solid choice since it can be configured to not set any cookies and anonymize IP addresses. However, it's use of fingerprinting is somewhat dubious, so it's a grey area whether you need consent to use it. A couple smaller alternatives like Netlify Analytics (for Netlify sites) or fathom look good enough, but their pricing isn't really feasible for small personal projects. I have added the Cloudflare tracking script to my tutorial site processwire.dev, for now I'll use it alongside Matomo. Will be interesting to see how their numbers compare to Matomo. What do you all think? Are there better alternatives out there?5 points
-
I will send out an expression of interest request nearer the time. I haven't yet decided on a criteria for selection. Current plan is PayPal IPN due to its multi-notifications types. However, I may also do PDT, but not 100% sure at this time. No decisions made yet regarding Stripe's implementation. All frontend implementation is up to the developer. You will have the API at your disposal to do whatever you need to do. In the backend/model, orders can have different status (paid, unpaid, complete, partial, etc). Order line items also have individual status (returned, delayed, refunded, etc). This means you should be able to implement a pre-order feature yourself by, for example, marking an order as unpaid, in progress, etc. Padloper 2 does not render anything in the frontend. It is like ProcessWire. There will be no more in-built Padloper templates. You implement Padloper 2 however you wish using the API. I will, however, as part of the technical documentation, write a tutorial on ajaxifying the frontend. In addition, a separate frontend commercial module (fully functional frontend shop) is in the works. Good question :-). Padloper 2 does not utilise a single ProcessWire field, nor any custom ProcessWire fields, nor any ProcessWire pages (except just the one for the Padloper Process Module). All data is stored in custom Padloper tables. Having said that, it uses ProcessWire selectors just the same way you are used to, offering the same security and ease of use you are used to. You won't have to learn a new syntax. In addition, it is fully multilingual (descriptions, titles, etc). I plan to do a write-up of this when I'm done, a sort of 'the making of Padloper 2', the iterations, decision made, etc. No, you won't be able to do this. Not this way. There are no (ProcessWire) pages in Padloper 2 :-). That said you would still be able to use hooks. You've got some very interesting (albeit very advanced) ideas/thoughts there. Whilst not yet planned, when the dust settles, I'd like to further discuss/explore some of these with you. I cannot make any promises as to the outcome. Yes. There are four types of products (housed under Shipment Type). Physical Products that require shipping. Physical Products that do not require shipping (POS, etc). Digital Products. Products that are Events or Services (e.g., booking system). ---------------------- I hope I have managed to answer all your queries. I decided to just do it inline. I might reference some of the responses in the first post if I need to. Thanks.5 points
-
Thanks for the great post @ryan, but I am curious about this - why does the combo field have to load all subfields (db table columns). Why not have a way to select just the subfields we want? I know it's not a completely correct example, but think of SELECT field1, field2 FROM table vs SELECT * FROM table. Perhaps I am missing something about how it all works though?3 points
-
Announcing the current status, planned release, roadmap and preview of Padloper 2. Status Feature freeze. Full multilingual support. Only PHP 7.2+ supported. Support for ProcessWire 3.0 only. Backend support for modern browsers only (that support JavaScript ES6 modules). Current Work Finish work on admin/backend. Work on installer and uninstaller (including configurable availability of some features). Work on UI/UX improvements. Start work on documentation with special focus on technical documentation. Continue work on Padloper API and data/model component. Roadmap Please note that these ARE NOT hard and fast targets. The roadmap may have to be adjusted to accommodate technical and non-technical constraints. Q1 2021 Inbuilt support for (latest) PayPal (full rewrite, no external modules required). Additional work on Padloper API. Invite a limited number of early alpha testers (fully-priced product). Soft and closed release of Padloper 2. Q2 2021 Start work on relaunch of Padloper website. Inbuilt support for Stripe (no external modules required). Future Plans Support for more Payment Gateways. Support for order, customers, etc imports and exports. Support for AdminThemeReno and AdminThemeDefault. Separate fully-featured frontend shop module. Consider support for multiple currencies. FAQ 1. Have you abandoned this project? No. 2. When will Padloper 2 be released? First early alpha release is scheduled for Q1 2021. This target may change depending on circumstances! Access will be by invite only for this first release. 3. What is the pricing model of Padloper 2? Three licences: Single Site, Developer and Agency licences (12 months’ updates and VIP support). 4. How much will Padloper 2 Cost? No price has been set yet. It will cost more than Padloper 1. 5. Can we upgrade from Padloper 1? No. 6. Will existing users of Padloper 1 get a discount for Padloper 2? No, this will not be possible. Apologies for the earlier announcement. It was unrealistic and unworkable. 7. Can we pay for Padloper 2 in advance? No. 8. Does Padloper 2 render markup/templates in the frontend? No. Access to all data you need to build your shop’s frontend is via the Padloper API. 9. Can we keep sending you ‘Are we there yet’ messages? No, please. Preview Here is a video preview of the current state of the backend/admin of Padloper 2. Please note the following: This is early alpha. There are bugs! It even includes WIP/notes!! FOUC, misaligned things, etc. The video shows the near-raw implementation of Vuetify UI. The UI/UX improvements work is yet to start. What you see here is the development version. Some of the incomplete features may not be available in the early releases. Most of the features you see will be optional to install.2 points
-
@adrian It's definitely possible, and maybe we'll add the option for that. For this first version though it loads all the data in the field in the same way that other PW fields do. But you are right that it would likely be relatively simple to have it load them dynamically.2 points
-
According to the bureau of the Datenschutzbeauftragte of NRW, germany: If you (self)host Matomo on the same domain or subdomain with anonymized IPs, you can start to use it without opt-in, regardless if cookie-less or a use with cookies. Only thing you must provide and respect is a opt-out option. Don't know if this is different in other Bundesländer of germany, but I have gotten a written answer with the above content from the NRW-bureau. ?2 points
-
2 points
-
@adrian Having the option in something like Combo is definitely a nice choice. But like with the Table field, it places limitations on what kind of fields you can use in it (focused on simpler types), and what those fields can do. The approach isn't nearly flexible enough for a "supports any kind of field" modular system like PW. For instance, you could never have the likes of a Repeater field in a Combo field, among others. So it's kind of limited and primitive in that respect. But for a focused purpose module like Combo, that's where I think this approach is really useful and worthwhile.1 point
-
That's sounds great and if this is available, I wonder if this might change your answer to @Robin S's question about using this approach for all fields in PW. From my point of view I have always had a bit of a love / hate relationship with Tall-Thin vs Short-Wide approach to data storage in PW. There are obviously huge advantages in flexibility with Tall-Thin, and I know that in some cases they are more performant to query, but other times they are much less performant. While I don't expect moving the PW core in the direction of the Combo field actually makes sense, I do think that using it in certain templates for all fields (except page name / title) might make sense.1 point
-
@horst Hm that's interesting, though I'm not sure if that would be enough for me to call it a day ... I've found that in many cases the Datenschutzbeauftragte don't have a solid grasp on the technologies and what actually constitutes PII, so they basically judge compliance of a tracking mechanism based on whether it sets cookies or not. Even though fingerprinting is much more perversive and there's no real opt-out for that. I'm also fairly certain that using Matomo with cookies requires consent under GDPR – since it allows you to identify returning visitors, you can collect pseudonymous information, which requires consent. I'm surprised the Datenschutzbeauftragte of NRW would make such a blanket statement, and I don't think it would hold up in court (not sure if the written statement would help there, I'm not a lawyer ?). But even without cookies, it's a gray area at best. The reason people jump on the cookie debate while ignoring other mechanisms such as fingerprinting is probably because the legal precent (most notable the EUGH decisions) have mostly been about cookies. So right now there's a grey area regarding fingerprinting. But I think as soon as there's a test case / precedent fingerprinting will be shut down the same way consent-less tracking cookies were shut down. Anyway, regardless of the current legality I think it's a good approach to drop cookies and fingerprinting in favor of referrers to differentiate between visits and page views (as Cloudflare claims they do). Though we'll see how well it works and if it's as privacy-friendly as they claim ?1 point
-
1 point
-
That's interesting, I'd never actually tested that. I'll make a note to disable those translation connections in the module. Also really odd that capability doesn't exist... seems like literally the easiest translation they'd have to do haha.1 point
-
This week we’ll take a brief look at a powerful new ProFields module for ProcessWire that’s just around the corner—the Combo field: https://processwire.com/blog/posts/about-the-new-processwire-profields-combo-field/1 point
-
Thank @ryan. Great additon to ProFields family. Will Combo field work within RepeaterMatrix field?1 point
-
@FireWire - not sure if you want to bother with this, but I just found out that DeepL doesn't yet support translating between US and UK English. I was hoping to be able to change things like neighbor to neighbour. I have contacted DeepL and asked them to consider supporting this. Strange thing is that you can actually translate from US English to Spanish to UK English, eg: neighbor -> vecino -> neighbour. Hopefully they'll support direct translation in the future, but until then I wonder if you want to remove that option from the Translation process module?1 point
-
@LAPS With this new version, there are even more customizations available. Have a look into the comments in PrivacyWire.module. This example code does the same as the one from my post from last Friday, but with the new syntax of the new version: <html> <head> <!-- your head content --> <?php $privacywire = $modules->get("PrivacyWire"); // load the css files echo $procache->link([ $config->urls->template . "path/to/your/stylesheet.css", $privacywire->getPrivacyWireStyles()->url ]); // load the inline script echo $privacywire->renderPrivacyWireConfigAsInlineJs(); ?> </head> <body> <!-- your body content --> <!-- your body content --> <!-- your body content --> <?php // render the required DOM elements echo $privacywire->bodyContent; // render the JavaScript files echo $procache->script([ $config->urls->template . "path/to/your/scripts.js", $privacywire->getPrivacyWireCore()->url ]); ?> </body> </html>1 point
-
Hi all, To those who have asked questions, just noting here that I have seen them and will answer you when I get a bit of time. I will respond by editing the first post, whenever possible, just so we have Q&As in one easy-to-find location. Thanks.1 point
-
I haven't used the DataSet XML import for a while. Let me know if it needs some polishing.1 point
-
1 point
-
The module source is below. Example usage: a checkbox on a contact form (using Form Builder) for the user to subscribe. It's used on https://ricardo-vargas.com/contact/ EXAMPLE A method on _hooks.php. If you don't use Form Builder, use this code on your form page. $forms->addHookBefore('FormBuilderProcessor::emailForm', function($event) { $processor = $event->object; if ($processor->formName == 'contact-form') { $formData = $event->arguments(1); $contact_name = $event->sanitizer->text($formData['contact_name']); $contact_name = substr($contact_name, 0, 30); // limit length further $contact_name = $event->sanitizer->emailHeader($contact_name); $contact_email = $event->sanitizer->text($formData['contact_email']); $contact_email = $event->sanitizer->emailHeader($contact_email); $processor->emailFrom = $contact_email; //reply to $processor->emailSubject = 'Message from '.$contact_name; $form = $event->object->getInputfieldsForm(); $subscribe = $form->get('receive_updates'); $list_id = $form->get('sendy_list_id')->attr('value'); // check to see if they subscribed if ($subscribe->attr('checked')) { $success_url = '/contact'; // $fail_url = '/contact?error=1'; $ProcessSendyAPI = wire('modules')->getModule('ProcessSendyAPI'); $ProcessSendyAPI->subscribeInSendy($contact_name, $contact_email, $list_id, $success_url); } } }); MODULE https://gist.github.com/sjardim/2d834ebb0bd66d4da1ac16072f4075cd <?php namespace ProcessWire; class ProcessSendyAPI extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return array( 'title' => __('Process Sendy API'), 'summary' => __('Handle API calls to a Sendy installation'), 'author' => 'Sérgio Jardim', 'version' => '001', 'singular' => true, 'autoload' => false, 'icon' => 'envelope' ); } /** * Data as used by the get/set functions * */ protected $data = array(); /** * Default configuration for module * */ static public function getDefaultData() { return array( "sendy_api_key" => '', "sendy_installation_url" => 'http://www.example.com/sendy' ); } /** * Populate the default config data * */ public function __construct() { foreach(self::getDefaultData() as $key => $value) { $this->$key = $value; } } public static function getModuleConfigInputfields(array $data) { $data = array_merge(self::getDefaultData(), $data); $wrapper = new InputfieldWrapper(); $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'sendy_api_key'); $f->label = __('Sendy API Key', __FILE__); $f->description = __('Further instructions at https://sendy.co/api', __FILE__); $f->notes = __('Get your key at http://your_sendy_installation/settings.', __FILE__); $f->value = $data['sendy_api_key']; $wrapper->add($f); $f = wire('modules')->get('InputfieldURL'); $f->attr('name', 'sendy_installation_url'); $f->label = __('Sendy instalation URL', __FILE__); $f->description = __('Your Sendy installation URL without a trailing slash', __FILE__); $f->notes = 'http://www.example.com/sendy'; $f->value = $data['sendy_installation_url']; $wrapper->add($f); return $wrapper; } /** * [subscribeUserOrGuest description] * @param [type] $name [description] * @param [type] $email [description] * @param [type] $list_id [description] * @param [type] $success_url [description] * @param [type] $fail_url [description] * @return [type] [description] */ public function subscribeInSendy($name, $email, $list_id, $success_url = null, $fail_url = null) { $api_key = $this->data['sendy_api_key']; $sendy_url = $this->data['sendy_installation_url']; $postdata = http_build_query( array( 'name' => $name, 'email' => $email, 'list' => $list_id, 'boolean' => 'true' //set this to "true" so that you'll get a plain text response ) ); $opts = array('http' => array('method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $postdata)); $context = stream_context_create($opts); $result = file_get_contents($sendy_url.'/subscribe', false, $context); //check result and redirect if($result) { $this->wire('log')->save("newsletter", 'A new user subscribed to the site mailing list: '.$email); if($success_url) { header("Location: $success_url"); } } else { $this->wire('log')->save("error", 'Error occurred on subscribing '.$email); if($fail_url) { header("Location: $fail_url"); } } } }1 point