Leaderboard
Popular Content
Showing content with the highest reputation on 07/29/2020 in all areas
-
You $test variable may have been overwritten somewhere. In general, global variables are considered bad style because of their lack of scoping. For things you need to access from multiple places, I would either use the $config object or the setting() function: $config->myCutomSetting = 'test'; setting('my-custom-setting', 'test'); // use from anywhere, even within functions echo wire('config')->myCustomSetting; // 'test' echo setting('my-custom-setting'); // 'test' Note that the setting() function requires $config->useFunctionsAPI = true6 points
-
Well this was a great one to do. The Beyond Banglatown site is the public facing site for a report conducted by researchers at the London School of Economics and the University of Manchester, and published by the Runnymede Trust. The aim is to present the findingsof the report in a publically accessible way that can be used by teachers and students and well as the general public. It covers the changing face of the neighbourhood around Brick Lane in East London. https://beyondbanglatown.org.uk/ Technically this was the first project we used ProcessWire on - although what with one thing and another it's taken 18 months to get the site launched, so we've managed to get a few others out before this one. It was certainly jumping in at the deep end though and we learned a lot. The site involves a fair amount of ajax driven content and we had to get to grips with importing the data used for the maps and graphs as well as sorting out how best to manage templates and assets. As far as modules that we used there's nothing too exciting, but certainly we made good use of TracyDebugger as we fumbled about. A special mention also needs to go to the PW documentation, the API references and of course this forum which provided a great deal of help. Anyway - we're really pleased with the site and it's already been getting very good coverage. Hopefully the first of many PW sites. s.5 points
-
Do not use global unless you really know what you are doing. Read this: https://stackoverflow.com/questions/16959576/reference-what-is-variable-scope-which-variables-are-accessible-from-where-and $this->wire() is another option to assign global vars, especially for more complex stuff ... like class instances. /** * Get an API variable, create an API variable, or inject dependencies. * * * @param string|object $name Name of API variable to retrieve, set, or omit to retrieve the master ProcessWire object. * @param null|mixed $value Value to set if using this as a setter, otherwise omit. * @param bool $lock When using as a setter, specify true if you want to lock the value from future changes (default=false). * @return object|string|mixed * @throws WireException * * */ public function wire($name = '', $value = null, $lock = false) {}4 points
-
No, because you are finding Field objects these are neither empty nor not empty. Instead you need to check the values of the field names for the current page to see which are empty. Something like this... $i = 1; foreach($page->fields as $field) { // Limit to 3 non-empty field values if($i > 3) break; // Check if value is empty $value = $page->getFormatted($field->name); if($value instanceof WireArray) { if(!$value->count) continue; } else { if(!$value) continue; } // Do whatever with $field here echo "{$field->name}<br>"; $i++; }4 points
-
If I understand you correctly, you only need the inputfields to appear in the form, but you don't need actual tables in the database for persistent storage for those fields? In this case, I wouldn't create a new field at all, but instead add the inputfields you want using hooks. For custom inputs / markup you can use InputfieldMarkup, for everything else use the appropriate Inputfield module. This should work in any ProcessWire form context. For example, here's a hook that adds my InputfieldHCaptcha module to the edit form of a page in the backend: wire()->addHookAfter('ProcessPageEdit::buildForm', function (HookEvent $event) { $form = $event->return; $submitButton = $form->getChildByName('submit_save'); if ($submitButton) { $hCaptcha = $event->wire('modules')->get('InputfieldHCaptcha'); $hCaptcha->set('label', __('Spam Protection')); // ... configuration goes here $form->insertBefore($hCaptcha, $submitButton); } $event->return = $form; }); See InputfieldHCaptcha. Like that you should be able to add any inputfield you want to the form. This approach has the added benefit that you don't leave behind dead fields after your module is uninstalled.3 points
-
A huge thank you to @Gadgetto for this tutorial. This is my first time with multi-language, and Gadgetto provided the perfect tutorial for beginners such as myself to learn from. I worked my way through the tutorial and can attest it works beautifully and I could follow along without getting lost. I now have a wonderful language switcher on my site AND the html lang tag AND the href lang alternate tags, all thanks to Gadgetto's great tutorial above. The screenshots were really helpful too. His code really should be a must-read for those making their first multi-language site. Thanks again! ?3 points
-
Just wanted to share the simplest language switcher possible if you only have two languages: <a href="<?= $page->localUrl($languages->findOther()->first()) ?>">DE/EN</a> ??3 points
-
this looks like a good fit for my usecase! Now I hit another roadblock. I want the fields to be grouped together in a fieldset that can be collapsed. How would I do this with the inputfields approach? EDIT: Figured it out, here is my final code: $this->addHookAfter('ProcessPageEdit::buildForm', function (HookEvent $event) { $form = $event->return; $page = $event->object->getPage(); // make sure we're editing a page and not a user if ($event->process != 'ProcessPageEdit') return; if (!$page->template->hasField("pgrid")) return; $submitButton = $form->getChildByName('submit_save'); if ($submitButton) { $fieldset = $event->wire('modules')->get('InputfieldFieldset'); $fieldset->label = 'Fieldset Test'; $field1 = $event->wire('modules')->get('InputfieldText'); $field1->set('label', __('Test123')); $field1->set('name', __('test123')); $field1->addClass('test123'); $fieldset->append($field1); // append the field $form->insertBefore($fieldset, $submitButton); } $event->return = $form; }); @MoritzLost thanks again for your help, this was very helpful!2 points
-
@jploch Exactly! Regular ProcessWire fields have both a fieldtype (responsible for storing stuff in the database) and an inputfield (responsible for displaying the input form element, validating the input etc). The hCaptcha module only comes with an inputfield without a corresponding fieldtype, because the captcha response doesn't really need to be stored. The module just needs to display some markup in the frontend and validate the input after submission, which is both done in the inputfield. Because you can't create a regular ProcessWire field without a corresponding fieldtype and database table (at least as far as I'm aware), an instance of the inputfield is inserted in the edit form through a hook. I don't think it will make any noticable difference, since the the inputfields need to be constructed for each page load anyway, regardless of whether they come from regular fields or a hook. Maybe a super tiny overhead because of the hook, but then we're talking nanoseconds ?2 points
-
Just wanted to link a solution for people that are in a hurry ?2 points
-
We relaunched the website of German health insurance broker KLforExpats, who provide a service that is specifically tailored for expatriates in Germany. The website includes very extensive, completely custom-built forms for data entry and multiple custom interfaces for management and handling of requests. Concept, design, branding and development by schwarzdesign. If you are moving to Germany and need health insurance, KLforExpats is the contact for you! Read on below for some technical insights. Features A beautful, streamlined website including an extensive knowledge area (Expert Corner) Custom-built forms for initial contact and data collection A central database of clients / leads An analytics dashboard that displays key performance indicators based on the lead database A client / lead template with multiple workflow-related actions Automatic generation of Trello cards for new leads using the Trello API Notable modules Dashboard TrelloWire ProFields Hanna Code ListerPro Cacheable Placeholders Cache Control Automatically link page titles Unique Image Variations Regular shoutout to Tracy Debugger Building custom forms based on ProcessWire fields The forms on the site are built from scratch, which is a lot of work but opens up a lot of fine-tuning that isn't possible with form builder modules or services. There are a couple of interesting features of the form system we built. In particular, using built-in HTML5 features for form input and constraint validation makes developing simple, cross-browser and mobile-friendly forms a breeze. The forms make heavy use of modern input types and attributes. In particular, all date fields use the date input type, which is supported in all major browsers except Safari. This way, the forms come with good accessibility out of the box. A cleaner solution than using some rickety jQuery UI datepicker. Client-side validation is pure HTML5 as well. Since each form consists of multiple steps, the validation is triggered when the user tries to go to the next step. This is easily done by iterating through the inputs in the current step and calling reportValidity on them. The browser takes care of reporting errors – no need for a popup library. We use ProcessWire's field settings to generate field labels and validation attributes (like the required flag, minimum and maximum length settings etc.). This way, changes to the fields are always kept in sync between the frontend and the backend. For server-side validation, we used an open source library (rakit/validation). We added some custom rules to integrate it with ProcessWire's CSRF protection, a honeypot spam protection, and file uploads using WireUpload. This way, validation and error reporting can be done through a uniform interface. Using custom page classes as data models New leads are represented by ProcessWire pages. We ended up writing a lot of custom functionality for those pages – for example, automatically generating a vCard based on the contact information entered in the form. We used a custom page class as a nice way to group those methods and be able to call them from anywhere. <?php namespace schwarzdesign\Page; use Processwire\Page; class ClientPage extends Page { /*** methods here */ } Since each lead is a regular page, we used the regular page template to display all the data collected for this lead as well as to provide an interface to perform lead-related actions, like create form access keys, generate PDF protocols, etc. Since we use Twig as a templating layer, we ended up with a MVC-like approach, where the PHP-template is only used to call the appropriate methods of the ClientPage based on URL parameters. You can read more on the process and the client-facing functionality on the KLforExpats project showcase on our website (in German).2 points
-
@BillH, another way to change such labels is to install LanguageSupport (you don't need to install any of the related language modules if you're not using multi-language in your site). Then edit the Default language under Setup and "translate" any strings in the ProcessPageEdit.module that you want to change.2 points
-
I needed a language switcher so i searched the forum and docs, found very useful stuff, but as im new in processwire (came from joomla) all this is still a bit confusing for me, need time to get use to it i guess. There's probably other people like me get confused in all the power of pw, and need language switcher on the site, so i decided to share what i came up with. Actually its very simple, a lot easier then i thought. There is very nice example in docs using select box (there i found out about $languages array, and its clicked), but you need additional script if you wanna style it, so i came up with something simple: TADAAAAA <?php foreach($languages as $language) : ?> <a href="<?=$page->localUrl($language)?>"><?=$language->title?></a> <?php endforeach;?> Here's another example, but now active language is just a span with active class. (Style used on most websites). <?php foreach($languages as $language) : ?> <?php if($user->language->id == $language->id) :?> <span class="active"><?=$language->title?></span> <?php else : ?> <a href="<?=$page->localUrl($language)?>"><?=$language->title?></a> <?php endif;?> <?php endforeach;?> Here is UIkit dropdown example, same can do with bootstrap, just a bit different markup. <div class="uk-button-dropdown" data-uk-dropdown> <button class="uk-button"><?=$user->language->name?></button> <div class="uk-dropdown uk-dropdown-bottom"> <ul class="uk-nav uk-nav-dropdown"> <?php foreach($languages as $language) : ?> <?php if($user->language->id != $language->id) :?> <li><a href="<?=$page->localUrl($language)?>"><?=$language->title?></a></li> <?php endif;?> <?php endforeach;?> </ul> </div> </div>2 points
-
Its not or I'm not aware of how that would happen for tests I did before posting. I'm always up for learning Does the trick. It seems that defining variables this way should be done in config.php file. Either way thanks for help. ?1 point
-
Thanks @MoritzLost ! yep thats what I meant. So just to understand your approach better. You insert the fields after buildForm so they won't get saved to bd when the page is saved? This would be called everytime the page is loaded, wouldn't this be bad for performance with a lot of fields?1 point
-
Hello, I'm very new to ProcessWire but already fell in love with this CMS/CMF! I just finished my first small project and as I saw a lot of questions and different answers in this forum on how to set up a nice language switcher for your website, I decided to write my first tutorial. ---------- Please note: I rewrote this tutorial since I was made aware and learned that flags should not be used for language selectors! There are some threads here in the forum (and from external sources) where this question is discussed: https://processwire.com/talk/topic/13196-adding-image-field-to-language/ http://daily.unitedlanguagegroup.com/stories/editorials/inside-design-language-selector-no-flags https://processwire.com/talk/topic/16524-extending-languages-template/ http://www.flagsarenotlanguages.com/blog/why-flags-do-not-represent-language/ https://processwire.com/talk/topic/14241-language-names-and-utf8-page-names/ Thanks, @ottogal @bernhard @jmartsch @kongondo an all others for your hints! ---------- TUTORIAL - Set up a nice language switcher for your website - here we go: This will be the desired result! Step 1) Setup at least 2 languages in your PW install. In my case it's German (default language) + English: Step 2) Add a custom field Type = Text Name = languagecode This will hold the ISO 639-1 two-letter language code for the respective language. The field is needed to provide a simple method for outputting the language code in your templates. Without this field, you will need to programmatically construct your two-letter language code output via PHP (at least for the default language, as ProcessWire doesn't allow to rename the default language and it will always be called default). Here is an overview for ISO 639-1 two-letter language codes: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes Step 3) Add this field to the system template: language. To achieve this, go to Setup / Templates and activate the filter Show system templates: Now you can add the previously created field languagecode to the language template. Step 4) Edit your languages and fill in the appropriate values. a) default (German) Name = default (this can't be changed and is read only) Title = Deutsch (in both language tabs! - this is important as your visitor should always see his language item ... in his language) languagecode = de b) english (English) Name = english Title = English (in both language tabs! - this is important as your visitor should always see his language item ... in his language) languagecode = en Step 5) Now we are ready to write our template output! As we already have the appropriate two-letter ISO language code (languagecode field), we can use this in our html lang property: <html lang="<?php echo $user->language->languagecode; ?>"> Also the rel alternate output in the html head is simple. Put the following code within your <head></head> area: <?php // Handle output of 'hreflang' link tags for multi-language (SEO!) foreach ($languages as $language) { if (!$page->viewable($language)) { continue; } // Get the http URL for this page in the given language $url = $page->localHttpUrl($language); // Get the language code using custom languagecode field $languagecode = $language->languagecode; echo PHP_EOL.'<link rel="alternate" hreflang="'.$languagecode.'" href="'.$url.'">'; } ?> In my sample I've used Boostrap 4 and the code below shows a complete navbar with our language switcher (BTW the language switcher will always be visible, even when the bootstrap navbar is collapsed): <nav id="mainnav" class="navbar navbar-expand-lg navbar-light px-4 px-md-5 sticky-top"> <a class="navbar-brand" href="<?php echo $config->urls->root; ?>"> <img src="<?php echo $config->urls->templates; ?>images/logo-rund-80x80.png" alt=""> Your Site Title </a> <ul class="navbar-nav ml-auto mr-3 mr-lg-0 order-lg-last d-none d-xs-custom-flex language-switcher" aria-label="<?php echo __('Sprache wechseln') ?>"> <?php echo '<li class="nav-item dropdown">'; // Construct the language prompt in the current user language $prompt = $user->language->title.' ('.strtoupper($user->language->languagecode).')'; // Current language = dropdown-toggle echo '<a class="nav-link dropdown-toggle" href="#languages" id="language-select" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">'; echo '<span class="world-icon"></span><span class="sr-only">'._x('(aktuelle Sprache)', 'navigation').': </span> '.$prompt; echo '</a>'; echo '<div id="languages" class="dropdown-menu dropdown-menu-right" aria-labelledby="language-select">'; foreach ($languages as $language) { // Get the http URL for current page in the given language $url = $page->localHttpUrl($language); // Construct the language prompt in the given language $prompt = $language->title.' ('.strtoupper($language->languagecode).')'; // Next language item (except current language) if ($user->language->id != $language->id) { if (!$page->viewable($language)) { echo '<span class="dropdown-item disabled">'.$prompt.'</span>'; } else { echo '<a class="dropdown-item" href="'.$url.'">'.$prompt.'</a>'; } } } echo '</div>'; echo '</li>'; ?> </ul> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarMainMenu" aria-controls="navbarMainMenu" aria-expanded="false" aria-label="<?php echo __('Menü einblenden / ausblenden') ?>"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse my-3 my-lg-0" id="navbarMainMenu"> <ul class="navbar-nav mr-auto"> <?php // Top navigation consists of homepage and its visible children foreach ($homepage->and($homepage->children("template=main-page|news|contact-points")) as $item) { if ($item->id == $page->rootParent->id) { echo '<li class="nav-item active">'; echo '<a class="nav-link" href="'.$item->url.'">'.$item->title.'<span class="sr-only"> '._x('(aktuelle Seite)', 'navigation').'</span></a>'; echo '</li>'; } else { echo '<li class="nav-item">'; echo '<a class="nav-link" href="'.$item->url.'">'.$item->title.'</a>'; echo '</li>'; } } ?> </ul> </div> </nav> That's it! I hope you will like my tutorial and if there are any questions, critics or improvements please let me know! Thanks, Martin1 point
-
Thank you so much @Robin S! Your solution works really well!1 point
-
1 point
-
Good call. I checked and I had Rubik installed so I disabled it but the problem remained, maybe I need to restart the computer to work, but I can't do it right now. But I also checked again the website and the only problem appears to be on 300 font-weight. 400 or more are fine ( I changed it to 400 on the Body as a test, screenshot attached).1 point
-
@maddmac - I built that action before PW supported logging in via an email address, so it will need some modifications. I am really busy at the moment but I'll try to take a look on a procrastination break. Unless of course you feel like working on a PR?1 point
-
The site hasn't been on ProcessWire for some time, but payment processing was all done "offline". The advertising packages were just pages that had associated prices. I believe I have a page structure called "Advertising Subscriptions" that had a single-select user field, a single-select advertising package field, and a start and end date. This is the PW equivalent of join table (is that the right word?).1 point
-
@Zeka Thanks for the examples. Reading what you write made me realize I've got a similar case too: https://www.tripsite.com/reviews/ — it's just one page, but hundreds (maybe thousands) of URLs underneath are all URL segment powered for 3 levels. What you are asking for would come in handy here for sure. In the hook example below (which is functional) I clear all /reviews/bike-tours/* segments on the /reviews/ page when a page using template "tour" is saved. And when a page using template "boat" is saved, it clears all of the /reviews/boat-[name]/ segments, where [name] is the name of the boat page that was saved. This is going to come in very handy here, and if I understand your case correctly, I think it's what you are looking for also? $wire->addHook('ProCacheStaticClear::clearedPage', function($event) { $page = $event->arguments(0); // Page that was saved/cleared $clearSegment = ''; if($page->template == 'tour') { // clear all /reviews/bike-tours* segments $clearSegment = 'bike-tours*'; } else if($page->template == 'boat') { // clear all /reviews/boat-name/ segments $clearSegment = "boat-{$page->name}*"; } if($clearSegment) { // clear segments below the /reviews/ page $reviews = $event->pages->get('/reviews/'); $event->object->clearPage($reviews, [ 'urlSegmentStr' => $clearSegment ]); } });1 point
-
1 point
-
Just an observation from "an outsider": folks who are not competent in German might still need a German language pack for their projects. It'd be a little difficult to figure out what's going on if the discussion related to said language pack was in German as well. Also, forum guidelines state that the language here is English — though personally I don't think it's going to be a major issue if you do decide that German would be better (in this specific context, based on a good reason; the key point being that it would be an exception to the rule) ?1 point
-
Ryan might've had other reasons as well, but... this keeps the textformatter from making a mess out of user-generated content where video URLs could be used in links, within regular text, inside table cells, in image captions, etc. ? That being said, it'd be nice if the module provided a public method for converting known single video URL into an embed code. Currently it looks a little hacky: $url = '<p>https://www.youtube.com/watch?v=ytWz0qVvBZ0</p>'; $modules->get('TextformatterVideoEmbed')->format($url); echo $url;1 point
-
Found the issue. To whoever it may concern, as of this day the select file fieldtype doesn't support being a condition for other field's visibility. It's easy to hack though. On the module's file, find the render function and add the id property to the <select> element that it outputs, with $this->name as value. That will make it work1 point
-
Hi @sodesign , interesting, did your issue is fixed ? if yes, how ? thanks; And to answer the two questions : Yes, but it should only do that if you tell NGINX to do it. About the issue, and based on your post, it look like a cache issue where cookies has been cached. The official statement say that by default, NGINX respects the Cache-Control headers from origin servers. It does not cache responses with Cache-Control set to Private, No-Cache, or No-Store or with Set-Cookie in the response header. This is the current behavior; I will add that before the version 0.8.44, the header was stripped then the request cached. All that to say that theses parameters can be overridden, and you should show us your NGINX config file before we go further.1 point
-
Good day! I got a strange issue with webp images on one server. The same image works fine on Firefox and does not show (though blinks for a momдnent when trying to open it) in Chrome. I did regenerate it without luck. Then installed Imagick sizer, regenerated - same thing. Same code works fine on local machine, generating images readable in both browsers (or serving them in correct way), so must be a server issue. But what could it be? Example image used on this page. Please help!1 point
-
I know this is an old thread. But just had a similar problem. You added the $events instantiation code to your _init.php which, most likely, is in site/templates/. But this code needs to go in site/init.php as stated in the tutorial. Then everything works as expected. The $page object inside template file site/templates/event.php is of Type Event and not Page. Sometimes it's the little details that matter. Posting this info mainly for my own reference. But maybe someone else might find it useful, too :)1 point
-
Hi everyone, To celebrate @teppo featuring this module in PW Weekly, I have added a new action: Create Users Batcher It lets you quickly create many users by entering: "username, email address" on separate lines in a textarea. This action requires the EmailNewUser module and it must be configured to automatically generate a user password. The action checks for this module and that setting before it will let you proceed. I also recommend that you install the PasswordForceChange module to reduce the risks of having emailed a password to a user. You can set multiple roles to be added to each new user. Be aware that at least one of these roles must have the "profile-edit" permission so that the user has the ability to change their password when they first login. Note that EmailNewUser will report each successful user creation and email sent success. I hope that the next time you need to create several users for a new client that you'll give this a try. And of course, don't forget to send me your own actions for inclusion with the module1 point
-
A bootstrap 4 alpha version exemple with navbar and dropdown : <nav class="navbar navbar-dark bg-primary bg-faded"> <button class="navbar-toggler hidden-sm-up" type="button" data-toggle="collapse" data-target="#exCollapsingNavbar2"> ☰ </button> <div class="collapse navbar-toggleable-xs" id="exCollapsingNavbar2"> <a class="navbar-brand" href="#">Responsive navbar</a> <ul class="nav navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a> </li> <li class="nav-item dropdown"> <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Languages <span class="caret"></span></a> <ul class="dropdown-menu"> <h6 class="dropdown-header">Dropdown header</h6> <?php foreach($languages as $language) { // determine the "local" URL for this language $url = $page->localUrl($language); // output echo "<li><a class='dropdown-item' href='{$url}'>{$language->title}</a></li>"; } ?> </ul> </li> </ul> </div> </nav>1 point