Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
module.exports = do ->
Dependencies
ld = require 'lodash'
cuid = require 'cuid'
storage = require '../storage.js'
common = require './common.js'
GPREFIX = storage.DBPREFIX.GROUP
UPREFIX = storage.DBPREFIX.USER
PPREFIX = storage.DBPREFIX.PAD
Groups belongs to users. Each user can have multiple groups of pads.
A group object can be represented like :
var group = { _id: 'autoGeneratedUniqueString', name: 'group1', pads: [ 'padkey1', 'padkey2' ], admins: [ 'userkey1', 'userkey2' ], users: [ 'ukey1' ], visibility: 'restricted' || 'public' || 'private', password: 'secret', readonly: false };
group = {}
Group reading
This function uses common.getDel
with del
to false and GPREFIX
fixed. It will takes mandatory key string and callback function. See
common.getDel
for documentation.
group.get = ld.partial common.getDel, false, GPREFIX
This function adds a new group or updates an existing one. It checks the fields, throws error if needed, set defaults options. As arguments, it takes mandatory :
params
object, with
a name
string that can't be empty
admin
string, the unique key identifying the initial administrator
of the groupvisibility
, a string defined as restricted by default to invited
users. Can be set to public, letting non authenticated users access to
all pads in the group with the URL, or private, protected by a password
phrase chosen by the administratorreadonly
, false on creation. If true, pads that will be linked to
the group will be set on readonly modepassword
string field, only usefull if visibility has been fixed to
private, by default an empty stringusers
and admins
arrays, with ids of users invited to read and/or edit
pads, for restricted visibility only; and group administrators
callback
function returning Error if error, null otherwise and the
group object.
set
creates an empty pads
array in case of creation, otherwise it just
gets back old value. pads
array contains ids of pads attached to the
group via model.pad
creation or update.
group.set = (params, callback) ->
common.addSetInit params, callback, ['name', 'admin']
g = group.fn.assignProps params
if params._id
g._id = params._id
storage.db.get GPREFIX + g._id, (err, res) ->
return callback err if err
return callback new Error 'group does not exist' if not res
g.pads = res.pads
group.fn.checkSet g, callback
else
g._id = cuid()
g.pads = []
group.fn.checkSet g, callback
Group removal
This function uses common.getDel
with del
to true and GPREFIX
fixed. It will takes mandatory key string and callback function. See
common.getDel
for documentation.
It uses the callback
function to handle secondary indexes for users and
pads.
/
group.del = (key, callback) ->
if not ld.isFunction callback
throw new TypeError 'callback must be a function'
common.getDel true, GPREFIX, key, (err, g) ->
return callback err if err
group.fn.indexUsers true, g, (err) ->
return callback err if err
group.fn.cascadePads g, callback
Helper here are public functions created to facilitate interaction with the API and improve performance, avoiding extra checking when not needed. TODO : may be written to improve API usage
group.helper = {}
linkPads
is a function to attach new pads to an existing group.
It takes mandatory arguments :
\_id
entifier, a stringadd
, a string for only one addition, an array for multiple adds. group.helper.linkPads = ld.noop
group.helper.unlinkPads = ld.noop
string or array
group.helper.inviteUsers = ld.noop
string or array
group.helper.setAdmins = ld.noop
string of false
group.helper.setPassword = ld.noop
boolean
group.helper.setPublic = ld.noop
boolean
group.helper.archive = ld.noop
These functions are not private like with closures, for testing purposes, but they are expected be used only internally by other MyPads functions. All of these are tested through public API.
group.fn = {}
assignProps
takes params object and assign defaults if needed.
It creates :
admins
array, unioning admin key to optional others adminsusers
array, empty or with given keyspads
array, empty on creation, can't be fixed eithervisibility
string, defaults to restricted, with only two other
possibilities : private or publicpassword
string, null by defaultreadonly
boolean, false by defaultIt returns the group object.
group.fn.assignProps = (params) ->
p = params
g = name: p.name
if ld.isArray(p.admins)
p.admins = ld.filter(p.admins, ld.isString)
else
p.admins = []
g.admins = ld.union [ p.admin ], p.admins
g.users = ld.uniq p.users
v = p.visibility
vVal = ['restricted', 'private', 'public']
isValidVisib = ld.isString(v) and ld.includes(vVal, v)
g.visibility = if isValidVisib then v else 'restricted'
g.password = if ld.isString p.password then p.password else null
g.readonly = if ld.isBoolean p.readonly then p.readonly else false
g
cascadePads
is an asynchronous function which handle cascade removals
after group removal. It takes :
group
objectcallback
function, returning Error or null if succeeded group.fn.cascadePads = (group, callback) ->
if not ld.isEmpty group.pads
padsKeys = ld.map group.pads, (p) -> PPREFIX + p
storage.fn.delKeys padsKeys, (err, res) ->
return callback err if err
e = 'something goes wrong with cascade pads removal'
return callback new Error e if not res
callback null
else
callback null
indexUsers
is an asynchronous function which handles secondary indexes
for users.groups after group creation, update, removal. It takes :
del
boolean to know if we have to delete key from index or add itgroup
objectcallback
function, returning Error or null if succeeded group.fn.indexUsers = (del, group, callback) ->
usersKeys = ld.map ld.union(group.admins, group.users), (u) ->
UPREFIX + u
storage.fn.getKeys usersKeys, (err, users) ->
return callback err if err
ld.forIn users, (u, k) ->
if del
ld.pull u.groups, group._id
else
if not ld.includes u.groups, group._id
u.groups.push group._id
users[k] = u
storage.fn.setKeys users, (err) ->
return callback err if err
callback null
set
is internal function that set the user group into the database.
It takes care of secondary indexes for users and pads by calling
indexUsers
.
It takes, as arguments :
g
group objectcallback
function returning an Error or null and the g
object. group.fn.set = (g, callback) ->
storage.db.set GPREFIX + g._id, g, (err) ->
return callback err if err
group.fn.indexUsers false, g, (err) ->
return callback err if err
callback null, g
checkSet
will ensure that all users and pads exist. If true, it calls
fn.set
, else it will return an Error. checkSet
takes :
g
group objectcallback
function returning an Error or null and the g
object. group.fn.checkSet = (g, callback) ->
pre = ld.curry (pre, val) -> pre + val
admins = ld.map g.admins, pre UPREFIX
users = ld.map g.users, pre UPREFIX
pads = ld.map g.pads, pre PPREFIX
allKeys = ld.union admins, users, pads
common.checkMultiExist allKeys, (err, res) ->
return callback err if err
if not res
e = 'Some users, admins or pads have not been found'
return callback new Error e
group.fn.set g, callback
return group