# HG changeset patch # User Goffi # Date 1498585102 -7200 # Node ID 5f0dbf42aa9cd1130d209adbcd1f69f64ead2479 # Parent b5befe7722d3b7d1b130fcfd672c97f34976d40e 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 diff -r b5befe7722d3 -r 5f0dbf42aa9c frontends/src/jp/cmd_blog.py --- a/frontends/src/jp/cmd_blog.py Tue Jun 27 19:38:20 2017 +0200 +++ b/frontends/src/jp/cmd_blog.py Tue Jun 27 19:38:22 2017 +0200 @@ -33,7 +33,6 @@ import time import tempfile import subprocess -import shlex from sat.tools.common import data_format __commands__ = ["Blog"] @@ -47,7 +46,7 @@ CONF_SYNTAX_EXT = 'syntax_ext_dict' -BLOG_TMP_DIR=u"blog" +BLOG_TMP_DIR = u"blog" # key to remove from metadata tmp file if they exist KEY_TO_REMOVE_METADATA = ('id','content', 'content_xhtml', 'comments_node', 'comments_service', 'updated') @@ -97,21 +96,6 @@ # if not found, we use current syntax return self.host.bridge.getParamA("Syntax", "Composition", "value", self.profile) - def parse_args(self, cmd_line, **format_kw): - """Parse command arguments - - @param cmd_line(unicode): command line as found in sat.conf - @param format_kw: keywords used for formmating - @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: - self.disp(u"Couldn't parse editor cmd [{cmd}]: {reason}".format(cmd=cmd_line, reason=e)) - return [] - class Get(base.CommandBase, BlogCommon): TEMPLATE = u"blog/articles.html" @@ -423,7 +407,7 @@ def _launchPreviewExt(self, cmd_line, opt_name): url = 'file:{}'.format(self.urllib.quote(self.preview_file_path)) - args = self.parse_args(cmd_line, url=url, preview_file=self.preview_file_path) + args = common.parse_args(self.host, cmd_line, url=url, preview_file=self.preview_file_path) if not args: self.disp(u"Couln't find command in \"{name}\", abording".format(name=opt_name), error=True) self.host.quit(1) diff -r b5befe7722d3 -r 5f0dbf42aa9c frontends/src/jp/common.py --- 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