Leaderboard
Popular Content
Showing content with the highest reputation on 03/17/2021 in all areas
-
@teppo - this sounds like a pretty decent solution. I wonder if it might be something that could be incorporated into the ARB module as a feature. Let me know if you'd be willing/able to share the code at some point, or even better if you would be happy to incorporate into ARB as a PR.2 points
-
And not just the doors. The guys have more than 1000 lamps of all the electrified ages ? They are clients of mine since 2004. The website design is a little bit outdated but the priority is to keep everything rolling... according to the owner ?2 points
-
Recently needed to add something similar to a site I've been working on, and ended up with a slightly different approach. Can't share the code right now, but here's the general concept: Store all allowed branches in user profile, similar to the screenshot above. Hook into ProcessPageList::execute and add a select option for choosing which branch is "active". Hook into AdminRestrictBranch::getBranchRootParentId and check if a branch GET param was provided and matches one of those stored in user profile; if yes, store the value in session. Return either the first branch from user profile, or the value from session. Technically it's only restricting the user to one branch at a time, so there are some limitations, but in my specific case that's actually preferable: this lets users focus on one section of the site at a time ?2 points
-
Here is what I came up with: Codepen Let me know if it is what you were after ?2 points
-
This means that your MySQL server is in strict mode regarding group by expressions. MySQL used to be rather lenient there, but normally standard SQL requires that you name all non-aggregate (SUM, COUNT, AVG etc.) columns that appear in the SELECT clause also in the GROUP BY expression. Since along version 5.6, the ONLY_FULL_GROUP_BY settings was enabled as a default, and nowadays many hosters and Linux distributions also set this flag in installations of 5.5. Your "SELECT *" include the columns id, member_name, member_id and member_score, so for MySQL it looks like: SELECT id, member_name, member_id, member_score FROM golfscores GROUP BY member_id LIMIT 0, 1000; To understand why MySQL complains, you have to understand what MySQL does under the hood: It scans through the member_id column (or its index) for all unique values. For any of the other columns, since they aren't in the group by, it picks the first value it finds in a row found in the first step. This means, in your example data, it might find a member_score of either 30 or 38 for member_id 222. Which one it finds first and returns is pure chance. Since the SQL standard desires predictability and doesn't like chance at all, such a behavior is unacceptable. Very few databases allow such a thing at all. On the other hand, if you, as the developer, know that mismatched rows like in your example cannot be found in the database (i.e. only one combination for id, member_name and member_score is in the table for each member_id), grouping just by member_name tells MySQL "don't worry about checking uniqueness and order of the other columns, just return the first hit for each member_id" and saves the database server (sometimes quite) a bit of lookup work for those columns. This is, of course, a bit dangerous. Thus, the ONLY_FULL_GROUP_BY setting was switched on at some point. You can switch it off per connection, or globally for all new connection through a SET statement, but these won't be persisted (PW does this every time it initializes a database connection). If you want to use that optimized group by syntax, the best way is to do it in my.cnf (the server configuration). Somewhere there, you will find line in the [mysqld] section with the sql_mode. Remove ONLY_FULL_GROUP_BY from that line. /* MySQL < 5.7.x */ sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY /* newer MySQL */ sql-mode="NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY" Make sure to keep the format (underscore vs. dash, no quotes vs. double quotes) the same and restart the server.2 points
-
LogMaintenance A simple ProcessWire module to give some maintenance control over log files. I found myself often having lots of log files for different things that can grow more or less quickly to a size where they can be difficult to maintain. The built in Logger of PW does a good job of giving you the possibility to delete or prune logs. But it has to be done manually and sometimes a log grows into millions of lines, which makes it often impossible to even prune it as it's too large. LogMaintenance uses LazyCron to run the maintenance task and there's several settings you can setup on a global or per log basis. Archive: will create zip files for each log file in logs/archive/ folder and add the log each time the maintenance is run to a subfolder containing the datetime. Lines: keeps logs to a certain number of lines Days: keeps the log to a certain number of days Bytes: keeps the log to a certain amount of bytes Each setting is checked from top down, the first setting to contain something is used. So if you check the "Archive" option, all other settings are ignored and logs are archived everytime the LazyCron is executed. If you want to keep your logs to a certain amount of bytes just leave all other settings to 0 or blank. Per Log Settings There's a textarea that you can use to setup a config for a specific log file one per line. All the logs you define here ignore the global settings above. The syntax for the settings is: logname:[archive]:[lines]:[days]:[bytes] errors:1:0:0:0 // would archive the errors log messages:0:10000:0:0 // will prune the errors log to 10000 lines The module can be found on github for you to check out. It's still fresh and I'm currently testing. https://github.com/somatonic/LogMaintenance1 point
-
I was glad to see there was interest in the new URL hooks added last week, thanks! There were still a few details to work out, so thought I'd take care of those this week before moving on to other updates, and they are now in 3.0.174. Today I've also updated last week's blog post with the additional info, so that it's all in one place. This will likely be converted over to a dedicated documentation page, but for now, here is what's been added: The post last week introduced you to using named arguments. This week another kind was added, called simple named arguments (click the link for details). The idea is based off an example from another post in these forums by BitPoet. The handling of trailing slashes vs non-trailing slashes was undefined last week. This week it has been defined and is now enforced by ProcessWire. All of the details are here. Pagination was another grey area last week, but no longer. Here are all of the details on how you can utilize pagination in URL/path hooks. In addition, in 3.0.174 URL/path hooks can now have control even after a Page (template file) throws its own 404, whether by wire404() or throw new Wire404Exception(). I found this was necessary because it is possible to enable URL segments for your homepage template. And, depending on your homepage template settings, that means it is possible for the homepage to be the recipient of all URLs that don't match pages. This would leave no opportunity for URL/path hooks to execute. So now ProcessWire gives URL/path hooks another opportunity to run if it matches the URL, even after a 404 is thrown from a Page template file. Beyond the above, there's been a lot of additional optimization and improvement to the hooks system that handles the path/URL hooks, but nothing further that affects its API... just internal improvements. ProcessWire 3.0.174 also adds a feature requested by Adrian, which was to add object return value support for the recently added $pages->findRaw() method. The method by default returns arrays for everything, which can be helpful in making clear you are working with raw data, in cases where it matters. (As opposed to formatted or prepared page data). But if you specify objects=1 in your selector to the method, it will instead return StdClass objects where it previously returned arrays. This makes working with the data more consistent with how you might work with Page object data, even if it is still raw data. Should you be using any findRaw() data for output purposes, you can now also specify entities=1 in your selector to have it automatically entity-encode all string values, or entities=field, replacing "field" with pipe-separated field names you want entity encoded. The following example summarizes all of the recent additions to findRaw, as pretty much everything here was not possible on the first implementation: $items = $pages->findRaw("parent=/blog/posts, fields=title|url, entities=title, objects=1"); foreach($items as $item) { echo "<li><a href='$item->url'>$item->title</a></li>"; } Thanks for reading and have a great weekend!1 point
-
I am looking for an output strategy for repater fields or Repater Matrix fields (Pro fields) in my case. I would like to replicate the approach Ryan described in the blog post https://processwire.com/blog/posts/more-repeaters-repeater-matrix-and-new-field-rendering/ , but using Template Engine Factory (because I want to use Smarty or another template language). In short: Instead of a foreach loop that then uses many if clauses to distinguish the type, I would like to have a separate controller and template for each repeater matrix item. The controller should be used to modify the output before. Here is my working code, but I wonder if it might be better to use hooks like Template Engine Factory's hookBeforePageRender, or something else? //_init.php $factory = $this->modules->get('TemplateEngineFactory'); // check if the actual page has a modules_repeater (Repeater Matrix field) // and provide the output of the different modules as a variable if ($this->page->hasField('modules_repeater')) { // now get the output based on the type foreach ($this->page->modules_repeater as $module) { // Get the controller, to render a repeater item. $controller = $factory->controller("modules_controllers/{$module->type}.php", "modules/{$module->type}.tpl"); // You might pass some data via to the controller file, which you find at site/templates/modules_controller/nameOfTheRepeaterMatrixItem. // We assign the module repeater item to the $page variable so we can use it in our template and controller like this // $page->hero_headline $controller->page = $module; $controller->execute(); $output = $factory->render("modules/{$module->type}", ['page' => $module]); // Executing the controller renders the associated template via template engine. $this->view->set('modules_repeater', $output); } } Any suggestions?1 point
-
Searching for values of page reference fields assigned to images is unfortunately not fully supported (yet?). All custom fields for an image are stored in the database as a single JSON string, so things get a bit tricky there and the regular subfield logic of PW doesn't work. What does work is searching for numeric page ids or full page paths. $pages->find('images.tagpage=1098') should work, as should $pages->find('images.tagpage=/tags/funny/'). $pages->find('images.tagpage.title%=tagtitle') won't work.1 point
-
Something like here? https://www.historische-baustoffe-ostalb.de/baustoffe/tueren/haustueren/ Sorry, website only in german, but I could give you some tips to get you started.1 point
-
I'd still recommend backing everything up before-hand, but it should all be ok. If it's a critical website that can't have any downtime, I would also suggest making a copy and upgrading that just to check there aren't any issues, especially with third party modules.1 point
-
1 point
-
@CliffG - I'd also suggest it might be time to upgrade to PW 3 ? - it's usually just a simple upgrade of the wire directory (.htaccess and index.php files). Rarely does it require anything more.1 point
-
Thank you, Adrian! A higher level of tech support managed to fix the problem after I posted, but I will take a look at the info you linked to. This is the only problem I've ever had with ProcessWire — or, I should say, MySQL.1 point
-
This post should get you up and running again: https://processwire.com/talk/topic/18341-mysql-group-by-error-help/?do=findComment&comment=1603591 point
-
That sounds interesting ? Though I'm not sure if that approach can actually work well ? I mean... It can work for somebody of course, but I'm not sure how big the difference to existing solutions (field/pages import/export) would be? Maybe you could clarify that a bit? But I don't want to discourage you from trying to find a way that makes migrations more easy to use. I know that this is a big hurdle at the beginning - it took me years to understand why Benjamin built his migrations module ? On the other hand migrations are a totally different concept compared to building websites via the PW backend... you need to take care of logical restrictions that sometimes arise (for example field settings depending on the existance of other fields or pages etc). That's why migrations SHOULD be code and that's why it is so hard to build some kind of UI or recording/diff tool for it... Whenever I don't know the code for a migration I simply create the field and have a look at Tracys field code panel: Then I copy the parts that I need to my migration and that's it. My IDE then tells me about the changes, I can commit them and everybody is happy ? But I'm really happy to see different approaches coming up and I'm looking forward to seeing any drafts ?1 point
-
Easier said than done. I'll see what I can do. I can see that if you are using RockMigrations from the start, then the issue does not arise as all fieldnames will be lowercase. My situation is a bit different as I only use PW occasionally, and find the Admin UI, rather than a 'headless' approach, works better for me. However, I find migrations a pain. Consequently, I have started working on a UI interface module for your module, which is coming along quite nicely and which I will provide more details on once I have it in a reasonable state. It generates json files from the database which are then used as input to the migration, so it uses the existing fieldnames, which may not be lower case. Having run into this, as well as a few other problems with using RockMigrations in this way, I have switched to using the core code directly as I appreciate that RockMigrations was not designed with this in mind. For fields, I am using a modified copy of ProcessFieldExportImport::processImport() (although that has problems with options fields), for templates, I call ProcessTemplatesExportImport::setImportData() and am just using the standard API for pages. However, there are attractions in using the RockMigrations methods so I may return to this once all the UI stuff is working, particularly if you think it is a good idea.1 point
-
I had a different setup where I wanted my files served from a sub-domain (even though in the end it was from /site/assets/files), but it's somehow related. What I did was to point the sub-domain to /site/assets/files, and then add this hook in ready.php : $wire->addHookAfter("Pagefile::url", function($event) { static $n = 0; if(++$n === 1) { $file = $event->object; $url = $file->httpUrl; $host = wire("config")->httpHost; $url = str_replace("$host/site/assets/files", "sub.domain.com", $url); $event->return = $url; } $n--; }); You could replace "sub.domain.com" to "domainB.com/site/assets/files" (or even clear the "site/assets/files" part in your case). Hope it helps !1 point
-
@LAPS With this new version, there are even more customizations available. Have a look into the comments in PrivacyWire.module. This example code does the same as the one from my post from last Friday, but with the new syntax of the new version: <html> <head> <!-- your head content --> <?php $privacywire = $modules->get("PrivacyWire"); // load the css files echo $procache->link([ $config->urls->template . "path/to/your/stylesheet.css", $privacywire->getPrivacyWireStyles()->url ]); // load the inline script echo $privacywire->renderPrivacyWireConfigAsInlineJs(); ?> </head> <body> <!-- your body content --> <!-- your body content --> <!-- your body content --> <?php // render the required DOM elements echo $privacywire->bodyContent; // render the JavaScript files echo $procache->script([ $config->urls->template . "path/to/your/scripts.js", $privacywire->getPrivacyWireCore()->url ]); ?> </body> </html>1 point
-
My journey with CSS frameworks has been: Bootstrap v2 → Zurb Foundation → Bootstrap 3 → UIkit 2 → Uikit 3. This is over the course of 9 years, with plain CSS for several years before that. All of the frameworks I mentioned come with pre-define components along with the JavaScript to do the usual things like accordions, tabs, modals, etc. I really fell in love with UIkit 3 because it goes very deep with JS components, giving you things like filters, lazy loading and slideshow. With Bootstrap, you have to use 3rd party libraries to get the same level of functionality, which in the past has lead to breaking packages for whatever reason, compatibility issues and a lack of cohesiveness. Maybe my workflow these days would alleviate some of those issues, but the fact remains UIkit solves like 95% of my use cases. I completely see the appeal of Tailwind having done CSS for a long time, but the lack of an official set of JS components is holding me back from giving it a try. I'm still waiting to see how UIkit 4 turns out and see how much further they go with utilities. Or I just may sit down and write a set of UIkit classes using Tailwind @apply that uikit.js expects so components looks correct (has anyone done this?). But, that feels a little hacky.1 point
-
Just in case somebody needs to get the override label in the current language… I did… // Get label in current language echo $templates->get('template_name')->fieldgroup->getField('my_field_mame', true)->getLabel(); // OR directly from the page (no matter how the templates name is): echo $page->template->fieldgroup->getField('my_field_mame', true)->getLabel(); This is a way faster than using translate stings in the template: __("I'm an in english headline, translate me!")1 point
-
With respect, it's replies like these when people post their sites in the showcase that somewhat annoy me and I think would either be better handled through a private message or just left alone. With a site as complex as the one Ryan posted, how could you possibly know what's best? Sites like these take thousands of hours and go through many iterations. There's a lot of group-think with any "best practice" but in my belief and experience, especially with ecommerce sites, visitors usually don't care. Here's 2 examples: A friend of mine started a Shopify website, which is probably the biggest Shopify website in the world (not an exaggeration). For the first 3 years, his website was nothing "special" and could probably be designed by anyone here in their sleep. A basic logo, simple lines, decent pictures of products. Knee-jerk snarky-developer reaction would be "omg, your website sucks lol bye". But, customers don't care. They want a good deal and he provided that and had great timing. Needless to say, he's probably a billionaire at this point. With one of my own ecommerce websites, I'm not using whatever the latest cutting edge web development practices are (React / all that JS stuff / AJAX everywhere / crazy system infrastructure / etc.). Customers don't care (customers in this case being Fortune 100 companies, even Fortune 1 companies... ? ). They are looking for something specific and will find what they need. Snarky developer reaction: lol bro you should be using technology 'x', a read about it in a blog this week. Do the end customers care? No, they are people looking to buy a product on our website, not judging the behind-the-scenes code. It's easy to fall into this trap of taking whatever latest article you read and thinking you must apply it or you are falling behind / going to lose customers / are a bad developer / whatever. I went through years of this until the above to examples opened my eyes a lot. I would recommend focusing on your customers and what's working for you... not what trends are.1 point
-
Hello! I am working on a pull request, to make Smarty and Twig templates translatable within ProcessWire. I stuck a little bit at the RegExp patterns. Maybe some RegExp-Professional want's to help me? :-) Should be possible in Smarty files: {$this->__('text')} {__('text')} {_x('text')} {_n('text')} Should be possible in Twig Files: {{ $this->__('text') }} {{ __('text') }} {{ _x('text') }} {{ _n('text') }} The whole patters are in the parseFile Function: https://github.com/processwire/processwire/blob/341342dc5b1c58012ae7cb26cffe2c57cd915552/wire/modules/LanguageSupport/LanguageParser.php#L120 /** * Run regex's on file contents to locate all translation functions * */ protected function parseFile($file) { $matches = array( 1 => array(), // $this->_('text'); 2 => array(), // __('text', [textdomain]); 3 => array(), // _x('text', 'context', [textdomain]) or $this->_x('text', 'context'); 4 => array(), // _n('singular', 'plural', $cnt, [textdomain]) or $this->_n(...); ); if(!is_file($file)) return $matches; $data = file_get_contents($file); // Find $this->_('text') style matches preg_match_all( '/(>_)\(\s*' . // $this->_( '([\'"])(.+?)(?<!\\\\)\\2' . // "text" '\s*\)+(.*)$/m', // and everything else $data, $matches[1]); // Find __('text', textdomain) style matches preg_match_all( '/([\s.=(]__|^__)\(\s*' . // __( '([\'"])(.+?)(?<!\\\\)\\2\s*' . // "text" '(?:,\s*[^)]+)?\)+(.*)$/m', // , textdomain (optional) and everything else $data, $matches[2]); // Find _x('text', 'context', textdomain) or $this->_x('text', 'context') style matches preg_match_all( '/([\s.=>(]_x|^_x)\(\s*' . // _x( or $this->_x( '([\'"])(.+?)(?<!\\\\)\\2\s*,\s*' . // "text", '([\'"])(.+?)(?<!\\\\)\\4\s*' . // "context" '[^)]*\)+(.*)$/m', // , textdomain (optional) and everything else $data, $matches[3]); // Find _n('singular text', 'plural text', $cnt, textdomain) or $this->_n(...) style matches preg_match_all( '/([\s.=>(]_n|^_n)\(\s*' . // _n( or $this->_n( '([\'"])(.+?)(?<!\\\\)\\2\s*,\s*' . // "singular", '([\'"])(.+?)(?<!\\\\)\\4\s*,\s*' . // "plural", '.+?\)+(.*)$/m', // $count, optional textdomain, closing function parenthesis ) and rest of line $data, $matches[4]); return $matches; }1 point
-
@Wanze I ended up in using your solution but to avoid writing the key everytime in two files per hand I created a node.js script which is executed in my build script of the whole project. The scripts reads all translatable strings from my .tpl files which look like this {translate}Lorem Ipsum{/translate} and creates in every view-directory a translation.php file. If anyone needs it here is the code of the node.js module (you have to install async and glob via npm..) "use strict"; const glob = require('glob'); const async = require('async'); const fs = require('fs'); // find all directories glob("site/templates/views/**/", null, (error, directories) => { // iterate in paralell over directories async.each(directories, function(directory, directoryCallback) { // find all template files in the current directory glob(`${directory}*.tpl`, null, (error, templateFiles) => { if(error) return directoryCallback(error); let translationKeys = []; // iterate in paralell over templateFiles async.each(templateFiles, function(templateFile, templateFileCallback) { fs.readFile(templateFile, (error, data) => { if(error) throw err; let tpl = data.toString('utf8'); // find all keys let pattern = new RegExp('{translate\}(.+?)\{\/translate\}', 'gm'); let result = tpl.match(pattern); if(result && result.length > 0) { // extract key without smarty block syntax // transform it to PHP syntax result = result.map(item => item.replace(pattern, '__("$1");')); // push all keys to the collector of all keys in the current directory translationKeys.push(...result); } templateFileCallback(); }); }, function(error) { if(error) console.log("Error in the templateFiles logic", error); // executed after alle keys are collected from the tpl files in the current directory if(translationKeys.length > 0) { // filter duplicates // sort alphabetical translationKeys = [ ...new Set(translationKeys) ].sort(); // make the final PHP file let phpFile = [ '<?php namespace ProcessWire;', ...translationKeys ].join("\n"); fs.writeFile(`${directory}translations.php`, phpFile, 'utf8', function(error) { if(error) throw err; directoryCallback(); }); } else { directoryCallback(); } }); }); }, function(error) { if(error) console.log("Error in the directory logic", error); // all done exit the process process.exit(); }); });1 point
-
Hi @Soma, Could you please add this very useful module to the PW modules directory? Thanks!1 point
-
Sorry, I know I am getting way OT with this Tracy stuff, but just wanted to let you know that the latest version adds the SQL Query column to the Selector Queries section of the Debug Mode panel. It seems to be stable enough to release, but let me know if you get any errors. Hopefully this should be a useful learning tool.1 point