سیاره دروپال

Drupal.org - aggregated feeds in category Planet Drupal
Subscribe to خوراک سیاره دروپال
Simple Website Approach Using a Headless CMS: Part 1 I strongly believe that the path for innovation requires a mix of experimentation, sweat, and failure. Without experimenting with new solutions, new technologies, new tools, we are limiting our ability to improve, arresting our potential to be better, to be faster, and sadly ensuring that we stay rooted in systems, processes and...
Configuring migrations via a form mikeryan Tuesday, May 22, 2018 - 09:29pm

Frequently, there may be parts of a migration configuration which shouldn’t be hard-coded into your YAML file - some configuration may need to be changed periodically, some may vary according to environment (for example, a dev environment may access a dev or test API endpoint, while prod needs to access a production endpoint), or you may need a password or other credentials to access a secure endpoint (or for a database source which you can’t put into settings.php). You may also need to upload a data file for input into your migration. If you are implementing your migrations as configuration entities (a feature provided by the migrate_plus module), all this is fairly straightforward - migration configuration entities may easily be loaded, modified, and saved based on form input, implemented in a standard form class.

Uploading data files

For this project, while other CSV source files were static enough to go into the migration module itself, we needed to periodically update the blog data during the development and launch process. A file upload field is set up in the normal way:

$form['acme_blog_file'] = [ '#type' => 'file', '#title' => $this->t('Blog data export file (CSV)'), '#description' => $this->t('Select an exported CSV file of blog data. Maximum file size is @size.', ['@size' => format_size(file_upload_max_size())]), ];

And saved to the public file directory in the normal way:

$all_files = $this->getRequest()->files->get('files', []); if (!empty($all_files['acme_blog_file'])) { $validators = ['file_validate_extensions' => ['csv']]; if ($file = file_save_upload('acme_blog_file', $validators, 'public://', 0)) {

So, once we’ve got the file in place, we need to point the migration at it. We load the blog migration, retrieve its source configuration, set the path to the uploaded file, and save it back to active configuration storage.

$blog_migration = Migration::load('blog'); $source = $blog_migration->get('source'); $source['path'] = $file->getFileUri(); $blog_migration->set('source', $source); $blog_migration->save(); drupal_set_message($this->t('File uploaded as @uri.', ['@uri' => $file->getFileUri()])); } else { drupal_set_message($this->t('File upload failed.')); } }

It’s important to understand that get() and set() only operate directly on top-level configuration keys - we can’t simply do something like $blog_migration->set(‘source.path’, $file->getFileUri()), so we need to retrieve the whole source configuration array, and set the whole array back on the entity.

Endpoints and credentials

The endpoint and credentials for our event service are configurable through the same webform. Note that we obtain the current values from the event migration configuration entity to prepopulate the form:

$event_migration = Migration::load('event'); $source = $event_migration->get('source'); if (!empty($source['urls'])) { if (is_array($source['urls'])) { $default_value = reset($source['urls']); } else { $default_value = $source['urls']; } } else { $default_value = 'http://services.example.com/CFService.asmx?wsdl'; } $form['acme_event'] = [ '#type' => 'details', '#title' => $this->t('Event migration'), '#open' => TRUE, ]; $form['acme_event']['event_endpoint'] = [ '#type' => 'textfield', '#title' => $this->t('CF service endpoint for retrieving event data'), '#default_value' => $default_value, ]; $form['acme_event']['event_clientid'] = [ '#type' => 'textfield', '#title' => $this->t('Client ID for the CF service'), '#default_value' => @$source['parameters']['clientId'] ?: 1234, ]; $form['acme_event']['event_password'] = [ '#type' => 'password', '#title' => $this->t('Password for the CF service'), '#default_value' => @$source['parameters']['clientCredential']['Password'] ?: '', ];

In submitForm(), we again load the migration configuration, insert the form values, and save:

$event_migration = Migration::load('event'); $source = $event_migration->get('source'); $source['urls'] = $form_state->getValue('event_endpoint'); $source['parameters'] = [ 'clientId' => $form_state->getValue('event_clientid'), 'clientCredential' => [ 'ClientID' => $form_state->getValue('event_clientid'), 'Password' => $form_state->getValue('event_password'), ], 'startDate' => date('m-d-Y'), ]; $event_migration->set('source', $source); $event_migration->save(); drupal_set_message($this->t('Event migration configuration saved.'));

Note that we also reset the startDate value while we’re at it (see the previous SOAP blog post).

Tags Drupal Planet Drupal Migration Use the Twitter thread below to comment on this post:

Configuring migrations via a form https://t.co/EZTiUKBazX

— Virtuoso Performance (@VirtPerformance) May 22, 2018

 

Drupalistas Spent Our Entire Swag Budget. Where did the Money Go? Shannon O'Malley Tue, 05/22/2018 - 15:09

This April at DrupalCon Nashville, in addition to wanting to meet colleagues and soak up the great talks, we wanted to create a forum for the international Drupal community to do good. That’s why we used our sponsor booth wall as a space for attendees to promote nonprofits that work for causes that matter to them.

Categories Articles Community Drupal Nonprofits Author Shannon O'Malley

Yesterday, Adobe announced that it agreed to buy Magento for $1.68 billion. When I woke up this morning, 14 different people had texted me asking for my thoughts on the acquisition.

Adobe acquiring Magento isn't a surprise. One of our industry's worst-kept secrets is that Adobe first tried to buy Hybris, but lost the deal to SAP; subsequently Adobe tried to buy DemandWare and lost out against Salesforce. It's evident that Adobe has been hungry to acquire a commerce platform for quite some time.

The product motivation behind the acquisition

Large platform companies like Salesforce, Oracle, SAP and Adobe are trying to own the digital customer experience market from top to bottom, which includes providing support for marketing, commerce, personalization, and data management, in addition to content and experience management and more.

Compared to the other platform companies, Adobe was missing commerce. With Magento under its belt, Adobe can better compete against Salesforce, Oracle and SAP.

While Salesforce, SAP and Oracle offer good commerce capability, they lack satisfactory content and experience management capabilities. I expect that Adobe closing the commerce gap will compel Salesforce, SAP and Oracle to act more aggressively on their own content and experience management gap.

While Magento has historically thrived in the SMB and mid-market, the company recently started to make inroads into the enterprise. Adobe will bring a lot of operational maturity; how to sell into the enterprise, how to provide enterprise grade support, etc. Magento stands to benefit from this expertise.

The potential financial outcome behind the acquisition

According to Adobe press statements, Magento has achieved "approximately $150 million in annual revenue". We also know that in early 2017, Magento raised $250 million in funding from Hillhouse Capital. Let's assume that $180 million of that is still in the bank. If we do a simple back-of-the-envelope calculation, we can subtract this $180 million from the $1.68 billion, and determine that Magento was valued at roughly $1.5 billion, or a 10x revenue multiple on Magento's trailing twelve months of revenue. That is an incredible multiple for Magento, which is primarily a licensing business today.

Compare that with Shopify, which is trading at a $15 billion dollar valuation and has $760 million of twelve month trailing revenue. This valuation is good for a 20x multiple. Shopify deserves the higher multiple, because it's the better business; all of its business is delivered in the cloud and at 65% year-over-year revenue growth, it is growing much faster than Magento.

Regardless, one could argue that Adobe got a great deal, especially if it can accelerate Magento's transformation from a licensing business into a cloud business.

Most organizations prefer best-of-breed

While both the product and financial motivations behind this acquisition are seemingly compelling, I'm not convinced organizations want an integrated approach.

Instead of being confined to proprietary vendors' prescriptive suites and roadmaps, global brands are looking for an open platform that allows organizations to easily integrate with their preferred technology. Organizations want to build content-rich shopping journeys that integrate their experience management solution of choice with their commerce platform of choice.

We see this first hand at Acquia. These integrations can span various commerce platforms, including IBM WebSphere Commerce, Salesforce Commerce Cloud/Demandware, Oracle/ATG, SAP/hybris, Magento and even custom transaction platforms. Check out Quicken (Magento), Weber (Demandware), Motorola (Broadleaf Commerce), Tesla (custom to order a car, and Shopify to order accessories) as great examples of Drupal and Acquia working with various commerce platforms. And of course, we've quite a few projects with Drupal's native commerce solution, Drupal Commerce.

Owning Magento gives Adobe a disadvantage, because commerce vendors will be less likely to integrate with Adobe Experience Manager moving forward.

It's all about innovation through integration

Today, there is an incredible amount of innovation taking place in the marketing technology landscape (full-size image), and it is impossible for a single vendor to have the most competitive product suite across all of these categories. The only way to keep up with this unfettered innovation is through integrations.

For reference, here are the 2011, 2012, 2014, 2015, 2016 and 2017 versions of the landscape. It shows how fast the landscape is growing.

Most customers want an open platform that allows for open innovation and unlimited integrations. It's why Drupal and Acquia are winning, why the work on Drupal's web services is so important, and why Acquia remains committed to a best-of-breed strategy for commerce. It's also why Acquia has strong conviction around Acquia Journey as a marketing integration platform. It's all about innovation through integration, making those integrations easy, and removing friction from adopting preferred technologies.

If you acquire a commerce platform, acquire a headless one

If I were Adobe, I would have looked to acquire a headless commerce platform such as Elastic Path, Commerce Tools, Moltin, Reaction Commerce or even Salsify.

Today, there is a lot of functional overlap between Magento and Adobe Experience Manager — from content editing, content workflows, page building, user management, search engine optimization, theming, and much more. The competing functionality between the two solutions makes for a poor developer experience and for a poor merchant experience.

In a headless approach, the front end and the back end are decoupled, which means the experience or presentation layer is separated from the commerce business layer. There is a lot less overlap of functionality in this approach, and it provides a better experience for merchants and developers.

Alternatively, you could go for a deeply integrated approach like Drupal Commerce. It has zero overlap between its commerce, content management and experience building capabilities.

For Open Source, it could be good or bad

How Adobe will embrace Magento's Open Source community is possibly the most intriguing part of this acquisition — at least for me.

For a long time, Magento operated as Open Source in name, but wasn't very Open Source in practice. Over the last couple of years, the Magento team worked hard to rekindle its Open Source community. I know this because I attended and keynoted one of its conferences on this topic. I have also spent a fair amount of time with Magento's leadership team discussing this. Like other projects, Magento has been taking inspiration from Drupal.

For example, the introduction of Magento 2 allowed the company to move to GitHub for the first time, which gave the community a better way to collaborate on code and other important issues. The latest release of Magento cited 194 contributions from the community. While that is great progress, it is small compared to Drupal.

My hope is that these Open Source efforts continue now that Magento is part of Adobe. If they do, that would be a tremendous win for Open Source.

On the other hand, if Adobe makes Magento cloud-only, radically changes their pricing model, limits integrations with Adobe competitors, or doesn't value the Open Source ethos, it could easily alienate the Magento community. In that case, Adobe bought Magento for its install base and the Magento brand, and not because it believes in the Open Source model.

This acquisition also signals a big win for PHP. Adobe now owns a $1.68 billion PHP product, and this helps validate PHP as an enterprise-grade technology.

Unfortunately, Adobe has a history of being "Open Source"-second and not "Open Source"-first. It acquired Day Software in July 2010. This technology was largely made using open source frameworks — Apache Sling, Apache Jackrabbit and more — and was positioned as an open, best-of-breed solution for developers and agile marketers. Most of that has been masked and buried over the years and Adobe's track record with developers has been mixed, at best.

Will the same happen to Magento? Time will tell.

Well sure, ok, maybe we might be slightly biased on this. We are, as it turns out, a consulting firm in the business of selling outsourced programming. Ahem...

But, nonetheless, I’ll try to be reasonably fair and balanced here. As a consultancy I think we have, in fact, a unique vantage point on such matters, since we spend each day of our lives straddling both sides of this topic: That is, we sell outsourced programming to organizations for whom outsourcing is a good fit; whereas we ourselves hire in house staff programmers, i.e. we are an organization for whom outsourcing is not a good fit.

If a user needs to create an account on a Drupal site, they go to the user registration page at "/user/register". This page is the registration form on a Drupal site. You can customize it by adding or removing fields. But what if you want to have multiple registration pages?

Let's say you have two different roles on your Drupal site and you need a separate form for each role. How would you build that?

You could handle all of this writing custom code but remember we're using Drupal so means there's a module that can handle this type of functionality and It's called Multiple Registration.

The Multiple Registration module allows you to create individual registration forms base off a user role in Drupal. When you register on one of the forms, you're automatically assigned the configured role.

In this tutorial, you'll learn how to use Multiple Registration to create individual registration forms.

In an older article we looked at how to render an entity form programatically using custom form display modes. Fair enough. But did you ever need to combine this form with a few form elements of yours which do not have to be stored with the corresponding entity? In other words, you need to be working in a custom form…

What I used to do in this case was write my own clean form elements in a custom form and on submit, deal with saving them to the entity. This is not a big deal if I am dealing with simple form elements, and of course, only a few of them. If my form is big or complex, using multivalue fields, image uploads and stuff like this, it becomes quite a hassle. And who needs that?

Instead, in our custom form, we can load up and add the field widgets of our entity form. And I know what you are thinking: can we just build an entity form using the EntityFormBuilder service, as we saw in the previous article, and just copy over the form element definitions? Nope. That won’t work. Instead, we need to mimic what it does. So how do we do that?

We start by creating a nice form display in the UI where we can configure all our widgets (the ones we want to show and in the way we want them to show up). If the default form display is good enough, we don’t even need to create this. Then, inside the buildForm() method of our custom form we need to do a few things.

We create an empty entity of the type that concerns us (for example Node) and store that on the form state (for the submission handling that happens later):

$entity = $this->entityTypeManager->getStorage(‘node’)->create([ 'type' => ‘article’ ]); $form_state->set(‘node’, $node);

Next, we load our newly created form display and store that also on the form state:

/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ $form_display = $this->entityTypeManager->getStorage('entity_form_display')->load('node.article.custom_form_display'); $form_state->set('form_display', $form_display);

You’ll notice that the form display is actually a configuration entity whose ID is made up of the concatenation of the entity type and bundle it’s used on, and its unique machine name.

Then, we loop over all the components of this form display (essentially the field widgets that we configure in the UI or inside the base field definitions) and build their widgets onto the form:

foreach ($form_display->getComponents() as $name => $component) { $widget = $form_display->getRenderer($name); if (!$widget) { continue; } $items = $entity->get($name); $items->filterEmptyItems(); $form[$name] = $widget->form($items, $form, $form_state); $form[$name]['#access'] = $items->access('edit'); }

This happens by loading the renderer for each widget type and asking it for its respective form elements. And in order for it to do this, it needs an instance of the FieldItemListInterface for that field (which at this stage is empty) in order to set any default values. This we just get from our entity.

And we also check the access on that field to make sure the current user can access it.

Finally, we need to also specify a #parents key on our form definition because that is something the widgets themselves expect. It can stay empty:

$form['#parents'] = [];

Now we can load our form in the browser and all the configured field widgets should show up nicely. And we can add our own complementary elements as we need. Let’s turn to the submit handler to see how we can easily extract the submitted values and populate the entity. It’s actually very simple:

/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */ $form_display = $form_state->get('form_display'); $entity = $form_state->get('entity'); $extracted = $form_display->extractFormValues($entity, $form, $form_state);

First, we get our hands on the same form display config and the entity object we passed on from the form definition. Then we use the former to “extract” the values that actually belong to the latter, from the form state, into the entity. The $extracted variable simply contains an array of field names which have been submitted and whose values have been added to the entity.

That’s it. We can continue processing our other values and save the entity: basically whatever we want. But we benefited from using the complex field widgets defined on the form display, in our custom form.

Ain't that grand?

This is going to be a simple exercise to create a decoupled site using Drupal 8 as the backend and an Elm app in the frontend. I pursue two goals with this:

  • Evaluate how easy it will be to use Drupal 8 to create a restful backend.
  • Show a little bit how to set up a simple project with Elm.

We will implement a very simple functionality. On the backend, just a feed of blog posts with no authentication. On the frontend, we will have a list of blog posts and a page to visualize each post.

Our first step will be the backend.

Before we start, you can find all the code I wrote for this post in this GitHub repository.

Drupal 8 Backend

For the backend, we will use Drupal 8 and the JSON API module to create the API that we will use to feed the frontend. The JSON API module follows the JSON API specification and currently can be found in a contrib project. But as has been announced in the last DrupalCon “Dries”-note, the goal is to move it to an experimental module in core in the Drupal 8.6.x release.

But even before that, we need to set up Drupal in a way that is easy to version and to deploy. For that, I have chosen to go with the Drupal Project composer template. This template has become one of the standards for site development with Drupal 8 and it is quite simple to set up. If Composer is already installed, then it is as easy as this:

composer create-project drupal-composer/drupal-project:8.x-dev server --stability dev --no-interaction

This will create a folder called server with our code structure for the backend. Inside this folder, we have now a web folder, where we have to point our webserver. And is also inside this folder where we have to put all of our custom code. For this case, we will try to keep the custom code as minimal as possible. Drupal Project also comes with the two best friends for Drupal 8 development: drush and drupal console. If you don’t know them, Google it to find more out about what they can do.

After installing our site, we need to install our first dependency, the JSON API module. Again, this is quite easy, inside the server folder, we run the next command:

composer require drupal/jsonapi:2.x

This will accomplish two things: it will download the module and it will add it to the composer files. If we are versioning our site on git, we will see that the module does not appear on the repo, as all vendors are excluded using the gitignore provided by default. But we will see that it has been added to the composer files. That is what we have to commit.

With the JSON API module downloaded, we can move back to our site and start with site building.

Configuring Our Backend

Let’s try to keep it as simple as possible. For now, we will use a single content type that we will call blog and it will contain as little configuration as possible. As we will not use Drupal to display the content, we do not have to worry about the display configuration. We will only have the title and the body fields on the content type, as Drupal already holds the creation and author fields.

By default, the JSON API module already generates the endpoints for the Drupal entities and that includes our newly created blog content type. We can check all the available resources: if we access the /jsonapi path, we will see all the endpoints. This path is configurable, but it defaults to jsonapi and we will leave it as is. So, with a clean installation, these are all the endpoints we can see:

JSON API default endpoints

But, for our little experiment, we do not need all those endpoints. I prefer to only expose what is necessary, no more and no less. The JSON API module provides zero configurable options on the UI out of the box, but there is a contrib module that allows us to customize our API. This module is JSON API Extras:

composer require drupal/jsonapi_extras:2.x

JSONAPI Extras offer us a lot of options, from disabling the endpoint to changing the path used to access it, or renaming the exposed fields or even the resource. Quite handy! After some tweaking, I disabled all the unnecessary resources and most of the fields from the blog content type, reducing it just to the few we will use:

JSONAPI blog resource

Feel free to play with the different options. You will see that you are able to leave the API exactly as you need.

Moving Our Configuration to Version Control

If you have experience with Drupal 7, you probably used the Features module to export configuration to code. But one of the biggest improvements of Drupal 8 is the Configuration Management Interface (CMI). This system provides a generic engine to export all configuration to YAML files. But even if this system works great, is still not the most intuitive or easy way to export the config. But using it as a base, there are now several options that expand the functionality of CMI and provide an improved developer experience. The two bigger players on this game are (Config Split)[https://www.drupal.org/project/config_split] and the good old (Features)[https://www.drupal.org/project/features].

Both options are great, but I decided to go with my old friend Features (maybe because I’m used to it’s UI). The first step, is to download the module:

composer require drupal/features:3.x

One of the really cool functionalities that the Drupal 8 version of the Features module brings is that can instantly create an installation profile with all our custom configuration. Just with a few clicks we have exported all the configuration we did in previous steps; but not only that, we have also created an installation profile that will allow us to replicate the site easily. You can read more of Features in the (official documentation on drupal.org)[https://www.drupal.org/docs/8/modules/features/building-a-distribution-with-features-3x].

Now, we have the basic functionality of the backend. There are some things we should still do, such as restricting the access to the backend interface, to prevent login or registration to the site, but we will not cover it in this post. Now we can move to the next step: the Elm frontend.

Sidenote

I used Features in this project to give it a try and play a bit. If you are trying to create a real project, you might want to consider other options. Even the creators of the Features module suggest not to use it for this kind of situations, as you can read here.

The Frontend

As mentioned, we will use Elm to write this app. If you do not know what it is, Elm is a pure functional language that compiles into Javascript and it is used to create reliable webapps.

Installing Elm is easy. You can build it from the source, but the easiest and recommended way is just use npm. So let’s do it:

npm install -g elm

Once we install Elm, we get four different commands:

  • elm-repl: an interactive Elm shell, that allows us to play with the language.
  • elm-reactor: an interactive development tool that automatically compiles our code and serves it on the browser.
  • elm-make: to compile our code and build the app we will upload to the server.
  • elm-package: the package manager to download or publish elm packages.

For this little project, we will mostly use elm-reactor to test our app. We can begin by starting the reactor and accessing it on the browser. Once we do that, we can start coding.

elm-reactor Elm Reactor Our First Elm Program

If you wish to make apple pie from scratch, you must create first the universe. Carl Sagan

We start creating a src folder that will contain all our Elm code and here, we start the reactor with elm reactor. If we go to our browser and access http://localhost:8000, we will see our empty folder. Time to create a Main.elm file in it. This file will be the root of our codebase and everything will grow from here. We can start with the simplest of all the Elm programs:

module Main exposing main import Html exposing (text) main = text "Hello world"

This might seem simple, but when we access the Main.elm file in the reactor, there will be some magic going on. The first thing we will notice, is that we now have a page working. It is simple, but it is an HTML page generated with Elm. But that’s not the only thing that happened. On the background, elm reactor noticed we imported a Html package, created a elm-packages.json file, added it as dependency and downloaded it.

This might be a good moment to do our first commit of our app. We do not want to include the vendor packages from elm, so we create a .gitignore file and add the elm-stuff folder there. Our first commit will include only three things, the Mail.elm file, the .gitignore and the elm-packages.json file.

The Elm Architecture

Elm is a language that follows a strict pattern, it is called (The Elm Architecture)[https://guide.elm-lang.org/architecture/]. We can summarize it in this three simple components:

  • Model, which represents the state of the application.
  • Update, how we update our application.
  • View, how we represent our state.

Given our small app, let’s try to represent our code with this pattern. Right now, our app is static and has no functionality at all, so there are not a lot of things to do. But, for example, we could start moving the text we show on the screen to the model. The view will be the content we have on our main function, and as our page has no functionality, the update will do nothing at this stage.

type alias Model = String model : Model model = "Hello world" view : Model -> Html Msg view model = text model main = view model

Now, for our blog, we need two different pages. The first one will be the listing of blog posts and the second one, a page for the individual post. To simplify, let’s keep the blog entries as just a string for now. Our model will evolve into a list of Posts. In our state, we also need to store which page we are located. Let’s create a variable to store that information and add it to our model:

type alias Model = { posts : List Post , activePage : Page } type alias Post = String type Page = List | Blog model : Model model = { posts = ["First blog", "Second blog"] , activePage = List }

And we need to update or view too:

view Model : Model -> Html Msg view model = div [] [ List.map viewPost model.posts ] viewPost : Post -> Html Msg viewPost post = div [] [ text post ]

We now have the possibility to create multiple pages! We can create our update function that will modify the model based on the different actions we do on the page. Right now, our only action will be navigating the app, so let’s start there:

type Msg = NavigateTo Page

And now, our update will update the activePage of our model, based on this message:

update : Msg -> Model -> (Model, Cmd Msg) update msg model = case msg of NavigateTo page -> ( {model | activePage = page}, Cmd.none )

Our view should be different now depending on the active page we are viewing:

view : Model -> Html Msg view model = case model.activePage of BlogList -> viewBlogList model.posts Blog -> div [] [ text "This is a single blog post" ] viewBlogList : List Post -> Html Msg viewBlogList posts = div [] [ List.map viewPost model.posts ]

Next, let’s wire the update with the rest of the code. First, we fire the message to change the page to the views:

viewPost post = div [ onClick <| NavigateTo Blog ] [ text post ]

And as a last step, we replace the main function with a more complex function from the Html package (but still a beginner program):

main : Program Never Model Msg main = beginnerProgram { model = model , view = view , update = update }

But we still have not properly represented the single blogs on their individual pages. We will have to update our model once again along with our definition of Page:

type alias Model = { posts : Dict PostId Post , activePage : Page } type alias PostId = Int type Page = List | Blog PostId model : Model model = { posts = Dict.fromList [(1, "First blog"), (2, "Second blog")] , activePage = List }

And with some minor changes, we have the views working again:

view : Model -> Html Msg view model = case model.activePage of BlogList -> viewBlogList model.posts Blog postId -> div [ onClick <| NavigateTo BlogList ] [ text "This is a single blog post" ] viewBlogList : Dict PostId Post -> Html Msg viewBlogList posts = div [] (Dict.map viewPost model.posts |> Dict.values) viewPost : PostId -> Post -> Html Msg viewPost postId post = div [ onClick <| NavigateTo <| Blog postId ] [ text post ]

We do not see yet any change on our site, but we are ready to replace the placeholder text of the individual pages with the content from the real Post. And here comes one of the cool functionalities of Elm, and one of the reasons of why Elm has no Runtime exceptions. We have a postId and we can get the Post from the list of posts we have on our model. But, when getting an item from a Dict, we always risk the possibility of trying to get an non-existing item. If we call a function over this non-existing item, it usually causes errors, like the infamous undefined is not a function. On Elm, if a function has a chance of return the value or not, it returns a special variable type called Maybe.

view : Model -> Html Msg view model = case model.activePage of BlogList -> viewBlogList model.posts Blog postId -> let -- This is our Maybe variable. It could be annotated as `Maybe Post` or a full definition as: -- type Maybe a -- = Just a -- | Nothing post = Dict.get postId model.posts in case post of Just aPost -> div [ onClick <| NavigateTo BlogList ] [ text aPost ] Nothing -> div [ onClick <| NavigateTo BlogList ] [ text "Blog post not found" ] Loading the Data from the Backend

We have all the functionality ready, but we have to do something else before loading the data from the backend. We have to update our Post definition to match the structure of the backend. On the Drupal side, we left a simple blog data structure:

  • ID
  • Title
  • Body
  • Creation date

Let’s update the Post, replacing it with a record to contain those fields. After the change, the compiler will tell us where else we need to adapt our code. For now, we will not care about dates and we will just take the created field as a string.

type alias Post = { id : PostId , title : String , body : String , created : String } model : Model model = { posts = Dict.fromList [ ( 1, firstPost ), ( 2, secondPost ) ] , activePage = BlogList } firstPost : Post firstPost = { id = 1 , title = "First blog" , body = "This is the body of the first blog post" , created = "2018-04-18 19:00" }

Then, the compiler shows us where we have to change the code to make it work again:

Elm compiler helps us find the errors -- In the view function: case post of Just aPost -> div [] [ h2 [] [ text aPost.title ] , div [] [ text aPost.created ] , div [] [ text aPost.body ] , a [ onClick <| NavigateTo BlogList ] [ text "Go back" ] ] -- And improve a bit the `viewPost`, becoming `viewPostTeaser`: viewBlogList : Dict PostId Post -> Html Msg viewBlogList posts = div [] (Dict.map viewPostTeaser model.posts |> Dict.values) viewPostTeaser : PostId -> Post -> Html Msg viewPostTeaser postId post = div [ onClick <| NavigateTo <| Blog postId ] [ text post.title ]

As our data structure now reflects the data model we have on the backend, we are ready to import the information from the web service. For that, Elm offers us a system called Decoders. We will also add a contrib package to simplify our decoders:

elm package install NoRedInk/elm-decode-pipeline

And now, we add our Decoder:

postListDecoder : Decoder PostList postListDecoder = dict postDecoder postDecoder : Decoder Post postDecoder = decode Post |> required "id" string |> required "title" string |> required "body" string |> required "created" string

As now our data will come from a request, we need to update again our Model to represent the different states a request can have:

type alias Model = { posts : WebData PostList , activePage : Page } type WebData data = NotAsked | Loading | Error | Success data

In this way, the Elm language will protect us, as we always have to consider all the different cases that the data request can fail. We have to update now our view to work based on this new state:

view : Model -> Html Msg view model = case model.posts of NotAsked -> div [] [ text "Loading..." ] Loading -> div [] [ text "Loading..." ] Success posts -> case model.activePage of BlogList -> viewBlogList posts Blog postId -> let post = Dict.get postId posts in case post of Just aPost -> div [] [ h2 [] [ text aPost.title ] , div [] [ text aPost.created ] , div [] [ text aPost.body ] , a [ onClick <| NavigateTo BlogList ] [ text "Go back" ] ] Nothing -> div [ onClick <| NavigateTo BlogList ] [ text "Blog post not found" ] Error -> div [] [ text "Error loading the data" ]

We are ready to decode the data, the only thing left is to do the request. Most of the requests done on a site are when clicking a link (usually a GET) or when submitting a form (POST / GET), then, when using AJAX, we do requests in the background to fetch data that was not needed when the page was first loaded, but is needed afterwards. In our case, we want to fetch the data at the very beginning as soon as the page is loaded. We can do that with a command or as it appears in the code, a Cmd:

fetchPosts : Cmd Msg fetchPosts = let url = "http://drelm.local/jsonapi/blog" in Http.send FetchPosts (Http.get url postListDecoder)

But we have to use a new program function to pass the initial commands:

main : Program Never Model Msg main = program { init = init , view = view , update = update , subscriptions = subscriptions }

Let’s forget about the subscriptions, as we are not using them:

subscriptions : Model -> Sub Msg subscriptions model = Sub.none

Now, we just need to update our initial data; our init variable:

model : Model model = { posts = NotAsked , activePage = BlogList } init : ( Model, Cmd Msg ) init = ( model , fetchPosts )

And this is it! When the page is loaded, the program will use the command we defined to fetch all our blog posts! Check it out in the screencast:

Screencast of our sample app

If at some point, that request is too heavy, we could change it to just fetch titles plus summaries or just a small amount of posts. We could add another fetch when we scroll down or we can fetch the full posts when we invoke the update function. Did you notice that the signature of the update ends with ( Model, Cmd Msg )? That means we can put commands there to fetch data instead of just Cmd.none. For example:

update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of NavigateTo page -> let command = case page of Blog postId -> fetchPost postId BlogList -> Cmd.none ( { model | activePage = page }, command )

But let’s leave all of this implementation for a different occasion.

And that’s all for now. I might have missed something, as the frontend part grew a bit more than I expected, but check the repository as the code there has been tested and is working fine. If yuo have any question, feel free to add a comment and I will try to reply as soon as I can!

End Notes

I did not dwell too much on the syntax of elm, as there is already plenty of documentation on the official page. The goal of this post is to understand how a simple app is created from the very start and see a simple example of the Elm Architecture.

If you try to follow this tutorial step by step, you will may find an issue when trying to fetch the data from the backend while using elm-reactor. I had that issue too and it is a browser defense against (Cross-site request forgery)[https://es.wikipedia.org/wiki/Cross-site_request_forgery]. If you check the repo, you will see that I replaced the default function for get requests Http.get with a custom function to prevent this.

I also didn’t add any CSS styling because the post would be too long, but you can find plenty of information on that elsewhere.

Continue reading…

During the past few CharDUG (Charlotte Drupal User Group) meetings, I realized that we have a real need to help our existing Charlotte area Drupalers using Drupal 7 to move on to Drupal 8. There is also a huge opportunity to train those completely new to web development and Drupall. Out of some recent conversations, I have put together a training series that will span the next 6 months and become the focus for each of our monthly meetings on the 2nd Wednesday of each month.

The CharDUG Drupal 8 Training Series is a comprehensive set of training workshops to get attendees up to speed with all aspects of Drupal 8. Whether you are brand new to Drupal, focused on content management, a frontend or backend developer, or devops engineer, this series contains what you will need to utilize Drupal 8 to its potential. Attendees should bring friends, laptops, and questions. There is no need to attend all sessions, though recommended for those new to Drupal. An outline of what to expect is provided below. If you have questions or suggestions, don’t hesitate to reach out on meetup.com or @chardug.

Drupal 8: Installation and Features
  • June 2018
    • What is Drupal?
      • Community
      • Open Source
      • Contributing to Drupal
    • How to install Drupal 8
    • Important features of note
    • Changes since Drupal 7
    • Drupal release schedule
Drupal 8: Site Building
  • Julu 2018
    • Installing Drupal 8
    • Using Composer with Drupal
    • How to build a site without any development
      • Installing modules and themes
    • Top Drupal 8 contributed modules
    • Users, roles, and permissions
    • Configuring Drupal
Drupal 8: Managing Content
  • August 2018
    • Content management concepts
      • Content types
      • Content authoring experience
      • Content moderations
      • Permissions and roles
      • Content scheduling
    • Creating and editing content
    • Blocks
    • Layout Builder
    • Paragraphs
Drupal 8: Layout and Theming
  • September 2018
    • Layout options
    • Responsive design
    • Images and media
    • Developing a Drupal theme
    • Drupal and responsive layouts
    • Decoupled Drupal frontends
Drupal 8: Module Development
  • October 2018
    • Developing a module
      • Menus and routes
      • Permissions
      • Creating pages and admin forms
      • Event subscribers
    • Writing and running tests
Drupal 8: Deployment and Next Steps
  • November 2018
    • Deploying to production
      • Development workflows
      • Security
        • Guardr
      • SEO
    • Next steps for Drupal
      • Drupal 9
      • New initiatives
      • Decoupled Drupal
Blog Category: 

I started Hosted Apache Solr almost 10 years ago, in late 2008, so I could more easily host Apache Solr search indexes for my Drupal websites. I realized I could also host search indexes for other Drupal websites too, if I added some basic account management features and a PayPal subscription plan—so I built a small subscription management service on top of my then-Drupal 6-based Midwestern Mac website and started selling a few Solr subscriptions.

Back then, the latest and greatest Solr version was 1.4, and now-popular automation tools like Chef and Ansible didn't even exist. So when a customer signed up for a new subscription, the pipeline for building and managing the customer's search index went like this:

Original Hosted Apache Solr architecture, circa 2009.

Rebuilding jeffbeeman.com: My local development environment and workflow Last week I talked about setting up a new project using BLT, Dev Desktop, and Lightning. Today, I’ll talk more about my local environment setup and give a brief overview of my development and deployment workflow. Jeff Beeman Sun, 05/20/2018 - 13:20

Have you ever been building a form and found yourself wishing that you could insert additional help text - or even other forms of content (images, video) inline with the form? While each field's "Description" field is useful, sometimes it isn't enough.

The Markup module solves this problem in an elegant way by providing a new "Markup" field type.

 

This field doesn't expose any input widgets to the end user, rather it just allows for site builders to add additional markup (content) to an entity form.

 

The markup isn't saved with the resulting entity - it's just there to provide additional information to the user filling out the form.

Granted, this has always been possible by writing a small custom module utilizing hook_form_alter(), but having it as a field type makes it much more convenient to use.

 

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?

How to Integrate Alexa with Your Drupal 8 Website: A Step-by-Step Guide radu.simileanu Fri, 05/18/2018 - 16:05

Just imagine: a user asks Amazon Alexa to read out loud to him/her the headline of your latest blog post! Or maybe to look for a specific section on your Drupal site! Or, even better: quit imagining this and start implementing it instead! Right on your website. And here's how you integrate Alexa with your Drupal 8 website via the Alexa integration APIs.

A 7-step tutorial:
 

  • on how to get Alexa to “talk to” your site users/online customers
  • on turning your site's content into the needed “raw material” for setting up your custom Alexa skills
  • on how you can leverage Drupal 8's outstanding third-party integration capabilities to “fuel” your implementation plan with
     

So, here's how it's done: 
 

The Content Moderation core module was marked stable in Drupal 8.5. Think of it like the contributed module Workbench Moderation in Drupal 7, but without all the Workbench editor Views that never seemed to completely make sense. The Drupal.org documentation gives a good overview.

Content Moderation requires the Workflows core module, allowing you to set up custom editorial workflows. I've been doing some work with this for a new site for a large organization, and have some tips and tricks.

Less Is More

Resist increases in roles, workflows, and workflow states and make sure they are justified by a business need. Stakeholders may ask for many roles and many workflow states without knowing the increased complexity and likelihood of editorial confusion that results.

If you create an editorial workflow that is too strict and complex, editors will tend to find ways to work around the  system. A good compromise is to ask that the team tries something simple first and adds complexity down the line if needed.

Try to use the same workflow on all content types if you can. It makes a much simpler mental model for everyone.

Transitions are Key

Transitions between workflow states will be what you assign as permissions to roles. Typically, you'll want to lock down who can publish content, allowing content contributors to create new drafts only.

Read more

This blog has been re-posted and edited with permission from Dries Buytaert's blog. Please leave your comments on the original post.

As web applications have evolved from static pages to application-like experiences, end-users' expectations of websites have become increasingly demanding. JavaScript, partnered with effective user-experience design, enable the seamless, instantaneous interactions that users now expect.

The Drupal project anticipated this trend years ago and we have been investing heavily in making Drupal API-first ever since. As a result, more organizations are building decoupled applications served by Drupal. This approach allows organizations to use modern JavaScript frameworks, while still benefiting from Drupal's powerful content management capabilities, such as content modeling, content editing, content workflows, access rights and more.

While organizations use JavaScript frameworks to create visitor-facing experiences with Drupal as a backend, Drupal's own administration interface has not yet embraced a modern JavaScript framework. There is high demand for Drupal to provide a cutting-edge experience for its own users: the site's content creators and administrators.

At DrupalCon Vienna, we decided to start working on an alternative Drupal administrative UI using React. Sally Young, one of the initiative coordinators, recently posted a fantastic update on our progress since DrupalCon Vienna.

Next steps for Drupal's API-first and JavaScript work

While we made great progress improving Drupal's web services support and improving our JavaScript support, I wanted to use this blog post to compile an overview of some of our most important next steps:

1. Stabilize the JSON API module

JSON API is a widely-used specification for building web service APIs in JSON. We are working towards adding JSON API to Drupal core as it makes it easier for JavaScript developers to access the content and configuration managed in Drupal. There is a central plan issue that lists all of the blockers for getting JSON API into core (comprehensive test coverage, specification compliance, and more). We're working hard to get all of them out of the way!

2. Improve our JavaScript testing infrastructure

Drupal's testing infrastructure is excellent for testing PHP code, but until now, it was not optimized for testing JavaScript code. As we expect the amount of JavaScript code in Drupal's administrative interface to dramatically increase in the years to come, we have been working on improving our JavaScript testing infrastructure using Headless Chrome and Nightwatch.js. Nightwatch.js has already been committed for inclusion in Drupal 8.6, however some additional work remains to create a robust JavaScript-to-Drupal bridge. Completing this work is essential to ensure we do not introduce regressions, as we proceed with the other items in our roadmap.

3. Create designs for a React-based administration UI

Having a JavaScript-based UI also allows us to rethink how we can improve Drupal's administration experience. For example, Drupal's current content modeling UI requires a lot of clicking, saving and reloading. By using React, we can reimagine our user experience to be more application-like, intuitive and faster to use. We still need a lot of help to design and test different parts of the Drupal administration UI.

4. Allow contributed modules to use React or Twig

We want to enable modules to provide either a React-powered administration UI or a traditional Twig-based administration UI. We are working on an architecture that can support both at the same time. This will allow us to introduce JavaScript-based UIs incrementally instead of enforcing a massive paradigm shift all at once. It will also provide some level of optionality for modules that want to opt-out from supporting the new administration UI.

5. Implement missing web service APIs

While we have been working for years to add web service APIs to many parts of Drupal, not all of Drupal has web services support yet. For our React-based administration UI prototype we decided to implement a new permission screen (i.e. https://example.com/admin/people/permissions). We learned that Drupal lacked the necessary web service APIs to retrieve a list of all available permissions in the system. This led us to create a support module that provides such an API. This support module is a temporary solution that helped us make progress on our prototype; the goal is to integrate these APIs into core itself. If you want to contribute to Drupal, creating web service APIs for various Drupal subsystems might be a great way to get involved.

6. Make the React UI extensible and configurable

One of the benefits of Drupal's current administration UI is that it can be configured (e.g. you can modify the content listing because it has been built using the Views module) and extended by contributed modules (e.g. the Address module adds a UI that is optimized for editing address information). We want to make sure that in the new React UI we keep enough flexibility for site builders to customize the administrative UI.

All decoupled builds benefit

All decoupled applications will benefit from the six steps above; they're important for building a fully-decoupled administration UI, and for building visitor-facing decoupled applications.

Useful for decoupling of visitor-facing front-ends Useful for decoupling of the administration backend 1. Stabilize the JSON API module ✔ ✔ 2. Improve our JavaScript testing infrastructure ✔ ✔ 3. Create designs for a React-based administration UI ✔ 4. Allow contributed modules to use React or Twig ✔ ✔ 5. Implement missing web service APIs ✔ ✔ 6. Make the React UI extensible and configurable ✔ Conclusion

Over the past three years we've been making steady progress to move Drupal to a more API-first and JavaScript centric world. It's important work given a variety of market trends in our industry. While we have made excellent progress, there are more challenges to be solved. We hope you like our next steps, and we welcome you to get involved with them. Thank you to everyone who has contributed so far!

Special thanks to Matt Grill and Lauri Eskola for co-authoring this blog post and to Wim Leers, Gabe Sullice, Angela Byron, and Preston So for their feedback during the writing process.

My Approach to PatternLab?

I'm sometimes asked for an overview of my general approach to PatternLab. Simple: put everything for each component in the same directory!

markconroy Fri, 05/18/2018 - 15:43

When working with PatternLab, which I use for all my Drupal themes, including the theme for this website, I don’t use the full atomic approach. I don't use the approach of atoms > molecules > organisms > etc. I’m sure many people seriously disagree with me for that ( I do think it's a very clever concept). Instead I’ve renamed things to match the language we use with our clients.

I tried talking about atoms and molecules to some clients and their eyes glazed over. Clients do not want a science lesson. They do not want to be told that we are going to take two of these atoms, and mix them with one of these atom, and eventually we'll have water. No, they want to know what their final website is going to look like. When I changed the conversation and started talking about ‘Building Blocks’ (what we call our Drupal paragraph types), site blocks (Drupal's search block, branding block), display types (Drupal's view modes such as teaser, search result), etc, they immediately understood. Then we started hearing things like, "Oh, so we can create a page by adding a number of different building blocks?" and "I see, so the search results page is made up of a group of pages using the 'Search Result' display type?". And my response, "Yes!". You see, we are using plain English to ease with understanding.

Another aspect of my approach that I really like is that _everything_ for each of my components is within the same directory. For example, if it’s a nested paragraph component such as an accordion (so we need a paragraph type called 'Accordion' and one called 'Accordion Item') each template and css and js and readme and json and yaml is all in the same folder. That means when I want to reuse one in another project, I don’t need to remember what sub-particles (atoms/molecules) are used to create the organism. It also means my CSS is scoped to that specific component and doesn’t bleed out of it, so making changes or adding new features is very easy, you just scope the new component's CSS to it, so it won't affect other previously-created components.

Now the top bar of my PatternLab that used to say Atoms | Molecules | Organisms, etc has tabs for:

  • Base
    • Colours
    • Spacing
    • Breakpoints
  • Basic Elements
    • Headings
    • Paragraphs
    • Lists
  • Site Blocks (Drupal Blocks)
    • Search Block
    • Login Block
    • Branding Block
  • Building Blocks (Paragraph Types)
    • Accordion
    • Image with Text
    • Video
  • Content
    • Display Types (View Modes)
      • Teaser
      • Card
      • Search Result
    • Lists (Views)
      • Blog
      • Search Results
    • Content Types
      • Basic Page
      • Blog
      • Event
  • Page Sections (Regions)
    • Header
    • Footer
    • Sidebar
  • Sample Pages
    • Homepage
    • Blog Listing Page
    • Blog Node

After that, I have Backstop.js set up to regression test all of these, so each time I create a new component I can quickly run the visual regression tests and check that nothing has broken. Since all my CSS/JS is scoped to each individual component, it's rare if something is.

The high demand for creating a seamless digital experience across numerous devices and channels has challenged the classic approach of the media industry. The changing technology landscape and the business models have forced the companies to think out-of-the-box to drive customer interaction and avoid competitive differentiation to catch up with them.
User login in Drupal 8... Drupal Web Development titi 18 May 2018 Idea We've recently attended the AWS London summit and this year it seemed like the focus was AI and machine learning. One of the services I've been meaning to play with is Rekognition, whichh can do face detection and compare two faces among many other things. While sitting in one…
Drupal ensuring the Web Accessibility Standards Akshita Fri, 05/18/2018 - 15:18

Just like land, air, and water are meant for everyone, the web was designed to work for all people and expel any hindrance, irrespective of the surroundings and capabilities of people. But the effect of incapacity (of individuals) in the light of the fact that the web standards don’t include all in itself has become a barrier. Creating quite the paradox in the situation. 

Before completing this blog, my ignorance led me to believe that web accessibility was limited to ‘accessibility only for people with disability’. Another thing that I was coxed to believe was that it is almost synonymous with visibility issues. But it is as much for a person with auditory disabilities as it is for a person with cognitive or neurological disabilities. However, I realized I was not the only one associating such wrong notions with disabilities and web accessibility. Lack of awareness and taboos associated with disabilities often mislead us.

Ensuring that people with disability have equal and inclusive access to the resources on the web, governments and agencies follow certain guidelines in order to establish equal accessibility for all without any bias. 

What are Web Accessibility Standards and why do they matter? “Web Content Accessibility Guidelines (WCAG) is developed through the World Wide Web Consortium process with a goal of providing a single shared standard for web content accessibility that meets the needs of individuals, organizations, and governments internationally.”

The WCAG explains how the web content be made more accessible to people. Here the word "content" refers to any and every kind of information in a web page, such as text (include heading and captions too), images, sounds, codes, markup - anything that defines the layout and framework.  

Take examples of physical infrastructures like ramps and digital vision signboards, which can be used by anyone, in a similar fashion web accessibility is for everyone.

When you go out in the noon, the level of contrast can be an issue as much for a person with 6/6 vision as it can be for a person with visibility issues. Or say, older people (due to aging) face problems with changing abilities, as much as people with “temporary disabilities” such as a broken arm or lost glasses. Thus, not only web accessibility standards ensure justice for people with disability but, it is inclusive for all. 

According to the Convention on the Rights of Persons with Disabilities by the United Nations, enjoying equal human rights is a fundamental freedom. To ensure the dignity of people with disability is not a subject of ridicule, governments across the globe signed a treaty for easy web accessibility. 

How does Drupal help?

A person may face an issue either when building a website or when using it. The WCAG ensures that both the times the guidelines are followed. The World Wide Web Consortium (W3C) guidelines are then divided into two: ATAG 2.0 and WCAG 2.0. Authoring Tool Accessibility Guidelines (ATAG 2.0) addresses authoring tools and Web Content Accessibility Guidelines (WCAG 2.0) addresses Web content and is used by developers, authoring tools, and accessibility evaluation tools. 

Drupal conforms to both the guidelines. The initiative started with Drupal 7 accessibility and the community has been committed to ensuring that accessibility for all. 

What Drupal does...

The community has an accessibility team which works to identify the barriers both at the code level and the awareness level to resolve them. As a person using assistive technologies to browse the web, Drupal is built to encourage and support the semantic markup (which comes out-of-box in Drupal 8 now).

One can realize that the improvements are meant for both the visitor and administrator in the:

  • Color contrast and intensity
  • Drag and Drop functionality
  • Adding skip navigation to core themes
  • Image handling
  • Form labeling
  • Search engine form and presentation
  • Removing duplicate or null tags
  • Accessibility for Developers
Modules For Accessibility

Following are some of the Drupal modules which will assist you in keeping up with the accessibility standards. 

  1. Automatic Alt text
    The basic principle at work here is the idea of easy perceivability. Any and every information should be, thus, presented in such a way that is easily perceivable to the user. It is required for any non-text information like images and video to describe the content in the form of text for the screen readers to read it. 



    The Automatic Alt text module automatically generates an alternative text for images when no alt text has been provided by the user. This module works great for the websites and portals with user-generated content where the users may even not be aware of the purpose and importance of the Alternative text. 

    It describes the content of the image in one sentence but it doesn’t provide face recognition. 
     
  2. Block ARIA Landmark Roles
    Inspired by Block Class, Block ARAI Landmark Roles adds additional elements to the block configuration forms that allow users to assign a ARIA landmark role to a block.
     
  3. CKEditor Abbreviation
    The CKEditor Abbreviation module adds a button to CKEditor which helps in inserting and editing abbreviations in a given text. If an existing abbr tag is selected, the context menu also contains a link to edit the abbreviation.

    Abbr tag defines the abbreviation or an acronym in the content. Marking up abbreviations can give useful information to browsers, translation systems, and help boost search-engines.
     
  4. CKEditor Accessibility Checker
    The CKEditor Accessibility Checker module enables the Accessibility Checker plugin in your WYSIWYG editor. A plugin, the module lets you inspect the accessibility level of content created and immediately solve any accessibility issues that are found.
     
  5. High Contrast
    On April 13, 2011, Joseph Dolson published an article "Web Accessibility: 10 Common Developer Mistakes" stating the most common mistakes related to web accessibility and quoted that most of the issues have "more to do with a failure to understand what constitutes accessible content than with a failure to understand the technology"

    In most of the surveys, poor contrast level is often cited as the most commonly overlooked feature by the developers.

    High Contrast module, provides a quick solution to allow the user to switch between the active theme and a high contrast version of it helping them pull out of the problem.

  6. htmLawed
    According to the "Ten Common Accessibility Problems" an article by Roger Hudson, failure to use HTML header elements appropriately is one of the key accessibility issues. 

    The htmLawed module utilizes the htmLawed PHP library to limit and filter HTML for consistency with site administrator policy and standards and for security. Use of the htmLawed library allows for highly customizable control of HTML markup.

  7. Style Switcher
    The Style Switcher module takes the fuss out of creating themes or building sites with alternate stylesheets. Most of the accessibility issues have been confronted at the theming level. With this module, themers can provide a theme with alternate stylesheets. Site builder can add other alternate stylesheets right in the admin section to bring it under the right guidelines of accessibility. Allowing special styling of some part of the site, the module presents all those styles as a block with links. So any site user is able to choose the style of the site he/she prefers.

  8. Text Resize
    The handiest feature giving the end users just the right autonomy to resize the text as per their comfort of the eyesight. The Text Resize module provides the end-users with a block that can be used to quickly change the font size of text on your Drupal site. 

    It includes two buttons that can increase and decrease the size of the printed text on the page.

  9. Accessibility
    A module for the developer, Accessibility module gives you a list of available Accessibility tests, (most of which are) aligned with one or more guidelines like WCAG 2.0 or Section 508. 

    It immediately informs the site maintainer about the missing an “alt” attribute in an image, or if the headers are used appropriately. Further, each test can be customized to fit your site’s specific challenges, and customize messages users see for each test so that you can provide tips on fixing accessibility problems within the context of your site’s editing environment.

Drupal 8 Features for Accessibility 

Other than the modules that can assist you to overcome web compatibility issues, here is a list of top Drupal 8 features for easier web accessibility. 

  1. Semantics in the Core
    When an assistive device scans a web page for information, it extracts the data about the Document Object Model (DOM), or the HTML structure of the page. No further information is read by the screen reader.

    Often these assistive devices only allow a user to select to read the headings on the page or only the links. It prioritizes according to the hierarchy in which the headings and links are presented making browsing easier for users of assistive devices. 

    Drupal 8 is based on HTML5. Presenting new and better semantic components HTML5 is, in fact, one of five major initiatives outlined in Drupal 8 development. It allows theme developers to control where to use the new semantic elements and opt out entirely if they so choose. 

    When we compose semantically correct HTML, we’re telling the browser and the assistive technology what type of content it is managing with and how that information relates to other content. By doing this, assistive technology is all the more effortlessly ready to carry out its activity since it has a structure that it can work with.
     
  2. Aural Alerts
    Often page updates are expressed visually through color changes and animations. But listening to a site is a very different experience from seeing it, therefore, Drupal provides a method called “Drupal.announce()”. This helps make page updates obvious in a non-visual manner. This method creates an aria-live element on the page.

    This also lets the user know of any alert box appearing along with providing instructions to screen reader users about the tone as well. Text attached to the page is read by the assistive technologies. Drupal.announce accepts a string to be read by an audio UA. 
     
  3. Controlled Tab Order
    The accessibility issues also crop when a user uses different mediums while navigating the web. Not every user uses a mouse to navigate the website. The TabbingManager, in Drupal, is an awesome medium to direct both non-visual and non-mouse users to access the prime elements on the page in a logical order. It, thus, permits more control when exploring complex UIs.

    The tabbing manager helps in defining explicit tab order. It also allows elements besides links and form to receive keyboard focus. Without breaking the tab order it places the elements in a logical navigation flow as if it were a link on the page.
     
  4. Accessible Inline Form Errors
    It is important to provide the necessary feedback to users about the results of their form submission. Both the times when successful and when not.  This incorporates an in-line feedback that is typically provided after form submission.

    Notifications have to be concise and clear. The error message, in particular, should be easy to understand and provide simple instructions on how the situation can be resolved. And in case of successful submission, a message to confirm would do. 

    Drupal forms have turned out to be impressively more open to the expansion of available inline form errors. It is now easier for everyone to identify what errors they might have made when filling in a web form.

  5. Fieldsets
    Fieldset labels are utilized as systems for gathering related segments of forms. Effectively implemented label gives a visual diagram around the shape field gathering. This can, to a great degree, be valuable for individuals with cognitive disabilities as it viably breaks the form into subsections, making it easier to understand.

    Drupal presently uses fieldsets for radios & checkboxes in the Form API. This helps towards additionally upgrading forms in Drupal.

Conclusion

However good the features Drupal offers, in the end, it is up to the organizations to strategize and build the websites and applications around the web accessibility.   

We ensure that our different teams and interaction work together in order to make the Web more accessible to people with disabilities. At OpenSense Labs we design and develop the web technologies to ensure universal accessibility. Connect with us at hello@opensenselabs.com to make the web a better place. 

blog banner blog image Blog Type Articles Is it a good read ? On
CCBot/2.0 (http://commoncrawl.org/faq/)

آخرین ارسال ها

محتواهای محبوب

درباره ما

Author
اینجا دروپال یعنی همه چیز. در مورد دروپال صحبت میکنیم. ماژول هامون رو به اشتراک میزاریم در مورد قالب دروپال ، فروشگاه دروپال، دروپال فارسی و تاریخ شمسی دروپال صحبت میکنیم و هرچیزی که در مورد طراحی سایت با دروپال میدونیم به هم انتقال میدیم. دروپالیون یک سایت شخصی نیست. ما دست همه کسانی که برای پیشرفت دروپال تلاش میکنند رو میفشاریم و با آغوش باز اونها رو در این سایت میپذیریم.

تماس با ما

با ما تماس بگیرید.

logo-samandehi