Jump to content

schwarzdesign

Members
  • Posts

    123
  • Joined

  • Last visited

  • Days Won

    4

Everything posted by schwarzdesign

  1. @bernhard It's a little trick you can do with Google Chrome: In the responsive view mode, select one of the iPhone models in the responsive mode settings bar. In the flyout menu on the upper right, there's an option "show device frame". Only works with some of the devices, I've used "iPhone 6/7/8 Plus". If you take a screenshot (via the same flyout menu) while in responsive design mode with the show device frame option turned on, the device frame will be included in the screenshot.
  2. @netcarver Thanks! @bernhard Thank you for the QA ? Regarding the "back" button, I tried to make it more intelligent, but I got stuck on the history API not having a reliable way to check the last items. I thought about turning it off only on the homepage, but I wanted to have a way to go back to the last page if you go e.g. from an object detail page to the homepage and want back to the object. I'd have to track the last history states manually, since the Vue Router just wraps around the history API and doesn't keep a memory of itself. I'll put that back on the to do list! As for the share button, we only have that on the object pages in the subnavigation; it would take up to much space too have it on the bottom all the time. Maybe we can put it at the end of every page, or in the navigation. Though it's only supported in Chrome on Android and Safari at the moment, and most browsers have their own native sharing button, so it wasn't the most important feature to us. @pwired Thank you so much! ?
  3. We recently rebuilt the Architekturführer Köln (architectural guide Cologne) as a mobile-first JavaScript web app, powered by VueJS in the frontend and ProcessWire in the backend. Concept, design and implementation by schwarzdesign! The Architekturführer Köln is a guidebook and now a web application about architectural highlights in Cologne, Germany. It contains detailled information about around 100 objects (architectural landmarks) in Cologne. The web app offers multiple ways to search through all available objects, including: An interactive live map A list of object near the user's location Filtering based on architect, district and category Favourites saved by the user The frontend is written entirely in JavaScript, with the data coming from a ProcessWire-powered API-first backend. Frontend The app is built with the Vue framework and compiled with Webpack 4. As a learning exercise and for greater customizability we opted to not use Vue CLI, and instead wrote our own Webpack config with individually defined dependencies. The site is a SPA (Single Page Application), which means all internal links are intercepted by the Vue app and the corresponding routes (pages) are generated by the framework directly in the browser, using data retrieved from the API. It's also a PWA (Progressive Web App), the main feature of which is that you can install it to your home screen on your phone and launch it from there like a regular app. It also includes a service worker which catches requests to the API and returns cached responses when the network is not available. The Architekturführer is supposed to be taken with you on a walk through the city, and will keep working even if you are completely offline. Notable mentions from the tech stack: Vue Vue Router for the SPA functionality VueX for state management and storage / caching of the data returned through the API Leaflet (with Mapbox tiles) for the interactive maps Webpack 4 for compilation of the app into a single distributable Babel for transpilation of ES6+ SASS & PostCSS with Autoprefixer as a convenience for SASS in SFCs Google Workbox to generate the service worker instead of writing lots of boilerplate code Bootstrap 4 is barely used here, but we still included it's reboot and grid system Backend The ProcessWire backend is API-only, there are no server-side rendered templates, which means the only PHP template is the one used for the API. For this API, we used a single content type (template) with a couple of pre-defined endpoints (url segments); most importantly we built entdpoints to get a list of all objects (either including the full data, or only the data necessary to show teaser tiles), as well as individual objects and taxonomies. The API template which acts as a controller contains all the necessary switches and selectors to serve the correct response in <100 lines of code. Since we wanted some flexibility regarding the format in which different fields were transmitted over the api, we wrote a function to extract arbitrary page fields from ProcessWire pages and return them as serializable standard objects. There's also a function that takes a Pageimage object, creates multiple variants in different sizes and returns an object containing their base path and an array of variants (identified by their basename and width). We use that one to generate responsive images in the frontend. Check out the code for both functions in this gist. We used native ProcessWire data wherever possible, so as to not duplicate that work in the frontend app. For example: Page names from the backend translate to URLs in the frontend in the form of route parameters for the Vue Router Page IDs from ProcessWire are included in the API responses, we use those to identify objects across the app, for example to store the user's favourites, and as render keys for object lists Taxonomies have their own API endpoints, and objects contain their taxonomies only as IDs (in the same way ProcessWire uses Page References) Finally, the raw JSON data is cached using the cache API and this handy trick by @LostKobrakai to store raw JSON strings over the cache API. Screenshots
  4. We recently finished the relaunch auf camac.de, a client from the software industry, focused on controlling and business integration solutions. Concept, design and implementation by schwarzdesign. The site is focussed on longer content pages with text and image combination, with technical information for potential customers. It also offers multiple options for Call-to-Action elements to maximize conversions. Finally, the site is heavily optimized with caching and minification at every stop to be instantly readable even on slow connections. Features No hard-coded sections in any template: Almost all content types use the same repeater matrix field with several section types. This way, the entire site and all parts of the layout can be moved around and combined in any way imaginable. Custom Call-to-Action options: Two different contact forms, custom external links or a general CTA text. Automatically generated page navigation and human-readable anchors (as seen here) Every page fully loaded and interactive in ~1 second, even on poor mobile connections Automatically generated SEO meta tags, with overwrite fields available on every page Modules used ProFields FormBuilder Tracy Debugger Sitemap Duplicator ALIF - Admin Links In Frontend WireMailSMTP Content and conversions Every visitor is a potential customer - this is why we made sure there are ample opportunities to generate leads. At the end of every page, there are multiple options for the CTA section: A contact form with a customizable message (built with FormBuilder) A download form that allows visitors to download PDFs (e.g. the full article) in exchange for their name and E-Mail address. Other text + image combinations with custom links or buttons. The download form was custom coded, as it allows the editor to upload a file specific to the current page and make it available behind a small form. After successfully submitting the form, an e-mail is sent to the site owner, and the file is directly streamed to the client as a download. Technical insights This is one of the first pages where I used Twig for templating, and it's been a great developing experience. With Twig, you get content escaping and much better seperation between logic and view / display. I also spent some time working out a solid structure for the twig templates, with useful defaults, reusable blocks for page and section template (you can read more about the approach in my recent tutorial on integrating Twig with ProcessWire, part 1 and part 2). I also started using Parcel as a lightweight alternative for Webpack, when all I need is to compile a couple of small scripts (for the navigation, the lightbox, a dismissable cookie notice et c). What's great about parcel is that you get bundling of your own code and external libraries out of the box, as well as ES6+ transpilation and minification for production usage. Still, it required no configuration but a couple of command line options. This way, you get one bundled, minified JavaScript file, the same way we produce a minified CSS file with SCSS, but without the additional overhead of configuring Webpack. Screenshots
  5. @szabesz Thanks, I have opened an issue: https://github.com/processwire/processwire-issues/issues/869
  6. @dragan Maybe it has to do with permissions, see below. Ok so I have dug deep and determined why it isn't working. The $page->trashable() method is added by PagePermissions.module as a hook. The method PagePermissions::trashable first calls $this->deleteable(), which returns false for the current page (so far expected and documented behaviour). However, the $this->wire('permissions')->has('page-edit-trash-created') check in this line fails, since the page-edit-trash-created permission doesn't exist in a default install. Since it directly calls the permissions fuel whose has method doesn't check for superuser, and the check fails if the permission doesn't exist. If I either create the permission through the backend or replace the above check with $this->user->hasPermission('page-edit-trash-created'), it works as expected. @ryan Is this a bug or intended behaviour? Seems pretty counterintuitive that the superuser fails a permissions check on a default install (basic profile). Maybe we can replace the line in PagePermissions.module above: // current if(!$event->return && $this->wire('permissions')->has('page-edit-trash-created') && $page->editable()) { // change to if(!$event->return && $this->wire('user')->hasPermission('page-edit-trash-created') && $page->editable()) { I'd be happy to open a Github issue or pull request, but I'm not sure if this is actually the intended behaviour, so it would be nice to get a statement on this. Thanks!
  7. @dragan This happens even when I'm logged in as the superuser, so it should bypass any permission checks. The permissions are definitely right, it only occurs for the current page. For example, I have a news listing being generated by a loop, and $news->trashable correctly returns true inside the loop. But when I view an individual news page, $page->trashable returns false for that very news ... I'm using the latest Dev-Branch (3.0.130) by the way, maybe this is a bug that was introduced recently?
  8. I'm having a problem with the $page API. I'm working on some custom admin actions / links for the frontend, and I want to check permissions before I display some links. Specifically, I'm trying to display a trash link if the page is trashable by the current user. I've tried both $page->trashable and $page->deleteable. Unfortunately, both of those seem to always return false when the page I'm calling this on is the page being viewed (i.e. I'm using the literal $page variable in my template). Is this by design? Some sort of prevention against trashing the page that is currently being viewed? I can't seem to find the method in the source code, so I'm not sure how to look it up. The documentation only says: Nothing about a special case for the current page ... Anyway, how can I reliably if a page is trashable? Do I need to take a detour through the template settings? Thanks!
  9. Thanks, that's what I assumed. Though it would be cool if ProcessWire did this under the hood, since having a start/offset but no limit is a realistic scenario / requirement in this case for example. Thanks, that's exactly what I was looking for! For some reason I thought I needed two different queries since I need different fields from the two sections ^^ I need to look into the WireArray methods again, some really useful stuff there ... ?
  10. I'm working on a news feed that will show the most recent news in a full teaser grid, and all older news as a simpler archive-type list view. My selectors so far: $news_full = $page->children("template=news, limit={$page->feed_count_full}"); $news_archive = $page->children("template=news, start={$page->feed_count_full}, limit=9999"); The $page->feed_count_full field controls how many items to show in the teaser grid (I've confirmed it contains the correct value, and the $news_full selector works as intended). This works, but I don't like the limit in the second selector. Unfortunately, if I leave it out (i.e. I only specify a start, not a limit), the start is ignored and I get all news instead. Not a big problem as we will never have more than 9999 news, but it still bothers me, as semantically speaking I don't want to set a limit in this case. Is this the intended behaviour of start/limit selectors? Is there a cleaner way to specify an offset (start selector) without a limit? ProcessWire Version 3.0.123 Thanks!
  11. @ryan Hey Ryan, I have a minor problem with the site showcase. A while ago I submitted Engfer Consulting (https://processwire.com/sites/list/engfer-consulting/) to the showcase. If you look at the description, the last character is a question mark, that was supposed to be a unicode heart (♥, see https://unicode.org/cldr/utility/character.jsp?a=2665). I don't see a way to edit a submitted site, could you change the question mark to the heart, pretty please? ? If unicode is not possible there, something like "<3" would work as well ^^ As a future feature, it would be cool to have unicode support in there, or get a warning in those cases, though that's really a minor thing ? Edit: Also, right now after submitting a site I'm redirected to https://processwire.com/sites/thanks/, which gives a 404 page. Can you confirm if the submission feature is working at the moment? I just submitted schwarzdesign.de and wanted to submit two more sites, would like to know if the submissions are not vanishing into nirvana right now ^^
  12. Just a few days before christmas we relaunched our own agency website at schwarzdesign.de using ProcessWire! The old Drupal-based site was getting a bit outdated, we wanted to rebuilt it with fewer, more focused content pages, clean & minimalistic design and better performance for mobile visitors. Note that the site copy is only available in German. After a couple of successful ProcessWire projects such as Engfer Consulting and Joachim Kobuss, we decided to use it for our own site as well, mostly for it's small footprint and developer-focused API. We also have a dedicated ProcessWire page on our new site, check it out here! Modules used ProFields Automatically Link Page Titles TinyPNG Image Compression Color Tracy Debugger Sitemap Duplicator ALIF - Admin Links In Frontend A focus on content We went for a minimalistic approach with a limited set of design elements to highlight our content, which is mostly copy about our services and approach to web design. The two-column layout is built with a single Repeater Matrix section with fields for left and right copy, left and right images and a couple of display options for column width and alignment. Other Repeater Matrix types include a full-width image with some additional links (used on the homepage), teaser sections for current news and projects as well as a list of services. We used dedicated page types (templates) for services, projects and news. This allowed us to use those as taxonomies for our reference projects, and create cross-references to those pages in one go. For this purpose, there's also a dedicated CMS template and pages for all Content-Management-Systems we use, allowing us to categorize our projects and news in regards to the CMS used and show appropriate pages in the recommended content sections. Performance Loading times become ever more important with the rising amount of mobile traffic, both for the bounce rate and for SEO purposes. For client-side performance, we got rid of all external libraries (CSS and JavaScript). All CSS is written in SASS; we use a very stripped down version of Bootstrap 4 that includes only the grid system, the utility classes and a select few of the components. We also avoided the components requiring jQuery, allowing us to get rid of jQuery entirely. The few interactive parts of the site (flyout menus, the theme switch, adding the blur filter for the background image on scroll) are written in simple vanilla JavaScript. Server-side, we use the ProcessWire page cache to minimize server response times, as well as Cache-Control headers and GZIP compression to make sure all assets are as lightweight as possible and cached client-side. One problem we had were the large header images requiring transparency. We ended up combining a couple of approaches to minimize image loading times: SVG assets where possible (for example, all the service pages (Kompetenzen) use SVG for the header images. Responsive images to serve the smallest possible image for each device. PNG compression using the TinyPNG service and the TinyPNG module. This had possibly the highest impact, sometimes reducing the image size by about 80%! Design highlights One of the adornments of the site are the large, variable sized header images. Position and size can be controlled through the backend: We built display settings for size in viewport width or height, vertical and horizontal alignment as well as optional offsets. The header images are fixed to the viewport, so they always stay in the background. The blurred background uses CSS filters, with a fallback to a simple opacity reduction for older browsers. As a gimmick, there is a light and a dark theme for the website, the latter being the default. You can switch between themes using the sun icon in the menu. On the technical sides, both themes have their own stylesheets, compiled using the same SASS source files with different variables. Only the active theme's CSS is directly loaded on the site, the other is included as a preloaded asset. The active theme is saved in the session, so it stays active for the duration of the browser session. For our reference projects (Referenzen), we replaced the background color with the primary color of the project (each project has a color field for that purpose). We determine the luminance of the primary color programmatically to find if the color has higher contrast to black or white, and use the light or dark theme accordingly. Screenshots
  13. Thanks ? Yes we built multiple sites for them, all based on the same design with theme/color variations for individual sites. E.g. Osho UTA, UTA Akademie and Osho's Place. Those are built with Drupal though. Hi Bernhard, I agree that a module would be the more ProcessWire-friendly way, I mostly wrote it this way to save time. Since it is client work, we have to budget our development time; given that I haven't really started digging into module development yet, I didn't want the additional overhead. Since the site only has one template, I didn't need to duplicate any code, so I didn't think the benefits of building a module were worth the additional development time in this case. I thought about extending the Page class, but I went with composition over inheritance in this case to avoid unforseen side effects. Though it probably would've been cleaner to use inheritance. I'm not sure this class is general enough to be used in different sites at the moment, but if I have some free time at work or need it for another client site, I'll try to turn this into a configurable module!
  14. We have relaunched the old website of design business expert Joachim Kobuss. The old site desperataly needed a facelift and a streamlined CMS that allowed our client to fill over 130 pages of content in a span of two weeks. We at schwarzdesign used ProcessWire + ProFields for the Backend and built a Bootstrap 4.1-based frontend. Features / Services provided Responsive design featuring a desktop layout with two fixed navigation areas and a fixed sidebar that form a frame around the content. Carefully set typography and spacing for a smooth reading experience for long texts on all devices. A hierarchical page tree with three levels of nested pages, all using the same simple set of fields. Password protection for individual pages and an optional password request form. Privacy by design: There's no tracking, no Google Map, no Facebook Like Button or anything like that on the page. Because we don't set any cookies or include third-party code, we don't need a cookie notice ? Modules used Profields (especially Repeater Matrix, Textareas and Functional Fields) Form Builder Tracy Debugger Wire Mail SMTP Duplicator Admin Links in Frontend Sitemap Optimizing for content The most distinguishing feature is the large amount of long texts our client has written. So it was important to provide a good editing experience in the frontend and have a content centered design that made long text sections easy to follow. We didn't want to give our client just a large CK Editor body text, since in our experience, those WYSIWYG editors aren't actually that easy to use for non-technical folk. It also reduces the amount of control we as designers and CSS-authors have over the presentation of the content. So instead, we built a flexible section system using the Repeater Matrix field of the ProFields module. There are three sections: text content, downloads and single image. Each section has a headline field and a radio selection field to switch between two levels of headline hierarchy (corresponding to h2 and h3). For the text section, there's a CK Editor textarea stripped to the bare minimum (strong, italic, lists, blockquotes and links), with no confusing styles or format dropdown. The image section features an additional option for full- or half-width images and an option to display the image description as a caption. And of course, the download section has a multivalue file field that is displayed as download buttons in the frontend. This simple sectioning system allowed us to build the entire site (except for the homepage) using just one template fieldset. There are three templates: Basic Page, Topic and Article. Basic pages are for generic pages like imprint and data policy. Topics may only be children of the homepage, while articles may be children of a topic or of another article, so the pages can be nested indefinitely. This provides the hierarchical structure used for the page layout. Topic and article inherit the fieldset of the basic page, so it's just one template under the hood. There's also smart Open Graph tag generation; title, description and preview image are automatically generated based on the first suitable sections on each page and can be manually overridden using the SEO tab available on all pages. Laying out the page Our design has three fixed areas: The top navigation, the left side navigation and the right sidebar, which shows some general contact info and a random image. The top navigation always shows the top-level topic pages, the sidebar navigation contains all articles in the current topic, displayed in a hierarchical structure. This was technically challenging, as the many fixed layout areas broke the normal pageflow and didn't work well for smaller devices. I solved this using four seperate Bootstrap containers, three of which are fixed. Only the container with the main page content flows normally. The left sidebar only fills the three leftmost columns of its container, the right sidebar the three rightmost columns, and the main content takes up the six center columns of it's container. All three have an offset to the top to make space for the fixed header navigation. This way, all the seperate layout areas can never overlap. The CSS that applies position: fixed to the containers is wrapped in a media query with a min-width, so on tablets and mobile devices, the areas simply stack. Check out the source code to see how those containers are laid out! The logo is text by the way, not an image. It's positioned using CSS3 transforms. Password protection Our client wanted to password protect some of his pages with varying passwords, so he can give different people access to different sets of pages. Also, he wanted to have a password request form, where people can submit their name and contact information to request access to a specific page. In the backend, we added a password field that activates the password protection for that page. There's also a checkbox to display the password request form. The form is built using the Form Builder module (and a simple hook to add the current page title to the outgoing mail). However, for the password protection, we built a custom solution. While there is a readymade module available, we didn't want something based on user accounts. A visitor should be able to simply input the password on a protected page and start reading, without bothering with registering an account. The authentification is stored in the session, so after closing the browser you have to input the password again, but that seemed to be a worthy tradeoff. Anyway, when visiting a password protected page, the template checks if the user is already authentificated to read this page; if so, it displays the page content, otherwise it displays the password input form and the password request form if it was also activated in the CMS. A nice-to-have feature that we built in addition is that if multiple pages use the same password and you input it on one of them, you are automatically authentificated to view all of them during the current session. A caveat of this method is that since the page content depends on the current session, we couldn't use the inbuilt page cache at all. So instead we used the cache API to cache rendered section markup and the navigation menues, which still results in a blazingly fast site. For the techies amongst you, here's the PasswordProtectedPage class we wrote for this functionality: <?php namespace schwarzdesign; use Processwire\Wire; use Processwire\Page; use Processwire\PageArray; use Processwire\User; class PasswordProtectedPage extends Wire { const PASSWORD_FIELD = 'page_password'; const PASSWORD_INPUT_NAME = 'page_password'; const STORAGE_KEY = 'authentificatedPages'; const STATUS_FREE_ACCESS = 'free_access'; const STATUS_AUTHENTIFICATED = 'authentificated'; const STATUS_NOT_AUTHENTIFICATED = 'not_authentificated_yet'; const STATUS_WRONG_PASSWORD = 'wrong_password'; /** * The status of the authentification. */ public $status = self::STATUS_NOT_AUTHENTIFICATED; private $page; private $authentificated_pages = []; public function __construct(Page $page) { $this->page = $page; $this->authentificated_pages = $this->getAuthentificatedPagesFromStorage(); } public function handleRequest() { // pages with no password set if (empty($this->page->get(self::PASSWORD_FIELD))) { return $this->status = self::STATUS_FREE_ACCESS; } // pages that the user is already authentificated to use if (in_array($this->page->id, $this->authentificated_pages)) { return $this->status = self::STATUS_AUTHENTIFICATED; } // if the user set the input, check the password if ($password = $this->getInputPassword()) { if ($this->passwordIsCorrect($password)) { $this->authentificatePagesByPassword($password); $this->storeAuthentificatedPages(); return $this->status = self::STATUS_AUTHENTIFICATED; } else { return $this->status = self::STATUS_WRONG_PASSWORD; } } else { return $this->status = self::STATUS_NOT_AUTHENTIFICATED; } } public function userCanAccess() { return in_array($this->status, [self::STATUS_AUTHENTIFICATED, self::STATUS_FREE_ACCESS]); } public function getInputPassword() { $password = $this->wire('input')->post(self::PASSWORD_INPUT_NAME); if (empty($password)) { return null; } return $password; } public function authentificatePagesByPassword(string $password) { $pages = $this->wire('pages')->find(self::PASSWORD_FIELD . '=' . $password); $ids = array_map(array($this, 'extractIdFromPage'), iterator_to_array($pages)); $this->addAuthentificatedPages($ids); } /** * Private helper Functions. */ private function passwordIsCorrect(string $password) { return (string) $this->page->get(self::PASSWORD_FIELD) === $password; } private function extractIdFromPage(Page $page) { return $page->id; } private function addAuthentificatedPages(array $page_ids) { $this->authentificated_pages = array_merge($this->authentificated_pages, $page_ids); } private function getAuthentificatedPagesFromStorage() { $ids = $this->wire('session')->get(self::STORAGE_KEY); return $ids ? $ids : []; } private function storeAuthentificatedPages() { $this->wire('session')->set(self::STORAGE_KEY, $this->authentificated_pages); } } This is used in the template like this: <?php namespace Processwire; use schwarzdesign\PasswordProtectedPage; $protectedPage = new PasswordProtectedPage($page); $protectedPage->handleRequest(); if (!$protectedPage->userCanAccess()) { if ($user->hasPermission('display-page-password')) { /* Display the password in plaintext for editors & admins */ } /* Display the password input form */ if ($page->show_password_request_form) { /* Display the password request form */ } } else { /* Display the page content */ } Screenshots
  15. Thanks ? Yeah we are really happy with the speed, despite using using 'only' the normal page cache ... I was planning to utilize caching for some of the overview pages (those pull in data from several other pages), but it wasn't even necessary in the end, definitely a pro for ProcessWire.
  16. We recently finished a relaunch of the website of the consulting firm Engfer Consulting: engfer-consulting.de. Built with ProcessWire 3 and Bootstrap 4. Features Bilingual website (UPDATE: The English page is now available! the English page is inactive at the moment, as the content is not ready yet) A strong, simple data structure for job offers and news, allowing for a custom search page and fulltext search Sectioned content-blocks built with a Repeater Matrix (ProFields) Reusable components available as page reference fields Automatic generation of open graph-tags, with manual overrides available on each page Contact forms with a custom recipient depending on the current page Modules used: ProFields ProForms Sitemap Cookie Management Banner Duplicator Tracy Debugger Wire Mail SMTP Admin Links in FrontEnd Technical insights Most of the content is built using a Repeater Matrix field. Each section has an optional heading and a select-field for background colors. Some of the sections available include: Text blocks (a repeater-field for multi-column text) Feeds / listing of the latest job offers or news (with a field to control the number of entries shown) Displaying a team member or quote (selected through a page reference field) Downloadable files (a multivalue field for file uploads) Those sections can be combined and stringed together arbitrarily, so any number of page templates showing different contents can be built. The job offers template, in contrast, features fixed fields for taxonomy assignment, description, contact person, preview image and so on, making the job offer pages uniform and easily accesible. The (technically) most interesting part of the site is the job overview page, which contains custom filters that are automatically generated from the available taxonomy terms. The stylesheets are written in SASS, built with Bootstrap 4. Only the base and grid SASS files of Bootstrap are included, along with the Bootstrap components that we ended up using. Using Bootstrap 4 as a framework in this way makes development & styling blazingly fast, once all the utility classes are commited to muscle memory ... > Our Agency. Screenshots
  17. @jmartsch Thanks for the suggestion, unfortunately that didn't help ...
  18. Quick question, the automatic backups through LazyCron aren't working for me. Manual backups are working fine (both locally and with remote FTP backup), but the automatic backups won't trigger at all. Here are my settings: Backup mode: LazyCron - Triggered by a pageview (installed) Event trigger: 1 hour Remove backups older than: 1 year I have waited for several hours, the site had traffic during that time, and I also visited some pages as a logged-in administrator and as an anonymous visitor. But no backups occured, there are also no log entries at all, so it doesn't look like it just bugged out. Do I need to configure anything else? LazyCron is installed, it doesn't have any module options though. Or do I need to manually call the LazyCron hooks in ready.php or something like that? Not sure what I am missing. Thanks!
  19. Ok, I couldn't figure out a way to do this with only selectors, so I built a custom solution. In case anyone has the same problem, here's my approach: I created a hidden text field "offer_taxonomy_cache" and hooked a function after the saveReady hook to automatically populate this field with a space-seperated list of the values of the selected options for the fields that I want to search, in both languages. This is somewhat crude, but has the advantage that it's even more performant than using the select option fields themselves, as this way only one table has to be searched for matches. It's basically the same idea as the FieldType Cache core module, but this one didn't work for me, since it only saved the ID's of the selected options, leaving me with the same problem. Here's my code: // /site/ready.php /** * Write taxonomy values to the cache field. */ $pages->addHookAfter('saveReady', function(HookEvent $event){ // page to be processed $page = $event->arguments(0); // only hook onto job offers if ($page->template->name !== 'job_offer') { return; } $page->of(false); $user = wire('user'); // all the fields to be included in the text cache $fields_to_cache = ['offer_area', 'offer_sector', 'offer_region', 'offer_rank', 'offer_type']; $taxonomy_cache_text = ''; // loop over all fields foreach ($fields_to_cache as $field) { $fields = $page->get($field); // save the current language for later $current_language = $user->language; foreach (wire('languages') as $language) { // set the user language and loop over all selected options, then // add the title in the current language to the taxonomy cache text $user->language = $language; foreach ($page->get($field) as $option) { $taxonomy_cache_text .= ' ' . $option->getTitle(); } } // reset the language $user->language = $current_language; } // trim the leading space $taxonomy_cache_text = trim($taxonomy_cache_text); // write the value to the cache field of the page $page->offer_taxonomy_cache = $taxonomy_cache_text; // write a log message wire('log')->message("Wrote taxonomy cache for page {$page->title} ({$page->id}): \"{$taxonomy_cache_text}\""); }); One problem with this is the triple nested loop, so this might become quite slow for sites with many languages, select option fields and options per field. But in this case using a real page for the select options would be preferable anyway, as suggested. Thanks @dragan for your help!
  20. @dragan Thanks for the reply! I do have some select-options on the filter page, but I need those fields to be searchable through fulltext search unfortunately. I have used the value|title approach, but since I need to support fulltext search, searching the values instead of the titles is not an option. I'll probably use pages from the start the next time, but for now I have to work with what I got. Regarding @heldercervantes's comment, I actually found a solution to that. Turns out that while the magic getter for $field->title and $field->value always return the default values, the proper methods $field->getTitle() and $field->getValue() return the correct values in the current user language. I did post a reply, it never got accepted though .. Still, I'm not sure how to get this fulltext search to work with the current setup.
  21. I'm looking for a way to search multilanguage titles in a couple of select option fields. I have looked through some threads here but couldn't find a solution that works for my specific problem. Any help is very much appreciated! My setup: I have a multilanguage site with two languages, English and German. The default language is German. I'm building a search form for the template "job_offer" that has a couple of category fields. Those fields are built with the Select Options FieldType Module. They are categories for the region, industry, department et c. for the job offer. They have titles defined in German and English. For example, for the field "offer_region" there are the options "Austria", "Switzerland" and "Germany" that have the German country names as their German option titles. The search field is supposed to search for text matches in the job description and category field titles. This is the selector is have so far: $pages->find("template=job_offer, title|offer_body|offer_region|offer_rank%={$input->whitelist('search')}"); While the selector works for the title and offer_body fields, it only matches the German Select Option titles. For example, there's a job offer with the area set to "Austria" ("Österreich" in German). When I search for "Österreich", it shows up as expected. However, when I search for "Austria", I get know results, regardless of whether the current user language is English or German (note that the default language is German on this site). Any ideas how to get it to work? Ideally, I would like to be able to filter through the Select Option titles in every language, i.e. I get the same results when I search for "Austria" or "Österreich" regardless of the current user language. However, for the moment I would be happy enough with a solution that will yield a result both when I search for "Austria" (while the user language is English) and when I search for "Österreich" (while the user language is German). Thanks in advance!
  22. @Soma It's a multilanguage textarea field; the field is one field in a Repeater Matrix type. There are multiple Repeater Matrix items of that type on the page, so there are multiple instances of the field on the same page (this is why it's running in inline mode).
  23. @dragan I'm not sure it's related, as the Github issue mentions the allowedExtraContent settings string to be missing from the source code. In my case it appears there correctly, only the id-attributes are still being filtered. @Robin S Is the id still there after you have saved the page? For me, it only disappears after I've saved the page. Come to think of it, doesn't CK Editor apply filtering after you close the source editor? In that case, maybe it's not an issue with CK Editor at all, but some other filtering applied by Processwire .. the id only disappears after I save the page.
  24. I need to allow the id-attribute for the <a>-Element, but I can't seem to get it to work. I have tried setting the extraAllowedContent in the backend: a[!href,id]; I have also tried using the line without the semicolon, no effect either. I have tried setting the extraAllowedContent in the /site/modules/InputfieldCKEditor/config.js file instead as well: CKEDITOR.editorConfig = function( config ) { // Define changes to default configuration here. For example: // config.uiColor = '#AADC6E'; config.format_tags = 'p;h3;h4'; config.extraAllowedContent = 'a[!href,id];'; }; But that didn't work either. I have verified that the javascript-settings on the edit page include that line: "InputfieldCKEditor_body_block_text": { "baseHref": "/", "contentsCss": "/wire/modules/Inputfield/InputfieldCKEditor/contents.css", "extraAllowedContent": "a[!href,id];", "extraPlugins": "pwlink,sourcedialog", "removePlugins": "image,magicline", "toolbar": [ [ "Format", "Styles", "-", "Bold", "Italic" ], [ "NumberedList", "BulletedList", "Blockquote" ], [ "JustifyLeft", "JustifyCenter", "JustifyRight" ], [ "PWLink", "Unlink", "-", "HorizontalRule" ], [ "Sourcedialog" ] ], "format_tags": "p;h3;h4", "language": "en", "entities": false, "uploadUrl": "", "pwUploadField": "", "pwAssetPageID": 1167, "height": "20em", "stylesSet": "engfer-styles:/site/modules/InputfieldCKEditor/mystyles.js?nc=1529934076", "customConfig": "/site/modules/InputfieldCKEditor/config.js?nc=1530805498" } But every time I save, the id i add to a link in the source panel disappears. No idea how to debug this or what else I can try. Here are the rest of my settings for that field: Editor Mode is Inline (I tried switching to regular mode, doesn't work either). Use ACF is On Use HTML Purifier is On Any help is very much appreciated.
×
×
  • Create New...