Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 12/13/2023 in all areas

  1. If you haven't yet noticed it, @teppo has hit the 500th issue of PW Weekly! That is a ridiculously massive milestone and an amazing achievement, and after some quick maths, also tells me that he's held strong for almost 10 years now! I'm not sure what's more surprising, that he's managed to keep it going continuously for this long, or that I remember when he started it... That's a long darn time. I may not be a hugely active member of the community, but I'm darn proud to be a part of it regardless. Thank you so much for your devotion to the PW Weekly project, @teppo!!!!
    14 points
  2. In this tutorial I want to show you how to set up a simple ajax-driven auto-complete search within ProcessWire. Something that looks like this: Requirements: 1. Use the Fieldtype Cache to create a search index For fast and easy search queries we will create a search index field. You can read more on this here: https://processwire.recipes/recipes/set-up-search-index-with-fieldtypecache/ First of all, go to Modules and install a Core module called FieldtypeCache. Add a new field “search_cache” and select “Cache” as it’s type. Save the field. On the Details tab choose the fields you want to make searchable. Save again. Add “search_cache” to all templates you want to include in your search results. Optional but recommended: use the “Regenerate Cache” option found from the Details tab of the field to make existing content instantly searchable. In my case i only want to search inside body and title fields, so I included those fields in the settings of the search cache field: 2. Install the Pages2JSON module We want to make ajax requests to the search template and those results should be returned in JSON format so that our java script can process the results inside the auto-complete dropdown. Therefore we make use of this module here: https://processwire.com/modules/pages2-json/ In the module settings we define what data will be included in the JSON object that is returned. Remember that this is the data that we want to display in our auto-complete dropdown. So for my case I only want the title and the url field. Now let's start: 1. Setting up the search template Set up a template file called „search“. Then create a page using this template. On most ProcessWire installations this is already the case and this template exits in the file system. This template will handle the search queries and list all the results for us and that in two ways: - you can send a search query and the search result page will list all the results (as you would expect) - you can send an ajax search query and the result page will return the results as a json object to be processed within javascript and displayed in the frontend in real-time search.php: <?php if($config->ajax) { // Return search results in JSON format $q = $sanitizer->selectorValue($input->get->q); $results = $pages->find('search_cache%=' . $q);; // Find all pages and save as $results header("Content-type: application/json"); // Set header to JSON echo $results->toJSON(); // Output the results as JSON via the toJSON function return $this->halt(); } ?> <main> <div class="uk-container"> <?php // look for a GET variable named 'q' and sanitize it $q = $sanitizer->selectorValue($input->get->q); // did $q have anything in it? if($q) { // Find pages that match the selector $matches = $pages->find('search_cache%=' . $q); // did we find any matches? ... if($matches->count) { echo "<h2>We found $matches->count results:</h2>"; echo "<ul class='uk-list uk-list-square'>"; foreach($matches as $match) { echo "<li><a href='$match->url'>$match->title</a>"; echo "<div class='summary'>$match->summary</div></li>"; } echo "</ul>"; } else { ?> <h2>No results found.</h2> <div uk-grid class="uk-flex uk-flex-center"> <div class="uk-width-1-1 uk-width-1-2@m"> <?= $files->render('elements/_searchbox'); ?> </div> </div> <? } } else { ?> <h2>Search:</h2> <div uk-grid class="uk-flex uk-flex-center"> <div class="uk-width-1-1 uk-width-1-2@m"> <?= $files->render('elements/_searchbox'); ?> </div> </div> <? } ?> </div> </main> Explanation: This part here at the top of the template handles the requests that are send via ajax. This is the important part for later on. <?php if($config->ajax) { // Return search results in JSON format $q = $sanitizer->selectorValue($input->get->q); $results = $pages->find('search_cache%=' . $q);; // Find all pages and save as $results header("Content-type: application/json"); // Set header to JSON echo $results->toJSON(); // Output the results as JSON via the toJSON function return $this->halt(); } ?> What this does: check if the current request is an ajax request if so, search inside the search cache field return the results in JSON format then quit processing the rest of the template (we don’t want to render any markup in that case!) Everything below this part is the normal search template logic. If you send a search request via a form somewhere on the website you want to be redirected to the result page and all the results will be listed on the page just like you would expect. 2. Make the search „work“ Create a template file called "_searchbox.php" (this file is also included in the search template code above, so adjust your paths/names accordingly). To make search requests you want to include this search form anywhere on your page. _searchbox.php: <div class="search-wrapper uk-padding uk-text-center"> <h4>Searchbox</h4> <form class="searchform uk-position-relative uk-flex" method="get" action="<?= $pages->get("template=search")->url ?>"> <input class="uk-input" id="searchInput" name="q" type="search" aria-label="Suchen" autocomplete="off" placeholder="z.B. Bagger"> <button class="uk-button uk-button-primary search-button" type="submit"> <span class="uk-visible@s">Search</span> </button> <div id="suggestions" class="uk-box-shadow-medium"> </div> </form> </div> With this simple search form you should now be able to do a basic search that leads you to a search result page. 3. Make the ajax-search „work“ Now comes the interesting part. We will add in a java script snippet into our just created _searchbox.php that sends ajax requests to the search template page while we are typing into the search intput field and it will display the results in a nice little dropdown. Feel free to adjust the code to your needs! <script> document.addEventListener("DOMContentLoaded", () => { const searchForm = document.querySelector('.searchform'); const searchUrl = searchForm.getAttribute('action'); const searchInput = document.getElementById('searchInput'); const suggestionsDiv = document.getElementById('suggestions'); let selectedSuggestionIndex = -1; // close the auto-complete container when clicked outside the element document.addEventListener('click', function(event) { if (!suggestionsDiv.contains(event.target)) { suggestionsDiv.style.display = 'none'; } }); searchInput.addEventListener('input', () => { const searchText = searchInput.value.toLowerCase(); // Immediately Invoked Function Expression (IIFE) (async () => { try { const response = await fetch(searchUrl+"?q="+searchText, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }); if (response.status === 200) { const data = await response.json(); showResults(data); } else { console.log(response.status); console.log(response.statusText); } } catch (error) { console.error(error); } })(); function showResults(data) { // Show suggestions only if the input has at least two characters if (searchText.length >= 2) { const suggestionHTML = data.map(item => { // Highlight the matching characters using a <span> element with a CSS class const highlightedTitle = item.title.replace( new RegExp(searchText, 'gi'), match => `<span class="highlight">${match}</span>` ); return `<li class="suggestion"><a href="${item.url}">${highlightedTitle}</a></li>`; }).join(''); // Array to string conversion suggestionsDiv.innerHTML = ''; // Clear the suggestions if input length is less than two characters // create list and append search results const suggestionList = document.createElement("ul"); suggestionList.classList.add('suggestion-list', 'uk-list'); suggestionList.innerHTML = suggestionHTML; suggestionsDiv.appendChild(suggestionList); selectedSuggestionIndex = -1; // show the results suggestionsDiv.style.display = "block"; } else { suggestionsDiv.innerHTML = ''; // Clear the suggestions if input length is less than two characters } } }); // Event listener for arrow key presses searchInput.addEventListener("keydown", function (event) { const suggestions = document.querySelectorAll(".suggestion"); if (event.key === "ArrowDown") { event.preventDefault(); selectedSuggestionIndex = Math.min( selectedSuggestionIndex + 1, suggestions.length - 1 ); } else if (event.key === "ArrowUp") { event.preventDefault(); selectedSuggestionIndex = Math.max(selectedSuggestionIndex - 1, -1); } else if (event.key === "Enter") { event.preventDefault(); if (selectedSuggestionIndex === -1) { searchForm.submit(); } else { window.location = suggestions[selectedSuggestionIndex].querySelector('a').href; } } // Highlight the selected suggestion suggestions.forEach((suggestion, index) => { suggestion.classList.toggle( "selected", index === selectedSuggestionIndex ); }); }); }); </script> Keep in mind that you need some CSS styes to make it look good and actually work. These are my styles(in LESS format) but feel free to write you own stylesheet for this. search.less: .search-wrapper { position: relative; background: @secondary-blue; h4 { color: @primary-blue; } .highlight { color: @primary-red; } } #suggestions { display: none; position: absolute; top: 100%; left: 0; right: 0; z-index: 10; background: @white; .suggestion-list { margin: 0; li { transition: background-color 150ms ease-in-out; a { padding: 10px; display: block; text-align: left; &:hover, &:focus, &:active { text-decoration: none; } } &:hover, &:focus, &:active, &.selected { background: @secondary-blue; } } li + li { border-top: 1px solid @secondary-blue; } } } That's it! Again feel free to adjust all of the code to your needs. If you have any suggestions how to achieve things a bit easier just let me know.
    6 points
  3. Well, since @monollonom tagged me I thought I'd tinker. I added a one-click button to Fluency that translates any module file, core or otherwise, in seconds. It's integrated with the native ProcessWire language translator pages. Some details here. It makes it entirely possible for everything a user interacts with in the admin to be translated within minutes, not hours. I didn't know about this module... this looks great! Language is a tough nut to crack and applying a universal solutions are difficult. It's huge task for a module developer, contributor, or a core maintainer like Ryan, to take on when they are providing free and open source software for the community. The fact that ProcessWire handles language so well to begin with shows a thoughtful approach by Ryan and ProcessWire contributors, especially if it is as rarely as you assume. Try Fluency and let me know if it helps out.
    5 points
  4. Hear, hear! I love the PW Weekly - a highlight of the week! Putting out such a well-written newsletter week after week for 10 years - what an incredible contribution to the community! Hats off to @teppo! I like to read each new issue when it's hot off the press, which is normally on a Sunday (NZ time), but the official email from newsletter@processwire.com doesn't arrive until the following Saturday. A long while ago I asked Ryan why this is and there was a sensible reason that I've forgotten now, but I can't wait that long for my PW news ?. So my solution has been to set up an email alert for the RSS feed using Blogtrottr - that way I can dive into each issue when it's at maximum freshness ✨?
    4 points
  5. "Everything" in ProcessWire is a page. So for example I have a "Global Settings" Page (which is actually a page!) in my backend here: Inside this page you can add whatever fields you like to store data that you want to use "globally" on your website. For example text blocks, or repeaters that contain page references to use as a footer menu: In my ready.php file have this global variable that acts as a page reference to the settings page: // Global Settings Page as API variable $wire->wire('globalSettings', $pages->get('template=global-settings')); Then on any template file I can access a field on that global settings page like this: <?= $globalSettings->body; ?>
    4 points
  6. Got tagged by @monollonom over in this Weekly Update post. Dropped a new Fluency feature. One click translation for any module, core or otherwise. I translated all user-facing strings for Fluency itself to Slovak in 4 seconds. This may be useful for developers who need to get modules that end users will interact with in the admin localized much much faster. This uses a feature in Fluency where you can bulk translate a large group of texts to from one language to another at the same time. Just for kicks I translated the entire ProcessWire core in under 30 minutes- that includes modules I don't even use. It would certainly need some proofreading, but the amount of work up front is significantly reduced. Available on the dev branch.
    4 points
  7. @nloyolareading my post after almost 2 month, I realize it can indeed be confusing if you are not deep into the code. In the meantime there is a method createRepeaterMatrixField() available that should avoid this problem. See https://github.com/baumrock/RockMigrations#repeatermatrix-field
    2 points
  8. This week I've continued work on the page versions support that I wrote about last week. While the main PagesVersions module needs more work before it's ready to commit to the dev branch and test externally, it is so far working quite well in internal testing. I mentioned last week how it will support an interface where Fieldtypes can declare that they will handle their own versions. That interface has been pushed to the dev branch as FieldtypeDoesVersions. I've also implemented it with the Repeater Fieldtype, which is visible starting here. Repeaters are so far working really well with versions! As far as core Fieldtypes go, Repeater, PageTable and FieldsetPage are likely the only ones that will need custom implementations. For ProFields, RepeaterMatrix already works with the implementation in the core Repeater (already tested). It's possible that other ProFields will not need custom implementations, though not yet positive. The module that provides version capability is called PagesVersions and the plan so far is to make it a core module that provides API version support for pages. A separate module provides interactive version support in the page editor. I've built this module initially so that I can test versions more easily, but it'll be expanded to provide a lot more. Below is a screenshot if what it looks like in the page editor Settings tab so far: As you can see, you can Edit or Restore any existing version. You can also create a new version from the Live version, or any other version. And of course you can view and delete any versions. When you Restore a version, it essentially replaces the live version with the one that you are restoring. All of this can be done from the module's API as well. Note that the API is provided by a $pagesVersions API variable that is present when PagesVersions module is installed. The API method names and such are a bit verbose right now but may be simplified before it's final. // Get page and add a new version of it $page = $pages->get(1234); $page->title = 'New title'; $version = $pagesVersions->addPageVersion($page); echo $version; // i.e. "2" // Get version 2 of a page $pageV2 = $pagesVersions->getPageVersion($page, 2); // Update a version of a page $pageV2->title = "Updated title"; $pagesVersions->savePageVersion($pageV2); // Restore version to live page $pagesVersions->restorePageVersion($pageV2); // Delete page version $pagesVersions->deletePageVersion($pageV2); Thanks for reading! More next week.
    1 point
  9. Hi @alexm, Sorry, I saw this late. Yes, that's the easiest way to do do it. You could also have done this programmatically but much easier at the DB level especially if you don't have many line items. A third way would have been to recreate the whole order manually in the backend using Padloper but this would have meant creating a new order. As you have found out the hard way, manual order creation and frontend orders are not compatible (for now) ?.
    1 point
  10. S'all good. I created 2 new children (order line items) under the order page and then went in the DB and added the relevant data in there
    1 point
  11. Can you share what platform(s) you use as an alternate to ProcessWire in these instances? As the developer of a language module maybe there is some inspiration to be had.
    1 point
  12. It’s actually on the current master version (3.0.229) though the fix for svg files mentionned in the issue as well is only on the dev one
    1 point
  13. @Robin S brought up this issue not so long ago and it’s been fixed since. So yes it does pull from the database directly, see https://github.com/processwire/processwire-issues/issues/1812
    1 point
  14. Great way of managing the docs. I'll make a PR.
    1 point
  15. Something I've wanted in ProcessWire for a long time is full version support for pages. It's one of those things I've been trying to build since ProcessWire 1.0, but never totally got there. Versioning text and number fields (and similar types) is straightforward. But field types in ProcessWire are plugin modules, making any type of data storage possible. That just doesn't mix well with being version friendly, particularly when getting into repeaters and other complex types. ProDrafts got close, but full version support was dropped from it before the first version was released. It had just become too much to manage, and I wanted it to focus just on doing drafts, and doing them as well as we could. ProDrafts supports repeaters too, though nested repeaters became too complex to officially support, so there are still some inherent limitations. I tried again to get full version support with a module called PageSnapshots developed a couple years ago, and spent weeks developing it. But by the time I got it fully working with all the core Fieldtypes (including repeaters), I wasn't happy with it. It was functional but had become too complex for comfort. So it was never released. This happens with about 1/2 of the code I write – it gets thrown out or rewritten. It's part of the process. What I learned from all this is that it's not practical for any single module to effectively support versions across all Fieldtypes in ProcessWire. Instead, the Fieldtypes themselves have to manage versions of their own data, at least in the more complicated cases (repeaters, ProFields and such). The storage systems behind Fieldtypes are sometimes unique to the type, and version management needs to stay internal [to the Fieldtype] in those cases. Repeaters are a good example, as they literally use other pages as storage, in addition to the field_* tables. For the above reasons, I've been working on a core interface for Fieldtypes to provide their own version support. Alongside that, I've been working on something that vaguely resembles the Snapshots module's API. But rather than trying to manage versions for page field data itself, it delegates to the Fieldtypes when appropriate. If a Fieldtype implements the version interface, it calls upon that Fieldtype to save, get, restore and delete versions of its own data. It breaks the complexity down into smaller chunks, to the point where it's no longer "complexity", and thus reasonable and manageable. It's a work in progress and I've not committed any of it to the core yet, but some of this is functional already. So far it's going more smoothly than past attempts due to the different approach. My hope is to have core version support so that modules like ProDrafts and others can eventually use that API to handle their versioning needs rather than trying to do it all themselves. I also hope this will enable us to effectively version the repeater types (including nested). I'm not there yet, but it's in sight. If it all works out as intended, the plan is to have a page versions API, as part of the $pages API. I'll follow up more as work continues. Thanks for reading and have a great weekend!
    1 point
  16. You could be right on that. I'm thinking however that it would be beneficial for three (subjective) reasons: 1. It's easier to see the status of translations and contribute to them from a GUI. 2. I personally don't want to create a PR for just some of the translations. With that system you could add some suggestions here and there 3. It would be easier for non-tech people, although admittedly most of the ProcessWire users probably are Haven"t seen it, but it does seem a lot easier than the core way to add translations.
    1 point
  17. Fluency has hit v1.0.1 and has been submitted to the modules directory, should be available pending review/approval. Really appreciate everyone's feedback and input to help on this project. All new features from the development branch have been added to the latest release. This is a recommended upgrade for everyone that also fixes a couple of issues. https://github.com/SkyLundy/Fluency/releases/latest If you there are any bugs found during usage, please file an issue on Github where you'll get credit for your help, or consider creating a pull request with a fix! @ryangorley That is most excellent to hear and I am very glad it helped! I've re-added a donation box on the module config page in the latest release, very appreciative of your support!
    1 point
  18. Have you seen RockLanguage? There was no feedback at all...
    1 point
  19. You are right @bernhard, my wording wasn't completely fair – sorry for that. Thank you for all the time you've put into RockMigrations. Of course you are not obliged to take care of features that you don't need. It's open source, after all. I hope you get some subscriptions for your premium plugins. I guess the person really responsible for all the drama in this thread here is @ryan. I'd expect from a premium module like Pro Fields (NOT FOSS software) that it would provide a robust migration/syncing mechanism along with it. Otherwise, the ecosystem is doomed to playing catch-up forever. I'm pretty concerned with starting to learn and use a new CMS where deployment is such a big pain point. The WordPress vs ProcessWire article needs a serious overhaul IMO. I'm getting more and more frustrated by so many systems claiming that they are superior and then exposing such dirty corners (the ProcessWire panel feels pretty outdated to me as well to be honest).
    1 point
  20. I'm not 100% happy with that wording. No offense, just want to be clear on that. It's not that I don't want to support RepeaterMatrix to push sales of RockPageBuilder. I'm just not using RepeaterMatrix any more because RockPageBuilder is far better for what I use it and it integrates well with RockMigrations. RockMigrations is a module containing thousands of hours of work that you get for free and that does not pay any bills for me. I just can't put any (more) unpaid work into it unless I need the features for my own work. But I'm happy to support RepeaterMatrix and any other pro field of Ryan if people provide PRs even though that might mean less sales for RockPageBuilder. I'd be even more happy if @MarkE had a look at RockMigrations and used it as common base so that we can work together on an api that does all the heavy lifting - independent from wether it is used by RockMigrations or MarkE's GUI. And even more happy, of course, if we had a migrations API in the core...
    1 point
  21. Hello I am trying to use this module on a site. And I am getting the following error when trying to add a video to a page. - Param given by constructor is not valid gd resource Any clue what this is? [UPDATE] I replace the content of the folder ffmpeg-php inside the module, for a recent version of this library, and this solve that issue. And edit the line 114 of the file InputfieldVideo.module.php to prevent a PHP error about Implicit conversion from float to int From this $duration = gmdate("H:i:s", $media->getDuration()); To this $duration = gmdate("H:i:s", intval($media->getDuration()));
    1 point
  22. I do the following. Copy all files of the live site via FTP to my local dev environment (e.g, /htdocs/pw-test). Create a backup of the MySQL/MariaDB via phpMyAdmin (online). Open my local dev instance of phpMyAdmin (XAMPP) and import the database. You may need to change the DB settings in your local /site/config.php file in case the DB credentials differ between live and local DB connection.
    1 point
  23. @ryan Good points. The slim core and the enormous flexibility is why so many love ProcessWire and trust you with your decisions. Still I want to comment on one point: I know it is simple, but the reality is that in client projects it often still ends up being a mix of <language> and English, because not all modules provide translations (the community seldom submits pull requests for languages) and there is no time to translate everything. I think that ProcessWire is lacking in this area has to do with you rarely needing it in another language but English. My idea how this could be improved would involve three relative simple steps: 1. Set up a community translation tool, like Weblate (https://weblate.org/), where people that want to contribute can easily see which translations are still missing. Weblate is really good feature-wise, but the interface for adding translations could be simpler, so another tool might fit better. 2. Write a crawler that goes through all the uploaded modules and core files, extract the translatable phrases and upload those packages to Weblate 3. Write a ProcessWire module that maps the languages defined in ProcessWire with country codes, then automatically downloads the matching translation files from Weblate whenever a module is added or updated In general: would that something you would consider useful? If so I could try implementing a POC when I find the time.
    1 point
  24. I’ve never worked with panels (just yet) but from what I’m seeing if you have a view ready in your Process module, say “__executeSomething()”, isn’t it just a matter of having a link pointing to that view? <a href="/something/" class="pw-panel">Open in panel</a>
    1 point
  25. Hi @gebeer no, of course the page class' migrate() method can only be called once the template exists. But that is done automatically by RM if you use the createTemplate() method: That means all you need is to create your pageclass in /site/classes/FooPage.php and then in your Site.module.php (or wherever else) you do $rm->createTemplate('foo'); This is an example of my modules overview page on baumrock.com where I'm even too lazy to create the view file manually to make the page be publicly accessible, but you can do that manually if you prefer: <?php namespace ProcessWire; use RockMigrations\MagicPage; class ModulesPage extends Page { use MagicPage; const tpl = 'modules'; public function migrate() { $rm = $this->rockmigrations(); $rm->createViewFile($this); $rm->migrate([ 'fields' => [], 'templates' => [ $this->getTplName() => [ 'fields' => [ 'title', RockPageBuilder::field_blocks, ], 'childTemplates' => [ 'module', ], 'sortfield' => 'title', ], ], ]); } } This is my migrate() in Site.module.php: public function migrate() { /** @var RockMigrations $rm */ $rm = $this->wire->modules->get('RockMigrations'); // cleanup/reverts $this->cleanup($rm); $rm->installModule("RockPageBuilder"); $rm->setPagenameReplacements('de'); $rm->setModuleConfig('AdminThemeUikit', [ // use consistent inputfield clicks // see https://github.com/processwire/processwire/pull/169 'toggleBehavior' => 1, ]); $rm->setModuleConfig('ProcessPageList', [ 'useTrash' => true, // show trash in tree for non superusers ]); // install german language pack for the default language // this will install language support, download the ZIP and install it // $rm->setLanguageTranslations('DE'); // $rm->installModule("LanguageSupportFields"); // $rm->installModule("LanguageSupportPageNames"); // $rm->installModule("LanguageTabs"); // $rm->setFieldData("title", ['type' => 'textLanguage']); $rm->installModule("RockPdf"); $rm->installModule("RockMoney"); $rm->installModule("RockSearch"); $rm->installModule("RockMails"); $rm->installModule("RockForms"); $rm->installModule("RockCommerce"); ... // create global fields $rm->migrate([ 'fields' => [ self::field_search => [ 'type' => 'RockSearch', ], ], ]); $rm->createTemplate('pdf'); $rm->createTemplate('modules'); $rm->createTemplate('module'); $rm->createTemplate('release'); $rm->createTemplate('releases'); $rm->createPage( template: 'releases', parent: '/', title: 'Releases', status: ['hidden'], ); ... } Language specific migrations are commented out for performance reasons and only necessary once or maybe if new updates are available.
    1 point
  26. Just sent out the October Newsletter and at the moment we're holding at 49 subscribers, one being myself ? and basically stagnating since last month (+1). Not sure if the newsletter is helpful for anybody, but I try my best to provide interesting content. Here are some numbers from the first two issues: If anybody has some ideas, feedback or other input feel free ? The first two issues were basically written by ChatGPT with some minor manual tweaks. The input came from a RockShell script that scans all my repos and then creates a log and lists all commits. ChatGPT did a quite good job in converting these into some nice sentences. This months version I decided to put more manual work into it. There have been less commits and so it was possible to give it a more personal touch. Have a great month! PS: It's not too late ? https://www.baumrock.com/rock-monthly/ ?
    1 point
  27. This is the rm-defaults snippet which I always use and works for me: <?php $rm->setPagenameReplacements('de'); $rm->setModuleConfig('AdminThemeUikit', [ // use consistent inputfield clicks // see https://github.com/processwire/processwire/pull/169 'toggleBehavior' => 1, ]); $rm->setModuleConfig('ProcessPageList', [ 'useTrash' => true, // show trash in tree for non superusers ]); // install german language pack for the default language // this will install language support, download the ZIP and install it $rm->setLanguageTranslations('DE'); $rm->installModule('LanguageSupportFields'); $rm->installModule('LanguageSupportPageNames'); $rm->installModule('LanguageTabs'); $rm->setFieldData('title', ['type' => 'textLanguage']);
    1 point
  28. From PW Weekly: This week we're happy to introduce a brand new module from Bernhard Baumrock, called RockLanguage. As the name suggests, RockLanguage is a tool for dealing with translations, and more specifically with translations related to ProcessWire modules. While ProcessWire natively ships with extensive language support, including the ability to ship module translations with the module itself, this does still require some manual work. That is exactly what RockLanguage aims to solve. Here's what the module translation process looks like with RockLanguage: Install RockLanguage on your site and (optionally) configure custom language code mapping via the module configuration screen. Add a directory for the language you'd like to include translations for within your own module's directory, e.g. /site/modules/MyModule/RockLanguage/FI/ for the Finnish language. Translate your module for said language via ProcessWire's translations manager. RockLanguage will automatically notice the update and duplicate the translation file from its original source to the directory you've just created. Now if you install this module to another site with the language folder included, and the site has RockLanguage installed and Finnish as one of its languages, the translation files for your module will be automatically synced with ProcessWire. What's nice about this workflow is that it takes some manual steps out of the equation, thus streamlining translation management. It's too early to say how widely this module will be adopted among public third party modules, but if you like the concept, you can easily start using it for your own modules right away. Download + Docs: baumrock.com/RockLanguage
    1 point
  29. I wanted to update this thread because I had to solve some unexpected problems when importing my content. Maybe documenting this will save someone else the struggle—even me, if my memory fails and I search the forum for answers to the same question. Firstly, my site requires multi-language URLs, and titles. This requires some deliberate configuration with the multi-language modules that must be taken care of BEFORE attempting to import data. This seems obvious in hindsight, but I assumed that these features were enabled by default once the core Multi-language module was active. One thing I noticed when attempting to import data was that the setLanguageValue() method would not work for the name field, even after enabling multi-language URLs. I had to do a lot of searching the forum before finding out that the language-specific name fields have a different way of assigning values via the API than what I could find in the Multi-language documentation. In the end, using the API to assign appropriate data to the fields in ProcessWire required two incantations that I failed to find in the Multi-language docs: In order to make the non-default language(s) "active", I had to use the page()->set() method to establish a hidden property "status$language". This property did not appear as unset when I buffer dumped the page() using TracyDebugger. Assigning the URL title (commonly called the "name" in ProcessWire) in the non-default language(s) required using the same set method to establish a "name$language" property. Here's a stripped down, abstracted example of my working import script, so far. <?php namespace ProcessWire; $en = languages()->get("default"); $es = languages()->get("es-es"); $articles = = database()->query(" // Magic SQL query here "); foreach ($articles as $article) { $parent = pages()->get('/' . IMPORT_SECTION); $template = templates()->get('my-article-template'); $newArticle = pages()->add($template, $parent); $newArticle->set("status$es", 1); $newArticle->title->setLanguageValue($en, $article['Title_en']); $newArticle->title->setLanguageValue($es, $article['Title_es']); $newArticle->name = sanitizer()->pageName($article['url_title_en']); $newArticle_name_es = sanitizer()->pageName($article['url_title_es']); $newArticle->set("name$es", $newArticle_name_es); $newArticle->body->setLanguageValue($en, $article['Body_en']); $newArticle->body->setLanguageValue($es, $article['Body_en']); // Etc., setting each field's value using setLanguageValue() method $newArticle->save(); } I have moved on to other aspects of the project, but I wanted to post this before I forget. It makes me wonder what else I'm missing out on, and how I might learn to do this better. Thank you!
    1 point
  30. This is now possible using owner selectors! http://processwire.com/blog/posts/processwire-3.0.95-core-updates/ $pages->find('template=repeater_collections_detail_images, your_repeater_field.owner.collections_detail_designer=123, include=hidden, sort=name');
    1 point
  31. Latest dev branch: https://github.com/baumrock/RockMigrations/blob/da0b08b9cc65ee5573a5041971fe6ec575130b98/RockMigrations.module.php#L4107 In the phpdoc the return value is RepeaterMatrixField|null. But inside the function when the field is created with $this->createField($name, 'FieldtypeRepeaterMatrix', $options); it seems to return the generic class Field. I also sometimes experienced the problem, that you described. As a workaround, I first created the field and then added the items in a second run inside a conditional which tests for the correct class of the created field from the first run. But in my latest tests, this did not occur. Unfortunately I can't seem to find out why this is happening sometimes.
    0 points
  32. Hello! I still have a problem related to creation of repeater matrix types. There is a function: public function createRepeaterMatrixField(string $name, array $options, bool $wipe = false) { $items = array_key_exists('matrixItems', $options) ? $options['matrixItems'] : null; if ($items) unset($options['matrixItems']); // var_dump($items); // create field $field = $this->createField($name, 'FieldtypeRepeaterMatrix', $options); // populate matrix items if ($field && wireInstanceOf($field, 'RepeaterMatrixField')) { $this->setMatrixItems($field, $items, $wipe); } return $field; } $field variable is supposed to be RepeaterMatrixField, but it's only a Field. What I'm doing wrong? I use the code from the documentation from https://github.com/baumrock/RockMigrations/tree/dev#repeatermatrix-field. Can you please tell me why it's Field and not RepeaterMatrixField? Edit 1: I think it's field because $this->createField returns Field but not RepeaterMatrixField. Edit 2: If I use setMatrixItems, then items are being added in the second run.
    0 points
×
×
  • Create New...