Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 05/01/2019 in all areas

  1. Thanks to @Macrura for the idea behind this module. Page Field Info Adds information about options in Page Reference fields. Supports InputfieldSelect and inputfields that extend InputfieldSelect: InputfieldSelect InputfieldRadios InputfieldSelectMultiple InputfieldCheckboxes InputfieldAsmSelect Requires ProcessWire >= 3.0.61 and AdminThemeUikit. Screenshots Field config Example of changes to inputfield Example of info field filled out in Page Edit Installation Install the Page Field Info module. Configuration In the Input tab of the settings for a Page Reference field... Tick the "Add info tooltips to options" checkbox to enable tooltips for the options in the field. Tooltips are not possible for Select or AsmSelect inputfield types so for those types you would want to tick the next option. Tick the "Append info about selected options" checkbox to append information about the selected options to the bottom of the inputfield. If the Page Reference field is a "multiple pages" field then the info for each selected option will be prefixed with the option label (so the user will know what option each line of info relates to). In the "Info field" dropdown select a text field that will contain information about the page, to be used in the tooltips and appended info. Of course this field should be in the template(s) of the selectable pages for the Page Reference field. Hook In most cases the "Info field" will supply the text for the tooltips and appended info, but for advanced usages you can hookPageFieldInfo::getPageInfo() to return the text. For example: $wire->addHookAfter('PageFieldInfo::getPageInfo', function(HookEvent $event) { $page = $event->arguments(0); // The page $inputfield = $event->arguments(1); // InputfieldPage $field = $event->arguments(2); // The Page Reference field $info = $event->return; // Text from the info field, if any // Set some custom text as the $event->return... }); https://github.com/Toutouwai/PageFieldInfo https://modules.processwire.com/modules/page-field-info/
    11 points
  2. This is part two of my tutorial on integrating Twig in ProcessWire sites. As a reminder, here's the table of contents: Part 1: Extendible template structures How to initialize a custom twig environment and integrate it into ProcessWire How to build an extendible base template for pages, and overwrite it for different ProcessWire templates with custom layouts and logic How to build custom section templates based on layout regions and Repeater Matrix content sections Part 2: Custom functionality and integrations How to customize and add functionality to the twig environment How to bundle your custom functionality into a reusable library Thoughts on handling translations A drop-in template & functions for responsive images as a bonus Make sure to check out part one if you haven't already! This part will be less talk, more examples, so I hope you like reading some code ? Adding functionality This is more generic Twig stuff, so I'll keep it short, just to show why Twig is awesome and you should use it! Twig makes it super easy too add functions, filters, tags et c. and customize what the language can do in this way. I'll show a couple of quick examples I built for my projects. As a side note, I had some trouble with functions defined inside a namespace that I couldn't figure out yet. For the moment, it sufficed to define the functions I wanted to use in twig inside a separate file in the root namespace (or, as shown further below, put all of it in a Twig Extension). If you want a more extensible, systematic approach, check out the next section (going further). Link template with external target detection This is a simple template that builds an anchor-tag (<a>) and adds the necessary parameters. What's special about this is that it will automatically check the target URL and include a target="_blank" attribute if it's external. The external URL check is contained in a function: // _functions.php /** * Finds out whether a url leads to an external domain. * * @param string $url * @return bool */ function urlIsExternal(string $url): bool { $parser = new \League\Uri\Parser(); [ 'host' => $host ] = $parser->parse($url); return $host !== null && $host !== $_SERVER['HTTP_HOST']; } // _init.php require_once($config->paths->templates . '_functions.php'); // don't forget to add the function to the twig environment $twig_env->addFunction(new \Twig\TwigFunction('url_is_external', 'urlIsExternal')); This function uses the excellent League URI parser, by the way. Now that the function is available to the Twig environment, the link template is straightforward: {# # Renders a single anchor (link) tag. Link will automatically # have target="_blank" if the link leads to an external domain. # # @var string url The target (href). # @var string text The link text. Will default to display the URL. # @var array classes Optional classes for the anchor. #} {%- set link_text = text is not empty ? text : url -%} <a href="{{ url }}" {%- if classes is not empty %} class="{{ classes|join(' ') }}"{% endif %} {%- if url_is_external(url) %} target="_blank"{% endif %}> {{- link_text -}} </a> String manipulation A couple of functions I wrote to generate clean meta tags for SEO, as well as valid, readable IDs based on the headline field for my sections. /** * Truncate a string if it is longer than the specified limit. Will append the * $ellipsis string if the input is longer than the limit. Pass true as $strip_tags * to strip all markup before measuring the length. * * @param string $text The text to truncate. * @param integer $limit The maximum length. * @param string|null $ellipsis A string to append if the text is truncated. Pass an empty string to disable. * @param boolean $strip_tags Strip markup from the text? * @return string */ function str_truncate( string $text, int $limit, ?string $ellipsis = ' …', bool $strip_tags = false ): string { if ($strip_tags) { $text = strip_tags($text); } if (strlen($text) > $limit) { $ell_length = $ellipsis ? strlen($ellipsis) : 0; $append = $ellipsis ?? ''; $text = substr($text, 0, $limit - ($ell_length + 1)) . $append; } return $text; } /** * Convert all consecutive newlines into a single space character. * * @param string $text The text to convert. */ function str_nl2singlespace( string $text ): string { return preg_replace( '/[\r\n]+/', ' ', $text ); } /** * Build a valid html ID based on the passed text. * * @param string $title * @return string */ function textToId(string $title): string { return strtolower(preg_replace( [ '/[Ää]/u', '/[Öö]/u', '/[Üü]/u', '/ß/u', '/[\s._-]+/', '/[^\w\d-]/', ], [ 'ae', 'oe', 'ue', 'ss', '-', '-', ], $title )); } // again, add those functions to the twig environment $twig_env->addFilter(new \Twig\TwigFilter('truncate', 'str_truncate')); $twig_env->addFilter(new \Twig\TwigFilter('nl2ss', 'str_nl2singlespace')); $twig_env->addFilter(new \Twig\TwigFilter('text_to_id', 'textToId')); Example usage for SEO meta tags: {% if seo.description %} {% set description = seo.description|truncate(150, ' …', true)|nl2ss %} <meta name="description" content="{{ description }}"> <meta property="og:description" content="{{ description }}"> {% endif %} instanceof for Twig By default, Twig doesn't have an equivalent of PHP's instanceof keyword. The function is super simple, but vital to me: // instanceof test for twig // class must be passed as a FQCN with escaped backslashed $twig_env->addTest(new \Twig\TwigTest('instanceof', function ($var, $class) { return $var instanceof $class; })); In this case, I'm adding a TwigTest instead of a function. Read up on the different type of extensions you can add in the documentation for Extending Twig. Note that you have to use double backslashes to use this in a Twig template: {% if og_img is instanceof('\\Processwire\\Pageimages') %} Going further: custom functionality as a portable Twig extension Most of the examples above are very general, so you'll want to have them available in every project you start. It makes sense then to put them into a single library that you can simply pull into your projects with git or Composer. It's really easy to wrap functions like those demonstrated above in a custom Twig extension. In the following example, I have wired the namespace "moritzlost\" to the "src" folder (see my Composer + ProcessWire tutorial if you need help with that): // src/MoritzFuncsTwigExtension.php <?php namespace moritzlost; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; use Twig\TwigFilter; use Twig\TwigTest; class MoritzFuncsTwigExtension extends AbstractExtension { // import responsive image functions use LinkHelpers; public function getFunctions() { return [ new TwigFunction('url_is_external', [$this, 'urlIsExternal']), ]; } public function getFilters() { return [ new TwigFilter('text_to_id', [$this, 'textToId']), ]; } public function getTests() { return [ new TwigTest('instanceof', function ($variable, string $namespace) { return $variable instanceof $namespace; }), ]; } } // src/LinkHelpers.php <?php namespace moritzlost; trait LinkHelpers { // this trait contains the textToId and urlIsExternal methods // see the section above for the full code } Here I'm building my own class that extends the AbstractExtension class from Twig. This way, I can keep boilerplate code to a minimum. All I need are public methods that return an array of all functions, filters, tests et c. that I want to add with this extension. As is my custom, I've further split the larger functions into their own wrapper file. In this case, I'm using a trait to group the link-related functions (it's easier this way, since classes can only extend one other class, but use as many traits as they want to). Now all that's left is to add an instance of the extension to our Twig environment: // custom extension to add functionality $twig_env->addExtension(new MoritzFuncsTwigExtension()); Just like that we have a separate folder that can be easily put under version control and released as a micro-package that can then be installed and extended in other projects. Translations If you are building a multi-language site, you will need to handle internationalization of your code. ProcessWire can't natively handle translations in Twig files, so I wanted to briefly touch on how to handle this. For a recent project I considered three approaches: Build a module to add twig support to ProcessWire's multi-language system. Use an existing module to do that. Build a custom solution that bypasses ProcessWire's translation system. For this project, I went with the latter approach; I only needed a handful of phrases to be translated, as I tend to make labels and headlines into editable page fields or use the field labels themselves, so there are only few translatable phrases inside my ProcessWire templates. But the beauty of ProcessWire is that you can build your site whatever way you want. As an example, here's the system I came up with. I used a single Table field (part of the ProFields module) with two columns: msgid (a regular text field which functions as a key for the translations) and trans (a multi-language text field that holds the translations in each language). I added this field to my central settings page and wrote a simple function to access individual translations by their msgid: /** * Main function for the translation API. Gets a translation for the msgid in * the current language. If the msgid doesn't exist, it will create the * corresponding entry in the settings field (site settings -> translations). * In this case, the optional second parameter will be used as the default * translation for this msgid in the default language. * * @param string $msgid * @param ?string $default * @return string */ function trans_api( string $msgid, ?string $default = null ): string { // this is a reference to my settings page with the translations field $settings = \Processwire\wire('config')->settings; $translations = $settings->translations; $row = $settings->translations->findOne("msgid={$msgid}"); if ($row) { if ($row->trans) { return $row->trans; } else { return $msgid; } } else { $of = $settings->of(); $settings->of(false); $new = $translations->makeBlankItem(); $new->msgid = $msgid; if ($default) { $default_lang = \Processwire\wire('languages')->get('default'); $new->trans->setLanguageValue($default_lang, $default); } $settings->translations->add($new); $settings->save('translations'); $settings->of($of); return $default ?? $msgid; } } // _init.php // add the function with the key "trans" to the twig environment $twig_env->addFunction(new \Twig\TwigFunction('trans', 'trans_api')); // some_template.twig // example usage with a msgid and a default translation {{ trans('detail_link_label', 'Read More') }} This function checks if a translation with the passed msgid exists in the table and if so, returns the translation in the current language. If not, it automatically creates the corresponding row. This way, if you want to add a translatable phrase inside a template, you simply add the function call with a new msgid, reload the page once, and the new entry will be available in the backend. For this purpose, you can also add a second parameter, which will be automatically set as the translation in the default language. Sweet. While this works, it will certainly break (in terms of performance and user-friendliness) if you have a site that required more than a couple dozen translations. So consider all three approaches and decide what will work best for you! Bonus: responsive image template & functions I converted my responsive image function to a Twig template, I'm including the full code here as a bonus and thanks for making it all the way through! I created a gist with the extension & and template that you can drop into your projects to create responsive images quickly (minor warning: I had to adjust the code a bit to make it universal, so this exact version isn't properly tested, let me know if you get any errors and I'll try to fix it!). Here's the gist. There's a usage example as well. If you don't understand what's going on there, make sure to read my tutorial on responsive images with ProcessWire. Conclusion Including the first part, this has been the longest tutorial I have written so far. Again, most of this is opinionated and influenced by my own limited experience (especially the part about translations), so I'd like to hear how you all are using Twig in your projects, how you would improve the examples above and what other tips and tricks you have!
    3 points
  3. I've worked much more in the admin those days, but I also like markup regions a lot! The only two "fancy" things I can think of are: 1) Remove the "boxed layout" (uk-container class) for some regions (eg when you have a boxed website in general but want the frontpage to be full width): <region id="mainsection" class="-uk-container">...</region> The nice thing is that this works completely without any if/else blabla as one would usually do it ? 2) Region at the end of the body: <body> ... <region id="bodyend"></region> </body> Then I can inject code from views where I need additional javascripts or the like, eg sliders or here using foxycart: This example also shows how easy (and clean) it is to add FormBuilder to this "view" (just using pw-append on the head).
    3 points
  4. Boom! Finally I've got Intelephense working as expected ? Before: After: Turns out that it is already available as setting in settings.json:
    2 points
  5. Astounding! I'm installing now on several sites! Working great so far, had to change line 195 to support textarea, as that's what i use for the info $text_fields = $this->wire('fields')->find('type=FieldtypeText|FieldtypeTextarea'); Testing the single select now, but noticing that the options don't have the data-info attribute, so there is no way to update the selected option's description by javascript – do you think it is possible to add the data-info to the options on plain selects?
    2 points
  6. I was going to write a tutorial but then I thought I might as well take the next step and turn it into a module: If @tpr wants to include the functionality in AOS and people would prefer that rather than a standalone module then I'm fine with that. ?
    2 points
  7. @Robin S Adding the slash at the end worked. You rock and my remaining strand of hair thanks you immensely for your prompt, working reply!
    2 points
  8. A couple of things to check: That you are calling the endpoint URL with/without a trailing slash according to the template "Should page URLs end with a slash?" setting. If the site is multi-language that you are calling the endpoint URL with the language string in it, e.g. http://site/en/subscription-new/
    2 points
  9. I just recently started using markup regions and gotta say I'm starting to like it a lot, but honestly I wouldn't think of anything actually "fancy", just really like working as if I were doing direct output, just like MarkupRegions proposes in its docs.
    2 points
  10. ProFields (especially Matrix Field) and ProCache.
    2 points
  11. I have recently started to integrate Twig in my ProcessWire projects to have a better separation between logic and views as well as have cleaner, smaller template files as opposed to large multi-purpose PHP-templates. Though there is a Twig module, I have opted to initialize twig manually to have more control over the structure and settings of the twig environment. This is certainly not required to get started, but I find that having no "secret sauce" gives the me as a developer more agency over my code structure, and better insights into how the internals of the libraries I'm using work. Framework like Drupal or Craft have their own Twig integration, which comes with some opinionated standards on how to organize your templates. Since Twig is not native to ProcessWire, integrating Twig requires one to build a solid template structure to be able to keep adding pages and partials without repeating oneself or having templates grow to unwieldy proportions. This will be an (opinionated) guide on how to organize your own flexible, extensible template system with ProcessWire and Twig, based on the system I developed for some projects at work. Somehow this post got way too long, so I'm splitting it in two parts. I will cover the following topics: Part 1: Extendible template structures How to initialize a custom twig environment and integrate it into ProcessWire How to build an extendible base template for pages, and overwrite it for different ProcessWire templates with custom layouts and logic How to build custom section templates based on layout regions and Repeater Matrix content sections Part 2: Custom functionality and integrations How to customize and add functionality to the twig environment How to bundle your custom functionality into a reusable library Thoughts on handling translations A drop-in template & functions for responsive images as a bonus However, I will not include a general introduction to the Twig language. If you are unfamiliar with Twig, read the Twig guide for Template Designers and Twig for Developers and then come back to this tutorial. That's a lot of stuff, so let's get started ? Initializing the Twig environment First, we need to install Twig. If you set up your site as described in my tutorial on setting up Composer, you can simply install it as a dependency: composer require "twig/twig:^2.0" I'll initialize the twig environment inside a prependTemplateFile and call the main render function inside the appendTemplateFile. You can use this in your config.php: $config->prependTemplateFile = '_init.php'; $config->appendTemplateFile = '_main.php'; Twig needs two things: A FilesystemLoader to load the templates and an Environment to render them. The FilesystemLoader needs the path to the twig template folder. I'll put my templates inside site/twig: $twig_main_dir = $config->paths->site . 'twig'; $twig_loader = new \Twig\Loader\FilesystemLoader($twig_main_dir); As for the environment, there are a few options to consider: $twig_env = new \Twig\Environment( $twig_loader, [ 'cache' => $config->paths->cache . 'twig', 'debug' => $config->debug, 'auto_reload' => $config->debug, 'strict_variables' => false, 'autoescape' => true, ] ); if ($config->debug) { $twig_env->addExtension(new \Twig\Extension\DebugExtension()); } Make sure to include a cache directory, or twig can't cache the compiled templates. The development settings (debug, auto_reload) will be dependent on the ProcessWire debug mode. I turned strict_variables off, since it's easier to check for non-existing and non-empty fields with some parts of the ProcessWire API. You need to decide on an escaping strategy. You can either use the autoescape function of twig, or use textformatters to filter out HTML tags (you can't use both, as it will double escape entities, which will result in a broken frontend). I'm using twig's inbuilt autoescaping, as it's more secure and I don't have to add the HTML entities filter for every single field. This means pretty much no field should use any entity encoding textformatter. I also added the Debug Extension when $config->debug is active. This way, you can use the dump function, which makes debugging templates much easier. Now, all that's left is to add a few handy global variables that all templates will have access to, and initialize an empty array to hold additional variables defined by the individual ProcessWire templates. // add the most important fuel variables to the environment foreach (['page', 'pages', 'config', 'user', 'languages', 'sanitizer'] as $variable) { $twig_env->addGlobal($variable, wire($variable)); }; $twig_env->addGlobal('homepage', $pages->get('/')); $twig_env->addGlobal('settings', $pages->get('/site-settings/')); // each template can add custom variables to this, it // will be passed down to the page template $variables = []; This includes most common fuel variables from ProcessWire, but not all of them. You could also just iterate over all fuel variables and add them all, but I prefer to include only those I will actually need in my templates. The "settings" variable I'm including is simply one page that holds a couple of common settings specific to the site, such as the site logo and site name. Page templates By default, ProcessWire loads a PHP file in the site/templates folder with the same name of the template of the current page; so for a project template/page, that would be site/templates/project.php. In this setup, those files will only include logic and preprocessing that is required for the current page, while the Twig templates will be responsible for actually rendering the markup. We'll get back to the PHP template file later, but for the moment, it can be empty (it just needs to exist, otherwise ProcessWire won't load the page at all). For our main template, we want to have a base html skeleton that all sites inherit, as well as multiple default regions (header, navigation, content, footer, ...) that each template can overwrite as needed. I'll make heavy use of block inheritance for this, so make sure you understand how that works in twig. For example, here's a simplified version of the html skeleton I used for a recent project: {# site/twig/pages/page.twig #} <!doctype html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{% block title %}{{ '%s | %s'|format(page.get('title'), homepage.title) }}{% endblock %}</title> <link rel="stylesheet" type="text/css" href="{{ config.urls.site }}css/main.css"> </head> <body class="{{ page.template }}"> {% block navigation %}{{ include("components/navigation.twig") }}{% endblock %} {% block header %}{{ include("components/header.twig") }}{% endblock %} {% block before_content %}{% endblock %} {% block content %} {# Default content #} {% endblock %} {% block after_content %}{% endblock %} {% block footer %}{{ include("components/footer.twig") }}{% endblock %} </body> </html> All layout regions are defined as twig blocks, so each page template can override them individually, without having to touch those it doesn't need. I'll fill the content block with default content soon, but for now this will do. Now, templates for our content types can just extend this base. This is the template for the homepage: {# site/twig/templates/pages/page--home.twig #} {% extends "pages/page.twig" %} {# No pipe-seperated site name on the homepage #} {% block title page.get('title') %} {# The default header isn't used on the homepage #} {% block header %}{% endblock %} {# The homepage has a custom slider instead of the normal header #} {% block before_content %} {{ include('sections/section--homepage-slider.twig', { classes: ['section--homepage-slider'] }) }} {% endblock %} Note that I don't do most of the actual html markup in the page templates, but in individual section templates (e.g. section--homepage-slider.twig) that can be reused across content types. More on this in the next section. We still need to actually render the template. The page template (which will be the entry point for twig) will be loaded in our _main.php, which we defined as the appendTemplateFile earlier, so it will always be included after the template specific PHP file. $template_file = 'pages/page--' . $page->template->name . '.twig'; $twig_template = file_exists($twig_main_dir . '/' . $template_file) ? $template_file : 'pages/page.twig'; echo $twig_env->render($twig_template, $variables); This function checks if a specific template for the current content type exists (e.g. pages/page--home.twig) and falls back to the default page template if it doesn't (e.g. pages/page.twig). This way, if you want a blank slate for a specific content type, you can just write a twig template that doesn't extend page.twig, and you will get a blank page ready to be filled with whatever you want. Note that it passes the $variables we initialized in the _init.php. This way, if you need to do any preprocessing or data crunching for this request, you can do it inside the PHP template and include the results in the $variables array. At this point, you can start building your default components (header, footer, navigation, et c.) and they will be included on every site that extends the base page template. Custom sections Now we've done a great deal of setup but haven't actually written much markup yet. But now that we have a solid foundation, we can add layout components very easily. For most of my projects, I use the brilliant Repeater Matrix module to set up dynamic content sections / blocks (this is not the focus of this tutorial, here's a detailed explanation). The module does have it's own built-in template file structure to render it's blocks, but since it won't work with my Twig setup, I'll create some custom twig templates for this. The approach will be the same as with the page template itself: create a "base" section template that includes some boilerplate HTML for recurring markup (such as a container element to wrap the section content in) and defines sections that can be overwritted by section-specific templates. First, let's create a template that will iterate over our the Repeater Matrix field (here it's called sections) and include the relevant template for each repeater matrix type: {# components/sections.twig #} {% for section in page.sections %} {% set template = 'sections/section--' ~ section.type ~ '.twig' %} {{ include( [template, 'sections/section.twig'], { section: section }, with_context = false ) }} {% endfor %} Note that the array syntax in the include function tells Twig to render the first template that exists. So for a section called downloads, it will look look for the template sections/section--downloads.twig and fallback to the generic sections/section.twig. The generic section template will only include the fields that are common to all sections. In my case, each section will have a headline (section_headline) and a select field to choose a background colour (section_background) : {# sections/section.twig #} {% set section_classes = [ 'section', section.type ? 'section--' ~ section.type, section.section_background.first.value ? 'section--' ~ section.section_background.first.value ] %} <div class="{{ section_classes|join(' ')|trim }}"> <section class="container"> {% block section_headline %} {% if section.section_headline %} <h2 class="section__headline">{{ section.section_headline }}</h2> {% endif %} {% endblock %} {% block section_content %} {{ section.type }} {% endblock %} </section> </div> This section template generates classes based on the section type and background colour (for example: section section--downloads section--green) so that I can add corresponding styling with CSS / SASS. The specific templates for each section will extend this base template and fill the block section_content with their custom markup. For example, for our downloads section (assuming it contains a multivalue files field download_files): {# sections/section--download.twig #} {% extends "sections/section.twig" %} {% block section_content %} <ul class="downloads"> {% for download in section.download_files %} <li class="downloads__row"> <a href="{{ download.file.url }}" download class="downloads__link"> {{ download.description ?: download.basename }} </a> </li> {% endfor %} </ul> {% endblock %} It took some setup, but now every section needs only care about their own unique fields and markup without having to include repetitive boilerplate markup. And it's still completely extensible: If you need, for example, a full-width block, you can just not extend the base section template and get a clean slate. Also, you can always go back to the base template and add more blocks as needed, without having to touch all the other sections. By the way, you can also extend the base section template from everywhere you want, not only from repeater matrix types. Now we only need to include the sections component inside the page template {# pages/page.twig #} {% block content %} {% if page.hasField('sections') and page.sections.count %} {{ include('components/sections.twig' }} {% endif %} {% endblock %} Conclusion This first part was mostly about a clean environment setup and template structure. By now you've probably got a good grasp on how I organize my twig templates into folders, depending on their role and importance: blocks: This contains reusable blocks that will be included many times, such as a responsive image block or a link block. components: This contains special regions such as the header and footer that will probably be only used once per page. sections: This will contain reusable, self-contained sections mostly based on the Repeater Matrix types. They may be used multiple times on one page. pages: High-level templates corresponding to ProcessWire templates. Those contain very little markup, but mostly include the appropriate components and sections. Depending on the size of the project, you could increase or decrease the granularity of this as needed, for example by grouping the sections into different subfolders. But it's a solid start for most medium-sized projects I tackle. Anyway, thanks for reading! I'll post the next part in a couple of days, where I will go over how to add more functionality into your environment in a scalable and reusable way. In the meantime, let me know how you would improve this setup!
    1 point
  12. Thanks @szabesz and @bernhard - I decided to just switch the service to https://checker.html5.org/ - I don't think an option to specify any service will be very useful because I parse the returned response before displaying it in the panel, so the service must return the expected html. Anyway, hopefully this will keep us going for a while - let me know if you notice any problems with this option.
    1 point
  13. My template is set to end with a slash. But I implemented your override and that worked!! Thanks!
    1 point
  14. Yes and I will never ever recommend it to anyone in the near future. It's too bloated and too much of an overhead for most things and the things I want to accomplish. The idea is nice but but no. I use a local LAMP setup which is way easier to maintain, to extend and to use. Maybe its because I focus on frontend and less on backend or plain programming so maybe you should still try to play with it. Well... you know how to contact me for further discussions. ?
    1 point
  15. For sure Adrian will be faster than anybody else, but just see how he does those things with eg the vscode file editor links setting and do the same. This will never take you 2-3 days, I'm sure. And I guess you'll learn something new (otherwise you would not think that such a modification would take so long). But I don't mean that as an offense. I just think that everybody of us (me included) should try to contribute as much as possible and not just request things ?
    1 point
  16. I would probably need to spend 2 or 3 days on it. I guess it is not comparable to the time-frame Adrian needs to do it.
    1 point
  17. Sounds like an easy and valuable PR ?
    1 point
  18. Is it just me who is upset by not being able to validate "real-time"? Anyway, in Tracy's source code I read: "what about switching to: https://checker.html5.org ?" So I just manually changed it to : $this->validatorUrl = 'https://checker.html5.org/'; Seems to work. @adrian Any chance of adding options to the Settings so that we can provide a working ULR without changing the source code? I am thinking of a text inputbox, so that any working service can be provided, just in case. As far as I can see, currently we have three options: https://validator.github.io/validator/ of which html5.validator.nu/validator.nu does not work at the moment.
    1 point
  19. ProCache, ProFields (Matrix), ProLister
    1 point
  20. it's still some kind of a Ubuntu-based distribution. ? But yes... they do several things different and right. I played a lot with linux the past days and to be honest... Ubuntu seems to be the perfect fit for developers (or at least me) as there are so many ready-made packages for Ubuntu or at least .deb packages available. Tried Manjaro in all its flavours (Gnome, KDE, XFCE, i3) but I haven't had the option of packages I wanted. Now on Ubuntu it's a complete different thing. I can change from Gnome to KDE or i3 almost instantly if I wanted. And in terms of an editor or IDE I saw a lot of really nice things that were done with VIM.
    1 point
  21. Agree after doing a lot of testing during the last days... Wow, thx for that list @gmclelland! c9 looks promising, but I want to stay with vscode. Something like c9+vscode would be awesome. I decided to stay with a local dev environment for now. Thx @FrancisChung for the popOS link. I've watched some youtube reviews. It looks nice indeed.
    1 point
  22. Any change if you use the version from the PW3 branch in the github repo for PaymentModule (which is basically the same with the namespace added)?
    1 point
  23. There is a module in Ruby called Deface which is widely used in an ecommerce system called Spree: https://github.com/spree/deface It's been around for a while. "Deface is a library that allows you to customize HTML (ERB, Haml and Slim) views in a Rails application without editing the underlying view." MarkupRegions essentially works in a similar way. It's a bit of a shift in thinking but it's quite nice if you set things up the right way. I'm experimenting with a site profile that takes this approach extensively. How have you used MarkupRegions in creative ways?
    1 point
  24. Online Editors/IDE's look great from the outside, but personally I'd rather be in control of my environment. E.g. I had to disable tests in one project for my CD pipeline (and run tests on gitlab instead) for about a year because the platform only supported hardcoded databases, but I needed to run postgres with the (rather popular) postgis extension. They since switched to allow for custom docker containers as additional services, but it show's how limiting platforms can be. At least for me it's hardly just about having a fancy text editor on the web, but there are quite a few more components to a usable dev environment.
    1 point
  25. If you're in the mood for trying out a different distro, I can heartily recommend PopOS. I've been using it for a year now, and I will never go back to OS/X as a dev environment. https://itsfoss.com/pop-os-linux-review/
    1 point
  26. I don't have much time to review these. Not sure if it helps, but here's some of my notes: Online Editors https://c9.io/ - Amazon’s Cloud 9 https://www.koding.com/ - can work on local files and sync remotely https://codeanywhere.com/ https://codenvy.io https://coder.com/ https://gitpod.io - open any github page in an online VScode editor that can create a pull request back to github. Uses Docker. Prefix any github page with https://gitpod.io# ex. https://gitpod.io#https://github.com/arunoda/learnnextjs-demo Test and Share code - Web based IDE https://codesandbox.io/ - can install VSCode extensions see https://hackernoon.com/announcing-codesandbox-v3-4febbaba1963 https://stackblitz.com/ - online VSCode Editor than can run react like CodePen https://repl.it/ https://hyperdev.com/ - like http://plnkr.co/ but better to test and share your code https://snack.expo.io/ https://codio.com/ - full dev stack testing https://glitch.com/ http://runnable.com/ http://codepen.io/ https://codepicnic.com - ex. https://codepicnic.com/posts/that-moment-when-i-showed-my-students-how-to-bring-their-code-to-life-bd686fd640be98efaae0091fa301e613 Maybe one of these would fit your needs?
    1 point
  27. Hi MoritzLost, In case you didn't already know... You can also check out https://github.com/wanze/TemplateEngineTwig as a good example of a module that doesn't bundle a php library with the module. In this case you can run: composer require wanze/template-engine-twig:^2.0 --no-dev That one line would install the needed php libraries in the correct vendor folder and install the Processwire module in the correct directory.
    1 point
  28. Recently I have been trying to improve the user friendliness of various page select fields, including single select and checkboxes. The issue comes down to the fact that if say for example you have a single checkbox, you can explain what the effect of checking that box is. But if you are using checkboxes on a page select field, there is no way to have any extended information about the option. An example of where something like this is already in use is on the Status field - each option has the title of the option, e.g. Unpublished, and then additional info, like "Not visible on site". Currently my solution is to use a custom inputfield that extends the primary inputfield, which extends the attributes for each option, and then use javascript for handling the display. On checkboxes, adding uk-tooltip to the checkbox labels with the data-description on the option becoming the tooltip content. (see screenshot below) On single selects, i have some JS replace the field description with the info about the selected option. (see screenshot). The reason i'm posting this here is to see if there is any simpler/better way to do this, e.g. hook into the creation of the options for any page field and add the custom attributes, without having to change the inputfield type. And i thought this could be a good candidate for AOS. To better illustrate how it works, i have included some screenshots and links to the repos for the select extended and checkboxes extended. Select Extended: nothing selected Option selected, with option's description showing: https://github.com/outflux3/InputfieldSelectExtended ----- Checkboxes Extended hovering over any option shows the option's description: https://github.com/outflux3/InputfieldCheckboxesExtended
    1 point
  29. Sounds like your issue is in fact with the ProcessWire module. But if you need help integrating the Mollie SDK, I wrote a tutorial on setting up Composer with ProcessWire, this includes how to initialize a Composer project, require (i.e. install) third-party dependendencies and connect the autoloader. If you're having problems with the setup, let me know in the comments and I'll try to answer ? A good place to install the libraries is one directory above the webroot. since you don't want individual files of external libraries to be publicly accessible (since this opens up quite a lot of security issues). My tutorial includes an explanation of the directory structure I would recommend.
    1 point
  30. It only loads the defined .htaccess.local files. The regular .htaccess files are completly ignored. Yes, you may need to copy all locally required parts from regular to local AccessFiles. But I locally work with the pw distri htaccess file. I don‘t want to have settings included for caching or for ProCache. Also rewrites for different domain names to one final domain etc. only stays in the online file. Nothing of that is needed or explicitly must be avoided locally. Maintaining the .htaccess for online deploy exclusivly and add .htaccess.local to the .gitignore. ?
    1 point
  31. You already said it, it depends... I never go without ListerPro because listing, editing, searching and filtering is so easy, even for inexperienced editors. Since I hate doing forms by myself I use FormBuilder in almost every project. From the ProFields package Table and Multiplier (some of them unfortunately not multilanguage capable)
    1 point
  32. Another possibility is to use different names for htaccess files for local development and for live servers. In your local httpd.conf you can set for AccessFilename something like AccessFileName .htaccess.test or AccessFileName .htaccess.local where you can define different settings for local development by keeping the .htaccess file untouched for online server settings.
    1 point
  33. validator.nu (utilized by Tracy) seems to have been down for more than 16 hours or so. Most "is it down or just me" websites also report that it is down, a few report it is not down. Does anyone have more info on this? I could not google any news regarding this issue.
    1 point
  34. Thanks for testing. And yes, this is right. But for me personally, (and for most of my clients), the main goal is to present pages with text and images on the screen, and not to let the visitors save images for later use. I think, if a visitor need a visual copy of a page, he still can save the complete page with his browser, or he can do screenshots. On photographer sites, the use of images independend from viewing the site / page are often prohibited. There it even should not be recognized that images with filetype jpeg in real are weppys. But that is my personal opinion, and maybe thats not a sufficient reason to include this into the PW htaccess. But it also could be done the other way round: send .webp in markup and let the htaccess directives change this to (png|jpg) if a) the browser doesn't support this, or b) if there is no webp file available. This way round, in a 100% perfect site, all newer browsers would get 100% webp with correct file type. Only older browsers would get jpegs or pngs with file type webp. ?
    1 point
  35. Have a look also to the section on Sub-selectors in that doc. So this should do it: $matches = $pages->find("template=group, children=[my_int_field=0]"); And for the other case: $groups = $pages->find("template=task,parent=[closed=0]");
    1 point
  36. Some introduction... This module is experimental and there are probably bugs - so treat it as alpha and don't use it on production websites. I started on this module because there have been quite a few requests for "fake" or "invisible" parent functionality and I was curious about what is possible given that the idea sort of goes against the PW page structure philosophy. I'm not sure that I will use this module myself, just because I don't really see a long list of pages under Home (or anywhere else) as untidy or cluttered. I would tend to use Lister Pro when I want to see some set of pages as a self-contained group. But maybe others will find it useful. At the moment this module does not manipulate the breadcrumb menu in admin. So when you are editing or adding a virtual child the real location of the page is revealed in the breadcrumb menu. That's because I don't see the point in trying to comprehensively fool users about the real location of pages - I think it's better that they have some understanding of where the pages really are. But I'm open to feedback on this and it is possible to alter the breadcrumbs if there's a consensus that it would be better that way. Virtual Parents Allows pages in Page List to be grouped under a virtual parent. This module manipulates the page list and the flyout tree menu to make it appear that one or more pages are children of another page when in fact they are siblings of that page. Why would you do that instead of actually putting the child pages inside the parent? Mainly if you want to avoid adding the parent name as part of the URL. For example, suppose you have some pages that you want to be accessed at URLs directly off the site root: yourdomain.com/some-page/. But in the page list you want them to be appear under a parent for the sake of visual grouping or to declutter the page list under Home. Example of how the page structure actually is Example of how the page structure appears with Virtual Parents activated How it works This module identifies the virtual parents and virtual children by way of template. You define a single template as the virtual parent template and one or more templates as the virtual child templates. Anytime pages using the child template(s) are siblings of a page using the parent template, those child pages will appear as children of the virtual parent in the page list and tree menu. You will want to create dedicated templates for identifying virtual parents and virtual children and reserve them just for use with this module. Features Adjusts both page list and tree flyout menu to show the virtual parent/child structure, including the count of child pages. Works everywhere page list is used: Page List Select / Page List Select Multiple (and therefore CKEditor link dialog). Intercepts the "Add page" process in admin, so that when an attempt is made to add a child to a virtual parent, the child is added where it belongs (the next level up) and the template selection is limited to virtual child templates. Intercepts moving and sorting pages in the page list, to ensure only virtual children may be moved/sorted under the virtual parent. Superusers have a toggle switch at the bottom of the page list to easily disable/enable Virtual Parents in order to get a view of what the real page structure is. Usage Install the Virtual Parents module. In the module config, enter pairs of parent/child template names in the form virtual_parent_template=virtual_child_template. If needed you can specify multiple pipe-separated child templates: virtual_parent_template=child_template_1|child_template_2. One pair of template names per line. There is a checkbox in the module config to toggle Virtual Pages on and off, but it's more convenient to use this from the page list. Notes It's important to keep in mind the real location of the virtual child pages. This module is only concerned with adjusting the appearance of page list and tree menu for the sake of visual grouping and tidiness. In all other respects the virtual children are not children of the virtual parent at all. It's recommended to select an icon for the virtual parent template (Advanced tab) so virtual parents are marked out in the page list as being different from normal parent pages. Do not place real children under a virtual parent. There is some protection against this when moving pages in the page list, but when it comes to changing a page's parent via the Settings tab the only protection is common sense. https://github.com/Toutouwai/VirtualParents
    1 point
  37. Offtopic: no offense, but this is one reason why I stopped using bootstrap, uikit, foundation et al. Looks tempting at first but maintenance becomes a nightmare later.
    1 point
  38. You can filter the notices with a hook before Page::render in /site/ready.php The file compiler notices are useful information so probably sensible to filter these notices only for non-superusers. $this->addHookBefore('Page::render', function($event) { $user = $this->user; if(!$user->isSuperuser()) { $notices = $this->notices; $notices->not("class=FileCompiler"); $notices->not("text~=Compiled file"); } });
    1 point
×
×
  • Create New...