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
This commit is contained in:
Goffi 2017-06-27 19:38:22 +02:00
commit 2449721982
2 changed files with 46 additions and 39 deletions

View file

@ -33,7 +33,6 @@ import os
import time
import tempfile
import subprocess
import shlex
from sat.tools.common import data_format
__commands__ = ["Blog"]
@ -47,7 +46,7 @@ SYNTAX_EXT = {
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 @@ class BlogCommon(object):
# 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 @@ class Preview(base.CommandBase, BlogCommon):
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)

View file

@ -29,6 +29,7 @@ import time
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 @@ def getTmpDir(sat_conf, cat_dir, sub_dir=None):
"""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 @@ def getTmpDir(sat_conf, cat_dir, sub_dir=None):
@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 @@ class BaseEdit(object):
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 @@ class BaseEdit(object):
"""
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 @@ class BaseEdit(object):
"""
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 @@ class BaseEdit(object):
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 @@ class BaseEdit(object):
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 @@ class BaseEdit(object):
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 @@ class BaseEdit(object):
@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 @@ class BaseEdit(object):
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 @@ class BaseEdit(object):
@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 @@ class BaseEdit(object):
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