Jump to content

Search the Community

Showing results for 'render('.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Welcome to ProcessWire
    • News & Announcements
    • Showcase
    • Wishlist & Roadmap
  • Community Support
    • Getting Started
    • Tutorials
    • FAQs
    • General Support
    • API & Templates
    • Modules/Plugins
    • Themes and Profiles
    • Multi-Language Support
    • Security
    • Jobs
  • Off Topic
    • Pub
    • Dev Talk

Product Groups

  • Form Builder
  • ProFields
  • ProCache
  • ProMailer
  • Login Register Pro
  • ProDrafts
  • ListerPro
  • ProDevTools
  • Likes
  • Custom Development

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


AIM


MSN


Website URL


ICQ


Yahoo


Jabber


Skype


Location


Interests

  1. I think you have to do this by hand and render each page with the table. Example from the core.
  2. @Ivan Gretsky, just wanted to let you know that I'm currently working on the theme feature, but it will require a bit more testing at least. Admittedly this was a bit more complicated than I had originally assumed. That being said, I've just pushed an initial version with a new "view prefix" feature to dev branch, in case you have time / want to give it a try: https://github.com/wireframe-framework/Wireframe/tree/dev. This is pretty much the first option that you described. I ended up rewriting it a couple of times, though, but hopefully have now landed on a solution that makes sense. View prefix can be set in a couple of different ways, but likely the easiest is calling Wireframe::setViewPrefix() in the bootstrap file (wireframe.php): // init Wireframe $wireframe = $modules->get('Wireframe'); $wireframe->init(); // if we're on a theme page, set view prefix if ($page->template == 'theme-home') { $wireframe->setViewPrefix('theme/'); } // render the page echo $wireframe->render(); View prefix will be used whenever Wireframe is rendering a (template/page) view file, component view file, or partial file. It should also work with separate renderer modules, but that's not something I've yet had a chance to test. At least for now default view files are used in case a prefixed version doesn't exist. This could be made configurable, but it felt more intuitive to me that this is (at least) the default behaviour. Note that in the example above I've intentionally used a prefix that is a path, e.g. has "/" — it's not necessary to use a path, it could just as well be something like "alternative-" 🙂 And a big, big warning at the end: I quite literally got this thing up and running half an hour ago. I've done very little testing, so it's entirely possible that there are still issues with it. I hope to get back to it and test properly soon.
  3. Hello community! I want to share a new module I've been working on that I think could be a big boost for multi-language ProcessWire sites. Fluency is available in the ProcessWire Modules Directory, via Composer, and on Github Some background: I was looking for a way for our company website to be efficiently translated as working with human translators was pretty laborious and a lack of updating content created a divergence between languages. I, and several other devs here, have talked about translation integrations and the high quality services now available. Inspired by what is possible with ProcessWire, I built Fluency, a third-party translation service integration for ProcessWire. With Fluency you can: Translate any plain textarea or text input Translate any TinyMCE or CKEditor (inline, or regular) Translate page names/URLs Translate in-template translation function wrapped strings Translate modules, both core and add-ons Installation and usage is completely plug and play. Whether you're building a new multi-language site, need to update a site to multi-language, or simply want to stop manually translating a site and make any language a one-click deal, it could not be easier to do it. Fluency works by having you match the languages configured in ProcessWire to those offered by the third party translation service you choose. Currently Fluency works with DeepL and Google Cloud Translation. Module Features Translate any multilanguage field while editing any page. Translate fields in Repeater, Repeater Matrix, Table, Fieldset Page, Image descriptions, etc. Translate any file that added in the ProcessWire language pages. It's possible to translate the entire ProcessWire core in ~20 minutes Provide intuitive translation features that your clients and end-users can actually use. Fluency is designed for real-world use by individuals of all skill levels with little to no training. Its ease-of-use helps encourage users to adopt a multilanguage workflow. Start for free, use for free. Translation services supported by Fluency offer generous free tiers that can support regular usage levels. Fluency is, and will always be, free and open source. Use more than one Translation Engine. You may configure Fluency to use either DeepL, Google Cloud Translation, or both by switching between them as desired. AI powered translations that rival humans. DeepL provides the highest levels of accuracy in translation of any service available. Fluency has been used in many production sites around the world and in commercial applications where accuracy matters. Deliver impressive battle-tested translation features your clients can count on. Disable translation for individual fields. Disable translation for multilanguage fields where values aren't candidates for translation such as phone numbers or email addresses Configure translation caching. Caching can be enabled globally so that the same content translated more than once anywhere in ProcessWire doesn't count against your API usage and provides lightning fast responses. Set globally ignored words and text. Configure Fluency to add exclusionary indicators during translation so that specific words or phrases remain untranslated. This works either for specific strings alone, or present in other content while remaining grammatically correct in translation. Choose how translation is handled for fields. Configure Fluency to have buttons for either "Translate from {default language}" on each tab, or "Translate To All Languages" to populate every language for a field from any language to any language you have configured. No language limits. Configure as few or as many languages as you need. 2, 5, 10, 20 language website? Absolutely possible. If the translation service you choose offers a language, you can use it in ProcessWire. When new languages are introduced by third parties, they're ready to use in Fluency. Visually see what fields and language tabs have modified content. Fluency adds an visual indication to each field language tab to indicate which has different content than when opening the edit page. This helps ensure that content updated in one language should be updated in other languages to prevent content divergence between languages. Render language meta tags and ISO codes. Output alt language meta tags, add the current language's ISO code to your <html lang=""> attribute to your templates that are automatically generated from accurate data from the third party translation service. Build a standards-compliant multi-language SEO ready page in seconds with no additional configuration. Render language select elements. - Fluency can generate an unordered list of language links to switch between languages when viewing your pages. You can also embed a <select> element with JS baked in to switch between languages when viewing your pages. Render it without JS to use your own. Manage feature access for users. Fluency provides a permission that can be assigned to user roles for managing who can translate content. Track your translation account usage. View your current API usage, API account limit, and remaining allotment to keep an eye on and manage usage. (Currently only offered by DeepL) Use the global translation tool. Fluency provides translation on each field according to the languages you configure in ProcessWire. Use the global translation tool to translate any content to any language. Use Fluency from your templates and code. All translation features, usage statistics, cache control, and language data are accessible globally from the $fluency object. Perform any operation and get data for any language programmatically wherever you need it. Build custom AJAX powered admin translation features for yourself. Fluency provides a full RESTful API within the ProcessWire admin to allow developers to add new features for ProcessWire applications powered by the same API that Fluency uses. Robust plain-language documentation that helps you get up to speed fast. Fluency is extremely easy to use but also includes extensive documentation for all features both within the admin and for the Fluency programming API via the README.md document. The module code itself is also fully annotated for use with the ProDevTools API explorer. Is and will always be data safe. Adding, upgrading, or removing Fluency does not modify or remove your content. ProcessWire handles your data, Fluency sticks to translating. Full module localization. Translate Fluency itself to any language. All buttons, messages, and UI elements for Fluency will be presented in any language you choose for the ProcessWire admin. Built for expansion. Fluency provides translation services as modular "Translation Engines" with a full framework codebase to make adding new translation services easier and more reliable. Contributions for new translation services are welcome. Fluency is designed and built to provide everything you need to handle incredibly accurate translations and robust tools that make creating and managing multi-language sites a breeze. Built through research on translation plugins from around the web, it's the easiest and most friendly translation implementation for both end users and developers on any CMS/CMF anywhere. Fluency complements the built-in first class language features of ProcessWire. Fluency continues to be improved with great suggestions from the community and real-world use in production applications. Big thanks to everyone who has helped make Fluency better. Contributions, suggestions, and bug reports welcome! Please note that the browser plugin for Grammarly conflicts with Fluency (as it does with many web applications). To address this issue it is recommended that you disable Grammarly when using Fluency, or open the admin to edit pages in a private window where Grammarly may not be loaded. This is a long-standing issue in the larger web development community and creating a workaround may not be possible. If you have insight as to how this may be solved please visit the Github page and file a bugfix ticket. Enhancements Translate All Fields On A Page Compatibility with newest rewrite of module is in progress... An exciting companion module has been written by @robert which extends the functionality of Fluency to translate all fields on a page at once. The module has several useful features that can make Fluency even more useful and can come in handy for translating existing content more quickly. I recommend reading his comments for details on how it works and input on best practices later in this thread. Get the module at the Github repo: https://github.com/robertweiss/ProcessTranslatePage Requirements: ProcessWire 3.0+ UIKit Admin Theme That's Fluency in a nutshell. The Module Is Free This is my first real module and I want to give it back to the community as thanks. This is the best CMS I've worked with (thank you Ryan & contributors) and a great community (thank you dear reader). DeepL Developer Accounts In addition to paid Pro Developer accounts, DeepL now offers no-cost free accounts. All ProcessWire developers and users can use Fluency at no cost. Learn more about free and paid accounts by visiting the DeepL website. Sign up for a Developer account, get an API key, and start using Fluency. Download You can install Fluency by adding the module to your ProcessWire project using any of the following methods. Method 1: Within ProcessWire using 'Add Module From Directory' and the class name Fluency Method 2: Via Composer with composer require firewire/fluency Method 3: Download from the Github repository and unzip the contents into /site/modules/ Feedback File issues and feature requests here (your feedback and testing is greatly appreciated): https://github.com/SkyLundy/Fluency/issues Thank you! ¡Gracias! Ich danke Ihnen! Merci! Obrigado! Grazie! Dank u wel! Dziękuję! Спасибо! ありがとうございます! 谢谢你
  4. This module adds CSV import and export functionality to Profields Table fields on both the admin and front-end. http://modules.processwire.com/modules/table-csv-import-export/ https://github.com/adrianbj/TableCsvImportExport Access to the admin import/export for non-superusers is controlled by two automatically created permissions: table-csv-import and table-csv-export Another permission (table-csv-import-overwrite) allows you to control access to the overwrite option when importing. The overwrite option is also controlled at the field level. Go to the table field's Input tab and check the new "Allow overwrite option" if you want this enabled at all for the specific field. Please consider limiting import overwrite option to trusted roles as you could do a lot of damage very quickly with the overwrite option Front-end export of a table field to CSV can be achieved with the exportCsv() method: // export as CSV if csv_export=1 is in url if($input->get->csv_export==1){ $modules->get('ProcessTableCsvExport'); // load module // delimiter, enclosure, file extension, multiple fields separator, names in first row $page->fields->tablefield->exportCsv('tab', '"', 'tsv', '|', true); } // display content of template with link to same page with appended csv_export=1 else{ include("./head.inc"); echo $page->tablefield->render(); //render table - not necessary for export - just displaying the table echo "<a href='./?csv_export=1'>Export Table as CSV</a>"; //link to initiate export include("./foot.inc"); } Front-end import can be achieved with the importCsv() method: $modules->get('TableCsvImportExport'); // load module // data, delimiter, enclosure, convert decimals, ignore first row, multiple fields separator, append or overwrite $page->fields->tablefield->importCsv($csvData, ';', '"', true, false, '|', 'append'); Please let me know if you have any problems, or suggestions for improvements. Enjoy!
  5. Hey @Jan Romero that's true, but it shows it in a totally different way. I wanted to have a list of all methods that I can hook into, in the order of execution! I don't know how tracy sorts the list of added hooks, but it's definitely not execution order. On the other hand tracy debugger adds nice links to the hooks and shows some more information, like for RockFrontend: after RockFrontend::addAlfredStyles() AdminStyleRock::addAlfredStyles() class method 100.0 And that line does not show up in my hooks log: cat hooks.txt | grep RockFrontend ProcessWire\RockFrontend::addLiveReload ProcessWire\RockFrontend::render ProcessWire\RockFrontend::loadLatte ProcessWire\RockFrontend::addLiveReload RockFrontend\StylesArray::renderAssets RockFrontend\ScriptsArray::renderAssets Oh... that actually makes sense, because I have not had an ALFRED call on that page yet, so obviously the hookable method does not get called! 😄 I added it and boom, there it is: cat hooks.txt | grep RockFrontend ProcessWire\RockFrontend::addLiveReload ProcessWire\RockFrontend::render ProcessWire\RockFrontend::loadLatte ProcessWire\RockFrontend::getIcons ProcessWire\RockFrontend::addLiveReload ProcessWire\RockFrontend::addAlfredStyles RockFrontend\StylesArray::renderAssets RockFrontend\StylesArray::renderAssets RockFrontend\ScriptsArray::renderAssets RockFrontend\ScriptsArray::renderAssets Another one. I saved a page from the backend and the grep for "::save" looks like this: cat hooks.txt | grep ::save ProcessWire\Pages::save ProcessWire\Pages::saveReady ProcessWire\Pages::savePageOrFieldReady ProcessWire\Pages::saved ProcessWire\Pages::savedPageOrField I think this is really neat! Maybe @adrian has an idea how we could integrate this into Tracydebugger and maybe get the best of both worlds (tracy current implementation + my hacky one)? I think it would need a modification in the core, but that should not be a big deal for @ryan.
  6. I don't know when I last found a tool for my daily work that had such a huge impact as Latte. The last one was ProcessWire I guess (which had an even greater impact of course, but still). Latte uses PHP syntax so you don't have to learn a new language! It plays extremely well with the PW API and it makes my markup files so much cleaner and easier to read and maintain I can't tell you. From time to time I stumble over situations where I'm once more impressed by latte, so this thread is intended to collect small latte snippets to show how one can use it. If you want use Latte for your ProcessWire projects you can either install it via composer or you wait some more days until I release RockFrontend - a frontend module that can render latte files and comes with several other nice frontend helpers ? ----- Breadcrumbs the Latte+PW way: <ul> <li n:foreach="$page->parents()->add($page) as $item"> <a href="{$item->url}" n:tag-if="$item->id != $page->id"> {$item->title} </a> </li> </ul> The cool thing here is that the last item (the page we are currently viewing) is NOT linked. The <a> tag is only rendered for all other items. Still the label of the link is rendered, because we are using n:tag-if and not n:if ----- Output an image, but only if it exists: <img n:if="{$page->yourimage}" src="{$page->yourimage->maxSize(400,300)->url}" alt="..."> Note that the ->maxSize() call will NOT throw an error even if you didn't upload an image! This is one of my favourite features of latte: Write the markup once and then add simple instructions directly within that tag. No more if/else/foreach chaos ?
  7. Hi all, I've been doing some work to extend this great module created by @nbcommunication to meet a specific need we have. This is very much as-yet untested in production, but I wanted to share it here now anyway in case anyone wants to point out any flaws in the logic, try out the changes or suggest improvements before we push it to any live sites or make a PR to NB. The challenge: We manage a number of sites where we would like to get forms behind Turnstile. But in many cases these forms are connected to or hosted by third-party CRMs/Email marketing platforms etc which makes integrating Turnstile tricky and time consuming. The proposed solution: Rather than modifying a plethora of different third-party form integrations 1-by-1 and verifying submissions with Cloudflare on submit, we'll remove the forms from the DOM and only render them once Turnstile verifies the site visitor. As mentioned this is as-yet untested so I've no idea how robust this will be against bots and scrapers, but it seems worth trying IMO! In case people are wondering why we wouldn't use Cloudflare's other bot mitigation tools - we don't control all site's DNS and not all sites run behind Cloudflare. Turnstile works regardless of whether the site uses Cloudflare nameservers. The approach: I've modified the Turnstile module with a few new methods to do the following: Utilises @ryan's awesome URL hooks to create some endpoints to handle Turnstile token verification and: Dynamic loading of HTML render files into a page only once the token is verified. Use of data-attributes in page elements to specify the target location and file for rendering. Forked branch available here: https://github.com/warp-design/MarkupCloudflareTurnstile/tree/feature-protect-content-turnstile All probably best illustrated visually: Example page structure using a mix of the existing module methods and the new ones: <body> <?php //Load module in page $captcha = $modules->get('MarkupCloudflareTurnstile'); ?> <!-- Existing module widget render call. Set inside #turnstile-container to auto hide turnstile after verification --> <div id="turnstile-container"> <?= $captcha->render(); ?> </div> <!-- Elements you wish to protect with Turnstile. Set data attribute with render file path, relative to templates folder. There divs will populate with the specified render files when Turnstile verifies. --> <div class="turnstile_content" data-render-path="inc/exampleForm"></div> <div class="turnstile_content" data-render-path="inc/exampleContent"></div> <!-- New method to load the scripts if you want to protect dom elements behind Turnstile --> <?= $captcha->renderProtectedContent(); ?> <!--Existing module script call--> <?= $captcha->getScript(); ?> </body> And how this page looks live. Note: .turnstile_content elements only populate when Turnstile is verified: Test - 11 December 2024 - Watch Video Let me know what you think - is this a workable approach, or am I overlooking pitfalls? Likely issues/TODOs you may encounter: Things may need more sanitization and better error handling to come out during testing. Not sure how this will behave when loading in external JS. I've tried to ensure it will initialise any scripts in the render files when they are loaded. In theory things shouldn't be too different to loading content with frameworks or something like HTMX. Would be nice to eventually add support for passings $vars and $options params to the render files like any other PW template call. Someone may have already implemented the above in another module and I hadn't noticed! 😅
  8. Hi I intended to test RockPageBuilder with a fresh site-rockfrontend profile and most Rock-Modules. I installed page-edit-front and adjusted the profile in Rockfrontend to rockpagebuilder. Where to put the following render <?= $rockpagebuilder->render(true) ?> I tried to set it in different php/less files in /site/templates with this profile. Many thanks for a hint.
  9. Went down a rabbit hole on this one... Really curious why they chose a structureless document format like markdown when there are rich and mature data standards like Schema.org. The foundational work would have already been completed, the syntax well established/adopted, and there could be a lot of areas where the wheel may not need to be reinvented. Already exists on millions of websites and generators/parsers already exist for it- adoption by devs and orgs could adopt it so much more quickly with updates to existing packages/libraries. Almost all of the examples they give on the llmstxt website could be satisfied out of the box and if not, remedied by extending the specification. Maybe I'm missing the boat on this one, but is their rejection of an existing data structure due to the fact that they want LLMs to read the content "naturally"? Are LLMs incapable? If that's the case, should LLMs be giving anyone programming tips... I know I've veered off the topic of this post, but this proposal is 3 months old and curious if it has any legs. ANYWAY. Considerations for a module... How would it handle different information at different URL paths? Would it just dump all content into a single file? May create a massive file that starts to introduce performance issues on larger sites with things like blogs. This issue filed on the proposal Github repo brings up multiple URLs but the proposal itself doesn't seem to have anything concrete that takes this into consideration. Thinking about this out loud because creating a module to satisfy this need may end up being a more generalized module. I think this would end up turning into a Markdown generator, if not a library outright. I did a good amount of searching and there are tons of PHP packages that parse MD but I couldn't find any that generate MD from values. If that library existed, the module would be a lot easier to build. In the case of this module we'd essentially be building two versions of the same site because Markdown is as concerned about presentation as it is about content rather than just logic. Each field would have to be configured for rendering in MD. The llmstxt example of Franklin's BBQ is a good illustration. They have an unordered list of weekly hours, but their menu is formatted as a table. In that example, either one could be rendered as a list or table. Assuming we are using a repeater for hours and a repeater for menu items, each field would need to have settings for how it should be rendered (list or table). In the case of a table, fields for table headers need to be mapped and the subfields in the repeater mapped to column values. I don't even know what the settings would look like to render the business hours as a list according to the example. I'm thinking that putting all of the configuration into a module would be a significant challenge. I'm not sure that this proposed standard lends itself well to creating content for the markdown file via a user-friendly UI. It may need a developer to handle it all separately. This is one of the reasons I mentioned Schema data. It would be trivial to implement a Schema object, we already to for Google's structured data. The biggest lift would be to write a library that the developer uses to render the MD data and minimizing per-field configuration, and probably making the module just a formatter that outputs Markdown using defined methods. Here's a hypothetical implementation that uses page classes and an imaginary MarkdownGenerator module. This would render something like the Franklin's BBQ example in the link above <?php namespace ProcessWire; // site/classes/HomePage class HomePage extends DefaulePage { public function renderLlmsMarkdown(): string { $md = wire('modules')->get('MarkdownGenerator'); return $md->render([ $md->h1($this->title), $md->quote($this->summary), $md->text('Here are our weekly hours'), $md->ul( array_map( fn ($row) => "{$row->days}: {$row->hours}", $this->pages->get(1012)->operating_hours->getArray(), ), ), $md->h2('Menus'), $md->ul( array_map( fn ($menuPage) => $md->link($menuPage->title, "{$menuPage->httpUrl}/llms.txt"), $this->get('template=menu')->menus->getArray(), ), ), ]); } } // site/classes/MenuPage.php class MenuPage extends DefaulePage { public function renderLlmsMarkdown(): string { $md = wire('modules')->get('MarkdownGenerator'); $markdownItems = array_map(function($menuSection) use ($md) { return $md->render([ $md->h2($menuSection->title), $md->table( ['Item', 'Price'], array_map( fn ($item) => [$item->title, $item->price], $section->menu_items->getArray(), ), ), ]); }, $this->menu->getArray()); return $md->renderBlocks($markdownItems); } } // site/init.php foreach ($pages->find('template=home|menu') as $llmPage) { $wire->addHook( "{$llmPage->url}llms.txt", fn (HookEvent $e) => $e->pages->get($llmPage)->renderLlmsMarkdown() ); } That should really leverage caching in the real world. This approach will render an llms file at each individual URL for pages that are configured. This standard proposal seems to be taking a non-web approach and, as mentioned in that Github issue above, haven't considered leveraging web URLs but instead creating a stack of separate linked MD documents that an LLM reads like a book at the root URL. Since the standard doesn't say "look for llms.txt at every available url', then any pages with llms data will have to be specifically referenced/rendered on either the root llms.txt document or another llms.txt document that is or has an ancestor that is referenced in the root document. This follows the BBQ example, but just uses actual URLs rather than generating a stack of separate MD documents at the root. I assume you could just hook file names that contain Page IDs or something, but this makes more sense to me. Seems like an incredibly efficient way to build a whole new internet just for robots without any value provided to the people doing the work 🤔 At the very least I want a promise from someone that my life and the lives of my family will be spared when Skynet takes over. tl;dr Creating a module specifically to render llms data may not be the most efficient way to go about this A module that puts configurations into the UI would have to be extremely complex and account for the many types of fields available in ProcessWire Accounting for fields requires that each type of field is limited to the type of MD is can generate if the module attempted to make everything configurable The best way would probably be to create fields that will contain the content and then have your code do the rendering This is basically just creating two websites. One for people and one for LLMs Because this proposed standard has chosen markup over a logical data structure, it's probably always going to be on the shoulders of the developer unless they change something Another challenge is their expectation of additional content management: If this is important enough then there may be a need to manage LLM consumable information separately in fields that contain content sufficiently dumbed down for LLMs. Maybe the real module here is one that connects to an LLM API which auto-summarizes the content to then be used when creating MD files that describe the content to other LLMs. Solution: a library or Module that takes inputs and renders Markdown. Wouldn't be anything specific to AI. Or this standard could be tossed and we can just render structured data on the web page so LLMs can use the internet as a natively hyperlinked set of documents located at stable and discoverable URLs... Having thought this out I think even less of this standard than when I first read the proposal 🤣
  10. At first I used regions, then markup regions, and I found that I wasn't happy about performances. I did some benchmarks and noticed that the time consumed to render the page was growing faster than the amount of data to render. For example, if I double the data, rendering may take 3x the time (just a random number, I don't remember values, but I think I saved them in a text file). That's why I switched to Twig, Twig is slower for small content but is robust with big content. And I like how it's easy with Twig to write clean readable code, reuse twig fragments several times, import, extend, include, embed... there's a lot of choice, and no performance issue when including twigs into twigs. For very small html snippets that I use everywhere in the site, and are closely related to a page/template, I also have a method on my custom page classes, that calls a "Renderer" instance associated with this page and print some HTML. I use it for example for the user cards that are displayed on almost all pages of the previous site I did: <div>{{user.renderer('card')}}</div>
  11. Process Render File A Process module that renders markup from files in /site/templates/ProcessRenderFile/. This provides an easy way to create simple admin screens where it might be overkill to create a dedicated Process module for the purpose. Process page When you install the module a page with the name "render-file" and the title "Render File" is created under Setup. To change the page name or title, expand Page List to Admin > Setup > Render File and open the page for editing. Tip: if you create a new text field with the name "page_icon" and add it to the core "admin" template you can set a custom menu icon for the page by entering the icon name into the field. E.g. enter "smile-o" to use a smiley icon for the page. If you want to use ProcessRenderFile for more than one Process page you can create a new page under Setup. Select "admin" for the template, enter a title for the page, and in the next step choose ProcessRenderFile in the Process dropdown. Render files for URL segments On install the module will create a "ProcessRenderFile" folder in /site/templates/. Any .php files you save to this folder will be rendered as the output of a matching URL segment for Process pages using ProcessRenderFile. For example, if you create a file foo.php containing code... echo "This is the output of foo.php"; ...then you'll see this output when you visit /processwire/setup/render-file/foo/ (assuming the default ProcessWire admin page name and the default ProcessRenderFile page name). In the render file you can use all the ProcessWire API variables like $pages, $input, etc, as you would in a template file. If you create foo.js and foo.css (or foo.scss if you have ProCache installed) then those assets will be automatically loaded when you visit /processwire/setup/render-file/foo/. When a file is rendered the filename will be converted into a capitalised format that's used as a browser title and a headline. If you want to set a custom browser title and/or headline you can do this in your render file: $this->wire('processBrowserTitle', 'Custom browser title'); $this->wire('processHeadline', 'Custom headline'); Render file for the Process page If you create a render file that has the same name as the ProcessRenderFile Process page then this file will be rendered when the Process page is viewed without any URL segment. So for the default ProcessRenderFile page name you would create render-file.php. Note that this will override the default list output described below. Configuration The module configuration screen lets you select URL segment render files to appear as menu items. The selected render files will appear in the flyout menu for the Process page, and also as a list when the Process page is viewed without any URL segment. The list is only shown if you haven't created a render file for the Process page as described above. If you've created more than one ProcessRenderFile Process page then you'll see menu options for each page in the module configuration. https://github.com/Toutouwai/ProcessRenderFile https://processwire.com/modules/process-render-file/
  12. 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.
  13. Padloper 010 Padloper 010 (aka Padloper 2 version 010) is ready for download. Requirements PHP>= 8.2.0 ProcessWire>=3.0.200 Maintenance Padloper 010 is mainly a maintenance release: Fixes money rounding bugs found in Padloper 009 (and earlier). Adds the Money for PHP Library to handle monetary values in an easy and powerful way. Fixes money math issues when prices include tax. For the sake of internal maintenance, reorganises files and directory structure. Money Matters Specifically regarding 'prices include tax', and I will be adding this to the docs, it is important that you display your prices in the frontend in a way that doesn't confuse consumers. This is not unique to Padloper but a general e-commerce issue. This means making careful decisions around displaying unit vs total prices as well as whether to include notes about potential rounding pence/cents issues in your shop's policy documents. Due to rounding issues (pence, cents, etc) when it comes to money, especially if quantity is more than 1, the math might look off. I.e., (item * quantity) + tax IS NOT NECESSARILY EQUAL TO (item + tax) * quantity. See this article for a thorough discussion. Breaking Changes There are a few unavoidable breaking changes due to code rationalisation. API Some API had to be changed for maintenance and usage simplicity reasons. This affects checkout, cart and discounts. For instance: <?php namespace ProcessWire; # OLD API $checkout = $padloper->checkout; $response = $checkout->render($options); # NEW API // $response = $padloper->render($options);// this will be deprecated in a future release $response = $padloper->renderCheckout($options); ########## # OLD API $cartRender = $padloper->cartRender; $content = $cartRender->editCart(); # NEW API $content = $padloper->editCart(); ########## # OLD API $cartRender = $padloper->cartRender; $content = $cartRender->viewCart(); # NEW API $content = $padloper->viewCart(); HOOKS Backend hooks rename: // OLD NAMING FORMAT PadloperProcessRenderXXX e.g. PadloperProcessRenderOrders // NEW NAMING FORMAT PadloperAdminRenderXXX e.g. PadloperAdminRenderOrders Other hooks rename: // OLD NAMING FORMAT PadloperXXX::methodName e.g. PadloperUtilities::getProductWeight, PadloperCart::getProductPrice // NEW NAMING FORMAT Padloper::methodName e.g. Padloper::getProductWeight, Padloper::getProductPrice Upgrading Replace all files (not overwrite!). This is because some files have been deprecated, renamed or moved. Safest way to upgrade is to get rid of all existing Padloper files and folders under /site/modules/Padloper/ and then copy over the new 010 files and folders then do a module refresh. New Features The two new features are: $padloper->money(); More information will be added to docs. Bank Name as a property (bankAccountName) in the return value of $padloper->getShopBankDetails();. See request in this topic. Next Steps As mentioned in Padloper 009 topic, the focus now will be the documentation. Any questions or issues, please let me know. Thanks.
  14. hi @bernhard, trying to set up RPB 5.5.4 in PW 3.0.240 there's no working site "out of the box" after installation, regardless the site template i use. <div id="content"> Homepage content <?php echo $rockpagebuilder->render() ?> </div> gives the mentioned error Fatal Error: Uncaught Error: Call to a member function render() on string in C:\wamp64\www\oh1\site\modules\RockFields\RockPageBuilder.module.php:1535 Stack trace: #0 C:\wamp64\www\oh1\site\templates\home.php(18): RockPageBuilder->render() #1 C:\wamp64\www\oh1\wire\core\TemplateFile.php(328): require('C:\\wamp64\\www\\o...') #2 C:\wamp64\www\oh1\wire\core\Wire.php(413): TemplateFile->___render() #3 C:\wamp64\www\oh1\wire\core\WireHooks.php(968): Wire->_callMethod('___render', Array) #4 C:\wamp64\www\oh1\wire\core\Wire.php(484): WireHooks->runHooks(Object(TemplateFile), 'render', Array) #5 C:\wamp64\www\oh1\wire\modules\PageRender.module(581): Wire->__call('render', Array) #6 C:\wamp64\www\oh1\wire\core\Wire.php(416): PageRender->___renderPage(Object(HookEvent)) #7 C:\wamp64\www\oh1\wire\core\WireHooks.php(968): Wire->_callMethod('___renderPage', Array) #8 C:\wamp64\www\oh1\wire\core\Wire.php(484): WireHooks->runHooks(Object(PageRender), 'renderPage', Array) #9 C:\wamp64\www\oh1\wire\core\WireHooks.php(1094): Wire->__call('renderPage', Array) #10 C:\wamp64\www\oh1\wire\core\Wire.php(484): WireHooks->runHooks(Object(HomePage), 'render', Array) #11 C:\wamp64\www\oh1\wire\modules\Process\ProcessPageView.module(184): Wire->__call('render', Array) #12 C:\wamp64\www\oh1\wire\modules\Process\ProcessPageView.module(114): ProcessPageView->renderPage(Object(HomePage), Object(PagesRequest)) #13 C:\wamp64\www\oh1\wire\core\Wire.php(416): ProcessPageView->___execute(true) #14 C:\wamp64\www\oh1\wire\core\WireHooks.php(968): Wire->_callMethod('___execute', Array) #15 C:\wamp64\www\oh1\wire\core\Wire.php(484): WireHooks->runHooks(Object(ProcessPageView), 'execute', Array) #16 C:\wamp64\www\oh1\index.php(55): Wire->__call('execute', Array) #17 {main} thrown as i am not able to setup RPB at all i wonder if PW 3.0.240 has an error (or - more likely - me myself & I)
  15. Hello @ all I want to share a new module with you, which makes the creation and validation of forms easy. Take a look at the following example of a simple contact form: // A very simple example of a contactform for demonstration purposes $form = new Form('contactform'); $gender = new Select('gender'); $gender->setLabel('Gender'); $gender->addOption('Mister', '0'); $gender->addOption('Miss', '1'); $form->add($gender); $surname = new InputText('surname'); $surname->setLabel('Surname'); $surname->setRule('required'); $form->add($surname); $name = new InputText('name'); $name->setLabel('Name'); $name->setRule('required'); $form->add($name); $email = new InputText('email'); $email->setLabel('E-Mail'); $email->setRule('required'); $form->add($email); $subject = new InputText('subject'); $subject->setLabel('Subject'); $subject->setRule('required'); $form->add($subject); $message = new Textarea('message'); $message->setLabel('Message'); $message->setRule('required'); $form->add($message); $privacy = new InputCheckbox('privacy'); $privacy->setLabel('I accept the privacy policy'); $privacy->setRule('required')->setCustomMessage('You have to accept our privacy policy'); $form->add($privacy); $button = new Button('submit'); $button->setAttribute('value', 'Send'); $form->add($button); if($form->isValid()){ print_r($form->getValues()); // do what you want } // render the form echo $form->render(); This piece of code creates a simple contact form and validates it according to the validation rules set. Inside the isValid() method you can run your code (fe sending an email) Highlights: 30+ validation types Support for UiKit 3 and Bootstrap 5 CSS framework SPAM protection Highly customizable Hookable methods for further customization Multi-language You can download and find really extensive information on how to use at https://github.com/juergenweb/FrontendForms. Please report errors or suggestions directly in GitHub. Best regards and happy testing ? If you have downloaded the module in the past I recommend you to uninstall the module completely and install the newest version 2.1.14. There are a lot of changes in the new version, so please test carefully.
  16. Hi @bernhard we just updated RockFrontend on a site from 3.20.0 to 3.23.3 and the site stopped working. I fixed it by changing templates/_main.php <?= $rockfrontend->renderLayout($page); // worked before update ?> <?= $rockfrontend->render($page); // works after update ?> We use RockPageBuilder and in templates/sections/main.latte <div sortable x-ref="additional-sections"> {$page->get('rockpagebuilder_blocks')->render()|noescape} </div> Now after the update that threw an error on our home page because template home has no field rockpagebuilder_blocks I am wondering why this is. Do you have any idea? I looked at the function signature for render() and the examples in the PHPDoc do not mention that we can pass a Page object as $path argument. So I thought we could not call render($page) at all. But it works. I guess because $page is then overwritten with $this->wire->page. So renderLayout($page) would be the correct method to use. But something in the inner workings of renderLayout must have changed and stopped the site from working. I suspect that the way we have setup rendering with RockPageBuilder, RockFrontend and Latte is fundamentally wrong.
  17. Hi @bernhard, I really like to work with RockFrontend (and RockPageBuilder)! Now I have some questions resp. want to make sure what would be the best practice: I am using the autoload-layout feature and am loading a _layout.latte file. But I also would want to make some template specific modifications, like having an element on the startpage, which is not included on all the other pages. I can add this element in home.php, but then it will be included before the opening <html>-tag. When I put the elements into home.latte, this file won't be output until I add the {do $rockfrontend->noLayoutFile = true} directive to it. But if I add this line, the home.latte will of course override my _layout.latte, which is also not what I want. So I added the following to my _layout.latte: ... {* only on the startpage *} {if $page->id==1} {include "src/latte/partials/custom-element.latte"} {/if} </body> </html> - Is this currently a recommended way to e.g. have a custom element on the startpage? - I noticed that content in home.php will always be added to the output, while home.latte will only be added, when using {do $rockfrontend->noLayoutFile = true} in it. Is it possible to load home.latte while preserving _layout.latte, too? - Is RockFrontend compatible with ProcessWire's MarkupRegions? => I removed _main.latte and am now rendering the template-specific latte-files "manually". With "echo" in the respective template PHP-files, and using a _output.php, ProcessWire's markup regions work. But I think one cannot use both techniques, _main.latte and Markup Regions, at the same time. Also, _rockfrontend.php has to be deactivated.* - Currently the page in the docs regarding the render()-method is empty. https://www.baumrock.com/en/processwire/modules/rockfrontend/docs/render/ - Is it possible to render any latte-file with render()? Then I could render home.latte within home.php ... but I would again need MarkupRegions to insert the rendered markup in the right place. I tried it, but with no luck so far. => RockFrontend's render() method can render any latte-file. The trick is to omit _main.latte and implement a more "traditional" approach, with a custom _output.php, in which the Markup Regions are put together.* Maybe you have any clues ... and what method would you use to insert template specific markup while using a global _layout.latte template? * Of course, the purpose of RockFrontend is not to only use the render()-method, so I recommend to not use Markup Regions and use the full feature set of RockFrontend instead. Also, it could be that I have overlooked something, so this information may be subject to change. Edit: Answered some of the questions
  18. Ok. Now I got it better, I think. Now I have _init.php prepending wireframe.php that generates settings array, that I then pass to wireframe->render(). Some absolutely general settings I pass down with setting() function. If we had composers, we could move all my code in _init.php and wireframe.php to a dedicated class. Where would these composers be in places if file structure? How would we access data from them in views?
  19. Exactly. Composers are useful for cases where we need to share data among different views, or certain types of views. Say, you want all your component views, partials, or perhaps field specific render files to have access to some shared data. Or you have a set of "conceptually related" templates and/or views that all need same variables. In the context of WordPress / Sage I've used this for things like shared settings, details about current session (such as user specific content, or some stats that I don't want to fetch/calculate multiple times, or in multiple locations), blocks of data that all events or news/articles share (such as a list of latest news/events), etc. You can achieve similar results via the bootstrap file (e.g. /site/templates/wireframe.php), and for some use cases it works very well. Especially when combined with utility classes or feature modules. But if there are a lot of "if template is x then..." type if-statements there, it can get a bit out of hand. Not to mention that some things are not automatically passed to all views. Anyway, the main reason I've not jumped right into this is that I'm also debating whether it truly makes sense. I do like the concept and it "feels right", and could potentially be beneficial outside of Wireframe as well. But at the same time I'm not too keen to introduce new concepts just because they sound fun 😄
  20. Time to answer my own question... and love it or hate it AI came up with the goods on this one! Defining the urlHook as follows: public function init() { $this->addHook('/custom-url/', $this, 'handleCustomURL'); } And the handleCustomURL function (this was the second iteration I got for this to include having a default template in the module, whilst allowing them to be overridden in the site/templates/ folder): public function handleCustomURL(HookEvent $event) { // Path to the override template in the site's templates directory $overrideTemplatePath = $this->config->paths->templates . 'custom-template.php'; // Path to the default template in the module's directory $defaultTemplatePath = $this->config->paths->siteModules . $this->className() . '/templates/custom-template.php'; // Determine which template to use $templatePath = file_exists($overrideTemplatePath) ? $overrideTemplatePath : $defaultTemplatePath; // Check if the determined template exists (for safety) if (!file_exists($templatePath)) { throw new Wire404Exception("Template not found"); } // Render the template and pass variables $output = $this->files->render($templatePath, [ 'someVariable' => 'someValue', // Add additional data if needed ]); // Output the rendered content echo $output; // Stop further processing $event->replace = true; } I did make a small to change to the end of the function as follows: // Output the rendered content //echo $output; return $output; // Stop further processing - Doesn't look like this is needed! // $event->replace = true; } Not sure any amount of Google-Fu would have got me to this, so it's interesting to see how AI can pull the correct bits of information together! I have obviously tested the above code, and it seems to work a treat!
  21. Hi @Alamut, You can do this with URL hook: https://processwire.com/blog/posts/pw-3.0.173/#telling-processwire-what-page-to-render Another solution is to create a template and a unique page "product-sheet" to display all your products. So the URL will always be the same except for the product ID.
  22. Menu Builder As of 29 December 2017 ProcessWire versions earlier than 3.x are not supported Modules Directory Project Page Read Me (How to install, use, etc..) For highly customisable menus, please see this post. If you want a navigation that mirrors your ProcessWire page tree, the system allows you to easily create recursive menus using either vanilla PHP or Soma's great MarkupSimpleNavigation. In some cases, however, you may wish to create menus that: 1. Do not mirror you site's page tree (hirarchies and ancestry); and 2. You can add custom links (external to your site) to. That is primarily where Menu Builder comes in. It is also helpful if you: 3. Prefer creating menus via drag and drop 4. Have a need for menus (or other listings) that will be changing regularly or that you want to allow your admin users to edit. The issue of custom menus is not new here in the forums. The difference is that this module allows you to easily create such menus via drag and drop in the Admin. Actually, you can even use it to just create some list if you wanted to. In the backend, the module uses the jQueryUI plugin nestedSortable by Manuele J Sarfatti for the drag and drop and is inspired in part by the WP Custom Menu feature. Please read the Read Me completely before using this module. For Complex or highly-customised menus, it is recommended to use the getMenuItems() method as detailed in this post. Features Ability to create menus that do not mirror your ProcessWire Page Tree hierarchy/structure Menus can contain both ProcessWire pages and custom links Create menu hierarchies and nesting via drag and drop Easily add CSS IDs and Classes to each menu item on creating the menu items (both custom and from ProcessWire pages) or post creation. Optionally set custom links to open in a new tab Change menu item titles built from ProcessWire pages (without affecting the original page). E.g. if you have a page titled 'About Us' but you want the menu item title to be 'About' Readily view the structure and settings for each menu item Menus stored as pages (note: just the menu, not the items!) Menu items stored as JSON in a field in the menu pages (empty values not stored) Add menu items from ProcessWire pages using page fields (option to choose between PageAutocomplete and AsmSelect [default]) or a Selector (e.g. template=basic-page, limit=20, sort=title). For page fields, you can specify a selector to return only those specified pages for selection in the page field (i.e. asm and autocomplete) For superusers, optionally allow markup in your menu titles, e.g. <span>About</span> Menu settings for nestedSortable - e.g. maxLevels (limit nesting levels) Advanced features (e.g. add pages via selector, menu settings) currently permissible to superadmins only (may change to be permission-based) Delete single or all menu items without deleting the menu itself Lock down menus for editing Highly configurable MarkupMenuBuilder - e.g. can pass menu id, title, name or array to render(); Passing an array means you can conditionally manipulate it before rendering, e.g. make certain menu branches visible only to certain users [the code is up to you!] Optionally grab menu items only (as a Menu object WireArray or a normal array) and use your own code to create custom highly complex menus to meet any need. More... In the backend, ProcessMenuBuilder does the menu creation. For the frontend, menus are displayed using MarkupMenuBuilder. Credits In this module's infancy (way back!), I wanted to know more about ProcessWire modules as well as improve my PHP skills. As they say, what better way to learn than to actually create something? So, I developed this module (instead of writing PW tutorials as promised, tsk, tsk, naughty, naughty!) in my own summer of code . Props to Wanze, Soma, Pete, Antti and Ryan whose modules I studied (read copied ) to help in my module development and to Teppo for his wonderful write-up on the "Anatomy of fields in ProcessWire" that vastly improved my knowledge and understanding of how PW works. Diogo and marcus for idea about using pages (rather than a custom db table), onjegolders for his helpful UI comments, Martijn Geerts, OrganizedFellow, dazzyweb and Mike Anthony for 'pushing me' to complete this module and netcarver for help with the code. Screens
  23. Good day everybody! How are you doing @teppo! Wireframe\Controller has int() and render() methods. Their docblocks say that inti() is to perform early checks and render is the place to pass vars to view. I've been working with this a while but still do not quite grasp how to use them together. I either use one or a another. Demo repo doesn't have any examples with both methods. Could plrease someone in the know share their wisdom (or opinion) on this? And a more specific question. If I use them together and put checks into init(), how do I avoid triggering render, if those checks do not pass? For example, some fields are empty and regular render should not happen, but an error should be shown instead. Thanks!
  24. Hey @verdeandrea thx, I understood that. My question was why you only want to show "open / closed" to "courses" and "in prep / registration open" to "masters" and at the same time make it possible to add new statuses. Because that means anybody could just add "open" or "closed" which are only available under "courses" to any page under "masters" as well - which makes hiding them obsolete from a logical perspective. And if you didn't hide them, you would not have the problem that you observe. My guess would be that you want to keep the list of available statuses smaller as the global list of statuses might grow large over time? I'd probably add a custom <input> to that page reference field and hook into processInput to create the status page on my own, but only if it does not yet exist: <?php // append <input> to the page ref field wire()->addHookAfter( 'InputfieldPage::render', function (HookEvent $event) { $f = $event->object; // execute this hook only for the field in question if ($f->name !== 'your_page_ref_field') return; // append custom <input> $f->appendMarkup('<div class="uk-margin-top"> ...or create a new status:<br> <input type="text" name="newstatus"> </div>'); } ); // hook processInput to create new status wire()->addHookAfter( 'ProcessPageEdit::processInput', function (HookEvent $event) { // sanitize received status, adjust to your needs $status = trim(wire()->input->post('newstatus', 'string')); // no new status, no todos if (!$status) return; // try to find existing status page $statusPage = wire()->pages->get([ 'parent' => 123, 'title' => $status, ]); // create new status page if it does not exist yet if (!$statusPage->id) { $statusPage = new Page(); $statusPage->template = 'status'; $statusPage->parent = 123; $statusPage->title = $status; $statusPage->save(); } // set new statuspage as selected status $page = $event->object->getPage(); $page->setAndSave('your_page_ref_field', $statusPage); } );
  25. 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!
×
×
  • Create New...