Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 05/23/2020 in all areas

  1. ProcessWire 3.0.157 on the development branch continues the trend of core refactoring that’s been happening quite a bit in 2020. Rather than doing a rewrite every few years (like some CMS projects) we instead refactor parts as we go, constantly improving and optimizing the core. This works because the core design/architecture is right where it needs to be, even 10 years in. But there’s always still bits of legacy code, and code that can be improved. So in the context of ProcessWire, refactoring means incrementally rewriting code on the inside, without changing its behavior on the outside (other than making it faster and/or more secure). This has been happening regularly over the last 10 years, and will likewise continue happening over the next 10 years and likely beyond. This week the code behind ProcessWire’s core Database and PageFinder classes got a major refactoring. This is some of the most used code in PW, as it handles everything involved in taking a selector and converting it to a database query. But it’s always been a little bit of a pain point for me because it had to build queries in a way that I thought wasn’t ideal, in order to make it possible for lots of different modular parts (mostly Fieldtype modules) to contribute to the query and for PageFinder to put it all together. It was fast and secure, but still one of those parts that felt like a little too much duct tape to me. But considering how crucial the code is, I’ve always been reluctant to make major changes, since it all worked just fine. Spending lots of years thinking about it (on and off), a desire to work out any pain points, and having better tools available (like Phpstorm and Tracy) made it possible to finally massage out this pain point. Some work still remains to be done, but it’s mostly there and I’m feeling good about it. Stuff like this is key for the maintenance and longevity of the core, and involved a lot of time and effort, but makes very little difference to users, even if it makes a lot of difference to me in maintaining the core. It would make a boring blog post for sure—lots of work and changes, but no new toys to show for it. Nevertheless, it has that feeling of a good house cleaning, even if you can't see it from the outside. The scope of changes here means that there may actually be new bugs to work out, so to be on the safe side, consider 3.0.157 to be a little more “beta” than the dev branches usually are. Though I’m running it here on processwire.com and it’s working well. Beyond the fairly major updates to the Database classes, there are also a few new Sanitizer convenience methods that are primarily variations on existing ones, but useful ones for sure. Thanks for reading and have a great weekend!
    18 points
  2. Great job! With the time you start appreciating such things as robust and maintainable core as much if not more than some shiny new features) And doing it little by little is surely better than a death by rewrite) Way to go, @ryan! And I'm sure you'll fix a lot of bugs by the way in addition to make a few new ones)
    4 points
  3. We recently launched the website “Resilienz-Kongress”, which is kind of an online summit / online conference about resilience (only in German language). The users can register as a participant and will receive daily emails during the conference timeframe with a conference room link for each day. Within this virtual conference room the participants can watch several interviews with experts and exchange ideas via comments. This concept is especially during the COVID-19 lockdown an interesting possibility for idea exchanges. Some technical details: • ProcessWire 3.0.155+ • ProCache • ProFields (RepeaterMatrix) • PrivacyWire • Seo Maestro • WireMail SendGrid • CodyFrame UI Framework • CopeCart (as the E-Commerce API) • Git, Node, Yarn, Gulp, PostCSS, Autoprefixer for a modern workflow combined with our individual local <-> stage <-> live environment and sync helper build with Python We heavily used the ability to specify custom Page classes (introduced in PW 3.0.152) and a lot of custom helper classes for cleaner code. Every frontend user registration with double opt-in is linked with the SendGrid API. The login process for frontend users is quite individual as we wanted to integrate a password-free login method similar to “Magic Links”. To achieve this we developed a “FrontendUserManagement” Class, which takes care of all the magic in the background. I’ll write another article about this topic as soon as I find the time, as I like the idea to implement this solution also as an open-source ProcessWire module. For the after-selling area we used CopeCart as our cart system with their IPN. With this combination we don’t have to handle the billing process at all. Also a clean cross-device affiliate tracking system between CopeCart, ProcessWire and SendGrid is integrated even though we’re currently not using the affiliate system at all. As frontend framework we used CodyFrame with an intense usage of CSS Custom Properties (for modern browsers with a classic fallback CSS for old browsers). https://www.resilienz-kongress.de/
    3 points
  4. Thank you! ? Yes, sure, I can give some examples. The workflow is getting refactored right now, so I can't show any specific details, but I'll describe the process. A quite simple example for a tiny helper class is this Headline Class for rendering headlines without risking to use more than one <h1> tag per page call: I like to use this on content heavy pages (e.g. within RepeaterMatrix Elements) where I want to be sure to not have more than one <h1>. By default only the first call of this helper method returns the <h1> tag, all the others an <h2> (unless otherwise specified with $sizeTag). To output a headline with this, simply use <?php echo Headline::render($page->headline); ?> I also have some helpers for rendering responsive (lazy loading) images or buttons and in this project most of the business logic of the "magic link" login is within one of these helper functions. Currently I require these classes manuelly within the ready.php include_once($_SERVER['DOCUMENT_ROOT'] . "/site/templates/helpers/Headline.php"); include_once($_SERVER['DOCUMENT_ROOT'] . "/site/templates/helpers/Image.php"); include_once($_SERVER['DOCUMENT_ROOT'] . "/site/templates/helpers/Link.php"); include_once($_SERVER['DOCUMENT_ROOT'] . "/site/templates/helpers/FrontendUserManagement.php"); include_once($_SERVER['DOCUMENT_ROOT'] . "/site/templates/helpers/CopeCartHandler.php"); But I definitely want to dig deeper into autoloading them. I want to refactor especially my Link and Image helper soon, perhaps I'll explain them afterwards further... As example for the custom Page Class here is an extract from my SpeakerPage: My Workflow Local <-> Stage <-> Live Our webserver uses Plesk Onyx as the hosting control panel. For each web project we create a separate "subscription" where the stage and live domain will live. For the dev / stage domain we use the plesk integrated git repo as dev branch. The live domain gets connected to the master branch of the same repo. As we exclude a lot of files in git (e.g. the whole /wire/ directory or the assets and module directories) we had to find a solution to copy them between stages. I'll come to that point later again. In my local environment I'm using MAMP Pro for the server environment. After pulling everything from the dev branch, we need also pull the database and exluded files / directories. To do so, the python script connects itself via SSH to the server and dumps the database, creates the same database locally and imports its contents. The files / directories will be pulled as zip files and extracted locally. Our first try here was to use rsync, but with asset-heavy pages this can take quite a while to copy... During development we usually do all the database-related stuff in the staging environment and just pull the database with our Python CLI script. When we need to pull also modules or the wire dir, our CLI script can handle that as well. Our content-editors mostly work in the staging environment ( on new web projects) or directly in the live environment (on existing projects). I'll definitely write an in-depth article about this, as soon as we have refactored it completely! Then I can also show some code snippets or even publish it, we'll see.
    3 points
  5. No problem! I'll try to work around the lack of Toggle inputfield (rather than make it required), just so it's usable on more versions of PW :)
    2 points
  6. Maybe a bit OT, but if you do not use sessions, your visits do not alter the database, it looks like you are only serving static html and assets to your regular visitors. So are you already using ProCache? You know, with it you completely bypass the DB-server and the PHP engine. (?)
    2 points
  7. As I get it, you just want to show a list of some pages in the page edit screen, without the need to interact with them. I think you need not use Page Reference field for that, but something like RuntimeMarkup.
    2 points
  8. I've been using Fathom Analytics for a while now and on a growing number of sites, so thought it was about time there was a PW module for it. WayFathomAnalytics WayFathomAnalytics is a group of modules which will allow you to view your Fathom Analytics dashboard in the PW admin panel and (optionally) automatically add and configure the tracking code on front-end pages. Links GitHub Readme & documentation Download Zip Modules directory Module settings screenshot What is Fathom Analytics? Fathom Analytics is a simple, privacy-focused website analytics tool for bloggers and businesses. Stop scrolling through pages of reports and collecting gobs of personal data about your visitors, both of which you probably don't need. Fathom is a simple and private website analytics platform that lets you focus on what's important: your business. Privacy focused Fast-loading dashboards, all data is on a single screen Easy to get what you need, no training required Unlimited email reports Private or public dashboard sharing Cookie notices not required (it doesn't use cookies or collect personal data) Displays: top content, top referrers, top goals and more
    1 point
  9. Developing custom fields for ProcessWire is great! And it's easier than many might think once you get the basic concepts. But it's hard to learn those concepts by reading the code and doing some trial and error... That's why I think we need a good tutorial about that topic. It took me quite long, but now I feel knowledgeable enough to write such a tutorial. I also have the idea (or the need) for some new fields that might be helpful to the community. What I don't have is time ? So I thought to share the workload and share my knowledge while the development of the module (and testing, writing docs, etc) could be done by someone else under my supervision (hope that does not sound scary ? ). What do you think? I'm happy to hear your opinions - we are in the PUB ? Have a great week and happy coding!
    1 point
  10. Hello, Do you think it would be possible to build a "Macro Rocorder"? A module where we click on a button and every action on admin will be saved to a file (may be with the aftersave Hook) with every detail in a way that all steps could be repeated when ever we want. Imagine you have crm project with a huge amount of templates and fields and want to reuse it on new projects... Such a solution would help. Is there something like this? If not is this possible?
    1 point
  11. You are looping through the repeater field items (buildings) but not their images (one or many per building) as well. By default an 'images' field can hold multiple images and you simply loop through them. If the only field in your repeater is 'r_galeria_image' then it's unnecessary to have a repeater. foreach ($page->r_galeria_image as $image) { $thumbnail = $image->size(450, 250); echo"<article>"; echo "<a href='{$image->url}' class='fresco' data-fresco-caption='{$image->description}' data-fresco-group-options='preload: [1,2], effects: {spinner: {show:150, hide:150 } }' data-fresco-group='kolace'><img src='$thumbnail->url' alt='obrazok'></a>"; echo"</article>"; }
    1 point
  12. Hi @Klenkes - it's because the modue is using InputfieldToggle, which only arrived in PW 3.0.139 - sorry! Feel free to update your site to .139 or later if you're able or want to, but I'll update the code to use checkboxes when the toggle input isn't available.
    1 point
  13. @HMCB Not a lot left to be done for this particular update. Mostly just details as I come across them. For instance, this morning I noticed that FieldtypeMulti needs some of the same updates I applied to Fieldtype, but nothing that's going to change the way it works or how it behaves. So while not particularly urgent, I just want to cover all the related updates while still fresh on the mind.
    1 point
  14. The read is as cool as the site @joshua! Could you share some more details (and maybe even code) about this: and this:
    1 point
  15. Love this Ryan. Are you about a couple of weeks away from finishing? You noted there needs a little work to be done.
    1 point
  16. 1 point
  17. @bernhard https://all-inkl.com/ https://kasapi.kasserver.com/dokumentation/ I wrote a PHP class (SOAP API client wrapper) which is used by the ProcessWire module KasProcessEmail to connect with the API.
    1 point
  18. Sounds interesting. Which hoster are you using? Links to the api please ?
    1 point
  19. We've been doing basically the same thing but like this: $selector .= ", has_started=(start_date=''), has_started=(start_date<=today)"; $selector .= ", not_ended=(end_date=''), not_ended=(end_date>today)"; It seems to work ok. I don’t know of an easier way.
    1 point
  20. Thank you for this module! It best for my mobile apps!
    1 point
  21. I've seen a couple of questions regarding namespaces and autoloading floating around the forum recently, so I decided to write a little tutorial. In general, I often see people getting confused when they try to wrap their head around namespaces, autoloading, Composer and the mapping of namespaces to directory structures all at once. In fact, those are very much independent, distinct concepts, and it is much easier to explain and understand them separately. So this guide is structured as follows: How namespaces work in PHP. How autoloading works in PHP. Conventions for mapping namespaces to directory structures: PSR-4. How autoloading works in Composer and ProcessWire's class loader. How to use the class loader in a ProcessWire module. Feel free to skip the sections you're already familiar with. Namespaces in PHP The purpose of namespaces in PHP is to avoid naming conflicts between classes, functions and constants, especially when you're using external libraries and frameworks. Nothing more. It's important to understand that this has nothing at all to do with autoloading, directory structures or file names. You can put namespaced stuff everywhere you want. You can even have multiple namespaces inside a single file (don't try this at home). Namespaces only exist to be able to use a generic name – for example, ProcessWire's Config class – multiple times in different contexts without getting a naming conflict. Without namespaces, I couldn't use any library that includes a Config class of it's own, because that name is already taken. With namespaces, you can have a distinction between the classes ProcessWire\Config and MoritzLost\Config. You can also use sub-namespaces to further segregate your code into logical groups. For example, I can have two classes MoritzLost\Frontend\Config and MoritzLost\Backend\Config– a class name only needs to be unique within it's namespace. You can declare the namespace for a PHP file using the namespace statement at the top: // file-one.php <?php namespace ProcessWire; // file-two.php <?php namespace MoritzLost\Frontend; This way, all classes, methods and constants defined inside this file are placed in that namespace. All ProcessWire classes live in the ProcessWire namespace. Now to use one of those classes – for example, to instantiate it – you have a couple of options. You can either use it's fully qualified class name or import it into the current namespace. Also, if you are inside a namespaced file, any reference to a class is relative to that namespace. Unless it starts with a backward slash, in this case it's relative to the global namespace. So all of those examples are equivalent: // example-one.php <?php namespace ProcessWire; $page = new Page(); // example-two.php <?php use ProcessWire\Page; $page = new Page(); // example-three.php <?php $page = new ProcessWire\Page(); // example-four.php <?php namespace MoritzLost\Somewhere\Over\The\Rainbow; $page = new \ProcessWire\Page(); The use statement in the second example can be read like this: “Inside this file, all references to Page refer to the class \ProcessWire\Page” How autoloading works Every PHP program starts with one entry file – for ProcessWire, that's usually it's index.php. But you don't want to keep all your code in one file, that would get out of hand quickly. Once you start to split your code into several individual files however, you have to take care of manually including them with require or include calls. That becomes very tedious as well. The purpose of autoloading is to be able to add new code in new files without having to import them manually. This, again, has nothing to do with namespaces, not even something with file locations. Autoloading is a pretty simple concept: If you try to use a class that hasn't been loaded yet, PHP calls upon it's registered autoloaders as a last-ditch attempt to load them before throwing an exception. Let's look at a simple example: // classes.php <?php class A { /** class stuff */ } class B { /** class stuff */ } // index.php <?php spl_autoload_register(function ($class) { include_once 'classes.php'; }); new A(); new B(); This is a complete and functional autoloader. If you don't believe me, go ahead and save those two files (classes.php and index.php) and run the index.php with php -f index.php. Then comment out the include_once call and run it again, then you'll get an error that class A was not found. Now here's what happens when index.php is executed (with the autoloader active): Our anonymous function is added to the autoload queue through spl_autoload_register. PHP tries to instantiate class A, but can't because it's not loaded yet. If there was no autoloader registered, the program would die with a fatal error at this point. But since there is an autoloader ... The autoloader is called. Our autoloader includes classes.php with the class definition. That was a close one! Since the class has been loaded, execution goes back to the index.php which can now proceed to instantiate A and B. If the class was still not loaded at this point, PHP would go back to the original plan and die. One thing to note is that the autoloader will only be called once in this example. That's because both A and B are in the same file and that file is included during the first call to the autoloader. Autoloading works on files, not on classes! The important takeaway is that PHP doesn't know if the autoloader knows where to find the class it asks for or, if there are multiple autoloader, which one can load it. PHP just calls each registered autoloader in turn and checks if the class has been loaded after each one. If the class still isn't loaded after the last autoloader is done, it's error time. What the autoloader actually does is pretty much wild wild west as well. It takes the name of the class PHP is trying to load as an argument, but it doesn't have to do anything with it. Our autoloader ignores it entirely. Instead, it just includes classes.php and says to itself “My job here is done”. If class A was in another file, it wouldn't have worked. This process has two main advantages: Since autoloaders are only called on-demand to load classes just in time, we only include the files we actually need. If in the example above class A and B are not used in some scenarios, the classes.php will not be included, which will result in better performance for larger projects (though this isn't as cut and dry, since autoloading has it's own overhead, so if you load most classes anyway during a single request, it will actually be less efficient). If the autoloader is smart enough to somehow map class names to the files they're located in, we can just let the autoloader handle including the classes we need, without having to worry about jamming include statements everywhere. That brings us to ... PSR-4, namespaces and directory structures As you see, namespaces and autoloading are both pretty limited concepts. And they aren't inherently linked to each other. You can namespace your classes without ever adding an autoloader, and you can autoload classes that are all in the same namespace. But they become useful when you put them together. At the core of all that autoloading talk is a simple idea: By putting classes in files named after their class names, and putting those files in directory hierarchies based on the namespace hierarchy, the autoloader can efficiently find and load those files based on the namespace. All it needs is a list of root namespaces with their corresponding directories. The exact way class names and namespaces are mapped to directory structures and file names is purely conventional. The accepted convention for this is PSR-4. This is a super simple standard which basically just sums up the ideas above: A base namespace is mapped to a specific directory in the file system. When the autoloader is asked to load a class in that namespace (or a sub-namespace of it), it starts looking in that folder. This "base" namespace may include multiple parts – for example, I could use MoritzLost\MyAwesomeLibrary as a base and map that to my source directory. PSR-4 calls this a "namespace prefix". Each sub-namespace corresponds to a sub-directory. So by looking at the namespace, you can follow subdirectories to the location where you expect to find the class file. Finally, the class name is mapped directly to the file name. So MyCoolClass needs to be put inside MyCoolClass.php. This all sounds simple and straightforward - and it absolutely is! It's only once you mash everything together, mix up language features, accepted conventions and proprietary implementations like Composer on top that it becomes hard to grasp in one go. Composer and ProcessWire's class loader Now all that's left is to talk about how Composer and ProcessWire provide autoloading. Composer, of course, is primarily a tool for dependency management. But because most libraries use namespaces and most developers want to have the libraries they're using autoloaded, those topics become a prerequisite to understanding what Composer does in this regard. Composer can use different autoloading mechanisms; for example, you can just give it a static list of files to include for every request, or use the older PSR-0 standard. But most modern libraries use PSR-4 to autoload classes. So all Composer needs to function is a mapping of namespace prefixes to directories. Each library maintains this mapping for it's PSR-4-structured classes through the autoload information in their composer.json. You can do this for your own site to: Just include the autoload information as shown in the documentation and point it to the directory of your class files. Composer collects all that information and uses it to generate a custom file at vendor/autoload.php — that's the one you need to include somewhere whenever you set up Composer in one of your projects. Bells and whistles aside, this file just registers an autoloader function that will use all the information collected from your own and your included libraries' composer.json to locate and include class files on demand. You can read more about how to optimize Composer's autoloader for production usage here. If you want to read up on how to set up Composer for your own sites, read my ProcessWire + Composer integration guide instead. And finally, what does ProcessWire do to handle all this? Turns out, ProcessWire has it's own autoloader implementation that is more or less PSR-4 compliant. You can access it as an API variable ($classLoader or wire('classLoader'), depending on context). Instead of using a static configuration file like Composer, the namespace -> directory mapping is added during the runtime by calling $classLoader->addNamespace. As you would expect, this function accepts a namespace and a directory path. You can use this to register your own custom namespaces. Alternatively, if you have site-specific classes within the ProcessWire namespace, you can just add their location to the class loader using the same method: $classLoader->addNamespace('ProcessWire', '/path/to/your/classes/'). Utilizing custom namespaces and autoloading in ProcessWire modules Now as a final remark, I wanted to give an example of how to use custom namespaces and the class loader in your own modules. I'll use my TrelloWire module as an example: Decide what namespace you're going to use. The main module file should live in the ProcessWire namespace, but if you have other classes in your module, they can and should use a custom namespace to avoid collisions with other modules. TrelloWire uses ProcessWire\TrelloWire, but you can also use something outside the ProcessWire namespace. You need to make sure to add the namespace to the class loader as early as possible. If either you or a user of your module tries to instantiate one of your custom classes before that, it will fail. Good places to start are the constructor of your main module file, or their init or ready methods. Here's a complete example. The module uses only one custom namespaced class: ProcessWire\TrelloWire\TrelloWireApi, located in the src/ directory of the module. But with this setup, I can add more classes whenever I need without having to modify anything else. /** * The constructor registers the TrelloWire namespace used by this module. */ public function __construct() { $namespace = 'ProcessWire\\TrelloWire'; $classLoader = $this->wire('classLoader'); if (!$classLoader->hasNamespace($namespace)) { $srcPath = $this->wire('config')->paths->get($this) . 'src/'; $classLoader->addNamespace($namespace, $srcPath); } } Source Thanks for making it through to the very end! I gotta learn to keep those things short. Anyway, I hope this clears up some questions about namespaces and autoloading. Let me know if I got something wrong, and feel free to add your own tips and tricks!
    1 point
  22. Hi huseyin, 1. Use the appropriate Sanitizer methods to test user input from post and get. Depending on the circumstance, you'll either want to validate (reject it completely if there's something wrong with it), or filter/sanitize it (accept it but strip out unwanted characters). If doing both filter and validate, do your validation AFTER your filtering. 2. Even more important than step #1 is to use escaping on your output. This means using htmlspecialchars() or htmlentities() or $sanitizer->entities() when you output any field from the database or user input to the page (if you have htmlentities setup on your field's output formatting, then you can skip this step for those fields). Even if you mess up on the filter/validation from #1, as long as you've escaped all of the html, you should be ok. 3. When using user input (get or post variables) inside ProcessWire selector strings, use the Sanitizer::selectorValue() method on the value first. Even better, just use Selector Arrays since selectorValue can sometimes strip out characters (quotes and commas) that you actually want to search for. 4. If you're using any SQL directly, you must use prepared statements to bind any user input, which automatically escapes the input for SQL. 5. For protection against Cross Site Request Forgery (CSRF), use ProcessWire's SessionCSRF class when building custom forms. See https://processwire.com/api/ref/session-c-s-r-f/ for details on how to use this. 6. Don't use GET for secret data (passwords, security codes, etc). That data can get picked up by browser extensions or appear in server logs that might get compromised. 7. Use SSL/https on your whole site.
    1 point
  23. Most of us know and use site/config-dev.php file. If present, it is used instead of site/config.php, so it is easy to set database connection and debug mode for local development, not touching the production config. It is also very useful when working with git. You can simply ignore it in the .gitignore file, so local settings won’t end up in the repo. But sometimes you need to add code to site/ready.php or site/init.php just for the dev environment. For example, to add ryan’s super cool on demand images mirrorer. I can’t live without it when working with big sites, which have more assets then I want to download to my desktop. It would be great if there was something like site/ready-dev.php for this. Not out-of-the-box, but it’s pretty easy to achieve. Unlike site/config-dev.php, site/ready.php is not hardcoded. It’s name is set with a special config setting: // wire/config.php $config->statusFiles = array( 'boot' => '', 'initBefore' => '', 'init' => 'init.php', 'readyBefore' => '', 'ready' => 'ready.php', 'readySite' => '', 'readyAdmin' => '', 'render' => '', 'download' => '', 'finished' => 'finished.php', 'failed' => '', ); As you can see, we can not only define, which files are loaded on init, ready and finished runtime states, but probably even add more if we need to. So we override this setting in site/config-dev.php like this: // site/config-dev.php // Change ready.php to ready-dev.php $temp = $config->statusFiles; $temp['ready'] = 'ready-dev.php'; $config->statusFiles = $temp; For some reason we can’t just do $config->statusFiles['ready'] = 'ready-dev.php'; and have to override the whole array. Maybe you PHP gurus can explain this in the comments. Now we can create the site/ready-dev.php file and place all the dev-only code there. Important thing is to include the main site/ready.php. // site/ready-dev.php include 'ready.php'; // DEV HOOK TO MIRROR ASSETS ON DEMAND $wire->addHookAfter('Pagefile::url, Pagefile::filename', function($event) { $config = $event->wire('config'); $file = $event->return; if($event->method == 'url') { // convert url to disk path $file = $config->paths->root . substr($file, strlen($config->urls->root)); } if(!file_exists($file)) { // download file from source if it doesn't exist here $src = 'https://mysite.com/site/assets/files/'; $url = str_replace($config->paths->files, $src, $file); $http = new WireHttp(); try { $http->download($url, $file); } catch (\Exception $e) { bd($file, "Missing file"); } } }); Do not forget to replace "mysite.com" if you’re copypasting this)) Now, add the newly created file to the `.gitignore` and we’re done. # .gitignore # Ignore dev files site/config-dev.php site/ready-dev.php Thanks for reading!
    1 point
  24. Hi Ivan, very nice tip. I haven't known about this. May come in handy. Thank you for sharing. ? Regarding your question on overwriting single items of config arrays, you can do it this way: $config->statusFiles('ready', 'ready-dev.php'); Also, if I want to overwrite more then one item, I prefer this way: $config->statusFiles = array_merge($config->statusFiles, [ 'init' => 'init-dev.php', //'readyBefore' => '', 'ready' => 'ready-dev.php', ]);
    1 point
  25. @DV-JF Kongongdo has outlined in his first post the billing / license structure will be similar to Ryan's pro modules, and that current Padloper users will get an upgrade discount on release. No mention of free upgrades however, and no way of knowing the actual pricing of upcoming Padloper version or when it will be released. Just my opinion but if you have experience with current Padloper seems like a no-brainer to me, you can charge the license cost on to your client for less than a month or two of shopify.
    1 point
  26. Password Generator Adds a password generator to InputfieldPassword. Usage Install the Password Generator module. Now any InputfieldPassword has a password generation feature. The settings for the generator are taken automatically from the settings* of the password field. *Settings not supported by the generator: Complexify: but generated passwords should still satisfy complexify settings in the recommended range. Banned words: but the generated passwords are random strings so actual words are unlikely to occur. https://modules.processwire.com/modules/password-generator/ https://github.com/Toutouwai/PasswordGenerator
    1 point
×
×
  • Create New...