bsw/jbe@0: Member = mondelefant.new_class() bsw/jbe@0: Member.table = 'member' bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw@9: mode = "1m", bsw@9: to = "MemberHistory", bsw@9: this_key = 'id', bsw@9: that_key = 'member_id', bsw@9: ref = 'history_entries', bsw@9: back_ref = 'member' bsw@9: } bsw@9: bsw@9: Member:add_reference{ bsw/jbe@4: mode = '1m', bsw@2: to = "MemberImage", bsw@2: this_key = 'id', bsw@2: that_key = 'member_id', bsw/jbe@4: ref = 'images', bsw@2: back_ref = 'member' bsw@2: } bsw@2: bsw@2: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Contact", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'contacts', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"other_member_id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Contact", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'foreign_contacts', bsw/jbe@0: back_ref = 'other_member', bsw/jbe@0: default_order = '"member_id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Session", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'sessions', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"ident"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Draft", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'author_id', bsw/jbe@0: ref = 'drafts', bsw/jbe@0: back_ref = 'author', bsw/jbe@0: default_order = '"id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Suggestion", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'author_id', bsw/jbe@0: ref = 'suggestions', bsw/jbe@0: back_ref = 'author', bsw/jbe@0: default_order = '"id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Membership", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'memberships', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"area_id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Interest", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'interests', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Initiator", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'initiators', bsw@10: back_ref = 'member' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Supporter", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'supporters', bsw@2: back_ref = 'member' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Opinion", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'opinions', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Delegation", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'truster_id', bsw/jbe@0: ref = 'outgoing_delegations', bsw/jbe@0: back_ref = 'truster', bsw/jbe@0: default_order = '"id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Delegation", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'trustee_id', bsw/jbe@0: ref = 'incoming_delegations', bsw/jbe@0: back_ref = 'trustee', bsw/jbe@0: default_order = '"id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "DirectVoter", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'direct_voter', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"issue_id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = '1m', bsw/jbe@0: to = "Vote", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'member_id', bsw/jbe@0: ref = 'vote', bsw/jbe@0: back_ref = 'member', bsw/jbe@0: default_order = '"issue_id", "initiative_id"' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = 'mm', bsw/jbe@0: to = "Member", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'id', bsw/jbe@0: connected_by_table = 'contact', bsw/jbe@0: connected_by_this_key = 'member_id', bsw/jbe@0: connected_by_that_key = 'other_member_id', bsw/jbe@0: ref = 'saved_members', bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = 'mm', bsw/jbe@0: to = "Member", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'id', bsw/jbe@0: connected_by_table = 'contact', bsw/jbe@0: connected_by_this_key = 'other_member_id', bsw/jbe@0: connected_by_that_key = 'member_id', bsw/jbe@0: ref = 'saved_by_members', bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = 'mm', bsw@281: to = "Unit", bsw@281: this_key = 'id', bsw@281: that_key = 'id', bsw@281: connected_by_table = 'privilege', bsw@281: connected_by_this_key = 'member_id', bsw@281: connected_by_that_key = 'unit_id', bsw@281: ref = 'units' bsw@281: } bsw@281: bsw@281: Member:add_reference{ bsw@281: mode = 'mm', bsw/jbe@0: to = "Area", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'id', bsw/jbe@0: connected_by_table = 'membership', bsw/jbe@0: connected_by_this_key = 'member_id', bsw/jbe@0: connected_by_that_key = 'area_id', bsw/jbe@0: ref = 'areas' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = 'mm', bsw/jbe@0: to = "Issue", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'id', bsw/jbe@0: connected_by_table = 'interest', bsw/jbe@0: connected_by_this_key = 'member_id', bsw/jbe@0: connected_by_that_key = 'issue_id', bsw/jbe@0: ref = 'issues' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = 'mm', bsw/jbe@0: to = "Initiative", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'id', bsw/jbe@0: connected_by_table = 'initiator', bsw/jbe@0: connected_by_this_key = 'member_id', bsw/jbe@0: connected_by_that_key = 'initiative_id', bsw/jbe@0: ref = 'initiated_initiatives' bsw/jbe@0: } bsw/jbe@0: bsw/jbe@0: Member:add_reference{ bsw/jbe@0: mode = 'mm', bsw/jbe@0: to = "Initiative", bsw/jbe@0: this_key = 'id', bsw/jbe@0: that_key = 'id', bsw/jbe@0: connected_by_table = 'supporter', bsw/jbe@0: connected_by_this_key = 'member_id', bsw/jbe@0: connected_by_that_key = 'initiative_id', bsw/jbe@0: ref = 'supported_initiatives' bsw/jbe@0: } bsw/jbe@0: bsw@279: model.has_rendered_content(Member, RenderedMemberStatement, "statement") bsw@279: bsw@193: function Member:build_selector(args) bsw@193: local selector = self:new_selector() bsw@193: if args.active ~= nil then bsw@193: selector:add_where{ "member.active = ?", args.active } bsw@193: end bsw@581: if args.locked ~= nil then bsw@581: selector:add_where{ "member.locked = ?", args.locked } bsw@581: end bsw@199: if args.is_contact_of_member_id then bsw@199: selector:join("contact", "__model_member__contact", "member.id = __model_member__contact.other_member_id") bsw@199: selector:add_where{ "__model_member__contact.member_id = ?", args.is_contact_of_member_id } bsw@199: end bsw@297: if args.voting_right_for_unit_id then bsw@299: selector:join("privilege", "__model_member__privilege", { "member.id = __model_member__privilege.member_id AND __model_member__privilege.voting_right AND __model_member__privilege.unit_id = ?", args.voting_right_for_unit_id }) bsw@297: end bsw@581: if args.admin_search then bsw@581: local search_string = "%" .. args.admin_search .. "%" bsw@581: selector:add_where{ "member.identification ILIKE ? OR member.name ILIKE ?", search_string, search_string } bsw@581: end bsw@193: if args.order then bsw@193: if args.order == "id" then bsw@193: selector:add_order_by("id") bsw@581: elseif args.order == "identification" then bsw@581: selector:add_order_by("identification") bsw@193: elseif args.order == "name" then bsw@193: selector:add_order_by("name") bsw@193: else bsw@193: error("invalid order") bsw@193: end bsw@193: end bsw@193: return selector bsw@193: end bsw@193: bsw/jbe@0: function Member.object:set_password(password) bsw@728: local hash = extos.crypt( bsw/jbe@0: password, bsw/jbe@0: "$1$" .. multirand.string( bsw/jbe@0: 8, bsw/jbe@0: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./" bsw/jbe@0: ) bsw/jbe@0: ) bsw@728: assert(hash, "extos.crypt failed") bsw/jbe@0: self.password = hash bsw/jbe@0: end bsw/jbe@0: bsw/jbe@0: function Member.object:check_password(password) bsw/jbe@0: if type(password) == "string" and type(self.password) == "string" then bsw@728: return extos.crypt(password, self.password) == self.password bsw/jbe@0: else bsw/jbe@0: return false bsw/jbe@0: end bsw/jbe@0: end bsw/jbe@0: bsw/jbe@0: function Member.object_get:published_contacts() bsw/jbe@0: return Member:new_selector() bsw/jbe@0: :join('"contact"', nil, '"contact"."other_member_id" = "member"."id"') bsw/jbe@0: :add_where{ '"contact"."member_id" = ?', self.id } bsw/jbe@0: :add_where("public") bsw/jbe@0: :exec() bsw/jbe@0: end bsw/jbe@0: bsw/jbe@0: function Member:by_login_and_password(login, password) bsw/jbe@0: local selector = self:new_selector() bsw/jbe@5: selector:add_where{'"login" = ?', login } bsw@203: selector:add_where('NOT "locked"') bsw/jbe@0: selector:optional_object_mode() bsw/jbe@0: local member = selector:exec() bsw/jbe@0: if member and member:check_password(password) then bsw/jbe@0: return member bsw/jbe@0: else bsw/jbe@0: return nil bsw/jbe@0: end bsw/jbe@0: end bsw/jbe@0: bsw/jbe@5: function Member:by_login(login) bsw/jbe@5: local selector = self:new_selector() bsw/jbe@5: selector:add_where{'"login" = ?', login } bsw/jbe@5: selector:optional_object_mode() bsw/jbe@5: return selector:exec() bsw/jbe@5: end bsw/jbe@5: bsw/jbe@5: function Member:by_name(name) bsw/jbe@5: local selector = self:new_selector() bsw/jbe@5: selector:add_where{'"name" = ?', name } bsw/jbe@5: selector:optional_object_mode() bsw/jbe@5: return selector:exec() bsw/jbe@5: end bsw/jbe@5: bsw@2: function Member:get_search_selector(search_string) bsw/jbe@0: return self:new_selector() bsw@2: :add_field( {'"highlight"("member"."name", ?)', search_string }, "name_highlighted") bsw@2: :add_where{ '"member"."text_search_data" @@ "text_search_query"(?)', search_string } bsw@362: :add_where("activated NOTNULL AND active") bsw/jbe@0: end bsw@2: bsw@388: function Member.object:send_invitation(template_file, subject) bsw@286: trace.disable() bsw@286: self.invite_code = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" ) bsw@388: self:save() bsw@388: bsw@388: local subject = subject bsw@388: local content bsw@388: bsw@388: if template_file then bsw@388: local fh = io.open(template_file, "r") bsw@388: content = fh:read("*a") bsw@388: content = (content:gsub("#{invite_code}", self.invite_code)) bsw@388: else bsw@388: subject = config.mail_subject_prefix .. _"Invitation to LiquidFeedback" bsw@388: content = slot.use_temporary(function() bsw@388: slot.put(_"Hello\n\n") bsw@388: slot.put(_"You are invited to LiquidFeedback. To register please click the following link:\n\n") bsw@388: slot.put(request.get_absolute_baseurl() .. "index/register.html?invite=" .. self.invite_code .. "\n\n") bsw@388: slot.put(_"If this link is not working, please open following url in your web browser:\n\n") bsw@388: slot.put(request.get_absolute_baseurl() .. "index/register.html\n\n") bsw@388: slot.put(_"On that page please enter the invite key:\n\n") bsw@388: slot.put(self.invite_code .. "\n\n") bsw@388: end) bsw@388: end bsw@388: bsw@286: local success = net.send_mail{ bsw@286: envelope_from = config.mail_envelope_from, bsw@286: from = config.mail_from, bsw@286: reply_to = config.mail_reply_to, bsw@286: to = self.notify_email_unconfirmed or self.notify_email, bsw@388: subject = subject, bsw@286: content_type = "text/plain; charset=UTF-8", bsw@286: content = content bsw@286: } bsw@286: return success bsw/jbe@0: end bsw@2: bsw/jbe@6: function Member.object:set_notify_email(notify_email) bsw@224: trace.disable() bsw/jbe@6: local expiry = db:query("SELECT now() + '7 days'::interval as expiry", "object").expiry bsw/jbe@6: self.notify_email_unconfirmed = notify_email bsw/jbe@6: self.notify_email_secret = multirand.string( 24, "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" ) bsw/jbe@6: self.notify_email_secret_expiry = expiry bsw/jbe@6: local content = slot.use_temporary(function() bsw/jbe@6: slot.put(_"Hello " .. self.name .. ",\n\n") bsw/jbe@6: slot.put(_"Please confirm your email address by clicking the following link:\n\n") jbe@326: slot.put(request.get_absolute_baseurl() .. "index/confirm_notify_email.html?secret=" .. self.notify_email_secret .. "\n\n") bsw/jbe@6: slot.put(_"If this link is not working, please open following url in your web browser:\n\n") jbe@326: slot.put(request.get_absolute_baseurl() .. "index/confirm_notify_email.html\n\n") bsw/jbe@6: slot.put(_"On that page please enter the confirmation code:\n\n") bsw/jbe@6: slot.put(self.notify_email_secret .. "\n\n") bsw/jbe@6: end) bsw/jbe@6: local success = net.send_mail{ bsw/jbe@6: envelope_from = config.mail_envelope_from, bsw/jbe@6: from = config.mail_from, bsw/jbe@6: reply_to = config.mail_reply_to, bsw/jbe@6: to = self.notify_email_unconfirmed, bsw/jbe@6: subject = config.mail_subject_prefix .. _"Email confirmation request", bsw/jbe@6: content_type = "text/plain; charset=UTF-8", bsw/jbe@6: content = content bsw/jbe@6: } bsw@75: if success then bsw@75: local lock_expiry = db:query("SELECT now() + '1 hour'::interval AS lock_expiry", "object").lock_expiry bsw@75: self.notify_email_lock_expiry = lock_expiry bsw@75: end bsw@75: self:save() bsw/jbe@6: return success bsw/jbe@6: end bsw@11: bsw/jbe@19: function Member.object:get_setting(key) bsw@79: return Setting:by_pk(self.id, key) bsw/jbe@19: end bsw/jbe@19: bsw/jbe@19: function Member.object:get_setting_value(key) bsw@79: local setting = Setting:by_pk(self.id, key) bsw/jbe@19: if setting then bsw/jbe@19: return setting.value bsw/jbe@19: end bsw@11: end bsw@11: bsw@11: function Member.object:set_setting(key, value) bsw/jbe@19: local setting = self:get_setting(key) bsw/jbe@19: if not setting then bsw/jbe@19: setting = Setting:new() bsw@79: setting.member_id = self.id bsw/jbe@19: setting.key = key bsw/jbe@19: end bsw/jbe@19: setting.value = value bsw/jbe@19: setting:save() bsw@11: end bsw@11: bsw@11: function Member.object:get_setting_maps_by_key(key) bsw@11: return SettingMap:new_selector() bsw@11: :add_where{ "member_id = ?", self.id } bsw@11: :add_where{ "key = ?", key } bsw@11: :add_order_by("subkey") bsw@11: :exec() bsw@11: end bsw@11: bsw@11: function Member.object:get_setting_map_by_key_and_subkey(key, subkey) bsw@11: return SettingMap:new_selector() bsw@11: :add_where{ "member_id = ?", self.id } bsw@11: :add_where{ "key = ?", key } bsw@11: :add_where{ "subkey = ?", subkey } bsw@11: :add_order_by("subkey") bsw@11: :optional_object_mode() bsw@11: :exec() bsw@11: end bsw@11: bsw@11: function Member.object:set_setting_map(key, subkey, value) poelzi@144: setting_map = self:get_setting_map_by_key_and_subkey(key, subkey) poelzi@144: if not setting_map then poelzi@144: setting_map = SettingMap:new() poelzi@144: setting_map.member_id = self.id poelzi@144: setting_map.key = key poelzi@144: setting_map.subkey = subkey poelzi@144: end poelzi@144: setting_map.value = value poelzi@144: setting_map:save() bsw@11: end bsw@75: bsw@75: function Member.object_get:notify_email_locked() bsw@75: return( bsw@75: Member:new_selector() bsw@75: :add_where{ "id = ?", app.session.member.id } bsw@75: :add_where("notify_email_lock_expiry > now()") bsw@75: :count() == 1 bsw@75: ) poelzi@134: end poelzi@134: bsw@273: function Member.object_get:units_with_voting_right() bsw@273: return(Unit:new_selector() bsw@273: :join("privilege", nil, { "privilege.unit_id = unit.id AND privilege.member_id = ? AND privilege.voting_right", self.id }) bsw@273: :exec() bsw@273: ) bsw@273: end bsw@273: poelzi@134: function Member.object:ui_field_text(args) poelzi@134: args = args or {} bsw@527: if app.session.member_id or config.public_access == "pseudonym" or config.public_access == "full" then poelzi@134: -- ugly workaround for getting html into a replaced string and to the user poelzi@134: ui.container{label = args.label, label_attr={class="ui_field_label"}, content = function() poelzi@134: slot.put(string.format('%s', poelzi@134: encode.url{ poelzi@134: module = "member", poelzi@134: view = "show", poelzi@134: id = self.id, poelzi@134: }, poelzi@134: encode.html(self.name))) poelzi@134: end poelzi@134: } poelzi@134: else poelzi@134: ui.field.text{ label = args.label, value = _"[not displayed public]" } poelzi@134: end poelzi@134: end bsw@281: bsw@281: function Member.object:has_voting_right_for_unit_id(unit_id) bsw@547: if not self.__units_with_voting_right_hash then bsw@547: local privileges = Privilege:new_selector() bsw@547: :add_where{ "member_id = ?", self.id } bsw@547: :add_where("voting_right") bsw@547: :for_share() bsw@547: :exec() bsw@547: self.__units_with_voting_right_hash = {} bsw@551: for i, privilege in ipairs(privileges) do bsw@551: self.__units_with_voting_right_hash[privilege.unit_id] = true bsw@551: end bsw@547: end bsw@547: return self.__units_with_voting_right_hash[unit_id] and true or false jbe@326: end bsw@525: bsw@525: function Member.object:get_delegatee_member(unit_id, area_id, issue_id) bsw@525: local selector = Member:new_selector() bsw@525: if unit_id then bsw@525: selector:join("delegation", nil, { "delegation.trustee_id = member.id AND delegation.scope = 'unit' AND delegation.unit_id = ? AND delegation.truster_id = ?", unit_id, self.id }) bsw@525: end bsw@525: selector:optional_object_mode() bsw@525: return selector:exec() bsw@533: end