# HG changeset patch # User bsw # Date 1337428730 -7200 # Node ID 25aba6a34c4467e2daedc1a09bb1d60eabd728d0 # Parent be8ca05d0315794a1cfdc4de6bb52d9c2b1b3a65 Added missing related objects, fixed smaller bugs, diff -r be8ca05d0315 -r 25aba6a34c44 config.js --- a/config.js Sat Feb 25 21:56:55 2012 +0100 +++ b/config.js Sat May 19 13:58:50 2012 +0200 @@ -16,15 +16,15 @@ exports.public_access_level = 'full'; // connection string to access the LiquidFeedback Core database -exports.connectionString = 'pg://localhost/liquid_feedback'; +exports.connectionString = 'pg://localhost/liquid_feedback2'; // public base url (including trailing slash) -exports.public_url_path = 'http://lf.example.org/api/'; +exports.public_url_path = 'http://apitest.liquidfeedback.org:25520/'; // mail server, email sender and subject settings exports.mail = { - from: 'Sender name ', - subject_prefix: '[email subject prefix] ' + from: 'LiquidFeedback Maintainers ', + subject_prefix: '[lfapi alpha] ' }; exports.settings = { diff -r be8ca05d0315 -r 25aba6a34c44 lfapi/fields.js --- a/lfapi/fields.js Sat Feb 25 21:56:55 2012 +0100 +++ b/lfapi/fields.js Sat May 19 13:58:50 2012 +0200 @@ -2,7 +2,7 @@ // fields of main data structures // -------------------------------------------------------------------------- -exports.member = ['id', 'name', 'identification', 'organizational_unit', 'internal_posts', 'realname', 'birthday', 'address', 'email', 'xmpp_address', 'website', 'phone', 'mobile_phone', 'profession', 'external_memberships', 'external_posts', 'statement', 'active', 'locked', 'created', 'last_activity']; +exports.member = ['id', 'name', 'organizational_unit', 'internal_posts', 'realname', 'birthday', 'address', 'email', 'xmpp_address', 'website', 'phone', 'mobile_phone', 'profession', 'external_memberships', 'external_posts', 'statement', 'active', 'locked', 'created', 'last_activity']; exports.member_pseudonym = ['id', 'name']; exports.policy = ['id', 'index', 'active', 'name', 'description', 'admission_time', 'discussion_time', 'verification_time', 'voting_time', 'issue_quorum_num', 'issue_quorum_den', 'initiative_quorum_num', 'initiative_quorum_den', 'direct_majority_num', 'direct_majority_den', 'direct_majority_strict', 'direct_majority_positive', 'direct_majority_non_negative', 'indirect_majority_num', 'indirect_majority_den', 'indirect_majority_strict', 'indirect_majority_positive', 'indirect_majority_non_negative', 'no_reverse_beat_path', 'no_multistage_majority']; exports.unit = ['id', 'parent_id', 'active', 'name', 'description', 'member_count']; diff -r be8ca05d0315 -r 25aba6a34c44 lfapi/main.js --- a/lfapi/main.js Sat Feb 25 21:56:55 2012 +0100 +++ b/lfapi/main.js Sat May 19 13:58:50 2012 +0200 @@ -99,6 +99,7 @@ http_status, { 'Content-Type': content_type, + 'Access-Control-Allow-Origin': '*' //'Content-Length': body.length // TODO doesn't work in chrome with JSONP } ); @@ -331,13 +332,15 @@ var html = []; html.push('

welcome to lfapi public developer alpha test

'); html.push('

This service is provided for testing purposes and is dedicated to developers interested in creating applications based on LiquidFeedback.

'); + html.push('

developer registration

'); + html.push('

To register as developer and receive an account, please send an email to beta20120312@public-software-group.org and you\'ll receive an invitation to the testing system. Read access is available without registering an account.

'); html.push('

how to use

'); html.push('

The programming interface is described in the LiquidFeedback API specification.

') html.push('

The current implementation status of lfapi is published at the LiquidFeedback API server page in our Wiki.

'); 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.

'); html.push('

questions and suggestions

'); html.push('

Please use our public mailing list if you have any questions or suggestions.

'); - html.push('

developer registration

'); +/* 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.
'); html.push('

'); html.push('     '); @@ -353,6 +356,7 @@ html.push(''); html.push('
'); html.push(''); +*/ respond('html', null, req, res, 'ok', html.join('')); }, @@ -483,7 +487,7 @@ db.query(conn, req, res, query, function (member_history_result, conn) { var result = { result: member_history_result.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); addRelatedData(conn, req, res, result, includes); }); }); @@ -493,8 +497,17 @@ requireAccessLevel(conn, req, res, 'full', function() { var query = new selector.Selector(); query.from('"member_image" JOIN "member" ON "member"."id" = "member_image"."member_id"'); - query.addField('"member_image".*'); + query.addField('"member_image"."member_id"'); + query.addField('"member_image"."image_type"'); + query.addField('"member_image"."scaled"'); + query.addField('"member_image"."content_type"'); + query.addField('encode("member_image"."data", \'base64\')', 'data'); query.addWhere('member_image.scaled'); + if (params.type == "avatar") { + query.addWhere('member_image.image_type = \'avatar\''); + } else if (params.type == "photo") { + query.addWhere('member_image.image_type = \'photo\''); + } general_params.addMemberOptions(req, query, params); query.addOrderBy = ['member_image.member_id, member_image.image_type']; db.query(conn, req, res, query, function (result, conn) { @@ -536,8 +549,8 @@ db.query(conn, req, res, query, function (privilege_result, conn) { var result = { result: privilege_result.rows } includes = []; - if (params.include_units) includes.push({ class: 'unit', objects: 'result'}); - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'result'}); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); addRelatedData(conn, req, res, result, includes); }); }); @@ -582,7 +595,7 @@ db.query(conn, req, res, query, function (area_result, conn) { var result = { result: area_result.rows } includes = []; - if (params.include_units) includes.push({ class: 'unit', objects: 'result'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'result'}); addRelatedData(conn, req, res, result, includes); }); }); @@ -601,9 +614,9 @@ db.query(conn, req, res, query, function (allowed_policy_result, conn) { var result = { result: allowed_policy_result.rows } includes = []; - if (params.include_policies) includes.push({ class: 'policy', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'result'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'result'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); addRelatedData(conn, req, res, result, includes); }); }); }, @@ -620,9 +633,9 @@ db.query(conn, req, res, query, function (membership_result, conn) { var result = { result: membership_result.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'result'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'result'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); addRelatedData(conn, req, res, result, includes); }); }); @@ -639,9 +652,9 @@ db.query(conn, req, res, query, function (issue_result, conn) { var result = { result: issue_result.rows } includes = []; - if (params.include_areas) includes.push({ class: 'area', objects: 'result'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'result' }); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'result'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'result' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -690,11 +703,11 @@ db.query(conn, req, res, query, function (population_result, conn) { var result = { result: population_result.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'areas'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'areas'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -752,11 +765,11 @@ db.query(conn, req, res, query, function (interest_result, conn) { var result = { result: interest_result.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -774,11 +787,11 @@ db.query(conn, req, res, query, function (issue_comment_result, conn) { var result = { result: issue_comment_result.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -795,10 +808,10 @@ db.query(conn, req, res, query, function (initiative_result, conn) { var result = { result: initiative_result.rows } includes = []; - if (params.include_issues) includes.push({ class: 'issue', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -820,12 +833,12 @@ db.query(conn, req, res, query, function (initiator, conn) { var result = { result: initiator.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'initiatives'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'initiatives'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -892,12 +905,12 @@ db.query(conn, req, res, query, function (supporter, conn) { var result = { result: supporter.rows } includes = []; - if (params.include_members) includes.push({ class: 'member', objects: 'result'}); - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'initiatives'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_members) includes.push({ clazz: 'member', objects: 'result'}); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'initiatives'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -914,11 +927,11 @@ db.query(conn, req, res, query, function (result, conn) { var result = { result: result.rows } includes = []; - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'initiatives'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'initiatives'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -926,14 +939,14 @@ '/draft': function (conn, req, res, params) { requireAccessLevel(conn, req, res, 'anonymous', function() { - var fields = ['draft.initiative_id', 'draft.id', 'draft.formatting_engine', 'draft.content', 'draft.author_id']; + var fields = ['draft.initiative_id', 'draft.id', 'draft.formatting_engine', 'draft.created']; var query = new selector.Selector(); query.from('draft JOIN initiative ON initiative.id = draft.initiative_id JOIN issue ON issue.id = initiative.issue_id JOIN policy ON policy.id = issue.policy_id JOIN area ON area.id = issue.area_id JOIN unit ON area.unit_id = unit.id'); fields.forEach( function(field) { query.addField(field, null, ['grouped']); }); if (req.current_access_level != 'anonymous' || req.current_member_id) { - query.addField('draft.author_id'); + query.addField('draft.author_id', null, ['grouped']); } if (params.draft_id) { query.addWhere('draft.id = ?', params.draft_id); @@ -941,17 +954,23 @@ if (params.current_draft) { query.join('current_draft', null, 'current_draft.initiative_id = initiative.id AND current_draft.id = draft.id') } + if (params.render_content == "html") { + query.join('rendered_draft', null, 'rendered_draft.draft_id = draft.id AND rendered_draft.format = \'html\''); + query.addField('rendered_draft.content', null, ['grouped']); + } else { + query.addField('draft.content', null, ['grouped']); + } general_params.addInitiativeOptions(req, query, params); query.addOrderBy('draft.initiative_id, draft.id'); general_params.addLimitAndOffset(query, params); db.query(conn, req, res, query, function (result, conn) { var result = { result: result.rows } includes = []; - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'initiatives'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'initiatives'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -972,11 +991,11 @@ db.query(conn, req, res, query, function (result, conn) { var result = { result: result.rows } includes = []; - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'initiatives'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'initiatives'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -997,12 +1016,12 @@ db.query(conn, req, res, query, function (result, conn) { var result = { result: result.rows } includes = []; - if (params.include_suggestions) includes.push({ class: 'suggestion', objects: 'result'}); - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'suggestions'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'initiatives'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_suggestions) includes.push({ clazz: 'suggestion', objects: 'result'}); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'suggestions'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'initiatives'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -1043,12 +1062,42 @@ }); }, + '/voter': function (conn, req, res, params) { + requireAccessLevel(conn, req, res, 'pseudonym', function() { + var query = new selector.Selector(); + query.from('direct_voter JOIN member ON member.id = direct_voter.member_id JOIN issue ON issue.id = direct_voter.issue_id JOIN policy ON policy.id = issue.policy_id JOIN area ON area.id = issue.area_id JOIN unit ON area.unit_id = unit.id'); + query.addField('direct_voter.*'); + query.addWhere('issue.closed NOTNULL'); + general_params.addMemberOptions(req, query, params); + general_params.addIssueOptions(req, query, params); + general_params.addLimitAndOffset(query, params); + db.query(conn, req, res, query, function (result, conn) { + respond('json', conn, req, res, 'ok', { result: result.rows }); + }); + }); + }, + + '/delegating_voter': function (conn, req, res, params) { + requireAccessLevel(conn, req, res, 'pseudonym', function() { + var query = new selector.Selector(); + query.from('delegating_voter JOIN member ON member.id = delegating_voter.member_id JOIN issue ON issue.id = delegating_voter.issue_id JOIN policy ON policy.id = issue.policy_id JOIN area ON area.id = issue.area_id JOIN unit ON area.unit_id = unit.id'); + query.addField('delegating_voter.*'); + query.addWhere('issue.closed NOTNULL'); + general_params.addMemberOptions(req, query, params); + general_params.addIssueOptions(req, query, params); + general_params.addLimitAndOffset(query, params); + db.query(conn, req, res, query, function (result, conn) { + respond('json', conn, req, res, 'ok', { result: result.rows }); + }); + }); + }, + '/vote': function (conn, req, res, params) { requireAccessLevel(conn, req, res, 'pseudonym', function() { var query = new selector.Selector(); query.from('vote JOIN member ON member.id = vote.member_id JOIN initiative ON initiative.id = vote.initiative_id JOIN issue ON issue.id = initiative.issue_id JOIN policy ON policy.id = issue.policy_id JOIN area ON area.id = issue.area_id JOIN unit ON area.unit_id = unit.id'); query.addField('vote.*'); - query.addWhere('issue.closed_at NOTNULL'); + query.addWhere('issue.closed NOTNULL'); general_params.addMemberOptions(req, query, params); general_params.addInitiativeOptions(req, query, params); general_params.addLimitAndOffset(query, params); @@ -1068,16 +1117,16 @@ }); general_params.addMemberOptions(req, query, params); general_params.addInitiativeOptions(req, query, params); - query.addOrderBy('event.id'); + query.addOrderBy('event.id DESC'); general_params.addLimitAndOffset(query, params); db.query(conn, req, res, query, function (events, conn) { var result = { result: events.rows } includes = []; - if (params.include_initiatives) includes.push({ class: 'initiative', objects: 'result'}); - if (params.include_issues) includes.push({ class: 'issue', objects: 'result'}); - if (params.include_areas) includes.push({ class: 'area', objects: 'issues'}); - if (params.include_units) includes.push({ class: 'unit', objects: 'areas'}); - if (params.include_policies) includes.push({ class: 'policy', objects: 'issues' }); + if (params.include_initiatives) includes.push({ clazz: 'initiative', objects: 'result'}); + if (params.include_issues) includes.push({ clazz: 'issue', objects: 'result'}); + if (params.include_areas) includes.push({ clazz: 'area', objects: 'issues'}); + if (params.include_units) includes.push({ clazz: 'unit', objects: 'areas'}); + if (params.include_policies) includes.push({ clazz: 'policy', objects: 'issues' }); addRelatedData(conn, req, res, result, includes); }); }); @@ -1198,7 +1247,6 @@ \n\ Account ID: " + member_id + "\n\ Login: " + member_login + "\n\ -Password: " + member_password + "\n\ \n\ \n\ To make you able to actually access the API interface, we added the following\n\