Drupal

(Hexidecimal) Color Field

New Drupal Modules - 27 September 2018 - 5:12pm
Module Overview

This module provides a Hexidecimal Color Field API Field. The field collects and stores hexidecimal color strings, in the format #XXXXXX where X is a hexidecimal (0-9, a-f) character.

The module also provides a hexidecimal_string TypedData API data type. This can then be used in the Field API by defining a property as a hexidecimal_color. For example, in a class that extends FieldItemBase, the propertyDefitions() method would look something like this:

Categories: Drupal

MakeClassy!

New Drupal Modules - 27 September 2018 - 2:27pm

This module adds classy classes to any theme. Work in progress.

Categories: Drupal

Discoverable Entity Bundle Classes

New Drupal Modules - 27 September 2018 - 2:16pm

Currently in Drupal 8, there is no mechanism for deriving a base entity type's class implementation with a unique class type on a per-bundle basis. As a result, if overriding the entity type class, that class type will be used for all instances in which that entity type is created.

This module provides a simple proof of concept that takes control of the SqlContentEntityStorage to allow for derived content entity type classes on a per-bundle basis which are discovered through the @ContentEntityBundleClass annotation.

Categories: Drupal

Uppy File Uploader

New Drupal Modules - 27 September 2018 - 12:51pm

Adds a widget to upload files via Uppy (https://uppy.io/).

Uses the chunking, resumable, TUS file transfer protocol (https://tus.io/).

For decoupled / CORS purposes, you will need to add these allowed headers to your services.yml (included are a few other common headers):

Categories: Drupal

Donut Chart

New Drupal Modules - 27 September 2018 - 12:20pm
Synopsis

The Donut Chart module adds a formatter to output numeric field inputs as configurable donut charts.

Requirements

Simply install the module and every numeric field will have the option to output the field as a Donut Chart when selecting the formatter for the field. You can also specify the HEX value for the color of the rings and the text, as well as the width of the rings to get a more stylish output.

Categories: Drupal

DbLog Slack Notification

New Drupal Modules - 27 September 2018 - 10:55am

Here you can create a notification chamber while any error occurs in application. This is a flexible module so you can configure your own slack channel and configure what kind of log you want to put in notification.

Steps:
1. Enable the Module via drush or composer.
DRUSH: drush en slack_notify
COMPOSER: composer require drupal/slack_notify

2. Go to configuration and set your channel url and select what type of message you want to show.
3. Thats all, check your channel now :D

Categories: Drupal

Slack Log Notification

New Drupal Modules - 27 September 2018 - 10:36am

Here you can create a notification chamber while any error occurs in application. This is a flexible module so you can configure your own slack channel and configure what kind of log you want to put in notification.

Categories: Drupal

Chocolate Lily: Managing Shared Configuration Part 7: Core Configuration

Planet Drupal - 27 September 2018 - 9:00am

This is the seventh and (promise!) penultimate installment in a series presenting work on shared configuration that comes out of the Drutopia initiative and related efforts, beginning with Part 1, Configuration Providers.

In this series we've covered how to create and update reusable packages of configuration in Drupal, otherwise known as features.

In Part 6, we saw how the Features module can be used to package configuration that will be used by multiple different features into a "core" feature. An example is when multiple fields use the same storage. A core feature might provide a field_tags field storage, allowing multiple features to add a field_tags field to different content types. All the features that provide a field would require the core feature.

This approach helps to manage dependencies among different features, but it has at least two major shortcomings.

  • Any site that wants to install even a single feature that's dependent on the core feature will get all the core configuration--whether or not it's needed. For example, if the core feature provides five field storages but only one is required by the dependent feature, all five will still be created on the site.
  • Features from different sets or distributions will have conflicting dependencies. Say we have two different distributions, A and B. An event feature from distribution A requires the distribution A core feature, which provides the field_tags field storage. An article feature from distribution B requires the distribution B core feature, which provides an identical field_tags field storage. The event feature should theoretically be compatible with the article feature. But in practice they can't be installed on the same site, since an attempt to install both core features will raise an exception since configuration provided by the first-installed core feature will already exist on the site when the second is queued for installation.

In this installment we'll look at options for managing shared configuration that's required across multiple features--or multiple distributions.

Categories: Drupal

Affected by promotion

New Drupal Modules - 27 September 2018 - 7:36am

Provides a service to give you the entities that are affected by a promotion.

Categories: Drupal

Simple LinkedIn Autopost

New Drupal Modules - 27 September 2018 - 7:05am

Features
This module provides API integration with Simple LInkedIn API service.

Setup
Configuration page - admin/config/services/linkedin_autopost

User configuration - admin/config/services/user_linkedin_autopost

Categories: Drupal

Morpht: Simple Social Service links for Drupal 8

Planet Drupal - 27 September 2018 - 4:46am

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".

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'] %} "http://twitter.com/intent/tweet?status={{ node_title }}+{{ url('') }}" title="Share to {{ item.content }}">{{ item.content }} {% 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.

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.

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.

Categories: Drupal

OpenSense Labs: Power Of Microservices Architecture In Drupal Development

Planet Drupal - 27 September 2018 - 4:45am
Power Of Microservices Architecture In Drupal Development Shankar Thu, 09/27/2018 - 17:15

One of the reasons why The New York Times is able to catch up to its growing user base is its inclination towards technological advancements. That was evident when it leveraged the power of microservice architecture via a remodelled video publishing platform to scale with their newsroom demands. They also moved their infrastructure to the cloud which resulted in a stable and scalable email platform, powered by a suite of microservices, for sending emails to the readers.


Why are big enterprises like The New York Times leaning towards microservices? Microservices has grown exponentially and holds an astronomical future for the digital businesses. It will be interesting to see how traditional CMS like Drupal finds a place in the world of microservices. But before plunging into all that, one might wonder where did this ‘microservices’ thing originate from?

Tracing the roots in the UNIX world

New Relic has compiled an interesting and brief timeline of the evolution of microservices. Microservices has its roots in the Unix world that takes us back to more than three decades ago.

As a term, microservices was first documented in 2011 by Martin Fowler

Service-oriented architecture (SOA), a design principle where services are offered to other components by application components via communication protocol over a network, was all the rage decades ago. Due to a superabundance of failures and costly implementations, the SOA earned a poor reputation and took a backseat. Martin Fowler, among others, has said that microservices are a new spin on SOA.

As a term, it was first documented in 2011 by Fowler at a software architects’ workshop.

In 2012, a presentation was given by James Lewis at the 33rd Degree in Krakow which was titled “Microservices - Java, the Unix Way”. This delineated microservices as a means of building software more rapidly by dividing and conquering and used Conway’s Law to structure teams.

Since that time, the adoption of microservice architecture has grown and many organisations are going for microservices as their default style for building enterprise applications.

Understanding the terminology Source: LeanIX GmbH

What are microservices? Microservices are an architecture for splitting a monolithic application into smaller pieces. Each of those pieces offers a certain function through a well-defined and carefully handled API.

The collection delivers the same overall business value like the monolithic application with the difference being these independently working individual pieces in microservices. That means they can be updated swiftly without impacting an entire application.

“The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies”. - Martin Fowler

"A microservice architectural style is an approach to develop a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API".

Netflix is an unsurpassed example of microservices adoption. It moved from a traditional development model with several engineers producing a monolithic DVD-rental application to a microservices architecture. Small teams could focus on the end-to-end development of hundreds of microservices that work together to serve digital entertainment to millions of Netflix customers every day.

Source: LeanIX GmbH

The main difference between monolithic and microservices architecture, as can be seen in the depiction above, is that all the features and functionalities were under a single umbrella. That is, they were under a single instance sharing a single database. With microservices, each feature is allotted a different microservice, managing its own data, and performing a different set of functionalities.

How good or bad are microservices? Source: Logentries

The benefits of microservices are laid out below:

  • Autonomous deployments: You can update a service without having to redeploy the entire application and rollback or roll forward an update during mishaps. Fixing bugs and feature releases are much more manageable with fewer challenges.
  • Autonomous development: Building, testing and deploying a service would need a single development team leading to perpetual innovation and swift release cadence.
  • Small teams: Teams can lay their focus onto one service thereby simplifying the understanding of the codebase with the smaller scope for each service.
  • Isolation of faults: Downtime in one of the services won’t affect the overall application. This does not mean that you get resiliency for free.
  • Tech stack mixture: Technology that is deemed most fit for a service can be selected by the teams.
  • Scalability at granular levels: Independent scaling of services is possible.

Some of the challenges are outlined below:

  • Intricacy: More moving parts are there in microservice application than the equivalent monolithic application.
  • Development and testing: Developing against service dependencies would need a different approach and testing service dependencies is difficult particularly when the application is evolving rapidly.
  • The dearth of administration: The decentralised approach for building microservices may lead to numerous languages and frameworks thereby making it harder to manage.
  • Network congestion and latency: Usage of granular services can result in more inter-service communication. Chances are that if the chain of service dependencies gets too elongated, additional latency can be a challenge.
  • Data integrity: Data consistency can be a hurdle with each microservice responsible for its own data persistence.
  • Management: Correlated logging across services can become a formidable task.
  • Update issues: If not for a careful design, several services updating at a given time could result in backward or forward compatibility.
  • Team skill-set: As the highly distributed systems, microservices require a team with the right mix of skills and experience.
Taking Drupal into the context

Drupal is a monolith. How can it survive this trend of microservices? Drupal, being an amazing content management framework, provides a great content editing experience and has been pioneering digital innovation. With that being said, microservices architecture can be used for development and deployment of applications using Drupal. Let’s see how Drupal can put into the scheme of things.

Demonstration at DrupalCon Vienna 2017

A presentation held at DrupalCon Vienna 2017 demonstrated an effective way of integrating Drupal 8 in a microservices architecture.
 
Drupal 8 proved to be a useful content management framework for this implementing microservices architecture because of its:

  • Symfony components,
  • Composer to manage external dependencies,
  • and the magnificent results of the Web Services and Context Core Initiative (WSCCI).
     


It exhibited the delegation of asynchronous work from Drupal to a set of very reactive applications written in Go with some assistance of RabbitMq queues. Elasticsearch was leveraged as a common data storage between services and REST endpoints were exposed where the endpoints could notify back to Drupal.
 
Furthermore, methods of connecting websocket server to push and pull messages between services were shown. To run all these services in a controlled and replicable manner, services of Ansible and Docker were extracted.

Demonstration at Drupal Developer Days Lisbon 2018

Another session at Drupal Developer Days Lisbon 2018 delineated how the citizen portal of the city of Reykjavik (Iceland) was relaunched using Drupal and microservices.
 
With the incorporation of more than 100 web services ranging from simple services like registering a dog or renewing a driver’s license to the intricate services like the admissions of children to school or updating the residential address.


Powered by Drupal 8, this new portal integrates the services with a microservices architecture using JSON Schema as communication protocol. The microservices architecture was chosen to let centralised data collection and presentation in a single portal while simultaneously incorporating a heterogeneous landscape of services autonomously from one another.

Predictions ahead

Oracle’s Cloud Predictions 2018 report states that by 2020, the lion’s share of new applications will be powered by microservices architectures.

Open source has given a whopping push to the microservices architecture. Its several components support continuous integration and delivery pipelines, microservices platforms, containers, container management and orchestration, container registry service, and serverless capability.

Open source has given a whopping push to the microservices architecture

Adoption of cross-cloud containers like Docker and Kubernetes is on the upwards trajectory and developers consider an open cloud stack to prevent vendor lock-in.

Source: Market Research Future

According to a report on Market Research Future, the microservices architecture market is expected to reach $32.01 billion by 2023 with a Compound Annual Growth Rate (CAGR) of around 16.17% during the forecast period.

Another report on Research and Markets for the forecast period of 2017 to 2023 states that as far as the ‘Market Analysis’ is concerned, the rise in the cloud adoption is integral for microservices market. This is because the microservices architectures function on smaller and simpler services. Also, there is a high demand from North American companies as they have implemented it in e-commerce, financial, and travel services. This has helped in storing data and information cost-effectively and enhanced the efficacy, agility and scalability.

The report on Research and Markets has an interesting ‘Countries and Vertical Analysis’ vis-à-vis microservices. Most of the major players are in the American region with the prominent vendors covered in the report include the likes of Cognizant, IBM Corporation, Datawire, Salesforce, Infosys Ltd., MuleSoft Inc., and Software AG. Japan, the US and China are expected to witness a tremendous growth in microservices adoption.

Conclusion

Microservices architectures streamline the overall application development lifecycle leading to quicker testing, higher quality and more releases. Such an architecture can be hugely useful for efficient management of Drupal-based projects. Innovation has always been something Drupal is greatly supportive of. Adopting a microservice architecture for Drupal development is possible and is extremely fruitful.

Organisations should be wary of their digital business ecosystem and should understand the challenges that they might have to encounter during its adoption. Opensense Labs has been in the constant pursuit of bringing a positive change for our valued partners with our expertise in Drupal.

Contact us at hello@opensenselabs.com to know more about microservices architectures and its value to your organisational setup.

blog banner blog image microservices Drupal microservices Drupal 8 Drupal CMS Drupal and microservices UNIX Service-oriented architecture SOA Microservices architecture DrupalCon Drupal Developer Days monolithic monolithic architecture Blog Type Articles Is it a good read ? On
Categories: Drupal

OPcache Control

New Drupal Modules - 27 September 2018 - 4:07am

Simple Module to view OPcache statistics and configuration and reset OPcache.

Categories: Drupal

Matt Glaman: Tracking changes in Migrate with dynamic row hashes

Planet Drupal - 26 September 2018 - 11:00pm
Tracking changes in Migrate with dynamic row hashes Thu, 09/27/2018 - 01:00 mglaman

When it comes to Drupal and external data, I use Migrate. A lot. Like a lot, lot, lot. Many times this data is being imported over CSV files that are pushed to a server at some defined interval. Usually, the data can be derived directly from the CSV file itself, other times a custom process plugin derives data from other information. Drupal's Migrate system has two steps to check if new data should be imported or skipped. First, you can tell the migration source to track changes for each row. Then, if you are tracking changes, it hashes each row of data to see if it has been changed.

Categories: Drupal

BS Basic

New Drupal Modules - 26 September 2018 - 10:33pm

BS Basic Module

Basic Slide show block for website.

Categories: Drupal

ActiveLAMP: Quick Setup with Composer Template for Drupal Projects

Planet Drupal - 26 September 2018 - 5:34pm

Pairing Composer template for Drupal Projects with Lando gives you a fully working Drupal environment with barely any setup.

Read more...
Categories: Drupal

JSON API Hypermedia

New Drupal Modules - 26 September 2018 - 1:10pm

The JSON API Hypermedia module is complementary component of the JSON API module.

Categories: Drupal

a-fro.com: Creating Paragraphs Entities for Dynamic Content

Planet Drupal - 26 September 2018 - 10:28am

The paragraphs module has become a central ingredient for many component-based sites in recent years. However, our content strategy also often requires components that display dynamic content (think "Read Next", or "Also of Interest"). In this tutorial, I'll demonstrate how we've been solving this problem, by building paragraph bundles that serve as configuration entities that we can then use as arguments that we pass to a view via the Twig Tweak module. You can see a working version of the dynamic content component we'll be building in the "Up Next" card grid at the bottom of this tutorial. 

Categories: Drupal

Commerce Guys: 2018: The Decoupled Summer of Drupal Commerce

Planet Drupal - 26 September 2018 - 8:00am

We’ve had several great opportunities this summer to connect with the Drupal community and share our latest work on Drupal Commerce. We’ve been able to highlight specifically our efforts to progressively decouple Drupal Commerce on Drupal 8.

Drupal Camp Asheville 2018
Ryan Szrama gave a demo on Saturday, July 14, based on the Belgrade demo store that provided an overview of Commerce Cart API Flyout. We detailed this work in our recent blog post announcing the feature.

A fully decoupled Drupal Commerce experience—including support for complex forms like checkout—is something that Commerce Guys is committed to delivering by the end of 2019. Until then, our strategy is to progressively decouple the product catalog and shopping cart to help sites scale in addition to opening new user interfaces. In Ryan’s words, “We started with the shopping cart because that’s the obvious way to help large websites avoid a common bottleneck for performance.”

Watch Ryan’s session to learn more about the Commerce Cart API project and see the demo.

Decoupled Drupal Days 2018
Next, Matt Glaman presented his talk “The road to a headless Drupal Commerce future” at Decoupled Drupal Days in NYC.

The session reviewed the development of the Commerce Cart API in greater depth. It covers our research into the RESTful Web Services and contributed JSON API projects (potentially in core soon) as future dependencies that the Cart API can adopt. Matt demonstrated even more progress on the project since Ryan’s demo, including a fully decoupled React based front-end.

This talk put the progressively decoupled Drupal Commerce Add to Cart form and shopping cart on display for the community with the expressed desire that Drupal based merchants will have an out of the box experience rivaling other major e-commerce software platforms.

Drupal Europe 2018
Matt’s session at Drupal Europe covered our latest developments in the Commerce Cart API and Flyout as part of the dedicated eCommerce track. This was an iteration of the Drupal Drupal Days session, including any improvements and additions in the time between Drupal Europe and Decoupled Drupal Days.

If you’re interested in contributing to the roadmap for decoupling Drupal Commerce, connect with Matt to learn where to get involved or how to give us feedback from your implementations.

Categories: Drupal

Drupal Association blog: Drupal.org Terms of Service update - September 2018

Planet Drupal - 26 September 2018 - 6:58am

As part of our ongoing activities to ensure a safe and welcoming environment for collaboration in Open Source, we have updated the drupal.org Terms of Service, at drupal.org/terms

This change has clarified which behaviors will be regarded as “harassment” and are, therefore, not acceptable whilst using the Drupal online services. The language is now in line with that already employed in the DrupalCon Code of Conduct.

The updated text, from Section C - Activities, now reads as:

  • Harassment will not be tolerated in any form, including but not limited to: harassment based on gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, age or religion. Any report of harassment will be addressed immediately. Harassment includes, but is not limited to:
     

    • Comments or imagery that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, disability, physical appearance, body size, race, age, or religion.

    • Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment.

    • Abusive, offensive, or degrading language or imagery

    • Language or imagery that encourages, glorifies, incites, or calls for violence, emotional, or physical harm against an individual or a group of people

    • Intimidation, stalking, or following

    • Sexual imagery. At a minimum, no images containing nudity or expressions of sexual relationships that might be deemed inappropriate for a business environment should be uploaded or linked to

    • Unwelcome sexual attention or advances

    • Advocating for, or encouraging, any of the above behavior

You do not need to do anything to acknowledge this update.

Whilst you are here…

Are you receiving all the news and information you need? The Drupal Association publishes a number of news updates and you might be missing out. Check which news updates you are receiving by visiting our recently updated subscription page at http://eepurl.com/hWxwQ

Categories: Drupal

Pages

Subscribe to As If Productions aggregator - Drupal