summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--acf2/model/binary.lua50
-rw-r--r--acf2/model/init.lua2
-rw-r--r--acf2/persistence/init.lua4
-rw-r--r--acf2/persistence/util.lua5
-rwxr-xr-xinstall-deps.sh6
-rw-r--r--protocol.txt7
-rw-r--r--server.lua7
-rw-r--r--web/widget/abstract/base.js35
-rw-r--r--web/widget/abstract/fields.js1
-rw-r--r--web/widget/audio.js23
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)
diff --git a/server.lua b/server.lua
index 0425b5e..468cdc3 100644
--- a/server.lua
+++ b/server.lua
@@ -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();
+ }));
+ }
+ });
+ }
+);