jp (blog): edit command, first draft:
- can edit a new or existing item, by default edit a new item
- item are selected with a shortcut, for now only "new" and "last" are handled. URI handling is planed, specially xmpp: URIs
- file are edited using EDITOR env variable, or editor in [jp] section in sat.conf
- current syntax is used, file extension is choosed according to syntax, to make syntax coloration and other goodies in editor available
- if file is empty or not modified, nothing is published
- for now, title, tags and commend desactivation are handled with optional arguments, but other are planed, and a metadata system should come soon
This commit is contained in:
parent
893aa7a595
commit
dc8f07aec4
1 changed files with 128 additions and 1 deletions
|
|
@ -21,11 +21,138 @@
|
|||
import base
|
||||
from sat.core.i18n import _
|
||||
from sat.core.constants import Const as C
|
||||
from sat.tools import config
|
||||
import json
|
||||
URL_REDIRECT_PREFIX = 'url_redirect_'
|
||||
import os.path
|
||||
import os
|
||||
import time
|
||||
import tempfile
|
||||
import subprocess
|
||||
from sat.tools import common
|
||||
|
||||
__commands__ = ["Blog"]
|
||||
|
||||
SYNTAX_EXT = { '': 'txt', # used when the syntax is not found
|
||||
"XHTML": "xhtml",
|
||||
"markdown": "md"
|
||||
}
|
||||
CONF_SYNTAX_EXT = 'syntax_ext_dict'
|
||||
BLOG_TMP_DIR="blog"
|
||||
|
||||
URL_REDIRECT_PREFIX = 'url_redirect_'
|
||||
|
||||
|
||||
class Edit(base.CommandBase):
|
||||
|
||||
def __init__(self, host):
|
||||
super(Edit, self).__init__(host, 'edit', use_verbose=True, help=_(u'edit an existing or new blog post'))
|
||||
|
||||
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"))
|
||||
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("--no-comment", action='store_true', help=_(u"disable comments"))
|
||||
|
||||
def getTmpFile(self, sat_conf, tmp_suff):
|
||||
local_dir = config.getConfig(sat_conf, '', 'local_dir', Exception)
|
||||
tmp_dir = os.path.join(local_dir, BLOG_TMP_DIR)
|
||||
if not os.path.exists(tmp_dir):
|
||||
try:
|
||||
os.makedirs(tmp_dir)
|
||||
except OSError as e:
|
||||
self.disp(u"Can't create {path} directory: {reason}".format(
|
||||
path=tmp_dir, reason=e), error=True)
|
||||
self.host.quit(1)
|
||||
|
||||
try:
|
||||
return tempfile.mkstemp(suffix=tmp_suff,
|
||||
prefix=time.strftime('blog_%Y-%m-%d_%H:%M:%S_'),
|
||||
dir=tmp_dir, text=True)
|
||||
except OSError as e:
|
||||
self.disp(u"Can't create temporary file: {reason}".format(reason=e), error=True)
|
||||
self.host.quit(1)
|
||||
|
||||
def edit(self, sat_conf, tmp_file, tmp_file_obj, item_id=None):
|
||||
"""Edit the file contening the content using editor, and publish it"""
|
||||
# we first calculate hash to check for modifications
|
||||
import hashlib
|
||||
tmp_file_obj.seek(0)
|
||||
ori_hash = hashlib.sha1(tmp_file_obj.read()).digest()
|
||||
tmp_file_obj.close()
|
||||
|
||||
# the we launch editor
|
||||
editor = config.getConfig(sat_conf, 'jp', 'editor') or os.getenv('EDITOR', 'vi')
|
||||
editor_exit = subprocess.call([editor, tmp_file])
|
||||
|
||||
# we send the file if edition was a success
|
||||
if editor_exit != 0:
|
||||
self.disp(u"Editor exited with an error code, so temporary file has not be deleted, and blog item is not published.\nTou can find temporary file at {path}".format(
|
||||
path=tmp_file), error=True)
|
||||
else:
|
||||
with open(tmp_file, 'rb') as f:
|
||||
content = f.read()
|
||||
|
||||
if len(content) == 0:
|
||||
self.disp(u"Content is empty, cancelling the blog edition")
|
||||
|
||||
# time to re-check the hash
|
||||
elif ori_hash == hashlib.sha1(content).digest():
|
||||
self.disp(u"The content has not been modified, cancelling the blog edition")
|
||||
|
||||
else:
|
||||
# we can now send the blog
|
||||
mb_data = {
|
||||
'content_rich': content.decode('utf-8'),
|
||||
'allow_comments': C.boolConst(not self.args.no_comment),
|
||||
}
|
||||
if item_id:
|
||||
mb_data['id'] = item_id
|
||||
if self.args.tag:
|
||||
common.iter2dict('tag', self.args.tag, mb_data)
|
||||
|
||||
if self.args.title is not None:
|
||||
mb_data['title'] = self.args.title
|
||||
try:
|
||||
self.host.bridge.mbSend('', '', mb_data, self.profile)
|
||||
except Exception as e:
|
||||
self.disp(u"Error while sending your blog, the temporary file has been kept at {path}: {reason}".format(
|
||||
path=tmp_file, reason=e), error=True)
|
||||
self.host.quit(1)
|
||||
|
||||
os.unlink(tmp_file)
|
||||
|
||||
def start(self):
|
||||
# we get current syntax to determine file extension
|
||||
current_syntax = self.host.bridge.getParamA("Syntax", "Composition", "value", self.profile)
|
||||
self.disp(u"Current syntax: {}".format(current_syntax), 1)
|
||||
sat_conf = config.parseMainConf()
|
||||
# if there are user defined extension, we use them
|
||||
SYNTAX_EXT.update(config.getConfig(sat_conf, 'jp', CONF_SYNTAX_EXT, {}))
|
||||
|
||||
# we now create a temporary file
|
||||
tmp_suff = '.' + SYNTAX_EXT.get(current_syntax, SYNTAX_EXT[''])
|
||||
fd, tmp_file = self.getTmpFile(sat_conf, tmp_suff)
|
||||
|
||||
item_lower = self.args.item.lower()
|
||||
if item_lower == 'new':
|
||||
self.disp(u'Editing a new blog item', 2)
|
||||
self.edit(sat_conf, tmp_file, os.fdopen(fd))
|
||||
elif item_lower == 'last':
|
||||
self.disp(u'Editing last published item', 2)
|
||||
try:
|
||||
mb_data = self.host.bridge.mbGet('', '', 1, [], {}, self.profile)[0][0]
|
||||
except Exception as e:
|
||||
self.disp(u"Error while retrieving last comment: {}".format(e))
|
||||
self.host.quit(1)
|
||||
|
||||
content = mb_data['content_xhtml']
|
||||
if current_syntax != 'XHTML':
|
||||
content = self.host.bridge.syntaxConvert(content, 'XHTML', current_syntax, False, self.profile)
|
||||
f = os.fdopen(fd, 'w+b')
|
||||
f.write(content.encode('utf-8'))
|
||||
f.seek(0)
|
||||
self.edit(sat_conf, tmp_file, f, mb_data['id'])
|
||||
|
||||
|
||||
class Import(base.CommandAnswering):
|
||||
def __init__(self, host):
|
||||
|
|
|
|||
Loading…
Reference in a new issue