-
Posts
311 -
Joined
-
Last visited
-
Days Won
8
Everything posted by nbcommunication
-
Shetland.org | Welcome to the Islands of Opportunity
nbcommunication replied to nbcommunication's topic in Showcase
Hi @Marco Ro, The map uses LeafletJS with two layers, OS Maps (UK only) for the road map, and Mapbox for the satellite. For analytics, we're still experimenting with explicit consent. Matomo has a cookie-less option so theoretically we're able to record every site visit using it, but can only record those that have consented using GA. I really hope Google figure out a cookie-less analytics implementation soon, it is an absolute nuisance! Cheers, Chris -
Shetland.org | Welcome to the Islands of Opportunity
nbcommunication replied to nbcommunication's topic in Showcase
Hi @3fingers, Both a and c are part of an internal (proprietary) GraphQL API implementation. The short explanation is: A GraphQL query is generated from the filters when the form is 'submitted' The GraphQL endpoint is queried and returns results The result is rendered b would take an age to explain properly, but basically the query string ends up in sessionStorage (JS) and the filter strip JS checks this to set the active option(s). When the filter options are changed the sessionStorage values change too so that the state is essentially retained throughout the user session. I can't give you any indication on budget unfortunately. We run the Promote Shetland contract, which is much more than just the website, so the redevelopment project is effectively 'in-house'. Timespan was just short of 6 months (design/development) I think, I wasn't involved in the early stages so can't say exactly when it began, but my involvement as developer started in August and we launched mid-November. Cheers, Chris -
Shetland.org is a website run by Promote Shetland which inspires people to visit Shetland, encourages people to move to Shetland to live, work and study, and attracts people to invest in commercial activities in the isles. We (NB Communication) have run the Promote Shetland service on behalf of the Shetland Islands Council since 2017, and as part of the contract undertook a project to redevelop the existing Shetland.org website. In this showcase we’ll highlight a selection of modules we used and what they helped us achieve. Visit the site: www.shetland.org Pro Modules ProCache We use this on almost every site we build. Indispensable. The cache settings used are pretty simple – most templates are set to 1 week, with the entire cache being cleared on save. We use ProCache’s CDN functionality to serve assets from CloudFront via c.shetland.org. We also use the API provided by ProCache to compile (SCSS), minify and collate our styles and scripts via $procache->css() and $procache->js(). We then use the URLs returned to preload the assets, making the site even faster! ProFields: Repeater Matrix Again, we use this on almost every site we build. Another must have Pro module. This module allows us to create a really powerful page builder field (we call it ‘blocks’) that handles the majority of the content on the site. On a simple development, we just use two block types - Content and Images - the latter displaying as a gallery or a slideshow. On this site we have 13 different types, including ‘Quotes’, ‘Video’, ‘Accordion’, and ‘Links’. Additionally many of these are configurable in different ways, and some render in different ways depending on the template and context. Have a look at the links below for some examples: https://www.shetland.org/visit/do/outdoors/walk https://www.shetland.org/blog/how-shetland-inspires-me-artist-ruth-brownlee https://www.shetland.org/life/why/teach-shetland-school NB Modules We also used a number of modules we've authored: Instagram Basic Display API Used to retrieve the 6 latest images from instagram.com/promoteshetland. Markup Content Security Policy Used to implement a CSP for the site. Currently scoring a C on observatory.mozilla.org – not the best score possible but significantly better than all the other destination marketing websites I tested (all got an F but one which was a D-). Pageimage Srcset Used throughout to generate and serve images of different sizes via the srcset and sizes attributes. This module is really useful if you are looking to optimise the serving of images to improve page speed times/scores. Video markup for YouTube/Vimeo This module was developed specifically for use on this website, as we wanted more control over the rendering of the oEmbed data. In the example below, the video thumbnail is displayed with a text overlay – when clicked the video (YouTube embed) opens in a lightbox. And a big shout out to… Page Path History The previous site was also built by us in ProcessWire, but a number of years ago now. The new site has significant changes to the sitemap, but 1000+ blog posts were also migrated. Instead of an .htaccess file with thousands of 301 redirects, we were able to use the functionality provided by this module to implement redirects where required, and in the case of the blog posts which were migrated via an import script, implement the redirects via the API - $page->addUrl(). ... The above is just a fragment of the features present on this site, and the development just a part of a much larger project itself. We're really proud of what we've achieved, and we couldn't have done it without ProcessWire. Cheers, Chris (NB Communication)
- 14 replies
-
- 31
-
-
-
Hi @Roych, What is the template (in config) that you're using to output the oEmbed data? Are you using any hooks? Does the page have a og:description tag in the <head> and if so what does this show in the source? Cheers, Chris EDIT - apologies for the late response, was on holiday last week!
-
New blog: ProcessWire modules directory
nbcommunication replied to ryan's topic in News & Announcements
Hi @ryan, The new modules directory is looking great and what I've used so far has worked great ? I've a request for you to consider. Would it be possible to mark major version changes as not backwards compatible? The use case: We have a module PageimageSrcset, that contains a number of features that I thought would be useful at the time of writing, but I myself have never/rarely used (e.g. generating the sizes attribute value from an array of UIkit classes). In a future version I'd like to remove these features, and have the module be more in line with its purpose. I have enquired on the board, but not had any response to say that any of the features are being used. The solution as I see it, would be to release a new 2.0.0 version (currently 1.1.0) which removes these features, and any users with 1.1.0 installed would either not be able to upgrade directly (would require manual upload), or would be given an additional warning that the version introduces breaking changes and to check GitHub/support board before upgrading. Is this possible? Cheers, Chris -
Hi @Crowdland Technology, Thanks for the feedback! Yes, this would be the case. I'd advise a cache of 1 hour (3600). The request to the API itself isn't heavy, but there is rate limiting on IGs end which a cache time of an hour helps to manage. Yes this is odd, but this is what the Instagram Basic Display API provides. If you request media of the type IMAGE, you also get the poster of media of the type VIDEO and the first image from media of the type CAROUSEL_ALBUM so this is what the module returns. If you only want single image posts, then using getMedia() and then only using items of the type 'IMAGE' is the way to go. getMedia() returns all media, of the three types. IMAGE is accessed through the src property, VIDEO through src (the video) and poster (the thumbnail image) and CAROUSEL_ALBUM through children (all the images in the collection). I think what you're asking here is that if the tester user did have a CAROUSEL_ALBUM, how do you get just the first image from this? <?php foreach ($insta_media as $media): switch($media->type) { case 'VIDEO': $media_src = $media->poster; break; case 'CAROUSEL_ALBUM': // Just get the first item $media_src = $media->children->first->src; break; default: $media_src = $media->src; break; } ?> I haven't tested it, but I'm pretty sure that would get the first image from the album. Worth looking closer at the README as there's a lot there that isn't explicitly explained, but makes sense one you review the code examples ? Hope that helps! Cheers, Chris
-
Hi @alexmercenary, getImages() returns images, the main carousel album image, and the poster image of videos. To filter these so it is only IMAGEs you could do something like: <?php $images = $modules->get('InstagramBasicDisplayApi')->getImages(); foreach($images as $image) { if($image->type !== 'IMAGE') continue; echo "<img src=$image->src alt=$image->alt>"; } I think that getMedia() is maybe the call you are looking for? https://github.com/nbcommunication/InstagramBasicDisplayApi#getmediastring-username-int-limit Cheers, Chris
-
7G Firewall - tweak(s) required
nbcommunication replied to nbcommunication's topic in General Support
Found another one tonight while testing a sandbox install with a lot of Lorem Ipsum.... Wasn't loading a page with the name 'praesent-vel-quam-porttitor-nulla' because of 'null' being in the name. Seems a little extreme, or not very well implemented, but I just removed it from the name as it is unlikely that 'null' would appear in a page name. Worth noting though! Cheers, Chris- 1 reply
-
- 1
-
-
Hi, I added the 7G firewall htaccess rules to a site, and was getting errors in the PW admin as JS files were failing to load. The culprit is line 85 (v1.2): RewriteCond %{REQUEST_URI} (/)((php|web)?shell|conf(ig)?|crossdomain|fileditor|locus7|nstview|php(get|remoteview|writer)|r57|remview|sshphp|storm7|webadmin)(.*)(\.|\() [NC,OR] It is the conf(ig) part that prevents some PW admin JS files from being loaded (e.g. /wire/modules/AdminTheme/AdminThemeUikit/config-field.js). I removed this so the line becomes: RewriteCond %{REQUEST_URI} (/)((php|web)?shell|crossdomain|fileditor|locus7|nstview|php(get|remoteview|writer)|r57|remview|sshphp|storm7|webadmin)(.*)(\.|\() [NC,OR] Hopefully this will same some future users of the 7G firewall some time! If you come across any other issues using 7G with PW, please post them here! Cheers, Chris
- 1 reply
-
- 4
-
-
Hi @Roych, If CKEditor is automatically making a <a>link</a> when you paste in the URL, then you need to unlink it using that option from the toolbar (chain icon with the cross in the bottom right). Cheers, Chris
-
Hi @Roych, Thanks - the issue is the link. Both this and TextformatterVideoEmbed don't work on <a>links</a>, only plain URLs inside <p> tags. If you remove that it'll work *hopefully*! Cheers, Chris
-
Hi @Roych, I think I know what the issue might be, and I may add a config option to prevent it. Answers to the following should clarify: In the Details tab of your body field is Content Type set to Markup/HTML? (it was on the screenshot you posted, but I note that the CKEditor iframe method requires this to be set to unknown). When you paste directly into CKEditor, what source HTML is generated for that line? Conversely, if you paste into the source code, then come out of the dialog, then go back in, what source HTML is generated for the line? Cheers, Chris
-
Hi @Roych, I've pushed a fix (v0.2.1). I needed to switch output formatting off before getting the Inputfield, which is used to determine whether it's HTML or not. Thanks for letting me know about this - must set up a multi-language install for testing! Cheers, Chris
-
Hi @Roych, I've managed to replicate the problem with Multi-language enabled. Once I've figured out the issue I'll push a fix and let you know! Cheers, Chris
-
Thanks @Roych, Will get a similar setup in place and see if I can get it replicated and debugged. Cheers, Chris
-
Hi @Roych, Just done a test and the pasted code isn't the problem for me, so I'm the problem likely lies in #2 or #3. I also note the error messages from the other thread, particularly the mention of WireHttp, which suggests the module is falsely parsing a URL and trying to request oEmbed data from it. The setup of the body field would be a massive help in debugging this - if you are able to screenshot the Details and Input tabs, that would be brilliant. Cheers, Chris
-
Hi @Roych, Thanks for letting me know, I'll look into this in the morning - likely something to do with the regex. To aid in debugging, can you please: 1. Confirm that this is the code that was pasted into the body field using the source option: <iframe src="https://docs.google.com/forms/d/e/1FAIpQLSczjrIs47Nd9iJiPFtTNH_sRJgdBOXvw29vJFKLo20jJJwIvw/viewform?embedded=true" width="640" height="4264" frameborder="0" marginheight="0" marginwidth="0">Nalaganje …</iframe> 2. Post the Markup you are using in the module config. 3. Let me know what other textformatters are used on the body field and their order? That should hopefully allow me to debug the problem. Cheers, Chris
-
Thanks @DV-JF, The auto-renewal of tokens has been working for me so far, I have 10+ production installs I think. Where it may not work is if there isn't an API request the week prior to the renewal date. I did limit cache time to 1 week, so I'm hoping this won't happen on production sites. This did happen on my sandbox install which I used for testing the module - came back several months later to a token unable to renew, so just removed then re-added the account. Therefore, it is a remote possibility that renewal won't happen, but very unlikely on a site that is getting traffic. No need for coffee, just delighted to get feedback! Cheers, Chris
-
Hi, This module does not appear to be compatible with the latest master. Seems the refactoring of InputfieldDatetime since 3.0.148 is the culprit. Failed to construct module: \InputfieldDatetimeAdvanced - Method InputfieldDatetimeAdvanced::getInputFormats does not exist or is not callable in this context I added getInputFormats() from a previous version of InputfieldDatetime and that appears to have fixed it: <?php /** * Get the input format string for the user's language * * thanks to @oliverwehn (#1463) * * @param bool $getString Specify true to get a format string rather than an array * @return array|string of dateInputFormat timeInputFormat * */ protected function getInputFormats($getString = false) { $inputFormats = array(); $language = $this->wire('user')->language; $useLanguages = $this->wire('languages') && $language && !$language->isDefault(); foreach(array('date', 'time') as $type) { $inputFormat = ''; if($useLanguages) { $inputFormat = trim($this->getSetting("{$type}InputFormat{$language->id}")); } if(!strlen($inputFormat)) { // fallback to default language $inputFormat = $this->get("{$type}InputFormat"); } $inputFormats[] = $inputFormat; } if($getString) return trim(implode(' ', $inputFormats)); return $inputFormats; } Cheers, Chris
-
Render oEmbed data from YouTube/Vimeo URLs... or TextformatterVideoEmbed for power users. https://github.com/nbcommunication/TextformatterVideoMarkup The use case... On an upcoming project, we want to be able to render YouTube/Vimeo URLs as thumbnail images, that when clicked open up in a (UIkit) lightbox. Additionally, we want to be able to specify the thumbnail image - as part of a RepeaterMatrix block which contains a URL field (video) and an Image field (thumb). The result is this module, which allows you to specify the markup used to render the oEmbed data: The formatter can be used on any Text field e.g. Text, Textarea (CKEditor or not), URL etc. Global configuration options are available (e.g. rel=0), based on TextformatterVideoEmbedOptions. An 'empty value' can be specified for URLs that do not return data from the oEmbed endpoint The render method is hookable, allowing you to customise rendering on a per page, per field basis Plenty more information here ? https://github.com/nbcommunication/TextformatterVideoMarkup/blob/master/README.md Back to the use case... How do we render the thumbnail and then use the image from our Image field? In the module config Markup field: <figure data-uk-lightbox> <a href="{url}" data-poster="{thumbnail_url}" data-attrs="width: {width}; height: {height}"> <img src="{thumbnail_url}" alt="{title}"> </a> </figure> Then in site/ready.php <?php $wire->addHookBefore('TextformatterVideoMarkup::render', function(HookEvent $event) { // Arguments (for info) $tpl = $event->arguments(0); // string: The markup template $data = $event->arguments(1); // array: The oEmbed data $url = $event->arguments(2); // string: The requested URL $emptyValue = $event->arguments(3); // string: The empty value used if no data is returned // Object properties (for info) $page = $event->object->page; // Page: The page $field = $event->object->field; // Field: The field $html = $event->object->html; // bool: Is it HTML being parsed, or plain text? // Replace the thumbnail image if($field->name == 'video' && $page->hasField('thumb') && $page->thumb) { $data['thumbnail_url'] = $page->thumb->url; $event->arguments(1, $data); } }); The module requires PW >= 3.0.148 and PHP >= 7. It probably doesn't need to, but the expectation is that power users will be able to meet these requirements! The module is also Beta - please don't use in production yet. I suspect there will be edge cases related to the changes I made to the URL regexes from TextformatterVideoEmbed - so far though they are working for me. If you come across any issues please let me know! Cheers, Chris
-
That didn't take long - @kkalgidim, if you re-download (should now be version 1.4.1) then hopefully that'll work for you.
-
@kkalgidim - looks like PHP < 5.6. Arrays in class constants are allowed from 5.6 onwards. You really should be on 7.2 or above! Given that ProcessWire supports 5.4 and above, I'll look into tweaking this so you can install. Cheers, Chris
-
Hi @kkalgidim, What PHP version are you running? Cheers, Chris
-
Updated to 1.0.1 (Stable), mainly reducing hook priority < 200 so it runs before ProCache.
-
Hi @jonatan, You would probably need to do something like: var resetPagination = new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open("GET", window.location.href); oReq.addEventListener(xhr, "load", function() { if (xhr.status === 0 || xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) { resolve(xhr); } else { reject(); } }); oReq.addEventListener(xhr, "error", function() { reject(); }); oReq.addEventListener(xhr, "timeout", function() { reject() }); xhr.send(); }); resetPagination.then(function() { instagram.init(); }, function() { // Error }); If I remember correctly, this kind of plain Javascript request doesn't actually register as an ajax request, so $config->ajax returns false, and the pagination will be reset. I'm not sure this is the best way to go though - I'd probably be trying to store the requested data in an array/object and then just pull it from there when the page changes e.g. var requests = {}, var items = []; // On page change var page = "page" + pageNum; // whereever you get this info from if(page in requests) { items = requests[page]; } else { items = "an AJAX request to get the next page of items"; requests[page] = items; } It will probably require a bit of reworking of your code, but it's a far better way to go. You shouldn't have to request the data more than once - store it! Hope that helps, Chris