Leaderboard
Popular Content
Showing content with the highest reputation on 12/01/2019 in all areas
-
Although the PW backend is really intuitive, ever so often my clients need some assistance. Be it they are not so tech savvy or they are not working in the backend often. For those cases it is nice to make some help videos available to editors. This is what this module does. ProcessHelpVideos Module A Process module to display help videos for the ProcessWire CMS. It can be used to make help videos (screencasts) available to content editors. This module adds a 'Help Videos" section to the ProcessWire backend. The help videos are accessible through an automatically created page in the Admin page tree. You can add your help videos as pages in the page tree. The module adds a hidden page to the page tree that acts as parent page for the help video pages. All necessary fields and templates will be installed automatically. If there are already a CKEditor field and/or a file field for mp4 files installed in the system, the module will use those. Otherwise it will create the necessary fields. Also the necessary templates for the parent help videos page and it's children are created on module install. The module installs a permission process-helpvideos. Every user role that should have access to the help video section, needs this permission. I use the help video approach on quite a few production sites. It is stable so far and well received by site owners/editors. Up until now I installed required fields, templates and pages manually and then added the module. Now I added all this logic to the install method of the module and it should be ready to share. The module and further description on how to use it is available on github: https://github.com/gebeer/ProcessHelpVideos If you like to give it a try, I am happy to receive your comments/suggestions here.4 points
-
This week we’ll take a quick break from core updates and have a look at a new module called UserActivity, just added to the ProcessWire ProDevTools package— https://processwire.com/blog/posts/user-activity-module/3 points
-
@Robin S Thanks for your feedback. I'd not taken the public API very far on this yet because I wasn't sure exactly what people would want, so it's useful for me to hear what you are looking for here. Beyond the API functions mentioned in the blog post, there actually is more, but it's lower-level, returning arrays of IDs and stuff rather than Users or Pages. But it has a good foundation to build upon, so I will plan to add some more methods like the one you've mentioned. If you are interested in doing it now, here's a function that would find what you are looking for with the UserActivity module: /** * Find active users * * @param int $seconds Find active within this many seconds (default=90) * @param string $roleName Name of role to find or omit for no role filter * @return PageArray * */ function findActiveUsers($seconds = 90, $roleName = '') { $activeUsers = new PageArray(); $role = $roleName ? wire('roles')->get($roleName) : null; $module = wire('modules')->get('UserActivity'); $options = [ 'seconds' => $seconds ]; foreach($module->findActivity($options) as $item) { $user = wire('users')->get($item['uid']); if(!$user->id || ($role && !$user->hasRole($role))) continue; $page = wire('pages')->get($item['pid']); // set some properties to $user in case you want them $user->set('activePath', $item['act']); $user->set('activeProcess', $item['process']); $user->set('activePage', $page); $activeUsers->add($user); } return $activeUsers; } // Example $editors = findActiveUsers(90, 'editor'); foreach($editors as $u) { echo "<p>User $user->name is at $user->activePath</p>"; } @bernhard I hadn't considered that particular situation, though I wouldn't recommend sharing logins like that. But technically it would be possible to support the situation with this module since those logins would have different session IDs. I'm happy to add it if it's something people are doing. But regardless of whether using this module or not, if you've got an installation where people are sharing logins I would recommend changing it so each user has their own login, otherwise it would be impossible to know who changed what, and one person changing the password could lock out the other users, etc.2 points
-
I had a play around and this hook seems to do the job: Edit: the hook doesn't work because the selected pages are lost on page save. Maybe related to the discussion in this issue. $wire->addHookBefore('InputfieldPage::render', function(HookEvent $event) { /* @var InputfieldPage $inputfield */ $inputfield = $event->object; $page = $inputfield->hasPage; $field = $inputfield->hasField; // Return early if inputfield is not in a Repeater page if(!$page instanceof RepeaterPage) return; $selector = $inputfield->findPagesSelector; // Return early if no selector or selector doesn't include a dependency if(!$selector || strpos($selector, '=page.') === false) return; // Get suffix added to inputfields in this Repeater page $suffix = str_replace($field->name, '', $inputfield->name); // Add the suffix to dependency inputfield names $selector = preg_replace('/(=page.[_a-zA-Z0-9]+)/', "$1{$suffix}", $selector); // Replace the original findPagesSelector $inputfield->findPagesSelector = $selector; }); Demo... Page tree: Selector string for Subcategory field: Page Edit:2 points
-
LOVE the new module! I'm adding this to every site for sure. ? In terms of alternatives, besides the SystemNotifications module mentioned in the post there is also Page Edit Soft Lock, but UserActivity is more powerful. A question: is it possible to use this module via the API to find all users currently logged in by role? Something like (pseudo-code): $logged_in_editors = $userActivity->findLoggedIn("role=editor"); I think that could be useful. Thanks @ryan!2 points
-
Hello. I'd somewhat rewritten WireMailMailgun a while back, to implement some more features and implement PW coding guidelines etc. My version has been somewhat in limbo, although there's been various discussions in the thread linked above. I've decided to release my version as a separate module called WireMailgun, as it has breaking changes and a slightly different implementation. I've also got it using the email validation v4 endpoint. For simple usage, either module will do what you need it to do. If you need to do some more advanced things e.g inline images or adding data, this module will help you with that. Here's the readme... WireMail Mailgun Extends WireMail to use the Mailgun API for sending emails. Installation Download the zip file at Github or clone the repo into your site/modules directory. If you downloaded the zip file, extract it in your sites/modules directory. In your admin, go to Modules > Refresh, then Modules > New, then click on the Install button for this module. API Prior to using this module, you must set up a domain in your Mailgun account to create an API key. Add the API key and domain to the module's settings. Usage Usage is similar to the basic WireMail implementation, although a few extra options are available. Please refer to the WireMail documentation for full instructions on using WireMail, and to the examples below. Extra Methods The following are extra methods implemented by this module: Chainable The following methods can be used in a chained statement: cc(string|array|null $email) - Set a "cc" email address. Only used when $batchMode is set to false. Please refer to WireMail::to() for more information on how to use this method. bcc(string|array|null $email) - Set a "bcc" email address. Only used when $batchMode is set to false. Please refer to WireMail::to() for more information on how to use this method. addData(string $key, string $value) - Add custom data to the email. See https://documentation.mailgun.com/en/latest/user_manual.html#attaching-data-to-messages for more information. addInlineImage(string $file, string $filename) - Add an inline image for referencing in HTML. Reference using "cid:" e.g. <img src='cid:filename.ext'> Requires curl_file_create() (PHP >= 5.5.0) See https://documentation.mailgun.com/en/latest/user_manual.html#sending-inline-images for more information. addRecipientVariables(array $recipients) - Add recipient variables. $recipients should be an array of data, keyed by the recipient email address See https://documentation.mailgun.com/en/latest/user_manual.html#batch-sending for more information. addTag(string $tag) - Add a tag to the email. Only ASCII allowed Maximum length of 128 characters There is a maximum number of 3 tags allowed per email. addTags(array $tags) - Add tags in a batch. setApiKey(string $apiKey) - Override the Mailgun API Key module setting. setBatchMode(bool $batchMode) - Enables or disables batch mode. This is off by default, meaning that a single email is sent with each recipient seeing the other recipients If this is on, any email addresses set by cc() and bcc() will be ignored Mailgun has a maximum hard limit of recipients allowed per batch of 1,000. Read more about batch sending. setDeliveryTime(int $time) - The (unix)time the email should be scheduled for. setDomainName(string $domain) - Override the "Domain Name" module setting. setRegion(string $region) - Override the "Region" module setting. Valid regions are "us" and "eu" Fails silently if an invalid region is passed setSender(string $domain, string $key) - Set a different API sender than the default. The third argument is $region which is optional A shortcut for calling setDomainName(), setApiKey() and setRegion() setTestMode(bool $testMode) - Override the "Test Mode" module setting. setTrackOpens(bool $trackOpens) - Override "Track Message Opens" module setting on a per-email basis. Open tracking only works for emails with bodyHTML() set setTrackClicks(bool $trackClicks) - Override "Track Message Clicks" module setting on a per-email basis. Click tracking only works for emails with bodyHTML() set Other send() - Send the email. Returns a positive number (indicating number of emails sent) or 0 on failure. validateEmail(string $email) - Validates a single address using Mailgun's address validation service. Returns an associative array. To return the response as an object, set the second argument to false For more information on what this method returns, see Mailgun's documentation. getHttpCode() - Get the API HTTP response code. A response code of 200 indicates a successful response Examples Basic Example Send an email: $mg = $mail->new(); $sent = $mg->to("user@domain.com") ->from("you@company.com") ->subject("Message Subject") ->body("Message Body") ->send(); Advanced Example Send an email using all supported WireMail methods and extra methods implemented by WireMailgun: $mg = $mail->new(); // WireMail methods $mg->to([ "user@domain.com" => "A User", "user2@domain.com" => "Another User", ]) ->from("you@company.com", "Company Name") ->replyTo("reply@company.com", "Company Name") ->subject("Message Subject") ->bodyHTML("<p>Message Body</p>") // A text version will be automatically created ->header("key1", "value1") ->headers(["key2" => "value2"]) ->attachment("/path/to/file.ext", "filename.ext"); // WireMailgun methods $mg->cc("cc@domain.com") ->bcc(["bcc@domain.com", "bcc2@domain.com"]) ->addData("key", "value") // Custom X-Mailgun-Variables data ->addInlineImage("/path/to/file-inline.jpg", "filename-inline.jpg") // Add inline image ->addTag("tag1") // Add a single tag ->addTags(["tag2", "tag3"]) // Add tags in a batch ->setBatchMode(false) // A single email will be sent, both "to" recipients shown ->setDeliveryTime(time() + 3600) // The email will be delivered in an hour ->setSender($domain, $key, "eu") // Use a different domain to send, this one in the EU region ->setTestMode(true) // Mailgun won't actually send the email ->setTrackOpens(false) // Disable tracking opens ->setTrackClicks(false); // Disable tracking clicks // Batch mode is set to false, so 1 returned if successful $numSent = $mg->send(); echo "The email was " . ($numSent ? "" : "not ") . "sent."; Validate an Email Address $mg = $mail->new(); $response = $mg->validateEmail("user@domain.com", false); if($mg->getHttpCode() == 200) { echo $response->result == "deliverable" ? "Valid" : "Not valid"; } else { echo "Could not validate"; } I hope it is useful! Cheers, Chris1 point
-
A module created in response to the topic here: Page List Select Multiple Quickly Modifies PageListSelectMultiple to allow you to select multiple pages without the tree closing every time you select a page. The screencast says it all: https://github.com/Toutouwai/PageListSelectMultipleQuickly https://modules.processwire.com/modules/page-list-select-multiple-quickly/1 point
-
Checkout You Might Not Need jQuery for this, it will tell you some replacements for the most common jQuery operations! Here's a rough draft: function more(url) { window.location = url; } document.querySelectorAll('a').forEach( link => link.addEventListener('click', e => { e.preventDefault(); const href = e.currentTarget.href; document.body.classList.add('fade-out'); setTimeout(() => more(href), 500); }); ); Note that the callback to fadeOut in your code is "open" while the reload function is called "more", is that a mistake? Anyway, I corrected it. Note you have to add a CSS transition for the "fade-out" class yourself, check the link above for an easy example. Also make sure that the timeout (500ms in my example) matches the duration of the CSS transition. Cheers! Sidenote, if all you're doing is delaying the page reload by a couple hundred miliseconds to have the body fadeout, you might as well get rid of that, it only gets in the way of the user without any real benefit. But I don't know anything about your use-case, so I might be way off here!1 point
-
@bernhard I had looked into that and having the same page open in 2-windows/tabs in the same session is not a case the module can reliably alert you to. Sessions are not unique per browser window/tab, and browsers don't provide any indication about what window/tab a request comes from. The only foolproof way to detect it is to provide some variable unique to the window/tab in the query string (like ?window_id=123), and then make sure that variable stays in all URLs clicked on or posted to from that point forward. It's not practical, and also breaks as soon as you open a target=_blank window or cmd-click force a request to open in a new window/tab on your own.1 point
-
This was the code I used to replace all items, just in case anyone needs the same in the future. ? $blog_pages = $pages->find("template=blog, include=all"); foreach($blog_pages as $p) { foreach($p->content_repeater as $item) { if($item->type == 'rm_text') { $item->of(false); $string = $item->body; $pattern = '[\/site\/assets\/files\/(\d){1,20}\/]'; $replacement = '/site/assets/files/' . $p->id . '/'; $new_string = preg_replace($pattern, $replacement, $string); $item->body = $new_string; $item->save(); } } } Note: Don't forget to disable 'Fix broken images' under HTML options under your body content field, for the images not to be stripped from the output!1 point
-
Now that PHP 7.4 is stable, I have updated the Tracy core to the latest version and also made a few other fixes to the module for PHP 7.4. Please let me know if you notice any problems. Note that this issue in the PW core (https://github.com/processwire/processwire-issues/issues/1041) currently breaks the RequestInfo panel on 7.4.1 point
-
1 point
-
Here is a more detailed answer with the steps you have to take: Download the ProcessWire Zip (or use one of the other install methods) Unzip the file in your web root and rename it to the name how your project should be named Open Laragon control panel Click "reload" button for Apache This automatically creates a virtual host with the name of your folder like "processwire.dev" (I don't remember what is the default domain of Laragon, because I changed it to .localhost) You can then navigate in your browser to this URL You will see the ProcessWire installer and just have to follow the steps1 point
-
I use an IDE and found it stressful to have to work-around deploying config.php This is how I found your thread. Thank you! I integrated your solution into my own workflow and came up with this to share back here: /site/config.php (like @bernhard except last line) // include config file from outside of the repo $client = 'config-' . end(explode('/',__FILE__, -2)); include("../$client.php"); The PHP end() function and the minus 2 in the explode(), always gets the URI domain base. (As long as PW keeps it there.) Since my usual deployment flow is from ABC.dev -> test.mybox.com/ABC -> ABC.com ...in my root folder, safely out of reach (like @bernhard's concept), I then create 3 files each containing config deployment differences. ../config-ABC.dev.php and ../config-ABC.php and ../config-ABC.com.php For those also using PHPStorm, since these files are located out of project, I would add them in Favorites. (And add it's root folders in Deployment Mappings). I can then easily make use of IDE features like "Compare Two Files", "Sync with Deployed to", etc. Removing doubts about what config property I may have missed across the board. No more stress.1 point
-
I had a look at this, and it seems it isn't possible to directly set the optionAttributes property of inputfields that extend InputfieldSelect. That is probably why @kixe made this module. But what you can do is use a hook to remove the existing options, and then add them back with some attributes... $wire->addHookBefore('InputfieldCheckboxes::render', function(HookEvent $event) { $inputfield = $event->object; // Only for a specific field if($inputfield->hasField != 'your_field_name') return; $options = $inputfield->options; // Get the existing options $inputfield->options = []; // Remove all the existing options // Add the options back with attributes foreach($options as $value => $label) { // Set whatever attributes you want as $key => $value in the last argument of addOption() $inputfield->addOption($value, $label, ['disabled' => 'disabled', 'data-foo' => 'bar', 'class' => $this->sanitizer->pageName($label, true)]); } $event->return = $inputfield; });1 point