jp: pubsub options refactoring:

There is now only "use_pubsub", and specification are set using "pubsub_flags" argument when instantiating CommandBase.
Options are more Python Zen compliant by using explicit arguments for item, draft, url instead of trying to guess with magic keyword and type detection.
Pubsub node and item are now always using respecively "-n" and "-i" even when required, this way shell history can be used to change command more easily, and it's globally less confusing for user.
if --pubsub-url is used, elements can be overwritten with individual option (e.g. change item id with --item).
New "use_draft" argument in CommandBase, to re-use current draft or open a file path as draft.
Item can now be specified when using a draft. If it already exists, its content will be added to current draft (with a separator), to avoid loosing data.
common.BaseEdit.getItemPath could be simplified thanks to those changes.
Pubsub URI handling has been moved to base.py.
This commit is contained in:
Goffi 2018-03-21 19:13:22 +01:00
commit 6f8834f9da
8 changed files with 268 additions and 296 deletions

View file

@ -34,9 +34,11 @@ from importlib import import_module
from sat_frontends.tools.jid import JID
from sat.tools import config
from sat.tools.common import dynamic_import
from sat.tools.common import uri
from sat.core import exceptions
import sat_frontends.jp
from sat_frontends.jp.constants import Const as C
from sat_frontends.tools import misc
import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI
import shlex
from collections import OrderedDict
@ -316,14 +318,46 @@ class Jp(object):
verbose_parent = self.parents['verbose'] = argparse.ArgumentParser(add_help=False)
verbose_parent.add_argument('--verbose', '-v', action='count', default=0, help=_(u"Add a verbosity level (can be used multiple times)"))
for parent_name in ('pubsub', 'pubsub_node_req'):
parent = self.parents[parent_name] = argparse.ArgumentParser(add_help=False)
parent.add_argument("-s", "--service", type=unicode_decoder, default=u'',
help=_(u"JID of the PubSub service (default: PEP service)"))
if parent_name == 'pubsub_node_req':
parent.add_argument("node", type=unicode_decoder, help=_(u"node to request"))
else:
parent.add_argument("-n", "--node", type=unicode_decoder, default=u'', help=_(u"node to request"))
draft_parent = self.parents['draft'] = argparse.ArgumentParser(add_help=False)
draft_group = draft_parent.add_argument_group(_('draft handling'))
draft_group.add_argument("-D", "--current", action="store_true", help=_(u"load current draft"))
draft_group.add_argument("-F", "--draft-path", type=unicode_decoder, help=_(u"path to a draft file to retrieve"))
def make_pubsub_group(self, flags):
"""generate pubsub options according to flags
@param flags(iterable[unicode]): see [CommandBase.__init__]
@return (ArgumentParser): parser to add
"""
flags = misc.FlagsHandler(flags)
parent = argparse.ArgumentParser(add_help=False)
pubsub_group = parent.add_argument_group('pubsub')
pubsub_group.add_argument("-u", "--pubsub-url", type=unicode_decoder,
help=_(u"Pubsub URL (xmpp or http)"))
service_help = _(u"JID of the PubSub service")
if not flags.service:
service_help += _(u" (default: PEP service)")
pubsub_group.add_argument("-s", "--service", type=unicode_decoder, default=u'',
help=service_help)
node_help = _(u"node to request")
if not flags.node:
node_help += _(u" (DEFAULT: standard node)")
pubsub_group.add_argument("-n", "--node", type=unicode_decoder, default=u'', help=node_help)
if flags.single_item:
pubsub_group.add_argument("-i", "--item", type=unicode_decoder, help=_(u"item to retrieve"))
pubsub_group.add_argument("-L", "--last-item", action='store_true', help=_(u'retrieve last item'))
elif flags.multi_items:
# mutiple items
pubsub_group.add_argument("-i", "--item", type=unicode_decoder, action='append', dest='items', default=[], help=_(u"items to retrieve (DEFAULT: all)"))
if flags:
raise exceptions.InternalError('unknowns flags: {flags}'.format(flags=u', '.join(flags)))
return parent
def add_parser_options(self):
self.parser.add_argument('--version', action='version', version=("%(name)s %(version)s %(copyleft)s" % {'name': PROG_NAME, 'version': self.version, 'copyleft': COPYLEFT}))
@ -402,10 +436,89 @@ class Jp(object):
cls = getattr(module, class_name)
cls(self)
def get_xmpp_uri_from_http(self, http_url):
"""parse HTML page at http(s) URL, and looks for xmpp: uri"""
if http_url.startswith('https'):
scheme = u'https'
elif http_url.startswith('http'):
scheme = u'http'
else:
raise exceptions.InternalError(u'An HTTP scheme is expected in this method')
self.disp(u"{scheme} URL found, trying to find associated xmpp: URI".format(scheme=scheme.upper()),1)
# HTTP URL, we try to find xmpp: links
try:
from lxml import etree
except ImportError:
self.disp(u"lxml module must be installed to use http(s) scheme, please install it with \"pip install lxml\"", error=True)
self.host.quit(1)
import urllib2
parser = etree.HTMLParser()
try:
root = etree.parse(urllib2.urlopen(http_url), parser)
except etree.XMLSyntaxError as e:
self.disp(_(u"Can't parse HTML page : {msg}").format(msg=e))
links = []
else:
links = root.xpath("//link[@rel='alternate' and starts-with(@href, 'xmpp:')]")
if not links:
self.disp(u'Could not find alternate "xmpp:" URI, can\'t find associated XMPP PubSub node/item', error=True)
self.host.quit(1)
xmpp_uri = links[0].get('href')
return xmpp_uri
def parse_pubsub_args(self):
if self.args.pubsub_url is not None:
url = self.args.pubsub_url
if url.startswith('http'):
# http(s) URL, we try to retrieve xmpp one from there
url = self.get_xmpp_uri_from_http(url)
try:
uri_data = uri.parseXMPPUri(url)
except ValueError:
self.parser.error(_(u'invalid XMPP URL: {url}').format(url=url))
else:
if uri_data[u'type'] == 'pubsub':
# URL is alright, we only set data not already set by other options
if not self.args.service:
self.args.service = uri_data[u'path']
if not self.args.node:
self.args.node = uri_data[u'node']
uri_item = uri_data.get(u'item')
if uri_item:
try:
item, item_magic = self.args.item, self.args.item_magic
except AttributeError:
if not self.args.items:
self.args.items = [uri_item]
else:
if not item and not item_magic:
self.args.item = uri_item
else:
self.parser.error(_(u'XMPP URL is not a pubsub one: {url}').format(url=url))
flags = self.args._cmd._pubsub_flags
# we check required arguments here instead of using add_arguments' required option
# because the required argument can be set in URL
if C.SERVICE in flags and not self.args.service:
self.parser.error(_(u"argument -s/--service is required"))
if C.NODE in flags and not self.args.node:
self.parser.error(_(u"argument -n/--node is required"))
# FIXME: mutually groups can't be nested in a group and don't support title
# so we check conflict here. This may be fixed in Python 3, to be checked
try:
if self.args.item and self.args.item_last:
self.parser.error(_(u"--item and --item-last can't be used at the same time"))
except AttributeError:
pass
def run(self, args=None, namespace=None):
self.args = self.parser.parse_args(args, namespace=None)
if self.args._cmd._use_pubsub:
self.parse_pubsub_args()
try:
self.args.func()
self.args._cmd.run()
if self._need_loop or self._auto_loop:
self._start_loop()
except KeyboardInterrupt:
@ -582,6 +695,14 @@ class CommandBase(object):
- use_progress(bool): if True, add progress bar activation option
progress* signals will be handled
- use_verbose(bool): if True, add verbosity option
- use_pubsub(bool): if True, add pubsub options
mandatory arguments are controlled by pubsub_req
- use_draft(bool): if True, add draft handling options
** other arguments **
- pubsub_flags(iterable[unicode]): tuple of flags to set pubsub options, can be:
C.SERVICE: service is required
C.NODE: node is required
C.SINGLE_ITEM: only one item is allowed
@attribute need_loop(bool): to set by commands when loop is needed
"""
self.need_loop = False # to be set by commands when loop is needed
@ -632,9 +753,11 @@ class CommandBase(object):
else:
assert extra_outputs is None
if 'use_pubsub' in kwargs and 'use_pubsub_node_req' in kwargs:
raise exceptions.InternalError(u"use_pubsub and use_pubsub_node_req can't be used at the same time."
u"Use the later one when node is required (else an empty string is used as default)")
self._use_pubsub = kwargs.pop('use_pubsub', False)
if self._use_pubsub:
flags = kwargs.pop('pubsub_flags', None)
parents.add(self.host.make_pubsub_group(flags))
self._pubsub_flags = flags
# other common options
use_opts = {k:v for k,v in kwargs.iteritems() if k.startswith('use_')}
@ -650,7 +773,7 @@ class CommandBase(object):
if hasattr(self, "subcommands"):
self.subparsers = self.parser.add_subparsers()
else:
self.parser.set_defaults(func=self.run)
self.parser.set_defaults(_cmd=self)
self.add_parser_options()
@property

View file

@ -76,38 +76,28 @@ MB_KEYS = (u"id",
OUTPUT_OPT_NO_HEADER = u'no-header'
class BlogCommon(object):
def guessSyntaxFromPath(host, sat_conf, path):
"""Return syntax guessed according to filename extension
def __init__(self, host):
self.host = host
@param sat_conf(ConfigParser.ConfigParser): instance opened on sat configuration
@param path(str): path to the content file
@return(unicode): syntax to use
"""
# we first try to guess syntax with extension
ext = os.path.splitext(path)[1][1:] # we get extension without the '.'
if ext:
for k,v in SYNTAX_EXT.iteritems():
if k and ext == v:
return k
def addServiceNodeArgs(self):
self.parser.add_argument("-n", "--node", type=base.unicode_decoder, default=u'', help=_(u"PubSub node to request (default: microblog namespace)"))
self.parser.add_argument("-s", "--service", type=base.unicode_decoder, default=u'', help=_(u"JID of the PubSub service (default: request profile own blog)"))
def guessSyntaxFromPath(self, sat_conf, path):
"""Return syntax guessed according to filename extension
@param sat_conf(ConfigParser.ConfigParser): instance opened on sat configuration
@param path(str): path to the content file
@return(unicode): syntax to use
"""
# we first try to guess syntax with extension
ext = os.path.splitext(path)[1][1:] # we get extension without the '.'
if ext:
for k,v in SYNTAX_EXT.iteritems():
if k and ext == v:
return k
# if not found, we use current syntax
return self.host.bridge.getParamA("Syntax", "Composition", "value", self.profile)
# if not found, we use current syntax
return host.bridge.getParamA("Syntax", "Composition", "value", host.profile)
class BlogPublishCommon(object):
"""handle common option for publising commands (Set and Edit)"""
def add_parser_options(self):
self.addServiceNodeArgs()
self.parser.add_argument("-T", '--title', type=base.unicode_decoder, help=_(u"title of the item"))
self.parser.add_argument("-t", '--tag', type=base.unicode_decoder, action='append', help=_(u"tag (category) of your item"))
self.parser.add_argument("-C", "--comments", action='store_true', help=_(u"enable comments"))
@ -134,24 +124,22 @@ class BlogPublishCommon(object):
mb_data['title'] = self.args.title
class Set(base.CommandBase, BlogCommon, BlogPublishCommon):
class Set(base.CommandBase, BlogPublishCommon):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'set', help=_(u'publish a new blog item or update an existing one'))
BlogCommon.__init__(self, self.host)
base.CommandBase.__init__(self, host, 'set', use_pubsub=True, pubsub_flags={C.SINGLE_ITEM},
help=_(u'publish a new blog item or update an existing one'))
BlogPublishCommon.__init__(self)
self.need_loop=True
def add_parser_options(self):
BlogPublishCommon.add_parser_options(self)
self.parser.add_argument("item", type=base.unicode_decoder, nargs='?', default=None, help=_(u"id of the item to publish"))
def mbSendCb(self):
self.disp(u"Item published")
self.host.quit(C.EXIT_OK)
def start(self):
common.checkURI(self.args)
self.pubsub_item = self.args.item
mb_data = {}
self.setMbDataFromArgs(mb_data)
@ -169,20 +157,17 @@ class Set(base.CommandBase, BlogCommon, BlogPublishCommon):
exit_code=C.EXIT_BRIDGE_ERRBACK))
class Get(base.CommandBase, BlogCommon):
class Get(base.CommandBase):
TEMPLATE = u"blog/articles.html"
def __init__(self, host):
extra_outputs = {'default': self.default_output,
'fancy': self.fancy_output}
base.CommandBase.__init__(self, host, 'get', use_verbose=True, use_output=C.OUTPUT_COMPLEX, extra_outputs=extra_outputs, help=_(u'get blog item(s)'))
BlogCommon.__init__(self, self.host)
base.CommandBase.__init__(self, host, 'get', use_verbose=True, use_pubsub=True, pubsub_flags={C.MULTI_ITEMS},
use_output=C.OUTPUT_COMPLEX, extra_outputs=extra_outputs, help=_(u'get blog item(s)'))
self.need_loop=True
def add_parser_options(self):
self.addServiceNodeArgs()
self.parser.add_argument("-i", "--item", type=base.unicode_decoder, action='append', default=[], dest='items',
help=_(u"item(s) id(s) to get (default: request all items)"))
self.parser.add_argument("-m", "--max", type=int, default=10, help=_(u"maximum number of items to get ({} to get all items)".format(C.NO_LIMIT)))
# TODO: a key(s) argument to select keys to display
self.parser.add_argument("-k", "--key", type=base.unicode_decoder, action='append', dest='keys',
@ -339,7 +324,6 @@ class Get(base.CommandBase, BlogCommon):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.mbGet(
self.args.service,
self.args.node,
@ -351,19 +335,23 @@ class Get(base.CommandBase, BlogCommon):
errback=self.mbGetEb)
class Edit(base.CommandBase, BlogCommon, BlogPublishCommon, common.BaseEdit):
class Edit(base.CommandBase, BlogPublishCommon, common.BaseEdit):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'edit', use_verbose=True, help=_(u'edit an existing or new blog post'))
BlogCommon.__init__(self, self.host)
base.CommandBase.__init__(self, host, 'edit', use_pubsub=True, pubsub_flags={C.SINGLE_ITEM},
use_draft=True, use_verbose=True, help=_(u'edit an existing or new blog post'))
BlogPublishCommon.__init__(self)
common.BaseEdit.__init__(self, self.host, BLOG_TMP_DIR, use_metadata=True)
@property
def current_syntax(self):
if self._current_syntax is None:
self._current_syntax = self.host.bridge.getParamA("Syntax", "Composition", "value", self.profile)
return self._current_syntax
def add_parser_options(self):
BlogPublishCommon.add_parser_options(self)
self.parser.add_argument("-P", "--preview", action="store_true", help=_(u"launch a blog preview in parallel"))
self.parser.add_argument("item", type=base.unicode_decoder, nargs='?', default=u'new', help=_(u"URL of the item to edit, or keyword"))
common.BaseEdit.add_parser_options(self)
def buildMetadataFile(self, content_file_path, mb_data=None):
"""Build a metadata file using json
@ -436,8 +424,6 @@ class Edit(base.CommandBase, BlogCommon, BlogPublishCommon, common.BaseEdit):
def getTmpSuff(self):
# we get current syntax to determine file extension
if self.current_syntax is None:
self.current_syntax = self.host.bridge.getParamA("Syntax", "Composition", "value", self.profile)
return SYNTAX_EXT.get(self.current_syntax, SYNTAX_EXT[''])
def getItemData(self, service, node, item):
@ -466,26 +452,26 @@ class Edit(base.CommandBase, BlogCommon, BlogPublishCommon, common.BaseEdit):
def start(self):
# if there are user defined extension, we use them
SYNTAX_EXT.update(config.getConfig(self.sat_conf, 'jp', CONF_SYNTAX_EXT, {}))
self.current_syntax = self.args.syntax
if self.current_syntax is not None:
self._current_syntax = self.args.syntax
if self._current_syntax is not None:
try:
self.current_syntax = self.args.syntax = self.host.bridge.syntaxGet(self.current_syntax)
self._current_syntax = self.args.syntax = self.host.bridge.syntaxGet(self.current_syntax)
except Exception as e:
if "NotFound" in unicode(e): # FIXME: there is not good way to check bridge errors
self.parser.error(_(u"unknown syntax requested ({syntax})").format(syntax=self.args.syntax))
else:
raise e
self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path, content_file_obj, mb_data = self.getItemPath(self.args.item)
self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path, content_file_obj, mb_data = self.getItemPath()
self.edit(content_file_path, content_file_obj, mb_data=mb_data)
class Preview(base.CommandBase, BlogCommon):
class Preview(base.CommandBase):
# TODO: need to be rewritten with template output
def __init__(self, host):
base.CommandBase.__init__(self, host, 'preview', use_verbose=True, help=_(u'preview a blog content'))
BlogCommon.__init__(self, self.host)
def add_parser_options(self):
self.parser.add_argument("--inotify", type=str, choices=('auto', 'true', 'false'), default=u'auto', help=_(u"use inotify to handle preview"))
@ -626,7 +612,7 @@ class Preview(base.CommandBase, BlogCommon):
class Import(base.CommandAnswering):
def __init__(self, host):
super(Import, self).__init__(host, 'import', use_progress=True, help=_(u'import an external blog'))
super(Import, self).__init__(host, 'import', use_pubsub=True, use_progress=True, help=_(u'import an external blog'))
self.need_loop=True
def add_parser_options(self):
@ -637,10 +623,6 @@ class Import(base.CommandAnswering):
self.parser.add_argument("--ignore-tls-errors", action="store_true", help=_("ignore invalide TLS certificate for uploads"))
self.parser.add_argument('-o', '--option', action='append', nargs=2, default=[], metavar=(u'NAME', u'VALUE'),
help=_(u"importer specific options (see importer description)"))
self.parser.add_argument('--service', type=base.unicode_decoder, default=u'', metavar=u'PUBSUB_SERVICE',
help=_(u"PubSub service where the items must be uploaded (default: server)"))
self.parser.add_argument('-n', '--node', type=base.unicode_decoder, default=u'', metavar=u'PUBSUB_NODE',
help=_(u"PubSub node where the items must be uploaded (default: tickets' defaults)"))
self.parser.add_argument("location", type=base.unicode_decoder, nargs='?',
help=_(u"importer data location (see importer description), nothing to show importer description"))

View file

@ -42,13 +42,13 @@ class Get(base.CommandBase):
host,
'get',
use_output=C.OUTPUT_DICT,
use_pubsub_node_req=True,
use_pubsub=True, pubsub_flags={C.NODE, C.SINGLE_ITEM},
use_verbose=True,
help=_(u'get event data'))
self.need_loop=True
def add_parser_options(self):
self.parser.add_argument("-i", "--item", type=base.unicode_decoder, default=u'', help=_(u"ID of the PubSub Item"))
pass
def eventInviteeGetCb(self, result):
event_date, event_data = result
@ -57,7 +57,6 @@ class Get(base.CommandBase):
self.host.quit()
def start(self):
common.checkURI(self.args)
self.host.bridge.eventGet(
self.args.service,
self.args.node,
@ -100,7 +99,7 @@ class EventBase(object):
class Create(EventBase, base.CommandBase):
def __init__(self, host):
super(Create, self).__init__(host, 'create', use_pubsub_node_req=True, help=_('create or replace event'))
super(Create, self).__init__(host, 'create', use_pubsub=True, pubsub_flags={C.NODE}, help=_('create or replace event'))
EventBase.__init__(self)
self.need_loop=True
@ -126,12 +125,11 @@ class Create(EventBase, base.CommandBase):
class Modify(EventBase, base.CommandBase):
def __init__(self, host):
super(Modify, self).__init__(host, 'modify', use_pubsub_node_req=True, help=_('modify an existing event'))
super(Modify, self).__init__(host, 'modify', use_pubsub=True, pubsub_flags={C.NODE}, help=_('modify an existing event'))
EventBase.__init__(self)
self.need_loop=True
def start(self):
common.checkURI(self.args)
fields = self.parseFields()
date = 0 if not self.args.date else self.parseDate()
self.host.bridge.eventModify(
@ -154,7 +152,7 @@ class InviteeGet(base.CommandBase):
host,
'get',
use_output=C.OUTPUT_DICT,
use_pubsub_node_req=True,
use_pubsub=True, pubsub_flags={C.NODE},
use_verbose=True,
help=_(u'get event attendance'))
self.need_loop=True
@ -167,7 +165,6 @@ class InviteeGet(base.CommandBase):
self.host.quit()
def start(self):
common.checkURI(self.args)
self.host.bridge.eventInviteeGet(
self.args.service,
self.args.node,
@ -180,7 +177,7 @@ class InviteeGet(base.CommandBase):
class InviteeSet(base.CommandBase):
def __init__(self, host):
super(InviteeSet, self).__init__(host, 'set', use_output=C.OUTPUT_DICT, use_pubsub_node_req=True, help=_('set event attendance'))
super(InviteeSet, self).__init__(host, 'set', use_output=C.OUTPUT_DICT, use_pubsub=True, pubsub_flags={C.NODE}, help=_('set event attendance'))
self.need_loop=True
def add_parser_options(self):
@ -188,7 +185,6 @@ class InviteeSet(base.CommandBase):
metavar=(u"KEY", u"VALUE"), help=_(u"configuration field to set"))
def start(self):
common.checkURI(self.args)
fields = dict(self.args.fields) if self.args.fields else {}
self.host.bridge.eventInviteeSet(
self.args.service,
@ -210,7 +206,7 @@ class InviteesList(base.CommandBase):
'list',
use_output=C.OUTPUT_DICT_DICT,
extra_outputs=extra_outputs,
use_pubsub_node_req=True,
use_pubsub=True, pubsub_flags={C.NODE},
use_verbose=True,
help=_(u'get event attendance'))
self.need_loop=True
@ -355,7 +351,6 @@ class InviteesList(base.CommandBase):
def start(self):
if self.args.no_rsvp and not self.args.missing:
self.parser.error(_(u"you need to use --missing if you use --no-rsvp"))
common.checkURI(self.args)
if self.args.missing:
self.host.bridge.psNodeAffiliationsGet(
self.args.service,
@ -372,21 +367,19 @@ class InviteesList(base.CommandBase):
class InviteeInvite(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'invite', use_pubsub_node_req=True, help=_(u'invite someone to the event through email'))
base.CommandBase.__init__(self, host, 'invite', use_pubsub=True, pubsub_flags={C.NODE, C.SINGLE_ITEM}, help=_(u'invite someone to the event through email'))
self.need_loop=True
def add_parser_options(self):
self.parser.add_argument("-i", "--item", type=base.unicode_decoder, default=u'', help=_(u"ID of the PubSub Item"))
self.parser.add_argument("-e", "--email", action="append", type=base.unicode_decoder, default=[], help='email(s) to send the invitation to')
self.parser.add_argument("-n", "--name", type=base.unicode_decoder, default='', help='name of the invitee')
self.parser.add_argument("-N", "--host-name", type=base.unicode_decoder, default='', help='name of the host')
self.parser.add_argument("-N", "--name", type=base.unicode_decoder, default='', help='name of the invitee')
self.parser.add_argument("-H", "--host-name", type=base.unicode_decoder, default='', help='name of the host')
self.parser.add_argument("-l", "--lang", type=base.unicode_decoder, default='', help='main language spoken by the invitee')
self.parser.add_argument("-u", "--url", type=base.unicode_decoder, default='', help='template to construct the URL')
self.parser.add_argument("-U", "--url-template", type=base.unicode_decoder, default='', help='template to construct the URL')
self.parser.add_argument("-S", "--subject", type=base.unicode_decoder, default='', help='subject of the invitation email (default: generic subject)')
self.parser.add_argument("-b", "--body", type=base.unicode_decoder, default='', help='body of the invitation email (default: generic body)')
def start(self):
common.checkURI(self.args)
email = self.args.email[0] if self.args.email else None
emails_extra = self.args.email[1:]
@ -399,7 +392,7 @@ class InviteeInvite(base.CommandBase):
self.args.name,
self.args.host_name,
self.args.lang,
self.args.url,
self.args.url_template,
self.args.subject,
self.args.body,
self.args.profile,

View file

@ -35,12 +35,11 @@ class Edit(base.CommandBase, common.BaseEdit):
use_items=False
def __init__(self, host):
base.CommandBase.__init__(self, host, 'edit', use_pubsub=True, use_verbose=True, help=_(u'edit forums'))
base.CommandBase.__init__(self, host, 'edit', use_pubsub=True, use_draft=True, use_verbose=True, help=_(u'edit forums'))
common.BaseEdit.__init__(self, self.host, FORUMS_TMP_DIR)
self.need_loop=True
def add_parser_options(self):
common.BaseEdit.add_parser_options(self)
self.parser.add_argument("-k", "--key", type=base.unicode_decoder, default=u'',
help=_(u"forum key (DEFAULT: default forums)"))
@ -84,7 +83,6 @@ class Edit(base.CommandBase, common.BaseEdit):
exit_code=C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.forumsGet(
self.args.service,
self.args.node,
@ -146,7 +144,6 @@ class Get(base.CommandBase):
self.host.quit()
def start(self):
common.checkURI(self.args)
self.host.bridge.forumsGet(
self.args.service,
self.args.node,

View file

@ -20,7 +20,6 @@
import base
from sat.core.i18n import _
from sat_frontends.jp import common
from sat_frontends.jp.constants import Const as C
from functools import partial
import os.path
@ -46,7 +45,6 @@ class Set(base.CommandBase):
self.host.quit(C.EXIT_OK)
def start(self):
common.checkURI(self.args)
repository = os.path.expanduser(os.path.abspath(self.args.repository))
extra = {'update': 'true'} if self.args.item else {}
self.host.bridge.mergeRequestSet(

View file

@ -46,7 +46,7 @@ ALLOWED_SUBSCRIPTIONS_OWNER = ('subscribed', 'pending', 'none')
class NodeInfo(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'info', use_output=C.OUTPUT_DICT, use_pubsub_node_req=True, help=_(u'retrieve node configuration'))
base.CommandBase.__init__(self, host, 'info', use_output=C.OUTPUT_DICT, use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'retrieve node configuration'))
self.need_loop=True
def add_parser_options(self):
@ -71,7 +71,6 @@ class NodeInfo(base.CommandBase):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.psNodeConfigurationGet(
self.args.service,
self.args.node,
@ -83,7 +82,7 @@ class NodeInfo(base.CommandBase):
class NodeCreate(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'create', use_output=C.OUTPUT_DICT, use_pubsub_node_req=True, use_verbose=True, help=_(u'create a node'))
base.CommandBase.__init__(self, host, 'create', use_output=C.OUTPUT_DICT, use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'create a node'))
self.need_loop=True
def add_parser_options(self):
@ -123,7 +122,7 @@ class NodeCreate(base.CommandBase):
class NodeDelete(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'delete', use_pubsub_node_req=True, help=_(u'delete a node'))
base.CommandBase.__init__(self, host, 'delete', use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'delete a node'))
self.need_loop=True
def add_parser_options(self):
@ -134,7 +133,6 @@ class NodeDelete(base.CommandBase):
self.host.quit()
def start(self):
common.checkURI(self.args)
if not self.args.force:
if not self.args.service:
message = _(u"Are you sure to delete pep node [{node_id}] ?").format(
@ -161,7 +159,7 @@ class NodeDelete(base.CommandBase):
class NodeSet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'set', use_output=C.OUTPUT_DICT, use_pubsub_node_req=True, use_verbose=True, help=_(u'set node configuration'))
base.CommandBase.__init__(self, host, 'set', use_output=C.OUTPUT_DICT, use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'set node configuration'))
self.need_loop=True
def add_parser_options(self):
@ -184,7 +182,6 @@ class NodeSet(base.CommandBase):
return k
def start(self):
common.checkURI(self.args)
self.host.bridge.psNodeConfigurationSet(
self.args.service,
self.args.node,
@ -197,7 +194,7 @@ class NodeSet(base.CommandBase):
class NodeAffiliationsGet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_DICT, use_pubsub_node_req=True, help=_(u'retrieve node affiliations (for node owner)'))
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_DICT, use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'retrieve node affiliations (for node owner)'))
self.need_loop=True
def add_parser_options(self):
@ -213,7 +210,6 @@ class NodeAffiliationsGet(base.CommandBase):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.psNodeAffiliationsGet(
self.args.service,
self.args.node,
@ -225,7 +221,7 @@ class NodeAffiliationsGet(base.CommandBase):
class NodeAffiliationsSet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'set', use_pubsub_node_req=True, use_verbose=True, help=_(u'set affiliations (for node owner)'))
base.CommandBase.__init__(self, host, 'set', use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'set affiliations (for node owner)'))
self.need_loop=True
def add_parser_options(self):
@ -251,7 +247,6 @@ class NodeAffiliationsSet(base.CommandBase):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
affiliations = dict(self.args.affiliations)
self.host.bridge.psNodeAffiliationsSet(
self.args.service,
@ -272,7 +267,7 @@ class NodeAffiliations(base.CommandBase):
class NodeSubscriptionsGet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_DICT, use_pubsub_node_req=True, help=_(u'retrieve node subscriptions (for node owner)'))
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_DICT, use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'retrieve node subscriptions (for node owner)'))
self.need_loop=True
def add_parser_options(self):
@ -288,7 +283,6 @@ class NodeSubscriptionsGet(base.CommandBase):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.psNodeSubscriptionsGet(
self.args.service,
self.args.node,
@ -320,7 +314,7 @@ class StoreSubscriptionAction(argparse.Action):
class NodeSubscriptionsSet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'set', use_pubsub_node_req=True, use_verbose=True, help=_(u'set/modify subscriptions (for node owner)'))
base.CommandBase.__init__(self, host, 'set', use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'set/modify subscriptions (for node owner)'))
self.need_loop=True
def add_parser_options(self):
@ -347,7 +341,6 @@ class NodeSubscriptionsSet(base.CommandBase):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.psNodeSubscriptionsSet(
self.args.service,
self.args.node,
@ -367,7 +360,7 @@ class NodeSubscriptions(base.CommandBase):
class NodeSchemaSet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'set', use_pubsub_node_req=True, use_verbose=True, help=_(u'set/replace a schema'))
base.CommandBase.__init__(self, host, 'set', use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'set/replace a schema'))
self.need_loop = True
def add_parser_options(self):
@ -393,12 +386,12 @@ class NodeSchemaEdit(base.CommandBase, common.BaseEdit):
use_items=False
def __init__(self, host):
base.CommandBase.__init__(self, host, 'edit', use_pubsub_node_req=True, use_verbose=True, help=_(u'edit a schema'))
base.CommandBase.__init__(self, host, 'edit', use_pubsub=True, pubsub_flags={C.NODE}, use_draft=True, use_verbose=True, help=_(u'edit a schema'))
common.BaseEdit.__init__(self, self.host, PUBSUB_SCHEMA_TMP_DIR)
self.need_loop=True
def add_parser_options(self):
common.BaseEdit.add_parser_options(self)
pass
def psSchemaSetCb(self):
self.disp(_(u'schema has been set'), 1)
@ -431,7 +424,6 @@ class NodeSchemaEdit(base.CommandBase, common.BaseEdit):
self.runEditor("pubsub_schema_editor_args", content_file_path, content_file_obj)
def start(self):
common.checkURI(self.args)
self.host.bridge.psSchemaGet(
self.args.service,
self.args.node,
@ -445,7 +437,7 @@ class NodeSchemaEdit(base.CommandBase, common.BaseEdit):
class NodeSchemaGet(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_XML, use_pubsub_node_req=True, use_verbose=True, help=_(u'get schema'))
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_XML, use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'get schema'))
self.need_loop=True
def add_parser_options(self):
@ -459,7 +451,6 @@ class NodeSchemaGet(base.CommandBase):
self.host.quit()
def start(self):
common.checkURI(self.args)
self.host.bridge.psSchemaGet(
self.args.service,
self.args.node,
@ -487,7 +478,7 @@ class Node(base.CommandBase):
class Set(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'set', use_pubsub_node_req=True, help=_(u'publish a new item or update an existing one'))
base.CommandBase.__init__(self, host, 'set', use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'publish a new item or update an existing one'))
self.need_loop=True
def add_parser_options(self):
@ -506,7 +497,6 @@ class Set(base.CommandBase):
except ImportError:
self.disp(u"lxml module must be installed to use edit, please install it with \"pip install lxml\"", error=True)
self.host.quit(1)
common.checkURI(self.args)
try:
element = etree.parse(sys.stdin).getroot()
except Exception as e:
@ -532,12 +522,10 @@ class Set(base.CommandBase):
class Get(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_LIST_XML, use_pubsub_node_req=True, help=_(u'get pubsub item(s)'))
base.CommandBase.__init__(self, host, 'get', use_output=C.OUTPUT_LIST_XML, use_pubsub=True, pubsub_flags={C.NODE, C.MULTI_ITEMS}, help=_(u'get pubsub item(s)'))
self.need_loop=True
def add_parser_options(self):
self.parser.add_argument("-i", "--item", type=base.unicode_decoder, action='append', default=[], dest='items',
help=_(u"item(s) id(s) to get (default: request all items)"))
self.parser.add_argument("-S", "--sub-id", type=base.unicode_decoder, default=u'',
help=_(u"subscription id"))
self.parser.add_argument("-m", "--max", type=int, default=10, help=_(u"maximum number of items to get ({} to get all items)".format(C.NO_LIMIT)))
@ -555,7 +543,6 @@ class Get(base.CommandBase):
self.host.quit(C.EXIT_BRIDGE_ERRBACK)
def start(self):
common.checkURI(self.args)
self.host.bridge.psItemsGet(
self.args.service,
self.args.node,
@ -570,20 +557,18 @@ class Get(base.CommandBase):
class Delete(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'delete', use_pubsub_node_req=True, help=_(u'delete an item'))
base.CommandBase.__init__(self, host, 'delete', use_pubsub=True, pubsub_flags={C.NODE, C.SINGLE_ITEM}, help=_(u'delete an item'))
self.need_loop=True
def add_parser_options(self):
self.parser.add_argument("item", nargs='?', type=base.unicode_decoder, help=_(u"item to delete"))
self.parser.add_argument("-f", "--force", action='store_true', help=_(u"delete without confirmation"))
self.parser.add_argument("-n", "--notify", action='store_true', help=_(u"notify deletion"))
self.parser.add_argument("-N", "--notify", action='store_true', help=_(u"notify deletion"))
def psItemsDeleteCb(self):
self.disp(_(u'item {item_id} has been deleted').format(item_id=self.args.item))
self.host.quit(C.EXIT_OK)
def start(self):
common.checkURI(self.args)
if not self.args.item:
self.parser.error(_(u"You need to specify an item to delete"))
if not self.args.force:
@ -607,12 +592,12 @@ class Delete(base.CommandBase):
class Edit(base.CommandBase, common.BaseEdit):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'edit', use_verbose=True, use_pubsub_node_req=True, help=_(u'edit an existing or new pubsub item'))
base.CommandBase.__init__(self, host, 'edit', use_verbose=True, use_pubsub=True,
pubsub_flags={C.NODE, C.SINGLE_ITEM}, use_draft=True, help=_(u'edit an existing or new pubsub item'))
common.BaseEdit.__init__(self, self.host, PUBSUB_TMP_DIR)
def add_parser_options(self):
self.parser.add_argument("item", type=base.unicode_decoder, nargs='?', default=u'new', help=_(u"URL of the item to edit, or keyword"))
common.BaseEdit.add_parser_options(self)
pass
def edit(self, content_file_path, content_file_obj):
# we launch editor
@ -644,15 +629,14 @@ class Edit(base.CommandBase, common.BaseEdit):
return etree.tostring(payload, encoding="unicode", pretty_print=True), item_id
def start(self):
self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path, content_file_obj = self.getItemPath(self.args.item)
self.pubsub_service, self.pubsub_node, self.pubsub_item, content_file_path, content_file_obj = self.getItemPath()
self.edit(content_file_path, content_file_obj)
class Subscribe(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'subscribe', use_pubsub_node_req=True, use_verbose=True, help=_(u'subscribe to a node'))
base.CommandBase.__init__(self, host, 'subscribe', use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'subscribe to a node'))
self.need_loop=True
def add_parser_options(self):
@ -680,7 +664,7 @@ class Unsubscribe(base.CommandBase):
# TODO: voir pourquoi NodeNotFound sur subscribe juste après unsubscribe
def __init__(self, host):
base.CommandBase.__init__(self, host, 'unsubscribe', use_pubsub_node_req=True, use_verbose=True, help=_(u'unsubscribe from a node'))
base.CommandBase.__init__(self, host, 'unsubscribe', use_pubsub=True, pubsub_flags={C.NODE}, use_verbose=True, help=_(u'unsubscribe from a node'))
self.need_loop=True
def add_parser_options(self):
@ -758,7 +742,8 @@ class Search(base.CommandBase):
EXEC_ACTIONS = (u'exec', u'external')
def __init__(self, host):
base.CommandBase.__init__(self, host, 'search', use_output=C.OUTPUT_XML, use_pubsub=True, use_verbose=True, help=_(u'search items corresponding to filters'))
base.CommandBase.__init__(self, host, 'search', use_output=C.OUTPUT_XML, use_pubsub=True, pubsub_flags={C.MULTI_ITEMS},
use_verbose=True, help=_(u'search items corresponding to filters'))
self.need_loop=True
@property
@ -778,7 +763,6 @@ class Search(base.CommandBase):
return (type_, value)
def add_parser_options(self):
self.parser.add_argument("-i", "--item", action="append", default=[], dest='items', type=base.unicode_decoder, help=_(u"item id(s)"))
self.parser.add_argument("-D", "--max-depth", type=int, default=0, help=_(u"maximum depth of recursion (will search linked nodes if > 0, default: 0)"))
self.parser.add_argument("-m", "--max", type=int, default=30, help=_(u"maximum number of items to get per node ({} to get all items, default: 30)".format(C.NO_LIMIT)))
self.parser.add_argument("-N", "--namespace", action='append', nargs=2, default=[],
@ -1082,18 +1066,16 @@ class Search(base.CommandBase):
if self.args.filters is None:
self.args.filters = []
self.args.namespace = dict(self.args.namespace + [('pubsub', "http://jabber.org/protocol/pubsub")])
common.checkURI(self.args)
self.getItems(0, self.args.service, self.args.node, self.args.items)
class Uri(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'uri', use_profile=False, use_pubsub_node_req=True, help=_(u'build URI'))
base.CommandBase.__init__(self, host, 'uri', use_profile=False, use_pubsub=True, pubsub_flags={C.NODE, C.SINGLE_ITEM}, help=_(u'build URI'))
self.need_loop=True
def add_parser_options(self):
self.parser.add_argument("-i", "--item", type=base.unicode_decoder, help=_(u"item to link"))
self.parser.add_argument("-p", "--profile", type=base.unicode_decoder, default=C.PROF_KEY_DEFAULT, help=_(u"profile (used when no server is specified)"))
def display_uri(self, jid_):
@ -1127,7 +1109,7 @@ class Uri(base.CommandBase):
class HookCreate(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'create', use_pubsub_node_req=True, help=_(u'create a Pubsub hook'))
base.CommandBase.__init__(self, host, 'create', use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'create a Pubsub hook'))
self.need_loop=True
def add_parser_options(self):
@ -1143,7 +1125,6 @@ class HookCreate(base.CommandBase):
self.parser.error(_(u"{path} is not a file").format(path=self.args.hook_arg))
def start(self):
common.checkURI(self.args)
self.checkArgs(self)
self.host.bridge.psHookAdd(
self.args.service,
@ -1161,7 +1142,7 @@ class HookCreate(base.CommandBase):
class HookDelete(base.CommandBase):
def __init__(self, host):
base.CommandBase.__init__(self, host, 'delete', use_pubsub_node_req=True, help=_(u'delete a Pubsub hook'))
base.CommandBase.__init__(self, host, 'delete', use_pubsub=True, pubsub_flags={C.NODE}, help=_(u'delete a Pubsub hook'))
self.need_loop=True
def add_parser_options(self):
@ -1174,7 +1155,6 @@ class HookDelete(base.CommandBase):
self.host.quit()
def start(self):
common.checkURI(self.args)
HookCreate.checkArgs(self)
self.host.bridge.psHookRemove(
self.args.service,

View file

@ -21,7 +21,6 @@ from sat_frontends.jp.constants import Const as C
from sat.core.i18n import _
from sat.core import exceptions
from sat.tools.common import regex
from sat.tools.common import uri
from sat.tools.common.ansi import ANSI as A
from sat.tools import config
from ConfigParser import NoSectionError, NoOptionError
@ -105,40 +104,12 @@ def parse_args(host, cmd_line, **format_kw):
return []
def checkURI(args):
"""check if args.node is an URI
if a valid xmpp: URI is found, args.service, args.node and args.item will be set
"""
# FIXME: Q&D way to handle xmpp: uris, a generic way is needed
# and it should be merged with code in BaseEdit
if not args.service and args.node.startswith('xmpp:'):
try:
uri_data = uri.parseXMPPUri(args.node)
except ValueError:
pass
else:
if uri_data[u'type'] == 'pubsub':
args.service = uri_data[u'path']
args.node = uri_data[u'node']
if u'item' in uri_data:
try:
item = getattr(uri_data, 'item')
except AttributeError:
pass
else:
if item is None:
args.item = uri_data
class BaseEdit(object):
u"""base class for editing commands
This class allows to edit file for PubSub or something else.
It works with temporary files in SàT local_dir, in a "cat_dir" subdir
"""
# use_items(bool): True if items are used, will then add item related options
use_items=True
def __init__(self, host, cat_dir, use_metadata=False):
"""
@ -154,12 +125,6 @@ class BaseEdit(object):
self.cat_dir_str = cat_dir.encode('utf-8')
self.use_metadata = use_metadata
def add_parser_options(self):
if self.use_items:
group = self.parser.add_mutually_exclusive_group()
group.add_argument("--force-item", action='store_true', help=_(u"don't use magic and take item argument as an actual item"))
group.add_argument("--last-item", action='store_true', help=_(u"take last item instead of creating a new one if no item id is found"))
def secureUnlink(self, path):
"""Unlink given path after keeping it for a while
@ -365,145 +330,73 @@ class BaseEdit(object):
"""return suffix used for content file"""
return u'xml'
def getItemPath(self, item):
def getItemPath(self):
"""retrieve item path (i.e. service and node) from item argument
This method is obviously only useful for edition of PubSub based features
service, node and item must be named like this in args
@param item(unicode): item to get or url or magic keyword
item argument can be used to specify :
- HTTP(S) URL
- XMPP URL
- keyword, which can be:
- new: create new item
- last: retrieve last published item
- current: continue current local draft
- file path
- item id
"""
force_item = self.args.force_item
if force_item and not item:
self.parser.error(_(u"an item id must be specified if you use --force-item"))
command = item.lower()
pubsub_service = self.args.service
pubsub_node = self.args.node
pubsub_item = None
service = self.args.service
node = self.args.node
item = self.args.item
last_item = self.args.last_item
if not force_item and command not in ('new', 'last', 'current'):
# we have probably an URL, we try to parse it
import urlparse
url = self.args.item
parsed_url = urlparse.urlsplit(url.encode('utf-8'))
if parsed_url.scheme.startswith('http'):
self.disp(u"{} URL found, trying to find associated xmpp: URI".format(parsed_url.scheme.upper()),1)
# HTTP URL, we try to find xmpp: links
try:
from lxml import etree
except ImportError:
self.disp(u"lxml module must be installed to use http(s) scheme, please install it with \"pip install lxml\"", error=True)
self.host.quit(1)
import urllib2
parser = etree.HTMLParser()
try:
root = etree.parse(urllib2.urlopen(url), parser)
except etree.XMLSyntaxError as e:
self.disp(_(u"Can't parse HTML page : {msg}").format(msg=e))
links = []
else:
links = root.xpath("//link[@rel='alternate' and starts-with(@href, 'xmpp:')]")
if not links:
self.disp(u'Could not find alternate "xmpp:" URI, can\'t find associated XMPP PubSub node/item', error=True)
self.host.quit(1)
url = links[0].get('href')
parsed_url = urlparse.urlsplit(url)
if parsed_url.scheme == 'xmpp':
if self.args.service or self.args.node:
self.parser.error(_(u"You can't use URI and --service or --node at the same time"))
self.disp(u"XMPP URI used: {}".format(url),2)
# XXX: if we have not xmpp: URI here, we'll take the data as a file path
pubsub_service = parsed_url.path.decode('utf-8')
pubsub_data = urlparse.parse_qs(parsed_url.query)
try:
pubsub_node = pubsub_data['node'][0].decode('utf-8')
except KeyError:
self.disp(u'No node found in xmpp: URI, can\'t retrieve item', error=True)
self.host.quit(1)
pubsub_item = pubsub_data.get('item',[None])[0]
if pubsub_item is not None:
pubsub_item = pubsub_item.decode('utf-8')
if pubsub_item is None and self.args.last_item:
command = 'last'
elif pubsub_item is not None:
command = 'edit' # XXX: edit command is only used internaly, it similar to last, but with the item given in the URL
else:
command = 'new'
if self.args.last_item:
if pubsub_item is None:
command = 'last'
elif command != 'last':
self.parser.error(_(u"--last-item can't be used with a specified item"))
if not force_item and command in ('new', 'last', 'edit'):
if self.args.current:
# user wants to continue current draft
content_file_path = self.getCurrentFile(self.profile)
self.disp(u'Continuing edition of current draft', 2)
content_file_obj = open(content_file_path, 'r+b')
# we seek at the end of file in case of an item already exist
# this will write content of the existing item at the end of the draft.
# This way no data should be lost.
content_file_obj.seek(0, os.SEEK_END)
elif self.args.draft_path:
# there is an existing draft that we use
content_file_path = os.path.expanduser(self.args.item)
content_file_obj = open(content_file_path, 'r+b')
# we seek at the end for the same reason as above
content_file_obj.seek(0, os.SEEK_END)
else:
# we need a temporary file
content_file_obj, content_file_path = self.getTmpFile()
if command == 'new':
self.disp(u'Editing a new item', 2)
if item or last_item:
self.disp(u'Editing requested published item', 2)
try:
if self.use_metadata:
content, metadata, item = self.getItemData(service, node, item)
else:
content, item = self.getItemData(service, node, item)
except Exception as e:
# FIXME: ugly but we have not good may to check errors in bridge
if u'item-not-found' in unicode(e):
# item doesn't exist, we create a new one with requested id
metadata = None
elif command in ('last', 'edit'):
self.disp(u'Editing requested published item', 2)
try:
if self.use_metadata:
content, metadata, pubsub_item = self.getItemData(pubsub_service, pubsub_node, pubsub_item)
if last_item:
self.disp(_(u'no item found at all, we create a new one'), 2)
else:
content, pubsub_item = self.getItemData(pubsub_service, pubsub_node, pubsub_item)
except Exception as e:
self.disp(u"Error while retrieving last item: {}".format(e))
self.host.quit(1)
self.disp(_(u'item "{item_id}" not found, we create a new item with this id').format(item_id=item), 2)
content_file_obj.seek(0)
else:
self.disp(u"Error while retrieving item: {}".format(e))
self.host.quit(C.EXIT_ERROR)
else:
# item exists, we write content
if content_file_obj.tell() != 0:
# we already have a draft,
# we copy item content after it and add an indicator
content_file_obj.write('\n*****\n')
content_file_obj.write(content.encode('utf-8'))
content_file_obj.seek(0)
self.disp(_(u'item "{item_id}" found, we edit it').format(item_id=item), 2)
else:
self.disp(u'Editing a new item', 2)
if self.use_metadata:
metadata = None
if not force_item and command == 'current':
# user wants to continue current draft
content_file_path = self.getCurrentFile(self.profile)
self.disp(u'Continuing edition of current draft', 2)
content_file_obj = open(content_file_path, 'r+b')
elif not force_item and os.path.isfile(self.args.item):
# there is an existing draft that we use
content_file_path = os.path.expanduser(self.args.item)
content_file_obj = open(content_file_path, 'r+b')
else:
# last chance, it should be an item
content_file_obj, content_file_path = self.getTmpFile()
pubsub_item = self.args.item
try:
# we try to get existing item
if self.use_metadata:
content, metadata, pubsub_item = self.getItemData(pubsub_service, pubsub_node, self.args.item)
else:
content, pubsub_item = self.getItemData(pubsub_service, pubsub_node, self.args.item)
except Exception as e:
# FIXME: ugly but we have not good may to check errors in bridge
if u'item-not-found' in unicode(e):
# item doesn't exist, we create a new one with requested id
metadata = None
self.disp(_(u'item "{item_id}" not found, we create a new item with this id').format(item_id=pubsub_item), 2)
else:
# item exists, we write content if content file
content_file_obj.write(content.encode('utf-8'))
content_file_obj.seek(0)
self.disp(_(u'item "{item_id}" found, we edit it').format(item_id=pubsub_item), 2)
if self.use_metadata:
return pubsub_service, pubsub_node, pubsub_item, content_file_path, content_file_obj, metadata
return service, node, item, content_file_path, content_file_obj, metadata
else:
return pubsub_service, pubsub_node, pubsub_item, content_file_path, content_file_obj
return service, node, item, content_file_path, content_file_obj
class Table(object):

View file

@ -36,6 +36,12 @@ class Const(constants.Const):
OUTPUT_LIST_XML = u'list_xml' # list of XML nodes (as unicode strings)
OUTPUT_TYPES = (OUTPUT_TEXT, OUTPUT_DICT, OUTPUT_LIST, OUTPUT_LIST_DICT, OUTPUT_DICT_DICT, OUTPUT_COMPLEX, OUTPUT_XML, OUTPUT_LIST_XML)
# Pubsub options flags
SERVICE = u'service' # service required
NODE = u'node' # node required
SINGLE_ITEM = u'single_item' # only one item is allowed
MULTI_ITEMS = u'multi_items' # multiple items are allowed
# ANSI
A_HEADER = A.BOLD + A.FG_YELLOW
A_SUBHEADER = A.BOLD + A.FG_RED