Coder Social home page Coder Social logo

linz's Introduction

linz

npm Codefresh build status codecov

An administration interface framework for Node.js.

Linz is not a CMS, but is capable of CMS like functionality. Linz is a good choice of framework when the administration interface is the website itself.

Linz is in early stages of development. It's being used within a few production sites, but we're working on documentation and properly launching this as an open source project. You can view progress here.

Development

You can read more about developing Linz in CONTRIBUTING.md.

Documentation

We're in the middle of a major effort to update the documentation. You can read the documentation here https://linzjs.readthedocs.io/en/latest/ and for the latest bleeding edge version of the documentation, visit https://linzjs.readthedocs.io/en/doc-updates/

Contributing

If you'd like to contribute to Linz, read our Contribution guide.

Changelog

We have a Changelog that you can reference for an ongoing list of changes.

License

MIT License.

linz's People

Contributors

allanchau avatar dependabot[bot] avatar dinoleung avatar patelswapnil avatar sandytrinh avatar smebberson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

linz's Issues

The datepicker does not work in IE 11

Expected behaviour

Modals should open the same regardless of the browser.

Actual behaviour

The datepicker breaks modals in IE11.

Steps to reproduce the behaviour

  1. Open a modal in IE 11.

Notes

  • This should be reworked so all date fields use the datepicker (browser default should be hidden). The datepicker options should be controlled via data- attributes.

formtools test file is too large

We need to break the formtools test file into smaller chunks. It's way too big. I suggest one for the overral formtools functionality, and then another for each of the key components (i.e. grid, form, fields, overview).

Synchronise the various query methods.

Expected behaviour

getQuery should use listQuery as a base. When exporting a list of records that list should be filtered by what is in the list with the option to override using the getQuery method.

Actual behaviour

The two functions are completely separate. If you have filtered the list of records by organisation for example, you can still export all the records in the model.

Steps to reproduce the behaviour

  1. Modify a listQuery method to show a subset of the total records (only for a users organisation).
  2. Try to export the subset of records.
  3. The export should show all the records and not just the ones for the user's organisation.

Formtool checkbox widgets needs to support a list of objects with label and value properties

Currently the checkboxes widget only supports array of string literal. It needs to support array of objects like below:

options = [
  { label: 'Option 1', value: 'option1' },
  { label: 'Option 2', value: 'option2' }
]

lib/formtools/form.js needs to be updated so that it handles the different various types of data for list property and convert to the correct format supported by the widgets. Select widget currently handle it's own formatting for the list property. This needs to be re-factored so that it works with the proposed changes above.

Widget formatting

Would like the ability to pass in extra information to certain widgets, to control their appearance on the edit/create forms (i.e. select count for ).

Add support for automating modifiedBy and createdBy

Now that we have a required user model, we could add and automate the modifiedBy and createdBy fields.

We'd need to have the ability to:

  • set a default value for when a user record isn't available
  • provide a process to customise the generation of the value for both modifiedBy and createdBy

Record actions dropdown not accessible in mobile view

See screenshot below:

image

As you can see, when clicking on the dropdown for the record actions, the menu is hidden! This is due to the responsive layout for table in mobile view where it set overflow-y:hidden and overflow-x:hidden. Below is the full CSS style for the responsive table layout:

@media (max-width: 767px)
.table-responsive {
  width: 100%;
  margin-bottom: 15px;
  overflow-y: hidden;
  overflow-x: scroll;
  -ms-overflow-style: -ms-autohiding-scrollbar;
  border: 1px solid #ddd;
  -webkit-overflow-scrolling: touch;
}

Ability to add confirm password field without saving it to the database

At the moment we can't include a confirm password field using the formtool without having to specify an additional key in the model schema.

Perhaps we can include a new configuration for properties that are of password type, e.g.:

form: {
        password: {
            label: 'Password',
            type: 'password',
            bConfirmPassword: true,
        }
}

Linz formtool can then render a password field as well as the confirm password field based on bConfirmPassword value.

@smebberson, what do you think?

Make plugins external

All plugins should be moved into an external repo and required by Linz in package.json if needed.

We should also read the package.json for a linz property and load any 3rd part plugins that are listed there. A new file .linzrc or linz.config.js etc could also be used that does the same thing.

A list of plugins that we should create/move:

  • document - always include with linz
  • embedded document - always include with linz
  • versions (get rid of the mongoose version package in Linz)
  • query (open pr on this one) - always include with linz

Improve search placeholder text.

Expected behaviour

We should be able to customise the search placeholder text.

Actual behaviour

The text is set to the model name.

Notes

I think list.search should be updated to be an object that accepts fields and placeholder properties. The placeholder text would override the default if provided.

The default placeholder text should also be updated to list the labels of the search fields instead of the model name since the model title is already next to the search bar? eg Search ${label}, ${label2}, and ${label3}. This would prevent any confusion on what the user can search for.

An open source project

I think we should work on moving Linz into a proper open source project. I know it needs a lot of work still, but I think it would be positive to get the community involved now.

Here are a few steps we should take:

  • Add a license to the project.
  • Create a development environment to make it easy to develop new Linz features.
  • Get started on documentation.
  • Get started on a public website.
  • Create an example project.

I'll get started on some of these things and report back here.

Replace parts of the form + list dsl with arrays.

We should stop relying on objects being displayed in object order.

Instead, we should replace these fields with an array of objects. E.g. forms, list fields, list filters. A basic form dsl might become:

module.exports = [
    { field: 'username', fieldset: 'Authentication details' },
    { field: 'firstName', fieldset: 'Authentication details' },
    { field: 'lastname', fieldset: 'Authentication details' },
];

Date tests failing when server is not utc

Expected behaviour

The date tests should pass regardless of the server timezone.

Actual behaviour

The date tests are dependent on the server timezone being +0:00.

Steps to reproduce the behaviour

  1. change your vm timezone.
  2. Run the tests.

Clean up references to req.linz to linz unless it is required for the route

Need to clean up middlewares and routes to ensure reference to linz instance is through the require model instead of req.linz and simply keep req.linz for data that are required in the routes. This will we are not populating the linz scope.

Also some of the middlewares are not passing the err to the next function e.g. see middleware/recordEdit.js

Ability to access formtools settings from within the model

At present, you can't easily access the formtools settings which is associated to a model through the formtools Mongoose plugin.

I think this information should be accessible as keys on the model object, when retrieving the model via linz.get('models')['modelname']. However, it shouldn't pollute the mongoose model itself and shouldn't exist when retrieving the model via mongoose.model('modelname').

  • Automatically added form, grid, fields, model keys to the model object at load time
  • Improve syntax to retrieving a model from linz.get('models')['modelname'] to linz.get('models.modelname')

New build process to auto cache-bust files.

Expected behaviour

We should be able to run a build process and not have to worry about cache busting files when a change is made.

Actual behaviour

A version query tag is required.

Task

  • Add rollup to linz as a dev dependency.
  • Create a build process that builds a bundle file for each of the following paths
  • common
  • record save
  • model save
  • overview
  • list view
  • Make sure the files are in the format file.[hash].js and new hashes automatically clean up the previous hash.
  • All asset paths should be contained inside of an assets.json file where we would refer to the file using the non-hashed version { test.js: "test.adsfadfgasdgadfsg.js" }.
  • Update all routes to use the new js files (common + 1 other).
  • With this update, js scripts can be versioned using npm and bundled using rollup.

Ability to customise different size modal

Expected behaviour

The ability to provide your own HTML for the modal that renders it in different sizes based on the HTML markup. e.g.

<div class="modal-dialog modal-lg"> renders a large modal

<div class="modal-dialog"> renders a small model as a default

Check modal sizes for more details.

Actual behaviour

L68 and L69 of layout.jade was added in recent commit which limit the modal to the small size and there's no option to change it to the larger version.

Suggested change

Remove the 2 line of codes mentioned above and allowing the user to provide a the modal markup. This way user can choose the size they want to use.

Replace jade

Originally this issue was meant to just replace jade, but it is turning into more of a UI refactor :).

Tasks

  • Remove the jade rendering engine.
  • Add Marko.
  • Replace LESS with SASS. (Need help)
  • Improve asset management. (Need help)
    • Introduce automated cache busting. (Need help)
    • Allow dynamically loaded styles. (New api?)
    • Allow dynamically loaded scripts. (New api?)
  • Update Bootstrap to V4.
  • Replace jade templates and reorganise into the new structure.
  • Add the ability to override any templates with a custom one.
  • Refactor the modal divs so that there is only one.
  • Remove public folder.
    • Most of the files in the pubic folder are for the views. We could move these into a new folder as part of the restructure themes/linz/assets... etc.

Example layout

Note this structure will need to change a bit for Marko compatibility.

views                     # → Root of the views
├── layouts/              # → Handlebars layouts
│   ├── base.hbs          # → The main layout
│   └── modal.hbs         # → Modal view layouts
├── partials/             # → Partials/components
│   ├── action-record.hbs # → Record actions
│   └── action-group.hbs  # → Group actions
├── error.hbs             # → Error template
└── login.hbs             # → Login template

Managing tree structures

At present, Linz has the capability to manage only a standard collection (i.e. a list of documents). This issue documents the design to have it also manage a new type of collection, a tree.

Linz model edit urls are case sensitive, and non-configurable

If you register a model within mongoose like so:

linz.mongoose.model('cUser', 'cUserSchema');

the url to view instances of the model from within linz will be admin/model/cUser/list.

It would be nice to be able to customize this to admin/model/user/list, and at a minium, it really should be case insensitive.

Align export button with sort and filter

PR 47 adds support for model exporting. Once styling branch is merged in, we need to make sure the button exists in the correct location, next to sort and filter.

Asynchronous widgets

Expected behaviour

It would be nice if Linz widgets supported an asynchronous workflow. For example, you should be able to return a Promise and Linz will wait until the Promise is resolved to retrieve the content.

Actual behaviour

A Linz widget's render function must return the HTML string.

Desired example

Here is an example of how it might work:

const cloudinaryWidget = () => {

    return (name, field, value) => ({

        render: () => new Promise((resolve, reject) => {

            const data = {
                apiKey: config.get('cloudinaryApiKey'),
                cloudName: config.get('cloudinaryApiCloudName'),
                field,
                name,
                value
            };

            linz.app.render('widgets/cloudinary', data, (err, html) => {

                if (err) {
                    return reject(err);
                }

                return resolve(html);

            });

        })

    });

}

Introduce the idea of hooks.

I think it would be good to add the ability to hook into the Linz code.

I am thinking we could just replicate the functionality in WordPress:

A rough example of how this could work:

const testFunction = (data) => new Promise((resolve, reject) => { ... });

// Need to check if function.name is "anonymous" as we need a named function.
// Either remove the hook first or set override = true.
linz.hooks.addAction('actionName', testFunction, override = false);
linz.hooks.removeAction('actionName', 'testFunction');

linz.hooks.addFilter('filterName', testFunction, override = false);
linz.hooks.removeFilter('filterName', 'testFunction');

// Internal
linz.hooks.actions = [];
linz.hooks.filters = [];

// When calling hooks (internal to linz)
const callAction = (hook) => {

    const argsArray = [...arguments];
    const [hook] = argsArray;

    if (hook && linz.hooks.actions[hook]) {

        const args = argsArray.slice(1);

        // Must be a Promise
        return linz.hooks.actions[hook](...args);

    }

    return Promise.reject(new Error(`The hook: ${hook} does not exist`));

};

Add additional events.

Linz should have additional lifecycle events to allow adding code before certain events.

For example linz.on('load', () => {}) would allow you to plugin express routes that have an empty req.linz object. You could use this to add a global notification across all pages.

linz.on('end', () => {}) would allow you to load in mq and req.linz would be fully populated. The linz admin routes would be fully loaded at this point so anything you add here will happen after the Linz middleware has run.

Improve formtools interface for related collections

At present, formtools workflow and interface for related collections is really simple:

  • load all related records
  • stick them in a select list

There are quite obvious performance and memory issues here (for larger record sets).

We need to improve this scenario with a 'search and associate' interface, which dynamically loads the choices. A simple search and associate interface might look something like http://davidstutz.github.io/bootstrap-multiselect/, or we might need something more like FarCry's array properties interface.

Update the forgot reset password workflow to use methods

At present, the forgot password -> reset workflow uses Mongoose statics. The methods are provided the user ID and then require to make a request for the user. It would be better if Linz did this and then used Mongoose methods rather than statics.

Less coding for the developing, and more contextual in the methods with the use of this.

Cell renderers should be asynchronous

At present, cell renderers are not asynchronous. This makes it impossible to do anything asynchronous in it.

We should update the code that executes the cell renderers, to pass in a callback.

Linz needs a flexible permissions model

Permissions model for Linz

This document describes the proposed permissions model for Linz.

Background

At present, the goal for Linz is not to be a fully fledged CMS, but rather a fully fledged web content administration system.

Linz currently doesn't:

  • get involved in publishing
  • view caching, or know absolutely anything about the 'public view' of your website

Content administration permissions

Despite the above, a permissions model is required to enable configuration in which certain groups of users, can perform certain actions, and others can't.

The following needs to be taken into consideration:

  • Linz doesn't have a specific User type
    • this can be configured be defining the mongoose model name, the username key, the password key
    • you can override the default password check functionality
  • There are no groups, or permissions or ACL of any kind

As you can see, it's quite simple at this stage and will hopefully remain so.

Flexible permissions

With the above considerations, the permissions system needs to be very flexible, and highly configurable as is the user system.

The following design goals should allow such a system:

  • Linz knows what action is taking place, and by whom
  • What and who can be provided to an external function to determine if an action should be allowed
  • Linz doesn't need to know anything about the implementation of a particular permission
  • by default, Linz will allow all actions, so restrictions will need to be added as required
  • Linz will hand off to external functions to determine allowed actions (with no-op ones by default)
  • Linz should use a very simple asynchronous API for integration

Examples

The following list a few examples:

  • view a model (to display in the navigation, and view the models index)
  • edit a model
  • delete a model
  • create a model
  • customAction on a model

Flexible API

The following simple, yet flexible method signature will allow for easy integration and flexibility in implementing the permissions system:

actionFn (userId, action, model, callback)

Parameters

userid: String
The id of the user requesting the action.

action: String
The action the user wants to make.

model: String, or Object, or undefined
The model in context of the action. This may be undefined for global, non-model specific actions however.

callback: Function
A function to call with the result with following signature callback(error, success)

Default implementation

As mentioned the default implementation will allow everything, and should look something like:

function actionFn (user, action, model, callback) {
    callback(null, true);
}

Future considerations

At this point, there are no custom navigation capabilities within Linz, but this will come. At that time, we'll have to provide for wrapping permissions around those too.

Optimise the index page

The index page needs to be optimised so that we don't query the database too often. Hopefully one query (or two at most) will be enough...

Refactor Linz middleware async usage

Some of the middleware is becoming complex, in that certain functions always need to run and others don't based on certain conditionals.

For example, there is code that produces recordAction variables and only needs to be executed when there are recordActions to process.

I'm suggesting that we place the functions that might be required for a particular middleware in an a separate file (i.e. for modelIndex.js it would be _modelIndex.js) and we dynamically construct the async processing order based on the conditionals that we were using inside a function to return early (and stop execution).

Should the modelIndex populate referenced models

At present, you can't include values from referenced models in a model's datagrid. It would be handy to do this.

We would need to update the modelIndex middleware to check for referenced models and define them in the populate method for the mongoose query.

@sandytrinh, what are your thoughts?

Make all paths configurable

Expected behaviour

You should be able to override methods such as getAdminForgotPasswordLink without changing the admin path.

Actual behaviour

You have to change the admin path which forces all other admin routes to change.

Steps to reproduce the behaviour

  1. Try to change an admin path.

Allow navigation per user

Currently there is no way to customise the navigation per user, this would be useful for admin only links etc.

Have better integration with Schemas

Currently we have to manually set required in the Schema and form DSL.

I'm proposing we default to using the Schema for required and validation fields.

Of course, we should still be able to override this in the form DSL.

Util.js file

Expected behaviour

Ability to invoke javascript code using jQuery in a formtool widget.

Actual behaviour

Javascript using linz.addLoadEvent(); throws an error now in the latest tag because the "/admin/public/js/utils.js has been moved to the footer.

linz.addLoadEvent(); was added to allow queueing of javascript code till jQuery is loaded (in the footer)

Steps to reproduce the behaviour

  1. Create a custom widget a jQuery code snippet
  2. Run application
  3. Check the Chrome Dev Tool, you'll notice an error that says 'Linz' is undefined. This is because the file that instantiate linz have been moved to the footer and is not loaded before the widget code.

Permission issue for model listing page

Expected behaviour

A basic model setup like the sample below should render the Edit and Delete button for all views (model index, overview page etc).

var linz = require('linz');

// Create a new mongoose schema.
var personSchema = new linz.mongoose.Schema({
  name:  String,
  email: String
});

// Add the Linz formtools plugin.
personSchema.plugin(linz.formtools.plugins.document, {
  model: {
    label: 'Person',
    description: 'A person.',
    title: 'name'
  },
  labels: {
    name: 'Name',
    email: 'Email'
  },
  list: {
    fields: {
      name: true,
      email: true
    }
  },
  form: {
    name: {
      fieldset: 'Details',
      helpText: 'The users full name.'
    },
    email: {
      fieldset: 'Details'
    }
  },
  overview: {
    summary: {
      fields: {
        name: {
          renderer: linz.formtools.cellRenderers.defaultRenderer
        },
        email: {
          renderer: linz.formtools.cellRenderers.defaultRenderer
        }
      }
    }
  },
  fields: {
    usePublishingDate: false,
    usePublishingStatus: false
  }
});

var person = module.exports = linz.mongoose.model('person', personSchema);

Actual behaviour

With the latest tagged version of the code, in the model index page, the Edit and Delete button are omitted by default, unless specific permission settings are included like example below:

permissions: (user, cb) => cb(null, { canCreate: true, canDelete: true }),

Steps to reproduce the behaviour

  1. Use example code above in your application code
  2. Browse to your model index page in the browser, you should note that there are no Edit and Delete buttons

Linz error handler api

We need a linz error api where error for linz and application can be control in one central location. e.g logging error, format the error data etc.

An issue with modals

Expected behaviour

Opening the navigation, and then clicking the Export action for a modal should produce a modal that is visible and usable.

Actual behaviour

The modal opens, but everything has a dark overlay on it.

Steps to reproduce the behaviour

  1. Navigate to a model with an Export action.
  2. Click the hamburger menu to open the navigation.
  3. Click the export button.
  4. You'll see the issue.

Metadata reading library

Build the a simple library to skim metadata containing YAML from the top of a file.

The library would take a string, and attempt to skim metadata within comment tags:

/***
label: This is a friendly label
***/

The library should take the content within the comment tags, and attempt to parse it assuming it is YAML.

Below are a few bits of code that will help.

Regex to skim the metadata within comment tags:

/\/\*\*\*\n((?:.*\n)+?)\*\*\*\//i

Code to determine a match, and use js-yaml to parse the content:

var match = data.match(regex),
    result = {
        value: file,
        label: file
    },
    metadata = {};

if (Array.isArray(match)) {
    metadata = yaml.safeLoad(match[1]);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.