Jump to content


Popular Content

Showing content with the highest reputation since 02/19/2020 in all areas

  1. 17 points
    This week we’ve got a couple of really useful API-side improvements to the core in 3.0.151, among other updates. First we’ll take a look at a new $config setting that lets you predefine image resize settings, enabling you to isolate them from code that outputs them, not unlike how you isolate CSS from HTML. Following that, we’ll introduce and show you how to use a handy addition to our static language translation functions. If you’ve ever come across the term “abandoned translation”, you’ll really like what this adds— https://processwire.com/blog/posts/pw-3.0.151/
  2. 14 points
    Plenty of posts on the forum relating to Content Security Policy (CSP) and how to integrate it with Processwire. It's not too hard to implement a decent htaccess CSP that will get you a solid B+ at Mozilla Observatory. If you're after A+ it's a little harder because of all the back-end stuff... until you realize it's surprisingly easy. After a lot of testing, the easiest way I found was to specify only what is needed in the htaccess and then add your required CSP as a meta in your page template. Plenty of people have suggested similar. Works very easily for back-end vs front-end, but gets complicated if you want front page editing. Luckily, a little php will preserve back-end and front page editing capabilities while allowing you to lock down the site for anyone not logged in. None of this is rocket science, but CSPs are a bit of a pain the rear, so the easier the better, I reckon 😉 The only CSP I'd suggest you include in your site htaccess is: Header set Content-Security-Policy "frame-ancestors 'self'" The reason for this is you can't set "frame-ancestors" via meta tags. In addition, you can only make your CSP more restrictive using meta tags, not less, so leaving the back-end free is a solid plan to avoid frustration. Then in your public front-facing page template/s, add your desired Content Security Policy as a meta tag. Please note: your CSP should be the first meta tag after your <head>. For example: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Security-Policy" content="Your CSP goes here"> <!-- followed by whatever your normal meta tags are --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="format-detection" content="telephone=no"> If you haven't got Front Page Editing enabled, this works fine by itself. Just one extra step is needed to make sure you don't have to worry either way. The easiest way I found to allow both CSP and front page editing capabilities is the addition of a little php, according to whatever your needs are. Basically, if the user is a guest, throw in your CSP, if they're not do nothing. It's so simple I could have kicked myself when it finally dawned on me. I wish it had clicked for me earlier in my testing, but it didn't so I'm here to try to save some other person a little time. Example: <!DOCTYPE html> <html> <head> <?php if ($user->isGuest()): ?> <meta http-equiv="Content-Security-Policy" content="Your CSP goes here"> <?php endif; ?> <!-- followed by whatever your normal meta tags are --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="format-detection" content="telephone=no"> If you want it a bit more involved then you can add additional tests and be as specific as you like about what pages should get which CSP. For example, the following is what I use to expand the scope of the CSP only for my "map" page: <?php $loadMap = $page->name === "map"; ?> <!DOCTYPE html> <html> <head> <?php if ($user->isGuest()): ?> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; base-uri 'self'; manifest-src 'self'; form-action 'self'; font-src 'self' data: https://fonts.gstatic.com; frame-src 'self' https://www.youtube.com; img-src 'self' data:<?php echo ($loadMap) ? " https://maps.googleapis.com https://maps.gstatic.com" : ""; ?> https://www.google-analytics.com; script-src 'self' <?php echo ($loadMap) ? "https://maps.googleapis.com " : ""; ?>https://www.google-analytics.com https://www.googletagmanager.com; style-src 'self' <?php echo ($loadMap) ? "'unsafe-inline' https://fonts.googleapis.com" : ""; ?>"> <?php endif; ?> Hope this saves someone a little time testing. https://observatory.mozilla.org/analyze/bene.net.au
  3. 12 points
    Hi guys, I was very excited for this module, but my life took a huge direction change and I no longer have the time to invest in module development. I am gonna leave the files here. You guys can take it and run. Maybe there might be something useful here. Maybe not. I still think it's a good idea to do drag and drop modal building in PW. So hopefully one day something like that can come to light. I love this community and I love ProcessWire. Live long and prosper. - Joshua Designme 2.zip
  4. 7 points
    I've kept hearing that one for the last twenty years, yet here we are. I think that's enough said 😉
  5. 5 points
    Python has one big advantage over PHP, which people might or might not care about: It‘s widely used for devops tooling because it‘s preinstalled on so many systems and it‘s growing super fast in the space of ML/AI/Science based computing because of it‘s bindings to fast low level C code while still writing python on top. If you care about those things or you want to do them as well, but not introduce a mix of technology, then sure python is a great solution. If not I don‘t see any reason to switch from whatever one is using right now.
  6. 5 points
    I usually supplied "translation keys" instead of the default translation to translation functions, and translated them in all languages, eg. __('btn_submit') instead of __('Submit'). This way they rarely needed to be changed, only when the translation changed to something completely different.
  7. 4 points
    This module is (yet another) way for implementing a cookie management solution. Of course there are several other possibilities: - https://processwire.com/talk/topic/22920-klaro-cookie-consent-manager/ - https://github.com/webmanufaktur/CookieManagementBanner - https://github.com/johannesdachsel/cookiemonster - https://www.oiljs.org/ - ... and so on ... In this module you can configure which kind of cookie categories you want to manage: You can also enable the support for respecting the Do-Not-Track (DNT) header to don't annoy users, who already decided for all their browsing experience. Currently there are four possible cookie groups: - Necessary (always enabled) - Statistics - Marketing - External Media All groups can be renamed, so feel free to use other cookie group names. I just haven't found a way to implement a "repeater like" field as configurable module field ... When you want to load specific scripts ( like Google Analytics, Google Maps, ...) only after the user's content to this specific category of cookies, just use the following script syntax: <script type="optin" data-type="text/javascript" data-category="statistics" data-src="/path/to/your/statistic/script.js"></script> <script type="optin" data-type="text/javascript" data-category="marketing" data-src="/path/to/your/mareketing/script.js"></script> <script type="optin" data-type="text/javascript" data-category="external_media" data-src="/path/to/your/external-media/script.js"></script> <script type="optin" data-type="text/javascript" data-category="marketing">console.log("Inline scripts are also working!");</script> The type has to be "optin" to get recognized by PrivacyWire, the data-attributes are giving hints, how the script shall be loaded, if the data-category is within the cookie consents of the user. These scripts are loaded asynchronously after the user made the decision. If you want to give the users the possibility to change their consent, you can use the following Textformatter: [[privacywire-choose-cookies]] It's planned to add also other Textformatters to opt-out of specific cookie groups or delete the whole consent cookie. You can also add a custom link to output the banner again with a link / button with following class: <a href="#" class="privacywire-show-options">Show Cookie Options</a> <button class="privacywire-show-options">Show Cookie Options</button> This module is still in development, but we already use it on several production websites. You find it here: PrivacyWire Git Repo Download as .zip I would love to hear your feedback 🙂 CHANGELOG 0.0.5 Multi-language support included completely (also in TextFormatter). Added possibility to async load other assets (e.g. <img type="optin" data-category="marketing" data-src="https://via.placeholder.com/300x300">) 0.0.4 Added possibility to add an imprint link to the banner 0.0.3 Multi-language support for module config (still in development) 0.0.2 First release 0.0.1 Early development
  8. 4 points
    Exactly what BitPoet said. Over the years I've heard over and over again how PHP is a dead/dying language, and how the future is all about [insert any imaginable language here]. If anything, I've learned not to care: PHP has been around for a long time, and to date it's still a lively project with a massive ecosystem. When I started with PHP 4 (or 3 — can't remember for sure) back in the days, it was a whole different language, really. 5.x made it a viable object-oriented language, 7.x brought in massive improvements in terms of features and performance, and 8 looks like it's going to be a blast as well. So yeah, I don't see any reason to jump the ship at this point; if anything, I'm pretty sure that PHP will have a lot more to offer in the future 🙂
  9. 4 points
    While I agree that securing the superuser account with 2FA — and, preferably, not creating multiple of such accounts — is a very good idea, you can still make sure that even if a superuser account is compromised, it won't provide anyone with any more than the necessary capabilities on the host system. Don't allow PHP to write into directories where executable code is stored; most importantly index/config/htaccess files, /site/modules/, /site/templates/, and /wire/. This means that a) module installs via ProcessWire GUI are out of the question, and b) at least in production you can't use modules that allow authoring or executing code via a GUI. Technically File Compiler also writes executable code, but it's a bit different (and can usually be disabled as well). I wouldn't worry about it too much. While ProcessWire used to allow storing custom code in Page reference field settings, that's no longer the case (hooks are used instead). As such, at the moment I can't think of any core feature that would allow superusers to access the underlying system once aforementioned precautions are taken — so after this it's essentially about only installing modules you know and trust 🙂 Finally — and this is not a ProcessWire thing per se — make sure that the system itself is secure, and that whatever user Apache is running as doesn't have access to anything it doesn't need access to. Also: try not to store anything that doesn't need to be public under a publicly (web)accessible directory, whether or not there's .htaccess protection in place. I won't go into server side security here, but there's a truckload of stuff you can do there both to avoid user accounts being compromised, and to limit the impact of compromised accounts. For an example you can make it so that even if someone does gain shell access through the site, they won't have access to the whole server (this is always the case in shared hosting, at least if the host has any idea whatsoever of what they're doing). Often it all boils down to how hardcore you want to get with this stuff, really 🙂
  10. 4 points
    I've dabbled a bit and adapted my cache module to work with Redis (working mostly on Windows, this was a bit of an adventure, but Windows Subsystem For Linux saved the day). CacheRedis - a simple Redis cache interface for ProcessWire There should be enough documentation to get started in the README. In short, it has the following methods: $redis->fetch($key[, $expire, $func]) $redis->store($key, $expire, $value) $redis->delete($key) $redis->flush() $redis->renderFile($file[, $expire[, $options]])
  11. 4 points
    What I know has been pieced together from official docs and discovered through trial and error, so I can't provide a good link there. However, I've pushed a few pieces into a small module based on Memache that can be a (partial) stand in for WireCache as an example. You can find it on github as PwMemcache. It has just a few methods to set/get/delete cache entries and doesn't allow saving and restoring of full pages like WireCache does, but it does have a renderFile method similar to WireCache. Maybe that could be a starting point. Most of the magic in using memory caches is using well thought out cache keys to avoid conflicting names (you're likely to use them more intensively than database caching) using reasonable expiry values (same as with database caches) preventing outdated data (e.g. by deleting entries when pages are changed, there's little 'automatic magic' there) taking care of special considerations when data is serialized and later unserialized, since behavior differs between solutions How exactly that best goes depends on the features of the caching solution. Unlike WireCache, many in-memory solutions have no wildcard deletion operators, so you sometimes have to build workarounds. My module for example does this when you pass PwMemcache::expireSave to the renderFile method. The cache contains an extra array where all the entries are stored that have to be cleared when a page is saved. In a Pages::saved hook, this entry is read, all cache entries listed in the array are deleted and the entry is emptied.
  12. 4 points
    @elabx Don't give up. It also took me a long time to work on ModulesManager 2 because of client work and other stuff. Then I have taken some time just for developing the module and saying "not today" to clients, because I need to finish this important thing that I am developing. I wanted to have a base so others can give feedback and submit PR's to help me with the module. But that's only an option if you plan to release it as Open Source. I hope you find the time to work on this again.
  13. 4 points
    Having just wasted the best part of a day debugging an access issue because I hadn't realised that page-edit-created negated any related page-edit permissions, could I suggest that a note to this effect is included in the default title. I have amended the title on my system to read: Edit only pages user has created (IMPORTANT: This will negate any related page-edit permission - including permissions granted to a user by other roles) ..although it may be possible to make it briefer while not losing clarity and impact.
  14. 4 points
    In an ongoing effort to provide a sort of case study, and more info about this, I'll post random screens and info about the various features of this site. (1) Custom Dashboards The site uses a custom module that supports multiple dashboards. Any given dashboard is configurable and access controlled also. This is the main dashboard: (2) The admin editor pages take advantage of some great modules, namely RuntimeMarkup @kongondo, PageFieldInfo @Robin S, Field Descriptions Extended and more, There is also a new module not released yet called Admin Comments, which for this project got a lot of use. When dealing with a large and complex data collection as was the case with this project, the editors benefited from the ability to have the data auto-analyzed on each work so the "auto flags" field helped with that. The comments also allowed editors to post information, ideas and comments right into the page editor. The AdminComments module also provides the option for any posted comment to be emailed to the other team members (selectable), and the notification email (which is customizable) allows the recipient to click directly to the editor for that page. This saved incalculable hours of work, and enhanced communication during the project, across this large data set.
  15. 3 points
    Why not just mount the volume as site/assets/files and copy the existing folders there?
  16. 3 points
    New EMO version 1.2.0 released! https://github.com/BlowbackDesign/EmailObfuscation/releases/tag/1.2.0 In this version only encryption key is stored in the emo object of html document and crypted email strings as data attributes to span elements that are used to replace found email addresses. This makes it possible to obfuscate emails generated within AJAX request. There is also a new option at module config to lock the encryption key so that it does not change on every session like it does by default. This is required if you are caching AJAX output for more than session lifetime. Otherwise this option is good leave disabled. Here is quick example of a simple AJAX request with obfuscated output: <?php namespace ProcessWire; if($config->ajax) { $str = "<p><a href='mailto:foo@bar.com'>Click to mail</a></p>"; // auto obfuscation works only when complete html document is rendered so you // need to do manual obfuscation on AJAX calls even when mode is set to auto echo $sanitizer->emo($str); return $this->halt(); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>EMO ajax example</title> </head> <body> <p>john@doe.com</p> <div id="result"></div> <p><button onclick="sendExample()">Show me some</button></p> <script> var sendExample = function() { var xhttp = new XMLHttpRequest(); xhttp.open("POST", "<?= $page->url ?>", true); xhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); xhttp.onreadystatechange = function() { if(this.readyState == 4 && this.status == 200) { document.getElementById("result").innerHTML = xhttp.response; // run emo init when there's something new to digest emo.init(); } }; xhttp.send(); }; </script> </body> </html>
  17. 3 points
    I'm currently building a Fieldtype/Inputfield for selecting date and time ranges (eg for events). There was quite some interest in this thread, so I thought I start a dedicated discussion... Background: I guess everybody of us knows the problem: You want to present events on your website and you start with a new template and a title and body field... For events you'll also need a date, so you add a datetime field called "eventdate". Maybe the event does not take place on a specific day but on multiple days, so you need a second field... Ok, you rename the first field to "date_from" and add a second field called "date_to". So far, so good. Then you want to list your events on the frontend. Simple thanks to the pw API, you might think. But you realize that it's not THAT simple... Which events take place on a specific day? What would the selector be? Yeah, it's not that complicated... it would be something like: $from = strtotime("2020-01-01"); $to = strtotime("2020-02-01"); $events = $pages->find("template=event, date_from<$to, date_to>$from"); Why? See this example, where the first vertical line represents the $to variable and the second is $from: The start of the event must be left of $to and the end must be right of $from 🙂 Ok, not that complicated... but wait... what if the date range of the event (or whatever) was not from 2020-01-18 to 2020-02-25 but from 18 TO 25 (backwards)? The selector would be wrong in that case. And did you realize the wrong operator in the selector? We used date_to>$from, which would mean that an event starting on 2020-01-01 would NOT be found! The correct selector would be >=$from. That's just an example of how many little problems can arise in those szenarios and you quickly realize that the more you get into it, the more complicated it gets... Next, you might want to have full day events. What to do? Adding a checkbox for that could be a solution, but at the latest now the problems really begin: If the checkbox is checked, the user should not input times, but only dates! That's not possible with the internal datetime field - or at least you would have to do quite some javascript coding. So you add 2 other fields: time_from and time_to. You configure your date fields to only hold the date portion of the timestamp and show the time inputfields only if the "fullday" checkbox is not checked. We now have 5 fields to handle a seemingly simple task of storing an event date. That's not only taking up a lot of space in the page editor, you'll also have to refactor all your selectors that you might already have had in place until now! Idea So the idea of this module is to make all that tedious task of adding fields, thinking about the correct selectors etc. obsolete and have one single field that takes care of it and makes it easy to query for events in a given timeframe. The GUI is Google-Calendar inspired (I'm acutally right now liking the detail that the second time input comes in front of the date input). I went ahead and just adopted that: Next steps I'm now starting to build the FINDING part of the module and I'm not sure what is the best way yet. Options I'm thinking of are: // timestamps $from = strtotime("2020-01-01"); $to = strtotime("2020-02-01")+1; // last second of january // option 1 $pages->find("template=event, eventdate.isInRange=$from|$to"); $pages->find("template=event, eventdate.isOnDay=$from"); $pages->find("template=event, eventdate.isInMonth=$from"); $pages->find("template=event, eventdate.isInYear=$from"); // option 2 $finder = $modules->get("RockDaterangeFinder"); $finder->findInRange("eventdate", $from, $to, "template=event"); $finder->findOnDay("eventdate", $from, "template=event"); ... I think option 1 is cleaner and easier to use and develop, so I'll continue with this option 🙂 Future As @gebeer already asked here I'm of course already thinking of how this could be extended to support recurring events (date ranges) in the future... I'm not sure how to do that yet, but I think it could make a lot of sense to build this feature into this module. I'm not sure if/how/when I can realease this module. I'm building it now for one project and want to see how it works first. Nevertheless I wanted to share the status with you to get some feedback and maybe also get your experiences in working with dates and times or maybe working with recurring events (or the abandoned recurme field). For recurring events the finding process would be a lot more complicated though, so there it might be better to use an approach similar to option 2 in the example above.
  18. 3 points
    I had to do several page imports via Tracy Console today, so I had the challenge of populating the Daterange field easily via the API... I added support for basic daterange parsing from a given string: RockDaterange("22.02.2020") hasTime => false hasEnd => false fromH => "22.02.2020 00:00" (16) toH => "22.02.2020 23:59" (16) RockDaterange("22.02.2020 17:00 - 19:00") hasTime => true hasEnd => true fromH => "22.02.2020 17:00" (16) toH => "22.02.2020 19:00" (16) RockDaterange("22.02.2020 17:00 - 20-3-1 19:00") hasTime => true hasEnd => true fromH => "22.02.2020 17:00" (16) toH => "01.03.2020 19:00" (16) And it get's even better! This makes it possible to use $page->setAndSave() easily using string dateranges: $page->setAndSave('range', "2020-02-22"); // single full-day event $page->setAndSave('range', "2020-02-22 18:00"); // single day event with time $page->setAndSave('range', "2020-02-22 18:00 - 22:00"); // single day event with time range $page->setAndSave('range', "2020-02-22 - 2020-02-24"); // 3-day event (full-day) $page->setAndSave('range', "2020-02-22 06:00 - 2020-02-24 23:00"); // 3-day event with times You wonder how complicated that was to implement?! Once the parsing part was done it was nothing more than adding this one line to the sleepValue method of the fieldtype. How genius is ProcessWire?? 😎 public function sleepValue($page, $field, $value) { if(is_string($value)) $value = new RockDaterange($value); ... --- This is how I did the recurring events so far 😄 Got an excel from the client with page id of the master event and date + time of the recurring events. Using VSCode and multicursor I transferred this into a script to create pages: The save() call at the end of each line would not be necessary but triggers a hook that renames the page and adds the daterange to the URL. This prevents ugly urls like this /event-x /event-x-1 /event-x-1-1 /event-x-1-1-1 And creates URLs like this instead: /event-x-01.01.2020 /event-x-01.02.2020 /event-x-01.03.2020 /event-x-01.04.2020 There's a lot one has to think of when dealing with events 😮 I wonder if a "add daterange to URL" feature would make sense if a daterange field is present on a template... Would have to be optional of course.
  19. 3 points
    Just realized that mPDF version maximum was set to 7.x - RockPdf 2.0.0 uses mPDF library v8.0.5 and might introduce some breaking changes - so be careful with the update or just use RockPdf <= 1.0.2 Updating dependencies (including require-dev) Package operations: 0 installs, 2 updates, 0 removals - Updating setasign/fpdi (1.6.2 => v2.3.0): Downloading (100%) - Updating mpdf/mpdf (v7.1.9 => v8.0.5): Downloading (100%) v2.0.0
  20. 3 points
    Hi @Jens Martsch - dotnetic Almost a year later and still in the same situation, @joshuag jumped ship on this project and I'm full of client work 😩. I know it looks awesome but I it's still far from being good enough for release 😕 I seriously get the impostor syndrom when I see all the awesome users we all know, releasing amazing modules every now and then with such amazing quality, how do you guys do it?! Need to level up 😥
  21. 3 points
    This is an update about how it's been running blog sites using ProcessWire. I hope it's OK for me to post in this category even though I've already showcased my sites awhile back. I thought it would be helpful for people to get a feel for what it's like to use ProcessWire on an ongoing basis for blogging. Often people talk about the development of a site, but it's not quite as often that we hear about the ongoing running of a PW site and how the PW API influences that, which is what I'll cover here. As background, we at The GrayFly Group own and run the blogs goodkidsclothes.com and flipfall.com. The development of these PW sites has been covered in a showcase thread for GoodKidsClothes and another for FlipFall. Here are some of the unique experiences I've had running these two sites. "Running" covers everything from coding and making modifications to the templates, to writing our articles, to interacting with ad partners or with others seeking us out for something related to one of those sites. So this is a different experience from agencies who develop for others; we develop for ourselves. As background, the main traffic to the websites comes from organic search results. Income from sites is from affiliate marketing and advertisements. GoodKidsClothes PW experience: "to think it is to do it." For GoodKidsClothes.com, one of the things we noticed was that if we could think it, we could do it, thanks to the easy-to-use PW API. The need for a change Here is a concrete example of what I mean: we noticed that many people would enter the site on an older article (e.g. via a search result). However, we continually put out a lot of time-sensitive information, e.g. a style guide, a piece of news relating to a change in a children's clothing company, etc. I didn't want people to miss out on this, yet many were, because after reading their entry page, they'd leave. They had no idea (unless they clicked on the link to home page) that there was another article that could be of value to them. All too often, by the time people learned about that new article via search results, they'd be too late for the news to be relevant - in fact, it wouldn't even be the newest article anymore by that point. The solution So, using the PW API, we modified the article template so that if someone was reading any article that was not the most recent article, then at the end of what they're reading, they'd see a small section highlighting the most recent article. Here is a screenshot: As you can see above, our newest article is highlighted immediately below the article they're reading, unless of course they are already reading the newest article. In the case shown above, the newest article (recipe-related) did not happen to be time-sensitive, but in most cases that article would be time-sensitive, so that's why we made this change. To make the change we simply used the PW API to query what the latest article was and store its identity in a variable - those sorts of queries we set up in _init.php. Then we modified the article template such that if the current page was not the latest article, to include the featured box that you see above. Another need for a change You'll also notice links in boxes above and below where the featured article box is. These are ads (they blend OK right?!) These ads brought another problem to our attention: we'd put the ads blocks on all articles equally. However, in the case of the most recent article, often the most recent article would usually have a time-sensitive offer or some other call to action e.g. signing up for our newsletter (well, not in the case of the recipe article above, but in most cases the latest article would have something we prefer the reader to do). We didn't then want our readers to get distracted by the ads and either leave the site, or click on an ad and click away from the site, instead of doing whatever the call to action is. The solution Again using the ProcessWire API, we modified the "article" template so that there was conditional logic on the ads: if the current page is not the latest article, include the ad code (otherwise no ads). This mean no ads were seen on the most recent article, allowing for less distractions to the reader on time-sensitive articles and more likelihood of them following through on the call to action. Conclusion for GoodKidsClothes We were able to make all these changes within minutes of thinking of them! In-house, without a ton of knowledge of programming, thanks to the awesome ProcessWire API. We actually made all those changes live, i.e. going in there and making changes to the code of the site as its running live. Yes, we had backups of the entire site and we always first save a copy of the template file under a different name (usually prefixing it with OLD_ ) before modifying the live version. This is how helpful ProcessWire is. We can make changes that benefit our site and make them in-house as we think of them. If this was done under some other CMS, we would be unable to make those changes without either a) hiring a developer or b) training up in whatever the other CMS is to make the changes in-house. Either way, it would take considerably more time to do anything. So, despite not having a formal programming background, we now have a very "nimble" site that we can adapt as needed to changes that we desire, within minutes of thinking of the change we need, with only needing to know a little PHP, html, and CSS, just the very basics, and looking up the PW API. FlipFall PW experience: "the answer is yes." In the case of FlipFall, there have been times when a potential ad partner asks a question like "can you put different ads on different categories?" or other things. Sometimes they are questions I ask myself of the website "Can we do A/B testing of different ads; i.e. show a certain ad block 50% of the time totally randomly and another ad block the other 50% of the time?" "How about ads from this company some of the time and a different company other times?" The answer is always "yes." Coming from other CMS's (that I used but did not program with) I used to brace myself a bit if I saw an email that asked "Can you....?" but now thanks to ProcessWire I don't have that bracing reaction any more. So long as I can think of a way to do it (and so far I always have, thanks to the PW API), I can say "Yes we can." More to the point, I can actually say "Yes, we can make those changes in-house within [whatever brief timeframe I think it will be]" instead of having to be vague about timeframes because of needing a developer. So I no longer worry about "Can you ...?" questions because the answer is yes. Overall conclusions ProcessWire is a superb CMS for those who own and run a site. The PW API makes it easy to make changes to the look and functionality of the site as needed. Such modifications wouldn't easily be possible on alternative CMS's that are heavily "theme-based".
  22. 2 points
    Hi Orkun! I use the Firebase PHP JWT library when I need to use JWTs, and I would do it like this with PW: use \Firebase\JWT\JWT; // http://example.com/sso/?token=eyJhbGciOiJIUzI... $token = wire('input')->get('token'); // If not token, show error? $key = 'ABC123'; // Store/load your key somewhere (e.g. PW's $config var) try { $decoded = JWT::decode($token, $key, array('HS256')); } catch (\Exception $e) { // Error parsing/decoding token - do not accept. Show error/redirect. } // print_r($decoded); // Find user based on supplied email. // Use other PW user fields or properties from JWT as necessary. $u = wire('users')->get('email=' . wire('sanitizer')->selectorValue($decoded->email)); if ( ! $u->id) { // Could not find user - don't exist. // Could create them at this point, if you have enough detail in the JWT to do so. } // Force user login wire('session')->forceLogin($u); // Go to another page (don't stay on this one) wire('session')->redirect('/');
  23. 2 points
    Hi @Bacelo, I'm an artist who just coded his own website with PW. I basically learned enough PW and php to code it in the past 1.5 months. This is before I started learning about SEO...so I've been thinking about the right site construction. I might have to amend my menu links etc. I've been testing an SEO program and they linked to a number of publicly available articles. This one I think is the best. https://moz.com/blog/site-architecture-for-seo. The main takeaway from this article is that the site architecture should be as flat as possible. So keeping in mind that I'm a beginner in php and pw and all things programming: I may be misunderstanding your description, but why is a painting in two categories? Would there be an issue of duplicate content? Maybe you can solve this with the canonical link then? I like that a painting=page. That's the way I did mine as well (I'm a photographer). Image = page. I don't use the multi image field anywhere in the site (e.g. in a blog post). In learning about SEO, text is very important, will there be blog posts? In the previous website incarnations, my site had images with only title and caption, which google considered to be thin content. How is the client going to solve that? I'm still trying to solve this for myself. For now, what I've done is that when I talk about a certain image in a blog post, I will purposefully link to the image page from the blog_body. In the image page, i have Related Writing code that automatically picks up any page that links to it using pagelinks(). I think over time this will work well if I want to stay with image=page. I'm going through my past blog posts to link them over time. An alternative I've considered is not to have individual image pages (to avoid the thin content). Instead I would only have a gallery page with say all the images lazy loaded as you describe. If a user wants to see a larger version, then they click on it, and a larger version pops up (I forget the module that does this, but there's several). I ended up not doing this because I want each image to have more information displayed over time like edition, exhibition history, buy button, etc, and based on my reading of the module, you can't fit that into a pop-up (whatever you call those things). As stated the main takeaway I think is to have a flat structure for search engines while making it as pleasant for user experience. Regards,
  24. 2 points
    I think you might have come up against this bug (https://github.com/processwire/processwire-issues/issues/511). If you think it is related, perhaps you could nudge Ryan on that issue as he seems to be ignoring my concerns on this, especially when it's a really easy fix.
  25. 2 points
    How crazy is that piece of software called ProcessWire?? Exact matches are now possible using the string notation of date ranges. Writing this down and looking at the screenshot I realize that it would make a lot of sense to be consistant in the format when transforming a RockDaterange object into a string and vice versa. Amount of code necessary to support this? 10 Lines 😲
  26. 2 points
    @astock: I just checked the code. While the hasVoted method is called before renderPoll(), it doesn't get the current user. I have added a github branch with a fix if you want to give it a spin.
  27. 2 points
    @MarkE - The events just have a page field multiple where you select the works being performed. Some events come in through Boosey and those have a reference ID we store so those are auto assigned. Works pages just have a section that searches for upcoming events and outputs them. But there are a ton of other ways that works are connected to each other, using versions, members and custom taxonomies that focus the relationships that are specified...
  28. 2 points
    Maybe a directory traversal attack (to install backdoors or whatnot). I would immediately contact the hosting company, and also check for suspicious files. Lock down your forms, or any other potential areas that allow user-input.
  29. 2 points
    Another update - RockPdf now supports FontAwesome 5 in mPDF library 🥳😎 This works using webfonts (after I've unsuccessfully tried with SVG) and it supports resizing and coloring icons via CSS or LESS (wich didn't work when using SVG). I've updated the readme: Using FontAwesome 5 with mPDF Download a copy of fontawesome (https://fontawesome.com/download, eg Free for Web) Copy the TTF file into your /site/assets/RockPdf/fonts/ folder Add your font to your settings and start using icons in your PDFs // tracy console $pdf = $modules->get('RockPdf'); $pdf->settings([ 'fontdata' => (new \Mpdf\Config\FontVariables())->getDefaults()['fontdata'] + [ "far" => [ 'R' => "fa-regular-400.ttf", 'I' => "fa-regular-400.ttf", ], ], ]); $icon = "<i style='font-family: far;'>&#xf118;</i> "; $pdf->write($icon.'Hello World ' . date('H:i:s')); d($pdf->save()); You'll notice that we used the unicode representation of the icon. You can find all the codes on the cheatsheet (https://fontawesome.com/cheatsheet) or on the details page of the icon: https://fontawesome.com/icons/smile?style=regular Be careful to use the correct style (regular, solid, etc) and unicode! Special thx to jamesfairhurst Using metadata to get the unicode Too complicated? RockPdf comes with a helper so that you do not need to take care of all this and just use the regular fontawesome classes that you might already be familiar with! To make that work, just copy the icons.json file that is shipped with fontawesome in the metadata folder into the RockPdf assets folder /site/assets/RockPdf/fonts. // tracy console $pdf = $modules->get('RockPdf'); $pdf->settings([ 'fontdata' => (new \Mpdf\Config\FontVariables())->getDefaults()['fontdata'] + [ "far" => [ 'R' => "fa-regular-400.ttf", 'I' => "fa-regular-400.ttf", ], ], ]); $pdf->write("<style>.far { font-family: far; color: blue; }</style>"); $icon = $pdf->icon('far fa-smile'); $pdf->write($icon.'Hello World ' . date('H:i:s')); d($pdf->html()); // print content to console $pdf->save(); // save file to file system Using this technique you can easily style your icons using CSS or even LESS (when using RockLESS). Another example Unfortunately duotone icons do not work (if you know how to make them work please drop me a line!). Also styling the icons is sometimes a bit tricky - some CSS selectors work while others don't. Using classes directly on the icon worked best for me: $icons = $pdf->icon('fas fa-guitar red-2x') .$pdf->icon('far fa-guitar red-2x') .$pdf->icon('fal fa-guitar red-2x') .$pdf->icon('fad fa-guitar red-2x'); .fab { font-family: fab; } .fad { font-family: fad; } .fal { font-family: fal; } .far { font-family: far; } .fas { font-family: fas; } .red-2x { font-size: 10mm; color: red; } v2.0.1
  30. 2 points
    Interesting Tool => Code Snippets Manager { https://masscode.io/ }
  31. 2 points
    Just updated all the dependencies of this module to the newest versions: Package operations: 0 installs, 4 updates, 0 removals - Updating myclabs/deep-copy (1.7.0 => 1.9.5): Downloading (100%) - Updating paragonie/random_compat (v2.0.12 => v9.99.99): Downloading (100%) - Updating psr/log (1.0.2 => 1.1.2): Downloading (100%) - Updating mpdf/mpdf (v7.1.0 => v7.1.9): Downloading (100%) v1.0.2
  32. 2 points
    You might reach a point where table level locks kill (some of) the speed improvements of caching if you are using MyISAM as the database engine. In that case, switching to InnoDB might speed things up, though you'll lose a small bit of that in insert/update performance itself. I have a site with about the same number of cache entries that runs fine (using InnoDB). When the row count gets noticeably larger, using Memcached (or any other in-memory key-value storage) gives marked performance improvements, but that is of course only possible if you can run the necessary daemon software on the server.
  33. 2 points
    Hey Kei, Have you tried the skyscrapers profile by @dadish that can be found here. With a clean install of processwire, you can drop the profile in and proceed like you are setting up a new instance with the profile selected.
  34. 2 points
    You don't need PHP at all. I'd suggest to use Google Optimize (and a linked Google Analytics account where you set up your goals) instead. Here's a nice tutorial. (you can skip more or less to step 3 "Create an Experiment" - and make sure you also include the "anti-flicker" stuff at the end of step 2)
  35. 2 points
    Good thing I marked this as a beta release, because I'm still finding a lot of bugs 😅 I just pushed version 0.4.0. The module is now no longer autoloaded, because it's not really required anymore. This release splits the module into two seperate Modules: ProcessCacheControl and CacheControlTools. They are still installed alongside each other, and ProcessCacheControl will install CacheControlTools during install. For existing installs, you might have to reinstall the module, or install CacheControlTools manually after the upgrade. Also, the asset version management is now part of CacheControlTools, so the code has to be adjusted: // version 0.3.0 and below $CacheControlTools = $modules->get('ProcessCacheControl')->getTools(); // version 0.4.0 $CacheControlTools = $modules->get('CacheControlTools'); A bit of background for the split: I learned something new about the permission system. If a Process module defines a permission, the current user needs that permission not only to access the Process page in the admin, but also for the module to even be instantiated at all. Because of this, the asset management system didn't work for guest users. By splitting the repository into two modules, I can still have strict access control for the Process part of the module, while allowing the tools module to be instantiated even during requests from guest users. Anyway, the module is now, let's say, nearing stability. I'd like to publish a stable release soon, so let me know if you find any more bugs!
  36. 2 points
    This is a new module that provides a simple solution to clearing all your cache layers at once, and an extensible interface to perform various cache-related actions. The simple motivation behind this module was that I was tired of manually clearing caches in several places after deploying a change on a live site. The basic purpose of this module is a simple Clear all caches link in the Setup menu which clears out all caches, no matter where they hide. You can customize what exactly the module does through it's configuration menu: Expire or delete all cache entries in the database, or selectively clear caches by namespace ($cache API) Clear the the template render cache. Clear out specific folders inside your site's cache directory (/site/assets/cache) Refresh version strings for static assets to bust client-side browser caches (this requires some setup, see the full documentation for details). This is the basic function of the module. However, you can also add different cache management action through the API and execute them through the module's interface. For this advanced usage, the module provides: An interface to see all available cache actions and execute them. A system log and logging output on the module page to see verify what the module is doing. A CacheControlTools class with utility functions to clear out different caches. An API to add cache actions, execute them programmatically and even modify the default action. Permission management, allowing you granular control over which user roles can execute which actions. The complete documentation can be found in the module's README. Beta release Note that I consider this a Beta release. Since the module is relatively aggressive in deleting some caches, I would advise you to install in on a test environment before using it on a live site. Let me know if you're getting any errors, have trouble using the module or if you have suggestions for improvement! In particular, can someone let me know if this module causes any problems with the ProCache module? I don't own or use it, so I can't check. As far as I can tell, ProCache uses a folder inside the cache directory to cache static pages, so my module should be able to clear the ProCache site cache as well, I'd appreciate it if someone can test that for me. Future plans If there is some interest in this, I plan to expand this to a more general cache management solution. I particular, I would like to add additional cache actions. Some ideas that came to mind: Warming up the template render cache for publicly accessible pages. Removing all active user sessions. Let me know if you have more suggestions! Links https://github.com/MoritzLost/ProcessCacheControl ProcessCacheControl in the Module directory
  37. 2 points
    Thanks everyone for the feedback! I just pushed a new version that resolves a bug that prevented installs on PHP versions <7.4. PHP 7.1, 7.2 and 7.3 should work now! Thanks @Mikie for your help! I have also found some documentation in the ProCache store page. I've added an option to clear the ProCache now as well, though I haven't included it in the new version because I can't test it out. Would you mind giving it a go? The ProCache integration is in the procache branch on Github (download link). If it's working as intended, it should add a new option to clear the ProCache in the module configuration, including a note saying whether ProCache is installed. If selected, the 'Clear all' action should use $procache->clearAll and log the result. It would also be good to know if non-superusers can use this, or if it's limited to the superuser. Thanks! The module also appears to have been approved to the module directory, so it can now be downloaded directly through the backend using the module name ProcessCacheControl. Thanks @adrian (or is Ryan in charge of the module directory? 🙃)
  38. 2 points
    Right now only the default values can be overwritten via language translation files. But good hint - I'll add multi-language support for the config fields as well.
  39. 2 points
    No, it's not in the directory. You should really take a look at @netcarver's awesome: https://pwgeeks.com/ - just type "cache" in there and you'll find some other cache related tools. Thanks for the rundown of differences - that will be really helpful.
  40. 2 points
    Hi @adrian, I didn't know that module existed, it's not in the directory, is it? Because I looked if something like this already existed and didn't find anything. Might've saved me some trouble 😅 That said, I had a quick look, here's what I found in comparing both modules: ClearCacheAdmin exposes more options through the setup page, so there are available to everyone, whereas ProcessCacheControl has it's options in the module configuration, so they are only available to the developer. ClearCacheAdmin has multiple links (and multiple options) on the process page, giving the user more fine control, whereas ProcessCacheControl goes for a simpler interface and bundles all those actions into one (configurable) action with a single button. ProcessCacheControl can delete $cache entries without an expiry date, which ClearCacheAdmin doesn't as far as I can tell. ProcessCacheControl also lets you configure caches to be deleted by namespace, whereas ClearCacheAdmin offers each cache entry to be deleted individually (I think this has mostly to do with the module being a bit older, I believe $cache didn't have all those options back then). The largest difference is my concept of "cache actions", which ClearCacheAdmin doesn't have. I'm not sure how useful that will actually be yet. I think if I can expand on the actions available by default, it will be pretty handy. With ProcessCacheControl, you can add custom cache actions / profiles through the API, that may be useful depending on the use case. Adding to that, ProcessCacheControl has permission checks on a per-action basis. ProcessCacheControl can be modified and executed through the API. In particular, you can modify the default action and execute any action programmatically.
  41. 2 points
    Just a heads up, I hope this might save someone some time in the future. TL:DR> It's taken me hours to work out that Apache version 2.4 <If> statements are broken when trying to do regular expression matches against the %REQUEST_URI variable. Try matching against the variable called %THE_REQUEST instead - but be careful with your regex. Background: I've been trying to add more relaxed CSP headers for the admin portion of one of my PW sites because my default, strict, CSP headers stop some admin features from working properly. After diving in to Apache2 regexs and conditionals, I thought I had the answer with this... <If "%{REQUEST_URI} !~ m#^/admin/?#i"> # CSP headers for external visitors. Header set Content-Security-Policy "<strict policy for public site>" </If> <Else> # CSP headers for admin visitors. Header set Content-Security-Policy "<lax policy for admin>" </Else> ...but I couldn't make it work. After experimenting for several hours I found that the regex shown above does work if you limit it to a single-character match; which is essentially useless for determining if we are in the admin interface. However, you can achieve a useful match with the following... <If "%{THE_REQUEST} !~ m# /admin/?#i"> ... </If> <Else> ... </Else> ...which checks verses the entire HTTP request line made to the server. This block can go in your vhosts file or in the site's .htaccess file. Here's the Apache documentation: https://httpd.apache.org/docs/current/expr.html and it seems I'm not alone in having this issue: https://serverfault.com/questions/940953/apache-if-statement-not-working?noredirect=1&lq=1 although neither of these links had workable solutions for me.
  42. 1 point
    I've added a feature to define the link to the issue in the fix and also the author (github user) <?php namespace ProcessWire; class Foo extends PWFix { public $label = "My foo label"; public $description = "My foo description"; public $issue = "https://github.com/processwire/processwire-issues/issues/xxx"; public $author = "BernhardBaumrock"; // github user public function init() { // fix goes here } }
  43. 1 point
    I used this code on my site as well and added uppy meta information for setting the page id where the files will be uploaded. Here is the code if you are also looking how to do this: <script src="https://transloadit.edgly.net/releases/uppy/v1.9.2/uppy.min.js"></script> <script> const uppy = Uppy.Core({debug: true, autoProceed: false, meta: { pageid: 1488 } }) .use(Uppy.Dashboard, {target: '#uppy-box', inline: true}) .use(Uppy.Tus, {endpoint: 'MYURL/uppy/', limit:10}); </script> $td = $files->tempDir('uppy'); // CMS $td_path = (string) $td; // Create TusPhp server $server = new \TusPhp\Tus\Server(); // Set path to endpoint - no trailing slash here $server->setApiPath('/uppy'); // Set upload directory $server->setUploadDir($td_path); // Listener function for when an upload is completed $server->event()->addListener('tus-server.upload.complete', function(\TusPhp\Events\TusEvent $event) { $file_path = $event->getFile()->getFilePath(); p = wire('pages')->get(intval($event->getFile()->details()['metadata']['pageid'])); // extract page id from uppy metadata $p->of(false); // CMS related $p->files->add($file_path); //CMS related $p->save('files'); //CMS related }); // Send response $response = $server->serve(); $response->send(); exit(0);
  44. 1 point
    Let's say some bad guy steals my ProcessWire admin account password (by reading it off the post-it note I so conveniently have on my screen). Obviously, they have full control of my site now and can do whatever they please. That can not be prevented in this scenario. But on many ProcessWire installations, I think the attacker could also gain control of the server, e.g. by installing a PHP shell. That could probably be prevented somehow. My goal is to lock down things so that an admin user can not insert any PHP to the site. What would I need to do to achive that? I can think of a few things: Disable module install/upload/download. I guess the hanna code module must be uninstalled? Anything more? Any other modules or settings to look out for? I understand that features like one click module install are very useful, so this is not a critique against ProcessWire - it's the best CMS I have ever used. I am just interested how I can be a bit extra cautious. Also, I understand that it is best to just never lose your admin password, but I think defence in depth is a good thing.
  45. 1 point
    You are right; whilst it is possible to get a vhost/.htaccess-based A+ on security headers while keeping the admin interface fully operational, it is a pain and leaves the apache config files in a bit of a mess. Getting the admin interface sorted in the vhosts file prompted this post over in dev-talk that might also help someone out if they go down that route. What you've posted seems to be a fairly nice approach to the issue - thanks!
  46. 1 point
    @adrian Interesting point indeed, though I recall having changed the priority once or twice in the past for some reason or the other. To me, it makes sense that JL does its thing first as it acts as a user-intervention strategy in that we have to honour the user's wishes. Figuring out what those are, however, is a different story, given the different approaches/modules that are available. I wish this could be configured by the user, in the case conflicts/undesired redirects take place. Jumplinks was actually borne from a plugin I wrote for Bolt CMF, and that plugin was an absolute, undeniable 'before everything else even considers thinking of tunning running' scenario. Before we make a decision here, I think we should get more feedback from other JL users, and perhaps get a list together of any plugin that does route-redirection along with their priorities.
  47. 1 point
    Hello @Brett. Thank you for your feedback! I was able to reproduce your issue and hopefully tracked down the bug. Please, install the latest version and give it a try. Let me know if the issue is resolved for you. New Release 1.1.3 - Fixed bug with FieldtypePage returning only single value.
  48. 1 point
    You can use a hook to ProcessPageEdit::execute to redirect users with those roles to the frontend page. Make a new file /site/ready.php and put this code inside: wire()->addHookBefore('ProcessPageEdit::execute', function (Hookevent $event) { $restrictedRoles = array('admin', 'frontendeditor'); // list your roles here foreach ($this->user->roles as $role) { if ($role->name !== 'guest' && in_array($role->name, $restrictedRoles)) { $this->session->redirect($this->config->urls->root); } } }); This will redirect users with given roles to the homepage of your site. Note that this will restrict them only from accessing page edit screen in the admin panel. If you like to restrict access to the whole admin area for those roles, you would need to hook into Page::render or Page::viewable and than check if the Page has template admin.
  49. 1 point
    Perhaps you have auto-prepended and/or auto-appended template files in your /site/config.php, in which case you would want to use the $options array to override those when rendering the page, e.g. foreach ($pages->find("template=article") as $article) { $content .= $article->render('teaser.php', ['appendFile' => null]); }
  50. 1 point
    Reporting back to confirm that it was a max_input_vars issue. Once changed locally, the pages saved as expected. As well, once changed on the production server, the clone and original began to save again.
  • Create New...