Leaderboard
Popular Content
Showing content with the highest reputation on 11/20/2018 in all areas
-
News Update - 20 November 2018 Hi all. This update should have gone up about 3 weeks ago :-). Some of you are aware that I've been having computer issues but that's sorted (fingers crossed) for now. Before that, I'd managed to work on and mostly complete a number of things. I'll try and remember them now as I write and hopefully I don't forget anything (these posts not only help with generating discussions but they will be useful references when I get round to doing the docs). Orders First, thanks (especially @arjen and @szabesz) for the feedback on orders and customers. We now have a FieldtypeOrders and FieldtypeOrderItems which store permanent records of orders. The former stores the aggregates (total price, etc) and customer info. The latter stores data about each individual item in the order (name, price, etc). These two simplify querying orders (backend stuff). Customers FieldtypeCustomers stores registered customers' data. It is also useful for frontend customer login areas/dashboards and for returning customers in cases where customers have to login in to make purchases, e.g. retrieve their stored address. Products The products field has been updated to store product dimensions. Shipping This is by far the biggest work to date, I think. It consists of 3 modules, one each for Shipping Zones, Options and Rates. I think it is comprehensive enough to do away with the need to write custom shipping classes although we'll keep that option open. Please note that although the explanation of the underlying logic behind shipping can seem complicated, the GUI for setting up shipping will be quite simple and intuitive to use. Zones Setting up shipping zones To create a shipping zone, you will have first set up countries where you ship to. This is a global setting since it also affects taxes. Once that is sorted, you can start creating shipping zones. You can create as many shipping zones as you need. A shipping zone consists of geographical areas where you ship physical products to. A shipping zone can have as many shipping regions as needed. Regions A shipping region can be any of these: Continent (E.g. Asia) Country (Germany, Lithuania, UAE) State/Province (Kowloon, Goa, Texas, Arezzo) The selection of regions (GUI) will be inbuilt. I.e., shop admins will be able to select Quebec or Nigeria or Asia, etc. In addition, one will be able to further refine their region definitions using postal/zip codes. Postal/Zip codes Postal codes can be matched in several ways: Verbatim: in this case, you specify an exact postcode(s) to match, e.g. CD30 78GH or 18000 Range: For numerical postcodes such as US zip codes, shop admins can specify the desired range(s), e.g. 60001 - 61909 Wild cards: This will match the first n characters of the postal code, e.g. SW* or BH3* You can enter as many postal codes to match as you wish. A customer's shipping address details at checkout are used to match their order to the shipping zones defined in the system. If no match is found, the system defaults to a generic shipping zone/rate if you have one set up AND assuming the customer's delivery address is in a 'place' you ship to. If not match is found at this point, the customer will be shown an error or custom message you set up. Shipping zones examples 1. State/Province + Postcodes Say you ship only to California. It's a big place so you decide to divide up the place , into 5 shipping zones using zip codes. In this example, the first 4 shipping zones are defined by zip code ranges. The fifth zone takes care of any other areas inside California not within the zip code ranges in the first 4 zones. California zone 1: 90001 - 92999 California zone 2: 93000 - 93705 California zone 3: 94200 - 94799 California zone 4: 95000 - 95750 California zone 5: All other zip codes in California In this example, zone #5 will be matched as long as the State in the shipping address state is California and the zip code is not within the ranges described in zones 1 - 4. 2. Country + States/Provinces In this example, you ship only to Canada. You create several zones based on Canadian provinces as follows: Canada zone 1: Alberta, British Columbia Saskatchewan (multiple regions in one zone) Canada zone 2: Ontario, Quebec Canada zone 3: All other provinces In this example, zone #5 (all other provinces) will be matched as long as the shipping address country is Canada and the province is not one of those in zones 1 or 2. 3. Continent In this example, we use continents to set up shipping zones based on groups of countries. For instance, setting up zones for Africa: Africa zone 1: East Africa consisting of the countries (regions) Kenya, Uganda, Tanzania Africa zone 2: West Africa; Gambia, Cameroon, Togo, Niger Africa zone 3: North Africa: Egypt, Tunisia, Morocco Africa zone 4: South Africa: Zimbabwe, Botswana, South Africa In this case, the system will match the country to their continent. Customers do not have to enter 'continent' in their shipping address details :-). Rates Shipping zones alone are useless without rates :-). Padloper 2 allows you to create rates that you can use and reuse across different zones. Please note that rates in this case just define the applicable rates(s) for an order (or part of an order) based on certain conditions. Rates do not specify how much shipment should be paid. We will come to that later below. Rates can be based on: Flat rate: A single flat rate should be paid for this zone. For instance, using our examples above, we can set a flat rate for all shipments to California zone 1 but set a different rate for California zone 3. Price: Shipment is calculated based on price. E.g. if order is worth < $30, charge $2.50 Weight: Shipment rate calculated based on weight of items in basket Quantity: Shipment calculated based on quantity of items in basket Rate Applies To A rate 'based on' value in itself is incomplete. One also needs to specify how the shipment rate will be applied. The choices are: Order: Shipment will be charged PER ORDER (the whole order). This means apply the shipment once irrespective of number of items in the order, or their price or weight, etc. Item: Shipment will be charged PER ITEM in the order. So, if we have 10 items, shipping will be charged 10 times, etc. Item-group: Shipment will be charged once PER EACH MULTIPLES OF THE 'SAME ITEM'. By item-group is meant a product with the SAME ID and same VARIANT ID. So, if we have two shirts, that have the same product ID, but one is orange with variant ID 1 and the other is blue with variant ID 2, those are NOT the same item. If our basket contains 3 of the orange shirts, that is counted as one item-group and if we have 1 blue short, that is another item group. In this case, shipment will be charged once for each item-group - once for the orange shirts and once for the blue shirt. Product-class: This is a special rate so let's examine it more closely. Product Class Shipment is a powerful feature that allows shipment to be calculated based on a product class. A product can only belong to one class at a time. A class is a special category which you use to group similar products for the purposes of shipping. For instance, you can have the following classes: Heavy: This would be a class for heavy items Bulky: A class for bulky items Small Fragile Etc... You can create and use as many product classes as you wish. Product class shipment is used with any combination of the above Rates and Rate Applies To. With product class shipments, you can mix and match as required. For instance, for your Small items, you can use Flat Rate shipping applicable to the whole Order. You could also have a Weight-based Rate that is Applied per Item. Or, a ship your Fragile items using a Quantity Rate applying per Item-group. As you can see, using product class shipments allows for more flexibility within an order. Of course, you can set up different product class rates depending on your shipping zone. For example, you can Flat Rate shipment for Bulky items shipped locally and an Item Rate shipment for Small items shipped to your international customers. Please note that in the case of Rate Applies To Order with respect to Product Class shipment Rate Value Using a Rate Value setting, you can specify what value should be used to determined whether an order has met a shipping criteria. Consider the following examples: €2 shipment will be charged PER ITEM in the basket if the ITEM COSTS at least €15. In this case the Rate Value looks at the price of a single item (i.e, >= €15) in order to apply a €2 per item shipment Now consider a similar example but with a different Rate Value. €2 shipment to be charged PER ITEM in the basket if the TOTAL COST of the basket is at least €15. In this case, the total cost of the basket is used to determine if the threshold has been reached. The last piece of the puzzle is Shipping Methods. You might have a flat rate or a price-based rate, but in relation to time (or level of service) how do you actually deliver the product? Methods Shipping methods are straightforward. You can add as many shipping methods as you want. For instance: Collection: Buyer collects from your shop Same day delivery Standard delivery 3 - 4 Days' delivery Express delivery Etc There are no predefined methods. You set this up yourself to suit your needs. Options Shipping Options complete the shipment system. A shipping option is a combination of a shipping rate and a shipping method. Think of them as a matrix (similar to Table rate shipping in WooCommerce). Shipping options are automatically created based on selected Shipping Rates and Shipping Methods for a zone. The admin then has to enter a shipping fee/charge/cost for each combination of Rate and Method. At checkout, the customer will be presented with the shipping options available to them and how much that would cost them. Here are some examples Shipping Options (Methods X Rates): Options: a 1 x 1 combination You should could offer a Standard Delivery (Method) charging a Flat Rate £10 for all transactions. Options: a 2 x 1 combination Offer Normal and Express shipping charged at a Flat Rate £5 and £10 respectively for either Method. Options: a 1 x 2 combination Offer Standard delivery on all shipping based on Quantity of order and charging $7 if order contains less than 10 items and $4.50 if order contains more than 10 items Options: a 2 x 2 combination Delivery Methods: Collection Normal Same Day Weight-based Rates: Rate 1: Less than 10 kg Rate 2: Greater than 10 kg but less than 20 kg Rate 3: Greater than 20 kg Shipping options will be the matrix of the above methods and rates each with a shipment fee, e.g. Collection x Rate 1: Free Normal x Rate 1: £5 Same Day x Rate 1: £15 Normal x Rate 2: £8 Etc.. Options: a more complex combination: product class rates Delivery Methods Standard Express Product-class Rates + Applies To Small items: quantity-based rate charged per item-group Fragile items: flat rate charged per item Heavy goods: weight-based rate charged per item Rates Small items quantity less than 5: $0.75 Small Items quantity more than 5 but less than 15: $0.50 Heavy goods: less than 30 kg: €10 per product Shipping options will be a matrix of the above methods and rates each with its own shipment fee, e.g. Standard delivery of Small Items if less than 5: $0.75 per item-group Express delivery of Heavy Goods if item < 30 kg: €10 per product You get the idea ? Maximum shipping Cost and Handling Fee Padloper 1 included a maximum shipping cost. This is retained in version 2. In version 2, you can add a handling fee which will be applied PER EACH shipment calculation. So, if shipping applies per order, a handling fee is applied once. If shipping applies once per product class in an order, handling fee will be charged x the number of eligible product classes in the shipment (e.g. once for Small items, once for Heavy Goods, ETC). Merge Shipping This is work in progress and applies to Product Class-based shipping. It allows the shop admin to specify what shipping rates can be merged in order to use one shipping rate instead of two. For instance, a shopping cart/basket could contain 5 Small Items and 3 Medium-Sized Items. Rather than charge for and ship these separately, the items could be merged and shipped together using the Medium-Sized items rate. Shipping Package This is a planned feature that may not make it in the first release. It is useful where shop owners use delivery services like Fedex where package dimensions and weight or volumetric weight are important factors. I think that's it. I could have forgotten something or could have expressed something better. I'll edit this post if such needs arise.10 points
-
Here something very special, in the days of trillions of colors high dpi screens and VR... art by Mark Ferrari which is absolutely incredible work. http://www.effectgames.com/demos/canvascycle/7 points
-
The Katharinenhöhe is a rehabilitation clinic in the Black Forest (Germany) for teenagers, young adults and families with children who suffer from cancer. Our agency designconcepts was lucky to relaunch their website. Our goal was to unify the previous separated areas (families and teenagers/young adults) and provide a clear structure for potential patients. Also we wanted to show that the clinic is a nice place to live, despite the circumstances. We rebuild the website from scratch with the framework UIkit and used large images as well as videos. www.katharinenhoehe.de Features: Repeater Matrix Tour Contrast Theme Glossary Autocomplete Search Facebook Repeater Matrix Most of the pages use a basic page template which have one Repeater Matrix field. This field has around 15 different content elements, so it is easy to build a page with different elements in various amounts. Tour On the site tour we build a image map with markers showing interesting places of the clinic. For this task the nice module Image Marker and the Marker component of UIkit came in handy. The tour is available on every page containing a marker. Contrast Theme For a better reading experience you can switch to a more contrasting theme of the website by clicking the theme switcher (on the top right). This is a separate stylesheet with darker color variables. The choice will be saved in a session variable and stays as long as the browser is opened. Glossary To explain complicated medical terms better, we highlight a set of terms in every textarea they occur and explain them with a tooltip. For this task we wrote a simple Textformatter module which looks for the terms in a page and replaces the terms with the tooltip. This tutorial by @benbyf helped me. Thank you! ? Modules used: Color Email Obfuscation (EMO) Front-End Page Editor Functional Fields Image Marker Markup Sitemap XML Phone ProCache Repeater Matrix Tracy Debugger Upgrades Regards, Andreas4 points
-
Hey @Tom. JAMstack itself is just a marketing term created by Netlify’s CEO to onboard people onto their hosting platform and CMS. Netlify CMS is not really a CMS, it is an admin panel that integrates with static site generators like Hugo and Jekyll. The use case for static site generators is usually very simple sites with basic structured content like blogs and portfolios. See examples here https://jamstack.org/examples/ The JAMstack methodology also encourages using cloud CMS platforms like Contentful, however there are benefits and drawbacks to this I won't go into. To set up something like what you are asking about (but way more fun and flexible) with Processwire, imagine you have a domain http://api.website.com pointed to a Processwire instance with a homepage template like this: <?php namespace ProcessWire; header('HTTP/1.1 200'); header('Content-Type: application/json,charset=utf-8'); header("access-control-allow-origin: *"); $data = []; $projects = $pages->find("template=project"); foreach ($projects as $p) { $data[] = [ $title -> $p->title; $content -> $p->sometextareafield; ] } echo json_encode($data); and the main domain http://website.com is just a static hosted at netlify and your index.html contains the below: <body> <div id="projects"></div> <script type="text/javascript"> $(document).ready(function() { $.ajax({url: "http://api.website.com"}).done(function(data) { var $projects = $('#projects'); data.forEach(function(project) { var html = "<div class='project'>" + "<h2>" + project.title + "</h2>" + project.content + "</div>"; $projects.append(html); }); }); }); </script> </body> ... then you have the beginnings of a single page javascript app. Of course building an entire site like this and incorporating routing and state management etc would become tiresome very quickly in jQuery, which is why javascript frameworks like React and Vue exist. Regarding build tools and task runners like Webpack and Gulp, they exist to do what they say, i.e. bundle up static assets or run tasks. Before getting to that tho, it is important to understand the way people use node and npm for front end dev. See this article maybe: https://www.impressivewebs.com/npm-for-beginners-a-guide-for-front-end-developers/ I personally hate Webpack, I use npm scripts and the CLIs of my preferred libraries to do everything. I can post an example if you like, but these blog posts sum it up: https://deliciousbrains.com/npm-build-script/ https://www.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/4 points
-
Actually it's very easy. You can get the email of the page creator with $page->createdUser->email2 points
-
2 points
-
You want to add a method then; not a property :-). Wire::addHookMethod() https://processwire.com/api/ref/wire/add-hook-method/ http://processwire.com/api/hooks/#add_new_method Inside the hook, you can get your parameters using event->arguments. E.g. event->arguments(0) is the first parameter, event->arguments(1), the second one, etc.2 points
-
So imagine my shock today when i tried to d($var) and nothing happened (well, apart from a few crumbs of debug on the page)! Not again!, I groaned. I scanned Tracy's settings and nothing jumped at me. Everything seemed OK. I even tried debugInfo=>false/true, but no joy. A quick look at the docs and eureka! @options: DETECT | DEVELOPMENT | PRODUCTION @default: DETECT A docker container's IP address would be treated differently! It was being read as site is in 'PRODUCTION'!. A quick change to that setting and normal Tracy was back! You gave me a fright Tracy! I've never seen this side of you. We've only ever met 'locally' ?.2 points
-
Thanks @Robin S - really appreciate the quick fix. I actually have ~5000 siblings and it still seems to be working ok.1 point
-
@adrian, I've added a substitute getPageIndex() method to the module so the issue should be fixed in v0.1.12. Probably not as efficient as the core Traversal methods but should be okay unless you have a massive number of siblings.1 point
-
Thanks, seems like a core issue with Page::index(): https://github.com/processwire/processwire-issues/issues/751 Will see if I can find a workaround for the short term.1 point
-
I knew it is easy, after all we’re talking about The #Processwire ? Thanks for the help.1 point
-
If you don't have different names, then localUrls with be identical. Your select will just reload the page. What you need to do is pass some kind of indicator (e.g. a GET parameter with the language id) to the server and set the user's language before the page is rendered. So you need to output something like this in your page for the language selector: <select onchange="window.location=$(this).val();"> <?php foreach ($languages as $language) : ?> <?php $url = $page->localUrl($language) . "?lang=" . $language->id; ?> <option <?php if ($user->language->id == $language->id) : ?>selected<?php endif; ?> value="<?php echo $url; ?>"><?php echo $language->title; ?></option> <?php endforeach; ?> </select> Then, in the backend (best site/ready.php), you need to adapt the current language according to the parameter and store the selection in the session to make PW remember it (untested): <?php if($input->get->lang) { $newLang = (int)$input->get->lang; if(! $languages->get($newLang) instanceof NullPage) { $session->set("currlang", $newLang); } } $newLang = $session->get("currlang"); if($newLang) { $user->language = $languages->get($newLang); } You should think hard whether you want to do that though. Google & Co. will put a massive penalty on your site if you show differing content on identical URLs. Languages are different beasts from locations. I have, e.g., some colleagues from foreign countries who are a lot more comfortable doing their shopping in English rather than in German, but they still need to see the usual Euro prices and German VAT. So you might just use a different thing than the language, e.g. a GET variable named "country" that you store in the session just like the code above does and pass that to your conversion function.1 point
-
Thanks for the write up, it looks promising indeed! In the long run, my client would be interested in such a feature for sure.1 point
-
$page->setOutputFormatting(false); foreach($imagegallery as $filename) { $page->imagegallery->add($upload_path . $filename); $page->save(); } Though I'm not sure that it's obligatory to call $page->save() in the loop.1 point
-
Yes. I'm not sure how I'll approach it though. It's just an idea at the moment.1 point
-
Awesome! Keep a record of this post for the future module documentation! You mean "Shipping Package" will be the "Calculate Rate" based on the carriers API?1 point
-
1 point
-
Thanks for everything everyone. Much appreciated ?1 point
-
1 point
-
Of course. All ProcessWire API is available everywhere :-). In a function wire('cache') should do the trick. I think there are other variants of that but I never use them myself.1 point
-
I normally use bd($var) and only knew about the difference with d($var) when you pointed it out to me recently :-).1 point
-
I use multi-sites (ProcessWire inbuilt one, aka Option #1), locally and remotely without issues. Locally, I have two main multi-sites; one for ProcessWire 2.7 and the other for ProcessWire 3.x. It means I don't have to mess about with Apache virtual environments for the 'sub-sites'. For local dev, I just need to make sure that my domains are resolved in hosts file. For module development, I have each module as its own sub-site and the domain tells me what ProcessWire (main) version I'm testing on. So, something like: // processwire 2.7 array( 'mediamanager.pw27' => 'site-mediamanager', 'blog.pw27' => 'site-blog', 'runtimemarkup.pw27' => 'site-runtimemarkup', '*' => 'site' // main/mother site, e.g. sandpit.pw27 ); // processwire 3 array( 'mediamanager.pw3' => 'site-mediamanager', 'blog.pw3' => 'site-blog', 'runtimemarkup.pw3' => 'site-runtimemarkup', '*' => 'site' // main/mother site, e.g. sandpit.pw3 ); This shouldn't be an issue if using Option #1 as each site has it's own 'site' folder, e.g. 'site-a', 'site-b', etc. with their own assets and modules. The issue would of course be, if one of the modules is not compatible with the one 'wire' running the site. It means you might have to do away with that module or not upgrade 'wire' ?. I'd just create my own domains using hosts for lookup and have them served locally.1 point
-
Hey @arjen, thanks for the reply! I don't want to use them myself, I just need to make sure I don't included unpublished pages or drafts in my module (see link above). But I've added some additional checks for that purpose now, and I guess the draft module will surely add the correct status for unpublished drafts, so it should be ok. Anyway, thanks for your help!1 point
-
Or maybe something like this by @Pete? You can modify it to check the user's ID or role and log them out when their session comes to an end? I am not sure if ProcessWire currently stores the time a session started? If it does, you can maybe use that instead of PHP $_SESSION1 point
-
1 point
-
Sorry @MarcoPLY I didn't saw your last ping! This is the correct way to call the hook, put it in ready.php as you did then, go to Modules > Refresh if you still can't see the hook. Code : wire()->addHookAfter('Pages::saved', function(HookEvent $event) { // Get the object the event occurred on, if needed $pages = $event->object; // Get values of arguments sent to hook (if needed) $page = $event->arguments(0); // your code: $page = $event->arguments[0]; bd($page); // tracy debug: User page object if ($page->template == "user") { $mc = wire('modules')->get("SubscribeToMailchimp"); $email = wire('user')->email; $subscriber = [ 'FNAME' => wire('user')->pad_firstname, ]; $mc->subscribe($email, $subscriber); bd($mc); // tracy debug: MailChimp object } });1 point
-
Shopify (Lite) is easy to integrate (Buy button or other solution) and can be styled to fit your likings. However, you have to pay fees to use it and also if you use external payment providers. But it looks nice and can be customized.1 point
-
Hi all, Feel like I've been to hell and back :-)! No LAMP environment I set up worked! There was always something going wrong; not being able to connect to host, some PHP file extension missing, some cryptic errors!!! I tried Docker so many times I lost count. I even tried XAMPP and quite a number of other variants! Laradock almost worked but then failed on 'cannot connect to host'. In addition, it took a long time to set up. I don't know how many times I went back and forth with docker! I tried using it directly, I tried using docker clients, nothing worked! I almost installed WINE just to ran laragon! I then stumbled upon this Python (command line) docker app called Stakkr. It works! I'm using it in combination with Portainer and Traefik (both running in Stakkr as services). The Stakkr PHP images include all the extensions I need and then some. Yes, even Imagick! This was a pain to set up when I tried other docker PHP images. I needed a database tool to replace Heidi SQL now that I'm not on windows but still need to connect to remote hosts. I found this wonderful tool, DBeaver. I'm using it both locally (so I ditched phpMyAdmin andAdminer) and for remote connections. On the one hand I feel like I've wasted 2 weeks of dev work! On the other, I learnt quite a lot about docker and some related technologies. I have no more hair left to pull out and I haven't been 'nice' to be around not to mention the writer's block I picked up along the way... Back to work I go. Hopefully, no more technological interruptions...at least for some time ?. @fbg13, Thanks for Solus Plasma! I'd gone with Budgie but then tried plasma and wow! It's what I'm using now. It just works, even the printing. I was able to connect without using CUPS in the browser.1 point
-
Hello @felted, the translated name should show up, if you are on the translated page: $dataProtection = $pages->get("/datenschutz/"); // German on the default page echo $dataProtection->name; // danteschutz // English on the translated page echo $dataProtection->name; // data_protection If that doesn't work somehow, you could force this as mentioned with localName: // German echo $dataProection->localName("default"); // datenschutz // English echo $dataProtection->localName("english"); // data_protection Or you could try Multi-Language Field Values: $page->of(false); // turn off outputFormatting (if it's not already) // German echo $dataProtection->name->getLanguageValue("default"); // datenschutz // German echo $dataProtection->name->getLanguageValue("default"); // data_protection Important is to pass the language you are looking for. ? Regards, Andreas1 point
-
Probably you can make hook before session init, than get user id or role and change config->sessionExpireSeconds1 point
-
1 point
-
Thanks for this module, I am just getting into it - the long way ? I will explain what I needed to hack in order to make this work with my three-container Docker setup. First, I want to ask something: How can I set 'timezone' and 'httpHosts'? I saw in the example kickstartfile you had a numeric ID for Vienna, but that seems very odd (and how to find out the ID)!? I tried with these, but they didn't make it into the config.php 'timezone' => 'Europe/Helsinki' 'httpHosts' => ['0.0.0.0:2015','0.0.0.0','localhost:2015','localhost'] Now some interesting notes from my Docker adventure. These are hacks to the kickstart.php file. - I had to change the checkPHP function to always return true, because it wanted to run 'php -l', but php binary does not exist in my web server container! I can always validate the syntax of my recipe myself, so it's fine. - I learned I need to specify 0.0.0.0 in my Caddyfile instead of localhost, because otherwise curl will get connection refused. This was helpful in debugging: https://blog.kettle.io/debugging-curl-requests-in-php/ Then in the postToPW function I gave the container address: $url = 'http://caddy:2015/install.php'; - because Caddy is not Apache, I always get the Apache mod_rewrite on PW's compatibility check. So I silenced the error: // do all the pw installation steps if($this->postToPW(['step'=>1], ['stepname' => 'Check compatibility', 'errors' => [], 'quiet' => true])) { During the testing phase I benefited from commenting out the zip download and deletion: //$this->downloadAndSave($this->config['pw'], 'pw.zip'); and //if($delete) unlink($zipfile);1 point
-
Visit Pigtail Pundits For a digital agency, finding the time to do your own website is always difficult. It gets marred by other priorities - new projects and ongoing projects. Unless of course, there’s some slack, and you’re alert to leverage it. We were lucky to steal some 30 odd days from other work. In the bargain we revamped our old website into something fresh, complete with updates on our newer solutions and Case Studies. The Thinking Behind the Design & Copy The whole subject of digital marketing is abstract and fuzzy to most clients. Jargon, complicates its understanding further. If we could use a familiar metaphor to describe the digital marketing solutions that we provide along with its benefits, we could perhaps inch closer to getting messages understood. Or so we thought. Equally, we could introduce some flavour into the language that metaphors do allow. Metaphors, by the way, are double-edged swords. It clarifies and also confuses. It tickles your imagination. It can also put people off. Extreme reactions, we felt, are far better than neutral ones. That was a risk that we decided to take knowing the consequences. The theme lends itself to some delightful metaphors which are not part of the usual digital marketing lexicon. The tone is aggressive. The metaphors help in blunting the sharpness. The metaphors color the language and visuals. The overall effect of this is unique. We also attempted to peel off some of the fuzziness that exists in digital marketing, especially in India, using copy. We tried to identify with the pains of the customer and then focus on our solutions as the best answers to the pains. We followed the StoryBrand framework by Donald Miller to craft the copy in the Solutions section. Visit Pigtail Pundits Under the Hood We have been votaries of ProcessWire since 2014 and have always used it on our own and client projects to great benefit. This occasion too was no different for our own website. Under the hood it uses Processwire CMS Bootstrap for theming Form Builder ProCache [gives us a page speed between 2 and 3 secs] Custom PHP for image alt tags SVG icons to reduce the weight and improve page speed. Visit Pigtail Pundits Bouquets and brickbats, welcome.1 point
-
I agree ... more than a preference, I think it's mandatory due to touch screen devices without pointers.. maybe the optional is add or not the hover effect1 point
-
Just pushed a little update that makes the definition of recipes easier, see this sample: <?php $password = $this->randomPassword(); return [ 'pw' =>'https://github.com/processwire/processwire/archive/dev.zip', 'profile' => 'site-default', 'settings' => [ 'timezone' => 368, // vienna 'dbName' => 'yourdbname', 'dbUser' => 'root', 'dbPass' => $this->randomPassword(), 'admin_name' => 'youradminurl', 'username' => 'youradminusername', 'userpass' => $password, 'userpass_confirm' => $password, //'dbTablesAction' => 'remove', // overwrite existing tables? ], 'recipes' => [ function() { $this->msg('Installing RockMockup...'); $this->installModule('RockMockup', 'https://gitlab.com/baumrock/RockMockup/repository/master/archive.zip'); $this->succ('RockMockup installation completed'); }, ], ]; I also added this video to the first post to demonstrate how easy it is to use (I think all the screenshots may have made the impression that it is complicated)!1 point
-
Heads up: after going through all the posts here I still couldn't get german month names to work. Reason for that was the configuration of the output formatting WRONG: date output format j. F Y results in 18. July 2017 RIGHT: date output format %e. %B %G results in 18. Juli 2017 The field description also states »Alternatively, you may use a PHP strftime format if desired for localization.« So I guess RTFM :-/1 point
-
how could i have missed that. $image = $page->images->get("name=picture.jpg"); thanks adrian.1 point