Opened 5 years ago

# [PATCH] support of fully anonymous polls

Reported by: Owned by: falkb Ryan J Ollos high PollMacro normal

### Description

This patch adds the ability to make fully anonymous polls, simply by the use of pattern  (*)  at the end of the poll question:

• ## pollmacro/trunk/tracpoll/tracpoll.py

\n' % escape(self.title)) username = req.authname or 'anonymous' if isAnonymousPoll: username = hashlib.sha1(username).hexdigest() for id, style, vote in self.vote_defs: hid = escape(str(id)) out.write('\n' % (style and ' class="%s"' % style or '')) else: out.write(vote) if self.votes[id]: out.write(' (' + ', '.join(self.votes[id]) + ')') if isAnonymousPoll: out.write(' (%s)' % len(self.votes[id])) else: out.write(' (' + ', '.join(self.votes[id]) + ')') out.write('\n') if can_vote: out.write('') else: out.write("
You don't have permission to vote. You may need to login.") out.write('
\n\n') 'Path where poll pickle dumps should be stored.') def expand_macro(self, formatter, name, content): isAnonymousPoll = False req = formatter.req if not content: return system_message("A title must be provided as the first argument to the poll macro") if len(content) < 2: return system_message("One or more options must be provided to vote on.") title = content.pop(0) return self.render_poll(req, title, content) if title.endswith('(*)'): isAnonymousPoll = True return self.render_poll(req, title, content, isAnonymousPoll) def render_poll(self, req, title, votes): def render_poll(self, req, title, votes, isAnonymousPoll): add_stylesheet(req, 'poll/css/poll.css') if not req.perm.has_permission('POLL_VIEW'): return '' poll = Poll(self.base_dir, title, all_votes) if req.perm.has_permission('POLL_VOTE'): poll.populate(req) return poll.render(self.env, req) poll.populate(req, isAnonymousPoll) return poll.render(self.env, req, isAnonymousPoll) # IPermissionRequestor methods def get_permission_actions(self):

### comment:1 follow-up:  3 Changed 5 years ago by Ryan J Ollos

Looks like a nice feature. I think we should consider using a positional or kw arg rather than appending a pattern to the title. isAnonymousPoll should be is_anonymous_poll or just is_anonymous per the style used in the code.

### comment:2 Changed 5 years ago by Ryan J Ollos

Priority: normal → high new → assigned

### comment:3 in reply to:  1 ; follow-up:  9 Changed 5 years ago by anonymous

Looks like a nice feature. I think we should consider using a positional or kw arg rather than appending a pattern to the title.

I wonder how such arg can be programmed in a macro, even if the whole string is splitted in a string list by separator ';'.

### comment:4 Changed 5 years ago by Ryan J Ollos

Good point. I seem to have remember dealing with this for another plugin. I'll do some research and get back to you.

### comment:5 Changed 5 years ago by falkb

I also have another small patch that introduces a second pattern "(-)" which means the poll is closed. If one changes from "(*)" to "(-)", the anonymous polling is closed and no input is possible henceforward. I also somehow dislike that pattern trick but it's compatible with the ';'-separator divide mechanism.

### comment:6 Changed 5 years ago by Ryan J Ollos

Please do go ahead and post your new patch then. If we come up with a good way to handle kw args, we can always modify it. Could you take care of the style issue not in comment:1 as well?

### comment:7 Changed 5 years ago by falkb

Here we go, with closed-poll support now. Hope you like it. This replaces this first patch here:

• ## pollmacro/trunk/tracpoll/tracpoll.py

\n' % escape(self.title)) username = req.authname or 'anonymous' if is_anonymous_poll: username = hashlib.sha1(username).hexdigest() for id, style, vote in self.vote_defs: hid = escape(str(id)) out.write('\n' % (style and ' class="%s"' % style or '')) (this hunk was shorter than expected) else: out.write(vote) if self.votes[id]: out.write(' (' + ', '.join(self.votes[id]) + ')') if is_anonymous_poll: out.write(' (%s)' % len(self.votes[id])) else: out.write(' (' + ', '.join(self.votes[id]) + ')') out.write('\n') if can_vote: else: elif not is_closed_poll: out.write("
You don't have permission to vote. You may need to login.") out.write('
\n\n') can_vote and out.write('\n') 'Path where poll pickle dumps should be stored.') def expand_macro(self, formatter, name, content): is_anonymous_poll = False is_closed_poll = False req = formatter.req if not content: return system_message("A title must be provided as the first argument to the poll macro") if len(content) < 2: return system_message("One or more options must be provided to vote on.") title = content.pop(0) return self.render_poll(req, title, content) if title.endswith('(*)'): is_anonymous_poll = True if title.endswith('(-)'): is_anonymous_poll = True is_closed_poll = True return self.render_poll(req, title, content, is_anonymous_poll, is_closed_poll) def render_poll(self, req, title, votes): def render_poll(self, req, title, votes, is_anonymous_poll, is_closed_poll): add_stylesheet(req, 'poll/css/poll.css') if not req.perm.has_permission('POLL_VIEW'): return '' poll = Poll(self.base_dir, title, all_votes) if req.perm.has_permission('POLL_VOTE'): poll.populate(req) return poll.render(self.env, req) poll.populate(req, is_anonymous_poll) return poll.render(self.env, req, is_anonymous_poll, is_closed_poll) # IPermissionRequestor methods def get_permission_actions(self):

### comment:8 Changed 5 years ago by falkb

Addon: Not tested with closing of non-anonymous polls. Could be a TODO but could give you an idea with the given patch.

### comment:9 in reply to:  3 ; follow-ups:  10  11 Changed 5 years ago by Olemis Lang

Looks like a nice feature. I think we should consider using a positional or kw arg rather than appending a pattern to the title.

AFAICT +1

I wonder how such arg can be programmed in a macro, even if the whole string is splitted in a string list by separator ';'.

If I understood correctly your implicit question , that's what trac.wiki.api.parse_args is for .

PS: Notice that commas in argument values have to be escaped

### comment:10 in reply to:  9 Changed 5 years ago by Ryan J Ollos

PS: Notice that commas in argument values have to be escaped

Yeah, I think this is the best way to go. I've been trying to think about how we might better handle cases that the user hasn't escaped the args, since it will particularly cause problems for users that are upgrading and never took care to escape the poll question.

I haven't tested this out yet, but I was thinking of employing one of the following solutions:

• Define the variable that determines is_anonymous_poll as a kwarg and then append all of the regular args together, since it's safe to assume that if more than one regular arg exists then poll question wasn't escaped properly.
• Define is_anonymous_poll as a regular arg, but when evaluating the output of parse_args, if there isn't a case-insensitive match to true, assume that the poll question wasn't escaped properly and append the arg to the 0th arg.

A prerequisite to setting up the above behavior is to wire up unit tests, which will be very valuable here.

### comment:11 in reply to:  9 Changed 5 years ago by falkb

Looks like a nice feature. I think we should consider using a positional or kw arg rather than appending a pattern to the title.

AFAICT +1

Would it be compatible to older versions of poll, if we first split by ';', get a resulting string list, and then analyze the last one in the string list if it contains existing args?

Let's talk with the help of an example:

For instance,  [[Poll(Please, tell me what do you think about foo?;Yes, it's OK;No, I don't like it;is_anonymous_poll=true)]]  will then be split into 4 strings where the 4th one contains the arguments that we then analyze with  trac.wiki.api.parse_args , and the check of the analyzing will have success on checking for known arguments.

If we just have  [[Poll(Please, tell me what do you think about foo?;Yes, it's OK;No, I don't like it)]] , after the ';'-split,  trac.wiki.api.parse_args  will read the last partial string as (['No', 'I don't like it']), and then it can be decided as another poll answer instead of an argument list, because at least one of the strings is not known as valid argument.

Will this approach work?

### Modify Ticket

Change Properties