Introduction
DXPR Builder is the only easy-to-use drag and drop content builder for Drupal. It can build layouts and add marketing elements. Everything is stored in the body field, due to this simple storage method, our product is readily compatible with the following important Drupal features:
- Multi-lingual content
- Workflow states (draft/review/published/etc.)
- Content revisions (edit history)
- Fields API (DXPR Builder can attach to longtext fields on any entity type, not just pages but also blocks, users, etc.)
1. DXPR Builder Architecture
Main technology stack:
- Drupal (7 and up)
- jQuery UI (drag and drop)
- Bootstrap 3 (layout and design framework)
- CKEditor 4 (inline editing)
On top of that there are about 25 vendor libraries included to enrich the editor experience with elements like circle counters, carousels, and countdown timers, animation settings, and more.
DXPR Builder Code Statistics
Language | Drupal 7 | Drupal 8 |
---|---|---|
PHP | 2.900 lines | 6.100 lines |
Javascript (build folder only) | 11.100 lines | 11.300 lines |
YML | 0 lines | 1.100 lines |
DXPR Builder is big Javascript library with a deep integration into Drupal's PHPAPIs.
1. Bootstrap 3
We chose to go with Bootstrap 3 to reduce development and maintenance costs. Bootstrap 3 is still the most popular framework in the Drupal user base. Support for Bootstrap 4 is on the roadmap.
Bootstrap 3 based DXPR Builder elements:
Click element to see Bootstrap 3 docs.
- Section
- Row / Columns
- Tabs (we extended it with tabs-lef and tabs-right layouts)
- Collapsibles
- Alert
- Button
- Image Carousel
- Jumbotron
- Panel (we have a different default design for panels)
- Well
2. Javascript Development in DXPR Builder
In DXPR Builder we use a lot of jQuery, but in certain places where performance is critical we replace jQuery with plain javascript code.
DXPR theme uses Grunt.js as task runner. Grunt will run the following tasks:
- Watch Grunt will watch for changes and run tasks
- Concatenate Combine all small js files in the build/ folder into 3 large files
- SASS Turns Sass files into compressed CSS
- Postcss to add vendor prefixes, supporting older browsers (last 2 versions of each browser and ie 9)
- Terser Uglifies all javascript files, converts it to a pre-set standard (ECMA 2015)
The javascript code in DXPR Builder makes extensive use of prototyping: we have some important objects defined in code, for example the object for DXPR Builder elements, and via prototyping we extend the element with new settings, render functions, code routines, etc.
2.1. The build folder
The build folder contains the entirety of proprietary javascript code that generates our DXPR Builder tool. There are 3 subdirectories in the build folder:
- dxpr-builder (includes all files that make up the builder's core and core (layout) elements)
- glazed-elements (includes all extra elements)
- glazed-param-types (includes additional types of 'settings' we can add to elements)
2.2. dxpr_frontend.js:
dxpr_frontend.min.js is not for editors, but it is loaded for anonymous visitors of the site. It is used to execute any script that is needed for interactive components. For example, for starting an animation at a certain scroll position, or for starting a circle counter animation when it scrolls into view. dxpr_frontend.js can be automatically generated by turning on DXPR Builder development mode and saving a container. Pressing the save container will print a new copy of dxpr_frontend.js to the browser console.
The reason this file is generated automatically, is that it is a subset of dxpr_builder.js. All the javascript code that is meant to execute for anonymous users is coded into the "showed" function of our elements. All the showed functions are dumped into dxpr_frontend.min.js to support interactive elements. To get a better understanding please look at these examples of "showed" functions:
Steps to update dxpr-builder-frontend.js:
- Turn on DXPR Builder "Developer mode" in module configuration form
- On any DXPR Builder container press the Save button
- The dxpr-builder-frontend.js file is now printed to the console, copy the output
- Remove all contents of the dxpr-builder-frontend.js file
- Paste the new code into the file
- Test that the code works OK by opening the Elements page in the basic demo as anonymous user. Check that all dynamic elements are working ok. This includes number counters, circle counters, videos, animations, etc.
2.3. Element architecture
All elements are based off the same class: BaseElement
The BaseElement is extended by every other element through use prototyping. Important helper functions that we use for Class extension:
In modern Javascript you would use Extend and Mixin native keywords for this, but the of DXPR Builder has a lot of legacy code.
2.4. ac_drupal.js: The Drupal integration file
In ac_drupal.js we put all code that connects Drupal with DXPR Builder. We do this for practical purposes, but it's also important for licensing purposes. The contents of this file will differ a lot between the Drupal 7 and 8 version of the module, but both of them do the following things:
- Apply image styles to image assets
- Provide settings to image parameter in DXPR Builder modal with selection of image styles
- Facilitate image re-use by connecting media module (D7) and Entity Browser (D8)
- Integrate with standalone file upload library (D8)
Our D8 DXPR Builder architect @Jaypan did great work to rewrite ac_drupal.js in the D8 version, it's a better place to learn what it does than the D7 version.Any Javascript code that interacts directly with Drupal javascript functions should go into ac_drupal.js.
2.5. Javascript performance
jQuery is slower than pure javascript. Examples of where we remove jQuery and use additional functions for performance reasons this window resize callback and this window scroll callback. In both these cases performance is extremely important because the scroll and resize events will fire up to thousands of times per second. More importantly, on weaker systems and browser they will fire less frequently because of performance restraints. This means you will max out the user's system and the website can become sluggish or crash.
- The _.debounce function in the resize callback example above will run the code only once, after the user is done resizing the viewport.
- The _.throttle function in the scroll callback with a 100ms parameter will run our code a maximum of 10 times per second, while the user is scrolling.
The above 2 functions are part of the underscore library. Underscore is included in DXPR Builder but not in DXPR theme. Therefore, in DXPR theme we detect if these functions exist and create them when needed. Undercsore is also included in Drupal 8 core but not in Drupal 7 core.
4. User templates and page templates
User templates are saved to database when people click on the disk icon in any element's controls. Page templates are not user-editable and also saved in database. To update page templates we use Drupal's hook_update function to write to the database. DXPR Builder also uses shortcodes when cloning elements, the elements are translated to shortcode before being copied to a "clipboard" element.What these 2 template features have in common is that they use shortcodes. This is an inheritance from the pre-history of the module, in a time where shortcodes were believed to be needed for storing our content and attributes. Since then I've experimented with storing templates as plain HMTL, same as we do in body fields and found it should work OK but needs to be tested and developed extensively before we can actually stop using shortcodes for storing templates.
5. AJAX Architecture and CORS
Any time that a DXPR Builder container needs to request information from the Drupal back-end, a function call from backend-connect.js will be makde to the dxpr_builder/ajax end-point on the Drupal site. All backend requests first get a fresh CSRF token before making the POST HTTP request to the backend.
6. DXPR Builder Role Based Profiles
The Profiles feature in DXPR Builder Enterprise lets site builders customize the DXPR Builder interface for different user roles. For example a site builder role might need all of DXPR Builder's features, but a Marketing role might not need the Ajax container element, or the positioned elements element. A blog writer role might have the interface reduced even more by removing landing page elements like countdown timer, circle counter, number counter, and more.
The profiles feature can also be used to customize the CKEditor toolbars for these different rules.
7. DXPR Builder Testing Basics
Testing any changes in DXPR Builder should always include the following scenarios:
- Test element in desktop view mode AND in mobile view mode (<768px).
- Test element as DXPR Builder editor AND as anonymous website visitor.
- Test creating the element, editing the element, changing styling options (e.g. background color) on the element, cloning the element, and deleting the element.
- Test saving the element in a user template and loading it on another page.
- Test the element in the middle of a busy page (e.g. main demo homepage) and also in an empty page.
- If it's a change in backend code or architecture, test any hook_update code
- and test doing a fresh installation of the main demo to see if the installation process is error-free