comparison frontends/src/jp/cmd_blog.py @ 1866:397ef87958b9

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
author Goffi <goffi@goffi.org>
date Tue, 01 Mar 2016 01:54:21 +0100
parents 96ba685162f6
children 28b29381db75
comparison
equal deleted inserted replaced
1865:fc6eeacf31bc 1866:397ef87958b9
19 19
20 20
21 import base 21 import base
22 from sat.core.i18n import _ 22 from sat.core.i18n import _
23 from sat.core.constants import Const as C 23 from sat.core.constants import Const as C
24 from sat.tools import config
24 import json 25 import json
26 import os.path
27 import os
28 import time
29 import tempfile
30 import subprocess
31 from sat.tools import common
32
33 __commands__ = ["Blog"]
34
35 SYNTAX_EXT = { '': 'txt', # used when the syntax is not found
36 "XHTML": "xhtml",
37 "markdown": "md"
38 }
39 CONF_SYNTAX_EXT = 'syntax_ext_dict'
40 BLOG_TMP_DIR="blog"
41
25 URL_REDIRECT_PREFIX = 'url_redirect_' 42 URL_REDIRECT_PREFIX = 'url_redirect_'
26 43
27 __commands__ = ["Blog"] 44
45 class Edit(base.CommandBase):
46
47 def __init__(self, host):
48 super(Edit, self).__init__(host, 'edit', use_verbose=True, help=_(u'edit an existing or new blog post'))
49
50 def add_parser_options(self):
51 self.parser.add_argument("item", type=base.unicode_decoder, nargs='?', default=u'new', help=_(u"URL of the item to edit, or keyword"))
52 self.parser.add_argument("-T", '--title', type=base.unicode_decoder, help=_(u"Title of the item"))
53 self.parser.add_argument("-t", '--tag', type=base.unicode_decoder, action='append', help=_(u"tag (category) of your item"))
54 self.parser.add_argument("--no-comment", action='store_true', help=_(u"disable comments"))
55
56 def getTmpFile(self, sat_conf, tmp_suff):
57 local_dir = config.getConfig(sat_conf, '', 'local_dir', Exception)
58 tmp_dir = os.path.join(local_dir, BLOG_TMP_DIR)
59 if not os.path.exists(tmp_dir):
60 try:
61 os.makedirs(tmp_dir)
62 except OSError as e:
63 self.disp(u"Can't create {path} directory: {reason}".format(
64 path=tmp_dir, reason=e), error=True)
65 self.host.quit(1)
66
67 try:
68 return tempfile.mkstemp(suffix=tmp_suff,
69 prefix=time.strftime('blog_%Y-%m-%d_%H:%M:%S_'),
70 dir=tmp_dir, text=True)
71 except OSError as e:
72 self.disp(u"Can't create temporary file: {reason}".format(reason=e), error=True)
73 self.host.quit(1)
74
75 def edit(self, sat_conf, tmp_file, tmp_file_obj, item_id=None):
76 """Edit the file contening the content using editor, and publish it"""
77 # we first calculate hash to check for modifications
78 import hashlib
79 tmp_file_obj.seek(0)
80 ori_hash = hashlib.sha1(tmp_file_obj.read()).digest()
81 tmp_file_obj.close()
82
83 # the we launch editor
84 editor = config.getConfig(sat_conf, 'jp', 'editor') or os.getenv('EDITOR', 'vi')
85 editor_exit = subprocess.call([editor, tmp_file])
86
87 # we send the file if edition was a success
88 if editor_exit != 0:
89 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(
90 path=tmp_file), error=True)
91 else:
92 with open(tmp_file, 'rb') as f:
93 content = f.read()
94
95 if len(content) == 0:
96 self.disp(u"Content is empty, cancelling the blog edition")
97
98 # time to re-check the hash
99 elif ori_hash == hashlib.sha1(content).digest():
100 self.disp(u"The content has not been modified, cancelling the blog edition")
101
102 else:
103 # we can now send the blog
104 mb_data = {
105 'content_rich': content.decode('utf-8'),
106 'allow_comments': C.boolConst(not self.args.no_comment),
107 }
108 if item_id:
109 mb_data['id'] = item_id
110 if self.args.tag:
111 common.iter2dict('tag', self.args.tag, mb_data)
112
113 if self.args.title is not None:
114 mb_data['title'] = self.args.title
115 try:
116 self.host.bridge.mbSend('', '', mb_data, self.profile)
117 except Exception as e:
118 self.disp(u"Error while sending your blog, the temporary file has been kept at {path}: {reason}".format(
119 path=tmp_file, reason=e), error=True)
120 self.host.quit(1)
121
122 os.unlink(tmp_file)
123
124 def start(self):
125 # we get current syntax to determine file extension
126 current_syntax = self.host.bridge.getParamA("Syntax", "Composition", "value", self.profile)
127 self.disp(u"Current syntax: {}".format(current_syntax), 1)
128 sat_conf = config.parseMainConf()
129 # if there are user defined extension, we use them
130 SYNTAX_EXT.update(config.getConfig(sat_conf, 'jp', CONF_SYNTAX_EXT, {}))
131
132 # we now create a temporary file
133 tmp_suff = '.' + SYNTAX_EXT.get(current_syntax, SYNTAX_EXT[''])
134 fd, tmp_file = self.getTmpFile(sat_conf, tmp_suff)
135
136 item_lower = self.args.item.lower()
137 if item_lower == 'new':
138 self.disp(u'Editing a new blog item', 2)
139 self.edit(sat_conf, tmp_file, os.fdopen(fd))
140 elif item_lower == 'last':
141 self.disp(u'Editing last published item', 2)
142 try:
143 mb_data = self.host.bridge.mbGet('', '', 1, [], {}, self.profile)[0][0]
144 except Exception as e:
145 self.disp(u"Error while retrieving last comment: {}".format(e))
146 self.host.quit(1)
147
148 content = mb_data['content_xhtml']
149 if current_syntax != 'XHTML':
150 content = self.host.bridge.syntaxConvert(content, 'XHTML', current_syntax, False, self.profile)
151 f = os.fdopen(fd, 'w+b')
152 f.write(content.encode('utf-8'))
153 f.seek(0)
154 self.edit(sat_conf, tmp_file, f, mb_data['id'])
28 155
29 156
30 class Import(base.CommandAnswering): 157 class Import(base.CommandAnswering):
31 def __init__(self, host): 158 def __init__(self, host):
32 super(Import, self).__init__(host, 'import', use_progress=True, help=_(u'import an external blog')) 159 super(Import, self).__init__(host, 'import', use_progress=True, help=_(u'import an external blog'))