bsw@0: var api_version = '0.2.0';
bsw@0:
bsw@0: // creates a random string with the given length
bsw@0: function randomString(number_of_chars) {
bsw@0: var charset, rand, i, ret;
bsw@0: charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
bsw@0: random_string = '';
bsw@0:
bsw@0: for (var i = 0; i < number_of_chars; i++) {
bsw@0: random_string += charset[parseInt(Math.random() * charset.length)]
bsw@0: }
bsw@0: return random_string;
bsw@0: }
bsw@0:
bsw@0: var fields = require('./fields.js');
bsw@0:
bsw@0: var general_params = require('./general_params.js');
bsw@0:
bsw@0: var config = general_params.config;
bsw@0: exports.config = config;
bsw@0:
bsw@0: var db = require('./db.js');
bsw@0: exports.db = db;
bsw@0:
bsw@0: var selector = db.selector;
bsw@0:
bsw@0: var email = require('mailer');
bsw@0:
bsw@0:
bsw@0: // check if current session has at least given access level, returns error to client if not.
bsw@0: // used by request handlers below
bsw@0: function requireAccessLevel(conn, req, res, access_level, callback) {
bsw@0: switch (access_level) {
bsw@0: case 'anonymous':
bsw@0: if (req.current_access_level == 'anonymous') { callback(); return; };
bsw@0: case 'pseudonym':
bsw@0: if (req.current_access_level == 'pseudonym') { callback(); return; };
bsw@0: case 'full':
bsw@0: if (req.current_access_level == 'full') { callback(); return; };
bsw@0: case 'member':
bsw@0: if (req.current_member_id) { callback(); return; };
bsw@0: default:
bsw@0: respond('json', conn, req, res, 'forbidden', { error: 'Access denied' });
bsw@0: }
bsw@0: };
bsw@0:
bsw@0: // callback function, encoding result and sending it to the client
bsw@0: function respond(mode, conn, req, res, status, object, err) {
bsw@0: var http_status = 500;
bsw@0: var command;
bsw@0:
bsw@0: if (status == 'ok') {
bsw@0: command = 'COMMIT';
bsw@0: } else {
bsw@0: command = 'ROLLBACK';
bsw@0: };
bsw@0:
bsw@0: switch (status) {
bsw@0: case 'ok':
bsw@0: http_status = 200;
bsw@0: break;
bsw@0: case 'forbidden':
bsw@0: //http_status = 403;
bsw@0: break;
bsw@0: case 'notfound':
bsw@0: http_status = 404;
bsw@0: break;
bsw@0: case 'unprocessable':
bsw@0: //http_status = 422;
bsw@0: break;
bsw@0: case 'conflict':
bsw@0: //http_status = 409;
bsw@0: break;
bsw@0: };
bsw@0:
bsw@0: var query;
bsw@0: if (mode == 'json' && ! err) query = 'SELECT null';
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: db.query(conn, req, res, command, function (result, conn) {
bsw@0:
bsw@0: if (conn && typeof(conn) != 'string') conn.drain();
bsw@0:
bsw@0: if (mode == 'json') {
bsw@0: if (! object) object = {};
bsw@0: } else if (mode == 'html') {
bsw@0: if (! object) object = 'no content';
bsw@0: if (err) object = "Error: " + err;
bsw@0: }
bsw@0:
bsw@0: object.status = status;
bsw@0: object.error = err;
bsw@0:
bsw@0: if (mode == 'json') {
bsw@0: var body = JSON.stringify(object);
bsw@0: var content_type = 'application/json';
bsw@0: if (req.params && req.params.callback) {
bsw@0: body = req.params.callback + '(' + body + ')';
bsw@0: content_type = 'text/javascript';
bsw@0: }
bsw@0: res.writeHead(
bsw@0: http_status,
bsw@0: {
bsw@0: 'Content-Type': content_type,
bsw@0: //'Content-Length': body.length
bsw@0: }
bsw@0: );
bsw@0: res.end(body);
bsw@0: } else if (mode == 'html') {
bsw@0: var body = ['
lfapi']
bsw@0: body.push(object)
bsw@0: body.push('')
bsw@0: body = body.join('');
bsw@0: res.writeHead(
bsw@0: http_status,
bsw@0: {
bsw@0: 'Content-Type': 'text/html',
bsw@0: 'Content-Length': body.length
bsw@0: }
bsw@0: );
bsw@0: res.end(body);
bsw@0: }
bsw@0: })
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: exports.respond = respond;
bsw@0: db.error_handler = respond;
bsw@0:
bsw@0: // add requested related data for requests with include_* parameters
bsw@0: function addRelatedData(conn, req, res, result, includes) {
bsw@0: if (includes.length > 0) {
bsw@0: var include = includes.shift();
bsw@0: var class = include.class;
bsw@0: var objects = result[include.objects];
bsw@0:
bsw@0: var query;
bsw@0:
bsw@0: if (objects) {
bsw@0: var objects_exists = false;
bsw@0: query = new selector.Selector();
bsw@0: var ids_hash = {};
bsw@0: if (typeof(objects) == 'array') {
bsw@0: if (objects.length > 0) {
bsw@0: objects_exists = true;
bsw@0: objects.forEach( function(object) {
bsw@0: if (object[class + "_id"]) {
bsw@0: ids_hash[object[class + "_id"]] = true;
bsw@0: };
bsw@0: });
bsw@0: }
bsw@0: } else {
bsw@0: for (var key in objects) {
bsw@0: objects_exists = true;
bsw@0: var object = objects[key];
bsw@0: if (object[class + "_id"]) {
bsw@0: ids_hash[object[class + "_id"]] = true;
bsw@0: };
bsw@0: };
bsw@0: };
bsw@0:
bsw@0: if (objects_exists) {
bsw@0: var ids = [];
bsw@0: for (key in ids_hash) {
bsw@0: ids.push(key)
bsw@0: }
bsw@0:
bsw@0: query.from(class);
bsw@0: query.addWhere([class + '.id IN (??)', ids]);
bsw@0: fields.addObjectFields(query, class);
bsw@0: };
bsw@0: };
bsw@0:
bsw@0: db.query(conn, req, res, query, function (result2, conn) {
bsw@0: // add result to main result, regarding correct pluralization
bsw@0: var tmp = {};
bsw@0: if (result2) {
bsw@0: result2.rows.forEach( function(row) {
bsw@0: tmp[row.id] = row;
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: if (class == 'policy') {
bsw@0: result['policies'] = tmp;
bsw@0: } else {
bsw@0: result[class + 's'] = tmp;
bsw@0: }
bsw@0: addRelatedData(conn, req, res, result, includes);
bsw@0: });
bsw@0: } else {
bsw@0: respond('json', conn, req, res, 'ok', result);
bsw@0: };
bsw@0:
bsw@0: };
bsw@0:
bsw@0: function lockMemberById(conn, req, res, member_id, callback) {
bsw@0: var query = new selector.Selector('member');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['member.id = ?', member_id]);
bsw@0: query.forUpdate();
bsw@0: db.query(conn, req, res, query, callback);
bsw@0: };
bsw@0:
bsw@0: function requireUnitPrivilege(conn, req, res, unit_id, callback) {
bsw@0: var query = new selector.Selector('privilege');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['privilege.member_id = ?', req.current_member_id]);
bsw@0: query.addWhere(['privilege.unit_id = ?', unit_id ]);
bsw@0: query.addWhere('privilege.voting_right');
bsw@0: query.forShareOf('privilege');
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'You have no voting right for this unit.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: function requireAreaPrivilege(conn, req, res, area_id, callback) {
bsw@0: var query = new selector.Selector('privilege');
bsw@0: query.join('area', null, 'area.unit_id = privilege.unit_id');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['privilege.member_id = ?', req.current_member_id]);
bsw@0: query.addWhere(['area.id = ?', area_id ]);
bsw@0: query.addWhere('privilege.voting_right');
bsw@0: query.forShareOf('privilege');
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'You have no voting right for areas in this unit.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: function requireIssuePrivilege(conn, req, res, issue_id, callback) {
bsw@0: var query = new selector.Selector('privilege');
bsw@0: query.join('area', null, 'area.unit_id = privilege.unit_id');
bsw@0: query.join('issue', null, 'issue.area_id = area.id');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['privilege.member_id = ?', req.current_member_id]);
bsw@0: query.addWhere(['issue.id = ?', issue_id ]);
bsw@0: query.addWhere('privilege.voting_right');
bsw@0: query.forShareOf('privilege');
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'You have no voting right for issues in this unit.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: function requireInitiativePrivilege(conn, req, res, initiative_id, callback) {
bsw@0: var query = new selector.Selector('privilege');
bsw@0: query.join('area', null, 'area.unit_id = privilege.unit_id');
bsw@0: query.join('issue', null, 'issue.area_id = area.id');
bsw@0: query.join('initiative', null, 'initiative.issue_id = issue.id');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['privilege.member_id = ?', req.current_member_id]);
bsw@0: query.addWhere(['initiative.id = ?', initiative_id ]);
bsw@0: query.addWhere('privilege.voting_right');
bsw@0: query.forShareOf('privilege');
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'You have no voting right for initiatives in this unit.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: function requireIssueState(conn, req, res, issue_id, required_states, callback) {
bsw@0: var query = new selector.Selector('issue');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['issue.id = ?', issue_id]);
bsw@0: query.addWhere(['issue.state IN (??)', required_states]);
bsw@0: query.forUpdateOf('issue');
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'Issue is in wrong state.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: };
bsw@0:
bsw@0: function requireIssueStateForInitiative(conn, req, res, initiative_id, required_states, callback) {
bsw@0: var query = new selector.Selector('issue');
bsw@0: query.join('initiative', null, 'initiative.issue_id = issue.id');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['initiative.id = ?', initiative_id]);
bsw@0: query.addWhere(['issue.state IN (??)', required_states]);
bsw@0: query.forUpdateOf('issue');
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'Issue is in wrong state.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: }
bsw@0:
bsw@0: function requireContingentLeft(conn, req, res, is_initiative, callback) {
bsw@0: var query = new selector.Selector('member_contingent_left');
bsw@0: query.addField('NULL');
bsw@0: query.addWhere(['member_contingent_left.member_id = ?', req.current_member_id]);
bsw@0: query.addWhere('member_contingent_left.text_entries_left >= 1');
bsw@0: if (is_initiative) {
bsw@0: query.addWhere('member_contingent_left.initiatives_left >= 1');
bsw@0: }
bsw@0: db.query(conn, req, res, query, function(result, conn) {
bsw@0: if (result.rows.length != 1) {
bsw@0: respond('json', conn, req, res, 'forbidden', null, 'Contingent empty.');
bsw@0: return;
bsw@0: }
bsw@0: callback();
bsw@0: });
bsw@0: }
bsw@0:
bsw@0: // ==========================================================================
bsw@0: // GETT methods
bsw@0: // ==========================================================================
bsw@0:
bsw@0:
bsw@0: exports.get = {
bsw@0:
bsw@0: // startpage (html) for users
bsw@0: // currently used for implementing public alpha test
bsw@0: '/': function (conn, req, res, params) {
bsw@0:
bsw@0: var html = [];
bsw@0: html.push('welcome to lfapi public developer alpha test
');
bsw@0: html.push('This service is provided for testing purposes and is dedicated to developers interested in creating applications based on LiquidFeedback.
');
bsw@0: html.push('how to use
');
bsw@0: html.push('The programming interface is described in the LiquidFeedback API specification.
')
bsw@0: html.push('The current implementation status of lfapi is published at the LiquidFeedback API server page in our Wiki.
');
bsw@0: html.push('Neither the API specification nor the implementation of lfapi is finished yet. This public test should enable developers to join the specification process of the programming interface and makes it possible to start creating applications.
');
bsw@0: html.push('questions and suggestions
');
bsw@0: html.push('Please use our public mailing list if you have any questions or suggestions.
');
bsw@0: html.push('developer registration
');
bsw@0: html.push('To register as developer and receive an account, please submit the following form. You\'ll receive an email with instructions to complete the registration process by verifying your email address.
');
bsw@0: html.push('