+fs = require 'fs'
+path = require 'path'
+{existsSync:exists} = path
+
+_ = require 'underscore'
+Seq = require 'seq'
+yaml = require 'js-yaml'
+{mkdirp, mkdirpAsync} = require '../mkdirp'
+
+{ readJSONFilesAsync,
+} = require '../files'
+
+FileBackedController = require '../file-controller'
+
+
+/**
+ * @class Resource controller for dashboard requests.
+ */
+class DashboardController extends FileBackedController
+ PROTECTED_IDS : <[
+ main
+ reportcard
+ ]>
+ PROTECT : true
+
+ name : 'dashboards'
+ dataDir : 'data/dashboards'
+ -> super ...
+
+
+
+ # GET /dashboards.:format?
+ index: (req, res) ->
+ # if format is json, then return the dashboard JSON
+ switch req.format
+ case 'json'
+ pattern = @dataDir + "/*.json"
+ Seq()
+ # find list of data/dashboard/*.json files, send back the objects
+ .seq -> readJSONFilesAsync pattern, this
+ # remove filepath info
+ .seq (files) -> res.send _.values files
+ default
+ res.render "#{@id}/index"
+
+ # GET /dashboards/:dashboard
+ show: (req, res) ->
+ if req.format is 'json'
+ res.send req.dashboard
+ else
+ res.render "#{@id}/view"
+
+ # GET /dashboards/:dashboard/edit
+ edit: (req, res) ->
+ if req.format is 'json'
+ res.send req.dashboard
+ else
+ res.render "#{@id}/edit"
+
+ # GET /dashboards/new
+ new: (req, res) ->
+ res.render "#{@id}/edit"
+
+ # POST /dashboards
+ create: (req, res) ->
+ return unless data = @processBody req, res
+ file = @toFile data.id
+ if exists file
+ return res.send { result:"error", message:"Dashboard '#{data.id}' already exists!" }
+ else
+ fs.writeFile file, JSON.stringify(data), "utf8", @errorHandler(res, "Error writing Dashboard!")
+
+ # PUT /dashboards/:dashboard
+ update: (req, res) ->
+ return unless data = @processBody req, res
+ if @PROTECT and _ @PROTECTED_IDS .contains data.id
+ return res.send {result:"error", message:"Dashboard '#{data.id}' is read-only."}, 403
+ fs.writeFile @toFile(data.id), JSON.stringify(data), "utf8", @errorHandler(res, "Error writing Dashboard!")
+
+ # DELETE /dashboards/:dashboard
+ destroy: (req, res) ->
+ id = req.param.dashboard
+ if @PROTECT and _ @PROTECTED_IDS .contains id
+ return res.send {result:"error", message:"Dashboard '#{id}' is read-only."}, 403
+ fs.unlink @toFile(id), @errorHandler(res, "Dashboard '#{id}' does not exist!")
+
+
+
+
+
+module.exports = exports = DashboardController
--- /dev/null
+fs = require 'fs'
+path = require 'path'
+{existsSync:exists} = path
+
+_ = require 'underscore'
+Seq = require 'seq'
+yaml = require 'js-yaml'
+{mkdirp, mkdirpAsync} = require './mkdirp'
+
+{ readJSONFilesAsync,
+} = require './files'
+
+Controller = require './controller'
+
+
+/**
+ * @class Resource controller backed by flat json or yaml files.
+ */
+class FileBackedController extends Controller
+
+ name : null
+ dataDir : null
+
+ ->
+ @dataDir or= "data/#{@name}"
+ super ...
+
+
+
+ /**
+ * Override to customize lookup of files by ID.
+ *
+ * @param {String} id ID of this resource.
+ * @returns {String} Path to file for this resource.
+ */
+ toFile: (id) -> "#{@dataDir}/#id.json"
+
+
+ /**
+ * Auto-load :id for related requests.
+ *
+ * @param {String} id ID of the resource.
+ * @param {Function} cb Callback to invoke with the loaded object.
+ */
+ autoload: (id, cb) ->
+ file = @toFile id
+ parser = JSON.parse
+
+ yamlFile = file.replace /\.json$/i, '.yaml'
+ if exists yamlFile
+ file = yamlFile
+ parser = yaml.load
+
+ err, data <- fs.readFile file, 'utf8'
+
+ if 'ENOENT' is err?.code
+ return cb null, {}
+ if err
+ console.error "#{this}.autoload(#id, #{typeof cb}) -->\nerr"
+ return cb err
+ try
+ cb null, parser data
+ catch err
+ console.error "#{this}.autoload(#id, #{typeof cb}) -->\nerr"
+ cb err
+
+ ### Helpers
+
+ processBody: (req, res) ->
+ if not req.body
+ res.send {result:"error", message:"Data required!"}, 501
+ return false
+
+ data = req.body
+ data.slug or= data.id
+ data.id or= data.slug
+
+ if not data.slug
+ res.send {result:"error", message:"Slug required!"}, 501
+ return false
+
+ mkdirp @dataDir if not exists @dataDir
+ return data
+
+ errorHandler: (res, msg) ->
+ (err) ->
+ if err
+ msg or= err.message or String(err)
+ console.error msg
+ res.send { result:"error", message:msg }, 501
+ else
+ res.send { result:"ok" }
+
+
+module.exports = exports = FileBackedController