diff frontends/src/jp/common.py @ 2532:772447ec070f

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.
author Goffi <goffi@goffi.org>
date Wed, 21 Mar 2018 19:13:22 +0100
parents 21d43eab3fb9
children b27165bf160c
line wrap: on
line diff
--- a/frontends/src/jp/common.py	Wed Mar 21 19:07:06 2018 +0100
+++ b/frontends/src/jp/common.py	Wed Mar 21 19:13:22 2018 +0100
@@ -21,7 +21,6 @@
 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 @@
         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 @@
         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 @@
         """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):