design.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. #coding: utf8
  2. from datetime import datetime
  3. from tracim.models.data import VirtualEvent
  4. from tracim.models.data import ContentType
  5. from tracim.models import data
  6. # FIXME: fix temporaire ...
  7. style = """
  8. .title {
  9. background:#F5F5F5;
  10. padding-right:15px;
  11. padding-left:15px;
  12. padding-top:10px;
  13. border-bottom:1px solid #CCCCCC;
  14. overflow:auto;
  15. } .title h1 { margin-top:0; }
  16. .content {
  17. padding: 15px;
  18. }
  19. #left{ padding:0; }
  20. #right {
  21. background:#F5F5F5;
  22. border-left:1px solid #CCCCCC;
  23. border-bottom: 1px solid #CCCCCC;
  24. padding-top:15px;
  25. }
  26. @media (max-width: 1200px) {
  27. #right {
  28. border-top:1px solid #CCCCCC;
  29. border-left: none;
  30. border-bottom: none;
  31. }
  32. }
  33. body { overflow:auto; }
  34. .btn {
  35. text-align: left;
  36. }
  37. .table tbody tr .my-align {
  38. vertical-align:middle;
  39. }
  40. .title-icon {
  41. font-size:2.5em;
  42. float:left;
  43. margin-right:10px;
  44. }
  45. .title.page, .title-icon.page { color:#00CC00; }
  46. .title.thread, .title-icon.thread { color:#428BCA; }
  47. /* ****************************** */
  48. .description-icon {
  49. color:#999;
  50. font-size:3em;
  51. }
  52. .description {
  53. border-left: 5px solid #999;
  54. padding-left: 10px;
  55. margin-left: 10px;
  56. margin-bottom:10px;
  57. }
  58. .description-text {
  59. display:block;
  60. overflow:hidden;
  61. color:#999;
  62. }
  63. .comment-row:nth-child(2n) {
  64. background-color:#F5F5F5;
  65. }
  66. .comment-row:nth-child(2n+1) {
  67. background-color:#FFF;
  68. }
  69. .comment-icon {
  70. color:#CCC;
  71. font-size:3em;
  72. display:inline-block;
  73. margin-right: 10px;
  74. float:left;
  75. }
  76. .comment-content {
  77. display:block;
  78. overflow:hidden;
  79. }
  80. .comment, .comment-revision {
  81. padding:10px;
  82. border-top: 1px solid #999;
  83. }
  84. .comment-revision-icon {
  85. color:#777;
  86. margin-right: 10px;
  87. }
  88. .title-text {
  89. display: inline-block;
  90. }
  91. """
  92. _LABELS = {
  93. 'archiving': 'Item archived',
  94. 'content-comment': 'Item commented',
  95. 'creation': 'Item created',
  96. 'deletion': 'Item deleted',
  97. 'edition': 'item modified',
  98. 'revision': 'New revision',
  99. 'status-update': 'New status',
  100. 'unarchiving': 'Item unarchived',
  101. 'undeletion': 'Item undeleted',
  102. 'move': 'Item moved',
  103. 'comment': 'Comment',
  104. 'copy' : 'Item copied',
  105. }
  106. def create_readable_date(created, delta_from_datetime: datetime = None):
  107. if not delta_from_datetime:
  108. delta_from_datetime = datetime.now()
  109. delta = delta_from_datetime - created
  110. if delta.days > 0:
  111. if delta.days >= 365:
  112. aff = '%d year%s ago' % (delta.days / 365, 's' if delta.days / 365 >= 2 else '')
  113. elif delta.days >= 30:
  114. aff = '%d month%s ago' % (delta.days / 30, 's' if delta.days / 30 >= 2 else '')
  115. else:
  116. aff = '%d day%s ago' % (delta.days, 's' if delta.days >= 2 else '')
  117. else:
  118. if delta.seconds < 60:
  119. aff = '%d second%s ago' % (delta.seconds, 's' if delta.seconds > 1 else '')
  120. elif delta.seconds / 60 < 60:
  121. aff = '%d minute%s ago' % (delta.seconds / 60, 's' if delta.seconds / 60 >= 2 else '')
  122. else:
  123. aff = '%d hour%s ago' % (delta.seconds / 3600, 's' if delta.seconds / 3600 >= 2 else '')
  124. return aff
  125. def designPage(content: data.Content, content_revision: data.ContentRevisionRO) -> str:
  126. hist = content.get_history(drop_empty_revision=False)
  127. histHTML = '<table class="table table-striped table-hover">'
  128. for event in hist:
  129. if isinstance(event, VirtualEvent):
  130. date = event.create_readable_date()
  131. label = _LABELS[event.type.id]
  132. histHTML += '''
  133. <tr class="%s">
  134. <td class="my-align"><span class="label label-default"><i class="fa %s"></i> %s</span></td>
  135. <td>%s</td>
  136. <td>%s</td>
  137. <td>%s</td>
  138. </tr>
  139. ''' % ('warning' if event.id == content_revision.revision_id else '',
  140. event.type.fa_icon,
  141. label,
  142. date,
  143. event.owner.display_name,
  144. # NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0
  145. '<i class="fa fa-caret-left"></i> shown' if event.id == content_revision.revision_id else '' # '''<span><a class="revision-link" href="/.history/%s/(%s - %s) %s.html">(View revision)</a></span>''' % (
  146. # content.label, event.id, event.type.id, event.ref_object.label) if event.type.id in ['revision', 'creation', 'edition'] else '')
  147. )
  148. histHTML += '</table>'
  149. page = '''
  150. <html>
  151. <head>
  152. <meta charset="utf-8" />
  153. <title>%s</title>
  154. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  155. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
  156. <style>%s</style>
  157. <script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
  158. <script
  159. src="https://code.jquery.com/jquery-3.1.0.min.js"
  160. integrity="sha256-cCueBR6CsyA4/9szpPfrX3s49M9vUU5BgtiJj06wt/s="
  161. crossorigin="anonymous"></script>
  162. </head>
  163. <body>
  164. <div id="left" class="col-lg-8 col-md-12 col-sm-12 col-xs-12">
  165. <div class="title page">
  166. <div class="title-text">
  167. <i class="fa fa-file-text-o title-icon page"></i>
  168. <h1>%s</h1>
  169. <h6>page created on <b>%s</b> by <b>%s</b></h6>
  170. </div>
  171. <div class="pull-right">
  172. <div class="btn-group btn-group-vertical">
  173. <!-- NOTE: Not omplemented yet, don't display not working link
  174. <a class="btn btn-default">
  175. <i class="fa fa-external-link"></i> View in tracim</a>
  176. </a>-->
  177. </div>
  178. </div>
  179. </div>
  180. <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12">
  181. %s
  182. </div>
  183. </div>
  184. <div id="right" class="col-lg-4 col-md-12 col-sm-12 col-xs-12">
  185. <h4>History</h4>
  186. %s
  187. </div>
  188. <script type="text/javascript">
  189. window.onload = function() {
  190. file_location = window.location.href
  191. file_location = file_location.replace(/\/[^/]*$/, '')
  192. file_location = file_location.replace(/\/.history\/[^/]*$/, '')
  193. // NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0
  194. // $('.revision-link').each(function() {
  195. // $(this).attr('href', file_location + $(this).attr('href'))
  196. // });
  197. }
  198. </script>
  199. </body>
  200. </html>
  201. ''' % (content_revision.label,
  202. style,
  203. content_revision.label,
  204. content.created.strftime("%B %d, %Y at %H:%m"),
  205. content.owner.display_name,
  206. content_revision.description,
  207. histHTML)
  208. return page
  209. def designThread(content: data.Content, content_revision: data.ContentRevisionRO, comments) -> str:
  210. hist = content.get_history(drop_empty_revision=False)
  211. allT = []
  212. allT += comments
  213. allT += hist
  214. allT.sort(key=lambda x: x.created, reverse=True)
  215. disc = ''
  216. participants = {}
  217. for t in allT:
  218. if t.type == ContentType.Comment:
  219. disc += '''
  220. <div class="row comment comment-row">
  221. <i class="fa fa-comment-o comment-icon"></i>
  222. <div class="comment-content">
  223. <h5>
  224. <span class="comment-author"><b>%s</b> wrote :</span>
  225. <div class="pull-right text-right">%s</div>
  226. </h5>
  227. %s
  228. </div>
  229. </div>
  230. ''' % (t.owner.display_name, create_readable_date(t.created), t.description)
  231. if t.owner.display_name not in participants:
  232. participants[t.owner.display_name] = [1, t.created]
  233. else:
  234. participants[t.owner.display_name][0] += 1
  235. else:
  236. if isinstance(t, VirtualEvent) and t.type.id != 'comment':
  237. label = _LABELS[t.type.id]
  238. disc += '''
  239. <div class="%s row comment comment-row to-hide">
  240. <i class="fa %s comment-icon"></i>
  241. <div class="comment-content">
  242. <h5>
  243. <span class="comment-author"><b>%s</b></span>
  244. <div class="pull-right text-right">%s</div>
  245. </h5>
  246. %s %s
  247. </div>
  248. </div>
  249. ''' % ('warning' if t.id == content_revision.revision_id else '',
  250. t.type.fa_icon,
  251. t.owner.display_name,
  252. t.create_readable_date(),
  253. label,
  254. # NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0
  255. '<i class="fa fa-caret-left"></i> shown' if t.id == content_revision.revision_id else '' # else '''<span><a class="revision-link" href="/.history/%s/%s-%s">(View revision)</a></span>''' % (
  256. # content.label,
  257. # t.id,
  258. # t.ref_object.label) if t.type.id in ['revision', 'creation', 'edition'] else '')
  259. )
  260. thread = '''
  261. <html>
  262. <head>
  263. <meta charset="utf-8" />
  264. <title>%s</title>
  265. <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
  266. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  267. <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css">
  268. <style>%s</style>
  269. <script type="text/javascript" src="/home/arnaud/Documents/css/script.js"></script>
  270. </head>
  271. <body>
  272. <div id="left" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
  273. <div class="title thread">
  274. <div class="title-text">
  275. <i class="fa fa-comments-o title-icon thread"></i>
  276. <h1>%s</h1>
  277. <h6>thread created on <b>%s</b> by <b>%s</b></h6>
  278. </div>
  279. <div class="pull-right">
  280. <div class="btn-group btn-group-vertical">
  281. <!-- NOTE: Not omplemented yet, don't display not working link
  282. <a class="btn btn-default" onclick="hide_elements()">
  283. <i id="hideshow" class="fa fa-eye-slash"></i> <span id="hideshowtxt" >Hide history</span></a>
  284. </a>-->
  285. <a class="btn btn-default">
  286. <i class="fa fa-external-link"></i> View in tracim</a>
  287. </a>
  288. </div>
  289. </div>
  290. </div>
  291. <div class="content col-xs-12 col-sm-12 col-md-12 col-lg-12">
  292. <div class="description">
  293. <span class="description-text">%s</span>
  294. </div>
  295. %s
  296. </div>
  297. </div>
  298. <script type="text/javascript">
  299. window.onload = function() {
  300. file_location = window.location.href
  301. file_location = file_location.replace(/\/[^/]*$/, '')
  302. file_location = file_location.replace(/\/.history\/[^/]*$/, '')
  303. // NOTE: (WABDAV_HIST_DEL_DISABLED) Disabled for beta 1.0
  304. // $('.revision-link').each(function() {
  305. // $(this).attr('href', file_location + $(this).attr('href'))
  306. // });
  307. }
  308. function hide_elements() {
  309. elems = document.getElementsByClassName('to-hide');
  310. if (elems.length > 0) {
  311. for(var i = 0; i < elems.length; i++) {
  312. $(elems[i]).addClass('to-show')
  313. $(elems[i]).hide();
  314. }
  315. while (elems.length>0) {
  316. $(elems[0]).removeClass('comment-row');
  317. $(elems[0]).removeClass('to-hide');
  318. }
  319. $('#hideshow').addClass('fa-eye').removeClass('fa-eye-slash');
  320. $('#hideshowtxt').html('Show history');
  321. }
  322. else {
  323. elems = document.getElementsByClassName('to-show');
  324. for(var i = 0; i<elems.length; i++) {
  325. $(elems[0]).addClass('comment-row');
  326. $(elems[i]).addClass('to-hide');
  327. $(elems[i]).show();
  328. }
  329. while (elems.length>0) {
  330. $(elems[0]).removeClass('to-show');
  331. }
  332. $('#hideshow').removeClass('fa-eye').addClass('fa-eye-slash');
  333. $('#hideshowtxt').html('Hide history');
  334. }
  335. }
  336. </script>
  337. </body>
  338. </html>
  339. ''' % (content_revision.label,
  340. style,
  341. content_revision.label,
  342. content.created.strftime("%B %d, %Y at %H:%m"),
  343. content.owner.display_name,
  344. content_revision.description,
  345. disc)
  346. return thread