Files
entropy/server/eit/commands/notice.py

395 lines
12 KiB
Python

# -*- coding: utf-8 -*-
"""
@author: Fabio Erculiani <lxnay@sabayon.org>
@contact: lxnay@sabayon.org
@copyright: Fabio Erculiani
@license: GPL-2
B{Entropy Infrastructure Toolkit}.
"""
import sys
import os
import errno
import argparse
import functools
import tempfile
import codecs
from entropy.i18n import _
from entropy.output import blue, darkred, darkgreen, purple, brown, teal
from eit.commands.descriptor import EitCommandDescriptor
from eit.commands.command import EitCommand
class EitNotice(EitCommand):
"""
Main Eit notice command.
"""
NAME = "notice"
ALIASES = []
def __init__(self, args):
EitCommand.__init__(self, args)
self._repository_id = None
def _get_parser(self):
""" Overridden from EitCommand """
descriptor = EitCommandDescriptor.obtain_descriptor(
EitNotice.NAME)
parser = argparse.ArgumentParser(
description=descriptor.get_description(),
formatter_class=argparse.RawDescriptionHelpFormatter,
prog="%s %s" % (sys.argv[0], EitNotice.NAME))
subparsers = parser.add_subparsers(
title="action", description=_("execute given action"),
help=_("available actions"))
add_parser = subparsers.add_parser("add",
help=_("add notice-board entry"))
add_parser.add_argument("repo", nargs='?', default=None,
metavar="<repo>", help=_("repository"))
add_parser.set_defaults(func=self._add)
remove_parser = subparsers.add_parser("remove",
help=_("remove notice-board entry"))
remove_parser.add_argument("repo", nargs='?', default=None,
metavar="<repo>", help=_("repository"))
remove_parser.set_defaults(func=self._remove)
show_parser = subparsers.add_parser("show",
help=_("show notice-board"))
show_parser.add_argument("repo", nargs='?', default=None,
metavar="<repo>", help=_("repository"))
show_parser.set_defaults(func=self._show)
return parser
INTRODUCTION = """\
Notice-board is the way to communicate news or other misc info to your users
through repositories. The repository notice-board is downloaded (if available)
whenever the user updates the local repositories. The Entropy Client is going to
list notice-board titles for user consumption.
"""
def man(self):
"""
Overridden from EitCommand.
"""
return self._man()
def parse(self):
parser = self._get_parser()
try:
nsargs = parser.parse_args(self._args)
except IOError:
return functools.partial(self.print_help, parser), []
self._repository_id = nsargs.repo
return self._call_locked, [nsargs.func, self._repository_id]
def _add_notice_editor(self, entropy_server, repository_id):
"""
Open $EDITOR and let user write his notice-board entry.
"""
notice_text = """
# Please enter the notice-board text above as follows:
# - the first line is considered the title
# - the following lines are considered the actual message
# body of the notice-board entry
# - the last line, if starting with URL: will be parsed as the
# actual notice-board entry URL
# - any line starting with "#" will be removed
#
# Example:
# [message title]
# [message
# body]
# URL: http://<url>
"""
notice_title = None
notice_body = ""
notice_url = None
tmp_path = None
while True:
if tmp_path is None:
tmp_fd, tmp_path = tempfile.mkstemp(
prefix = 'entropy.server',
suffix = ".conf")
with os.fdopen(tmp_fd, "w") as tmp_f:
tmp_f.write(notice_text)
tmp_f.flush()
success = entropy_server.edit_file(tmp_path)
if not success:
# retry ?
os.remove(tmp_path)
tmp_path = None
continue
notice_title = None
notice_body = ""
notice_url = None
all_good = False
with codecs.open(tmp_path, "r", encoding="utf-8") as tmp_f:
line = None
last_line = None
for line in tmp_f.readlines():
if line.startswith("#"):
# skip
continue
strip_line = line.strip()
if not strip_line:
# ignore
continue
if notice_title is None:
notice_title = strip_line
continue
last_line = line
notice_body += line
if last_line is not None:
if last_line.startswith("URL:"):
url = last_line.strip()[4:].strip()
if url:
notice_url = url
# drop last line then
notice_body = notice_body[:-len(last_line)]
if notice_title and notice_body:
all_good = True
if not all_good:
os.remove(tmp_path)
tmp_path = None
continue
# show submitted info
entropy_server.output(
"%s: %s" % (
darkgreen(_("Title")),
purple(notice_title)),
level="generic")
entropy_server.output("", level="generic")
entropy_server.output(
notice_body, level="generic")
url = notice_url
if url is None:
url = _("no URL")
entropy_server.output(
"%s: %s" % (teal(_("URL")), brown(url)),
level="generic")
entropy_server.output("", level="generic")
# ask confirmation
while True:
try:
rc_question = entropy_server.ask_question(
"[%s] %s" % (
purple(repository_id),
teal(_("Do you agree?"))
),
responses = (_("Yes"), _("Repeat"), _("No"),)
)
except KeyboardInterrupt:
# do not allow, we're in a critical region
continue
break
if rc_question == _("Yes"):
break
elif rc_question == _("No"):
return None, None, None
# otherwise repeat everything again
# keep tmp_path
if tmp_path is not None:
try:
os.remove(tmp_path)
except (OSError) as err:
if err.errno != errno.ENOENT:
raise
return notice_title, notice_body, notice_url
def _add(self, entropy_server):
"""
Actual Eit notice add code.
"""
if self._repository_id is None:
self._repository_id = entropy_server.repository()
notice_title, notice_body, notice_url = \
self._add_notice_editor(entropy_server, self._repository_id)
if (notice_title is None) or (notice_body is None):
return 1
status = entropy_server.Mirrors.update_notice_board(
self._repository_id, notice_title, notice_body,
link = notice_url)
if status:
return 0
return 1
def _remove(self, entropy_server):
"""
Actual Eit notice remove code.
"""
if self._repository_id is None:
self._repository_id = entropy_server.repository()
data = entropy_server.Mirrors.read_notice_board(
self._repository_id)
if data is None:
entropy_server.output(
purple(_("Notice board not available")),
importance=1, level="error")
return 1
items, counter = data
changed = False
while True:
try:
sel = self._show_notice_selector(
entropy_server,
darkgreen(_("Choose the one you want to remove")),
items)
except KeyboardInterrupt:
break
if (sel >= 0) and (sel <= counter):
self._show_notice(entropy_server, sel, items.get(sel))
q_rc = entropy_server.ask_question(
_("Are you sure you want to remove this?"))
if q_rc == _("Yes"):
changed = True
entropy_server.Mirrors.remove_from_notice_board(
self._repository_id, sel)
data = entropy_server.Mirrors.read_notice_board(
self._repository_id, do_download = False)
items, counter = data
elif sel == -1:
break
if changed or (counter == 0):
if counter == 0:
status = entropy_server.Mirrors.remove_notice_board(
self._repository_id)
else:
status = entropy_server.Mirrors.upload_notice_board(
self._repository_id)
if not status:
return 1
return 0
def _show(self, entropy_server):
"""
Actual Eit notice show code.
"""
if self._repository_id is None:
self._repository_id = entropy_server.repository()
data = entropy_server.Mirrors.read_notice_board(
self._repository_id)
if data is None:
entropy_server.output(
purple(_("Notice board not available")),
importance=1, level="error")
return 1
items, counter = data
while True:
try:
sel = self._show_notice_selector(entropy_server, '', items)
except KeyboardInterrupt:
return 0
if (sel >= 0) and (sel <= counter):
self._show_notice(entropy_server, sel, items.get(sel))
elif sel == -1:
return 0
return 0
def _show_notice(self, entropy_server, key, mydict):
"""
Print notice board entry content
"""
mytxt = "[%s] [%s]" % (
blue(str(key)),
brown(mydict['pubDate']),
)
entropy_server.output(mytxt)
entropy_server.output("", level="generic")
entropy_server.output(
"%s: %s" % (darkgreen(_("Title")),
purple(mydict['title'])),
level="generic")
entropy_server.output("", level="generic")
entropy_server.output(mydict['description'], level="generic")
entropy_server.output("", level="generic")
mytxt = "%s: %s" % (
darkgreen(_("URL")),
blue(mydict['link']),
)
entropy_server.output(mytxt)
def fake_callback(dummy_s):
return True
input_params = [
('idx', _('Press Enter to continue'), fake_callback, False)
]
data = entropy_server.input_box(
'', input_params, cancel_button = True)
def _show_notice_selector(self, entropy_server, title, mydict):
"""
Show notice board entries selector, return selected entry id.
"""
entropy_server.output("")
mykeys = sorted(mydict.keys())
for key in mykeys:
mydata = mydict.get(key)
mytxt = "[%s] [%s] %s: %s" % (
blue(str(key)),
brown(mydata['pubDate']),
_("Title"),
darkred(mydata['title']),
)
entropy_server.output(mytxt)
entropy_server.output("")
mytxt = "[%s] %s" % (
blue("-1"),
darkred(_("Exit/Commit")),
)
entropy_server.output(mytxt)
def fake_callback(s):
return s
input_params = [
('id', blue(_('Choose one by typing its identifier')),
fake_callback, False)]
data = entropy_server.input_box(title, input_params,
cancel_button = True)
if not isinstance(data, dict):
return -1
try:
return int(data['id'])
except ValueError:
return -2
EitCommandDescriptor.register(
EitCommandDescriptor(
EitNotice,
EitNotice.NAME,
_('manage repository notice-board'))
)