Skip to content

BuildFire SDK Plugin Boilerplate Template Update

Mahmoud AlSharif edited this page May 24, 2023 · 26 revisions

New BuildFire SDK Plugin Boilerplate Template

Created On Developer Email
05/15/2023 Mahmoud AlSharif malsahrif@madaincorp.com

I. Overview

The need for updating the currently used plugin template has increased as many new technologies have emerged or changed. Over the time of developing and observing, a pattern of development behaviors has been noticed, and recently a set of BuildFire standards and best practices are being applied to almost every shipped plugin, also an overall understanding has evolved of how plugins structure should look like and which BuildFire services and/or components should be used in most use cases.

All things considered, a need for a batteries-included template has emerged that will introduce a wide range of features and tools that are ready to use out of the box, meaning that plugin developers don't have to spend as much time and effort setting up their development environment instead they can focus on building the plugin and take advantage of the many built-in features the template will provide.

Overall, the goal here is to help in shipping plugins faster, better development experience, and force a set of standards and best practices to follow.


II. Template Core Changes

A. Structure Changes

1. New sample Data Model

Most plugin developers use a data access layer that mainly consists of a model (entity) and a repository, introducing this file would contribute to getting the developer familiar with the "BuildFire way", and developing faster for the experienced ones.

The following model includes all of the base attributes in addition to one method that would generate a data entity with BuildFire indexes.

/widget/global/js/models/Post.js

export default class Post {
  constructor(data = {}) {
    this.id = data.id || undefined;
    this.content = data.content || '';
    this.createdOn = data.createdOn || new Date();
    this.createdBy = data.createdBy || null;
    this.lastUpdatedOn = data.lastUpdatedOn || new Date();
    this.lastUpdatedBy = data.lastUpdatedBy || null;
    this.deletedOn = data.deletedOn || null;
    this.deletedBy = data.deletedBy || null;
    this.isActive = data.isActive || 1;
  }

  toJSON() {
    return {
      id: this.id,
      content: this.content,
      createdOn: this.createdOn,
      createdBy: this.createdBy,
      lastUpdatedOn: this.lastUpdatedOn,
      lastUpdatedBy: this.lastUpdatedBy,
      deletedOn: this.deletedOn,
      deletedBy: this.deletedBy,
      isActive: this.isActive,
      _buildfire: {
        index: {
          date1: this.createdOn,
        },
      },
    };
  }
}

2. A New base repository file

/widget/global/js/bases/BaseRepository.js

export default class BaseRepository {
  
  validate() {
    return { isValid: false, errors: ['missing_validate_function'] };
  }

  
  /**
   * base save
   * @param {Object} payload
   * @returns {Promise}
   */
  save(payload) {
    const { isValid, errors } = this.validate(payload);
    
    if (!isValid) {
      return reject(errors);
    }
    
    // save BL
  }


  /**
   * base update
   * @param {Object} payload
   * @returns {Promise}
   */
  update(payload) {
    // update BL
  }
}

3. A New sample repository file

The following sample repository file contains a complete CRUD methods reference that is ready to be used right away.

/widget/global/js/repositories/Posts.js

class Posts extends BaseRepository {

  validate(data = {}) {
    const errors = [];

    if (!data.name) errors.push('missing key "name"');
    if (typeof data.name !== 'string') errors.push('"name" must be of type string');

   return { isValid: !errors.length, errors };
  }
}

export default new PostRepository();

3. New common directory

/widget/global/

Having a redundant method or even a whole file is a common plugin developer mistake, many BuildFire services files such as LanguageManager, AuthManager, and AnalyticsManager are being used in multiple modules, this promotes using a tree-shakeable code.

4. Include the Tests directory and its dependencies

  • Using Jasmine framework, will add its dependencies at src/control/tests/lib/
  • Sample spec at src/control/tests/spec/

B. Gulp Bundling

No major changes should be made to the current default bundling, the door remains open for future ecosystem enhancements including the introduction of new plugins or upgrading the core.

C. Development Tools (Optional)

The below tools would be ready to be used out of the box, their packages and config files will be included, however, they are optional and plugin developers can opt not to use them.

1- ESLint

Should be used as the main linter tool for code quality concerns.

.eslintrc.json

{
  "extends": ["eslint:recommended"],
  "parserOptions": {
    "ecmaVersion": 2020,
    "sourceType": "module"
  },
  "env": {
    "browser": true,
    "node": true,
    "es6":true
  },
  "globals": {
    "buildfire":true
  }
}

2- Prettier

Should be used for code formatting concerns.


III. BuildFire Related Services/Components/Customizations

A. Core Features

1- CSS Injection

The template should support CSS Injection by default with minimal settings:

plugin.json

    "cssInjection": { 
      "enabled": true,
      "layouts": [ 
        {
            "name": "Layout",
            "imageUrl": "resources/layouts/layout.png",
            "cssPath": "widget/style/layouts/layout.css"
        },
     ]
    }

src/widget/index.html

<meta name="buildfire" content="enablePluginJsonLoad">

2- Language Settings

Enabling BuildFire-supported localization with base configurations:

plugin.json

"language": {
  "enabled": true,
  "languageJsonPath": "resources/languages.json"
}

src/resources/language.json

{
  "mainInfo": "main info of language tab",
  "sections": {
    "YOUR_SECTION_KEY": {
      "title": "Your Title",
      "labels": {
        "YOUR_LABEL_KEY": {
          "title": "Section Title",
          "defaultValue": "Default Value"
        }
      }
    }
  }
}

src/widget/index.html

<meta name="buildfire" content="enablePluginJsonLoad">

B. Code Blocks

Code snippets related to commonly used BuildFire services/features, these blocks should be well described and easily removed if not needed

1- Deeplinking handlers (Sharing):

/widget/global/js/deeplinking.js

/**
 * @description BuildFire sharing and deep linking
 * See {@link https://sdk.buildfire.com/docs/share-links-and-deep-linking}
 **/

buildfire.deeplink.getData((deepLinkData) => {
  // handle deeplink init
});

buildfire.deeplink.onUpdate((deepLinkData) => {
  // handle deeplink update
}, true);


const share = () => {
  buildfire.deeplink.generateUrl(
    {
      title: 'Title',
      description: 'Description',
      imageUrl: '',
      data: { itemId: 'xyz'},
    },
    (err, result) => {
      if (err) return console.error(err);
      buildfire.device.share({
        subject: 'Title',
        text: 'Description',
        link: result.url
      }, (err, result) => {
        if (err) console.error(err);
        if (result) console.log(result);
      });
    }
  );
};

2- Authentication changes handlers

/widget/global/js/auth.js

/**
 * @description Authentication changes handlers 
 * See {@link https://sdk.buildfire.com/docs/auth#onlogin-}
 **/

buildfire.auth.onLogin(AuthManager.onUserChange, true);
buildfire.auth.onLogout(AuthManager.onUserChange, true);

3- Control & Widget syncing

/widget/global/js/syncing.js

/**
 * @description Sync changes with the widget
 * See {@link https://sdk.buildfire.com/docs/messaging-to-sync-your-control-to-widget}
 **/
buildfire.messaging.sendMessageToWidget({ scope: 'new_entries' });

/**
 * @description Sync control changes
 * See {@link https://sdk.buildfire.com/docs/messaging-to-sync-your-control-to-widget#onreceivedmessage}
 **/
buildfire.messaging.onReceivedMessage = (message) => {
  // If the domain is specified, then refresh a specific area only! 
  // if (message.scope === 'new_entries') fetch();
  // Reload the whole app otherwise
  // else window.location.reload();
};

/**
 * @description control datastore changes
 * See {@link https://sdk.buildfire.com/docs/datastore#onupdate-}
 **/
buildfire.datastore.onUpdate((event) => {
  console.log("Data has been updated ", event);
});

4- Tablet CSS query selector

widget/style/layouts/layout.css

@media only screen and (min-device-width: 768px) {
    // tablet customizations
}

5- CSS Safe areas

widget/style/layouts/layout.css

html[safe-area="true"] body {
  padding-top: calc(var(--base-padding) + constant(safe-area-inset-top)) !important;
  padding-top: calc(var(--base-padding) + env(safe-area-inset-top)) !important;
  padding-bottom: calc(var(--base-padding) + constant(safe-area-inset-bottom)) !important;
  padding-bottom: calc(var(--base-padding) + env(safe-area-inset-bottom)) !important;
}

6- Getting app theme

/widget/global/js/app-theme.js

/**
 * @description Handle app theme changes
 * See {@link https://sdk.buildfire.com/docs/appearance#getapptheme-}
 **/
const getAppTheme = () => {
  return new Promise((resolve, reject) => {
    buildfire.appearance.getAppTheme((err, appTheme) => {
      if (err) return console.error(err);
      state.appTheme = appTheme;
      resolve(appTheme);
    });

    buildfire.appearance.onUpdate((appTheme) => {
      state.appTheme = appTheme;
    }, true);
  });
};

7- Updating plugin.json keys

plugin.json

{
  "supportSite":"",
  "pluginKeywords": "keyword1,keyword2,keyword3",
}

8- Empty State

Need to be spec'ed!


C. Other Features

The features below involve adding handlers that can be reused whenever the business logic requires, and including them with the boilerplate promotes consistency and usability.

1- Analytics

/widget/global/js/AnalyticsManager.js

2- Authentication

/src/shared/AuthManager.js

3- Bookmarking

The bookmarking feature has proven useful in many production plugins, the class would expose add, delete, and getAll functions.

/widget/global/js/BookmarksManager.js

4- Notes

Only openDialog, get and a deep link handler will be added

/widget/global/js/NotesManager.js

5- State manager (state.js)

6- Widget Hello World page that demonstrates:

  • Language Settings feature
  • BuildFire Skeleton
  • TinyMCE

D. Resources

1- sample resources/layout.png


IV. BuildFire CLI Integration

The current default template should be renamed to pluginBackboneTemplate or a better name, and a new repository named defaultPluginTemplate will be created to host this new template, gulpPluginTemplate should get deprecated as well.

Example commands:

Initializing without specifying any preferences would clone this new template (default)

  1. buildfire plugin init sample-plugin

Specifying gulp would also clone this new template

  1. buildfire plugin init sample-plugin gulp

We can proceed without having to make changes to the CLI

  • If no changes are made to the CLI, then what about point number 2 above?

V. SDK Docs + README.md

BuildFire SDK documentation should well-document how to get started with this template and any necessary information, I recommend getting rid of the medium article link here, and instead write a new inhouse Get started page

VI. Forked repository for internal use only (Phase II)

1- Global Search (Search Engine) Q: Should it be included in the default public template?

2- Report Abuse (Better be mentioned only)

3- Navigation System: Router

4- Navigation System: ViewsManager

5- Navigation System: Templates directory

6- Navigation System: Pages directory

7- Getting BuildFire context

src/widget/js/state.js

{
// ...
  get context() { return buildfire.getContext(); },
// ...
}

VII. Modification History

Date Developer Description
05/15/2023 Mahmoud AlSharif Document Creation
05/17/2023 Mahmoud AlSharif Teamleader feedback
05/21/2023 Mahmoud AlSharif CTO feedback
05/25/2023 Mahmoud AlSharif CTO feedback #2
Clone this wiki locally