def prompt_tags(session, term, msg, colors, public=True): xpos = max(0, (term.width // 2) - (80 // 2)) # conditionally enforce tag moderation moderated = get_ini("msg", "moderated_tags", getter="getboolean") tag_moderators = set(get_ini("msg", "tag_moderators", split=True)) # enforce 'public' tag if public and "public" not in msg.tags: msg.tags.add("public") elif not public and "public" in msg.tags: msg.tags.remove("public") # describe all available tags, as we oft want to do. do_describe_available_tags(term, colors) # and remind ourselves of the available network tags, description = get_network_tag_description(term, colors) if description: show_description(term=term, color=None, description=description) echo( u"".join( (term.move_x(xpos), term.clear_eos, u"Enter tags, separated by commas.\r\n", term.move_x(xpos), u":: ") ) ) all_tags = list_tags() while True: inp = LineEditor( subject_max_length, u", ".join(sorted(msg.tags)), colors={"highlight": colors["backlight"]} ).read() if inp is None: echo(u"".join((term.move_x(xpos), colors["highlight"]("Message canceled."), term.clear_eol))) term.inkey(1) return False msg.tags = set(filter(None, set(map(unicode.strip, inp.split(","))))) if moderated and not (tag_moderators | session.user.groups): cannot_tag = [_tag for _tag in msg.tags if _tag not in all_tags] if cannot_tag: echo( u"".join( ( u"\r\n", term.move_x(xpos), u", ".join((quote(tag, colors) for tag in cannot_tag)), u": not allowed; this system is moderated.", ) ) ) term.inkey(2) echo(term.move_up) map(msg.tags.remove, cannot_tag) continue return True
def prompt_tags(session, term, msg, colors, public=True): xpos = max(0, (term.width // 2) - (80 // 2)) # conditionally enforce tag moderation moderated = get_ini('msg', 'moderated_tags', getter='getboolean') tag_moderators = set(get_ini('msg', 'tag_moderators', split=True)) # enforce 'public' tag if public and 'public' not in msg.tags: msg.tags.add('public') elif not public and 'public' in msg.tags: msg.tags.remove('public') # describe all available tags, as we oft want to do. do_describe_available_tags(term, colors) # and remind ourselves of the available network tags, description = get_network_tag_description(term, colors) if description: show_description(term=term, color=None, description=description) echo(u''.join( (term.move_x(xpos), term.clear_eos, u'Enter tags, separated by commas.\r\n', term.move_x(xpos), u':: '))) all_tags = list_tags() while True: inp = LineEditor(subject_max_length, u', '.join(sorted(msg.tags)), colors={ 'highlight': colors['backlight'] }).read() if inp is None: echo(u''.join( (term.move_x(xpos), colors['highlight']('Message canceled.'), term.clear_eol))) term.inkey(1) return False msg.tags = set(filter(None, set(map(unicode.strip, inp.split(','))))) if moderated and not (tag_moderators | session.user.groups): cannot_tag = [_tag for _tag in msg.tags if _tag not in all_tags] if cannot_tag: echo(u''.join((u'\r\n', term.move_x(xpos), u', '.join( (quote(tag, colors) for tag in cannot_tag)), u': not allowed; this system is moderated.'))) term.inkey(2) echo(term.move_up) map(msg.tags.remove, cannot_tag) continue return True
def set_subscription_tags(session, term): """ This function is called to assign a new set of subscription tags for a user. If escape is pressed, the existing value is used, or '*' is used if not previously set. This should be called for first-time users, and optionally at any later time to change a subscription. """ line_no = display_banner(art_file, encoding=art_encoding) # introduce the user to the concept ... description = ( u"{term.yellow}You can think of tags as a system of providing " u"context to any message stored on this system. A tag might signify " u"that it was received on a particular message network, or it might " u"provide the topic of conversation, such as " u"{term.bold_yellow}python{term.normal}{term.yellow} or " u"{term.bold_yellow}rock music{term.normal}{term.yellow}. " u"This is similar to flicker's tags, or twitter hashtags. " u"Use glob expressions as a comma-delimited list, for example, " u"the expression {term.bold_yellow}x84net{term.normal}{term.yellow}, " u"{term.bold_yellow}sysop{term.normal}{term.yellow}, " u"{term.bold_yellow}python*{term.normal}{term.yellow} " u"will subscribe to all tags of the x84net message network, " u"sysop messages, and any topics that begin with the phrase " u"{term.bold_yellow}python{term.normal}{term.yellow}. You can " u"subscribe to all topics using the expression, " u"{term.bold_yellow}*{term.normal}{term.yellow}.".format(term=term) ) echo(u"\r\n") line_no += 1 line_no += show_description(description, color=None) description = u" ".join( [u"tags available:"] + ["{tag}{term.yellow},{term.normal}".format(tag=tag, term=term) for tag in list_tags()] ) echo(u"\r\n\r\n") line_no += 2 # display our prompt prefix, input_prefix: input_prefix = u" {sep} {key:>18}: ".format(sep=term.bright_yellow(u"::"), key="subscription tags") echo(input_prefix) xloc = term.length(input_prefix) # and prompt an editor on that row editor = ScrollingEditor( xloc=xloc, yloc=line_no, width=(term.width - xloc - 2), colors={"highlight": term.black_on_yellow} ) value = editor.read() or u"" echo(term.normal + u"\r\n\r\n") return map(unicode.strip, value.split(","))
def do_nua(user): """ Perform new user account field setting and validation. """ session, term = getsession(), getterminal() session.activity = u'Applying for an account' idx = 0 validation_fields = get_validation_fields(user) while idx != len(validation_fields): field = validation_fields.values()[idx] echo(fixate_next(term, newlines=1)) if field.description: show_description(term, field.description, color=getattr(term, color_secondary)) echo(u'\r\n') echo(fixate_next(term, newlines=0)) value = prompt_input(term=term, key=field.prompt_key, content=field.getter(), **field.kwargs) # user pressed escape, prompt for cancellation if value is None: if prompt_yesno('Cancel'): return None # re-prompt for current field continue value = value.strip() # validate using function, `field.validation_function()' if field.validation_function: errmsg, mod = field.validation_function(user, value) if errmsg: show_validation_error(errmsg) # re-prompt for current field idx += mod continue # set local variable, increment index to next field; money shot for passing pass to glftpd if field.name: if field.name == 'password': plaintext_password = value setattr(user, field.name, value) idx += 1 return user, plaintext_password
def do_nua(user): """ Perform new user account field setting and validation. """ session, term = getsession(), getterminal() session.activity = u'Applying for an account' idx = 0 validation_fields = get_validation_fields(user) while idx != len(validation_fields): field = validation_fields.values()[idx] echo(fixate_next(term, newlines=1)) if field.description: show_description(term, field.description, color=getattr(term, color_secondary)) echo(u'\r\n') echo(fixate_next(term, newlines=0)) value = prompt_input(term=term, key=field.prompt_key, content=field.getter(), **field.kwargs) # user pressed escape, prompt for cancellation if value is None: if prompt_yesno('Cancel'): return None # re-prompt for current field continue value = value.strip() # validate using function, `field.validation_function()' if field.validation_function: errmsg, mod = field.validation_function(user, value) if errmsg: show_validation_error(errmsg) # re-prompt for current field idx += mod continue # set local variable, increment index to next field if field.name: setattr(user, field.name, value) idx += 1 return user
def do_describe_available_tags(term, colors): sorted_tags = sorted([(len(list_msgs(tags=(tag,))), tag) for tag in list_tags() or [u"public"]], reverse=True) decorated_tags = [ colors["text"](tag) + colors["lowlight"]("({0})".format(num_msgs)) for num_msgs, tag in sorted_tags ] description = u"".join( (colors["highlight"](u"available tags"), ": ", colors["text"](u", ").join(decorated_tags), colors["text"](u".")) ) return show_description(term, description, color=None)
def do_describe_message_system(term, colors): """ Display help text about message tagging. """ def describe_group_tags(): groups = getsession().user.groups if not groups: return u"" return u"".join( ( u"\r\n\r\n", colors["text"]( u"Finally, private messages may be shared among groups. You " u"may post messages to any group you are a member of: " ), colors["text"](u", ".join(quote(grp, colors) for grp in groups)), colors["text"](u"."), ) ) description = u"".join( ( u"\r\n", colors["text"]( u"You can think of tags as a system of providing context to any " u"message stored on this system. A tag might provide the " u"general label of the topic of conversation, which may be " u"subscribed to. For example, " ), quote(u"python", colors), colors["text"]( u" may be used for topics related to the python programming " u"language. This is similar to flicker or gmail tags, or " u"hashtags. Public messages are always tagged " ), quote(u"public", colors), colors["text"](u". "), get_network_tag_description(term, colors), u"\r\n\r\n", colors["text"](u"Furthermore, glob expressions may be used such as "), quote(u"*", colors), u" ", colors["text"]("for all messages, or expression "), quote(u"lang-*", colors), u" ", colors["text"]("might subscribe to both "), quote(u"lang-python", colors), colors["text"](u" and "), quote(u"lang-go", colors), colors["text"](u"."), describe_group_tags(), ) ) return show_description(term, description, color=None)
def do_describe_message_system(term, colors): """ Display help text about message tagging. """ def describe_group_tags(): groups = getsession().user.groups if not groups: return u'' return u''.join(( u'\r\n\r\n', colors['text']( u'Finally, private messages may be shared among groups. You ' u'may post messages to any group you are a member of: '), colors['text']( u', '.join(quote(grp, colors) for grp in groups)), colors['text'](u'.') )) description = u''.join(( u'\r\n', colors['text']( u'You can think of tags as a system of providing context to any ' u'message stored on this system. A tag might provide the ' u'general label of the topic of conversation, which may be ' u'subscribed to. For example, '), quote(u'python', colors), colors['text']( u' may be used for topics related to the python programming ' u'language. This is similar to flicker or gmail tags, or ' u'hashtags. Public messages are always tagged '), quote(u'public', colors), colors['text'](u'. '), get_network_tag_description(term, colors), u'\r\n\r\n', colors['text']( u'Furthermore, glob expressions may be used such as '), quote(u'*', colors), u' ', colors['text']('for all messages, or expression '), quote(u'lang-*', colors), u' ', colors['text']('might subscribe to both '), quote(u'lang-python', colors), colors['text'](u' and '), quote(u'lang-go', colors), colors['text'](u'.'), describe_group_tags(), )) return show_description(term, description, color=None)
def do_describe_available_tags(term, colors): sorted_tags = sorted([(len(list_msgs(tags=(tag,))), tag) for tag in list_tags() or [u'public'] ], reverse=True) decorated_tags = [ colors['text'](tag) + colors['lowlight']('({0})'.format(num_msgs)) for num_msgs, tag in sorted_tags] description = u''.join(( colors['highlight'](u'available tags'), ': ', colors['text'](u', ').join(decorated_tags), colors['text'](u'.'), )) return show_description(term, description, color=None)
def do_describe_available_tags(term, colors): sorted_tags = sorted([(len(list_msgs(tags=(tag, ))), tag) for tag in list_tags() or [u'public']], reverse=True) decorated_tags = [ colors['text'](tag) + colors['lowlight']('({0})'.format(num_msgs)) for num_msgs, tag in sorted_tags ] description = u''.join(( colors['highlight'](u'available tags'), ': ', colors['text'](u', ').join(decorated_tags), colors['text'](u'.'), )) return show_description(term, description, color=None)
def do_describe_message_system(term, colors): """ Display help text about message tagging. """ def describe_group_tags(): groups = getsession().user.groups if not groups: return u'' return u''.join( (u'\r\n\r\n', colors['text']( u'Finally, private messages may be shared among groups. You ' u'may post messages to any group you are a member of: '), colors['text'](u', '.join(quote(grp, colors) for grp in groups)), colors['text'](u'.'))) description = u''.join(( u'\r\n', colors['text']( u'You can think of tags as a system of providing context to any ' u'message stored on this system. A tag might provide the ' u'general label of the topic of conversation, which may be ' u'subscribed to. For example, '), quote(u'python', colors), colors['text']( u' may be used for topics related to the python programming ' u'language. This is similar to flicker or gmail tags, or ' u'hashtags. Public messages are always tagged '), quote(u'public', colors), colors['text'](u'. '), get_network_tag_description(term, colors), u'\r\n\r\n', colors['text'](u'Furthermore, glob expressions may be used such as '), quote(u'*', colors), u' ', colors['text']('for all messages, or expression '), quote(u'lang-*', colors), u' ', colors['text']('might subscribe to both '), quote(u'lang-python', colors), colors['text'](u' and '), quote(u'lang-go', colors), colors['text'](u'.'), describe_group_tags(), )) return show_description(term, description, color=None)
def main(quick=False): """ Main procedure. """ session, term = getsession(), getterminal() session.activity = 'checking for new messages' # set syncterm font, if any if term.kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) colors = dict(highlight=lambda txt: txt, lowlight=lambda txt: txt, backlight=lambda txt: txt, text=lambda txt: txt) if not colored_menu_items else dict( highlight=getattr(term, color_highlight), lowlight=getattr(term, color_lowlight), backlight=getattr(term, color_backlight), text=getattr(term, color_text)) yloc = top_margin = 0 subscription = session.user.get('msg_subscription', []) dirty = 2 while True: if dirty == 2: # display header art, yloc = display_banner(art_file, encoding=art_encoding, center=True) xloc = max(0, (term.width // 2) - 40) echo(u'\r\n') top_margin = yloc = (yloc + 1) elif dirty: echo(term.move(top_margin, 0) + term.normal + term.clear_eos) echo(term.move(top_margin, xloc)) if dirty: if not subscription: # prompt the user for a tag subscription, and loop # back again when completed to re-draw and show new messages. subscription = session.user['msg_subscription'] = ( prompt_subscription(session=session, term=term, yloc=top_margin, subscription=subscription, colors=colors)) continue messages, messages_bytags = get_messages_by_subscription( session, subscription) # When quick login ('y') selected in top.py, return immediately # when no new messages are matched any longer. if quick and not messages['new']: echo(term.move_x(xloc) + u'\r\nNo new messages.\r\n') return waitprompt(term) txt = describe_message_area(term=term, subscription=subscription, messages_bytags=messages_bytags, colors=colors) yloc = top_margin + show_description( term=term, description=txt, color=None, subsequent_indent=' ' * len('message area: ')) echo( render_menu_entries(term=term, top_margin=yloc, menu_items=get_menu(messages), colors=colors, max_cols=2)) echo(display_prompt(term=term, colors=colors)) echo(colors['backlight'](u' \b')) dirty = False event, data = session.read_events(('refresh', 'newmsg', 'input')) if event == 'refresh': # screen resized, redraw. dirty = 2 continue elif event == 'newmsg': # When a new message is sent, 'newmsg' event is broadcasted. session.flush_event('newmsg') nxt_msgs, nxt_bytags = get_messages_by_subscription( session, subscription) if nxt_msgs['new'] - messages['new']: # beep and re-display when a new message has arrived. echo(u'\b') messages, messages_bytags = nxt_msgs, nxt_bytags dirty = True continue elif event == 'input': # on input, block until carriage return session.buffer_input(data, pushback=True) given_inp = LineEditor(1, colors={ 'highlight': colors['backlight'] }).read() if given_inp is None: # escape/cancel continue inp = given_inp.strip() if inp.lower() in (u'n', 'a', 'v'): # read new/all/private messages message_indices = sorted( list({ 'n': messages['new'], 'a': messages['all'], 'v': messages['private'], }[inp.lower()])) if message_indices: dirty = 2 read_messages(session=session, term=term, message_indices=message_indices, colors=colors) elif inp.lower() == u'm' and messages['new']: # mark all messages as read dirty = 1 do_mark_as_read(session, messages['new']) elif inp.lower() in (u'p', u'w'): # write new public/private message dirty = 2 public = bool(inp.lower() == u'p') msg = Msg() if (not prompt_recipient( term=term, msg=msg, colors=colors, public=public) or not prompt_subject(term=term, msg=msg, colors=colors) or not prompt_body(term=term, msg=msg, colors=colors) or not prompt_tags(session=session, term=term, msg=msg, colors=colors, public=public)): continue do_send_message(session=session, term=term, msg=msg, colors=colors) elif inp.lower() == u'c': # prompt for new tag subscription (at next loop) subscription = [] dirty = 1 elif inp.lower() == u'?': # help echo(term.move(top_margin, 0) + term.clear_eos) do_describe_message_system(term, colors) waitprompt(term) dirty = 2 elif inp.lower() == u'q': return if given_inp: # clear out line editor prompt echo(colors['backlight'](u'\b \b'))
def prompt_subscription(session, term, yloc, subscription, colors): """ This function is called to assign a new set of subscription tags for a user. If escape is pressed, the existing value is used, or '*' is used if not previously set. This should be called for first-time users, and optionally at any later time to change a subscription. """ if session.user.get('msg_subscription', None) is None: # force-display introductory description for first-time users. yloc += do_describe_message_system(term, colors) echo(u'\r\n\r\n') yloc += 2 # remind ourselves of all available tags yloc += do_describe_available_tags(term, colors) + 2 # for small screens, scroll and leave room for prompt & errors if yloc > term.height + 3: echo(u'\r\n' * 3) yloc = term.height - 3 # and prompt for setting of message tags xloc = max(0, (term.width // 2) - 40) input_prefix = u':: subscription tags:' echo(u''.join((term.move(yloc, xloc), input_prefix))) xloc += len(input_prefix) wide = min(40, (term.width - xloc - 2)) while True: editor = ScrollingEditor(xloc=xloc, yloc=yloc - 1, width=wide, colors={'highlight': colors['backlight']}, content=u', '.join(subscription), max_length=100) # Prompt for and evaluate the given input, splitting by comma, # removing any empty items, and defaulting to ['*'] on escape. inp = editor.read() or u'' subscription = filter(None, set(map(unicode.strip, inp.split(',')))) or set([u'*']) # Then, reduce to only validate tag patterns, tracking those # that do not match any known tags, and display a warning and # re-prompt if any are removed. removed, subscription = validate_tag_patterns(subscription) # clear existing warning, if any echo(u''.join((term.normal, u'\r\n\r\n', term.clear_eos))) if removed: # and display any unmatched tags as a warning, re-prompt txt = ''.join( (term.bold_red(u"The following patterns are not matched: "), u', '.join(removed))) show_description(term, txt, color=None) continue # otherwise everything is fine, # return new subscription set return subscription
def main(quick=False): """ Main procedure. """ session, term = getsession(), getterminal() session.activity = 'checking for new messages' # set syncterm font, if any if term.kind.startswith('ansi'): echo(syncterm_setfont(syncterm_font)) colors = dict( highlight=lambda txt: txt, lowlight=lambda txt: txt, backlight=lambda txt: txt, text=lambda txt: txt ) if not colored_menu_items else dict( highlight=getattr(term, color_highlight), lowlight=getattr(term, color_lowlight), backlight=getattr(term, color_backlight), text=getattr(term, color_text)) yloc = top_margin = 0 subscription = session.user.get('msg_subscription', []) dirty = 2 while True: if dirty == 2: # display header art, yloc = display_banner(art_file, encoding=art_encoding, center=True) xloc = max(0, (term.width // 2) - 40) echo(u'\r\n') top_margin = yloc = (yloc + 1) elif dirty: echo(term.move(top_margin, 0) + term.normal + term.clear_eos) echo(term.move(top_margin, xloc)) if dirty: if not subscription: # prompt the user for a tag subscription, and loop # back again when completed to re-draw and show new messages. subscription = session.user['msg_subscription'] = ( prompt_subscription( session=session, term=term, yloc=top_margin, subscription=subscription, colors=colors)) continue messages, messages_bytags = get_messages_by_subscription( session, subscription) # When quick login ('y') selected in top.py, return immediately # when no new messages are matched any longer. if quick and not messages['new']: echo(term.move_x(xloc) + u'\r\nNo new messages.\r\n') return waitprompt(term) txt = describe_message_area( term=term, subscription=subscription, messages_bytags=messages_bytags, colors=colors) yloc = top_margin + show_description( term=term, description=txt, color=None, subsequent_indent=' ' * len('message area: ')) echo(render_menu_entries( term=term, top_margin=yloc, menu_items=get_menu(messages), colors=colors, max_cols=2)) echo(display_prompt(term=term, colors=colors)) echo(colors['backlight'](u' \b')) dirty = False event, data = session.read_events(('refresh', 'newmsg', 'input')) if event == 'refresh': # screen resized, redraw. dirty = 2 continue elif event == 'newmsg': # When a new message is sent, 'newmsg' event is broadcasted. session.flush_event('newmsg') nxt_msgs, nxt_bytags = get_messages_by_subscription( session, subscription) if nxt_msgs['new'] - messages['new']: # beep and re-display when a new message has arrived. echo(u'\b') messages, messages_bytags = nxt_msgs, nxt_bytags dirty = True continue elif event == 'input': # on input, block until carriage return session.buffer_input(data, pushback=True) given_inp = LineEditor( 1, colors={'highlight': colors['backlight']} ).read() if given_inp is None: # escape/cancel continue inp = given_inp.strip() if inp.lower() in (u'n', 'a', 'v'): # read new/all/private messages message_indices = sorted(list( {'n': messages['new'], 'a': messages['all'], 'v': messages['private'], }[inp.lower()])) if message_indices: dirty = 2 read_messages(session=session, term=term, message_indices=message_indices, colors=colors) elif inp.lower() == u'm' and messages['new']: # mark all messages as read dirty = 1 do_mark_as_read(session, messages['new']) elif inp.lower() in (u'p', u'w'): # write new public/private message dirty = 2 public = bool(inp.lower() == u'p') msg = Msg() if ( not prompt_recipient( term=term, msg=msg, colors=colors, public=public ) or not prompt_subject( term=term, msg=msg, colors=colors ) or not prompt_body( term=term, msg=msg, colors=colors ) or not prompt_tags( session=session, term=term, msg=msg, colors=colors, public=public )): continue do_send_message(session=session, term=term, msg=msg, colors=colors) elif inp.lower() == u'c': # prompt for new tag subscription (at next loop) subscription = [] dirty = 1 elif inp.lower() == u'?': # help echo(term.move(top_margin, 0) + term.clear_eos) do_describe_message_system(term, colors) waitprompt(term) dirty = 1 elif inp.lower() == u'q': return if given_inp: # clear out line editor prompt echo(colors['backlight'](u'\b \b'))
def prompt_subscription(session, term, yloc, subscription, colors): """ This function is called to assign a new set of subscription tags for a user. If escape is pressed, the existing value is used, or '*' is used if not previously set. This should be called for first-time users, and optionally at any later time to change a subscription. """ if session.user.get('msg_subscription', None) is None: # force-display introductory description for first-time users. yloc += do_describe_message_system(term, colors) echo(u'\r\n\r\n') yloc += 2 # remind ourselves of all available tags yloc += do_describe_available_tags(term, colors) + 2 # for small screens, scroll and leave room for prompt & errors if yloc > term.height + 3: echo(u'\r\n' * 3) yloc = term.height - 3 # and prompt for setting of message tags xloc = max(0, (term.width // 2) - 40) input_prefix = u':: subscription tags:' echo(u''.join((term.move(yloc, xloc), input_prefix))) xloc += len(input_prefix) wide = min(40, (term.width - xloc - 2)) while True: editor = ScrollingEditor(xloc=xloc, yloc=yloc - 1, width=wide, colors={'highlight': colors['backlight']}, content=u', '.join(subscription), max_length=100) # Prompt for and evaluate the given input, splitting by comma, # removing any empty items, and defaulting to ['*'] on escape. inp = editor.read() or u'' subscription = filter(None, set(map(unicode.strip, inp.split(','))) ) or set([u'*']) # Then, reduce to only validate tag patterns, tracking those # that do not match any known tags, and display a warning and # re-prompt if any are removed. removed, subscription = validate_tag_patterns(subscription) # clear existing warning, if any echo(u''.join((term.normal, u'\r\n\r\n', term.clear_eos))) if removed: # and display any unmatched tags as a warning, re-prompt txt = ''.join(( term.bold_red(u"The following patterns are not matched: "), u', '.join(removed))) show_description(term, txt, color=None) continue # otherwise everything is fine, # return new subscription set return subscription