diff frontends/src/jp/common.py @ 2273:5f0dbf42aa9c

jp (blog, common): various fixes in common and blog: - parse_args has been moved to common - cat_dir is converted to str on BaseEdit init, so it can be use to make str path for files manipulation - fixed use of EDITOR_ARGS_MAGIC when use_metadata is False - fixed unlink of metadata files when use_metadata is False
author Goffi <goffi@goffi.org>
date Tue, 27 Jun 2017 19:38:22 +0200
parents 07caa12be945
children 5cd45a79775b
line wrap: on
line diff
--- a/frontends/src/jp/common.py	Tue Jun 27 19:38:20 2017 +0200
+++ b/frontends/src/jp/common.py	Tue Jun 27 19:38:22 2017 +0200
@@ -29,6 +29,7 @@
 import tempfile
 import subprocess
 import glob
+import shlex
 
 # defaut arguments used for some known editors (editing with metadata)
 VIM_SPLIT_ARGS = "-c 'vsplit|wincmd w|next|wincmd w'"
@@ -50,7 +51,7 @@
     """Return directory used to store temporary files
 
     @param sat_conf(ConfigParser.ConfigParser): instance opened on sat configuration
-    @param cat_dir(str): directory of the category (e.g. "blog")
+    @param cat_dir(unicode): directory of the category (e.g. "blog")
     @param sub_dir(str): sub directory where data need to be put
         profile can be used here, or special directory name
         sub_dir will be escaped to be usable in path (use regex.pathUnescape to find
@@ -58,12 +59,28 @@
     @return (str): path to the dir
     """
     local_dir = config.getConfig(sat_conf, '', 'local_dir', Exception)
-    path = [local_dir, cat_dir]
+    path = [local_dir.encode('utf-8'), cat_dir.encode('utf-8')]
     if sub_dir is not None:
         path.append(regex.pathEscape(sub_dir))
     return os.path.join(*path)
 
 
+def parse_args(host, cmd_line, **format_kw):
+    """Parse command arguments
+
+    @param cmd_line(unicode): command line as found in sat.conf
+    @param format_kw: keywords used for formating
+    @return (list(unicode)): list of arguments to pass to subprocess function
+    """
+    try:
+        # we split the arguments and add the known fields
+        # we split arguments first to avoid escaping issues in file names
+        return [a.format(**format_kw) for a in shlex.split(cmd_line)]
+    except ValueError as e:
+        host.disp(u"Couldn't parse editor cmd [{cmd}]: {reason}".format(cmd=cmd_line, reason=e))
+        return []
+
+
 class BaseEdit(object):
     u"""base class for editing commands
 
@@ -71,7 +88,7 @@
     It works with temporary files in SàT local_dir, in a "cat_dir" subdir
     """
 
-    def __init__(self, host, cat_dir, use_metadata=True):
+    def __init__(self, host, cat_dir, use_metadata=False):
         """
         @param sat_conf(ConfigParser.ConfigParser): instance opened on sat configuration
         @param cat_dir(unicode): directory to use for drafts
@@ -82,7 +99,7 @@
         """
         self.host = host
         self.sat_conf = config.parseMainConf()
-        self.cat_dir = cat_dir.encode('utf-8')
+        self.cat_dir_str = cat_dir.encode('utf-8')
         self.use_metadata = use_metadata
 
     def add_parser_options(self):
@@ -98,12 +115,12 @@
         """
         if not os.path.isfile(path):
             raise OSError(u"path must link to a regular file")
-        if not path.startswith(getTmpDir(self.sat_conf, self.cat_dir)):
+        if not path.startswith(getTmpDir(self.sat_conf, self.cat_dir_str)):
             self.disp(u"File {} is not in SàT temporary hierarchy, we do not remove it".format(path.decode('utf-8')), 2)
             return
         # we have 2 files per draft with use_metadata, so we double max
         unlink_max = SECURE_UNLINK_MAX * 2 if self.use_metadata else SECURE_UNLINK_MAX
-        backup_dir = getTmpDir(self.sat_conf, self.cat_dir, SECURE_UNLINK_DIR)
+        backup_dir = getTmpDir(self.sat_conf, self.cat_dir_str, SECURE_UNLINK_DIR)
         if not os.path.exists(backup_dir):
             os.makedirs(backup_dir)
         filename = os.path.basename(path)
@@ -150,11 +167,14 @@
             editor_args = config.getConfig(self.sat_conf, 'jp', editor_args_opt, Exception)
         except (NoOptionError, NoSectionError):
             # no, we check if we know the editor and have special arguments
-            editor_args = EDITOR_ARGS_MAGIC.get(os.path.basename(editor), '')
+            if self.use_metadata:
+                editor_args = EDITOR_ARGS_MAGIC.get(os.path.basename(editor), '')
+            else:
+                editor_args = ''
         parse_kwargs = {'content_file': content_file_path}
         if self.use_metadata:
             parse_kwargs['metadata_file'] = meta_file_path
-        args = self.parse_args(editor_args, **parse_kwargs)
+        args = parse_args(self.host, editor_args, **parse_kwargs)
         if not args:
             args = [content_file_path]
 
@@ -198,20 +218,21 @@
                 self.host.quit()
 
             if len(content) == 0:
-                self.disp(u"Content is empty, cancelling the blog edition")
-                if not content_file_path.startswith(getTmpDir(self.sat_conf, self.cat_dir)):
+                self.disp(u"Content is empty, cancelling the edition")
+                if not content_file_path.startswith(getTmpDir(self.sat_conf, self.cat_dir_str)):
                     self.disp(u"File are not in SàT temporary hierarchy, we do not remove them", 2)
                     self.host.quit()
                 self.disp(u"Deletion of {}".format(content_file_path.decode('utf-8')), 2)
                 os.unlink(content_file_path)
-                self.disp(u"Deletion of {}".format(meta_file_path.decode('utf-8')), 2)
-                os.unlink(meta_file_path)
+                if self.use_metadata:
+                    self.disp(u"Deletion of {}".format(meta_file_path.decode('utf-8')), 2)
+                    os.unlink(meta_file_path)
                 self.host.quit()
 
             # time to re-check the hash
             elif (tmp_ori_hash == hashlib.sha1(content).digest() and
                   (not self.use_metadata or meta_ori == metadata)):
-                self.disp(u"The content has not been modified, cancelling the blog edition")
+                self.disp(u"The content has not been modified, cancelling the edition")
                 self.host.quit()
 
             else:
@@ -232,7 +253,8 @@
                     self.host.quit(1)
 
             self.secureUnlink(content_file_path)
-            self.secureUnlink(meta_file_path)
+            if self.use_metadata:
+                self.secureUnlink(meta_file_path)
 
     def publish(self, content):
         # if metadata is needed, publish will be called with it last argument
@@ -244,7 +266,8 @@
         @param suff (str): suffix to use for the filename
         @return (tuple(file, str)): opened (w+b) file object and file path
         """
-        tmp_dir = getTmpDir(self.sat_conf, self.cat_dir, self.profile.encode('utf-8'))
+        cat_dir_str = self.cat_dir_str
+        tmp_dir = getTmpDir(self.sat_conf, self.cat_dir_str, self.profile.encode('utf-8'))
         if not os.path.exists(tmp_dir):
             try:
                 os.makedirs(tmp_dir)
@@ -253,8 +276,8 @@
                     path=tmp_dir, reason=e), error=True)
                 self.host.quit(1)
         try:
-            fd, path = tempfile.mkstemp(suffix=suff,
-                prefix=time.strftime(self.cat_dir.encode('utf-8') + '_%Y-%m-%d_%H:%M:%S_'),
+            fd, path = tempfile.mkstemp(suffix=suff.encode('utf-8'),
+                prefix=time.strftime(cat_dir_str + '_%Y-%m-%d_%H:%M:%S_'),
                 dir=tmp_dir, text=True)
             return os.fdopen(fd, 'w+b'), path
         except OSError as e:
@@ -267,11 +290,11 @@
         @param profile(unicode): profile linked to the draft
         @return(str): full path of current file
         """
-        # we guess the blog item currently edited by choosing
+        # we guess the item currently edited by choosing
         # the most recent file corresponding to temp file pattern
         # in tmp_dir, excluding metadata files
-        cat_dir_str = self.cat_dir.encode('utf-8')
-        tmp_dir = getTmpDir(self.sat_conf, cat_dir_str, profile.encode('utf-8'))
+        cat_dir_str = self.cat_dir_str
+        tmp_dir = getTmpDir(self.sat_conf, self.cat_dir_str, profile.encode('utf-8'))
         available = [path for path in glob.glob(os.path.join(tmp_dir, cat_dir_str + '_*')) if not path.endswith(METADATA_SUFF)]
         if not available:
             self.disp(u"Could not find any content draft in {path}".format(path=tmp_dir), error=True)
@@ -284,7 +307,7 @@
 
     def getTmpSuff(self):
         """return suffix used for content file"""
-        return 'xml'
+        return u'xml'
 
     def getItemPath(self, item):
         """retrieve item path (i.e. service and node) from item argument