Nov 07 2018
Jay
Nov 07

Determining how content on your site should be cached isn't a simple topic. Last time, I covered cache contexts and tags. Today, I'd like to get into a couple more advanced topics: The use of custom cache tags and of max-age.

Custom Cache Tags

Drupal's built-in selection of cache tags is large, and some contributed modules add additional tags appropriate to what they do, but for really refined control you might want to create your own custom cache tags. This is, surprisingly, quite easy. In fact, you don't have to do anything in particular to create the tag – you just have to start using it and, somewhere in your code, invalidate it:

 \Drupal\Core\Cache\Cache::invalidateTags(['my_module:my_custom_tag']);

 As an example, to continue the scenario from part one about a page showing recent articles, there is one thing about this page that the tags we've already looked at don't quite cover. What if a new article gets created with a publish date that should be shown on your page? Or, maybe an article which isn't currently displayed has its publish date updated, and now it should start showing up? It's impractical to include a node-specific tag for every article that might possibly have to show up on your page, especially since those articles might not exist yet. But we do want the page to update to show new articles when appropriate.

The solution? A custom cache tag. The name of the tag doesn't matter much, but might be something such as my_module:article_date_published. That tag could be added on the page, and it could be invalidated (using the function above) in a node_insert hook for articles and in a node_update anytime that the Date Published field on an article gets changed. This might invalidate the cached version of your page a little more frequently than is strictly necessary (such as when an article's publish date gets changed to something that still isn't recent enough to have it show up on your custom page), but it certainly shouldn't miss any such updates.

This is a simple example of a custom cache tag, but they can be used for many other situations as well. The key is to figure out what conditions your content needs to be invalidated in and then start invalidating an appropriate custom tag when those conditions are met.Ready to get the most out of Drupal?  Schedule a free consultation with an Ashday Drupal Expert. 

Rules for Cache Max-Age

Setting a maximum time for something to be cached is sort of a fallback solution – it's useful in situations where contexts and tags just can't quite accomplish what you need, but should generally be avoided if it can be. As I mentioned in a previous article, a great example of this is content which is shown on your site but which gets retrieved from a remote web service. Your site won't automatically know when the content on the remote site gets updated, but by setting a max-age of 1 hour on your caching of that content, you can be sure your site is never more than an hour out of date. This isn't ideal in cases where you need up-to-the-minute accuracy to the data from the web service, but in most scenarios some amount of potential "delay" in your site updating is perfectly acceptable, and whether that delay can be a full day or as short as a few minutes, caching for that time is better than not caching at all. 

However, there is one big caveat to using max-age: It isn't directly compatible with the Internal Page Cache module that caches entire pages for anonymous users. Cache contexts and tags "bubble up" to the page cache, but max-age doesn't. The Internal Page Cache module just completely ignores the max-age set on any parts of the page. There is an existing issue on Drupal.org about potentially changing this, but until that happens, it's something that you'll want to account for in your cache handling.

For instance, maybe you have a block that you want to have cached for 15 minutes. Setting a max-age on that block will work fine for authenticated users, but the Internal Page Cache will ignore this setting and, essentially, cause the block to be cached permanently on any page it gets shown on to an anonymous user. That probably isn't what you actually want it to do.

You have a few options in this case.

First, you could choose to not cache the pages containing that block at all (using the "kill switch" noted in part one). This means you wouldn't get any benefit from using max-age, and would in fact negate all caching on that page, but it would guarantee that your content wouldn't get out of date. As with any use of the "kill switch," however, this should be a last resort.

Second, you could turn off the Internal Page Cache module. Unfortunately, it doesn't seem to be possible to disable it on a page-by-page basis (if you know a way, please drop us a line and we'll update this post), but if most of your pages need to use a max-age, this may be a decent option. Even with this module disabled, the Internal Dynamic Page Cache will cache the individual pieces of your page and give you some caching benefits for anonymous users, even if it can't do as much as both modules together.

My preferred option for this is actually to not use a max-age at all and to instead create a custom, time-based cache tag. For instance, instead of setting a max-age of 1 hour, you might create a custom cache tag of "time:hourly", and then set up a cron task to invalidate that tag every hour. This isn't quite the same as a max-age (a max-age would expire 1 hour after the content gets cached, while this tag would be invalidated every hour on the hour) but the caching benefits end up being similar, and it works for anonymous users.

Up Next

Now that we've gotten an overview of how to determine what rules you should use to cache content on your site, it's time to get a little bit more technical. Next time, I'll be taking a look at how Drupal stores and retrieves cached data, which can be immensely useful to understanding why the cache works the way it does, and it's also quite helpful to know when fixing any caching-related bugs you might encounter. Watch this blog for all the details!

Offer for a free consultation with an Ashday expert

Oct 31 2018
Jay
Oct 31

Leveraging Drupal's cache effectively can be challenging at first, but the benefits for your site's performance make it well worth the effort. It all starts with figuring out what sort of rules your site should use to cache its content. The improved page load times that come from properly handling caching rules can help improve SEO and are generally more appealing to those that visit the website. In the right hands, the techniques outlined in this article can get a website into a much more specialized caching system.

I previously covered the fundamentals of how caching works in Drupal 8, including what the two core caching modules do and what cache tags, contexts, and max-age are for. If you're familiar with those things, then this post is for you; otherwise, check out my previous article and get up to speed before we dive into a slightly more in-depth topic: Figuring out how you should set up caching on your site.

If you're using a simple Drupal installation with no custom code and with well-maintained contributed modules, Drupal's Internal Page Cache and Dynamic Internal Page Cache modules will likely cover your caching needs. This article focuses on some more complex and custom scenarios which, nonetheless, come up with some frequency. 

The Guiding Principle

Perhaps the most frequent issue custom code has when it comes to caching is that it doesn't account for caching at all. This isn't ideal if you want to take advantage of Drupal's caching system to optimize your site's speed, and it points to one principle which can be tricky to learn and is critical to master: If you write custom code, always think about its caching implications. Always. 

Often, the implications will be minimal, if there are any at all. It's important not to assume that every bit of custom code will cache perfectly on its own - that's a mistake that could lead to something being cached either for too long or not at all. 

When to Disable the Cache 

Most everything Drupal renders as output (to a web browser, to a RESTful web service, etc.) can be cached. However, sometimes the development time to ensure that your custom code handles caching precisely outweighs the performance benefits that caching might provide. So the first question when you're writing custom code is, should this be cached at all?

Ready to get the most out of Drupal?  Schedule a free consultation with an Ashday Drupal Expert. 

If you're building something like an administrative page that only a few users with special permissions will ever see, it may not be worth the time and effort to make sure it is cached perfectly, especially if the rules for doing so would be complicated. For these scenarios, Drupal has a "page cache kill switch" that can be triggered in code:

 \Drupal::service('page_cache_kill_switch')->trigger();

Calling the kill switch will stop both the page cache and the dynamic page cache from doing any caching on that page.

This should be used with caution though, and only as a last resort in situations where figuring out proper caching logic isn't worth the time. It should never be used on a page which is expected to be viewed by a large number of your website's visitors.

Rules for Cache Contexts

You should consider using a cache context if your content should look different when displayed in different situations. Let's look at a couple scenarios that benefit from using a cache context:

Say you have a site which can be accessed from two different domains and you want to display something a little different depending on which domain someone is looking at. Perhaps the site's logo and tagline change a little. In this case, the block containing the logo and tagline should be given the url.site context. With this context in place, Drupal will cache a separate version of the block for each domain and will show each domain's visitors the appropriate one.

Or, perhaps a block contains a bit of information about which content the currently logged-in user has permission to edit. This sounds like an excellent case for using the user.permissions context to indicate to Drupal that the block is different for each possible combination of permissions that a user might have. If two users have the same permission, the same cached version can be used for both of them.

There are many other contexts are available as well; take a look at the full list to see if one or more of them is applicable to your code.

Rules for Cache Tags 

Cache tags are probably the most important caching mechanism available to custom code. Drupal includes countless cache tags which can be used to invalidate a cache entry when something about your site changes, and it is also very easy to create your own cache tags (which we'll get to in a minute). For now, I'm going to focus on some of the cache tags Drupal has by default.

Say you're creating a page which shows the top five most recently published articles on your site. Now, Drupal sites can often make use of the Views module for this sort of thing, but depending on your exact requirements Views may not be the best approach – for instance, maybe part of the content has to come from a remote service that Views can't readily integrate with. The most obvious tags needed for this page are the tags for the specific pieces of content that are being shown, which are tags in the format of node:<nid>, for instance, node:5 and node:38. With these tags in place, whenever the content gets updated, the cache entry for your page gets invalidated, and the page will be built from scratch with the update information the next time somebody views it.

But that's not all there is to think about. Perhaps this page also shows what categories (using a taxonomy structure) each article is in. Now, the articles each have an entity reference field to their categories, so if a user changes what categories the article is in, the relevant node:<nid> tags already added to your page will get cleared. Easy enough. But what if somebody changes the name of the category? That involves editing a taxonomy term, not the article node, so it won't clear any node:<nid> tags. To handle this situation, you'd want to have appropriate taxonomy_term:<id> tags. If an article with ID 6 has terms with IDs 14 and 17, the tags you'd want are node:6, taxonomy_term:14, and taxonomy_term:17, and you'll want to do this for every article shown on your page.

Fortunately, most of the time, you don't need to worry about the specific tag names. Nodes, terms, and other cacheable objects have a getCacheTags() method that gets exactly whatever tags you should use for that object.

These are all simple entity-based tags, but there are many more available as well. There are tags for when various aspects of Drupal configuration changes as well as for things such as when certain theme settings get changed. Unfortunately, since the available cache tags vary from site to site, there isn't a ready-made list of them available for you to use as a reference. You can, however, look at the "cachetags" table in your Drupal database to see a list of all the tags that have been invalidated at least once on the site. This will be pretty minimal if your site is brand-new, but as people use the site it will start filling up.

The basic idea of tags is this: If you render something on a page, and there's a chance that something displayed on it might change in the future, there should be an appropriate tag in place to watch for that change.

Up Next

This is a big topic, but it looks like we're out of time for today. Next time, we'll delve a bit deeper into cache tags by seeing how to create custom ones that perfectly fit your site's needs and will also cover how to use max-age, including one important gotcha that makes them more complicated than they look. You can check that one out here.

Offer for a free consultation with an Ashday expert

Oct 03 2018
Jay
Oct 03

drupal 8 logo in spotlights

Although it can sometimes be easy to forget about that little URL bar at the top of your browser, what it contains for each page can be surprisingly important. Creating good paths (the part of the URL after the domain name) for your site content can have numerous benefits, especially for SEO, but doing so can be a bit frustrating. Usually, you end up needing to re-type most of the page title into Drupal's URL alias field, which isn't necessarily too bad on its own, but it's still an extra step whenever you create content. And what about when you update the page and change the title? This is where it gets very easy to forget to change the path, which can lead not only to worse SEO but also to your site's visitors not ending up on a page with the content they expected it to have.

Fortunately, the Pathauto module makes all such worries a thing of the past.

This module adds a whole host of new options to how you can configure Drupal's paths, perhaps the most important of which is path "patterns". Patterns allow you specify what you want your paths to look like for different pieces of content; for instance, if you have a site where each of its users can have a blog, you could set up a pattern for Blog nodes such as "/blog/[node:author]/[node:title]". Then, whenever a user creates a blog post, it will automatically be given an alias which includes their username and the title of the post, and it will even update the alias if the user edits the post to change its title.

If you already have a site which has inconsistent aliases, Pathauto can also be used to bulk generate new aliases for your content, which is also quite helpful if you ever decide you want to change the structure of your paths. The module also offers plenty of settings to configure exactly what the paths it generates look like, including the ability to use a wide range of tokens provided by the Tokens module.

Finally, Pathauto also integrates with the excellent Redirect module. If you have both modules installed, changing the title of your content won't just change the path, it will also create a redirect from the old path to the new one so that any links already out there to the old URL will continue to function as expected. This is a rather important bit of functionality, and thanks to Drupal's great selection of contrib modules, it's just a few clicks away from being set up and working on your site.

New Call-to-action

Sep 28 2018
Jay
Sep 28

Previously, we covered some simple tips that allow you to get more out of Drupal and I think we covered some basics. This time we are going to go a bit deeper to see what Drupal can really do. In the right hands, Drupal can be a very powerful tool for more than just content management. The following tips will take you through a few different topics to get more out of Drupal than ever before. Some of these tips are a bit more on the advanced side, but they are very useful.

Tip #1: Don't be afraid of caching

If you are at all familiar with website caching, then you know at least two things about it. It is useful for getting your pages to load faster, and it can be very complex. Caching is meant to speed up your site by putting your page together just one time and then simply redisplaying that rather than needing to build it anew every time. However, because of this, when something about how that content should display changes, that particular entry in the cache needs to be "invalidated" so that it doesn't continue to be used, which could result in it showing content that is no longer current.

Fortunately, Drupal 8 has a fantastic caching system, provided by two modules which are included in Core: Internal Page Cache and Internal Dynamic Page Cache. The former caches entire pages for users who aren't logged in, while the latter caches the individual components of pages (such as blocks and rendered nodes) for all users. On most Drupal sites, these should both be turned on. Drupal Core and most contributed modules are built with this caching already in mind, so it's easy enough to just turn on the modules and get a nice performance boost from doing so.

If you have lots of custom code which deals with how content renders, this may not be quite so simple, but it is still something worth looking into, especially if your site sometimes feels a bit slow.

Ready to get the most out of Drupal?  Schedule a free consultation with an Ashday Drupal Expert. 

Tip #2: Remove modules you don't need

Drupal, by default, usually comes with a whole suite of modules installed, some of which not every site needs. These include, for example, the Tour module (for creating tutorial-style interfaces which highlight certain parts of your site in) and the Search module (which provides Drupal's default searching mechanism and is useful only when your site doesn't warrant a different search solution). If you don't actually need a module, uninstall it, and if it is a contrib module rather than a core module, you can then remove it from your site's code entirely.

Every unnecessary module you have on your site can add clutter to the admin UI which makes it harder to find the things you actually want, and since each module can have its own potential security risks, uninstalling the ones you don't need can even help improve your site's security and stability.

Other core modules which are good candidates to consider removing are CKEditor (for sites which don't need WYSIWYG content), Color (for when you're using a custom theme and don't need to change its appearance through the UI), and Comment (if your site doesn't allow users to comment on content anyway). And that's just core modules in the C's!

Just be careful not to uninstall modules such as the Internal Page Cache module, which may not be specifically required to provide the site's intended functionality but which are important for keeping your site working smoothly. Consider each enabled module individually (and then, do the same with enabled themes!) 

Tip #3: Use the latest version of PHP

Drupal is written in PHP and is designed to take advantage of the new features and performance improvements provided by its latest versions. Although Drupal 8 can run on PHP versions as old as 5.5, it is now optimized for and fully compatible with PHP 7.2, and so a simple PHP version update can be a great benefit for your site's speed and reliability. You can check which PHP version your site is on from Drupal's Status report, and your hosting provider should provide a way to upgrade PHP if necessary.

Important: Versions of Drupal prior to 8.5 are only compatible up to PHP 7.1. If you're on Drupal 8.4 or older, you should be sure to update Drupal (which is an important thing to do anyway to get all of its latest features, bug fixes, and security updates) prior to switching to the new version of PHP.

Tip #4: Manage config the Drupal way

We've spoken a bit before about configuration management in Drupal 8, but it's important enough it's worth mentioning again. Back in the Drupal 7 and earlier versions, deploying changes to a site typically involved recreating a whole bunch of "clicks" in the user interface, to arrange fields and blocks, API settings, user roles and permissions, and pretty much any other aspect of the site's configuration. Drupal 8 makes that a whole lot simpler with its configuration management system. Although managing config can get quite complex for some sites, for most it is simple. Once you've made a bunch of changes on your development site that you want to roll out to live, you can export those changes into a zipped collection of YAML files. You can then upload that zipped file directly to your live site to import the changes, or save the YAML files into your codebase and roll them out alongside your code changes. We prefer to use the latter method since it also has the benefit of keeping your config in your version control system, but either method works fine, and a direct upload of config can be a bit simpler to manage.

Tip #5: Join the community

Drupal is open source software and is built by a large community of developers and designers from across the world, and joining that community by signing up for an account at drupal.org can result in some tangible benefits for your site. One easy benefit of this is that if you come across a bug in Drupal or one of the contributed modules you are using, or even if you just find that some feature you'd like it to have is missing, you can post a message in the drupal.org issue queues to get a discussion started with the very people who can make the sort of improvements you want. Often, if you search the issue queues, you may even find that somebody else has thought of the same thing you have, and there might even be a patch already available to give you the functionality you want. 

If you've had to create any custom modules or themes for your site, and they may be the sort of thing other people may find useful as well, it may be good to consider contributing them. If other people start looking at and using your custom modules, they may find ways to improve it and may even submit patches to fix bugs or add new features. Then it's easy to update your module with their recommendations, making it even more useful both for you and for the rest of the Drupal community.

There you have it. Five more tips to get the most out of Drupal. Some of these might seem obvious, but they are some big wins you can make for yourself and your website. We’ve been working with Drupal long enough that some of these seem like second nature and in time they may also be that way for you. Stay tuned for more Drupal tips in the future!

Offer for a free one-hour consultation, make you next project a success

Aug 29 2018
Jay
Aug 29

Drupal, especially once you consider the many contributed modules available for it, is a vast system of open source software, and as with most such software, there are a lot of little things you can do to make sure you get the most out of what it has to offer. In this post, I'm going to go over a few such things and touch on how to make Drupal's admin interface more useful while also finding ways to improve site performance and stability.

Tip #1: Clean up your content forms with Field Group

It can be pretty easy to end up with a content type (or another type of entity) that has a whole lot of settings on it, and even if you organize the fields into a logical order, it can make the content forms a bit difficult to navigate. On most any somewhat content-heavy site, Field Group is one of the first modules we install. It allows you to group fields together in various ways, such as with a tabbed interface or collapsible fieldsets, and with it, you can have your forms better organized in no time. Your content editors will thank you.

Tip #2: Have a cup of Coffee (and Admin Toolbar) 

 

When it comes to making it easy to find what you're looking for in Drupal's admin UI, few modules compare to Coffee and Admin Toolbar (including its Extra Tools submodule).

Coffee adds a "Go to" button to the admin bar which, when clicked, opens up a search bar similar to the Spotlight Search on a Mac. Then you can just start typing to find whatever admin page you might be looking for. For those of you who prefer using the keyboard over the mouse, there's also a keyboard shortcut to open the search.

The Admin Toolbar module modifies Drupal's existing toolbar to use a series of dropdown menus, making it easy to find the right page without needing go click through a bunch of screens to get to it, and it also adds some quick links to do common development tasks such as flushing caches and running cron.

Since Coffee and Admin Toolbar both are designed to make it easier to get where you want to go in Drupal, you may only need one of them, but it's worth giving both a try to see what makes your workflow fastest. 

Ready to get the most out of Drupal?  Schedule a free consultation with an Ashday Drupal Expert. 

Tip #3: Send emails your users can trust

Many sites have to send out emails of some sort – whether it's just for simple password resets or a more critical part of a site's functionality. Drupal can send emails on its own easily enough, but unfortunately, (especially if your site is on a shared hosting environment), there's a decent chance that the email may be automatically get marked as spam. There's an easy fix for this: First, you set up an account with an email delivery service such as Sendgrid or MailGun, then you add the SMTP Authentication Support module to your Drupal and configure it with the credentials provided by that mail service. And ta-da – now your site's emails are all delivered by a trusted service. No more ending up in the spam folder!

Tip #4: Limit the size of your images

Large images can take a long time for your site's visitors to load, especially if they're on a slower network. If they're too large, this can result in your visitors seeing blank space while the image is still downloading. To avoid this, ideally, images should be exactly as large as they need to be, and no larger. There are several great ways to accomplish this without needing to load up Photoshop for every image.

First, before even uploading your image to Drupal, use a tool such as Compressor.io to compress your image. This step is especially important for most JPEG images and photographs.

Within Drupal itself, there are a couple of possibilities available to you. If you are uploading your images to the image field of nodes, you can set restrictions on the field itself, such as maximum dimensions for the uploaded file (and Drupal can even resize the image to fit if you upload one that is too large). You should also set an appropriate image style for the image wherever you use it, which can include things like automatic cropping and centering.

Tip #5: Update often

New versions of Drupal and its many contributed modules are being released all the time, often bringing with them stability improvements as well as brand-new features. For Drupal Core and most contrib modules, each individual update is easy to apply (especially if you use Composer), but the further out of date your site is the trickier updating it can be, so keeping on top of updates means that updating stays simple. 

One other critical reason to keep up to date is that once in a while there are security updates necessary for Drupal, and if you're already on the latest version applying the security update is a fast and painless process. You don't want to end up delaying security fixes to the site because of difficulties in upgrading several versions at once. If you have a drupal.org account, it's worthwhile to go to your user account page and subscribe to their security notifications email newsletter so that you get alerted as soon as these important updates are released.

New Call-to-action

Jul 25 2018
Jay
Jul 25

 image of spotlights focused on drupal logo

It is essential that content be well-categorized, especially on large websites. Drupal includes the Taxonomy module for doing just this, and it is able to account for most content tagging scenarios. However, when you actually go to categorize content, it can be a bit confusing.

Drupal offers a helpful autocomplete, but this doesn't always work as well as you might hope if you have a taxonomy tree which includes nested terms (for instance, "Cats" as a subtopic of "Pets"). In fact, the autocomplete doesn't show the hierarchy of these terms at all, which can get confusing if you have multiple terms with the same (or similar) names which are children of different terms. And although Drupal allows you to create new terms right from the content editing page, doing so always causes it to create the new term at the topmost level of the taxonomy, which often isn't where you actually want it.

What is Straw?

Enter Straw - the Super Term Reference Autocomplete Widget. We built Straw when we were working with a multilevel taxonomy and found that Drupal's default widgets just weren't cutting it. Now, we've made Straw available as a module for anyone else who might find themselves in a similar situation. Straw does two main things which make working with these taxonomies a breeze.

Find Matching Content Tags

First, both when displaying a selected term on the edit page and when searching to find matching tags based on what the editor is typing, the Straw widget shows the entire hierarchy of terms. That means that instead of simply showing "Cats", it will show "Pets >> Cats", and if you start typing "Pets" it will show both "Pets" and "Pets >> Cats", rather than only showing "Pets" like Drupal's default autocomplete would. This makes it much easier to see which exact term is being matched, and also makes it easier to find sub-terms if you don't quite remember their names.

screen shot of Drupal Straw Widget

Create New Terms

Straw makes it a breeze to create new terms on the fly. Say you're writing a new article for your site about a particular breed of dog, but so far your taxonomy only has terms related to cats. Without Straw, you could type in "Beagles" and it would create a new term of that name, but then you'd have to go to your taxonomy tree and create a "Dogs" term beneath the "Pets" term and then move "Beagles" into it. With Straw, this is much simpler… you just type in "Pets >> Dogs >> Beagles", and it creates all the necessary terms and puts them in the right place in the taxonomy relative to each other.

With Straw, tagging your content can be easier than ever before. It's easy to install (with only a little bit of configuration required) and then you'll be able to see right away how much of a difference it makes.

Offer for a free consultation with an Ashday expert

Jul 18 2018
Jay
Jul 18

drupal 8 logo featured alongside drupal logos showing the growth of Drupal

Now that Drupal 8 has gained some momentum, it is time to start planning out your upgrade strategy. You want to upgrade to get the latest benefits and take advantage of the future stability that comes with the direction that Drupal will be taking from here on out. Before upgrading you will want to consider some things about what your current site has. In this article we will be covering some of those questions with some context to assist in the decision making process. Let’s determine if you website is adequately serving the current needs of your business and which content will need to be brought over to the new Drupal 8 site. There may be a difficulty in the switch, but being prepared will put you in position to handle whatever comes up.

So, you have a nice Drupal 7 site that's been running happily for years, and maybe you're thinking it's time to upgrade – or, if you haven't given it much thought yet, there's certainly plenty of reasons to do so now. But, Drupal 8 is a pretty big step forward from Drupal 7. What exactly is an upgrade going to entail? What do you need to consider before you do so? We've upgraded numerous sites now, and today I'd like to go over a few things that I think are important to know before starting on such a project.

Before You Start

Just because you've decided to upgrade, doesn't mean that you can do it right away and be done with it by dinner. Unfortunately, there isn't any one-click solution to take your site from Drupal 7 to Drupal 8 (although future upgrades should be closer to reaching that level of simplicity). Here are a few things to consider before you touch the first line of code or content:

Do you still want the same site? Doing a large upgrade such as this is an excellent time to re-evaluate your site's functionality and appearance. Now that you've had it for some time, is there anything you want to change? Any new features you want to add that maybe weren't feasible before because of how the old site was built? Although upgrading can be done in such a way that everything stays almost exactly the same for your site's visitors, it's good to at least consider what potential improvements you could make, perhaps by leveraging some of Drupal 8's great new features. Maybe your site just needs a facelift, or has some long-standing bugs you want to fix, or you want to take advantage of new CSS or JavaScript technologies which weren't available when you first built the site. Upgrading is the perfect time to take a fresh look at things.

How are you migrating? One critical part of most upgrades is to migrate content and user information from the old site to the new one. Databases for Drupal 8 are structured differently than they were in Drupal 7, which means that to upgrade the site you can't just swap out the code while keeping the same database… you have to actually move the content into a new database as well and adjust it to fit Drupal 8's slightly different structure. This is a pretty big departure from how Drupal upgrades worked in the past.

There are two main ways to do this migration. Drupal 8 includes a Migrate API, which is perfect for some simple sites. However, not all contributed modules have the plugins necessary to migrate their data in this way, and any data you might be storing in some customized way certainly doesn't. Although it is possible to write your own plugins, in our experience it's easier to take a different approach by writing an entirely custom migration. This does however require having some more familiarity with Drupal coding than the Migrate API route does. Generally, what a custom migration involves is writing a script and appropriate queries to first retrieve your information from the old database, and then to write it to the new Drupal 8 database in the appropriate format.

Do you know enough Drupal 8? If your site is mostly built using core and contributed modules and themes, you'll probably be good to go. Drupal 8's admin UI does have some changes from Drupal 7's, but none of them are very difficult to get used to and most introduce new ways to navigate and improve your site. However, if your site uses much in the way of custom modules or if it has a custom theme, then before upgrading you should make sure your developers and themers are familiar with Drupal 8, perhaps by working on some other simple Drupal 8 project first or by getting some training on the changes, either in-person or through an online course. The changes in how you code custom modules and themes in Drupal 8 are significant, so they may take some getting used to, but ultimately they are the foundation of what makes Drupal 8 leaps and bounds better than its predecessor. Knowing how to use them effectively is key to having a Drupal 8 site that continues to be maintainable for years to come.

Still Not Sure You're Ready to Upgrade?  Learn more about the benefits and our upgrade process. 

What to Expect

Alright, so you've gotten everything figured out. All that's left to do now is to, well, actually upgrade the site. Here are a couple of things you should plan for when starting most any upgrade.

Expect to have a few difficulties: Even if the upgrade seems like it will be a simple one, you should go into it expecting to have a few unexpected challenges come up, because they almost certainly will. Maybe it's one particular type of field that doesn't have the same settings in 8 as it does in 7. Maybe it's a combination of contrib modules which used to work together one way and don't anymore (one example: The Context module can no longer be used in conjunction with Metatags to set metadata in contextual circumstances). Maybe it's that your site uses a lot of different webforms, and although the submission data can get migrated without too much trouble, recreating the forms themselves can't be done automatically. 

Regardless of what it ends up being, plan to have some unexpected difficulties during the upgrade. In my experience, these challenges tend to be fairly easy to fix or work around through some combination of contributed modules and custom code, so although they can add some complexity to the upgrade it's rare to find a dealbreaker so late in the process.

Expect to have two sites for a little while: Your new Drupal 8 site is completely separate from your old Drupal 7 site, and that means that it needs to be installed somewhere other than where your old site is, and it probably has to have its own domain so you can access it during the upgrade process. You'll be able to keep your old domain though - once the upgrade is done and your Drupal 8 site is ready to go, all you should have to do is change your main domain's settings to point to the new website rather than the old one.

When you first start the upgrade, your Drupal 8 site is likely to be almost empty. If you have lots of custom modules to recreate or anything and the migration itself isn't done yet, then maybe it has some placeholder content to start with. Then, once the migration is done, all the relevant content from the old site gets migrated into the new one. At this point, your content and user information is being stored in both sites. It also means that, from now until the new version of the site launches, any changes you make to the content of the old site won't be automatically reflected in the new site and will be lost when you deploy the upgrade. For many sites, this problem can be mitigated by having a period of time in which changes get made to both sites, instead of just to one of them.

Still, it isn't ideal to have to enter new content twice for an extended period of time, so to solve this, one thing we like to do is to actually have two migrations. The first migration happens early on in the development of the Drupal 8 site, and we use the content from that migration while finishing most of the upgrade. Then, shortly before the upgrade goes live, we do a "final migration" to get any changes which have been made on the Drupal 7 site since the original migration. If all goes well, this results in only a day or two (or zero!) during which changes have to be made in both sites.

Once the upgrade is finished and your domain name settings adjusted so that the new site is live, it's worthwhile to do one last check of the old site to see if there are any recent changes or user registrations that may have been missed. Depending on how your migration script works, these could be migrated again with that, or they could be replicated manually. Once that's done though, huzzah! Your newly upgraded Drupal 8 site is finally ready for the world to see.

 Offer for a free consultation with an Ashday expert

Jun 22 2018
Jay
Jun 22

One of the many things Drupal excels at is integrating with other services. Some popular integrations are made even easier by the existence of contributed Drupal modules (such as the one for Google Analytics). But, many times, there isn't a ready-built solution, or the one that's available doesn't quite suit your needs. At Ashday, we've built many integrations between Drupal and other systems, and although every integration is different, there are some things we've learned that are good to consider when writing most any integration.

Do You Need to Build Something New?

Even if there is an existing Drupal module for the integration that you need, it's important to determine if you actually want to use it. Sometimes, integration modules are written for certain use cases, which may not line up with exactly what you need the integration to do, and could in fact make things more complicated for you than not using a pre-existing module at all.

For instance, when we created a Solr integration, we had to evaluate whether to use the existing Search API Solr module or write our own integration, and in the end, we decided to write our own. Why? Because when we were evaluating our options, we found that the module wasn't designed to index the content of our site in the particular way we needed it to, and it also didn't quite properly support the "More Like This"-style search which Solr itself is capable of. Because of this, we only had a few options.

  • We could modify our site to fit the capabilities of the module, but most of the time, as in our case, that isn't really an option.
  • We could use the module and try to adapt it to our needs. This would mean not only installing the module and figuring out how to use it, but also digging into how it was written in order to make changes to it. With some modules, such as Search API Solr, this can be a pretty big task. It's a large, complicated module, and figuring out where and how to make the necessary changes can be a challenge… and that's assuming that the module is structured in such a way that the changes are easy to make.
  • We could write an entirely custom integration.

We wound up going with the third option. "But wait," you might wonder, "If the existing module is so complicated, wouldn't writing your own integration mean needing to create all of that same stuff yourself?"

And the answer to that is no. Whereas a Drupal contrib module usually has to be designed in a rather generic way, so that it can suit as many people's disparate needs as it possibly can, an entirely custom-coded integration can focus solely on the functionality that is needed for your particular use case. Just because the system you're integrating with supports something, doesn't mean you necessarily have to. With the Solr integration in particular, building exactly what we needed as a custom integration wound up being much faster and more maintainable than it probably would have been to take the existing module and modify it to suit our needs.

How Much Drupal Should You Use?

Depending on what sort of integration you're writing, one thing to consider is how much it should rely on Drupal. Drupal includes numerous powerful tools, such as its service architecture and Form API, which can be leveraged to make your integration more flexible, and even save time in the process. At the same time, tying your integration to Drupal makes it less portable. For instance, if you were creating an integration which would display content from a remote system on several different websites, and only some of those sites actually use Drupal, then it may be worth creating the integration mostly using regular ol' PHP. By creating the integration as a set of classes or functions that don't depend on Drupal, each site can then use those same functions, and add just the few things that are needed to make them work well on that site; for instance, a Drupal site may need routes and permissions set up "the Drupal way" separate from the reusable part of the integration.

Creating a more generic integration in this way can also make it more compatible between different versions of Drupal. If your main integration doesn't rely on Drupal, it will work on Drupal 7, 8, 9, and even more, with only the minimal code to make it actually work on each site needing to be changed between them.

Another example: Say you're integrating with an analytics tool. You could either create a complex configuration form that fits into the Drupal administration interface and uses Drupal's form API, config system, and permissions, or you could just hardcode the specific settings you need somewhere in your codebase. The former way is the more "Drupal" way of doing it and is more configurable (this is the approach taken by the Google Analytics module), but the latter method can be very fast to code, and if you only rarely need to change anything about your configuration, it can be a great time savings over creating a complex Drupal-specific integration module.

Can This Be Contributed?

If your integration ultimately does make use of Drupal's helpful built-in features, great! Now it's time to ask "Can I contribute this module so that other other people can use it on their own Drupal sites?" If you are integrating with some tool or remote system that other people might also want to integrate with in a similar fashion, then the answer should probably be yes.

Contributing your custom integration module to Drupal.org can have a number of benefits—not the least of which is that it is an excellent way to give back to the Drupal community as a way to say "thank you" for all they've done to make Drupal possible. But there's another benefit as well: If you contribute a custom integration module, that means that other developers may start using it, looking at the code, and making improvements to it. Maybe your integration is fairly small, and only accounts for the handful of use cases that you need. The next person who uses the module thinks that something could be added or changed to improve the module. Suddenly, you have other people making suggestions or even writing code for you, which you can then benefit from by including such changes in the integration you're already using.

This is what we did with our HelloSign module for creating eSignature requests; we built it for one site which needed such functionality, then we contributed it. Once we did so, other developers started looking at the module and helping us to make all sorts of improvements to the module, and some even submitted patches to help improve the module. We also got free t-shirts out of it, so hey, who knows what might happen when you contribute!

Conclusion

There are many things to think about when you're building a Drupal integration, and although I've only written about three of them today, I think they are the guiding questions which help to determine how your integration is built. If you can answer all three of these questions before getting too far into writing your integration, you're much more likely to end up with a system in place that will serve you well long-term, even if the exact requirements for the integration eventually changes—which, in my experience, they almost certainly well.

If you need to integrate Drupal with anything, talk to us! We can integrate just about anything with Drupal

Jun 01 2018
Jay
Jun 01

Illustration of a person signing a document electronically.

Previously, I wrote a bit about the HelloSign eSignature platform and how it can be integrated into a Drupal 7 website. As promised, a Drupal 8 version of the integration is now available and ready for use on cutting-edge websites everywhere. But this new version is much more than a one-to-one upgrade of the original module— we've leveraged some of Drupal 8's great new features to make using HelloSign with your site even easier than it was before. Here are just some of the highlights of the new release:

Composing Your Site

Installing the old HelloSign module could be a bit of a pain. In addition to needing to download the HelloSign module for Drupal, it was also necessary to download a separate HelloSign PHP SDK and put it in the right place in your site's code. Now, the HelloSign module can be installed easily using Composer. Instead of downloading two files from two different sites, unzipping them, and carefully making sure to put them in the right place in your site code, all you have to do is run one simple Composer command:

composer require drupal/hellosign

That one command will install the module in the right place, and it also installs all of the module's dependencies, including the HelloSign PHP SDK and the Encryption module (more on that in a moment), making it even easier to get started with HelloSign in Drupal 8 than it was in Drupal 7.

Need to Integrate Drupal with HelloSign, or with Anything Else?  Request your free consultation with an Ashday Drupal integrations expert today. 

Security by Default

Many Drupal modules are available which, like the HelloSign module, help make it simpler to integrate with other services. One thing common to most such modules is that they add a configuration form on the Drupal site, which administrators can use to set up the integration by entering essential information such as credentials and API keys. The HelloSign module is no different; integrating with HelloSign requires the use of an API Key and a Client ID, and the module provides an easy way to set these (and other useful settings) through the administration interface.

One thing that often gets overlooked by integration modules, however, is the security of this saved information. Once the API key is configured, it is stored as plain text both in the database and (if you are using Drupal 8's configuration management feature) in the code of the site. Unfortunately, that means that if any ill-intentioned person somehow gained access to most any part of your site, they'd have access to your secret API key.

To mitigate this common issue, the HelloSign module in Drupal 8 makes use of the Encryption module to store this private information in a secure, encrypted format, decrypting it only when necessary to interact with the HelloSign API. This feature is on and enabled by default and, in fact, can't be disabled, because having such an option would make for inherently weaker security.

Built for the Modern Web

One issue with Drupal 7 was that it was not, on the whole, built with modern object-oriented PHP coding practices, and the modules built for Drupal 7 often followed suit. Drupal 8, however, is a whole new object-oriented world, and that's as true of the HelloSign module as it is of Drupal Core.

The Drupal 7 version of the HelloSign module had most of its features buried in a number of disparate functions, many of which were ultimately just wrappers for functionality made available in an object-oriented fashion by the HelloSign PHP SDK. For Drupal 8, we've simplified this. The HelloSign module provides a

service which, in addition to allowing the creation of signature requests, also provides direct access to the rest of the HelloSign API. Instead of making assumptions about what bits and pieces of the API's many features a site might need, now, all of it is available. The service also automatically establishes the necessary connection info, so that your developers never have to worry about how to connect to HelloSign.

In Conclusion

The HelloSign Module for Drupal 8 has all these improvements (the use of composer, enhanced security, and a modern object-oriented design) and many more, including vastly expanded documentation and more granular permission handling. We hope that it will be of use to you on your next Drupal project which needs to support eSignatures.New Call-to-action

May 18 2018
Jay
May 18

Illustration of finger selecting 5 stars

Drupal 8 has been available now for more than two years, but if your site is up and running on Drupal 6 or 7, you may be wondering… why should I upgrade? And why now?

Well, if you're on Drupal 6, the answer is easy: Drupal 6 is no longer an officially supported platform, which means that in addition to not getting the latest and greatest Drupal features, your site may be vulnerable to serious security issues and you will have to find backports of the latest security patches from the community or attempt to create your own.

But if you're already on Drupal 7, then security updates and occasional small new features are still being made available for your version. So… why upgrade? Well, I can think of five great reasons to do so, and for why now is a better time than ever.

Contrib Is Ready

One common hesitation about upgrading to Drupal 8 has been that many key contributed modules from Drupal 7 were not yet upgraded, and many sites depend on having such modules. It's taken a little while, but especially over the past year we've seen a huge increase in the number of modules ready for use on Drupal 8… and many of them are even better than they were on Drupal 7. Consider, for instance, the popular Webform module. Not only is it available for Drupal 8, but it's been rebuilt and redesigned from the ground up. The interface for managing your webforms is much more intuitive than it used to be, while also having many more options. At this point, if there's some feature you want your form to have, then Webform can probably handle it.

Of course, Webform isn't the only major module that's ready. Popular modules such as Drupal Commerce, Domain Access, Paragraphs, and Metatag are ready to go. And let's not forget that many vital Drupal 7 contributed modules, such as Views and Media, are now included in Drupal 8 by default, so you don't even have to install anything extra to use them.

Considering an Upgrade to Drupal 8?  Claim your free consultation with a Drupal expert!

Easier Migrations Than Ever Before

One thing that may have had some people holding off on upgrading to Drupal 8 was that Drupal's new migration features weren't quite ready, and writing a custom data migration or working with a buggy migration tool can be quite a challenge. Well no more! The Drupal Migrate module is finally stable. This module is included with Drupal and is designed to make it as easy as possible to move all of your content from a Drupal 6 or 7 site to your shiny new Drupal 8 site.

One caveat: If your site is multilingual, then you might have to wait another version or two for the last few bugs with multilingual migrations to get worked out. But, that doesn't mean you have to hold off on building your new Drupal 8 site! Now would be a great time to get the new site built, and once the multilingual migration bugs are fixed, moving your content over at that point should be a comparatively trivial task.

New Features in Drupal Core

Using the most recent version of Drupal means that your site can easily start making use of the many new features that are always being added to Drupal core. For instance, Drupal 8.5 includes a whole host of new features, such as the Media module being included by default, a new Layout Builder module, and enhanced interfaces and settings for managing content and site configuration… and that's not even talking about the many significant bug fixes and other minor enhancements which are detailed in the full release notes. With feature releases such as these, sites built with Drupal 8 get fantastic improvements twice each year essentially for free.

By comparison, although Drupal 7 sites still get security fixes and the occasional other bugfix, the last time that there was a significant update for Drupal 7 was back in June 2017 with Drupal 7.55, and even then, the handful of improvements made were still small compared to the rate of improvement Drupal 8 has been seeing. Upgrading to Drupal 8 means you get more new features, more frequently, in addition to being able to use all of the great new features added to Drupal 8 in the five feature releases it has already had.

Ease of Upgrades

I've written a bit about this before, but I think it bears repeating: Once you've upgraded to Drupal 8, the greatest upgrade challenges should be over. Because of how Drupal 8 is designed, future upgrades to Drupal 9 and beyond should be a much simpler transition than moving to 8 from older versions.

This is important to realize because, prior to Drupal 8, many people would "skip" a Drupal version… going from 5 to 7, or from 6 to 8, since the older version would be supported with bug fixes until the new version was released. Many people may be in a similar state now, waiting to upgrade from 7 until 9 is released, but at this point, holding off like that really shouldn't be necessary. If the upgrade from 8 to 9 should be a simple change, like Drupal creator Dries Buytaert has said, then there's no need to skip 8 entirely and miss out on all the great improvements that it can bring to your site today.

Upgrade While There's Time

There's a balance to be struck when deciding when to upgrade to Drupal 8. Doing it right after Drupal 8's release may have been tricky for many sites, due both to some bugs present in the new version and to the many important contrib modules not yet having been upgraded themselves. At the same time though… the longer you wait, the closer you get to your Drupal 7 no longer receiving critical security fixes. Although it's not yet clear when official support for Drupal 7 support will end, it's easy to see that it will happen at some point, and you'll want to already have upgraded by the time it does.

To me, right now feels like the ideal time. Drupal 8 is ready (and better than ever!), and there's still enough time left for Drupal 7 that if you have a complex site, you can take the necessary amount of time to upgrade without being rushed. The worst situation to be in would be to start upgrading a complex website when there's only a month or two of Drupal 7 support remaining, since then you might have to rush the upgrade and end up with more bugs (or things missing from your site) than you'd have if you took the time to upgrade now.

Conclusion

All of this is to say: It's time. Even if you weren't able to upgrade before, due to lacking contrib modules or the need for the Migrate module, those should no longer pose a problem. You can upgrade today and get countless major improvements over previous versions of Drupal (with more coming all the time) and you can even rest easy knowing that the next "big" upgrade shouldn't be anywhere near as daunting as they have been in the past.

It's been more than two years, but finally, Drupal 8 is ready for your site. The question now shouldn't be "Why upgrade?"... it should be "Why wait?"

Is there another reason why you are waiting to upgrade that we didn't mention? We can probably help, schedule your free consultation today.Offer for a free one-hour consultation, make you next project a success

Dec 13 2017
Jay
Dec 13

In this day and age, it is rare to find a truly "standalone" website. The web as a whole was fundamentally built upon a concept of having different sites connecting together. At first, this was just done with hyperlinks from one page to another, but those simple days are long gone. Almost every website in this day and age has some integration with other websites or web services, and fortunately, Drupal is the perfect tool for creating a well-integrated website.

Analytics

Analytics are among the most basic integrations. Tools such as New Relic and Google Analytics can be leveraged on almost every website, and they are invaluable tools for website owners to learn how people are actually using the site and, therefore, how the site can be improved.

Using an analytics tool is often as simple as adding a tiny bit of code to each page on each site, and doing that is as simple with a Drupal site as it is with any other site. Where Drupal shines in this regard is in how easy it makes more complicated use of some such tools. For instance, the Google Analytics module not only makes it easy to add Google Analytics to the site, it also provides an immense number of configuration options so that you can tailor your use of Google Analytics specifically for your site. And where there isn't a module, there are other options: Web hosting company Pantheon provides free access to New Relic Pro for every Drupal site built on their platform, and using it is only a few clicks away.

Brainstorm your next development project with an Ashday Drupal expert! Request your free session today. 

Searching with Solr

For how common it is, searching can be a surprisingly difficult feature to implement. Many sites have a built-in "site search" feature to help with browsing the site, but building such a thing - especially with the quality of search results that people have come to expect from search engines such as Google - isn't an easy thing to do.

By default, Drupal includes a Search module which can be used for simpler sites, but a more robust solution is often needed for more complex sites. There are many options available, but at Ashday, we tend to use Solr when we need to add search functionality to a website. With a Solr integration, the site stores information that somebody might want to search for on a Solr server, and then when somebody searches the site, it lets Solr do the searching. Since Solr is well-optimized for searching large amounts of content quickly, this can give both faster and more relevant results than an entirely custom search. There are some pre-packaged Drupal solutions for searching with Solr, such as the Search API Solr Search module, but if that doesn't quite suit a site's needs, then a custom Solr integration can be built using Solarium instead. One great advantage Drupal 8 has over Drupal 7 is the ease with which it is possible to make use of code "libraries" such as Solarium.

Data Management

Everyone has different data management needs. Sometimes, Drupal's standard content and user management is all that is needed, and this works quite well for many standalone sites.

However, many websites aren't standalone. They share their data with other things - perhaps, the site is tied together with a custom mobile app, or it is part of a whole suite of related websites. In either case, there are two main options: Either Drupal is at the center of the system, managing the data itself, or it connects to some other site that's fulfilling that role.

Using Drupal at the core of interconnected systems such as this has not always been easy, but Drupal 8 has made it much simpler. Out-of-the-box, Drupal 8 contains a number of features and modules designed to make this use a breeze. This also enables the use of "headless Drupal", where Drupal is used for data management only, with other software connecting to it even on the main website.

Also common, is Drupal being used to display and manipulate data stored elsewhere, and for this there often isn't an out-of-the-box solution, due to the sheer number of different possible things that Drupal might be integrated with. No two integrations are quite alike. Where Drupal shines, here, is in the tools it provides developers with. A mixture of Drupal, custom code, and integration-specific libraries can be leveraged quite effectively by a skilled developer to meet whatever needs a site may have.

eSignatures with HelloSign

While things like analytics, searching, and data management are all common tasks that are used by many sites, sometimes an integration is more specialized. One example of this is integrating with HelloSign or other eSignature services. This sort of integration is rare enough that no ready-to-use Drupal solution typically exists, but it is also important enough to the sites that use it that the integration has to be done right. 

Back when we were using Drupal 7, we created a Drupal module for this particular integration, which can now be used by other Drupal 7 sites which need to receive digital signatures from users. Now that Drupal 8 is out, we're looking forward to working on another project that needs a HelloSign integration so that we can update the module and take advantage of Drupal 8's new features.

Customer Engagement

In our experience, most customer engagement tools work great with Drupal! Often, a company's Drupal website is the main way people interact with the company online. As such, it is the perfect place to do collect potential leads and to keep in touch with people. For companies with separate CRM systems, Drupal provides all of the tools necessary to send data on from Drupal to that system. If somebody update their Drupal user account, or fills out a form, the relevant information can also be sent to wherever it needs to go.

What's more, Drupal can be leveraged to help keep in touch with customers directly. While Drupal itself can send emails, this often isn't the ideal setup. Instead, Drupal can be used to create the emails, using the content and customer data that it has, and then it can send that email off to a separate service to send it. That service - perhaps the mailing features available from Knowledge Marketing - can then actually send that email out and manage things like email lists and subscriptions. Although Drupal could be used for such things, using Drupal alongside a specialized tool designed specifically for email management can create a much more flexible system at a fraction of the cost.

Conclusion

Although Drupal isn't perfectly able to do everything on its own (what system can?), the ease with which it can be integrated with other tools more than makes up for it. At Ashday, we've created countless Drupal integrations - some of them used by many of our sites, some by just a single site - and they are some of our favorite projects to work on.

Offer for a free consultation with an Ashday expert

Dec 06 2017
Jay
Dec 06

Illustrations showing a person signing a document from their tablet

As more and more companies make the leap to having entirely-digital communications with their customers or clients, some things have a tendency to stay on paper.  One common thing which lags behind the rest of the digitization process is the signing of legally binding documents... but no more. Now, services such as HelloSign are able to fill this void, and thanks to the HelloSign API, Drupal websites can fully leverage that service to make this important task easier both for you and for your customers.

Why HelloSign?

One common question about eSignature services is simple: Why use one at all?

Using eSignatures can be a great time- and paper-saver compared to conventional document signing. With eSignatures, you can still print a hardcopy if you want, but it's not strictly necessary to do so... no more needing to keep a painstakingly organized file cabinet! It's also just what people have come to expect. If somebody is signing up for access to a web application, for instance, needing to print and send in a document feels archaic by comparison. For these reasons and more, the benefits of eSignatures seems clear.

But, why use a service for it? Can't you just add a checkbox to the registration page of your website that says "I agree"? Well, perhaps, but depending on your use-case that isn't always a suitable solution. Imagine, for instance, that the thing being signed is an apartment rental agreement. When somebody fills out a document like that, you want a signed PDF at the end... something for your own records, and something that the renter can refer to later to review the terms, so an eSigned document if much better than simply recording that somebody clicked a button.

Furthermore, you also need the signature to be legally binding. Unless you want to navigate all of the laws to figure out how to make an eSignature be just as binding as a physical signature, using a service that has already figured out all of those details is a fantastic solution.

At Ashday, we like HelloSign because, in addition to meeting all of these needs and having many other useful features, we can use the robust HelloSign API to integrate the eSignature process directly into websites that need it.

Interested in a smooth, hassle-free HelloSign Integration?  Request your free consultation with an Ashday Drupal expert today. 

Drupal 7 Integration: Overview

Our first HelloSign integration was with a Drupal 7 site, and with that, we created and released the initial version of our HelloSign module for Drupal. This module can be used by any Drupal 7 website to facilitate the integration of HelloSign with that site.

Since this is an integration, somebody using the module still needs to have a HelloSign account that includes access to the HelloSign API and, for the best user experience, its embedded signing feature. What this module does is help get your Drupal 7 site connected to your HelloSign account and provide some useful tools for creating and managing eSignature requests.

The way the module works is simple. Once it is enabled on your site, there will be a page available on your site to enter your HelloSign API credentials. This page also has a useful "Test Mode" option to toggle whether eSignatures on the site should be "real" or just tests, which is very useful for when you are making changes to your eSignature functionality and want to be sure that it all works before people start signing any legally binding documents.

Once the HelloSIgn connection has been established, you're ready to actually use the module. Some Drupal modules create a full-fledged user interface for interacting with them, but since eSignatures can be used for so many different things, we didn't want to make any wrong assumptions about what people would want to do with the module. As such, what this module provides is a set of useful PHP functions that greatly simplify the creation of a HelloSign integration, rather than building a whole UI that might not work for all sites.

Drupal 7 Integration: The Details

If you hire a company like Ashday to build your website, of if you have software engineers at your company, they'll be the ones using the module to create an integration with the HelloSign API. In this case, you can probably skip this section. If, on the other hand, you're writing the code yourself, then this section is for you! The module's README has more details, but this should give a good overview of the overall process for using the module to integrate with the HelloSign API.

The heart of the module is the PHP function hellosign_generate_esignature_request(). All this function needs is the location of the PDF file to be signed, the names and email addresses of everyone who should sign it, a title for the document being signed, and a subject line for any emails sent regarding the eSignature. You can also create the eSignature in either "email" or "embedded" mode; we'll use "embedded" mode, since that usually makes for a better user experience. With this information, the function connects to the HelloSign API and starts the eSignature process. Assuming that all goes well, the function returns the ID of the signature request as well as information about the individual signatures needed. The request ID can then later be used by other functions to do other things related to the eSignature, such as cancelling the request, and the signature information can be used to move on to the next step: Building a page where the user can actually sign the document.

Perhaps surprisingly, this is one of the easiest parts of the integration.  The module includes a function called hellosign_get_embed_url(); give it the ID of a particular signature that you want, and it will return the URL for an iframe which you can include on whatever page you want users to go to to sign their documents.

Now, this is a Drupal 7 module, and what would a Drupal 7 module be without hooks? This module provides a single, vital hook: hook_process_hellosign_callback().  Any implementations of this hook that you create will get called whenever HelloSign notifies the site about a signature request being updated. This way, your site can know when a document gets signed or completed, and can do anything that it needs to. Need to save a copy of the signed document to your own server? The module has that covered as well. Just use hellosign_fetch_esignature_document() to get exactly the file you need, and then save it wherever you want in the file system.

Finally, if you need other, more advanced features of the HelloSign API, the module ultimately uses the HelloSign PHP SDK, so you can leverage anything you need from that even if the Drupal module doesn't specifically include functions for it. In theory, you could even create a HelloSign integration using just the SDK, but the Drupal module handles many common eSignature needs without ever needing to delve into a much more complicated utility like the SDK.

What's Next: Drupal 8

Of course, at this point, Drupal 7 is old news, and Drupal 8 is what all the cool kids are talking about. Well, don't worry: We're currently working on a new version of our HelloSign module for use on Drupal 8 sites. We've been using Drupal 8 for more than two years now (since before it's first official release!) and at this point we're pretty comfortable with the Drupal 8 way of doing things, so it's high time we brought the HelloSign module up to date. Since Drupal 8 is a much more robust and object-oriented system than Drupal 7, we're fully leveraging that to improve the structure of the module. This makes it both more flexibile to use and easier to add new features to as new needs crop up. Expect another blog post once the module is ready for use, and you can see all the improvements for yourself.

We're looking forward to building HelloSign integrations in Drupal 8 sites ... stay tuned.  

Free offer, talk to a seasoned Drupal expert.

Nov 01 2017
Jay
Nov 01

drupal-upgrade-crisis.jpg

The Drupal Upgrade Crisis is Over

Back in March 2017, Dries Buytaert, the creator of Drupal, published a blog post entitled "Making Drupal upgrades easy forever" in which he confirmed what we here at Ashday had already suspected: Drupal 8 has laid the groundwork for seamless upgrades to future Drupal versions. But what exactly does that mean, and what benefits could this have for your website?

The Dark Age of Upgrades

Prior to the release of Drupal 8, Drupal releases broadly followed a policy that included minor releases and major releases. Minor releases (such as upgrading from Drupal 7.18 to 7.19) were usually fairly simple updates. They would fix bugs, add in some small new features, and resolve security issues, but ultimately, it was rare for a minor update to cause any problems for a well-built Drupal site.

However, major releases (such as upgrading from Drupal 6 to 7) were a whole 'nother matter; they're basically different systems entirely.. Standard policy with major updates was that most anything that needed to be changed could be, even if doing so would make it impossible to seamlessly update a site from one version to the next. For instance, Drupal 6 included a feature by default that allowed each of a site's users to select what theme they saw the site in (changing the site's overall look and feel). In practice, not many sites made use of this feature, so it was removed in Drupal 7 so that developers could focus on the more frequently-used parts of Drupal. Of course, if a site used this feature, it couldn't be upgraded to Drupal 7 without either losing that functionality or having new development done to add a replacement. The Drupal way to make progress was to not tie the potential of the future to the decisions of the past.

What this has lead to, however, is that oftentimes, once a site has been built on one version of Drupal, it doesn't get upgraded to the next version without good reason. In fact, there are many sites still out there running on Drupal 6 simply because upgrading to Drupal 8 would be too expensive and time-intensive of a process.

Fortunately, the dark age of upgrades may be at an end.

Enter: Drupal 8

When Drupal 8 was released, the paradigm shifted. Drupal 8 is very different from Drupal 7 (even more so than 7 was from 6), and so upgrading a site to Drupal 8 presents many of the same difficulties as upgrades in the past. But, thanks to the hard work of the Drupal developers and everyone else involved in the project, future upgrades should be much simpler. Drupal 8's code has been written to make good use of web development standards in ways that past versions never even tried. Because of this, Drupal 8 is fundamentally a foundation that can be built on by future versions without needing a complete overhaul.

What that means is that any site which is kept up to date as minor releases come out should continue to work on Drupal 9 with minimal effort needed to upgrade it. Instead of everything changing at once like it did going from Drupal 7 to Drupal 8, old code and little-used features can be phased out slowly as better alternatives are developed and implemented.

And that leads us to the final result of this change of approach: Prior to Drupal 8, a site could be built and then be minimally maintained with security updates until the next version of Drupal came out, at which point it had to be rebuilt entirely. Now, a Drupal 8 site can be built, and if it is minimally maintained until Drupal 9, there might be a few adjustments needed to remove deprecated features, but there will certainly be fewer than in the old way. But what's more, if that Drupal 8 site is actively maintained, it may not even be using any deprecated features at all by the time Drupal 9 comes out, allowing that upgrade to be made with ease.

What Does This All Mean for You?

From a technical perspective, these are all great changes, but what does it actually mean for your website? Several things:

If you are on an older version of Drupal: Now is the time to upgrade. Upgrading from 6 is long overdue (it doesn't even get official security updates anymore!), but upgrading from 7 makes sense now as well. Before Drupal 8, it was common to skip versions… for instance, a Drupal 5 site would skip Drupal 6 entirely and get rebuilt on Drupal 7. But upgrading a Drupal 7 site to 9 shouldn't really be too different from upgrading it to 8… so, why wait? Upgrading now will help avoid any last-minute scrambling to update the site when Drupal 7 eventually stops getting security updates.

From a business and marketing standpoint, there's another hidden advantage as well. Before, redesigning a website often got tied to the need to upgrade Drupal. There wasn't much reason to do a big redesign, if you'd have to upgrade from one major release of Drupal to another just a year later, so the changes would get bundled together to avoid having to do two rebuilds. But what's more, taking on all the concerns of a site redesign alongside the technological challenges of a Drupal upgrade could make doing both at once a challenging task in itself.

Once your site is on Drupal 8, major releases should no longer require rebuilding the site… which frees up your Drupal developers to be able to do a redesign whenever it makes sense to from a branding or business perspective. Drupal 8 gives you the power to decide when a website overhaul should be done, rather than tying such upgrades to the technology the site happens to be built with.

Offer for a free consultation with an Ashday expert

Jan 29 2017
Jay
Jan 29

Let's Encrypt is taking over the world with its free SSL certificates, and I'm using it on ALL of my websites (including the one you're reading right now). But using it with Drupal & Virtualmin is not as easy as it should be, so today I'll show everyone how to make it work 100%. It took me a few hours of detective work to figure out the following two prerequisites that need to be taken care of before using Let's Encrypt:

#1. Apply a patch (https://www.drupal.org/node/2408321, #53) to Drupal 7 core. You can skip this step for Drupal 8, because it's already been committed.

Now all domains in Virtualmin except the default one will work with Let's Encrypt. For the default website ("Server Configuration" -> "Website Options" -> "Default website for IP address") to also work, the following needs to be taken care of:

#2. In your server, find the ssl.conf file (in my Linode server's CentOS 7, it would be /etc/httpd/conf.d/ssl.conf), back up the file just in case and delete the following code in its entirety:

<VirtualHost _default_:443>

Delete everything here, including the top & bottom. ;)

</VirtualHost>

And that's it. Just restart your server, and Let's Encrypt should now work flawlessly with Drupal & Virtualmin. Welcome to the world of free SSL certificates! :D

P.S. As for a general guide on how to use Let's Encrypt in Virtualmin, look forward to another blog post in the near future. ;)

Sep 17 2016
Jay
Sep 17

Today I'll show you guys how to install the Relation module for Drupal 7. Believe it or not, it took me almost five years to fully figure out how to install what I think is one of the most confusing modules ever made lol.

So what exactly does Relation do? Let's say you have a film and its sequel:

* Film 1 is followed by film 2.
* Film 2 follows film 1.

The beauty of Relation is that once you create one relation (in either direction), it creates the other one for you automatically. It's this simple thing that this module does amazingly well.

If you want to look at a live example, take a look at the Connections page of a movie called Moonraker on IMDb. In this example, you can think of the module as basically the feature that allows any member of the site to create movie connections. ;)

But the main problem with this module is that it was built with administrators in mind, or at least this is what I've been told by a lot of people, including Steve Oliver, creator of the Relation Select module (speaking of which is another module I'm still trying to figure out, hehe). So basically, I've been wondering in vain for years how to enable users of a membership-based site to use this module while adding new content, because otherwise I always thought the module would be pretty much useless. In IMDb, this would be like only administrators being able to create movie connections. And if members can't create them, what's the point, right? And to make matters worse, the readme file itself has always been missing some critical information that would allow you to install the module in the way that I just described, which is how I think most people will use it for anyway.

So if you're one of those people who were never able to figure out how to install Relation in this way, you're in for a treat. Read on, as I will reveal a couple of secrets you need to know in order to get the module working in the way I believe it was meant to be:

#1. Download and install the Relation, Relation Endpoints Field & Relation UI modules (which are all part of the Relation module).

#2. Go to /admin/structure/relation and set up a new relation. For the purpose of today's example, let's check the "Directional" option, and just fill in "follows" for the "Label" field and "is followed by" for the "Reverse label" field. Now click the "Save" button.

At this point, you have a basic relation created.

#3. Here is the FIRST SECRET: Install the Relation Add module.

#4. Go to /admin/structure/types/manage/CONTENT_TYPE/fields and add a new "Relation add" field. For this blog post, we'll call the field "Connections". It is this field that will allow users other than administrators of your site to create a relation while adding new content. And when creating the field, make sure to choose both "Follows" & "Follows (reverse)" for the "Relation types" option and whatever content type you'll be using with the module for the "Target bundles" option.

So go ahead and create a couple of new nodes of whatever content type you want to use the module on. For the purpose of today's example, let's say we have two nodes called "Film 1" & "Film 2". When creating the second film, make sure to create the relation "Follows" (film 2 follows film 1).

At this point, you'll realize that the module at least works. But the problem now is that the display looks weird as hell. Here are what the two film pages look like in our example:

#I. Film 1:

Connections:
endpoints
* Film 2

#II. Film 2:

Connections:
endpoints
* Film 1

#5. Here is the SECOND SECRET: Install the Relation Dummy Field module (which is part of the Relation module).

#6. Go to /admin/structure/types/manage/CONTENT_TYPE/display and select "Natural language" as format. It is this that will change the display of the relation to match what you created in step #2 above. Here are what the two film pages look like now in our example:

#I. Film 1:

Connections:
Film 1 is followed by Film 2

#II. Film 2:

Connections:
Film 2 follows Film 1

Notice that the pages finally look similar to what you saw above in the Connections page of Moonraker on IMDb. Finally! :)

UPDATE (September 17, 2016): Now let's take a look at a minor limitation of Relation:

Let's say we have a film trilogy. Here's how the connections will look like:

#I. Film 1:

Connections:
Film 1 is followed by Film 2
Film 1 is followed by Film 3

#II. Film 2:

Connections:
Film 2 follows Film 1
Film 2 is followed by Film 3

#III. Film 3:

Connections:
Film 3 follows Film 1
Film 3 follows Film 2

But here's how I think the connections should look like instead:

#I. Film 1:

Connections:

Is followed by:
Film 2
Film 3

#II. Film 2:

Connections:

Follows:
Film 1

Is followed by:
Film 3

#III. Film 3:

Connections:

Follows:
Film 1
Film 2

It might seem minor, but imagine what it would look like for a movie like Moonraker. Per my reply to an old issue (#10), I hope somebody eventually fixes this annoying problem.

Now let's take a look at a couple of advanced options when setting up #2 above:

#7. Transitive: It's an option that actually does NOT work, and is related to what I just described above.

For example, if you create a couple of relations so that "Film 2 follows Film 1" & "Film 3 follows Film 2", this option would theoretically create another relation automatically so that "Film 3 follows Film 1". But the problem is that this option has never been implemented, and is briefly mentioned within the module's online tutorial page that "it is merely an information flag to be used by other modules". I take it that this probably means that we'll have to wait until a sympathetic developer creates a new module (called "Relation Transitive"?) that makes this amazing feature work. I sincerely believe this is a major limitation of this module, and I hope somebody eventually fixes it.

UPDATE (September 22, 2016): It looks like I might have to do some research into Bundle Inherit, which might actually be able to provide a simple transitive solution. One user also claims that Rules will work as well.

#8. Unique: It's an option that prevents you from creating duplicate relations. For example, try creating the same relation twice in #4 above when creating the second film ("film 2 follows film 1"). This option will automatically prevent you from doing this. There's also a Relation Unique module that looks like it might extend this feature (but I'm not exactly sure what at this point).

And last but not least, let's look at a couple of alternative solutions if this module didn't exist:

#A. Entity Reference & Corresponding Entity References

It is worth noting here that Corresponding Entity References is VERY unstable at the moment. You'll get error messages everywhere. :(

#B. Entity Reference & Views

There are at least three problems with both options:

#a. First, you have to choose Entity Reference on one side, and let Corresponding Entity References or Views choose the other side. Remember what I said above about Relation creating one relation (in either direction), and it creating the other one for you automatically? ;)

#b. Second, you can't prevent your members from wreaking havoc by creating duplicate relations.

UPDATE (September 22, 2016): It looks like the Field Validation module can actually prevent your members from creating duplicate relations, but I get an error message that's been active for the past few years. The module does look promising though, and I'll try this option again after the bug gets squashed.

#c. Third, you also won't be able to implement the transitive option that will probably be available for Relation in the hopefully not-too-distant future.

#d. Fourth, only Relation allows you to add fields to any relation. For example, you can add a "Notes" field explaining the relation. You can see plenty of these in the Connections page for Moonraker. However, it is worth mentioning here that I believe you might be able to get away with this problem for the alternative solutions by using Paragraphs (or Field Collection) and just adding whatever additional fields you need. I haven't actually tried it myself though, so don't quote me on this yet. :P

It is also worth mentioning that using Entity Reference & Views will enable you to solve the minor limitation of Relation that I described above. It is the one advantage that using this alternative solution currently has over using Relation (I haven't tried this with Entity Reference & Corresponding Entity References though).

So I hope this explains the raison d'être of Relation a bit more clearly. It's a great module that does one thing really, really well.

And that's it. I hope this helps some people start using this awesome module. :)

Sep 04 2016
Jay
Sep 04

When installing Drupal, one of the things that some people must do beforehand depending on their specific server configuration is to change "+FollowSymLinks" to "+SymLinksIfOwnerMatch" in the .htaccess file. And most people have already figured this out a long time ago, because they wouldn't have been able to install Drupal otherwise.

But a bigger issue here is that there are some more places where these people need to repeat the same exact step after installation, or they will undoubtedly encounter additional problems, one of which involves having images not showing up at all:

+FollowSymLinks Problem

+FollowSymLinks Problem

To make matters worse, some modules will seem like they just don't work right. For example, I remember a few moments of banging my head against the wall a while back because the Follow module didn't seem to work correctly after trying to use it right after installing Drupal. And unlike the broken image icon above, the images from this module seemed completely nonexistent, making it tougher to initially figure out what the hell was going on lol. But eventually I decided to check the server's error log, which gave the following message: "/home/website/public_html/sites/default/files/.htaccess: Option FollowSymLinks not allowed here, referer: http://website.com/"

The source of this problem is that there are some new .htaccess files that get created during the installation process, one of which is in the /public_html/sites/default/files directory. So you'll have to go into that file and change it again.

But that's not it. There are two more places where you might have to repeat the same process again, depending on how you set up Drupal. So in total, there are actually four potential locations where you need to check for the .htaccess files:

1) /public_html/.htaccess
2) /public_html/sites/default/files/.htaccess
3) /public_html/sites/default/files/private/.htaccess
4) /tmp/.htaccess

I think that's it. And since I'm one of those that used to keep forgetting to track all of these files, I decided to write this blog post to remind others. I hope this helps some people out.

Aug 28 2016
Jay
Aug 28

I recently noticed in Drupal that although there are modules that let you display login messages such as "Log in successful for @username." (LoginToboggan) or "Welcome back, @username." (Persistent Login), there's no easy way to display logout messages.

So after doing some research, I found a patch for Drupal 7 that I just updated to work on the recently released Drupal 7.50. Let's take a quick look at the simple code changes in the modules/user/user.pages.inc file (lines 194 - 214):

1) Before:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
user_logout_current_user();
drupal_goto();
}

/**
* Logs the current user out.
*/
function user_logout_current_user() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
}

2) After:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout($message = 'You are now logged out.', $status = 'status') {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

// Sends a message to the user.
if (!empty($message)) {
drupal_set_message($message, $status);
}

drupal_goto();
}

So there you have it. I tested the code out a few times and it works flawlessly.

P.S. Does anyone know how to display the username in the logout message, such as "You are now logged out, @username."? Let me know in the comments, and I'll give you credit. Thanks! :)

Aug 28 2016
Jay
Aug 28

I recently noticed in Drupal that although there are modules that let you display user login messages such as "Log in successful for @username." (LoginToboggan) or "Welcome back, @username." (Persistent Login), there's no easy way to display user logout messages.

So after doing some research, I found a patch for Drupal 7 that I just updated to work on the recently released Drupal 7.50. Let's take a quick look at the simple code changes in the modules/user/user.pages.inc file (lines 194 - 214):

1) Before:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
user_logout_current_user();
drupal_goto();
}

/**
* Logs the current user out.
*/
function user_logout_current_user() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
}

2) After:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout($message = 'You are now logged out.', $status = 'status') {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

// Sends a message to the user.
if (!empty($message)) {
drupal_set_message($message, $status);
}

drupal_goto();
}

As you can see, the code change is pretty much self-explanatory. Just change the message in red to customize it to however you want it.

So there you have it. I tested the code out a few times and it works flawlessly.

P.S. Does anyone know how to display the username in the user logout message, such as "You are now logged out, @username."? Let me know in the comments, and I'll give you credit. Thanks! :)

UPDATE (August 29, 2016): Thanks to user Barami below, I solved the problem. Here is the new code:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

$message = "You are now logged out, " . $user->name . ".";

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

// Sends a message to the user.
if (!empty($message)) {
drupal_set_message($message, 'status');
}

drupal_goto();
}

And I think this one is actually better than the original solution, whether you decide to include the @username in the message or not. Just change the message in red to customize it to however you want it.

Aug 28 2016
Jay
Aug 28

I recently noticed in Drupal that although there are modules that let you display user login messages such as "Log in successful for @username." (LoginToboggan) or "Welcome back, @username." (Persistent Login), there's no easy way to display user logout messages.

So after doing some research, I found a patch for Drupal 7 that I just updated to work on the recently released Drupal 7.50. Let's take a quick look at the simple code changes in the modules/user/user.pages.inc file (lines 194 - 214):

1) Before:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
user_logout_current_user();
drupal_goto();
}

/**
* Logs the current user out.
*/
function user_logout_current_user() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
}

2) After:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout($message = 'You are now logged out.', $status = 'status') {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

// Sends a message to the user.
if (!empty($message)) {
drupal_set_message($message, $status);
}

drupal_goto();
}

As you can see, the code change is pretty much self-explanatory. Just change the message in red to customize it to however you want it.

So there you have it. I tested the code out a few times and it works flawlessly.

P.S. Does anyone know how to display the username in the user logout message, such as "You are now logged out, @username."? Let me know in the comments, and I'll give you credit. Thanks! :)

UPDATE (August 28, 2016): Thanks to user @Barami below, I was inspired to solve the problem in my own simple way. Here is the new code:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

$message = "You are now logged out, <em>$user->name</em>.";

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

drupal_set_message($message, 'status');

drupal_goto();
}

And I think this code actually looks better than the original solution, whether you decide to include the @username in the message or not. Just change the message in red to customize it to however you want it.

UPDATE (August 29, 2016): It turns out that I needed to tweak my code one more time, because I use the Real Name module (which is compatible with LoginToboggan & Persistent Login). For those of you who also use it, here is the final code:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

$realname = format_username($user);
$message = "You are now logged out, <em>$realname</em>.";

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

drupal_set_message($message, 'status');

drupal_goto();
}

And yes, I think this code actually looks best, whether you decide to include the @realname in the message or not. Just change the message in red to customize it to however you want it. That's it! :D

UPDATE (August 29, 2016): Thanks to users @Barami & @joekers below, I was inspired to eventually create a working custom module (which I originally couldn't get to do exactly what I wanted). Most of the code was taken from a discussion thread in Drupal. Feel free to download and use it (I also created a sandbox project page, which I will update shortly), because we're never supposed to hack Drupal core if at all possible. Thanks again guys, I did it! ;)

Aug 28 2016
Jay
Aug 28

UPDATE (August 30, 2016): The module is now called User Login Logout Messages, which displays a message when users log in as well as log out. It works flawlessly on Mediography.by, my media tech startup. I'm so jazzed! :D

UPDATE (August 29, 2016): Thanks to users @Barami & @joekers below, I was inspired to eventually create a custom module (because we're never supposed to hack Drupal core if at all possible). Most of the tough code was taken from a discussion thread in Drupal. Feel free to visit my sandbox project page to test it out. Thanks again guys, I did it! ;)

I recently noticed in Drupal that although there are modules that let you display user login messages such as "Log in successful for @username." (LoginToboggan) or "Welcome back, @username." (Persistent Login), there's no easy way to display user logout messages.

So after doing some research, I found a patch for Drupal 7 that I just updated to work on the recently released Drupal 7.50. Let's take a quick look at the simple code changes in the modules/user/user.pages.inc file (lines 194 - 214):

1) Before:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
user_logout_current_user();
drupal_goto();
}

/**
* Logs the current user out.
*/
function user_logout_current_user() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();
}

2) After:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout($message = 'You are now logged out.', $status = 'status') {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

// Sends a message to the user.
if (!empty($message)) {
drupal_set_message($message, $status);
}

drupal_goto();
}

As you can see, the code change is pretty much self-explanatory. Just change the message in red to customize it to however you want it.

So there you have it. I tested the code out a few times and it works flawlessly.

P.S. Does anyone know how to display the username in the user logout message, such as "You are now logged out, @username."? Let me know in the comments, and I'll give you credit. Thanks! :)

UPDATE (August 28, 2016): Thanks to user @Barami below, I was inspired to solve the problem in my own simple way. Here is the new code:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

$message = "You are now logged out, <em>$user->name</em>.";

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

drupal_set_message($message, 'status');

drupal_goto();
}

And I think this code actually looks better than the original solution, whether you decide to include the @username in the message or not. Just change the message in red to customize it to however you want it.

And for those of you that use the Real Name module (which is compatible with LoginToboggan & Persistent Login), try the following code that I actually think looks best, whether you decide to include the @realname in the message or not:

/**
* Menu callback; logs the current user out, and redirects to the home page.
*/
function user_logout() {
global $user;

watchdog('user', 'Session closed for %name.', array('%name' => $user->name));

$realname = format_username($user);
$message = "You are now logged out, <em>$realname</em>.";

module_invoke_all('user_logout', $user);

// Destroy the current session, and reset $user to the anonymous user.
session_destroy();

drupal_set_message($message, 'status');

drupal_goto();
}

Again, just change the message in red to customize it to however you want it. That's it! :D

Aug 16 2016
Jay
Aug 16

For users of the awesome Web Widgets module, you'll notice that it'll no longer work after installing Drupal 7.50. But after digging into this issue, I realized that the problem was much simpler than I had originally imagined (Drupal 7.50 supporting PHP 7). Sometimes all it takes is to just RTFM lol. Here's how to solve it:

1) Understand what the source of the problem is. The cause of the issue was "Protection against clickjacking enabled by default".

2) If you browse the topic, you'll notice a link to find out more. Click on it and pay attention to "How to override the default behavior".

3) It goes to an issue page for the Security Kit module. Judging by the fact that the issue page was created on 2/2/16, you can see that you'll need to use 7.x-1.x-dev instead of the usual 7.x-1.9, which was released on 5/6/14.

4) So install the module, and go to admin/config/system/seckit and disable "X-Frame-Options".

That should do it. Web Widgets will now work with Drupal 7.50. :)

Aug 16 2016
Jay
Aug 16

For users of the awesome Web Widgets module, you'll notice that it'll no longer work after installing Drupal 7.50. But after digging into this issue, I realized that the problem was much simpler than I had originally imagined (Drupal 7.50 supporting PHP 7). Sometimes all it takes is to just RTFM lol. Anyway, here's how to solve it:

1) Understand what the source of the issue is. The cause of the problem was "Protection against clickjacking enabled by default".

2) If you browse the topic, you'll notice a link to find out more. Click on it and pay attention to "How to override the default behavior". There are three options you can choose from, but I'll use the first one because it's the most foolproof method. Once you get it working, you can try the other two methods if you don't want to be forced into using yet another module on your Drupal site.

3) It mentions something from an issue page (although I haven't tested it directly, I believe the most important part is the patch at #5) for the Security Kit module. And judging by the fact that the issue page was created on 2/2/16, you can see that you'll need to use the 7.x-1.x-dev version instead of the usual last stable 7.x-1.9 version (which was released on 5/6/14).

4) So install the module, go to admin/config/system/seckit and just disable "X-Frame-Options".

That's it. Your precious Web Widgets module will now work perfectly with Drupal 7.50. Easy enough for everyone, right? :)

May 09 2016
Jay
May 09

For the past few days, I wrote three blog posts on how to configure three modules: Drupal Background Images Module Configuration Manual, Drupal Background Images Formatter Module Configuration Manual & Drupal BackgroundField Module Configuration Manual

Today I'll finally reveal how to create clickable background takeover ads, something that I once thought was impossible. I'll use Drupal as an example, but the concept itself of clickable images as background takeover advertisements should apply to just about any type of website. I'll also use CSS, because it's the easiest method to understand and use. I believe other possible methods for creating any type of clickable background takeover images include using JavaScript.

But first I would like to give a huge thanks to Malcolm Young, who provided the inspiration for me to finally get this technology working exactly the way I want it:

Thanks to @malcomio, #ClickableBackgroundTakeoverAds are now 100% possible with CSS: https://t.co/KfwnUC0pRA #Advertisements #Images @drupal

— Jay L.ee (@wwwjaylee) May 10, 2016

Malcolm, you rock man! :)

Let's take a quick look at the source of my inspiration, his patch in response to an issue that I created in the issue queue of Background Images:

What it does is that it basically adds a link field to admin/content/background-images, described in step #5 of the Drupal Background Images Module Configuration Manual. Malcolm is probably not the first person on the planet to come up with the essential pieces of CSS code (shown in step #7 below) to make clickable background images in general, but it was the first time I've actually seen it work in person. I finally had my "a-ha" moment. ;)

There was one problem though, where the entire screen became a clickable link. But it gave me enough motivation to inspire me to further my research (which originally began a few years ago) and eventually make it work. So after all these years of frustration over trying to get this simple yet esoteric technology to work, I finally got it fully working.

So are you ready? Here we go:

1) Upload a background image to use as an ad using whatever module you decide to use. Whether it be BackgroundField, Background Images, Background Images Formatter or Dynamic Background, it doesn't matter.

2) Install and enable the Link module.

3) Go to whichever content type you want to apply the background image to, click the "MANAGE FIELDS" tab and create a new field ("FIELD TYPE": "Link") for the background image link that will be clicked on: admin/structure/types/manage/CONTENT-TYPE/fields

4) Click the "MANAGE DISPLAY" tab and change the "FORMAT" of the background image link to "<Hidden>", because of the next step: admin/structure/types/manage/CONTENT-TYPE/display

5) So are you ready to learn how the concept of a clickable image as a background takeover advertisement really works? Here is the first secret: You must somehow move the link field to somewhere above the main content region but below the header, because this is the only way that the entire main content region itself will not become clickable, since that's where the link field itself is originally part of. For most themes, this can be done via adding some code to the page.tpl.php file. It should look something like the following for Bootstrap (put it between "main-wrapper" & "main-container"):

<?php
if (!empty($node)) :
$field_background_image_link = field_get_items('node', $node, 'field_background_image_link');
if ($field_background_image_link):
$background_image_link = field_view_field('node', $node, 'field_background_image_link', array('label'=>'hidden'));
print drupal_render($background_image_link);
endif;
endif;
?>

6) Next you still have to apply proper CSS code to the main content region. The code should look something like the following for Bootstrap:

.main-container {
position: relative;
}

7) And here is the second and biggest secret: You must apply proper CSS code to the link field. The code should look something like the following:

.background-image-link a {
height: 100%;
width: 100%;
position: fixed;
left: 0px;
top: 0px;
opacity: 0;
}

8) At this point, pretty much the entire screen should be clickable, except the main content region because we already took care of that in steps #5 & #6. So let's take care of this problem by first applying proper CSS to the header so that it's not clickable anymore. The code should look something like the following for Bootstrap:

.navbar {
position: relative;
z-index: 10;
}

9) Now let's finish up by applying similar CSS to the footer. The code should look something like the following for Bootstrap:

.footer {
position: relative;
}

It's worth mentioning that you need to go through some trial and error to get the CSS right for whatever theme you're using (notice that the CSS code above is a bit different for the header & footer). I'm not an expert by any means, but feel free to leave a comment and I'll do my very best to help out. And that's all for today. Peace! :)

P.S. I'm currently moving ALL of my websites to Bootstrap, so I will most likely provide an update here in the next few weeks regarding anything specifically related to this awesome theme that might provide further help. Stay tuned!

May 08 2016
Jay
May 08

Yesterday I wrote a blog post on how to configure a Drupal module called Background Images Formatter, which is part 2 of the Drupal Background Images Module Configuration Manual. Today I'll continue with part 3 with a module called BackgroundField. Then tomorrow I'll finally reveal how to create clickable background takeover ads.

UPDATE (May 8, 2016): Clickable Background Takeover Ads (Part 4)

BackgroundField is probably the best alternative solution to using Background Images & Background Images Formatter, because this one module does the job much more simpler than using two.

I) The main reason why anyone would want to use this module would be my use case where I run a membership website and want my members to be able to upload background images, because the Background Images module only allows people with admin access (specifically to admin/content/background-images) to enable background images at all.

Other examples can be found in the description of the home page of BackgroundField.

II) If you don't run a membership website and just want to upload background images as an admin, you should use Background Images if you want to upload it via the admin interface, or Background Images Formatter (or BackgroundField) if you want to upload it while creating each node.

Ok, so here we go:

0) (Optional) Apply the following two patches that will take care of a couple of minor error messages: https://www.drupal.org/node/1841978 (#19) & https://www.drupal.org/node/2720959 (#2, created by yours truly! :D)

1) Install and enable the BackgroundField module.

2) Go to whichever content type you want to apply the background image to, click the "MANAGE FIELDS" tab and create a new field for the background image: admin/structure/types/manage/CONTENT-TYPE/fields

If you noticed, the biggest difference between this module and Background Images is that this module actually allows you to choose a "FIELD TYPE" called "BackgroundField" vs. step #2 of the Drupal Background Images Module Configuration Manual.

And yet again, ignore the fact that the "CSS Selector" field here once again has the default entry as "body", which most likely will NOT work. The correct entry for Bartik is "#main-wrapper", and for Bootstrap it is ".main-container".

3) (Optional) If you want to enable background images for user accounts, go to admin/config/people/accounts/fields. Then the rest is pretty much the same thing as step #2.

And that's really it for this module. Go create a node with a background image, and it should just show up magically. Now feel free to go back and play around with other settings. Anyway, this is now my favorite module for creating background images due to its simplicity.

If you want yet another alternative solution for creating background images, there is a module called Dynamic Background. The advantage of using this module is that it's most likely the most flexible of all the modules, as you can do more things than all the others. But the disadvantage is that you'll need to enable a total of at least three modules to get the same job done as BackgroundField: "Dynamic background" (the main module itself), "Dynamic background node" (which does the same thing as step #2) & "Dynamic background user" (which does the same thing as step #3)

Whatever module you decide to use, my advice is to choose wisely in the beginning. Since each module does things a bit differently, it might not be simple to switch between modules later on. So make sure you know exactly how you will be uploading background images before making up your mind.

Now stay tuned, as my next blog post will finally cover part 4 on an awesome topic regarding how to create clickable background takeover ads.

P.S. Sometimes my blog posts help me get out of my comfort zone and discover new modules. This is one of those modules, which I will most likely switch to from Background Images & Background Images Formatter, since using one module is better than using two for my own use case.

May 07 2016
Jay
May 07

Yesterday I wrote a blog post on how to configure a Drupal module called Background Images. Today I'll continue with part 2, and it's a simple one but essential as well, because this module doesn't even come with a README.txt file at all lol.

UPDATE (May 7, 2016): Drupal BackgroundField Module Configuration Manual (Part 3)
UPDATE (May 8, 2016): Clickable Background Takeover Ads (Part 4)

But before we begin, let's answer the million dollar question of why anyone would want to use this module:

A good example would be my use case where I run a membership website and want my members to be able to upload background images while they're creating new nodes, because the Background Images module only allows people with admin access (specifically to admin/content/background-images) to create background images.

Other examples can be found in the descriptions of the home pages of Background Images & Background Images Formatter.

Ok, so here we go:

1) Install and enable the Background Images Formatter module.

2) Go back to step #2 of my last blog post: admin/structure/types/manage/CONTENT-TYPE/display

A) Click the "FORMAT" of the background image, which should now give you a new option called "Background image". So change it to this from "<Hidden>".

B) Now you should see a gear icon to the right, and you should see a familiar set of settings just like those from step #4 of my last blog post: admin/config/content/background-image

C) Pay attention to the "Selector(s)" field, just like I said in my last blog post (did you notice that the default here is once again "body", which I already mentioned before will most likely NOT work at all for pretty much everybody?!? :P), and put in the right one for your theme. Again, ignore the fact that BOTH screenshots used in the module's home page has the field as "body", because it most likely will NOT work: https://www.drupal.org/files/project-images/Basic%20page.png & https://www.drupal.org/files/project-images/Dynamic%20Background%20%20Co...

3) Undo whatever you did in step #5 of my last blog post, because step #C above will override it anyway (as well as whatever settings you chose in admin/config/content/background-image): admin/content/background-images

4) Feel free to disable and uninstall "Background Image UI" from step #3 of my last blog post, because it's what this module pretty much replaces.

In summary, you will either use Background Images Formatter or the "Background Image UI" part of Background Images, but not both. And needless to mention, you'll always need the "Background Image" part of Background Images, without which Background Images Formatter can't even be enabled at all.

And that's it for this blog post. Stay tuned, as my next blog post will cover part 3, which is actually a better alternative solution to parts 1 & 2 (I just found out about it while doing research for these blog posts). Then part 4 will finally reveal how to create clickable background takeover ads.

P.S. Sometimes my blog posts help me remember things that I forgot to do. I just realized that I still forgot to do step #4 on my own media startup, Mediography.by, that uses it for clickable background takeover ads. It is now disabled and uninstalled. :)

UPDATE (May 7, 2016): As I mentioned in my next blog post, I will now most likely switch to BackgroundField from Background Images & Background Images Formatter, since using one module is better than using two for my own use case.

May 06 2016
Jay
May 06

During my San Diego Drupal Users Group lightning talk on March 8, I showed a brief demo of how background images can be made to be clickable via CSS, something that I once thought was impossible. But as I'll show you over the next few days, it actually is 100% possible, and I had a LOT of fun getting it to finally work exactly the way I want it. Today's tutorial will be part 1 of 4. My next three blog posts will cover the rest of the steps with the ultimate goal of creating clickable background takeover ads.

UPDATE (May 6, 2016): Drupal Background Images Formatter Module Configuration Manual (Part 2)
UPDATE (May 7, 2016): Drupal BackgroundField Module Configuration Manual (Part 3)
UPDATE (May 8, 2016): Clickable Background Takeover Ads (Part 4)

Before I begin, I want to make it clear that my four tutorials will focus on getting the necessary modules to be set up as quickly as possible. I'm sure there might be many different ways to use these wonderful modules, but I'll focus on my use case only, in which it'll eventually enable me to make money with my startup in the not-too-distant future. Here is an example of how I'm using Background Images for artists (you'll need a fairly large monitor to see the full effect): https://mediography.by/artist/simon-o%27shine

If you click on the background image, it'll go to Beatport on a new page so that fans can make purchases of artists' latest music releases. Read on if you want to know how to do something like this.

Here we go with part 1, which involves five steps to set up Background Images:

1) Go to whichever content type you want to apply the background image to, click the "MANAGE FIELDS" tab and create a new field ("FIELD TYPE": "Image") for the background image: admin/structure/types/manage/CONTENT-TYPE/fields

2) Click the "MANAGE DISPLAY" tab and change the "FORMAT" of the background image to "<Hidden>", because we want this field to be shown as a background image instead of the field itself: admin/structure/types/manage/CONTENT-TYPE/display

3) Now it's time to install the Background Images module. Make sure to enable both "Background Image" (needed for step #4) & "Background Image UI" (needed for step #5).

The next two steps can be confusing, so just do exactly as I say first to at least get the module itself going. Then you can experiment later. :P

4) THE MOST IMPORTANT SETTING to set up is the "Selector" field here: admin/config/content/background-image

First, let's take care of the easy fields. Choose the right settings for "Node Type" (the type you want to apply the background image to from step #1) and "Node field" (the field for the background image from step #1). Then you might want to choose "Tiled (repeat)" for "Background Repeat" (to initially see as much of your screen displaying the background image, repeated horizontally and vertically).

You can ignore the rest for now and go back to focus on the dreaded "Selector" field. Pay special attention to this field, because entering wrong information here will most likely be the main reason why your background image won't show up at all. For example, I can tell you that if you use Bartik (the default theme for Drupal 7), the field should be "#main-wrapper". If you use a custom theme, you would have to figure out what this needs to be. To make matters worse, the screenshot used in the module's home page has the field as "body" (which most likely will NOT work at all for pretty much everybody lol): https://www.drupal.org/files/project-images/bg_image_settings.png

Remember, you can't just put anything you want here. If you're not using Bartik, you'll need at least a basic knowledge of CSS to figure out what this field should be.

UPDATE (May 7, 2016): I'm currently switching ALL of my Drupal websites to use Bootstrap (a set of tutorials for which will also be available in the near future). Here is the correct field for this awesome theme: ".main-container"

5) But we're not done yet. The background image still won't show up until you complete this last step: admin/content/background-images

Just fill in the "PATH", choose the correct "BACKGROUND IMAGE NODE" for the resulting dropdown menu, and click "Save Changes". Then go to the page you just entered here to see the background image finally show up.

I personally don't use the module this way though, as this method is mainly for site admins. But this is how this module itself basically works. But! Don't worry too much, as I'll show you in part 2 how this can be easily overcome so that members of your site can use this module too.

And that's it. Stay tuned, as my next blog post will cover part 2. :)

P.S. Let me know in the comments if you're a total newbie especially regarding step #4, and I'll do my very best to help out as much as I can.

Jan 30 2016
Jay
Jan 30

* Blue: The code from a Stack Exchange developer that saved my life and got the ball rolling for everything else.
* Green: My own comments for the purpose of this blog post.
* Orange: Additional edits made by me.
* Pink: Code that was deleted by me.
* Yellow: Code that was copied and pasted by me.

When using the Fivestar module (version 7.x-2.1), I can show the result in three ways:

1) "As Stars" - This has an additional setting called "Text to display under the stars". For example, it'll look something like the following if I choose "Both user's and average vote":

*********

Your rating: 9 Average: 8.1 (235 votes)

2) "Rating" - There is no additional option whatsoever, and the above example will just look like the following:

8.1/10

3) "Percentage" - I've never even used this option, so I'm just gonna ignore it in this blog post.

Today I'll show you how I customized the display of this awesome module to almost exactly how I always wanted it, including:

A) How to make the "Rating" option show the total number of votes, just like the "As Stars" option, like the following:

8.1/10 (235 votes)

B) How to capitalize "vote" & "votes" to "Vote" & "Votes" for both the "As Stars" & "Rating" options, like the following:

*********

Your rating: 9 Average: 8.1 (235 Votes)

8.1/10 (235 Votes)

C) How to add get the rating of the "As Stars" option to look just like the rating of the "Rating" option, like the following:

*********

Your rating: 9/10 Average: 8.1/10 (235 Votes)

D) How to add a comma after the rating of "Your rating", like the following:

*********

Your rating: 9/10, Average: 8.1/10 (235 Votes)

E) How to change the default rating of "0/10" for the "Rating" option to "No votes yet", just like how it works for the "As Stars" option, like the following:

*********

No votes yet

As for the code that takes care of step E, I actually don't know lol. All I know is that the code that I used below automatically took care of this problem for me. :D

In summary, I'm a consistency freak (in case you haven't noticed already), and basically I mostly just wanted the two options to look consistent in display. The following is the full code that I originally took from the module and edited, that you just have to include in your template.php file:

// These are lines 279 - 301 of the fivestar.theme.inc file that I edited and then added to one of the functions below.
if (isset($votes)) {
if (!isset($user_rating) && !isset($average_rating)) {
$div_class = 'count';
}
if ($votes === 0) {
$output = '<span class="empty">'. t('No votes yet') .'</span>';
}
else {
if (!empty($microdata['rating_count']['#attributes'])) {
$rating_count_microdata = drupal_attributes($microdata['rating_count']['#attributes']);
}

// We don't directly substitute $votes (i.e. use '@count') in format_plural,
// because it has a span around it which is not translatable.
$votes_str = format_plural($votes, '!cnt vote', '!cnt votes', array(
'!cnt' => '<span ' . $rating_count_microdata . '>' . intval($votes) . '</span>'));
if (isset($user_rating) || isset($average_rating)) {
$output .= ' <span class="total-votes">(' . $votes_str . ')</span>';
}
else {
$output .= ' <span class="total-votes">' . $votes_str . '</span>';
}

}
}

// These are lines 126 - 140 of the fivestar.theme.inc file, with lines 279 - 301 from above added towards the end.
function YOURTHEMENAME_fivestar_formatter_rating($variables) {
$element = $variables['element'];
$votes = $variables['element']['#item']['count'];
$rating_count_microdata = NULL;

if (empty($element['#item']['average'])) {
$element['#item']['average'] = 0;
}
// Get number of stars.
$stars = (empty($element['#instance_settings']['stars'])) ? 5 : $element['#instance_settings']['stars'];
$average = $element['#item']['average'];
// Rating is X out of Y stars.
$rating = round(($average/100) * $stars, 1);
$output = $rating . '/' . $stars;

// Note the orange parts I that edited, after adding lines 279 - 301 from above, after deleting the pink parts.
if (isset($votes)) {
if (!isset($user_rating) && !isset($average_rating)) {
$div_class = 'count';
}
if ($votes === 0) {
$output = '<span class="empty">'. t('No votes yet') .'</span>';
}
else {
// We don't directly substitute $votes (i.e. use '@count') in format_plural,
// because it has a span around it which is not translatable.
$votes_str = format_plural($votes, '!cnt Vote', '!cnt Votes', array(
'!cnt' => '<span ' . $rating_count_microdata . '>' . intval($votes) . '</span>'));
$output .= ' <span class="total-votes">(' . $votes_str . ')</span>';
}
}

return $output;
}

// These are lines 248 - 306 of the fivestar.theme.inc file.
function YOURTHEMENAME_fivestar_summary($variables) {
$microdata = $variables['microdata'];
extract($variables, EXTR_SKIP);
$output = '';
$div_class = '';
$average_rating_microdata = '';
$rating_count_microdata = '';
if (isset($user_rating)) {
$div_class = isset($votes) ? 'user-count' : 'user';
$user_stars = round(($user_rating * $stars) / 100, 1);
$user_stars = $user_stars . '/10';
$output .= '<span class="user-rating">' . t('Your rating: <span>!stars</span>', array('!stars' => $user_rating ? $user_stars : t('None'))) . ',</span>';
}
if (isset($user_rating) && isset($average_rating)) {
$output .= ' ';
}
if (isset($average_rating)) {
if (isset($user_rating)) {
$div_class = 'combo';
}
else {
$div_class = isset($votes) ? 'average-count' : 'average';
}

$average_stars = round(($average_rating * $stars) / 100, 1);
if (!empty($microdata['average_rating']['#attributes'])) {
$average_rating_microdata = drupal_attributes($microdata['average_rating']['#attributes']);
}
$output .= '<span class="average-rating">' . t('Average: !stars',
array('!stars' => "<span $average_rating_microdata>$average_stars</span>")) . '/10</span>';
}

if (isset($votes)) {
if (!isset($user_rating) && !isset($average_rating)) {
$div_class = 'count';
}
if ($votes === 0) {
$output = '<span class="empty">'. t('No votes yet') .'</span>';
}
else {
if (!empty($microdata['rating_count']['#attributes'])) {
$rating_count_microdata = drupal_attributes($microdata['rating_count']['#attributes']);
}
// We don't directly substitute $votes (i.e. use '@count') in format_plural,
// because it has a span around it which is not translatable.
$votes_str = format_plural($votes, '!cnt Vote', '!cnt Votes', array(
'!cnt' => '<span ' . $rating_count_microdata . '>' . intval($votes) . '</span>'));
if (isset($user_rating) || isset($average_rating)) {
$output .= ' <span class="total-votes">(' . $votes_str . ')</span>';
}
else {
$output .= ' <span class="total-votes">' . $votes_str . '</span>';
}
}
}

$output = '<div class="fivestar-summary fivestar-summary-' . $div_class . '">' . $output . '</div>';
return $output;
}

And that's it. If you want to see how all this exactly looks like on a live website, take a look at a specific page that displays both options in my media startup company, Mediography.by: John '00' Fleming - Embed Media Rating Widgets

Nov 19 2015
Jay
Nov 19

Today is a big day for Drupal, as Drupal 8.0.0 just got released a few hours ago. So since people from all over the world will be installing it today to build websites, I'll show you guys how to solve some unusual problems that you might encounter during the installation process. When you install it on your web server, you will most likely be just fine. However, you will undoubtedly encounter the following three problems if you decide to install it on a brand new installation of WampServer (current version: 2.5):

Drupal 8.0.0 Requirements Problem

Drupal 8.0.0 Requirements Problem

The first two error messages are just warning messages, but the third one must be resolved for the installation to continue. Here's how to easily take care of all of them:

1) "Clean URLs - Disabled"

A) Open the "httpd.conf" file in C:\wamp\bin\apache\apache2.4.9\conf directory.

B) Find the line 154 that says: LoadModule rewrite_module modules/mod_rewrite.so

C) Get rid of "#" in front of it, and then save the file.

2) "PHP OPcode caching - Not enabled"

A) Open the php.ini link file in C:\wamp\bin\apache\apache2.4.9\bin. If you use Notepad++ like I do, you can just right-click this weird looking link file to open it (because double-clicking it won't do anything). For others, another easy way to open it is to click the green "W" icon on the bottom right of your screen, hover your mouse over "PHP", and then click on the "php.ini" file.

It's important to note here that editing the php.ini file (which is what I initially edited and then banged my head against the wall a few times wondering why the freaking hell it still didn't work?!?) in C:\Wamp\bin\php\php5.5.12 for steps 2 & 3 will NOT work. Why? I have no freaking clue. :P

B) Find the line 1943 that says: zend_extension = "c:/wamp/bin/php/php5.5.12/zend_ext/php_xdebug-2.2.5-5.5-vc11-x86_64.dll"

C) Add the following code just underneath it (and then go straight to step 3 without saving the file): zend_extension = "c:/wamp/bin/php/php5.5.12/ext/php_opcache.dll"

3) "Xdebug settings - xdebug.max_nesting_level is set to 100."

A) On the same exact php.ini link file from step 2, find the line 1953 (the last line of the file) that says: xdebug.max_nesting_level=100

B) Change the number from 100 to 256.

After going through all the steps, click the green "W" icon on the bottom right of your screen, then click "Restart All Services" (in the worst case scenario, you might have to close WampServer and restart the program), and then try refreshing the installation page to confirm that all of the error messages are gone.

And that's it. The rest of the installation process should be a breeze. Now go have some fun with building your new kickass website, and welcome to the wonderful new world of Drupal 8.0.0. :)

UPDATE (November 20, 2015): P.S. I've only tried installing Drupal 8.0.0 on a LAMP stack on Digital Ocean and WampServer on my PC, so I have no idea how it'll do on MAMP or XAMPP. But if you get similar error messages on them, at least you know what files to look for in order to take care of them. YMMV. Good luck.

Mar 08 2015
Jay
Mar 08

Over the past few years, I've written quite a bit of blog posts about Drupal. So I finally submitted my application to include them on Planet Drupal. Here is everything you need to know about it:

1) What is Planet Drupal?

Planet Drupal is an aggregated list of feeds about Drupal from around the web. Its purpose is the sharing of useful and relevant knowledge regarding Drupal, and it helps others learn details about Drupal that they might not otherwise encounter.

2) Why should I get on Planet Drupal?

A) If you're like me and write a lot about Drupal already, you might as well get more people to read your blog posts.

B) This helps boost your Alexa ranking.

C) Some of my blog posts are answers to my own questions, a few of which were difficult enough that I am not sure if I did everything correctly in terms of security, etc. The more people look at my blog posts, the better the chance that a guru of a certain topic might help me correct something that I might have done incorrectly.

3) How do I get on Planet Drupal?

Read and follow the Planet Drupal guidelines. Then keep reading if you get stuck in any of the steps, some of which are addressed below:

A) How do I create a "Drupal Planet" taxonomy term?

Go to Yoursite.com/admin/structure/taxonomy/tags.

B) How do I trim, or use a summary for my feed content so that each one is between 600 and 1000 characters?

I) Go to Yoursite.com/admin/structure/types/manage/blog/display and click on "CUSTOM DISPLAY SETTINGS" towards the bottom of the page.

II) Check "RSS" and then click "Save".

III) Go to Yoursite.com/admin/structure/types/manage/blog/display/rss.

IV) Change the "FORMAT" of "Body" from "Default" to "Summary or trimmed", and choose either "600" or "1000" for the "Trim length" and then click "Save".

C) How do I use Pathauto with the feed?

I'm a perfectionist, so I don't want my Planet Drupal feed to be http://jayl.ee/taxonomy/term/1/feed. If you want to know how I got it to redirect to http://jayl.ee/tag/drupal-planet/feed, just follow the next two simple steps:

I) Install the Global Redirect & Sub-Pathauto modules.

II) Install a patch that gets the two modules to work together.

4) Miscellaneous

I'll update this blog post after I get accepted into and play around a bit with Planet Drupal. I'll try to answer some odd questions such as the following:

A) What happens if I update my blog post at a later time (such as a few months later when I update this very blog post after I get answers to all of my own questions)? Will it show up again on Planet Drupal?

B) I'll add some more FAQs here that I'm sure will come up as time passes by.

Feb 27 2015
Jay
Feb 27

UPDATE: I will write a series of blog posts in the near future on how to configure each of these modules to work well with Flag Lists.

Today I'll be doing a presentation on a Drupal module called Flag Lists at SANDcamp. Here is the description:

We've all seen examples of them:

These are custom lists made by members of community websites, and were previously not possible to do via Drupal without custom coding. They are also different from official lists made by administrators of media websites (such as Rolling Stone's 500 greatest albums of all time), which can be created via Nodequeue (or Entityqueue). But thanks to Flag Lists, you can now easily let everyone in your website create such custom lists as part of their profile pages.

I was the main alpha-tester of the recently released 7.x-3.0-alpha1 version, which finally works with the latest version of the Flag module. While testing, I was eventually able to successfully create some really cool flags & flag lists by integrating the following 10 modules:

  1. Dialog
  2. Disqus
  3. DraggableViews
  4. Flag
  5. Flagging Form
  6. Flag Lists
  7. Global Redirect
  8. Pathauto
  9. Sub-Pathauto
  10. Userpoints Flag

This session will be about how to configure and integrate all of these modules. I will go through every major step of the configuration process for all 10 modules on a test site so that anyone can integrate them easily and create mind-blowing lists by the end of the session. I will also mention a list of desirable features not yet available for these modules that deserve attention.

Everyone is welcome to participate as this will be a hands-on session. If you want to follow along, just download and enable the above modules to a test site right before the session. Feel free to also read my recent blog post that will help you get a clear understanding of the main differences between Entityqueue, Flag and Flag Lists.

Feb 22 2015
Jay
Feb 22

On January 31, I installed SSL on my startup website, Mediography.by. If you want to know how to do this, look no further as this blog post will focus on the actual installation process for an SSL certificate. Then my next blog post will cover configuration issues that should address almost every newbie's problems. So let's get started.

First, a disclaimer. I am no Linux expert, and went through pretty much every error message that anybody could possibly imagine. The good news is that I solved every single one of my own problems, but the bad news is that if you're trying to do something that's not covered here (like installing multiple SSL certificates), I probably don't know the answer. So if you have any questions, make sure it's related to what is covered here. And if you are an expert and think that any of the information here is still incomplete or wrong, please let me know and I will correct it.

For Mediography.by, the SSL certificate was a Comodo PositiveSSL purchased on Black Friday for $0.98 from Namecheap and installed on a DigitalOcean server with CentOS 6.x & Virtualmin that has one IP address shared by multiple websites that were all built with Drupal (unlike the past, you no longer need a dedicated IP address to install an SSL certificate). So if you have a different configuration, you might have to make adjustments accordingly. Here we go.

A) https://www.digitalocean.com/community/questions/how-can-i-install-a-com...

Since I'm on DigitalOcean, this is a good place to start. According to DigitalOcean staff moderator kamaln7, we're supposed to follow some specific steps of instructions in the following two links:

I) https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-cer... -> Follow steps 1 & 2.

II) https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-cer... -> Follow steps 2 & 3.

III) https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-cer... -> Follow steps 4 & 5.

The problem is that you will most likely get lost at some point if you don't really know what you're doing. This is because the steps are technically correct, but is missing some valuable information that will leave a lot of people scratching their heads. So instead, follow my foolproof guide. I'll show you every single step, including necessary Linux commands for the newbies.

B) https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-cer...

Follow steps 1 & 2 verbatim as they are correct. Then you're supposed to go to step C below. But before you do that, you need to first go to the directory you just created. To do that, type the following command:

cd /etc/httpd/ssl

Now proceed to step C.

C) https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-cer...

Here you're supposed to go through just steps 2 & 3. However, the first part of step 2 is incorrect. The number 1024 should be 2048 like the following (or else you will eventually get an error in step D):

sudo openssl genrsa -des3 -out server.key 2048

Now follow the rest of the steps 2 & 3. Then you're supposed to go to step F. However, this is where you actually need to activate the SSL certificate you purchased, and then upload it to your server. So proceed to steps D & E first.

D) https://www.namecheap.com/support/knowledgebase/article.aspx/794/67/how-...

Just follow the instructions, and you should be fine. I got my SSL certificate emailed to me from Comodo within a few minutes.

E) Upload the SSL certificate (server.crt) to your server in the /etc/httpd/ssl directory that you created in step B. So in DigitalOcean, you would first log into your server via FTP as root and upload the file to the root directory. Then type the following command in the console to move it to the correct directory:

sudo mv server.crt /etc/httpd/ssl/server.crt

F) https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-cer...

Follow step 4 verbatim as it is correct. But let's take a closer look at step 5. Let's say you type what it says, which is the following command:

/etc/init.d/httpd restart

It'll work as long as you're logged in as root. If not, you will most likely get an error message or two, such as the following:

Stopping httpd: [FAILED]
Starting httpd: Warning: DocumentRoot [/home/yourwebsite/public_html] does not exist
Syntax error on line xxx of /etc/httpd/conf/httpd.conf:
Wrapper /home/yourwebsite/fcgi-bin/php5.fcgi cannot be accessed: (13)Permission denied
[FAILED]

If you're not logged in as root, the correct command should start with "sudo":

sudo /etc/init.d/httpd restart

Here's a screenshot that shows the whole process:

Httpd Restart Error Messages

Httpd Restart Error Messages

Also, note the "/" before the "etc". If you forget it, you'll get a "command not found" error. That's how Linux works. You have to get every single character correct, or you'll only be close but no cigar.

And that's it. Good luck!

P.S. My next blog post will focus on various configuration options (especially for servers with one IP address shared by multiple websites), including the following:
* Redirecting http:// to https:// for the website that has the SSL certificate.
* Redirecting https:// to http:// for the rest of your websites on your server. For example, https://jayl.ee kept redirecting to https://mediography.by, so I had to figure out how to redirect it to http://jayl.ee. This caused me some minor headaches.
* How to take care of the infamous "Index of /" problem. This caused me some major headaches.

Jan 29 2015
Jay
Jan 29

Look to your right and you'll see a list of social icons under "Follow Me" (my other websites Mediography.by & Vector.dj have their own versions). To do this I use a Drupal module called Follow on all of my websites because it's my favorite one for letting people know where to follow me on social networks. Today I'll show you some slick tricks on customizing this module. Here we go:

1) One of the main problems with this module is the lack of a good consistent image set. For example, most of the images are round, but some are square. And only images of the most popular websites are supported.

But if you noticed, my image sets are much nicer. Here's what you can do to get the same results:

A) Google "social icons", and you'll find a gazillion different sets of icons. Choose whatever set that has all the icons you need for your website. And if you are thinking about creating a custom icon so that members of your website can enter whatever link they want, make sure whatever you create will match the ones you use.

I got mine from Enfuzed (download) for their simplicity. Most of their icons are perfect, and I was able to easily create a better looking icon for SoundCloud. I was also able to create a matched set for Mixcloud & Mediography.by that integrated well with them.

Here is the full list of all the ones that I use on my websites (download), to save you a bit of time from having to create them yourself by cropping them one by one from the original file:

          
          
          

Mediography.by Social Media Icons

B) Then just replace the existing icons with this set.

2) When you go to configure the module in admin/config/services/follow, you'll notice a default set of social networks listed. To change this list, just open the follow.inc file and change the code in lines 325 - 393 to exactly how you want it.

3) If you want to add the capability for your users to add custom links, you'll need to do a couple of things:

A) First, add the following code to line 325 in the follow.inc file:

'custom' => array(
'title' => t('Custom'),
'domain' => '',
),

B) Now upload the file, and try submitting a custom link. You should get an error message. To get rid of this problem, change the code in lines 210 - 214 in the follow.inc file to the following:

form_error($form, $message);
}
//else {
//$message = t('The specified path is invalid. Please enter a path on this site (e.g. rss.xml or taxonomy/term/1/feed).');
//}

The result of all this can be best seen at my Mediography.by profile page. Towards the right top, you'll see the social icons for following the company itself. And towards the right, you'll see the social icons for following me.

I hope this blog post inspires you to customize this awesome module and use it to its full potential. Good luck! :)

Dec 14 2014
Jay
Dec 14

Last month I showed you guys how to show the Pinterest Pin It button's pin count for any image, and talked about a limitation with Colorbox / Lightbox. In today's blog post, I will show you guys how to show the Pinterest Pin It button when hovering over any image, including one that shows up via Colorbox / Lightbox. I'll use a Drupal module called Pinterest Hover Button as an example:

1) PInterest's Widget Builder

It's actually a good idea to take a quick look at Pinterest's Widget Builder, just to see how everything works and what's available for customization. If you take a look at the code for "Image Hover", you'll realize just how simple the actual code is that makes the magic happen (for the following, I chose "Small", "Rectangular", "Red" & "English" under "Appearance"):

<!-- Please call pinit.js only once per page -->
<script type="text/javascript" async defer data-pin-color="red" data-pin-hover="true" src="http://jayl.ee//assets.pinterest.com/js/pinit.js"></script>

All you have to do is put this code into whatever page that has an image. So for Drupal, you just put the code in the header section of your theme's html.tpl.php file. The only catch here is that the Pin It button will show up on every single image that you hover over in your entire website. If you don't want this to happen, read on.

2) Pinterest Hover Button

This module should suffice for most newbies that don't want to mess around with any code whatsoever. The module is very simple and easy to install and set up, and the button can be enabled per content type. The problem for me was that it didn't work well for my company website, Mediography.by. So I had to think a bit before coming up with a solution that fit my needs.

3) Custom Configuration

Since the Views configuration I have on my website is not of a typical use case scenario, I eventually had to figure out my own way to make everything work exactly the way I wanted to. What was surprising though was that using the knowledge I gained from step #1, I realized that the only thing I actually had to do was to put the code from the Widget Builder into the header area of all the Views that had images (meaning I just had to add "Global: Text area" in the "HEADER" section, and add the code in there).

The most important lesson here is realizing just how easy it actually is to learn how the Widget Builder works, and the rest is just figuring out exactly where to copy & paste the code. And that's it. Good luck!

P.S. If you want to visualize how step #3 works with Colorbox / Lightbox, take a look at the following page:

http://mediography.by/artist/john-'00'-fleming/images

First, click any of the thumbnail images and hover over the resulting image in overlay mode to make the Pin It button show up towards the left top of it. Then click on the image to get to the next one, and hover over that on to see the new Pin It button showing up accordingly. Now rinse, repeat and have fun. ;)

Dec 05 2014
Jay
Dec 05

I've been waiting patiently during the past few years for a stable version of a Drupal module called Flag Lists to come out. So when a developer finally came forward recently to get the job done, I jumped at the chance to become the main beta-tester. So what is Flag Lists and what sets it apart from similar modules such as Entityqueue & Flag? Read on to find out:

Over the years, there has been a lot of confusion mainly between Flag & Nodequeue. There were requests to merge them that were denied with explanations. There's also a comparison chart that's seriously outdated. And then of course there were requests for Flag Lists to become stable for years. But even the current maintainer of Flag was initially a bit confused about Flag Lists when I first asked to get it featured on its home page, so I had to explain it thoroughly to get him to understand it. Or not:

@wwwjaylee @ezrabg @merlinofchaos I'm still confused by Flag Lists... ;)

— Joachim (@joachimnicolas) December 7, 2014

Joachim, I thought you were a genius? :P

Here's a summary of the main features of each module:

1) Entityqueue is basically a rewrite of Nodequeue for Drupal 7 to support entities. There is a similar module called Entity Collection (which should definitely not be confused with another popular module called Field Collection). Nodequeue is a popular module written mainly for admins. Here is a list of the best use case scenarios:

A) Forum website where a block displays the 10 most important discussions of the day.
B) Media news website where a block displays the top news report of the day.
C) Music blog website where a block displays a random list of the author's favorite albums ever.

Here is a list of what makes this module different from others:

I) Unlike Views, it can be sorted according to however the admin wants.
II) Unlike Flag and Flag Lists, Entityqueue is mainly for admins. This means that general users cannot manipulate the queue in any way except view it.
III) The queue can have a fixed number of nodes after which if you add another node to it, the node at the end will be automatically removed.

2) Flag is for both admins and general users, and is a versatile module that can do a lot of things. There is a somewhat similar module called Commerce Wishlist. Here is a list of the best use case scenarios:

A) Flagging a node as offensive.
B) A general "Wish List" feature at various online stores.

Here is a list of what makes this module different:

I) Entityqueue is mainly for admins and Flag Lists is mainly for general users. Flag lies somewhere in between where both admins and general users can manipulate it in some way.
II) Unlike Flag Lists, the users cannot change the title since the admin initially creates the flag. But they can add whatever entity they want to it. This is exactly the case with Book Depository's Wish List. Think of it as like a list that's both global & individual where the users each have their own wish list, but has to be called "Wish List" and not "My Want List".

3) Flag Lists is mainly for general users, such as members of a community website. Here is a list of the best use case scenarios:

A) Any list that members at IMDb can create. As a matter of fact, Flag Lists was created specifically for this type of list.
B) "My Favorite Digital SLR Cameras", "The Greatest Underrated Books Ever", "The Best Classic Rock Albums Ever", etc. All those cool lists you've seen people create online, you can finally create them in Drupal with Flag Lists.
C) The "Wish List" feature at Amazon. Wait. Shouldn't this be under Flag? Allow me to explain:

Amazon Custom Wish List

Amazon Wish List

An e-commerce store typically has a default Wish List feature, which would fall under Flag. This is because everybody gets their own wish list, but it has to be called "Wish List" whether we like it or not since the store created it by default for all of its members.

However, Amazon's version actually allows members to create their own "Wish List", which would fall under Flag Lists. This is because members get to create it, they can call it whatever they want, and they can add whatever product that they can find on Amazon to this list. Also as far as I know, they can create an infinite amount of this type of list as they want. So even though Amazon calls them wish lists, these are technically custom lists.

Therefore, this is where you can actually create a list called "My Want List" that I mentioned above if you prefer it over "Wish List". Or "Own List". Or all three, and a few dozen others. You get the idea.

Here is a list of what makes this module different:

I) Entityqueue is mainly for admins. Flag is for both admins and general users. Flag Lists is mainly for general users.
II) Unlike Flag, the general users can create whatever list they want, however many lists they want, and add whatever node they want to whatever list they want. Think of it as like a custom list where everything is literally custom. The possibilities are endless.

So what's the status of these modules? Entityqueue and Flag have been stable for a while. But as of this writing, there is just one major beta blocker (which originally was an alpha blocker until the current maintainer decided to release an alpha version first) for Flag Lists, and a couple of minor bugs worth mentioning:

* DraggableViews - Permissions: Once configured, this module will do something amazing for Flag & Flag Lists. However, this otherwise awesome module is rendered completely useless for everyone except admins until this minor but very irritating bug is fixed.

* Flag - Permissions: Commenting features such as Disqus won't work with Flag until this small bug is fixed.

* Flag Lists - Fields: This is the only major beta blocker that I can think of. I will start using this module at Mediography.by as soon as this bug is taken care of.

One last thing worth mentioning here is that as you can see from the permissions issue just mentioned above, Flag is private by default. So anonymous users can't see your wish list whether you like it or not. Flag Lists is better in that you can make it so that anonymous users can see your custom lists. However, there is a minor limitation here in that the permissions can only be set by the admins.

I see a day when both modules support what I think is a cool feature that allows members themselves to choose privacy settings so that any list can be individually set to be either private or public, just like what you see on the Amazon Wish List screenshot above ("List is public"). That would be cherry on my pie. Now if enough people can go bug the maintainers to see if they can make this happen. ;)

P.S. If the beta blocker mentioned above can be fixed before the end of the year (developers, are you guys listening?), I plan on doing a presentation at SANDcamp 2015 on how to create awesome lists by integrating the following 10 modules: Dialog, Disqus, DraggableViews, Flag, Flag Lists, Flagging Form, Global Redirect, Pathauto, Sub-Pathauto & Views

Nov 18 2014
Jay
Nov 18

This blog post was initially going to be about why the Pinterest Pin It button's pin count doesn't show up for the "Any Image" button type in the Widget Builder. But while doing research, I found out that Pinterest finally enabled this as sort of a hidden feature. The problem was that it still didn't work, and I was so close to just giving up until I decided to give it one last try. Luckily I was able to finally figure it out and get it working. This is after initially looking into this problem about two years ago.

So today I am happy to be able to show you how to make the pin count show up for any image, using a Drupal module called ShareBar as an example. Here we go:

1) The basics

https://business.pinterest.com/en/widget-builder#do_pin_it_button -> If you compare the code for "One Image" & "Any Image", you'll realize that the actual code that does the magic is as follows:

data-pin-config="above" -> This shows the pin count bubble above the pin it button.
data-pin-config="beside" -> This shows the pin count bubble next to the pin it button on the right side.

The main problem was that this code never worked for "Any Image". Until about a year ago.

2) When did the pin count start showing up for any image?

https://github.com/pinterest/widgets/issues/5 -> According to Kent Brewster (who is currently one of Pinterest's engineers), this was initially left out intentionally. But the feature started working on November 20, 2013 (even though it's still not in the widget builder as of today, which is almost exactly a year later).

@wwwjaylee we need to fix the widget builder so you can ask for a count on Any Image button. [adding to list]

— Kent Brewster (@kentbrew) November 18, 2014

Update: This is now on Kent Brewster's to-do list.

The problem was that even after including the code above, I still couldn't see the pin count and was about to give up.

3) The CSS problem

This is where I gave it one last try and eventually had my aha moment when I realized that the pin count bubble was actually there, but that the button right above this button hid it. So the following code is what made the pin count finally show up:

margin-top: 30px;

@wwwjaylee post seems fine, thanks for making it! I know about the padding bug, but am afraid that fixing it would break a lot of layouts.

— Kent Brewster (@kentbrew) November 18, 2014

Update: Kent Brewster already knew about this issue, but had a reason not to fix it.

4) The full code

Here is the full code that makes the magic happen:

<div style="margin-top: 30px;"><a href="http://jayl.ee//www.pinterest.com/pin/create/button/" data-pin-do="buttonBookmark" data-pin-config="above"><img src="http://jayl.ee//assets.pinterest.com/images/pidgets/pinit_fg_en_rect_gray_20.png" /></a>
<!-- Please call pinit.js only once per page -->
<script type="text/javascript" async src="http://jayl.ee//assets.pinterest.com/js/pinit.js"></script></div>

In the code above, the highlighted code is what's behind the magic. The rest came straight from the Widget Builder, with settings that are best suited to go well with other buttons in the ShareBar module ("Small" & "Gray", explained further in step #5).

So for the ShareBar module, the code above is what you put into the "Big Button" field (the vertical version). Can you guess what the code should be for the "Small Button" field (the horizontal version)? The following code should do the trick:

<a href="http://jayl.ee//www.pinterest.com/pin/create/button/" data-pin-do="buttonBookmark" data-pin-config="beside"><img src="http://jayl.ee//assets.pinterest.com/images/pidgets/pinit_fg_en_rect_gray_20.png" /></a>
<!-- Please call pinit.js only once per page -->
<script type="text/javascript" async src="http://jayl.ee//assets.pinterest.com/js/pinit.js"></script>

Note the highlighted code and the fact that the CSS code from step #3 is no longer needed, although you might need something like margin-right: XXpx; if you have another button to the right of this button and they're too close to each other. I should also mention that unlike the vertical version, the pin count will NOT show up for the horizontal version if the count is "0". It will start showing up though once the number is at least "1". Hopefully Kent Brewster will let somebody know about this and fix it. :P

@wwwjaylee that's another legacy thing, and is also on my list.

— Kent Brewster (@kentbrew) November 18, 2014

Update: Kent Brewster is already aware of this issue as well.

5) Customization

https://business.pinterest.com/en/widget-builder#do_pin_it_button -> You definitely want to play around in this page if you want to learn how to customize your button. Currently there are four settings that you can change, so let's take a look at a couple of main ones:

A) "Large" or "Small" -> You have a choice of a large or a small button.

B) "Red", "Gray" or "White" -> You have a choice of a red, gray or white button.

Note how the code changes as you change each setting, and you'll have a good idea of how it all works.

6) What does it actually look like?

The following is a screenshot from Mediography.by (I decided not to use it after all, due to yet another problem explained in step #7):

Pinterest Pin It Button's Pin Count

7) Colorbox / Lightbox issues

It looks nice, doesn't it? But after I got the pin count to show up, I realized that I still had one more problem because the button doesn't work with images that show up via Colorbox / Lightbox. This is of course the intended behavior since there is no way to press the button when you view an image via these plugins, since it gets disabled behind them.

So this is when I started looking into a module called Pinterest Hover Button, which should take care of this final problem for most people. But since it doesn't work well for Mediography.by, I had to figure out yet another way to make it work. My next blog post will cover this topic, which should be done within the next few days. Stay tuned! :)

P.S. If you want to visualize what I'm talking about at step #7, take a look at the following page and click the thumbnail image: http://mediography.by/artist/john-'00'-fleming

Nov 09 2014
Jay
Nov 09

Look to your left and you'll see a social sharebar (my other websites Mediography.by & Vector.dj have their own versions). I use this module on all of my websites because it's my favorite one for social sharing due to its simplicity. On larger screens, you'll see the vertical version. But if you shrink your browser, it'll automatically turn to a horizontal version. I like it a lot. Today I'll show you how to configure & customize this for Drupal websites. Here we go:

1) Download the module called "ShareBar".

2) Upload, install & enable the module.

3) In your website's admin settings, go to Configuration -> ShareBar -> Configure ShareBar.

4) Basic configuration is pretty straightforward for "ADD SHAREBAR" & "DISPLAY OPTIONS", so I'll focus on how to customize the module so that it matches whatever layout you have on your website. The first customization you can do is for each button in "BUTTONS" (used for CSS below in step #6). For example, the following is what I have for the Facebook button used on this website:

I) "Big Button": No changes were made to the default code.
II) "Small Button": <li class="fbx"><div class="btn_fbx">Default code</div></li>

5) The second customization you can do is available in "CUSTOMIZE", so fill in the four fields:

A) "Sharebar Width": The default is 75, which is actually a good number. All of my websites use this default number.

B) "Twitter Username": Enter your Twitter username.

C) "Sharebar Background Color": For this website, I left this field empty and then used custom CSS code (second line of code as shown below at step #6A) to give it a transparent look to match the layout of the blocks at the right. For Mediography.by, I have it at F0F0F6 to match the background colors of the blocks on that website. For Vector.dj, I have it at C8B694.

D) "Sharebar Border Color": For this website, I left this field empty and then used custom CSS code (third line of code as shown below at step #6A) to match the layout of the blocks at the right. For Mediography.by I have it at F0F0F6 to match the background colors of the blocks on that website. For Vector.dj, I have it at AAAAAA.

6) Add some custom CSS code as necessary. There are two parts:

A) Vertical: CSS code for the vertical sharebar ("Big Button"). For this website, I had to add the following lines of code to match the layout of the blocks at the right:

body ul#sharebar {
background: transparent url(../images/white-trans-10.png) 0 0 repeat;
border-color: transparent;
}

B) Horizontal: CSS code for the horizontal sharebar ("Small Button"). Depending on how you have your website set up, you will realize that enabling too many buttons will cause issues, since you want to fit them all in one line if possible. Your code also needs to account for what percentage of width each button takes. For example, my website has four buttons, so each one should be 25% (which is actually 24% because I wanted some space before the first button). You also want to take expanding width of the sharing numbers into account because they will all be initially 0, but will eventually turn into double digits, triple digits and so on. Here's how I have my CSS code set up to take care of everything:

body ul#sharebarx li {
margin-right: 0px;
width: 24%;
}

body ul#sharebarx li.fbx {
padding-left: 4%;
}

.btn_fbx {
margin-left: -6px;
width: auto;
}

.btn_gpx {
margin-left: 52px;
width: auto;
}

.btn_lix {
margin-left: 51px;
width: auto;
}

.btn_twx {
padding-left: 46px;
width: auto;
}

And that's it. Good luck!

P.S. In the next few weeks, I'll show you how to configure & customize a Drupal module called Follow.

Oct 30 2014
Jay
Oct 30

Today I'll show you guys a few tricks that'll help you become a master Drupal sitebuilder. I'll use Views PHP as an example, a module that gave me a lot of headaches. The following blog post is a reminder that it's the little things that sometimes make all the difference in the world.

Basically, I had to figure out how to make the following piece of code to display correctly:

$row->field_artist_rating

To get this module working, I focused on two issue queues:

1) How to call the value of field within a node so that Views PHP can do its magic.

The first post in this issue queue provided me with enough information to figure out what the differences were between the three places in the module that can take code (I ended up using just the last one): Setup code, Value code and Output code.

2) Variable $row does not contain correct values ($data->_field_data does)

This is where I was initially mislead, spent a significant amount of time on trying out things that were outdated and eventually got dangerously close to just giving up on the module. I eventually learned three important lessons the hard way:

A) Don't be mislead by wrong information.

First of all, the title of the issue queue says that the variable $row doesn't contain correct values. Although that was true over three years ago on April 28, 2011 when it was written, it is now wrong information. To make matters worse, #181 (written just two months ago) says in capital letters: "THIS MODULE IS NOT USEFUL AT ALL NOW." Well guess what? It actually turned out to be a VERY useful module for me.

B) Learn to read the issue queue backwards.

I eventually had my aha moment when I read #167, which mentioned a patch that was commited to 7.x-2.x-dev. If I had tried this version out first, I would've saved myself hours of wasted time.

C) Try out all versions of the module.

There are currently three versions of this module: 7.x-1.0-alpha1, 7.x-1.x-dev & 7.x-2.x-dev. Don't be afraid to try out the development versions, as 7.x-2.x-dev is the one that worked out for me with no problems whatsoever. The following piece of code finally spitted out what it was supposed to spit out:

$row->field_artist_rating

The result? The module helped me display the URLs of rating widgets embedded on any website. See for yourself: V.E.C.T.O.R. - Dance Legacy Volume 001

Sep 04 2014
Jay
Sep 04

I recently made the decision to switch from Facebook Comments and exclusively use Disqus on all of my websites. If any of you are thinking about integrating either of these products into your website, read on to see why I made my decision:

1) Disqus - Advantages:

A) Disqus is platform-agnostic. What this means is that your comments will stay intact whether you switch from WordPress to Drupal and vice versa (or anything else).

B) Disqus is URL-agnostic. What this means is that your comments will stay intact even if you change the title of your post and the URL with it.

C) Disqus is domain-agnostic. What this means is that your comments will stay intact even if you decide to change the domain name of your website, which I think is incredible.

So let's say your domain name was initially http://yourname.net (because of course the .com domain is almost always taken), but later on the http://yourname.com domain name became available and you were lucky enough to acquire it and want to redirect your old domain name to your new one, your comments will not disappear.

Using another example, my startup company, Mediography.by, was initially Discography.by (which now just redirects to Mediography.by), and it had a few comments before the switch to the new URL. I was surprised to find that the comments still showed up after I changed the URL to Mediography.by. The only caveat in this case though is that your Disqus account would have to remain the same. So let's say your domain name was initially http://youroldwebsite.com with http://youroldwebsite.disqus.com as your Disqus account, and you decide to rebrand your website to http://yournewwebsite.com. Your comments will still work as long as you decide not to get a new account at Disqus, which would make it http://yournewwebsite.disqus.com. Maybe this can be a new feature for Disqus to consider, so that all the comments from the old account somehow gets migrated into the new account? ;)

D) Disqus has a robust system for comment notification. This means that you'll get notified via email whenever there is a new comment. And as you can see from the screenshot below, you'll also get notified right via the comment box whenever you get a reply (along with an email):

Disqus Reply Notification

Disqus Reply Notification

What's a bit confusing is that the notification via the comment box is only for when I get a reply, not for new comments. Why? I have no idea. Maybe somebody from Disqus can enlighten me on this subject. :P

E) Disqus is excellent at sorting comments by votes. This is something to consider only if you are thinking about whether to use Disqus over the default commenting system that your CMS offers (or your own), because Facebook's version (sort by likes) works just as well.

2) Disqus - Disadvantages:

A) Not everybody's always logged into Disqus. And since Disqus is still not as ubiquitous as Facebook, it'll be this way for the time being. But since the product has gained critical mass a long time ago, I think this is becoming less and less of a problem as each day goes by.

B) Comments are not viewable by Facebook friends. Comments are however shareable after they are posted via Facebook & Twitter, but that's one more step to take and you have enable it under settings too.

I'm sure there are other disadvantages, but the above two are the main reasons why I initially used Facebook Comments over Disqus.

3) Facebook Comments - Advantages:

A) The main advantage of using Facebook Comments is that it gives the posters the option to post the comments on their profiles, which means their friends will see that they posted on your website.

B) Everybody's always logged into Facebook, so there's almost no need to log in to comment, ever.

C) Facebook Comments is excellent at sorting comments by likes. This is something to consider only if you are thinking about whether to use Facebook Comments over the default commenting system that your CMS offers (or your own), because Disqus' version (sort by votes) works just as well.

4) Facebook Comments - Disadvantages:

A) Facebook Comments is platform-specific. What this means is that if you switch from one platform to another, you will lose ALL of your comments. This is exactly what happened to me when I switched from WordPress to Drupal on this very website. I lost dozens if not hundreds of comments and wasn't too happy. Then I started thinking, what if I upgrade from Drupal 7 (which my website is currently using) to Drupal 8, which is due out circa early next year? Will I lose all of my comments again? If anyone has ever upgraded from Drupal 6 to 7 with all their comments intact, feel free to let me know. But I decided not to bet my luck anymore, as I really am not that confident with this product anymore.

B) Facebook Comments is URL-specific. What this means is that if you change the title of your post and the URL with it, you will lose all of your comments. Your comments will be ok however if you just change the title of your post and leave the initial URL intact. For example, that's what TechCrunch does when they change the title of their articles. The following screenshot is a comment from one of those articles, making fun of it:

TechCrunch URL Comment

TechCrunch URL Comment

C) Facebook Comments is domain-specific. What this means is that you will lose all of your comments if you decide to change the domain name of your website.

D) Facebook Comments has a poor system for comment notification. I don't know about WordPress, but this feature for Drupal sucks shit. What happened was that initially I did get emails every time somebody commented on my website. The problem was that slowly but surely I started to get notified for only some of the new comments, and eventually the emails completely stopped. Why? Who knows.

I'm sure there are other disadvantages, but the above four are the main reasons why I eventually made the switch from Facebook Comments to Disqus.

To summarize, I really think that since Disqus only does comments, it does them better than anyone else on the planet. This is why I also don't think that newer competitors such as Livefyre has any chance of entering this space whatsoever, unless they pivot. In my opinion, Disqus is just that good. But if you are thinking about an alternative solution, just make sure you understand all the potential problems and especially the risks of losing all of your comments.

So there you have it. Whatever commenting platform you decide to use, think about the advantages & disadvantages of each one and hopefully you'll make the right decision all the way from the beginning. Good luck! :)

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web