-
Posts
65 -
Joined
-
Last visited
Everything posted by nurkka
-
RockFrontend 🔥🚀 The Powerful Toolbox for ProcessWire Frontend Development
nurkka replied to bernhard's topic in RockFrontend
Using RockFrontend in a real project for the first time since yesterday – I'm totally blown away! Currently I am only using Latte templates and LiveReload, but these two features alone are such game changers. @bernhard Thank you so much (again!) for donating such a genius module to the community! I don't know if anybody already asked/answered this, so I'll leave this info here: If you want to add additional folders to be watched by LiveReload, you can define them this way: /site/config.php: $config->livereload = [ 'interval' => 1, 'include' => [ $config->paths->root . '/some/folder', $config->paths->root . '/another/folder', ], ]; -
module Fluency - The complete translation enhancement suite for ProcessWire
nurkka replied to FireWire's topic in Modules/Plugins
In my current project, the default language is German and the second language is English. Now, I am using Ryan's LoginRegisterPro module, which is of course in English. I wanted to use Fluency to translate all fields in the language translator to German, but that's not possible. This is because Fluency does only translate from the first to the second (and other) languages, but not from "string constant in a php file" to the first language. At least in my case, it always copied the string constant into the field, which is in English. So, I changed my setup and made the default language English and the second language German, and now it worked: Fluency translated all strings from the module php file to German in one step. But now, I have the problem, that I can't leave the website with German as the second language, because to my knowledge that's only possible, if one has language tokens in the urls, also for the default language, and makes a redirect to the desired language. But that's no option in this project. Setting the guest user language to the second language (now German) didn't help either. My workaround now is, to export the translated JSON files, then switch the languages back to default = German and second = English, and import the translated JSON file(s) to the German language. So I have some questions: - Has anybody already found a better solution for this problem? - @FireWire Would it be possible to add a feature to Fluency, to be able to translate module or template files (which are mostly in English) also to the default language (for cases where it is a non-English language)?- 212 replies
-
- 1
-
- translation
- language
-
(and 1 more)
Tagged with:
-
In this case, the setup is for a legacy project, and I am still experimenting, which is the best setup for me. In this project, I have lots and lots of RepeaterMatrix items, with nested Repeaters, Fieldset Pages, Combo fields and of course a lot of "simple" fields, like text_1 to text_20 etc. I will not be able to refactor everything into migrations, because that would be too much work. But for new fields, I wanted to use RockMigrations. RockMigrations only adds things and does not remove anything, until you tell it to, so it is perfect for this use case. Thanks for pointing out, that the migrations must be executed in the right order. I hope that this won't be a problem in my case, although I don't know for sure yet. Having all migrations in migrate.php or in Site.module.php would stress me out, because I would have hundreds of lines of code in one file. Currently, I am only adding new fields, and I am grouping the migrations accordingly. E.g. when adding a fieldset page, I wrote a migration file with all its dependent fields and the fieldset page at the end. When adding a RepeaterMatrix item, I would define all the dependent fields and the RepeaterMatrix item migration in one file. So I would always try to group migrations which are connected/dependent into one file. Of course, this is not perfect, because I also use "global" fields like "text_1" in my RepeaterMatrix items. In a new project, I would probably simply add a new text field for every RepeaterMatrix item, if needed. I am sure, one could also write a custom page class for a RepeaterMatrix item, and add the migrations there, but I have to learn this yet. Perhaps it's also better not to mix several ways to integrate migrations, because of the maintainability of the site. Anyway, I am really happy with the new setup for now ?
-
@bernhard Thanks for your reply and valuable info! I wanted to try RockPageBuilder for some time, but I am still working on legacy projects. I will make a completely new website in september, so that will be my opportunity to finally use it. As for the case described above, where I want to have different migration files for different fields, templates, etc, I found a solution. It is possbile to tell RockMigrations to watch a whole folder and also to watch it recursively, by using rockmigrations()->watch( 'DIRECTORYNAME', true, [ 'recursive' => true ] ); So this works now: <?php namespace ProcessWire; class Site extends WireData implements Module { public static function getModuleInfo() { return [ 'title' => 'Site', 'singular' => true, 'autoload' => true, ]; } public function init() { $rm = $this->wire->modules->get('RockMigrations'); if ($rm) { // $rm->watch($this); // not needed b/c we have no migrations directly in this module class file $rm->watch( __DIR__ . '/migrations', true, [ 'recursive' => true ] ); } } // not needed anymore b/c of $rm->watch in init() /* public function migrate() { $this->migrate_fields(); } public function migrate_fields () { include __DIR__ . '/migrations/fields/fieldset_visible_responsive.php'; include __DIR__ . '/migrations/fields/fieldset_visible_language.php'; } */ } This way, I can have a lean Site.module.php and move all migrations to dedicated files.
-
I am still trying to wrap my head around RockMigrations. With my first attempts, my migrate.php grew to several hundred lines, so I wanted to structure the migrations. I also read that @bernhard always uses a module Site.module.php, which I also created in my project. I also wanted to split my migrations into several files, so I did the following: Directory structure: - modules - Site - migrations - fields fieldset_visible_responsive.php fieldset_visible_language.php Site.module.php Site.module.php <?php namespace ProcessWire; class Site extends WireData implements Module { public static function getModuleInfo() { return [ 'title' => 'Site', 'singular' => true, 'autoload' => true, ]; } public function init() { $rm = $this->wire->modules->get('RockMigrations'); if ($rm) { $rm->watch($this); $rm->watch( __DIR__ . '/migrations/fields/fieldset_visible_responsive.php' ); } } public function migrate() { $this->migrate_fields(); } public function migrate_fields () { include __DIR__ . '/migrations/fields/fieldset_visible_responsive.php'; } } fieldset_visible_responsive.php <?php namespace ProcessWire; $rm = rockmigrations(); $rm->createField('checkbox_visible_responsive_mobile', 'FieldtypeCheckbox', [ 'columnWidth' => 33, 'label' => 'Mobile', ]); [...] Now, I have some questions: - Is there a RockMigrations feature to combine the "watch" command with an "include"? As the watch commands apparently have to be placed in init() and the migrations in migrate(), the migration files to be included have to be referenced in both places. Okay, I could write a config array or something for this, but is there a _native_ RockMigrations feature or best practice for that? - I guess you also do not put all your migrations into Site.module.php – What is your strategy for staying on top of things? Assuming one has lots and lots of "global" fields, which are not related to specific templates, for what you would perhaps use custom page classes. - Is the .php suffix mandatory for the Site-module, or can it also be named Site.module, like other ProcessWire modules? @bernhard And thanks again for the module! I think it's fantastic that you have donated such an impressive module to the community. Even as I am not 100% grasping all the possibilities and features of RockMigrations yet, I think the possibility to create and modify fields by configuration could (and should) also become part of the core. I think other CMS/CMF are already doing that.
-
Here are some additional feature ideas: - The feature to define the TinyMCE-configuration of all your TinyMCE fields in a "parent"-field is so great - how about if we could have that with more fieldtypes? In my sites, I have tons of fields, which change from time to time, and as one can't use fields more than once on a template, I have e.g. image_single_1, image_single_2, image_single_3. It would be great to change all those e.g. image fields at once, by just changing a "parent field". Please tell me, if I missed that feature, if it already exists ? - Nearly all my sites have to be MultiLanguage sites, and therefore, a multilanguage image field would be great. I know Language-alternate fields, but a "real" field would be really great. The most complex project, I am working on, has currently 10 languages. When creating and maintaining all the needed fields, this is a lot of work, which could be much easier with a multilanguage image field. - As I am still working with (nested) RepeaterMatrix and Repeater fields, keeping track of fields which are really used or not used is a complicated, as simple fields often are displayed as "in use" on some pages, while those pages are not "real" pages, but system pages of repeaters. I hope you know what I mean. The feature suggestion in this case is to extend the display of fields on the fields admin page so the relations between the fields are somehow more comprehensible. - Also, the fields list get's confusing, if you have some hundred fields – the UI could be refined somehow, to make the fields list easier to work with.* * I'll try to start a thread about naming fields, because I tend to change my naming scheme with every project, because I try to find the perfect one - which I did't find yet. Perhaps you are willing to share your naming schemes. I am sure someone already invented the perfect scheme ?
-
New post – Implementing and using TinyMCE 6 in ProcessWire
nurkka replied to ryan's topic in News & Announcements
I noticed, that when using a TinyMCE-Field as a "template" for the configuration other TinyMCE-Fields, the "parent" field can be deleted without warning. @ryan Would it be possible to include a warning, like with "normal" fields? Additonally, another feature "wish": It would be great, if all TinyMCE-options would be configurable by text/json-files. Currently, some features can only be changed by copy/pasting code into backend fields, like "Extra CSS styles". -
Additional info: I just noticed that these two variants don't work the same: // this works: $this->addHookAfter( 'Pages::saved(status<' . Page::statusTrash . ')', $this, 'hookPageSaved', [ 'priority' => 200 ] ); // this does not work: $this->addHookAfter( 'Pages::saved(status<trash)', $this, 'hookPageSaved', [ 'priority' => 200 ] ); In my case, I a have a module which writes the pages as JSON files to disk, after they are saved. As a page will be saved, when moving it to the trash, the hook will be called either. The selector in the first variant prevents this, while the selector in the second variant doesn't.
-
@ryan you're right, my checkbox was configured to be a "reversed checkbox" via the module CheckboxReversed. I changed the value to 0 and now it works perfectly.
-
I am trying to learn more about Advanced Hooks, and the following worked perfectly: // this works perfectly: $this->addHookAfter( 'Pages::saved(status<' . Page::statusTrash . ')', $this, 'hookPageSaved', [ 'priority' => 200 ] ); This adds the hook only if the saved page is not already in the trash, and executes the hook as late as possible. Now, I would like to add the hook only, if a certain checkbox on the page is checked. I tried the following: // this does not work: $this->addHookAfter( 'Pages::saved(some_checkbox_field=1, status<' . Page::statusTrash . ')', $this, 'hookPageSaved', [ 'priority' => 200 ] ); But this did not work ... Update: This didn't work, because I had a non-standard checkbox via the module CheckboxReversed, so I should have used the selector some_checkbox_field=0. With a default checkbox, the code examples work perfectly.
-
I found the solution – of course right after posting ? One simply has to wrap the latte markup into {try} ... {/try} {try} {func($var)} {/try} And if you want to log the errors, you can do the following: $loggingHandler = function (\Throwable $e, \Latte\Runtime\Template $template) { wire()->log->save('latte_errors', $e->getMessage()); }; $latte->setExceptionHandler($loggingHandler);
-
I am using latte (not with RockFrontend yet, but "standalone" - I can't integrate RockFrontend in my current project yet, but hopefully in the next one), and I noticed that if I make errors in the .latte-file, the latte engine outputs a 500 Server Error, although I wrapped the latte call in a try/catch-block. My code in ProcessWire template: templateContent = '{func($var)}'; // contains an undefined function call $params = []; try { $output = $latte->renderToString( $templateContent, $params ); } catch (\Exception $e) { wire()->log->save('latte_errors', $e->getMessage()); } When testing the same latte markup in the latte sandbox https://fiddle.nette.org/latte/#0b4eb6af74 , it displays an appropriate error message. So there must be a way to catch the error, but I can't find it. Or can this only be done with PHP-features (which I do not know yet), or has ProcessWire any features to catch the errors? Does anyone know how to do it?
-
[SOLVED] $page->render can't render the current page twice
nurkka replied to nurkka's topic in General Support
Thanks for your ideas! I have to automatically upload the generated JSON files to the shop server via SFTP. So, I think URL hooks and WireCache would not help in this case. After several hours of testing, I am now quite sure, that the original page saving problems resulted from the missing namespace in templates and includes. The problems didn't occur again. I am using the hooks ProcessPageView::finished and Pages::saved to generate the JSON-files, and that works for now. When testing, I had the impression that ProcessWire cached some of the includes, possibly because of the FileCompiler. Several times, changes in the code were not always immediately noticeable, but only after several reloads and/or a click on “Refresh” for the modules. I also tried the CacheControl module, to get rid of cached code. Perhaps this has something to do with the hosting and them caching php code, too ... but I don't know for sure. Anyway, the module now seems to work, at least for now. -
[SOLVED] $page->render can't render the current page twice
nurkka replied to nurkka's topic in General Support
Thanks @poljpocket, you're right regarding the hook on Pages::saved, and I am already using that. But because of the nature of the online shop, which e.g. fetches up to four processwire pages per pageview, if I only use Pages::saved, in some cases, only one of those four pages will be up to date, when the others are outdated, until they are also saved. Which would never happen if it isn't done automatically. So, the perfect solution would be to determine the page ids of the other three pages, when saving one of those pages, and to render the JSON files of all those related pages. I am sure that is possible, but there are a lot of different cases, not only the described example. So I thought it would be a good idea, when processwire saved every page as JSON (additionally to displaying it as a normal page) when it is viewed - and of course every language variant (which is not viewed in the same request). To adress the issue with the same page not rendering twice: In the meantime, I found that the site had also a lot of templates without namespace. I corrected that, and now, the issue might have been gone. I am still testing that. -
[SOLVED] LazyCron possible on every page load, or other technique?
nurkka replied to nurkka's topic in General Support
@poljpocket Thanks for your reply! The site has quite good traffic, so LazyCron would be okay in principle. But no matter if I use LazyCron or the ProcessPageView::finished hook – you mentioned it already – one of the visitors (or, in the worst case, the google bot!) would get a very slow experience. So I practically have no other choice as to use a real cronjob, which unfortunately can only run every minute. -
[SOLVED] LazyCron possible on every page load, or other technique?
nurkka replied to nurkka's topic in General Support
I want to divide a possibly very long task into small parts, which should be completed relatively quickly. LazyCron leaves at least 30 seconds between two tasks, so I am looking for a way to run the script with every ProcessWire call. In my tests, I got different results when using PageRender::renderPage or ProcessPageView::finished and when the script was executed by LazyCron. With LazyCron, I got no errors, so I figured, it would be the best way to execute a task, independent from the original request or pageview. I don't know yet, why my version with ProcessPageView::finished did not work for me - but I will keep testing that. Thanks for your input! -
The shortest time between two LazyCron runs seems to be 30 seconds. Is there a way to let a LazyCron script run on every page load instead? If not, what would be a good technique to run a script on every page load, but outside of the normal ProcessWire request context, like LazyCron does? I tried a lot of Hooks, but every one of them kept my script within the current request. I am searching for the right hook or another technique, which let's me run the script in ProcessWire (in a module), but independent from the current request. Thanks and best regards! Update: I decided to use a "real" cronjob, which unfortunately can only run every minute, while LazyCron could run every 30 seconds, if your site had enough traffic. If you want to execute a task or parts of a task after every ProcessWire request, you could use the hook ProcessPageView::finished
-
I am using ProcessWire as the backend for an online shop system, which fetches the content as JSON files. The processwire installation has some legacy template code, which causes the startpage of the shop to require 3 or 4 actual ProcessWire pages. The JSON files are saved into a custom cache folder. When this cache is deleted, it has to be rebuilt. To achieve that, I implemented a Hook into PageRender::renderPage, which saves the currently viewed page and all of its language variants into JSON files. To get the actual markup, I am using $page->render(). Doing that, I noticed, that ProcessWire seems to prevent the currently requested page from also being generated (a second time) via $page->render(). The return value of $page->render() was always empty within the PageRender::renderPage Hook if $page was identical to the page of the current request. And it always returned the correctly rendered markup, if the current page was not identical to the Page in the $page->render() call. I assume that this has something to do with a mechanism in ProcessWire which shall prevent infinite loops. Does anybody know more about this? Are there any workarounds? Update: The issues were solved by cleaning up legacy template code – especially missing namespaces.
-
My suggestion would be to add a native global media manager and native multidomain support Media Manager: - Global management of images (also SVG) and Documents (e.g. PDFs) with a decent UI, Preview-Thumbnails, the possibility to organize the assets in folders, etc. - A field to reference those assets, with a usable UI and the possibility to define starting folders, e.g. having a field which only allows to select from a folder with employee portraits, etc. - References should be managed automatically, so one can't delete an image which is still referenced anywhere - If an image is not referenced anywhere anymore, there should be a dialog which asks, if one wants to delete the asset OR there could be a cleanup feature to find and delete unused media items - and so much more ideas, but the global management and the reference field with visual image preview in a clear UI would be great Multidomain Support: - Manage multiple Websites with different domains within one ProcessWire installation, with optional multilanguage support - Every website has a root parent page in the page tree, where everything is defined: domain name, language, etc. - Internal links will be managed by ProcessWire, so you can link between the domains. ProcessWire would determine if the links have to be prefixed with the domain name automatically - The root parent pages will be fully abstracted away, e.g. their page names won't be applied to urls I think that we really would need a native implementation of those features. Unfortunately, I don't have the time or expertise to develop them myself and make a PR, but I would like to add them to the wish list. And if they would be implemented, I would be happy to contribute ideas, feedback and provide beta testing.
-
New post – Implementing and using TinyMCE 6 in ProcessWire
nurkka replied to ryan's topic in News & Announcements
Hi all! If I had some feature requests for InputfieldTinyMCE, where would be the right place to post them? In https://github.com/processwire/processwire-requests or here in the forum in this thread or in a new thread? -
Proposal: Every module should have an "active" checkbox
nurkka replied to nurkka's topic in Modules/Plugins
@adrian @Robin S I didn't know about these features yet – many thanks! -
Hi all, my proposal is, that every module should have an "active" checkbox, which allows to quickly deactivate it, instead of being forced to uninstall/reinstall it. Like AdminOnSteroids had and TracyDebugger has. Best regards!
-
Here is an example of the template file: $json_data = []; foreach ($page->contentblocks as $cb) { // "contentblocks" is a repeatermatrix field $json_data_block = $cb->render(); // this returns an actual json array array_push($json_data, $json_data_block); } header('Content-Type: application/json'); echo json_encode($json_data); And here is an examle of a repeatermatrix field template file: $json_data = []; $json_data["headline"] = $page->text; // "text" is a multilanguage text field $json_data["text"] = $page->body; // "body" is a multilanguage textarea/tinymce field return $json_data; // no json_encode needed Returning JSON data from the template did not work, because the $page->render() method threw an error ("strlen(): Argument #1 ($string) must be of type string, array given"). So I had to convert the JSON data to a string in the template and convert it back in the module. The advantage is, that one can view the page in the browser and check the JSON output.
-
Hello everyone, I use ProcessWire with multilangage support (5 languages) to provide a shop with content as JSON files. To match the specifications regarding the structure of those JSON files, I wrote a module that exports the files on page save. It renders a page and outputs the language content in different JSON files, within different folders. The module hooks into the Pages::saved hook. As soon as a page is saved in the admin area, the module loops over the languages, renders the page content in each language and writes a JSON file for each language. The following phenomenon now occurs: If one edits individual fields of the page in the admin area and then saves it, the JSON files are output correctly. If one saves the page again – without having changed a field on the page – $page->render() does not return the text content of the multilanguage fields, but objects with the text content of all languages. These are e.g. LanguagesPageFieldValue objects or ComboLanguagesValue objects. I believe this must have something to do with the trackChanges behaviour. ProcessWire tracks the changes that have been made to fields. If no changes have been detected, the behaviour appears to be different when saving the page and with $page->render within the Pages::saved hook. Here is the code (simplified) I am using: <?php namespace ProcessWire; class ExportAsJson extends WireData implements Module { public function init() { $this->addHookAfter('Pages::saved', $this, 'hookPageSaved', ['priority' => 200]); } protected function hookPageSaved(HookEvent $event) { $page = $event->arguments(0); // get the saved page // save current settings $saved_lang = $this->wire->user->language; $saved_user = $this->wire->user; $saved_output_formatting = $page->of(); // set current user to guest user $this->wire->users->setCurrentUser($this->wire->users->getGuestUser()); $page->of(true); $json_data = []; foreach ($this->wire->languages as $l) { $lang_name = $l->name; // set current language for rendering the page $this->wire->user->language = $l; // get rendered json data as string $markup = $page->render(); $json_data[$lang_name] = json_decode($markup); } // restore saved settings $page->of($saved_output_formatting); $this->wire->users->setCurrentUser($saved_user); $this->wire->user->language = $saved_lang; // write the JSON data to separate files on the remote shop server $this->writeJsonToSftpAsSeparateFiles($page, $json_data); } } The correct result in JSON would be like this: [ { "featureHeadline": "Lorem ipsum dolor" } ] But when I save the page without changing a field beforehand, I get this: [ { "featureHeadline": {} } ] Does anyone have an idea how to fix this behaviour? Thanks and best regards!