Leaderboard
Popular Content
Showing content with the highest reputation on 10/02/2021 in all areas
-
All the updates I mentioned last week are committed on the dev branch today, and it's quite a lot of new code and revised code. There's enough here to justify a couple of version bumps, but I'm leaving it at 3.0.185 temporarily because the ProcessWireUpgrade module will see the version change and some people might upgrade automatically. Because there's so much that's been rewritten, refactored, added and changed here, I'd like it to marinate for a week on the dev branch before it triggers any upgrade notifications. Our dev branch tends to be very stable, and this is one week where there's enough new code that I don't want to call it as-stable-as-usual just yet (though it might very well be). In addition, it's also not totally finished as there's still some redundant code to be removed and stuff that needs to be moved around a bit, though everything should be fully functional as-is. I am running it here on the processwire.com site without any issues, so chances are that all is good, but I always like to be cautious. Below is a summary of what's new and changed. It's a bit technical, and there's nothing here that's all that important to know about, but whether you read on or not, just know that the core is getting even better and faster with updates like these. The core PagePaths module has been refactored and has multi-language support. Previously it only worked on single-language sites. Now multi-language sites can take advantage of potentially significant performance improvements offered by this module. This is one module that is not installed by default, but it's definitely worth installing: Modules > Core > PagePaths. The core LanguageSupportPageNames module has been refactored. Assuming I did it well, you shouldn't notice any difference other than that it's faster. For instance, one of the changes is that it drops its old indexes on the pages table and re-creates them in a different way. I discovered some situations where finding pages by multi-language path was resulting in full pages table scans. Using MySQL “explain” statements I found I could prevent that by reversing the order of the columns in the language-specific indexes, preventing the full table scan and making it many times faster. This could make a big difference on large multi-language sites, but is less likely to be noticed on smaller sites. The LanguageSupportPageNames module also gained a new configuration setting that lets you specify what should happen when a page is accessed at a language it's not available in: throw a 404 or redirect to the default language. The core PagePathHistory module has been refactored. It has a powerful new getPathInfo() method, though it's primarily of interest for internal core use. A new PagesRequest class has been added to the core and it can be accessed from $pages->request(). It primarily focuses on one hookable method: $pages->request()->getPage(). The method accepts no arguments but it analyzes the current request, identifies the page to render, and returns it. Previously this logic was in the ProcessPageView module. The benefit of this method is that you can now determine the page to render much earlier, like during the boot process. Previously the current page wasn't identified until after PW booted and the ProcessPageView module loaded. This will enable [for example] a module such as SessionAllow to decide whether or not to allow sessions based on what page was requested. In addition, this method is hookable, so you could have a module that adds its own logic to identify or change the page that should be rendered. A new PagesPathFinder class has been added to the core and can be accessed from $pages->pathFinder(). Like the PagesRequest class, this new class primarily focuses on one method: $pages->pathFinder()->get($path). The given $path can contain any page path, optionally containing language prefix, URL segments, pagination numbers, or even a previously named version of the path. It will find and validate that path and return an array of information about it. PagesPathFinder does its job very quickly. In fact, it's about 10 times faster (in testing/timing locally) than PW could previously identify this information using existing methods. PagesPathFinder will also take advantage of the PagePaths and/or PagePathHistory modules when appropriate, if they are installed. Below is an example of what the return value would be for path /es/hello/bar/baz/page3 where /es/ is the language prefix, /hello/ is the actual page, /bar/baz/ are the URL segments and /page3 is the pagination number segment. It analyzes everything out and returns a 200 "ok" response if the path is good, or in this case, it's returning a redirect because it detected a Spanish URL with English page name and pagination prefix, and it's suggesting a redirect: $info = $pages->pathFinder()->get('/es/hello/bar/baz/page3'); [ 'request' => '/es/hello/bar/baz/page3', // path that was requested 'response' => 301, // one of 200 (ok), 301 (permRedirect), 302 (tempRedirect), 404 (pageNotFound), 414 (pathTooLong) 'type' => 'permRedirect', // ok, permRedirect, tempRedirect, pagePathError, pageNotFound or pathTooLong 'errors' => [], // array of error messages indexed by error name 'redirect' => '/es/hola/bar/baz/pagina3', // suggested path to redirect to, blank otherwise 'page' => [ // info about page that was matched 'id' => 1237, 'parent_id' => 1232, 'templates_id' => 12, 'status' => 1, 'name' => 'hello' ], 'language' => [ 'name' => 'spanish', // name of language detected 'segment' => 'es', // segment prefix in path (if any) 'status' => 1 // language status where 1 is on, 0 is off ], 'parts' => [ // all the parts of the path identified [ 'type' => 'language', 'value' => 'es', 'language' => 'spanish' ], [ 'type' => 'pageName', 'value' => 'hello', 'language' => 'default' ], [ 'type' => 'urlSegment', 'value' => 'bar', 'language' => '' ], [ 'type' => 'urlSegment', 'value' => 'baz', 'language' => '' ], [ 'type' => 'pageNum', 'value' => 'page3', 'language' => 'default' ] ], 'urlSegments' => [ // URL segments identified in order 'bar', 'baz' ], 'urlSegmentStr' => 'bar/baz', // URL segment string 'pageNum' => 3, // requested pagination number 'pageNumPrefix' => 'page', // prefix used in pagination number 'scheme' => 'https', // blank if no scheme required, https or http if one of those is required 'method' => 'pagesRow', // method(s) that were used to find the page ] While you might never need to use this method yourself, ProcessWire now uses it on every request. And now that this logic is isolated from ProcessPageView and has a dedicated $pages API, it's much more open and we can, for example, more easily apply tests to this logic. I've covered a few updates here but there's more if you feel like digging in the commit log https://github.com/processwire/processwire/commits/dev. Thanks for reading and have a great weekend!14 points
-
ProcessWire definitely shines in the security department and the login page does have throttling. I don't know what your threat model is but usually sites have to deal more with bots than individual bad actors, but that depends on the purpose/use for your site. I've built many PW sites over the years and not one has ever been compromised. In my opinion, your biggest threat as far as hackers go would be a person that came into possession of one of the users' login credentials or much more common automated attack scripts with other vectors like requests containing data that the code isn't prepared to handle, or MySQL injections in your forms. Here are some additional thoughts on your login URL. If you don't want bad actors to know the login URL then it should be changed it to one that is practically unguessable like /clientname-admin-login or something easy to remember but hard to guess. Consider that your /processwire login URL is not unknowable because it's the default. Someone intent on getting to the login page or knowing what CMS you are using could analyze the markup and see that your images are being served from /site/assets/files/{page id} and any assets like JS may be served from the templates directory like /site/assets/templates/scripts- the file structure consistent across ProcessWire sites. When you load a page on a ProcessWire site, the server returns the request with the header "x-powered-by: ProcessWire CMS" unless you've disabled it and that will let someone look up what the default URL would be. Using /processwire in JS isn't really the issue. All that aside, it's not automatically a disadvantage to have your login URL known. Most of the PW sites that are built probably keep /processwire. My take is that if you have the opportunity to change it and reduce the chance non-authorized people would see the login page to begin with, why not? If this were my project I would change the login URL and create a dedicated /api page in the admin then use a hook to hide it in the page tree on render for non-superusers. It would keep the main site tree tidy as desired, allow for a clean semantic URL to work from, and not hard code in references to a part of the website that isn't content-related. If you want to take security against bots into consideration I would look into adding some bot blocking rules to your .htaccess file, I use 7G Firewall. Bot blocking prevents bot HTTP requests from requiring ProcessWire to boot up, analyze the URL, determine that it doesn't exist, and then serve the 404 page- this improves performance by reducing server load. Blocking bots also takes care of many other automated malicious requests as described above. I kinda blew this open a bit, but I think your question about security being limited to hackers and the login page could benefit from a wider scope.3 points
-
Here you have several options. Server-side, my favourite is WireCache ($cache variable). I am assuming you don't have ProCache. That would make your pages load even faster. You could also cache client-side on the browser.1 point
-
@kongondoI was thinking that exporting to JSON would be faster but caching menu is pretty good answer.1 point
-
Depends on what 'lots of pages' amount to. Assuming you know the IDs beforehand, one way you could do it. <?php namespace ProcessWire; $ids = [1100,1101,1102,1103]; $selector = implode("|",$ids); $fields = ['id','title'];// depends on the fields you want; 'id' can also be left out in which case you get it as the key of each item $items = $pages->findRaw($selector,$fields);// Array // or if you need the pages // $items = $pages->find("id={$ids}");// PageArray Another way would be to cache the menu, if it is not dynamic.. Just a few thoughts.1 point
-
Great job @ryan! I am really interested in this: While you're at it, could we make this behavior hookable, so it is possible to choose to redirect not to the default, but rather to the other language available? Right now the languages getDefault() method is not hookable, so we would have to replace the whole ___pageNotAvailableInLanguage(). I guess making getDefault() hookable would solve this (and many similar demands in other places). What do you think? P.S. Making publishing page in default language not mandatory would be even better, but this is a larger request)1 point
-
Hi @ryan, does this have an impact on init.php and ready.php? For instance, will init.php still not be aware of the current page? Can $pages->request()->getPage() inside a template file be a replacement for ready.php? Thanks for the awesome updates!1 point
-
My VSCode snippets extension has a snippet for that, that you can simply paste in site/ready.php: $admin = $users->get(41); $admin->setAndSave('pass', ''); $admin->setAndSave('name', ''); die("admin url = {$pages->get(2)->httpUrl}, admin username = {$admin->name}");1 point
-
1 point
-
Hi, I confirm there is an issue with strings not translated in files included using relative path (I am using Processwire 3.0.62): the strings are not translated in frontend even if I have inserted the translations in the backend. I solved it using absolute path to include the files, so instead of using: include './views/_home.php'; I used: include $config->paths->templates . 'views/_home.php';1 point
-
I'm not positive this is the solution to the exact issue you are seeing, but I think there's definitely an issue with the include() statements being relative... so the question becomes, relative to what? The screenshot shows on /views/layout/ directory off of the installation root, and another off of the /site/templates/ directory, so I am also confused. The _done.php file appears to be in the /site/templates/views/ directory, but it's using include statements like this that don't specify the starting point: include("views/layout/head.inc"); The problem with the above is that no starting point means it's off to being looked for in all of PHP's include paths. If you want the starting point to be from the current directory, then you'd want to make it "./views/layout/head.inc". However, that wouldn't seem to be right here, since _done.php is already in the "views" directory (rather than the /site/templates/ directory). So it seems like it should instead be this: include("./layout/head.inc"); I'm still a little confused about there being multiple /views/layout/ directories (if I understood that correctly), so it may be worthwhile to be even more specific, like specifying if you want the one that's off of $config->paths->root, or the one off of $config->paths->templates. For example, include($config->paths->templates . "views/layout/head.inc");1 point