Jump to content

Stefanowitsch

Members
  • Posts

    293
  • Joined

  • Last visited

  • Days Won

    5

Everything posted by Stefanowitsch

  1. Yes absolutely. I have this kind of monstrosities in my markup: <?= IntlDateFormatter::formatObject( new DateTime(date("d.m.Y", $event->date_event)), "EEEE, dd.MM.yy", 'de_DE' ); ?> Which works perfect but is a bit hard to read and to maintain (in fact I never want to touch this code again...). If you are working with a predefined date format all over your site it really makes sense to preconfig that.
  2. I agree, you have to use the IntDateFormatter class, like @Jan Romero mentioned. Have a look at this thread, I made a few examples how to use the IntDateFormatter as one-liner, etc.
  3. This is great. I am just about to finish a project that deals with events. I will give it a try.
  4. Oh I forgot to mention you for giving me help on this one 🙂 For all that that were not involved in the discussion here and to clear things up: The Google Maps API is in fact not for free. Depending how much API requests (and what kind of API requests) you send, you will have to pay for it. Google offers everyone a monthly discount of 200$. This discount should fit for most of the requests of a small website, so you never have to pay for anything as long as you are below this 200 $ Mark. There is also a nice calculator here: https://mapsplatform.google.com/intl/de/pricing/ If you just receive data over the place details API (which is the API the module uses) you have about 12.000 requests "for free". So I came up with a save function for the request. You can just fetch it once - save it - and don't have to use the API on each request form there on. But that would violate the Google Maps terms. Sadly I had to remove this save function from the module 😞
  5. Update: I finally released the module. Feel free to test it and give me some feedback: I scrapped the idea of saving the review data for any purpose. I contacted the google support with this question and they confirmed that it would violate their terms to save this data in any way.
  6. I am proud to announce my very first module: GooglePlaceDetails. I was in the need to include some google place reviews for a clients website. It turned out that no such review widget was available for ProcessWire. So I made my own solution which i want to share with you. Google Place Details for ProcessWire Modules Directory: https://processwire.com/modules/google-place-details/ Github: https://github.com/StefanThumann/GooglePlaceDetails What it does Google Place Details offers the possibility to send requests to the Google Maps API to receive information about a certain place. A typical use case would be to display the reviews of a place on your website. But you can receive any other information that the API offers. Before you start You need three things: A Google API Key The Place ID A project with a billing account activated You can set up all of those by using Googles quick start widget here: https://developers.google.com/maps/third-party-platforms/quick-start-widget-users How to install Copy this directory to /site/modules In your admin, go to Modules > Refresh, then Modules > New Click the "Install" button next to the Google Place Details module Fill out the API Key and Place ID fields in the module settings and you are ready to go. Module settings and field descriptions API Key This field is required and must contain your generated Google API key. Place ID This field is required. You can put the ID of any place into this field. Fields to include in request Specify a comma-separated list of place data types to return. Leave empty to load all default fields. For an overview of the available fields see: https://developers.google.com/maps/documentation/places/web-service/details Review Sorting Chose your sorting criteria. "Most relevant" is used by default. Preview Place Details If checked the place details can be previewed for debugging/development purpose on module page submit. Usage example Load the module in a page context: $module = $modules->get('GooglePlaceDetails'); Call a function to load data $module->getPlaceDetails(); This function fetches the data in realtime, on every page request and returns a php array containing the full response from the Google server. See the frontend example at the end of this document to see how to extract data from the array in a working example. Place details answer example The place details answer will be in JSON format and looks like this (depending of the fields you included in your request) { "html_attributions": [], "result": { "name": "Google Workplace 6", "rating": 4, "reviews": [ { "author_name": "Luke Archibald", "author_url": "https://www.google.com/maps/contrib/113389359827989670652/reviews", "language": "en", "profile_photo_url": "https://lh3.googleusercontent.com/a-/AOh14GhGGmTmvtD34HiRgwHdXVJUTzVbxpsk5_JnNKM5MA=s128-c0x00000000-cc-rp-mo", "rating": 1, "relative_time_description": "a week ago", "text": "Called regarding paid advertising google pages to the top of its site of a scam furniture website misleading and taking peoples money without ever sending a product - explained the situation, explained I'd spoken to an ombudsman regarding it. Listed ticket numbers etc.\n\nThey left the advertisement running.", "time": 1652286798, }, { "author_name": "Tevita Taufoou", "author_url": "https://www.google.com/maps/contrib/105937236918123663309/reviews", "language": "en", "profile_photo_url": "https://lh3.googleusercontent.com/a/AATXAJwZANdRSSg96QeZG--6BazG5uv_BJMIvpZGqwSz=s128-c0x00000000-cc-rp-mo", "rating": 1, "relative_time_description": "6 months ago", "text": "I need help. Google Australia is taking my money. Money I don't have any I am having trouble sorting this issue out", "time": 1637215605, }, { "author_name": "Jordy Baker", "author_url": "https://www.google.com/maps/contrib/102582237417399865640/reviews", "language": "en", "profile_photo_url": "https://lh3.googleusercontent.com/a/AATXAJwgg1tM4aVA4nJCMjlfJtHtFZuxF475Vb6tT74S=s128-c0x00000000-cc-rp-mo", "rating": 1, "relative_time_description": "4 months ago", "text": "I have literally never been here in my life, I am 17 and they are taking money I don't have for no reason.\n\nThis is not ok. I have rent to pay and my own expenses to deal with and now this.", "time": 1641389490, }, { "author_name": "Prem Rathod", "author_url": "https://www.google.com/maps/contrib/115981614018592114142/reviews", "language": "en", "profile_photo_url": "https://lh3.googleusercontent.com/a/AATXAJyEQpqs4YvPPzMPG2dnnRTFPC4jxJfn8YXnm2gz=s128-c0x00000000-cc-rp-mo", "rating": 1, "relative_time_description": "4 months ago", "text": "Terrible service. all reviews are fake and irrelevant. This is about reviewing google as business not the building/staff etc.", "time": 1640159655, }, { "author_name": "Husuni Hamza", "author_url": "https://www.google.com/maps/contrib/102167316656574288776/reviews", "language": "en", "profile_photo_url": "https://lh3.googleusercontent.com/a/AATXAJwRkyvoSlgd06ahkF9XI9D39o6Zc_Oycm5EKuRg=s128-c0x00000000-cc-rp-mo", "rating": 5, "relative_time_description": "7 months ago", "text": "Nice site. Please I want to work with you. Am Alhassan Haruna, from Ghana. Contact me +233553851616", "time": 1633197305, }, ], "url": "https://maps.google.com/?cid=10281119596374313554", "user_ratings_total": 939, "website": "http://google.com/", }, "status": "OK", } Usage in frontend example To display the reviews of a place you can do it like this (very basic markup!). I encourage every user to build their own markup of the reviews, fitting their design. <?php // 1. Connect to module $module = $modules->get('GooglePlaceDetails'); // 2. Save the details array to a variable $details = $module->getPlaceDetails(); // 3. Extract the data you want to iterate over $reviews = $details['result']['reviews']; // For debug purpose dump the array to inspect the data // TRACY DEBUGGER MODULE REQUIRED // dump($reviews); <? foreach ($reviews as $review) { ?> <div> <img src="<?=$review["profile_photo_url"]?>"/> <h4><?=$review["author_name"]?></h4> <? for ($i = 1; $i <= ($review['rating']); $i++) { ?> &#9733; <? } ?> <p><?=$review["text"]?></p> </div> <? } ?> ?> Here is a more advanced and nicer looking version (UIKit 3 Framework Markup is used) <div class="uk-container"> <div uk-slider> <div class="uk-position-relative"> <div class="uk-slider-container"> <ul class="uk-slider-items uk-child-width-1-2@s uk-child-width-1-3@m uk-grid uk-height-medium"> <? foreach ($reviews as $review) { ?> <li> <div class="uk-card uk-card-default uk-card-body"> <div> <img src="<?=$review["profile_photo_url"]?>" class="uk-responsive-width" style="height: 30px;" /> <span class="uk-text-middle"><?=$review["author_name"]?></span> </div> <div class="uk-margin"> <? for ($i = 1; $i <= ($review['rating']); $i++) { ?> &#9733; <? } ?> <? for ($i = 1; $i <= 5 - ($review['rating']); $i++) { ?> &#9734; <? } ?> </div> <div uk-overflow-auto="selContainer: .uk-slider-items; selContent: .uk-card"> <p><?=$review["text"]?></p> </div> </div> </li> <? } ?> </ul> </div> <a class="uk-position-center-left-out uk-position-small" href="#" uk-slidenav-previous uk-slider-item="previous"></a> <a class="uk-position-center-right-out uk-position-small" href="#" uk-slidenav-next uk-slider-item="next"></a> </div> </div> </div> If you are already using UIKit 3 and just want to get a quick result I put the code example above in a function that can can be called like this: <? $module = $modules->get('GooglePlaceDetails'); echo $module->getUIKitMarkupExample(); ?> The template file which is used for the markup lies inside the module directory. Adjust it to your needs.
  7. Long story short: When using the module all requests come directly over the Google Maps API. Every user has to generate an own and unique Google API Key and also activate a billing account. Otherwise no data will be send by Google. This is the only way to receive data over the Google API. In fact you pay for the data you are receiving. Would be a bummer if you are not allowed to store that data for your own usage.
  8. Greetings, everyone! I want to tell you that the module is finished. I have one little concern, though: I planned to in include a save and preview function of the received data for debugging/development purpose. However this would violate the terms of the google maps platform as it seems: https://cloud.google.com/maps-platform/terms 3.2.3 Restrictions Against Misusing the Services. (a) No Scraping. Customer will not export, extract, or otherwise scrape Google Maps Content for use outside the Services. For example, Customer will not: (i) pre-fetch, index, store, reshare, or rehost Google Maps Content outside the services; (ii) bulk download Google Maps tiles, Street View images, geocodes, directions, distance matrix results, roads information, places information, elevation values, and time zone details; (iii) copy and save business names, addresses, or user reviews; or (iv) use Google Maps Content with text-to-speech services. (b) No Caching. Customer will not cache Google Maps Content except as expressly permitted under the Maps Service Specific Terms. (c) No Creating Content From Google Maps Content. Customer will not create content based on Google Maps Content. For example, Customer will not: (i) trace or digitize roadways, building outlines, utility posts, or electrical lines from the Maps JavaScript API Satellite base map type; (ii) create 3D building models from 45° Imagery from Maps JavaScript API; (iii) build terrain models based on elevation values from the Elevation API; (iv) use latitude/longitude values from the Places API as an input for point-in-polygon analysis; (v) construct an index of tree locations within a city from Street View imagery; or (vi) convert text-based driving times into synthesized speech results. This also means that all the review scraper tools out there and the other strategies that we discussed here (fetch the reviews manually and save as page) are also not what google wants us to do. What do you think about that?
  9. It is happening 🙂 but I will need a few more days...
  10. Hi Chris! Thanks, now I get where my problem lies. Can you answer me one question: The rules that are defined here do crop the image, right? Does it work the same as using the Pageimage:size() method? $image->size(400, 300); I am worrying if the focus point which can be set for one image individually will still be used when cropping via the srcet rule. Another thing: When scaling down the browser window from very large (1920+) to mobile-small (640) the image which is loaded does not change. Even if I define custom aspect ratios. This is the intended and normal behaviour of a browser I guess. A large image that is loaded will look good on a small viewport too. So there is no need to load a new version. The other way around it works as you would expect: Going from a small viewport to a large one the image updates to larger versions step by step. I would like to update the image if the viewport shrinks, too. Which seems to work together with UIKit.
  11. Interesting. I was sure I saw this particular plugin on a website with more than 5 reviews being displayed. Anyway, there seems to be no way nowadays to fetch the reviews over any API key without enabling the billing. I got the same message in my console while testing. I now decided to use this solution: - Fetch the latest 5 reviews via the google API - Save the recieved JSON object to a field in the ProcessWire backend - Render the reviews based on that field data I will create a cronjob that fetches new reviews maybe once a week so hat you don't need to do an API request every single time someone visits the page.
  12. Hello there! I've come across a task to include the google reviews for a client website. You can see that kind of stuff nowadays on nearly every website, I personally am not a big fan of those review carousels but that doesn't matter. I am aware that there seems to be no module for ProcessWire to include those reviews into the site "out-of-the-box". So right now I only see two solutions: 1. Getting the reviews directly trough the Google Maps API PRO: You get a nicely formatted JSON Object that contains all data CONTRA: The maximum amount of reviews you recieve is limited to 5. Which is kind of a bummer. Plus: You have to register a Google Maps API Account to get an API Key which is mandatory for such requests. And this service is not free, too https://mapsplatform.google.com/pricing/ 2. Fetching the reviews over an HTTP request Thanks to @bernhard there is another solution. You take the link of a specific review and via DOM parsing (https://simplehtmldom.sourceforge.io/docs/1.9/api/file_get_html/) you fetch the review data directly form the source, save it as a page and use this as the source for your review. PRO: You only show the reviews you want in any order and as many as you need. CONTRA: A bit of work is involved, you have to add those review links manually to get the data. Other Solutions: It seems that every WordPress website includes such a review widget, and I just had to take a look to see what is different - please forgive me 🙂 This widget here only needs the place ID to fetch all the reviews. No API Key required and the amount is not limited to 5. I wonder how this is possible? So what are your thoughts about this? Have you ever included google reviews in one way or the other on a website?
  13. Basically the same image but in a more mobile friendly aspect ratio.
  14. I am able to get this code to work. But I don't know where to put the information for which picture source to use. On smaller screens I want to display a different image.
  15. I have a question about a possible feature. I read that you are using UIKit. UIkit offers the possibility to use multiple image sources and resolutions for one image. Like this: (example taken from: https://getuikit.com/docs/image) <div class="uk-height-medium uk-flex uk-flex-center uk-flex-middle uk-background-cover uk-light" sources="srcset: https://images.unsplash.com/photo-1464621922360-27f3bf0eca75?fit=crop&w=650&h=433&q=80 650w, https://images.unsplash.com/photo-1464621922360-27f3bf0eca75?fit=crop&w=1300&h=866&q=80 1300w; media: (min-width: 1200px)" data-src="https://images.unsplash.com/photo-1472803828399-39d4ac53c6e5?fit=crop&w=650&h=433&q=80" data-srcset="https://images.unsplash.com/photo-1472803828399-39d4ac53c6e5?fit=crop&w=650&h=433&q=80 650w, https://images.unsplash.com/photo-1472803828399-39d4ac53c6e5?fit=crop&w=1300&h=866&q=80 1300w" sizes="(min-width: 650px) 650px, 100vw" uk-img> <h1>Background Image</h1> </div> I would like to achieve the same markup but with the use of your module. Would this be possible? Right now I use a device check (mobile, tablet, desktop) to see which image resolution I want to use and render the image that way: <?php echo $image->size($imgFormat)->srcset() ?> The set rules are defined globally in the module settings. So the image gets cropped into the desired format (=custom resolution), then the srcset is generated. I am using different resolutions for different screen sizes: 16:9 on desktop, 4:3 on Tablet, 1:1 on mobile (for example) so just downscaling the image would not work for me. The problem is that this approach is not responsive in that way that the image format automatically changes when resizing the viewport. In fact you have to reload the page so that the new image format is applied and rendered.
  16. So basically it all depends on the screen width here. Take this for example. How do you change data-attributes or custom code inside HTML elements depending on the screen width directly inside your markup? For example when the plugin does not have responsive options. <div class="uk-card" uk-scrollspy="cls: uk-animation-slide-left;"> <p>Hello World</p> </div> I want to change the properties of the uk-scrollspy attribute for example. A different animation on small (=mobile) screens or maybe no animation at all. UIKit does not offer such responsive options (yet). For that I would use the detect-mobile library like this: <div class="uk-card" uk-scrollspy="<?= (!$deviceCheck->isMobile()) ? 'cls: uk-animation-slide-left': 'cls: uk-animation-slide-bottom' ?>"> <p>Hello World</p> </div> When resizing the window this change is not applied though, I have to reload the page to make it happen. (t might confuse some plugins that read those values on DOM ready but hat is another story)
  17. This is not the case. On some applications progressive enhancement is not possible (or let's say: ideal) because of the technological structure or other reasons. I am working for one of the biggest online stores in germany and for mobile/desktop devices we absolutely use browser detection and I am pretty sure most of the industry does. When visiting a product page there is a switch in the master template which then leads either to a mobile or desktop markup. These markup files share the same data source but code wise they are entirely different from each other. The reason behind this is that there was a completely different approach on how google ranked those mobile sites in the past AND since there are tons of elements and features (like custom written plugins) on the pages that massively differ depending on the device you're using this makes maintaining the code a bit easier. You have to load less/different assets, but it does add work when you have to make changes to both the desktop and mobile versions. Loading speed is an important ranking factor too, that's why you want to load only scripts and assets on your mobile page that you really need. Another reason why we use this approach. It's a giant application, though. On my private (and way simpler) ProcessWire projects I am sticking to progressive enhancement if possible. For images this will work. But I would never advice someone use media queries to hide elements with important content (SEO stuff) with "display: none" on any application. Just because google nowadays ignores this kind of content. Render the markup and the content that google needs to see (mobile first because mobile sites have top priority to google). And as you said: Take the same element and just make it look good on any device by using media queries. As I said I only use this browser detection for changing data-attributes or (only in one or two cases) the image format for a title image. Even advanced plugins like aos.js or high-end frameworks like UIKit are NOT responsive in some cases (https://getuikit.com/docs/animation). What do you do when you want to alter an animation based on the device? You have to somehow detect it and change the markup.
  18. I am using mobile detect to switch between different markups on mobile / desktop. For example on mobile I am using a different image format than on desktop devices. It would be much easier to make use of css media queries but in that case I do not want to load images that I do never display on a particular screen width. $imgFormat = 'title-img'; if ($deviceCheck->isTablet()) { // Tablet $imgFormat = 'title-img-tablet'; } --- and then: <img data-srcset="<?php echo $image->size($imgFormat)->srcset() ?> [...] Another example is the usage of html data attributes that vary on mobile/desktop devices: When using an animate on scroll plugin like aos.js you put your animation properties inside these data attributes. On desktop you might want animation A with duration X while on mobile you want animation B with duration Y. I set those properties via the mobile detect queries. <div class="feature-item-icon" data-aos="flip-right" data-aos-delay="<?= (!$deviceCheck->isMobile() && !$deviceCheck->isTablet()) ? $key*250 : '250'?>" data-aos-offset="200"> <img class="img-fluid" src="<?=$icon->url?>" alt="Feature Icon"/> </div> The major drawback is: the detection is server side. It is not responsive like css media queries where you see new properties instantly when switching through the breakpoints. Instead you have to reload the page to get the new values. In most cases this is no problem though.
  19. That is strange. I am using the same library for years now (http://mobiledetect.net/), on PHP 8+ i have no problems. Depending on where you put the mobile-detect files I have this in my _init.php: require_once '../../vendor/mobiledetect/mobiledetectlib/Mobile_Detect.php'; $deviceCheck = new Mobile_Detect; Then I can use the following expression to check the device. For example in a PHP template file. if (!$deviceCheck->isMobile()) { echo "this is not mobile"; }
  20. Alright, I'll keep that in mind. In my case the asterisk is not an important part of the search phrase anyway.
  21. I am using this module in all of my projects it has been in the /site folder eversince. Never had problems before with this one. UPDATE: I just renamed the folder into "ProcessPageCloneTest" and now I can view the login page again and log in successfully. This should be no permanent fix though...
  22. I have a strange behavior on one of my sites. The frontend works flawlessly but all of a sudden I cannot log in anymore. Last week it was working though. I never experienced that error before. When visiting the PW login page (in debug mode) I get this error message: Yow… Fatal Error: Uncaught Error: Class 'Process' not found in site/modules/ProcessPageClone/ProcessPageClone.module:20 #0 wire/core/Modules.php (1551): include_once() #1 wire/core/Modules.php (1507): Modules->includeModuleFile() #2 wire/core/WireClassLoader.php (284): Modules->includeModule() #3 [internal function]: WireClassLoader->loadClass() #4 [internal function]: spl_autoload_call() #5 wire/core/Functions.php (841): class_exists() #6 wire/core/Modules.php (1465): wireClassName() #7 wire/core/WireClassLoader.php (284): Modules->includeMo (line 20 of site/modules/ProcessPageClone/ProcessPageClone.module) This error message was shown because: site is in debug mode. ($config->debug = true; => site/config.php). Error has been logged. I did not make any critical changes to the file system or the module structure. No updates, nothing. On my local dev environment I can access the backend without any problems. PW Version is 3.0.165, PHP Version is 8.0.
  23. Thanks! Using the selectorValue sanitizer did the trick! I didn't even have to whitelist the asterisk. $q = $sanitizer->selectorValue($input->get->text('q'));
  24. Hi There! I am using the ProcessWire search function on a page and a client just told me that they encounter a server error when placing an asterisk symbol ( * ) into that field. PW Version is 3.0.200. So basically what they type into the search field is something like * string This resolves in an error message: Exception: Unrecognized operator: * (in /Users/XXXX/Sites/XXXXXXX/wire/core/Selectors.php line 411) My selector looks like this: $q = $input->get->text('q'); if ($q) { $input->whitelist('q', $q); $events = $pages->find("search_cache%=$q, template=event, date_event>=today, sort=date_event, limit=6"); } According to this link (https://github.com/processwire/processwire-issues/issues/1207) I found out that searching for '*' did work in the past but stopped in newer PW versions. I have an older site running on PW 3.0.164 and there is no error message there.
×
×
  • Create New...