diff options
-rw-r--r-- | acf2/model/binary.lua | 50 | ||||
-rw-r--r-- | acf2/model/init.lua | 2 | ||||
-rw-r--r-- | acf2/persistence/init.lua | 4 | ||||
-rw-r--r-- | acf2/persistence/util.lua | 5 | ||||
-rwxr-xr-x | install-deps.sh | 6 | ||||
-rw-r--r-- | protocol.txt | 7 | ||||
-rw-r--r-- | server.lua | 7 | ||||
-rw-r--r-- | web/widget/abstract/base.js | 35 | ||||
-rw-r--r-- | web/widget/abstract/fields.js | 1 | ||||
-rw-r--r-- | web/widget/audio.js | 23 |
10 files changed, 112 insertions, 28 deletions
diff --git a/acf2/model/binary.lua b/acf2/model/binary.lua new file mode 100644 index 0000000..2af10a0 --- /dev/null +++ b/acf2/model/binary.lua @@ -0,0 +1,50 @@ +--[[ +Copyright (c) 2012-2014 Kaarle Ritvanen +See LICENSE file for license details +--]] + +local M = {} + +local object = require('acf2.object') +local class = object.class +local super = object.super + + +local b64 = require('b64') + +local magic = require('magic') +magic = magic.open(magic.MIME_TYPE) +magic:load() + + +M.Data = class() + +function M.Data:init(path, data) + self.path = path + self.data = data + self.type = magic:buffer(data) +end + +function M.Data:encode() + return 'data:'..self.type..';base64,'..b64.encode(self.data) +end + + +M.Audio = class(require('acf2.model.field').Field) + +function M.Audio:init(params) + super(self, M.Audio):init(params) + self.dtype = 'binary' + self.widget = 'audio' +end + +function M.Audio:load(context) + local value = super(self, M.Audio):load(context) + return type(value) == 'string' and M.Data(context.path, value) or value +end + +-- not yet implemented +function M.Audio:save(context, value) end + + +return M diff --git a/acf2/model/init.lua b/acf2/model/init.lua index 376ec64..6e8b7f5 100644 --- a/acf2/model/init.lua +++ b/acf2/model/init.lua @@ -9,6 +9,8 @@ M.error = require('acf2.error') local raise = M.error.raise local relabel = M.error.relabel +M.binary = require('acf2.model.binary') + local combination = require('acf2.model.combination') M.Union = combination.Union M.Range = combination.Range diff --git a/acf2/persistence/init.lua b/acf2/persistence/init.lua index fee408a..6f7fbf8 100644 --- a/acf2/persistence/init.lua +++ b/acf2/persistence/init.lua @@ -70,7 +70,9 @@ function DataStore:get(path) res = false else res = res and true or false end - elseif t == 'reference' then assert(atype == 'string') + elseif contains({'binary', 'reference'}, t) then + assert(atype == 'string') + else assert(false) end end end diff --git a/acf2/persistence/util.lua b/acf2/persistence/util.lua index 2491381..d233b1d 100644 --- a/acf2/persistence/util.lua +++ b/acf2/persistence/util.lua @@ -1,5 +1,5 @@ --[[ -Copyright (c) 2012-2013 Kaarle Ritvanen +Copyright (c) 2012-2014 Kaarle Ritvanen See LICENSE file for license details --]] @@ -13,8 +13,7 @@ end function M.read_file(path) local file = M.open_file(path) - local data = '' - for line in file:lines() do data = data..line end + local data = file:read('*all') file:close() return data end diff --git a/install-deps.sh b/install-deps.sh index b78b4a0..020685a 100755 --- a/install-deps.sh +++ b/install-deps.sh @@ -1,10 +1,10 @@ #!/bin/sh -# Copyright (c) 2012-2013 Kaarle Ritvanen +# Copyright (c) 2012-2014 Kaarle Ritvanen # See LICENSE file for license details -PACKAGES="lua5.2-augeas lua5.2-cjson lua5.2-crypto lua5.2-openrc lua5.2-posix - lua5.2-stringy uwsgi uwsgi-lua" +PACKAGES="lua5.2-augeas lua5.2-b64 lua5.2-cjson lua5.2-crypto lua5.2-file-magic + lua5.2-openrc lua5.2-posix lua5.2-stringy uwsgi uwsgi-lua" [ "$1" = -d ] && PACKAGES="$PACKAGES bash curl" diff --git a/protocol.txt b/protocol.txt index 035f4ec..b881a96 100644 --- a/protocol.txt +++ b/protocol.txt @@ -41,11 +41,12 @@ req: GET /config/<obj_path> resp: JSON object, with the following attributes: data: JSON serialization of object - primitive types as JSON primitives + - binary data encoded as data URIs - references as path names (relative to scope) - models as JSON objects with members as attributes: - primitive members as JSON primitives - - reference, model, collection, list, and set members as - path names + - binary, reference, model, collection, list, and set + members as path names - collections, lists, and sets as JSON arrays with members as attributes (as for models) meta: JSON object, with the following attributes @@ -53,7 +54,7 @@ resp: JSON object, with the following attributes: - ui-name (shown to user) - description (optional help text) - type (e.g. model, collection, list, set, reference, - string, number, boolean) + string, number, boolean, binary) - widget (name of client-side JS module used to display the data) - editable (boolean) @@ -4,6 +4,7 @@ See LICENSE file for license details --]] local acf = require('acf2') +local mbin = acf.model.binary local mnode = acf.model.node local isinstance = acf.object.isinstance @@ -181,13 +182,17 @@ return function(env) 'read' ) v = mnode.path(v) - end + + elseif isinstance(v, mbin.Data) then v = v.path end if readable then node[k] = v end end res = {data=node, meta=mnode.meta(obj)} elseif mnode.has_permission(parent, user, 'read') then + if isinstance(obj, mbin.Data) then + obj = obj:encode() + end res = {data=obj, meta=mnode.mmeta(parent, name)} else return 403 end diff --git a/web/widget/abstract/base.js b/web/widget/abstract/base.js index df02ee3..d4ed661 100644 --- a/web/widget/abstract/base.js +++ b/web/widget/abstract/base.js @@ -24,12 +24,11 @@ define(["acf2/dom", "jquery", "underscore"], function(dom, $, _) { this.level = level; var value = data.get(name); - var status = data.status(name); if (!editable || !meta.editable) { var el = this.staticRender(value, meta); if (el) { - dom.setStatus(el, status); + dom.setStatus(el, data.status(name)); return el; } } @@ -37,18 +36,6 @@ define(["acf2/dom", "jquery", "underscore"], function(dom, $, _) { this.makeEl(); this.dynamic = meta.dynamic; - var self = this; - - var request; - function handleRequest(req) { - request = req; - req.done(function(value, meta) { - if (req != request) return; - self.render(value, meta); - self.setStatus(status); - }); - } - this.wrapped = this.wrap(); this.visible = true; @@ -63,10 +50,12 @@ define(["acf2/dom", "jquery", "underscore"], function(dom, $, _) { this.wrapped.append(link); } - handleRequest(this.requestData(value, meta)); + this.handleResponse(this.requestData(value, meta)); + + var self = this; function validate() { - request.done(function(value) { + self.request.done(function(value) { self.validate(value); }); } @@ -78,7 +67,7 @@ define(["acf2/dom", "jquery", "underscore"], function(dom, $, _) { }); this.wrapped.on("updated", function(event, field) { - if (self.dynamic) handleRequest(self.refreshData()); + if (self.dynamic) self.refresh(); if (!field || self.dynamic || (meta.condition && field in meta.condition)) @@ -104,6 +93,18 @@ define(["acf2/dom", "jquery", "underscore"], function(dom, $, _) { return def; }, + handleResponse: function(request) { + this.request = request; + var self = this; + request.done(function(value, meta) { + if (request != self.request) return; + self.render(value, meta); + self.setStatus(self.data.status(self.name)); + }); + }, + + refresh: function() { this.handleResponse(this.refreshData()); }, + wrap: function() { return this.el; }, showStatus: true, diff --git a/web/widget/abstract/fields.js b/web/widget/abstract/fields.js index a2ec5e0..89b2506 100644 --- a/web/widget/abstract/fields.js +++ b/web/widget/abstract/fields.js @@ -8,6 +8,7 @@ define( "acf2/widget/abstract/node", "jquery", "underscore", + "acf2/widget/audio", "acf2/widget/checkbox", "acf2/widget/checkboxes", "acf2/widget/combobox", diff --git a/web/widget/audio.js b/web/widget/audio.js new file mode 100644 index 0000000..184fd5e --- /dev/null +++ b/web/widget/audio.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2012-2014 Kaarle Ritvanen + * See LICENSE file for license details + */ + +define( + ["acf2/dom", "acf2/widget/abstract/inline", "jquery", "underscore"], + function(dom, Base, $, _) { + return Base.extend({ + render: function(value, meta) { + if (!value) return; + + var self = this; + + this.el.html(_.isObject(value) ? $("<audio>").attr( + {src: value.data, autoplay: true, controls: true} + ) : dom.href().text("Play").click(function() { + self.refresh(); + })); + } + }); + } +); |