Simple Social Service links for Drupal 8

27 September 2018

There are couple of online tools, and integration modules to get sharing widget to your site. They rely on JavaScript and the security of your users is questionable. This article will show you how to create a simple yet flexible and safer service sharing widget without line of JavaScript.

Background

The main reason why not to use some of the tools like AddToAny is the security. This is often a case for government or other public facing project such as GovCMS. Sharing widget of these services is not connecting directly to the social service, but it is processed on their servers first. And they can track the user on through the web because of the fingerprint they made. Another reason is that the JS code is often served from a CDN so you don’t know when the code changes and how? Have they put them some malicious script? I don’t want this on my site. And clients often as well. :)

Thankfully each service provide a simple way how to share content and we will use that.

Final example

You can see the final result in action with different styling applied at our example GovCMS 8 demo page (scroll down to the bottom of page).

Site build

First we need to prepare the data structure. For our purpose we will need to create a custom block type, but it can be easily done as a paragraph too.

Custom block name: Social Share
Machine name: [social_share]

And throw in few Boolean fields. One for each service.

Field label: [Human readable name] e.g. “Twitter”
Machine name: [machine_name] e.g. “social_share_twitter” – this one is important and we will use it later.

Go to the manage display screen of the block (/admin/structure/block/block-content/manage/social_share/display) and change the Output format to Custom. Then fill in the Custom output for TRUE with the text you like to see on the link e.g. "Share to twitter".

Social Share labels

Now we are able to create a new block of the Social share type and check some of these checkboxes. Users will see only the Labels as result.

Theming

The fun part is changing the output of the field from simple label to actual share link.
First we need to know how the final link looks like.
Links examples:

Facebook:
http://www.facebook.com/share.php?u=[PAGE_URL]&title=[PAGE_TITLE]

Twitter:
http://twitter.com/intent/tweet?status=[PAGE_TITLE]+[PAGE_URL]

LinkedIn:
http://www.linkedin.com/shareArticle?mini=true&url=[PAGE_URL]&title=[PAGE_TITLE]&source=[BASE_PATH]

E-mail: 
mailto:?subject=Interesting page [PAGE_TITLE]&body=Check out this site I came across [PAGE_URL]

To get it work we need a current page Page URL, Page title, and Base path. Only the page URL is directly accessible from TWIG template. The other two needs to be prepared in preprocess. Lets add these in the theme_name.theme file.

/**
 * Implements template_preprocess_field().
 */
function theme_name_preprocess_field(&$variables, $hook) {
  switch ($variables['field_name']) {
    case 'field_social_share_twitter':
      $request = \Drupal::request();
      $route_match = \Drupal::routeMatch();
      $title = \Drupal::service('title_resolver')
        ->getTitle($request, $route_match->getRouteObject());

  if (is_array($title)) {
    $variables['node_title'] = $title['#markup'];
  }
  else {
    $variables['node_title'] = (string) $title;
  }

  $variables['base_path'] = base_path();      
    break;
  }
}

As we probably will have more then one service we should use the DRY approach here. So we create extra function for the variable generation.

/**
 * Preprocess field_social_share.
 */
function _theme_name_preprocess_field__social_shares(&$variables) {
  $request = \Drupal::request();
  $route_match = \Drupal::routeMatch();
  $title = \Drupal::service('title_resolver')
    ->getTitle($request, $route_match->getRouteObject());

  if (is_array($title)) {
    $variables['node_title'] = $title['#markup'];
  }
  else {
    $variables['node_title'] = (string) $title;
  }

  $variables['base_path'] = base_path();
}

And we than call it for various cases. If some service will need more variables it will be easy to add it in different function. So we don’t process whats not required.

/**
 * Implements template_preprocess_field().
 */
function theme_name_preprocess_field(&$variables, $hook) {
  switch ($variables['field_name']) {

    case 'field_social_share_facebook':
      _theme_name_preprocess_field__social_shares($variables);
    break;

    case 'field_social_share_twitter':
      _theme_name_preprocess_field__social_shares($variables);
    break;

    case 'field_social_share_linkedin':
      _theme_name_preprocess_field__social_shares($variables);
    break;

    case 'field_social_share_email':
      _theme_name_preprocess_field__social_shares($variables);
    break;
  }
}

Now we have the Node title and Base path prepared to be used in field templates.

Enable twig debug and look in the markup for the checkbox. You will see couple of suggestions, the one we are looking for is field--field-social-share-twitter.html.twig.

As the output should be single link item it is safe to assume we can remove all the labels condition and the single/multiple check as well. On the other hand we need to ensure that if the checkbox is unchecked it will not output any value. That is particularly hard in TWIG as it doesn’t have any universal information about the state of checkbox. It has only access to the actual value. But since we don’t know the value of custom label we cannot use it. However there is a small workaround we can use. Remember we hav not set the FALSE value.
We can check if the field is outputting any #markup. The empty FALSE value will not produce anything, hence the condition will fail.

{% if item.content['#markup'] %}

Here is the full code for field template:

{%
  set classes = [
  'social-share__service',
  'social-share__service--twitter',
]
%}

{% for item in items %}
  {% if item.content['#markup'] %}
    <a {{ attributes.addClass(classes) }} href="http://twitter.com/intent/tweet?status={{ node_title }}+{{ url('<current>') }}" title="Share to {{ item.content }}">{{ item.content }}</a>
  {% endif %}
{% endfor %}

For other services you need to adapt it. But it will still follow the same pattern.

And we are done. Now your block should return links to sharing current page to the service.

Social Share links

Pro tip:

So far we have not use any contrib module. But obviously your client would like to have some fancy staying applied. You can add everything in the theme, but that will be only one hardcoded option. For easier live of editors you can use Entity Class formatter module to easily add classes to the block from a select list. You can provide multiple select list for Size, Color, Rounded corners, Style etc.

Result

At this point we have the simple social share widget ready. We can select which predefined services will show in each instance and how will they look. E.g. On blog post you can have sharing for Twitter, Facebook and Email styled as small rounded icons. But with another instance of the block you can have only large squared LinkedIn icon + label shown on Job offering content type.

Social Share icons

Further notes

After I wrote first draft of this article new module appeared which work in very similar way. Give it a try at Better Social Sharing Buttons. It will be quicker to get up ad running as it has predefined styles and services, but that can be a drawback at the same time. If I need different style, or extra service it can be harder to add it.