note description: "Provide an interface to issue data." author: "Peter Wyss " date: "$Date$" revision: "$Revision$" class ISSUE_INTERFACE inherit BASE_INTERFACE O_WORKITEM_CONSTANTS A_CONSTANTS create make feature -- Commands issue_add (a_msg: A_MESSAGE) -- Add a new issue and issue workitem according to a_msg. require a_msg_ok: a_msg /= Void local l_pc_msg: O_ISSUE_ADD_MESSAGE l_workitem_add_reply: O_ISSUE_ADD_STORAGE_REPLY_MESSAGE l_issue: ISSUE l_issue_revision: ISSUE_REVISION l_status: A_GENERAL_STATUS_MESSAGE l_user_ret: USER l_project_ret: PROJECT l_issue_id: INTEGER l_issue_revision_id: INTEGER l_tags: LIST [STRING] l_tag: STRING l_tag_id: INTEGER l_timestamp: INTEGER l_tags_cleaned: DS_ARRAYED_LIST [STRING] l_issue_workitem: WI_FULL_ISSUE do l_pc_msg ?= a_msg check issue_add_message: l_pc_msg /= Void end user_access.retrieve_user_by_session (l_pc_msg.session) if user_access.is_found then l_user_ret := user_access.last_user project_access.retrieve_project_by_id (l_pc_msg.project) if project_access.is_found then l_project_ret := project_access.last_project l_timestamp := (create {DATE_TIME}.make_now_utc).definite_duration (create {DATE_TIME}.make (1970, 1, 1, 0, 0, 0)).seconds_count.as_integer_32 create l_issue.make l_issue.set_project_id (l_project_ret.project_id) l_issue.set_title (l_pc_msg.title) l_issue.set_project_issue_id (issue_access.next_project_issue_id (l_project_ret.project_id)) l_issue.set_private (l_pc_msg.is_private.value.to_integer) l_issue.set_last_modified (l_timestamp) l_issue.set_deadline (l_pc_msg.deadline) l_issue.set_work_amount (l_pc_msg.work_amount) issue_access.insert_issue (l_issue) l_issue_id := issue_access.last_insert_id -- create main revision (holds description) create l_issue_revision.make l_issue_revision.set_creation_time (l_timestamp) l_issue_revision.set_issue_id (l_issue_id) l_issue_revision.set_text (l_pc_msg.description) l_issue_revision.set_user_id (l_user_ret.user_id) l_issue_revision.set_text_type (1) issue_access.insert_issue_revision (l_issue_revision) l_issue_revision_id := issue_access.last_insert_id -- insert tags l_tags := l_pc_msg.tags.value.split (',') from l_tags.start create l_tags_cleaned.make (l_tags.count) until l_tags.after loop l_tag := l_tags.item l_tag.right_adjust l_tag.left_adjust if l_tag.count > 0 then l_tag_id := issue_access.retrieve_tag_id (l_tag) if l_tag_id = 0 then issue_access.insert_tag_text (l_tag) l_tag_id := issue_access.retrieve_tag_id (l_tag) end if l_tag_id > 0 then issue_access.insert_issue_tag_association (l_issue_revision_id, l_tag_id) end -- Add to "clean" list l_tags_cleaned.force_first (l_tag.twin) end -- Increment l_tags.forth end -- Create workitem create l_issue_workitem.make ( l_project_ret.project_id, l_project_ret.name, l_user_ret.user_id, l_user_ret.name, get_project_link (l_project_ret.name) + "/issues/" + l_issue.project_issue_id.out, l_pc_msg.title, True, l_pc_msg.is_private, l_issue_revision_id, l_tags_cleaned, l_issue.project_issue_id, l_pc_msg.description, -- no attachments when freshly adding... create {DS_ARRAYED_LIST [TUPLE [INTEGER_32, STRING_8, STRING_8]]}.make(0), l_pc_msg.deadline, l_pc_msg.work_amount) workitem_access.store_wi (l_issue_workitem) -- send reply message create l_workitem_add_reply.make ( workitem_access.last_insert_id, l_user_ret.name, l_project_ret.project_id, l_project_ret.name, l_issue.project_issue_id) node.send_message_reply (l_workitem_add_reply, a_msg) else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_update (a_msg: A_MESSAGE) -- Update a issue and create issue workitem according to `a_msg'. require a_msg_ok: a_msg /= Void local l_pc_msg: O_ISSUE_UPDATE_MESSAGE l_workitem_add_reply: O_ISSUE_UPDATE_STORAGE_REPLY_MESSAGE l_last_rev_id: INTEGER l_status: A_GENERAL_STATUS_MESSAGE l_user_ret: USER l_project_ret: PROJECT l_issue_id: INTEGER l_tags: LIST [STRING] l_tag: STRING l_tag_id: INTEGER l_tags_cleaned: DS_ARRAYED_LIST [STRING] l_issue_workitem: WI_FULL_ISSUE l_issue_revision: ISSUE_REVISION l_timestamp: INTEGER do l_pc_msg ?= a_msg check issue_update_message: l_pc_msg /= Void end user_access.retrieve_user_by_session (l_pc_msg.session) if user_access.is_found then l_user_ret := user_access.last_user project_access.retrieve_project_by_id (l_pc_msg.project) if project_access.is_found then l_project_ret := project_access.last_project if is_private_access_granted (l_user_ret.user_id, l_pc_msg.project) then l_issue_id := issue_access.retrieve_issue_id (l_pc_msg.project, l_pc_msg.project_issue_id) if l_issue_id > 0 then -- update issue with title, private-flag, deadline & work_amount issue_access.update_issue ( l_issue_id, l_pc_msg.title, l_pc_msg.is_private, l_pc_msg.deadline, l_pc_msg.work_amount) -- create main revision (holds description) create l_issue_revision.make l_timestamp := (create {DATE_TIME}.make_now_utc).definite_duration (create {DATE_TIME}.make (1970, 1, 1, 0, 0, 0)).seconds_count.as_integer_32 l_issue_revision.set_creation_time (l_timestamp) l_issue_revision.set_issue_id (l_issue_id) l_issue_revision.set_text (l_pc_msg.description) l_issue_revision.set_text_type (1) l_issue_revision.set_user_id (l_user_ret.user_id) issue_access.insert_issue_revision (l_issue_revision) l_last_rev_id := issue_access.last_insert_id -- insert tags l_tags := l_pc_msg.tags.value.split (',') from l_tags.start create l_tags_cleaned.make (l_tags.count) until l_tags.after loop l_tag := l_tags.item l_tag.right_adjust l_tag.left_adjust if l_tag.count > 0 then l_tag_id := issue_access.retrieve_tag_id (l_tag) if l_tag_id = 0 then issue_access.insert_tag_text (l_tag) -- l_tag_id := issue_access.retrieve_tag_id (l_tag) l_tag_id := issue_access.last_insert_id end if l_tag_id > 0 then issue_access.insert_issue_tag_association (l_last_rev_id, l_tag_id) end -- Add to "clean" list l_tags_cleaned.force_last (l_tag) end -- Increment l_tags.forth end -- Create workitem create l_issue_workitem.make ( l_project_ret.project_id, l_project_ret.name, l_user_ret.user_id, l_user_ret.name, get_project_link (l_project_ret.name) + "/issues/" + l_pc_msg.project_issue_id.out, l_pc_msg.title, False, -- We can only update an existing issue l_pc_msg.is_private, l_last_rev_id, l_tags_cleaned, l_pc_msg.project_issue_id, l_pc_msg.description, -- no attachments when updating... create {DS_ARRAYED_LIST [TUPLE [INTEGER_32, STRING_8, STRING_8]]}.make(0), l_pc_msg.deadline, l_pc_msg.work_amount) workitem_access.store_wi (l_issue_workitem) -- send reply message create l_workitem_add_reply.make ( workitem_access.last_insert_id, l_user_ret.name, l_pc_msg.project, l_project_ret.name, l_pc_msg.project_issue_id) node.send_message_reply (l_workitem_add_reply, a_msg) else create l_status.make (False, err_invalid_issue) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_access_denied) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_comment (a_msg: A_MESSAGE) -- Add a new issue comment and issue workitem according to a_msg. require a_msg_ok: a_msg /= Void local l_pc_msg: O_ISSUE_COMMENT_MESSAGE l_workitem_add_reply: O_ISSUE_COMMENT_REPLY_MESSAGE l_issue_revision: ISSUE_REVISION l_status: A_GENERAL_STATUS_MESSAGE l_user_ret: USER l_project_ret: PROJECT l_issue_ret: TUPLE [issue_id: INTEGER; project_issue_id: INTEGER; project_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING; is_private: BOOLEAN; deadline: INTEGER; work_amount: INTEGER; revisions: ARRAYED_LIST [TUPLE [creation_time: INTEGER; text: STRING; tags: STRING]]] l_issue_id: INTEGER l_issue_revision_id: INTEGER l_date: DATE_TIME l_tags: LIST [STRING] l_tag: STRING l_tag_id: INTEGER l_reply_tags: STRING l_tags_cleaned: DS_ARRAYED_LIST [STRING] l_issue_workitem: WI_FULL_ISSUE do l_pc_msg ?= a_msg check issue_comment_message: l_pc_msg /= Void end user_access.retrieve_user_by_session (l_pc_msg.session) if user_access.is_found then l_user_ret := user_access.last_user project_access.retrieve_project_by_id (l_pc_msg.project) if project_access.is_found then l_project_ret := project_access.last_project issue_access.retrieve_issue_by_project_issue_id (l_pc_msg.project, l_pc_msg.project_issue_id, is_private_access_granted (l_user_ret.user_id, l_pc_msg.project)) if issue_access.is_found then l_issue_ret := issue_access.last_issue l_issue_id := l_issue_ret.issue_id create l_issue_revision.make create l_date.make_now_utc l_issue_revision.set_creation_time (l_date.definite_duration (create {DATE_TIME}.make (1970, 1, 1, 0, 0, 0)).seconds_count.as_integer_32) l_issue_revision.set_issue_id (l_issue_id) l_issue_revision.set_text (l_pc_msg.description) l_issue_revision.set_text_type (2) l_issue_revision.set_user_id (l_user_ret.user_id) issue_access.insert_issue_revision (l_issue_revision) issue_access.update_issue_data (l_issue_id, l_pc_msg.deadline, l_pc_msg.work_amount) -- Update last_modified timestamp issue_access.update_last_modified_timestamp (l_issue_id, l_date) l_issue_revision_id := issue_access.last_insert_id if (l_pc_msg.is_extended.value) then -- use new tags from this revision l_tags := l_pc_msg.tags.value.split (',') else -- use existing tags from the last issue l_tags := l_issue_ret.tags.split (',') end -- insert tags from l_tags.start create l_tags_cleaned.make (l_tags.count) until l_tags.after loop l_tag := l_tags.item l_tag.right_adjust l_tag.left_adjust if l_tag.count > 0 then l_tag_id := issue_access.retrieve_tag_id (l_tag) if l_tag_id = 0 then issue_access.insert_tag_text (l_tag) l_tag_id := issue_access.retrieve_tag_id (l_tag) end if l_tag_id > 0 then issue_access.insert_issue_tag_association (l_issue_revision_id, l_tag_id) end -- Add to "cleaned" list l_tags_cleaned.force_last (l_tag.twin) end -- Increment l_tags.forth end -- Create workitem create l_issue_workitem.make ( l_project_ret.project_id, l_project_ret.name, l_user_ret.user_id, l_user_ret.name, get_project_link (l_project_ret.name) + "/issues/" + l_issue_ret.project_issue_id.out, l_issue_ret.title, False, -- Commenting can only happen on an existing issue l_issue_ret.is_private, l_issue_revision_id, l_tags_cleaned, l_issue_ret.project_issue_id, l_pc_msg.description, -- no attachments when commenting... create {DS_ARRAYED_LIST [TUPLE [INTEGER_32, STRING_8, STRING_8]]}.make(0), l_pc_msg.deadline, l_pc_msg.work_amount) workitem_access.store_wi (l_issue_workitem) -- send reply message -- Add tags to the message because they are needed to update the frontend -- (speed optimization, prevents another call to the storage node to retrieve the issue tags) if (l_pc_msg.is_extended.value) then -- use new tags from this comment (only called from the web) l_reply_tags := l_pc_msg.tags.value else -- use existing tags from the last issue l_reply_tags := l_issue_ret.tags end create l_workitem_add_reply.make ( workitem_access.last_insert_id, l_user_ret.name, l_project_ret.project_id, l_project_ret.name, l_issue_ret.project_issue_id, l_issue_ret.title, l_reply_tags) node.send_message_reply (l_workitem_add_reply, a_msg) else create l_status.make (False, err_invalid_issue) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_delete (a_msg: A_MESSAGE) -- Delete an issue require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_DELETE_MESSAGE l_issue_id: INTEGER l_project_name: STRING l_reply: O_ISSUE_DELETE_STORAGE_REPLY_MESSAGE l_status_reply: A_GENERAL_STATUS_MESSAGE do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project_id) if project_access.is_found then -- Retrieve issue ID l_issue_id := issue_access.retrieve_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_issue_id > 0 then issue_access.delete_issue (l_issue_id) l_project_name := project_access.last_project.name create l_reply.make (True, "dummy", l_project_name) node.send_message_reply (l_reply, a_msg) else create l_status_reply.make (False, err_invalid_issue) node.send_message_reply (l_status_reply, a_msg) end else create l_status_reply.make (False, err_invalid_project) node.send_message_reply (l_status_reply, a_msg) end else create l_status_reply.make (False, err_session_timeout) node.send_message_reply (l_status_reply, a_msg) end end issue_retrieve (a_msg: A_MESSAGE) -- Retrieve issue. require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_RETRIEVE_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_reply: O_ISSUE_RETRIEVE_REPLY_MESSAGE l_issue_t: TUPLE [ issue_id: INTEGER; project_issue_id: INTEGER; project_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; tags: STRING; is_private: BOOLEAN; deadline: INTEGER; work_amount: INTEGER; revisions: ARRAYED_LIST [ TUPLE [ creation_time: INTEGER; user: STRING; text: STRING; text_type: INTEGER; tags: STRING ] ] ] l_revision_list: DS_ARRAYED_LIST [O_ISSUE_REVISION_MESSAGE] l_user: USER do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then l_user := user_access.last_user project_access.retrieve_project_by_id (l_msg.project) if project_access.is_found then -- retrieve issue issue_access.retrieve_issue_by_project_issue_id (l_msg.project, l_msg.issue_id, is_private_access_granted (l_user.user_id, l_msg.project)) if issue_access.is_found then l_issue_t := issue_access.last_issue create l_revision_list.make (l_issue_t.revisions.count) from l_issue_t.revisions.start until l_issue_t.revisions.after loop l_revision_list.force_last (create {O_ISSUE_REVISION_MESSAGE}.make ( l_issue_t.revisions.item_for_iteration.creation_time, l_issue_t.revisions.item_for_iteration.user, l_issue_t.revisions.item_for_iteration.text, l_issue_t.revisions.item_for_iteration.text_type, l_issue_t.revisions.item_for_iteration.tags) ) -- Increment l_issue_t.revisions.forth end create l_reply.make ( l_issue_t.creation_time, l_issue_t.last_modified, l_issue_t.user, l_issue_t.title, l_issue_t.tags, l_issue_t.is_private, l_issue_t.deadline, l_issue_t.work_amount, create {A_SEQUENCE_VALUE [O_ISSUE_REVISION_MESSAGE]}.make (l_revision_list)) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_issue) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_list (a_msg: A_MESSAGE) -- List issues. require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_LIST_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_reply: O_ISSUE_LIST_REPLY_MESSAGE l_issues: DS_HASH_TABLE [TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; deadline: INTEGER; work_amount: INTEGER; tags: DS_ARRAYED_LIST [STRING]], INTEGER] l_issue_map: DS_HASH_TABLE [O_ISSUE_ISSUE_MESSAGE, A_STRING_VALUE] do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project) if project_access.is_found then -- retrieve issues l_issues := issue_access.retrieve_project_issues (l_msg.project, is_private_access_granted (user_access.last_user.user_id, l_msg.project), 0) create l_issue_map.make (l_issues.count) from l_issues.start until l_issues.after loop l_issue_map.force ( create {O_ISSUE_ISSUE_MESSAGE}.make ( l_issues.item_for_iteration.issue_id, l_issues.item_for_iteration.creation_time, l_issues.item_for_iteration.last_modified, l_issues.item_for_iteration.user, l_issues.item_for_iteration.title, l_issues.item_for_iteration.deadline, l_issues.item_for_iteration.work_amount, tag_list_to_string(l_issues.item_for_iteration.tags)), l_issues.key_for_iteration.out ) -- Increment l_issues.forth end create l_reply.make(create {A_MAP_VALUE[O_ISSUE_ISSUE_MESSAGE]}.make (l_issue_map)) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_list_changed (a_msg: A_MESSAGE) -- List changed issues since a given date. require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_LIST_CHANGED_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_reply: O_ISSUE_LIST_REPLY_MESSAGE l_issues: DS_HASH_TABLE [TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; deadline: INTEGER; work_amount: INTEGER; tags: DS_ARRAYED_LIST [STRING]], INTEGER] l_issues_map: DS_HASH_TABLE[O_ISSUE_ISSUE_MESSAGE, A_STRING_VALUE] l_issues_item: O_ISSUE_ISSUE_MESSAGE do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project) if project_access.is_found then -- retrieve changed issues l_issues := issue_access.retrieve_changed_issues (l_msg.project, l_msg.date, is_private_access_granted (user_access.last_user.user_id, l_msg.project)) create l_issues_map.make (l_issues.count) from l_issues.start until l_issues.after loop create l_issues_item.make ( l_issues.item_for_iteration.issue_id, l_issues.item_for_iteration.creation_time, l_issues.item_for_iteration.last_modified, l_issues.item_for_iteration.user, l_issues.item_for_iteration.title, l_issues.item_for_iteration.deadline, l_issues.item_for_iteration.work_amount, tag_list_to_string (l_issues.item_for_iteration.tags)) l_issues_map.force (l_issues_item, l_issues.key_for_iteration.out) -- Increment l_issues.forth end create l_reply.make (create {A_MAP_VALUE [O_ISSUE_ISSUE_MESSAGE]}.make (l_issues_map)) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_list_tags (a_msg: A_MESSAGE) -- List tags of a project require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_LIST_TAGS_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_reply: O_ISSUE_LIST_TAGS_REPLY_MESSAGE l_tags: ARRAY[STRING] i: INTEGER l_tags_list: DS_ARRAYED_LIST[A_STRING_VALUE] do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project) if project_access.is_found then -- retrieve tags l_tags := issue_access.retrieve_project_tags(l_msg.project, is_private_access_granted (user_access.last_user.user_id, l_msg.project)) create l_tags_list.make (l_tags.count) from i := l_tags.lower until i > l_tags.upper loop l_tags_list.force_last (l_tags.item (i)) -- Increment i := i + 1 end create l_reply.make(create {A_SEQUENCE_VALUE[A_STRING_VALUE]}.make (l_tags_list)) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_search (a_msg: A_MESSAGE) -- Search issues. require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_SEARCH_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_reply: O_ISSUE_LIST_REPLY_MESSAGE l_issues: DS_HASH_TABLE [TUPLE [issue_id: INTEGER; creation_time: INTEGER; last_modified: INTEGER; user: STRING; title: STRING; deadline: INTEGER; work_amount: INTEGER; tags: DS_ARRAYED_LIST [STRING]], INTEGER] l_issue_map: DS_HASH_TABLE[O_ISSUE_ISSUE_MESSAGE, A_STRING_VALUE] l_issue_item: O_ISSUE_ISSUE_MESSAGE do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project) if project_access.is_found then -- retrieve matching issues l_issues := issue_access.search_issues (l_msg.project, l_msg.search_criteria.map, is_private_access_granted (user_access.last_user.user_id, l_msg.project)) -- convert for message create l_issue_map.make (l_issues.count) from l_issues.start until l_issues.after loop create l_issue_item.make ( l_issues.item_for_iteration.issue_id, l_issues.item_for_iteration.creation_time, l_issues.item_for_iteration.last_modified, l_issues.item_for_iteration.user, l_issues.item_for_iteration.title, l_issues.item_for_iteration.deadline, l_issues.item_for_iteration.work_amount, tag_list_to_string(l_issues.item_for_iteration.tags)) l_issue_map.force (l_issue_item, l_issues.key_for_iteration.out) -- Increment l_issues.forth end create l_reply.make(create {A_MAP_VALUE[O_ISSUE_ISSUE_MESSAGE]}.make (l_issue_map)) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_retrieve_planning_data (a_msg: A_MESSAGE) -- retrieve the planning data of an issue require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_RETRIEVE_PLANNING_DATA_MESSAGE l_reply: O_ISSUE_RETRIEVE_PLANNING_DATA_REPLY_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_planning_data: TUPLE[deadline: INTEGER; work_amount: INTEGER] do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project_id) if project_access.is_found then issue_access.retrieve_planning_data (l_msg.project_id, l_msg.project_issue_id) if issue_access.is_found then l_planning_data := issue_access.last_planning_data create l_reply.make (l_planning_data.deadline, l_planning_data.work_amount) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_issue) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_add_attachment (a_msg: A_MESSAGE) -- add an attachment require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_ADD_ATTACHMENT_MESSAGE l_issue_attachment: ISSUE_ATTACHMENT l_reply: O_ISSUE_ADD_ATTACHMENT_STORAGE_REPLY_MESSAGE l_issue_attachment_id: INTEGER l_status: A_GENERAL_STATUS_MESSAGE l_issue_id: INTEGER do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project_id) if project_access.is_found then l_issue_id := issue_access.retrieve_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_issue_id > 0 then create l_issue_attachment.make l_issue_attachment.set_issue_id (l_issue_id) l_issue_attachment.set_file_name (l_msg.file_name) l_issue_attachment.set_description (l_msg.description) issue_access.insert_issue_attachment (l_issue_attachment) l_issue_attachment_id := issue_access.last_insert_id create l_reply.make (l_issue_attachment_id) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_issue) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_retrieve_attachments (a_msg: A_MESSAGE) -- retrieve all attachment of an issue require a_msg_ok: a_msg /= Void local l_msg: O_ISSUE_RETRIEVE_ATTACHMENTS_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_reply: O_ISSUE_RETRIEVE_ATTACHMENTS_REPLY_MESSAGE l_attachments: DS_ARRAYED_LIST [TUPLE[issue_attachment_id: INTEGER; file_name: STRING; description: STRING]] l_attachment_list: DS_ARRAYED_LIST [O_ISSUE_ISSUE_ATTACHMENT_MESSAGE] l_attachment_item: O_ISSUE_ISSUE_ATTACHMENT_MESSAGE l_issue_id: INTEGER do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project_id) if project_access.is_found then l_issue_id := issue_access.retrieve_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_issue_id > 0 then l_attachments := issue_access.retrieve_issue_attachments (l_issue_id) create l_attachment_list.make(l_attachments.count) from l_attachments.start until l_attachments.after loop create l_attachment_item.make ( l_attachments.item_for_iteration.issue_attachment_id, l_attachments.item_for_iteration.file_name, l_attachments.item_for_iteration.description) l_attachment_list.force_last(l_attachment_item) -- Increment l_attachments.forth end create l_reply.make (create {A_SEQUENCE_VALUE [O_ISSUE_ISSUE_ATTACHMENT_MESSAGE]}.make (l_attachment_list)) node.send_message_reply (l_reply, a_msg) else create l_status.make (False, err_invalid_issue) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_invalid_project) node.send_message_reply (l_status, a_msg) end else create l_status.make (False, err_session_timeout) node.send_message_reply (l_status, a_msg) end end issue_remove_attachment (a_msg: A_MESSAGE) -- remove an attachment from an issue local l_msg: O_ISSUE_REMOVE_ATTACHMENT_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_issue_id: INTEGER do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project_id) if project_access.is_found then l_issue_id := issue_access.retrieve_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_issue_id > 0 then issue_access.remove_issue_attachment (l_issue_id, l_msg.file_name) create l_status.make (True, Void) else create l_status.make (False, err_invalid_issue) end else create l_status.make (False, err_invalid_project) end else create l_status.make (False, err_session_timeout) end node.send_message_reply (l_status, a_msg) end issue_remove_all_attachments (a_msg: A_MESSAGE) -- Remove all attachments of an issue local l_msg: O_ISSUE_REMOVE_ALL_ATTACHMENTS_MESSAGE l_reply_msg: O_ISSUE_REMOVE_ALL_ATTACHMENTS_STORAGE_REPLY_MESSAGE l_issue_id: INTEGER l_project_name: STRING l_attachments: DS_ARRAYED_LIST [TUPLE [issue_attachment_id: INTEGER; file_name: STRING; description: STRING]] l_attachment_list: DS_ARRAYED_LIST [A_STRING_VALUE] do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then project_access.retrieve_project_by_id (l_msg.project_id) if project_access.is_found then l_issue_id := issue_access.retrieve_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_issue_id > 0 then -- prepare the reply l_project_name := project_access.last_project.name l_attachments := issue_access.retrieve_issue_attachments (l_issue_id) create l_attachment_list.make(l_attachments.count) from l_attachments.start until l_attachments.after loop l_attachment_list.force_last (l_attachments.item_for_iteration.file_name) -- Increment l_attachments.forth end -- remove attachments from database issue_access.remove_issue_all_attachments (l_issue_id) -- send back reply create l_reply_msg.make (True, "", l_project_name, create {A_SEQUENCE_VALUE [A_STRING_VALUE]}.make (l_attachment_list)) else create l_reply_msg.make (False, err_invalid_issue, Void, Void) end else create l_reply_msg.make (False, err_invalid_project, Void, Void) end else create l_reply_msg.make (False, err_session_timeout, Void, Void) end node.send_message_reply (l_reply_msg, a_msg) end issue_add_subscription (a_msg: A_MESSAGE) -- add the subscription for the session user local l_msg: O_ISSUE_ADD_SUBSCRIPTION_MESSAGE l_user: USER l_tag_id: INTEGER l_tag_to_insert: STRING l_last_revision_id: INTEGER l_status: A_GENERAL_STATUS_MESSAGE l_current_tags: DS_ARRAYED_LIST[STRING] do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then l_user := user_access.last_user if is_private_access_granted (l_user.user_id, l_msg.project_id) then l_last_revision_id := issue_access.retrieve_last_revision_from_project_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_last_revision_id > 0 then l_tag_to_insert := "subscribed::" + l_user.name l_current_tags := issue_access.retrieve_revision_tags (l_last_revision_id) l_current_tags.set_equality_tester (create {KL_EQUALITY_TESTER [STRING]}) if not l_current_tags.has (l_tag_to_insert) then -- The user isn't subscribed yet l_tag_id := issue_access.retrieve_tag_id (l_tag_to_insert) if l_tag_id = 0 then issue_access.insert_tag_text (l_tag_to_insert) l_tag_id := issue_access.retrieve_tag_id (l_tag_to_insert) end issue_access.insert_issue_tag_association (l_last_revision_id, l_tag_id) end create l_status.make (True, Void) else create l_status.make (False, err_invalid_issue) end else create l_status.make (False, err_invalid_issue) end else create l_status.make (False, err_invalid_user) end node.send_message_reply (l_status, a_msg) end issue_remove_subscription (a_msg: A_MESSAGE) -- remove the subscription for the session user local l_msg: O_ISSUE_REMOVE_SUBSCRIPTION_MESSAGE l_status: A_GENERAL_STATUS_MESSAGE l_user: USER l_tag_to_remove: STRING l_last_revision_id: INTEGER l_tag_id: INTEGER do l_msg ?= a_msg check valid_message: l_msg /= Void end user_access.retrieve_user_by_session (l_msg.session) if user_access.is_found then l_user := user_access.last_user if is_private_access_granted (l_user.user_id, l_msg.project_id) then l_tag_to_remove := "subscribed::" + l_user.name l_last_revision_id := issue_access.retrieve_last_revision_from_project_issue_id (l_msg.project_id, l_msg.project_issue_id) if l_last_revision_id > 0 then l_tag_id := issue_access.retrieve_tag_id (l_tag_to_remove) if l_tag_id > 0 then issue_access.remove_issue_tag_association (l_last_revision_id, l_tag_id) end create l_status.make (True, Void) end else create l_status.make (False, err_invalid_issue) end else create l_status.make (False, err_invalid_user) end node.send_message_reply (l_status, a_msg) end feature {NONE} -- Implementation tag_list_to_string (a_tag_list: DS_ARRAYED_LIST [STRING]): STRING -- Converts a list of tags to a single, comma-separated string do create Result.make_empty if a_tag_list /= Void then from a_tag_list.start until a_tag_list.after loop if Result.is_empty then Result := a_tag_list.item_for_iteration else Result.append(", " + a_tag_list.item_for_iteration) end -- inc a_tag_list.forth end end ensure created: Result /= Void end is_private_access_granted (a_user_id, a_project_id: INTEGER): BOOLEAN -- checks if the user can access private issues local l_user_group: INTEGER l_user_assiciations: LIST[USER_ASSOCIATION] do Result := False -- the user can either be a member or owner of the project l_user_group := user_access.retrieve_user_project_association_group (a_project_id, a_user_id) if l_user_group = 3 or l_user_group = 4 then Result := True end if not Result then -- or he can be an admin user_access.retrieve_user_association (a_user_id) l_user_assiciations := user_access.last_user_associations from l_user_assiciations.start until Result or l_user_assiciations.after loop if l_user_assiciations.item_for_iteration.group_id = 1 then Result := True end l_user_assiciations.forth end end end end