Jump to content

schwarzdesign

Members
  • Posts

    123
  • Joined

  • Last visited

  • Days Won

    4

Everything posted by schwarzdesign

  1. Hi @Robin S, thanks for this module! I'm having a problem getting the toolbar element "Insert Hanna Tag" into the toolbar inside a nested RepeaterMatrix field. Everywhere else it's working fine, but it just won't appear on fields that are part of a RepeaterMatrix field that are nested inside another RepeaterMatrix field. They are loaded through AJAX, but I don't think it has something to do with that. It is working fine with a regular Repeater nested inside a RepeaterMatrix that is loaded through AJAX, for example. Do you have any idea what may be causing this? Thanks! EDIT: Ok scrap the above, the problem doesn't appear to be related to the fields being nested. In fact, ALL CKEditor fields that are part of a RepeaterMatrix field don't get the Hanna Dialog toolbar element. Regular repeaters work fine, even if nested inside a Repeater Matrix field. Maybe it has something to do with the way the module checks for CK Editor fields?
  2. Thanks @Robin S, that did the trick! @dragan Thank you for your suggestions as well! I adjusted the code a bit to only trigger during POST requests; this way, the form will still display the * required markers, but the form will validate successfully despite the missing fields. Here's my final code, if someone needs it: // site/template/admin.php $wire->addHookAfter('ProcessPageEdit::buildFormContent', function (HookEvent $e) { $formwrapper = $e->return; $page = $e->object->getPage(); $userRoles = wire('user')->roles; $isPrivilegedEditor = $userRoles->has('superuser') || $userRoles->has('editor'); $isPost = wire('input')->requestMethod() === 'POST'; if ($isPost && $page->template->name === 'client' && $isPrivilegedEditor) { foreach ($formwrapper->getAll() as $inputfield) { $inputfield->required = false; } } }); require($config->paths->adminTemplates . 'controller.php'); One thing I got stuck on is that the hook needs to come BEFORE the require statement at the bottom. Otherwise, the hook would be added to late and wouldn't fire at all.
  3. We use the field settings (required, min / max length, description, notes) mostly to display our custom-built front-end forms. So by marking a field as required, it will be rendered as such in the front-end form (frontend validation) and validated accordingly upon submission (backend validation). We do it this way because we have a lot of fields for this template – to make it easier to manage those, we just use the built-in field settings. Of course, the obvious solution would be to just not have those fields as 'required' fields. But that would mean we'd need another layer of settings to select required fields for the frontend-forms, or hard-code that into the template, both of which aren't good solutions. In fact, the normal page editor is much less important than the front-end forms in this case. The users won't see it, only site admins – and it's annoying for the latter. We could live with that, but it would be nice to suppress those warnings, because you need to scroll down two screens to see the actual content because of all the warnings and they have no meaning for us in this case.
  4. @dragan Thanks, but that doesn't solve the problem. I neither want to restore the previous value, nor unpublish the page upon saving. I just want the editors to be able to edit some fields without getting errors about missing required fields.
  5. Short question: I'm looking for a way to suppress the error messages that appear on the edit page (ProcessPageEdit) after saving a page while required fields are missing. I also want to remove the flagged status for those pages as well. Longer context: I'm building a very large template with many fields that are filled through a couple of custom forms in the frontend. We generate the frontend forms mostly based on the field settings from ProcessWire, so there are a bunch of required fields. However, we still allow site admins to manually edit pages in the backend. The problem is that if half the data is still missing, saving the page will generate a bunch of error messages because of the required fields. I would like to be able to suppress those warning, as well as the "flagged" status the page gets after saving it with invalid data, so that site admins can edit those pages without having to worry about the required fields. Is there a hook I can use? I had a quick look at ProcessPageEdit, but couldn't figure out yet where I need to hook in order to clear out the error messages after they are registered. I'm grateful for every advice. Thanks!
  6. I'm having a problem with the built-in $session->redirect() method, namely that it supports only the 302 and 301 status codes. I'm currently working on a form that uses POST requests to update some page fields. After processing the request, I want to send the visitor to another page. The correct HTTP response code for this situation is 303 See Other, because the user-agent is not supposed to repeat the POST request to the new page. It would be a great addition for $session->redirect to support any of the possible 30x status codes, so it can be used in cases like this. For now, I have added this functionality with a hook (see below), but there are two problems with that: $session->redirect does some additional work, namely making sure current notices are correctly queued up as well as forcing graceful shutdown in some situations. This can't be done in a hook because, for example, the method calls $this->queueNotice, which is a protected method, so it can't be called from outside the class. Hooking before or after the redirect method isn't an option here either: Hooking after won't work at all, because the method calls exit(0) at the end, so any hook after that won't be executed. Hooking before may work in some situations, but the method may still overwrite headers set in the before hook, so it won't be reliable in any way. Can you give this a look @ryan? Maybe the method could allow integer arguments as well as booleans, and set the status accordingly if it's one of the allowed 30x codes? For reference, here's my workaround, though this does not perform all the graceful shutdown actions the built-in $session->redirect handles ... // site/ready.php wire()->addHook('Session::redirectStatus', function (HookEvent $e) { $session = $e->object; $url = $e->arguments(0); $httpCode = $e->arguments(1) ?: 302; if (!in_array($httpCode, [300, 301, 302, 303, 304, 307, 308])) { throw new \InvalidArgumentException('HTTP response code for redirect must be a valid 30X code.'); } $statusData = array('redirectUrl' => $url, 'redirectType' => $httpCode); $e->wire()->setStatus(ProcessWire::statusFinished, $statusData); header("Location: $url", true, $httpCode); exit(0); }); // somewhere in a template file $session->redirectStatus($somePage->url(), 303);
  7. Thanks @BitPoet! In that case I would suggest simply calling strip_tags after htmlspecialchars, since strip_tags shouldn't remove the <= if it's properly entity encoded. @ryan Can you take a look? Should I open an issue on Github for this?
  8. I have a curious situation with the page tree inside the ProcessWire Admin (UI Kit Theme). I have a page containing the string "<=" (without quotes), which causes the page title displayed in the page tree to be cut off. It only happens with exactly that directly beside each other. Out of curiosity, I tested both ">=" and "=>", those work fine. Attached are two screenshots showing the bug. If I put a space between the two characters, it works fine. Is this possibly a core bug? I had a quick look at the ProcessPageList module but didn't find anything related to this ...
  9. @dragan Yeah, that's my experience as well. We're not using Google Analytics or Tag Manager anymore because of privacy concerns. To comply with the GDPR, we're now mostly using our own Matomo instance. The tracker is configured to not set any cookies, IP adresses are masked et c to yield completely anonymous statics. You can read about our approach here (in German). Anyway, we have some event tracking set up to track the installs. On Android, we can track both the native dialog as well as the user choice using the beforeinstallprompt event and the userChoice promise. On iOS, there's no PWA install event, so we can only track the popup that informs the visitors about the PWA installation. I've also set a campaign parameter that should be tracked when the PWA is started from the home screen (https://architekturfuehrer.koeln/manifest.webmanifest), but the amount of campaign reports are almost zero, so either some devices aren't correctly including the parameter, or opening rates are even lower than installations ?
  10. I wanted to write a bit of an update since this project was so well received last year. The Architekturführer has now been online for about nine months, and we have been getting mostly positive feedback. Thankfully, I was able to resolve all issues with Google's indexing of the page. The site's SEO is actually pretty solid now, being one of the first results when searching for "Architekturführer" (at least from within Cologne). We have also added some new content and restructured some pages. The homepage, for example, now features only selected projects. An iconic new entry that everyone who's been to Cologne in recent years will recognize is the Rheinboulevard Deutz. There's also an entirely new section for architecture-themed walks (Spaziergänge) that you can take in different areas of the city. If you come to visit for a day, make sure to check those out ? Finally, we got some traffic through word of mouth and a couple of architecture news sites, the visitor numbers are steadily rising each month. The only thing I find a bit lacking is that the number of PWA installations are very low. It seems the whole PWA concept has not really come through yet, or maybe it's just not that much of an added benefit compard to the normal site. I'm interested to see how PWAs will fare against native apps in the future, once they become more widely adopted.
  11. @Jens Martsch - dotnetic Thanks for the feedback! The search button was missing a translation, I have fixed that. As for the rest of the contents, not everything is translated just because of editorial time constraints (also diminishing returns, not all pages are that important). We usually go with the default behaviour (inheriting from the default language when a translation is missing), because in most cases it's still better to fallback to German instead of having empty sections. Of course, ideally everything would be translated!
  12. We recently relaunched DOMiD, the Documentation center and Museum of Migration in Germany. Concept, design and implementation by schwarzdesign. Features Bilingual site with German and English A section-based design focusing on flexibility and ease-of-use for editors Multiple forms built with FormBuilder that can be placed on any page Separate feeds for news & press releases Lightning fast page loads with almost perfect ratings in Lighthouse Completely accessible and SEO-friendly Notable tech decisions Forms and form placement There are multiple forms for different services that DOMiD offers. Those are built with the FormBuilder. The editors don't have access to the FormBuilder itself, but we still wanted to allow them to control which form is displayed on what page. For this purpose, every page has a select field to select which form to include (if any). Additionally, the form placement has additional fields for a headline and a description, so a generic contact form can be reused in different contexts. Section-based design Most pages are built through Repeater Matrix sections. There are multiple section types available, for example: A generic text / image column with up to three columns of text and images An accordion (rendered as <details> elements). An image gallery Downloads (for files and images, displayed as a list of downloads) External Embed (e.g. YouTube) All sections have an optional headline and a selection of three different background colours. In addition, text columns may be rendered as a coloured block with some padding. This allows for interesting and diverse layouts. Testimonial database One of the available sections is for testimonials or statements (you can see one at the bottom of the homepage). Because one testimonial may be displayed on multiple pages and the client wanted to be able to switch the displayed testimonial on the fly, there's a separate content type for statements. The statement content type has fields for the statement text, author, and author image. The testimonial section only has a page reference field to select which statement to show. This way, the testimonial definition is separate from the placement on a page. Modules used Form Builder Pro Fields (Repeater Matrix in particular) Unique Image Variations ALIF - Admin Links in Frontend Sitemap Wire Mail SMTP Developer Tools: Tracy Debugger, Duplicator, ProcesWire Upgrade Migration museum As a sidenote for anyone living in Germany, this month the German Bundestag has approved funding for DOMiD's first Migration Museum ("Haus der Einwanderungsgesellschaft")! The museum will be build in Cologne and is scheduled to be finished by 2023. We're looking forward to it! Check out this page if you want to learn more, or find out what people are saying about it here.
  13. Hi @BitPoet, wow, thanks for that great mini-tutorial! I haven't used the JSON data type at all yet, but this looks super interesting. Probably a bit overkill for my current project, but that is definitely something that would be worth considering for more individual searching requirements, or possibly a search module. I'll keep that in mind ?
  14. I'm working on a search page for a complex site where the search term is searched for in a couple dozen fields, including fields nested inside repeaters and repeater matrixes. While it's easy to get a list of pages that contain the search term somewhere, the result doesn't include the information which field matched the search term. I was hoping that there's an easy way to find out which field matched the find query. Of course, I could iterate over all fields that I'm searching through and check whether they match the search term with $page->matches (something like the solution I posted here), but is there an easier way?
  15. Thanks, but I don't think hooking after the page render will work, since I want to conditionally disable the render cache, so at this point it's too late ..
  16. I just spent some time trying to get a hook in a site/init.php file to work. What I wanted to do was disable the template/page cache based on the value of some field of the current page. I tried following @Soma's example here: Unfortunately, this doesn't work at all for me, since the hook gets called with no arguments. I tried this: wire()->addHookBefore('Page::render', function (HookEvent $event) { bd($event->arguments(0)); bd($event->arguments(1)); }); But both just evaluate to null. So I dug deep to find the PageRender::renderPage method and tried to go through there. I finally got it to work using this rather obscure syntax: wire()->addHookBefore('PageRender::renderPage', function (HookEvent $event) { $page = $event->arguments(0)->object; $disableCache = $page && $page->hasField('cta_display'); if ($isValidPage) { $event->arguments(0)->setArgument(0, ['allowCache' => false]); } }); I'd like to know why the Page::render hook doesn't receive any arguments? Was there some change regarding this since Soma's post? Since the version linked above doesn't work at all for me. What would be the 'correct' way to accomplish this? I'd appreciate any insights. Thanks! ProcessWire Version: 3.0.130 PHP Version: 7.2
  17. @dragan Referenced to the page that is being searched. I think Robin's explanation is pretty accurate. For example, I have five service pages, but only three of them are referenced from different pages (in a page reference field). I want to write a query that will retrieve those three services; the problem here is that numReferences is not a real field, but a computed property. @Robin S Thanks for the explanation! That's what I assumed - I guess it would be possible with a subquery, but I agree that the incurred performance hit would make it a bad tradeoff for selectors. Your module looks ingenious, I'll try it out for my usecase!
  18. ProcessWire added the numReferences property in 3.0.107, which returns the count of all pages having a reference to the page object. However, apparently it's not possible to use this property inside a selector. I'm trying to find all pages that are referenced at least once: $pages->find('template=service, numReferences>0'); This throws an error: "Field does not exist: numReferences". Is there another way to filter by the number of references? Of course, I could manually filter the results of the find query, but that feels overly complicated. Ideally, I would also like to filter the number of references from a specific field; that is, find all pages that are referenced at least once in one specific page reference field. Is there a way to do this? I guess supporting numReferences in selectors would be a feature request - if so, is it feasible? Thanks!
  19. @dragan Thanks for the tip, I fixed this alongside with a couple of other issues. The tool was throwing JavaScript errors that I couldn't figure out. For some reason one of my non-Vue components wasn't being transpiled by babel at all, so Google had trouble with the syntax. I have now rewritten the component as a native Vue SFC and added some more polyfills. Now it's finally working for me, and the mobile-friendly checker shows the rendered page correctly ... Hopefully it will improve the indexing situation ?
  20. @charger Oh I see ? Well, frontend routing is completely handled by Vue, so there are no existing routes (as in, provided by ProcessWire). The only routes ProcessWire knows about are the different API endpoints. One of the screenshots in my post shows the URL segments configuration for the API template. Besides that, @dragan's explanation is spot on; since there are only a handful of API endpoints, those are configured manually. It's not inside the vue-router, since that one is only concerned with routes the visitor sees in their browser; I just wrote a little API class that takes a list of endpoints and made an instance available globally through the Vue prototype. For reference, this is the original unminified code with the endpoint definitions: export const api = new Connector( `https://architekturfuehrer.koeln/api/v${config.API_VERSION}/`, { objects: { path: 'objects', }, object: { path: 'objects/{identifier}', }, taxonomies: { path: 'taxonomies', }, walks: { path: 'walks', }, pages: { path: 'pages', } } ); As for the frontend routes, they are also configured natively inside the Vue app, here's an excerpt: import ObjectList from './pages/ObjectList.vue'; import SingleObject from './pages/SingleObject.vue'; export default new VueRouter({ // ... routes: [ { name: 'objects', path: '/objekte/:taxonomy([-_.a-zA-Z0-9]+)?/:term([-_.a-zA-Z0-9]+)?', component: ObjectList, }, { name: 'object', path: '/objekt/:object([-_.a-zA-Z0-9]+)', component: SingleObject, } }, // ... ], // ... });
  21. I just published another update that makes full use of the Add to Home Screen functionality in Chrome (though the app is installable in Android Chrome, Android Firefox, iOS Safari and regular Chrome). In particular, there's a new page that explains how to install the PWA on different devices, with a button that triggers the system dialogue for installing the PWA on supported browsers (only Chrome at the moment). This was a bit difficult to implement since there's little common ground between browsers in this regard at the moment, so let me know if you find any bugs or other errors! @charger ProcessWire only handles specific paths, everything else is routed to the frontend app and handled by the router. A normal ProcessWire installation uses the .htaccess rules to redirect all requests to it's index.php file. Meanwhile, the Vue SPA uses one single index.html as it's entry point and performs all further routing inside the visitor's browser. Since I want most visitors and paths to go to the frontend app, I modified the .htaccess file ProcessWire comes with to only forward specific routes to ProcessWire, and route all other requests to the Vue app: # .htaccess # Requests to the root domain without a path should go to the Vue app (index.html) instead of ProcessWire (index.php) DirectoryIndex index.html index.php index.htm # This additional RewriteCond comes right before the main RewriteRule from ProcessWire # It lets ProcessWire handle only calls to specific paths (in this case, the admin url (/cms), the API endpoint and the sitemap which is generated server-side) RewriteCond %{REQUEST_URI} (^|/)(api|cms|sitemap) RewriteRule ^(.*)$ index.php?it=$1 [L,QSA] # Everything else is routed to index.html, so the Vue app will receive the request and the vue-router can show the appropriate page # Redirect index.html to / RewriteRule ^index\.html$ / [L,R=301] # Redirect everything else to the app FallbackResource /index.html
  22. @Moebius @dragan @bernhard Well, Google's crawlers can run JS, they even updated the Chromium version their crawlers run on a couple of weeks back, so it should have no problem with the app. architekturfuehrer.koeln ist also an entirely new domain we set up for the app, so there was nothing in the index to begin with. We'll see how the coverage is progressing going forward. I did built a sitemap to help with indexing the site by the way: https://architekturfuehrer.koeln/sitemap. This one is created server-side, though ...
  23. Thanks for the feedback everyone, I just made some updates to the app! You may have to fully clear the cache to see the changes immediately, as with the service worker the caching is pretty aggressive. @bernhard I have tweaked the functionality of the back button. The app now keeps track of the page history independently of the browser history API and only displays the back button if there are at least to items in the history stack. So you shouldn't see the back button on the first page visit, regardless of which route you're on. @tpr @bernhard For now, I have just disabled scroll wheel zoom for most of the embedded maps; this way, the map won't interfere with the normal page scrolling. The map can still be zoomed with the buttons to the left, and scrool zooming activates after clicking inside the map. Pinch zooming on touch devices should also work as normal. The only exception is the map page (architekturfuehrer.koeln/karte) which can always be zoomed with the scroll wheel; but that page should always fill the entire viewport with nothing to scroll below that, so it shouldn't be an issue there.
  24. @tpr Thanks! I see your point; I recently wrote a little snippet for leaflet to only activate mouse scrolling after clicking anywhere inside the map, maybe that would make sense here too. I'll put in on the to do list!
×
×
  • Create New...