diff --git a/backends/sqlite.js b/backends/sqlite.js index 2db3a19a..88a538e4 100644 --- a/backends/sqlite.js +++ b/backends/sqlite.js @@ -23,11 +23,15 @@ class sqliteDatabase { } async migrate() { - for (let doctype in frappe.models.data.doctype) { - if (await this.table_exists(doctype)) { - await this.alter_table(doctype); - } else { - await this.create_table(doctype); + for (let doctype in frappe.modules) { + // check if controller module + if (frappe.modules[doctype].Meta) { + if (await this.table_exists(doctype)) { + await this.alter_table(doctype); + } else { + await this.create_table(doctype); + } + } } await this.commit(); diff --git a/client/desk/index.js b/client/desk/index.js index d62d4c81..74287dfa 100644 --- a/client/desk/index.js +++ b/client/desk/index.js @@ -2,8 +2,8 @@ const frappe = require('frappejs'); const Search = require('./search'); const Router = require('frappejs/common/router'); const Page = require('frappejs/client/view/page'); -const List = require('frappejs/client/view/list'); -const Form = require('frappejs/client/view/form'); +const BaseList = require('frappejs/client/view/list'); +const BaseForm = require('frappejs/client/view/form'); const Navbar = require('./navbar'); module.exports = class Desk { @@ -58,8 +58,8 @@ module.exports = class Desk { get_list_page(doctype) { if (!this.pages.lists[doctype]) { - let page = new Page('List ' + doctype); - page.list = new List({ + let page = new Page('List ' + frappe.get_meta(doctype).name); + page.list = new (this.get_view_class(doctype, 'List', BaseList))({ doctype: doctype, parent: page.body }); @@ -73,8 +73,8 @@ module.exports = class Desk { get_form_page(doctype) { if (!this.pages.forms[doctype]) { - let page = new Page('Edit ' + doctype); - page.form = new Form({ + let page = new Page('Edit ' + frappe.get_meta(doctype).name); + page.form = new (this.get_view_class(doctype, 'Form', BaseForm))({ doctype: doctype, parent: page.body }); @@ -91,6 +91,20 @@ module.exports = class Desk { return this.pages.forms[doctype]; } + get_view_class(doctype, class_name, default_class) { + let client_module = this.get_client_module(doctype); + if (client_module && client_module[class_name]) { + return client_module[class_name]; + } else { + return default_class; + } + + } + + get_client_module(doctype) { + return frappe.modules[`${doctype}_client`]; + } + add_sidebar_item(label, action) { let item = frappe.ui.add('a', '', frappe.ui.add('p', null, frappe.desk.sidebar)); item.textContent = label; diff --git a/client/view/controls/base.js b/client/view/controls/base.js index 41c44db4..cc3fd30d 100644 --- a/client/view/controls/base.js +++ b/client/view/controls/base.js @@ -97,6 +97,14 @@ class BaseControl { value = await this.validate(value); await this.doc.set(this.fieldname, value); } + + disable() { + this.input.setAttribute('disabled', 'disabled'); + } + + enable() { + this.input.removeAttribute('disabled'); + } } module.exports = BaseControl; \ No newline at end of file diff --git a/client/view/form.js b/client/view/form.js index e69e86a7..21179c36 100644 --- a/client/view/form.js +++ b/client/view/form.js @@ -1,7 +1,7 @@ const frappe = require('frappejs'); const controls = require('./controls'); -module.exports = class Form { +module.exports = class BaseForm { constructor({doctype, parent, submit_label='Submit'}) { this.parent = parent; this.doctype = doctype; @@ -11,6 +11,9 @@ module.exports = class Form { this.controls_list = []; this.meta = frappe.get_meta(this.doctype); + if (this.setup) { + this.setup(); + } this.make(); } diff --git a/client/view/list.js b/client/view/list.js index fcf9239c..9fef6bba 100644 --- a/client/view/list.js +++ b/client/view/list.js @@ -1,6 +1,6 @@ const frappe = require('frappejs'); -module.exports = class List { +module.exports = class BaseList { constructor({doctype, parent, fields}) { this.doctype = doctype; this.parent = parent; @@ -18,13 +18,8 @@ module.exports = class List { async run() { this.make_body(); - this.set_filters(); - let data = await this.meta.get_list({ - filters: this.filters, - start:this.start, - limit:this.page_length + 1 - }); + let data = await this.get_data(); for (let i=0; i< Math.min(this.page_length, data.length); i++) { this.render_row(this.start + i, data[i]); @@ -40,16 +35,31 @@ module.exports = class List { this.update_more(data.length > this.page_length); } + async get_data() { + return await frappe.db.get_all({ + doctype: this.doctype, + fields: this.get_fields(), + filters: this.get_filters(), + start: this.start, + limit: this.page_length + 1 + }); + } + + get_fields() { + return ['name']; + } + async append() { this.start += this.page_length; await this.run(); } - set_filters() { - this.filters = {}; + get_filters() { + let filters = {}; if (this.search_input.value) { - this.filters.keywords = ['like', '%' + this.search_input.value + '%']; + filters.keywords = ['like', '%' + this.search_input.value + '%']; } + return filters; } make_body() { @@ -104,10 +114,14 @@ module.exports = class List { render_row(i, data) { let row = this.get_row(i); - row.innerHTML = this.meta.get_row_html(data); + row.innerHTML = this.get_row_html(data); row.style.display = 'block'; } + get_row_html(data) { + return `${data.name}`; - } - -} - -module.exports = { Meta: Meta } \ No newline at end of file +} \ No newline at end of file diff --git a/model/models.js b/model/models.js deleted file mode 100644 index 1339ba5c..00000000 --- a/model/models.js +++ /dev/null @@ -1,25 +0,0 @@ -const process = require('process'); -const frappe = require('frappejs'); - -class Models { - constructor() { - this.data = {doctype: {}}; - this.controllers = {}; - this.meta_classes = {}; - } - - get(doctype, name) { - return this.data[frappe.slug(doctype)][frappe.slug(name)]; - } - - get_controller(doctype) { - return this.controllers[frappe.slug(doctype)]; - } - - get_meta_class(doctype) { - return this.meta_classes[frappe.slug(doctype)] || frappe.meta.Meta; - } - -} - -module.exports = { Models: Models } \ No newline at end of file diff --git a/models/doctype/todo/todo.js b/models/doctype/todo/todo.js index b06a9227..f378d236 100644 --- a/models/doctype/todo/todo.js +++ b/models/doctype/todo/todo.js @@ -1,19 +1,13 @@ -const frappe = require('frappejs'); +const BaseMeta = require('frappejs/model/meta'); +const BaseDocument = require('frappejs/model/document'); -class todo_meta extends frappe.meta.Meta { +class ToDoMeta extends BaseMeta { setup_meta() { Object.assign(this, require('./todo.json')); - this.list_options.fields = ['name', 'subject', 'status']; } - - get_row_html(data) { - const sign = data.status === 'Open' ? '' : '✔'; - return `${sign} ${data.subject}`; - } - } -class todo extends frappe.document.Document { +class ToDo extends BaseDocument { setup() { this.add_handler('validate'); } @@ -24,4 +18,7 @@ class todo extends frappe.document.Document { } } -module.exports = {todo:todo, todo_meta:todo_meta}; +module.exports = { + Document: ToDo, + Meta: ToDoMeta +}; diff --git a/models/doctype/todo/todo_client.js b/models/doctype/todo/todo_client.js new file mode 100644 index 00000000..1394727f --- /dev/null +++ b/models/doctype/todo/todo_client.js @@ -0,0 +1,16 @@ +const BaseList = require('frappejs/client/view/list'); +const BaseForm = require('frappejs/client/view/form'); + +class ToDoList extends BaseList { + get_fields() { + return ['name', 'subject', 'status']; + } + get_row_html(data) { + return `${data.subject}`; + } +} + +module.exports = { + Form: BaseForm, + List: ToDoList +} \ No newline at end of file diff --git a/server/index.js b/server/index.js index 0428cf32..c48205a4 100644 --- a/server/index.js +++ b/server/index.js @@ -5,7 +5,7 @@ const express = require('express'); const app = express(); const frappe = require('frappejs'); const rest_api = require('./rest_api') -const models = require('frappejs/server/models'); +const init_models = require('frappejs/server/init_models'); const common = require('frappejs/common'); const bodyParser = require('body-parser'); const path = require('path'); @@ -13,11 +13,11 @@ const path = require('path'); module.exports = { async start({backend, connection_params, static, models_path}) { await this.init(); - models.init_models(path.resolve('node_modules', 'frappejs', 'models')); - models.init_models(models_path); - await this.init_db({backend:backend, connection_params:connection_params}); + this.init_models(models_path); + // database + await this.init_db({backend:backend, connection_params:connection_params}); // app app.use(bodyParser.json()); @@ -30,7 +30,14 @@ module.exports = { // listen frappe.app = app; frappe.server = app.listen(frappe.config.port); + }, + init_models(models_path) { + // import frappe modules + init_models(path.join(path.dirname(require.resolve('frappejs')), 'models')); + + // import modules from the app + init_models(models_path); }, async init() { diff --git a/server/init_models.js b/server/init_models.js new file mode 100644 index 00000000..519f1b0f --- /dev/null +++ b/server/init_models.js @@ -0,0 +1,23 @@ +const frappe = require('frappejs'); +const walk = require('walk'); +const path = require('path'); + +module.exports = function(models_path) { + if (!models_path) { + return; + } + + walk.walkSync(models_path, { + listeners: { + file: (basepath, file_data, next) => { + const doctype = path.basename(path.dirname(basepath)); + const name = path.basename(basepath); + const file_path = path.resolve(basepath, file_data.name); + if (doctype==='doctype' && file_data.name.endsWith('.js')) { + frappe.modules[file_data.name.slice(0, -3)] = require(file_path); + } + next(); + } + } + }); +} diff --git a/server/models.js b/server/models.js deleted file mode 100644 index 812c8a70..00000000 --- a/server/models.js +++ /dev/null @@ -1,42 +0,0 @@ -const frappe = require('frappejs'); -const walk = require('walk'); -const path = require('path'); - -module.exports = { - init_models(models_path) { - const setup_model = (doctype, name, file_path) => { - // add to frappe.models.data - if (!frappe.models[doctype]) { - frappe.models[doctype] = {}; - } - frappe.models.data[doctype][name] = require(file_path); - } - - const setup_controller = (doctype, file_path) => { - let _module = require(file_path); - frappe.models.controllers[doctype] = _module[doctype]; - if (_module[doctype + '_meta']) { - frappe.models.meta_classes[doctype] = _module[doctype + '_meta']; - } - } - - walk.walkSync(models_path, { - listeners: { - file: (basepath, file_data, next) => { - const doctype = path.basename(path.dirname(basepath)); - const name = path.basename(basepath); - const file_path = path.resolve(basepath, file_data.name); - if (file_data.name.endsWith('.json')) { - setup_model(doctype, name, file_path) - } - if (doctype==='doctype' && file_data.name.endsWith('.js')) { - setup_controller(path.basename(basepath), file_path); - } - next(); - } - } - }); - - } - -} \ No newline at end of file diff --git a/server/rest_api.js b/server/rest_api.js index 6996f49d..ed81aaa5 100644 --- a/server/rest_api.js +++ b/server/rest_api.js @@ -4,7 +4,6 @@ module.exports = { setup(app) { // get list app.get('/api/resource/:doctype', frappe.async_handler(async function(request, response) { - let fields, filters; for (key of ['fields', 'filters']) { if (request.query[key]) { request.query[key] = JSON.parse(request.query[key]); diff --git a/tests/helpers.js b/tests/helpers.js index c5c65091..dcbd6505 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -2,7 +2,8 @@ const server = require('frappejs/server'); module.exports = { async init_sqlite() { - server.init() + server.init(); + server.init_models(); server.init_db({ backend: 'sqlite', connection_params: {db_path: 'test.db'} diff --git a/tests/test_models.js b/tests/test_models.js index 67ceb25d..4016d764 100644 --- a/tests/test_models.js +++ b/tests/test_models.js @@ -8,7 +8,7 @@ describe('Models', () => { }); it('should get todo json', () => { - let todo = frappe.models.get('DocType', 'ToDo'); + let todo = frappe.get_meta('todo'); assert.equal(todo.issingle, 0); }); }); \ No newline at end of file diff --git a/tests/test_server.js b/tests/test_server.js index d6957ced..bb3d5cd6 100644 --- a/tests/test_server.js +++ b/tests/test_server.js @@ -1,5 +1,8 @@ const server = require('frappejs/server'); if (require.main === module) { - server.start({backend: 'sqlite', connection_params: {db_path: 'test.db'}}); + server.start({ + backend: 'sqlite', + connection_params: {db_path: 'test.db'} + }); } \ No newline at end of file