Jump to content

Stefanowitsch

Members
  • Posts

    380
  • Joined

  • Last visited

  • Days Won

    6

Everything posted by Stefanowitsch

  1. @bernhard Thanks for the hint. You can alternatively log out (and stay on the same page) via tracy debugger here. I didn't know this function existed! Just click on Logout (admin) This will be my new solution for this ?
  2. I know this code snippet that @Klenkes showed and was using it in some projects, too. In the past I found the reason for this on css-tricks.com, or here: https://snook.ca/archives/html_and_css/font-size-with-rem html {font-size: 62.5%;}/* set scale for the document */ body {font-size:1.6rem;} /* this makes 1.6rem 16px, 2rem 20px, and so on... rem sizes are easier to read that way */ The background is that if you use the markup above you don't need some weird "px to rem calculations" in the frontend. If you want to have a headline that is like 38px in your layout file (photoshop or XD, or whatever) you always had to calculate the REM value for the font size. For example: 38px would be 2.375rem in your stylesheet. That is hard to keep in mind and by just looking at it you get confused. font-size: 38px; font-size: 2.375rem; With the CSS markup above - 38px would be written as "3.8rem" which is far more straight-forward. font-size: 3.8rem; But you have to keep in mind that the base font size has to be changed to make this work. So the base font size hat is used by RockFrontend (16px I assume) does not work anymore for this kind of workflow. So I too would recommend to make the font base size a variable to make easy adjustments. 16 should be the standard value, though.
  3. I want to show a new website that I made at the beginning of this year using @bernhard's RockPageBuilder module: https://www.kurrat-terrassendaecher.de/ The client: Kurrat Terrassendächer (which translates to "Kurrat Terrace Roofs") is a dealer from germany that is specialized in building custom made terrace roofings, awnings and solar protection. The goal: The customer had a old website that was used as a basis for the new design. The new website should offer a more "catalogue-like" look with lots of information for the customer. Fortunately the client had access to high quality images of the products that really make the whole website shine. The page features three main categories: 1. Terrace Roofs 3. Solar Protection 3. Winter Gardens Each category subpage is made of modular content blocks: The user is able to make custom page layouts using these blocks directly in the frontend. With the RockPageBuilder module it is super fun an super straight forward to work with these content blocks. If you don't know this module yet I highly recommend to check it out! If you like the RepaterMatrix you will love this module. It is far superior IMHO! Inserting a content block looks like this: It is also possible to edit content on-the-fly in the frontend: As with the RepeaterMatrix each content block an also be added end edited in the backend of the page: Here you see the list of content blocks used on the home page. To edit a block, just click on it and you can edit it just like using the RepaterMatrix. Screenshots from the website: Modules used for this page: - RockFrontend (for asset bundling, minifying, etc.) - RockPageBuilder (instead of RepeaterMatrix! For building and editing all content in the frontend) - WireMailSMTP - PageImageSource (for creating webp image variations) - PrivacyWire (for cookie consent banner) - SEO Maestro - Redirects The frontend framework is UIKit
  4. I was in the need to add a "log-out" shortcut to the RockFrontend topbar. The motivation behind this is that I am using @bernhard's RockPageBuilder module and when you are logged in and do some frontend-editing you want to have a quick look on the page without the RockPageBuilder markup (all the edit-icons, etc.) . You just want to check how the the page looks like to the user. Yeah, you coooould open the page on a separate browser but I like this solution: In the topbar.php file in the RockFrontend module folder go to line 58 and append this code: <a href="<?=$config->urls->admin?>login/logout/"> <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M502.6 278.6C515.1 266.1 515.1 245.8 502.6 233.3L374.6 105.3C362.1 92.8 341.8 92.8 329.3 105.3C316.8 117.8 316.8 138.1 329.3 150.6L402.7 224H192C174.3 224 160 238.3 160 256C160 273.7 174.3 288 192 288H402.7L329.3 361.4C316.8 373.9 316.8 394.2 329.3 406.7C341.8 419.2 362.1 419.2 374.6 406.7L502.6 278.7V278.6ZM160 96C177.7 96 192 81.7 192 64C192 46.3 177.7 32 160 32H96C43 32 0 75 0 128V384C0 437 43 480 96 480H160C177.7 480 192 465.7 192 448C192 430.3 177.7 416 160 416H96C78.3 416 64 401.7 64 384V128C64 110.3 78.3 96 96 96H160Z" fill="black"/> </svg> </a> This will render a new "log-out" button in the top bar: I just inserted a random svg code here, so the styling does not 100% fit the look of the other icons. After clicking you will be logged out and redirected to the login-mask. BUT: What I want to achieve was: "Log me out but instead of redirecting me to the login mask, I want to stay on the same page that I logged out from!" 1. We alter the code from above to include a redirect parameter that includes the current page URL: <a href="<?=$config->urls->admin?>login/logout/?redirect=<?=urlencode($page->url)?>"> <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M502.6 278.6C515.1 266.1 515.1 245.8 502.6 233.3L374.6 105.3C362.1 92.8 341.8 92.8 329.3 105.3C316.8 117.8 316.8 138.1 329.3 150.6L402.7 224H192C174.3 224 160 238.3 160 256C160 273.7 174.3 288 192 288H402.7L329.3 361.4C316.8 373.9 316.8 394.2 329.3 406.7C341.8 419.2 362.1 419.2 374.6 406.7L502.6 278.7V278.6ZM160 96C177.7 96 192 81.7 192 64C192 46.3 177.7 32 160 32H96C43 32 0 75 0 128V384C0 437 43 480 96 480H160C177.7 480 192 465.7 192 448C192 430.3 177.7 416 160 416H96C78.3 416 64 401.7 64 384V128C64 110.3 78.3 96 96 96H160Z" fill="black"/> </svg> </a> 2. For the redirect you have to put this hook function inside your ready.php file: $wire->addHookBefore("ProcessLogin::executeLogout", null, "setRedirect"); function setRedirect(HookEvent $event) { $input = wire('input'); // Check if the 'redirect' query parameter is present if ($input->get->redirect) { // Get the redirect URL from the query parameter $redirectURL = $input->get->redirect; // Set the logout URL to the specified redirect URL $event->object->setLogoutURL($redirectURL); } } After doing that the log-out button will log you out BUT keep you staying on the same page.
  5. @Robin S THIS IS A BLAST! I just testet it on a image-heavy site and it works perfectly. Thanks a lot!!! This definitely belongs to the core of PW in a newer version.
  6. I know this behaviour and it bothers me on every project. I always get a 500 time out error when loading loads of images the first time. This happens mostly in my local dev environment, though. The live server seems to be powerful enough to handle it faster without the timeout.
  7. As one of the earliest RockPageBuilder users (I think you can say so) I have to admit that this is my go-to module since day one. If you liked the RepeaterMatrix module for building pages you will absolutely love this one. In my opinion it is far superior and the frontend editing possibilities seem to be the "missing link" that developers & designers were always looking for. I made around 7 websites with this tool (small and medium in scale) and I definitely will showcase some of them as this module is finally released ?
  8. Tutorial Time! So finally I was able to make fetching the Instagram data work. And here's my tutorial how to achieve this. These are my requirements: - Load images from an instagram account and append them into a carousel slider on a website - To reduce loading times and file sizes I want to apply a "scrollspy" behaviour that only fetches data when the carousel is about to enter the viewport - I only load a certain amount of data and once the user is about to reach the end of the carousel, new data is fetched and appended automatically Using the UIKit framework comes in handy here. I am in fact using the UIKit slider component in combination with the UIKit scrollspy component here. Let's start: 1. This piece of code has to be implemented at the very top of your template PHP file (in my case it's the home.php) What this code does is basically: "If the this page is requested through an ajax all then get the media from the instagram account, print it out and then stop processing the rest of the template code." <?php $instagram = $modules->get('InstagramBasicDisplayApi'); if($config->ajax) { echo $instagram->getImages('USERNAME',6); // change USERNAME to the name that is authorized in the InstagramBasicDisplayApi module settings die(); } ?> 2. This is the HTML markup for our slider carousel (using the UIKit slider). Put it into the same template that has the code above. <section class="instagram-feed" uk-scrollspy="cls:uk-animation-slide-bottom"> <div class="uk-container"> <div class="uk-text-center"> <h2>MY INSTAGRAM FEED</h2> </div> <div class="insta-spinner uk-text-center uk-flex uk-flex-column uk-flex-middle"> <div uk-spinner="ratio: 2"></div> <p><b>Loading data...</b></p> </div> <div uk-slider> <div class="uk-position-relative"> <div class="uk-slider-container uk-margin"> <ul class="uk-slider-items uk-child-width-1-2 uk-child-width-1-5@m uk-grid" uk-scrollspy="target: > li > .last-item"> </ul> </div> <div class="uk-visible@s"> <a href="#" class="slider-arrow uk-position-center-left-out uk-position-medium" uk-slider-item="previous"><img src="/site/templates/images/icons/icon-arrow-left.svg" width="30" height="30" alt="Pfeil Links" loading="lazy"></a> <a href="#" class="slider-arrow uk-position-center-right-out uk-position-medium" uk-slider-item="next"><img src="/site/templates/images/icons/icon-arrow-right.svg" width="30" height="30" alt="Pfeil Rechts" loading="lazy"></a> </div> </div> <ul class="uk-slider-nav uk-dotnav uk-flex-center uk-margin"></ul> </div> </div> </section> Note: - I am using a loading spinner right away, this will be removed later on when data is fetched. - We are implementing the scrollspy component for the whole markup block. So when this block enters the viewport an event is fired that allows us to then start fetching data. - We are also implementing the scrollspy component that looks for the <li> with the "last-item" class. This will be our indicator for appending new data later on. - There are currently no <li> elements! That is of of course what we desire. We will load images from instagram and create those <li> elements on the fly. 3. This is the script that will fetch data and append the new carousel items. Put this code below the HTML markup or in a separate JS file: <script> document.addEventListener("DOMContentLoaded", () => { let feed = document.querySelector('.instagram-feed'); let sliderList = document.querySelector('.uk-slider-items'); let spinner = document.querySelector('.insta-spinner'); // listen for the scrollspy 'inview' event and execute this function if the carousel element comes into the viewport feed.addEventListener('inview', () => fetchData()); // create new carousel items and append them to the carousel let appendSlides = function(data) { data = JSON.parse(data); data.forEach((item) => { let image = document.createElement('li'); image.innerHTML = `<a href="${item.link}" target="_blank" rel="noopener" class="uk-display-block uk-height-1-1 uk-position-relative"><img src="${item.src}" alt="${item.alt}"/></a>`; sliderList.appendChild(image); }); // add new 'last-item' class to the second last carousel item so that new data is fetched when this element comes into the viewport const lastItem = sliderList.querySelector('li:nth-last-child(2) > a'); lastItem.classList.add('last-item'); // listen for the scrollspy 'inview' event and execute this function if the last carousel item comes into the viewport if (lastItem) { lastItem.addEventListener('inview', () => fetchData()); } }; // Make an ajax request using the fetch API async function fetchData() { try { const response = await fetch(window.location.href, { method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest' } }); let data = await response.text(); // Once data is received pass this data to our function to generate the carousel items appendSlides(data); // and don't forget to remove the loading spinner! spinner.remove(); } catch (error) { console.error(error); } } }); </script> That's it! If everything works correctly it should look like this:
  9. Added before line 439 this does not log anything (but also no error!): $this->log(json_encode($data)); This logs "false": bd(json_encode($data));
  10. This outputs an Array containing all the data I would expect. No errors at all.
  11. No it does not change anything. This logs "false" (when executed in the header of the template in the if $config->ajax statment. bd($instagram->getMedia('myusername'))
  12. Yes I have a working instance of the InstagramBasicDisplayApi on my Page. The media is fetched from my account and displayed in a carousel: This is the code, as I mentioned it works fine: <ul class="uk-slider-items uk-child-width-1-2 uk-child-width-1-5@m uk-grid"> <?php // Get the 12 most recent items and render them based on type foreach($instagram->getMedia('thumann.media',12) as $item) { switch($item->type) { case 'VIDEO': $media_src = $item->poster; $media_icon = '<i class="fas fa-film"></i>'; break; case 'CAROUSEL_ALBUM': // Just get the first item $media_src = $item->children->first->src; $media_icon = '<i class="far fa-images"></i>'; break; default: $media_src = $item->src; $media_icon = '<i class="far fa-images"></i>'; break; } ?> <li> <a href="<?=$item->link?>" target="_blank" rel="noopener"> <img class="lazyload img-fluid" src="<?=$media_src?>" alt="<?=$item->alt?>"/> <?=$media_icon?> </a> </li> <? } ?> </ul> Right now only 12 items are fetched. I want to go the next step and load the content of my Instagram channel in batches and append them when scrolling through the carousel. So I went to the documentation on the PW modules pages under "Pagination": https://processwire.com/modules/instagram-basic-display-api/ Lazy loading of items can be achieved using getMedia(). The following example shows how to return batches of items using AJAX requests: $instagram = $modules->get('InstagramBasicDisplayApi'); // The pagination cursor is reset if the request is not AJAX, e.g. when the page is loaded. if($config->ajax) { header('Content-Type: application/json'); echo $instagram->getMedia(); // ['json' => true] is inferred by $config->ajax die(); } But when I include a Tracy Debugger bardump on the $instagram->getMedia() function here it returns nothing.
  13. @nbcommunication thanks for your input! It seems the problem lies somewhere else. I am trying to get the most basic output but all I am receiving is an empty data object. On my home.php template I have this code at the very top: <?php $instagram = $modules->get('InstagramBasicDisplayApi'); // The pagination cursor is reset if the request is not AJAX, e.g. when the page is loaded. if($config->ajax) { header('Content-Type: application/json'); echo $instagram->getMedia(); // ['json' => true] is inferred by $config->ajax bd($instagram->getMedia()); die(); } ?> Then inside my template I have this code below which should do nothing else than send an AJAX request to the page itself to execute the code shown above. Also to make tracy debugger log the WireArray that is returned by the $instagram->getMedia() function. <script> (async () => { console.log('try to fetch data'); try { const response = await fetch(window.location.href, { method: 'GET', headers: { 'X-Requested-With': 'XMLHttpRequest' }, responseType: 'json' }); if (response.status === 200) { const data = await response.text(); console.log(data); } else { console.log(response.status); console.log(response.statusText); } } catch (error) { console.error(error); } })(); </script> Wenn viewing the homepage this script is executed but the console.log(data) prints out an empty string. The bd($instagram->getMedia()) instruction in the header also logs nothing.
  14. @nbcommunication For my new project I want to make use of the lazy loading function. So I followed the steps in the documentation. My main question is: where do I have to put this code? $instagram = $modules->get('InstagramBasicDisplayApi'); // The pagination cursor is reset if the request is not AJAX, e.g. when the page is loaded. if($config->ajax) { header('Content-Type: application/json'); echo $instagram->getMedia(); // ['json' => true] is inferred by $config->ajax die(); } echo '<div id=instagram class="uk-grid-small uk-child-width-1-2 uk-child-width-1-3@s uk-child-width-1-4@l" data-uk-grid data-uk-lightbox data-uk-scrollspy="target: > div; cls: uk-animation-slide-bottom-small; delay: 128;"></div>'; It it necessary to enter the user name in the getMedia() function? $instagram->getMedia('my.account'); When viewing the page I receive the following error in the console. I guess that has to do with the placement of the code above. (Index):403 SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON When placing the code at the top of my template (before the <!DOCTYPE is declared) I receive the following error: SyntaxError: Unexpected end of JSON input
  15. @bernhard does this mean that RockFrontend soon will support the compiling of SCSS files? Please say no. After years of using SCSS I switched to LESS just because of this feature ?
  16. It's great to see new recommendations popping up after 4 years ? I was able to solve this via the multi select field from the page tree ever since.
  17. I can reproduce this behaviour. It seems if you copy a text from a website and paste it into the frontend editor all the formatting (this means all HTML tags and elements) are also copied. This works fine on regular body fields that allow HTML tags. For example you can just copy & paste entire HTML tables without creating them in the editor from scratch. But when you then copy this text into a field that is set to not allow html tags it results in this kind of mess in the frontend: In the Backend it looks like this: This is because of the Textformatter settings for this headline field. HTML Tags are just rendered as plain text.
  18. I guess that is the perfect conclusion to this ?
  19. I've implemented an ajax-driven auto complete search on a website and I am wondering if you should use the CSRF check on this one or if this would be a bit "over the top"? More information: https://processwire.com/api/ref/session/c-s-r-f/
  20. I like to re-use most of the fields. For example in most projects i have one body field, one headline field and one (single) image field. With fieldsets I can reuse these fields as often an as much on a single template as I want. As long as the field settings not differ that much there is no use to create a "body2" or "body3" field. BUT if you need to alter the field settings in one case it is possible to override these changes on a fieldset-basis. For example I have this fieldset here with two generic input fields: A single image field and a body copy field. These are generic fields that I use all over the place. If I want to make slight changes to those fields in the context of the fieldset I can edit the fieldset and then click on the name of one of the fields inside it. This will open a dialogue where you can override very basic field settings. It is in most cases not possible to customize a field very much this way. For example you can't change the width and height cropping properties on an image field.
  21. After upgrading the PW Version to the latest master version 3.0.227 I get the message that there are two versions of the TinyMCE installed: This is because before the TinyMCE was officially a part of the PW core I installed it manually. My question is: Is it "safe" to just delete the module folder of the manually installed version that lives in the /site/modules folder? I want to make use of the core version from now on.
  22. Yeah I had the pleasure to be one of the earliest customers who uses this module and already made a few websites with it. Once it's released I will give some insight here in the board how I created this and that with it. It's a really powerful tool!
  23. Hello! I know exactly what you are meaning. Well you have at least three possibilites: 1. Use the Fieldset Page in ProcessWire. With this you can combine fields into one Fieldset. I don't use this Fielset anmyore but if I remember correct you have to install it from the "Core Modules Tab" (under: /processwire/module/). You create a new field (name it for example fieldset_introduction) and then you add fields to it that you want to make use of (cool thing: you can reuse this fields in all other fieldset page fields and don't have to create a "body copy field" for each and everyone of them separetely!) And here is what it looks like on a page template. The fields shown above are aligned in a fieldset named "Introduction". You can add several Fieldsets this way and just put them in any order on your page template. 2. Use the Repeater Matrix Pro Field. This ones commercial but it makes handling and editing Sections on your pages much easier and more flexible! Cool thing is that you can define your content blocks in the field settngs and add the needed fields right there. Then on your page template you can select the type of content block (or name it section): After that it's super easy and comfortable to edit these sections and also change their order! 3. Use a third party page builder module like the RockPageBuilder from @bernhard. This module even beats the popular Repeater Matrix module in my opinion as it accelerates the creation and editing (and managing!) of content massively! Even better, it kind of combines the Repeater Matrix block-buidling functions with frontend editing which is a massive leap forward in terms of workflow optimization and customer happiness (cause it is also very easy to use for non-technically experienced users!).
  24. Truly an inspiring project! The technical solutions here are fascinating. I haven't touched a project with a custom backend for users (yet!) and I always wondered how to approach it.
  25. I now changed Line 322 in InputfieldImageMarker.module from $addInfoPages = $modules->get('InputfieldAsmSelect'); foreach($opts as $opt) $addInfoPages->addOption($opt->id, $opt->title); to: $addInfoPages = $modules->get('InputfieldAsmSelect'); foreach($opts as $opt) $addInfoPages->addOption($opt->id, $opt->headline); Now the headline is shown as title in the select field: But the bummer is: It turns out that this field does not work properly inside a repeater matrix. The table with the marker coordinates never shows up after saving ?
×
×
  • Create New...