issue.html 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314
  1. {% extends "repo_master.html" %}
  2. {% from "_formhelper.html"
  3. import render_bootstrap_field,
  4. show_comment, show_initial_comment, show_attachments %}
  5. {% block title %}Issue #{{ issueid }}: {{issue.title | noJS(ignore="img") | safe }} - {{ repo.name }}{% endblock %}
  6. {% set tag = "home"%}
  7. {% block header %}
  8. <link rel="stylesheet" nonce="{{ g.nonce }}" href="{{
  9. url_for('static', filename='vendor/emojione/emojione.sprites.css') }}?version={{ g.version}}"/>
  10. <link rel="stylesheet" nonce="{{ g.nonce }}" href="{{
  11. url_for('static', filename='vendor/selectize/selectize.bootstrap3.css') }}?version={{ g.version}}"/>
  12. <link rel="stylesheet" nonce="{{ g.nonce }}" href="{{
  13. url_for('static', filename='vendor/jquery.atwho/jquery.atwho.css') }}?version={{ g.version}}"/>
  14. {% endblock %}
  15. {% block repo %}
  16. <div class="d-flex align-items-start">
  17. <h4 class="ml-1">
  18. {% if g.authenticated and (g.repo_user or open_access or g.fas_user.username == issue.user.user) %}
  19. <form action="{{ url_for('ui_ns.update_issue', username=username,
  20. namespace=repo.namespace, repo=repo.name, issueid=issueid)
  21. }}" method="post" class="hidden" id="changestatusform">
  22. {{form.csrf_token}}
  23. <input type="hidden" id="statusform_status" name="status" value=""/>
  24. <input type="hidden" id="statusform_close_status" name="close_status" value=""/>
  25. {% endif %}
  26. {% if g.authenticated and (g.repo_user or open_access) %}
  27. <input type="hidden" id="statusform_tag" name="tag" value=""/>
  28. <input type="hidden" id="statusform_depending" name="depending" value=""/>
  29. <input type="hidden" id="statusform_blocking" name="blocking" value=""/>
  30. <input type="hidden" id="statusform_assignee" name="assignee" value=""/>
  31. <input type="hidden" id="statusform_milestone" name="milestone" value=""/>
  32. <input type="hidden" id="statusform_priority" name="priority" value=""/>
  33. {{form.private}}
  34. {% if repo.issue_keys %}
  35. {% for field in repo.issue_keys %}
  36. <input type="hidden" id="statusform_{{ field.name | replace(' ', '_') }}" name="{{ field.name }}" value=""/>
  37. {% endfor %}
  38. {% endif %}
  39. {% endif %}
  40. {% if g.authenticated and (g.repo_user or open_access or g.fas_user.username == issue.user.user) %}
  41. </form>
  42. {% endif %}
  43. <div>
  44. {% if issue.private %}
  45. <span title="Private ticket" class="text-danger fa fa-fw fa-lock"></span>
  46. {% endif %}
  47. {% if issue.status == 'Open' %}
  48. <span class="fa fa-fw text-success fa-exclamation-circle pt-1"></span>
  49. <span class="text-success font-weight-bold">#{{issue.id}}</span>
  50. {% elif issue.status == 'Closed' %}
  51. <span class="fa fa-fw text-danger fa-exclamation-circle pt-1"></span>
  52. <span class="text-danger font-weight-bold">#{{issue.id}}</span>
  53. {% endif %}
  54. <span class="font-weight-bold">
  55. {{ issue.title | noJS(ignore="img") | safe}}
  56. </span>
  57. {% if g.repo_committer or (
  58. g.fas_user and g.fas_user.username == issue.user.username) %}
  59. <a class="btn btn-outline-secondary btn-sm border-0" href="{{
  60. url_for('ui_ns.edit_issue',
  61. repo=repo.name,
  62. username=username,
  63. namespace=repo.namespace,
  64. issueid=issueid)
  65. }}" title="Edit this issue">
  66. <i class="fa fa-pencil"></i></a>
  67. {% endif %}
  68. </div>
  69. <div>
  70. <small>
  71. {% if issue.status == 'Open' %}
  72. <span data-toggle="tooltip" title="{{issue.date_created | format_datetime}}">
  73. <span class="text-success font-weight-bold">Opened</span> {{ issue.date_created |humanize }}
  74. </span>
  75. <span title="{{ issue.user.html_title }}"> by {{ issue.user.user }}.</span>
  76. <span class="text-muted" data-toggle="tooltip" title="{{issue.last_updated | format_datetime}}">
  77. Modified {{ issue.last_updated |humanize }}
  78. </span>
  79. {% elif issue.status == 'Closed' %}
  80. <span data-toggle="tooltip" title="{{issue.closed_at | format_datetime}}">
  81. <span class="text-danger font-weight-bold">
  82. {% if issue.close_status %}
  83. Closed: {{issue.close_status}}
  84. {% else %}
  85. Closed
  86. {% endif %}
  87. </span> {{ issue.closed_at |humanize }}
  88. </span>
  89. {% if issue.closed_by %}
  90. by
  91. <span title="{{ issue.closed_by.html_title }}">{{ issue.closed_by.user }}.</span>
  92. {% endif %}
  93. <span class="text-muted" data-toggle="tooltip" title="{{issue.date_created | format_datetime}}">
  94. <span class="font-weight-bold">Opened</span> {{ issue.date_created |humanize }}
  95. </span>
  96. <span class="text-muted" title="{{ issue.user.html_title }}">by {{ issue.user.user }}.</span>
  97. {% endif %}
  98. </small>
  99. </div>
  100. </h4>
  101. <div class="ml-auto">
  102. <div class="btn-group">
  103. <div class="dropdown">
  104. {% if g.authenticated and (g.repo_user or open_access or g.fas_user.username == issue.user.user) %}
  105. <a class="font-weight-bold btn btn-sm {{'btn-success' if issue.status=='Open' else 'btn-danger'}} dropdown-toggle pointer"
  106. id="dropdownMenuButton" data-toggle='dropdown' aria-haspopup="true" aria-expanded="false">
  107. {% else %}
  108. <a class="font-weight-bold opacity-100 disabled btn btn-sm {{'btn-success' if issue.status=='Open' else 'btn-danger'}} pointer">
  109. {% endif %}
  110. {% if issue.status == 'Open' %}
  111. Open
  112. {% else %}
  113. {% if issue.close_status %}
  114. Closed: {{issue.close_status}}
  115. {% else %}
  116. Closed
  117. {% endif %}
  118. {% endif %}
  119. </a>
  120. <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
  121. {% if issue.status == 'Open' %}
  122. {% if repo.close_status %}
  123. <h6 class="dropdown-header">Close issue as:</h6>
  124. {% for close_status in repo.close_status %}
  125. <a class="dropdown-item close_status_dropdown_action pointer" data-value="{{close_status}}">{{close_status}}</a>
  126. {% endfor %}
  127. {% else %}
  128. <a class="dropdown-item close_status_dropdown_action pointer" data-value="">Close Issue</a>
  129. {% endif %}
  130. {% else %}
  131. <a class="dropdown-item close_status_dropdown_action pointer">Reopen Issue</a>
  132. {% endif %}
  133. {% if g.repo_committer %}
  134. <div class="dropdown-divider"></div>
  135. <a class="dropdown-item text-danger pointer" id="closeticket"
  136. title="Delete this ticket">
  137. <i class="fa fa-fw fa-trash"></i> Delete Issue
  138. </a>
  139. {% endif %}
  140. </div>
  141. </div>
  142. </div>
  143. </div>
  144. </div>
  145. <form action="{{ url_for('ui_ns.update_issue', username=username,
  146. namespace=repo.namespace, repo=repo.name, issueid=issueid)
  147. }}" method="post" class="mainform">
  148. {{ form.csrf_token }}
  149. <div class="row mt-4">
  150. <div class="col-md-8 mt-2">
  151. {{ show_initial_comment(issue, username, repo,issueid, form) }}
  152. <hr class="mb-1"/>
  153. <section id="comments" class="pt-1">
  154. {% if issue.comments %}
  155. {% for comment in issue.comments %}
  156. {% if comment.notification %}
  157. <div class="d-flex align-items-center px-3 py-2 mb-3">
  158. <div class="">
  159. {{ comment.user.default_email | avatar(16) | safe }}
  160. </div>
  161. <span class="font-size-09 autogenerated-comment pl-4">{{ comment.comment | markdown | noJS | safe }}</span>
  162. <div class="text-muted ml-auto">
  163. <span title="{{ comment.date_created | format_datetime }}">{{
  164. comment.date_created | humanize }}</span>
  165. </div>
  166. </div>
  167. {% else %}
  168. {{ show_comment(comment, comment.id, repo, username, issueid, form) }}
  169. {% endif %}
  170. {% endfor %}
  171. {% endif %}
  172. </section>
  173. {% if g.authenticated and form and not repo.settings.get('issue_tracker_read_only', False) %}
  174. <div class="card mt-5">
  175. <div class="card-header pb-0 pt-1 bg-light">
  176. <div class="row">
  177. <div class="col align-self-center">
  178. <span><strong>Add new comment</strong></span>
  179. </div>
  180. <div class="col">
  181. <ul class="nav nav-tabs float-right border-bottom-0">
  182. <li class="nav-item">
  183. <a class="nav-link pointer" id="previewinmarkdown">Preview</a>
  184. </li>
  185. <li class="nav-item">
  186. <a class="nav-link active pointer" id="editinmarkdown" >Edit</a>
  187. </li>
  188. </ul>
  189. {% if repo.quick_replies %}
  190. {% include "quick_reply.html" %}
  191. {% endif %}
  192. </div>
  193. </div>
  194. </div>
  195. <div class="card-body">
  196. <textarea class="form-control" rows=8 id="comment" name="comment"
  197. placeholder="Enter your comment here" tabindex=1></textarea>
  198. <div id="preview" class="p-1">
  199. </div>
  200. <div class="mt-2">
  201. <label class="custom-file font-size-09">
  202. <input type="file" id="file-picker" class="custom-file-input" name="file" accept="image/*" multiple tabindex=3>
  203. <label class="custom-file-label" for="file-picker">
  204. Browse to attach images or drag them into the comment field
  205. </label>
  206. </label>
  207. <div id="progress" class="progress hidden height-22p">
  208. <div id="progress-bar" class="progress-bar height-22p">0%</div>
  209. </div>
  210. </div>
  211. </div>
  212. <div class="card-footer bg-light">
  213. <div class="d-flex align-items-center">
  214. <small>Comments use <a href="https://docs.pagure.org/pagure/usage/markdown.html"
  215. target="_blank" rel="noopener noreferrer" class="notblue">Markdown Syntax</a></small>
  216. <div class="ml-auto">
  217. <div class="btn-group">
  218. {% if g.authenticated and (g.repo_user or open_access or g.fas_user.username == issue.user.user) %}
  219. {% if issue.status == 'Open' %}
  220. {% if repo.close_status %}
  221. <div class="btn-group">
  222. <a class="btn btn-outline-primary dropdown-toggle pointer"
  223. id="dropdownMenuButton" data-toggle='dropdown' aria-haspopup="true" aria-expanded="false" tabindex=3>
  224. Comment &amp; Close
  225. </a>
  226. <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
  227. <h6 class="dropdown-header">Close issue as:</h6>
  228. {% for close_status in repo.close_status %}
  229. <a class="dropdown-item comment_and_close_action pointer" data-value="{{close_status}}">{{close_status}}</a>
  230. {% endfor %}
  231. </div>
  232. </div>
  233. {% else %}
  234. <a class="btn btn-outline-primary comment_and_close_action pointer" data-value="">
  235. Comment &amp; Close
  236. </a>
  237. {% endif %}
  238. {% else %}
  239. <a class="btn btn-outline-primary comment_and_close_action pointer" data-value="">
  240. Comment &amp; Reopen
  241. </a>
  242. {% endif %}
  243. {% endif %}
  244. <input type="submit" class="btn btn-primary"
  245. value="Comment" tabindex=2 />
  246. </div>
  247. </div>
  248. </div>
  249. </div>
  250. </div>
  251. {% elif g.authenticated and form and repo.settings.get('issue_tracker_read_only', False) %}
  252. <p>
  253. This issue tracker is read-only.
  254. </p>
  255. {% else %}
  256. <p>
  257. <a href="{{ url_for('auth_login', next=request.url) }}">Login</a>
  258. to comment on this ticket.
  259. </p>
  260. {% endif %}
  261. </div>
  262. <div class="col-md-4">
  263. <div>
  264. <div class="mb-4">
  265. <h5 class="d-flex align-items-center font-weight-bold border-bottom">
  266. <div class="py-2 text-uppercase font-size-09">Metadata</div>
  267. {% if g.authenticated and (g.repo_user or g.fas_user.username == issue.user.user or open_access)
  268. and not repo.settings.get('issue_tracker_read_only', False) %}
  269. <div class="ml-auto">
  270. <a class="btn btn-outline-primary border-0 btn-sm issue-metadata-display editmetadatatoggle pointer inline-block"><i class="fa fa-fw fa-pencil"></i></a>
  271. <a class="btn btn-outline-secondary border-0 btn-sm issue-metadata-form hidden editmetadatatoggle pointer hidden"><i class="fa fa-fw fa-times"></i></a>
  272. </div>
  273. {% endif %}
  274. </h5>
  275. {% if g.authenticated and (g.repo_user or g.fas_user.username == issue.user.user) %}
  276. <div class="hidden">
  277. {{form.status}}
  278. {{form.close_status}}
  279. </div>
  280. {% endif%}
  281. <fieldset class="form-group issue-metadata-display mt-4">
  282. <label class="mb-1 pl-1"> <i class="fa fa-fw fa-user-plus"></i> <strong>Assignee</strong></label>
  283. <div id="assignee_plain">
  284. <div class="ml-2" title="{{ issue.assignee.html_title if issue.assignee else '' }}">
  285. {% if issue.assignee %}
  286. <div class="mt-1">{{issue.assignee.username| avatar(size=24) | safe}}
  287. <a href="{{ url_for(
  288. 'ui_ns.view_issues',
  289. repo=repo.name,
  290. username=username,
  291. namespace=repo.namespace,
  292. assignee=issue.assignee.username)
  293. }}" title="{{ issue.assignee.html_title }}">
  294. {{ issue.assignee.username }}
  295. </a>
  296. {% if g.authenticated and (issue.assignee.username == g.fas_user.username) %}
  297. &mdash; <a class="pointer" id="drop-btn"
  298. title="drop the assignment of this issue">
  299. Drop
  300. </a>
  301. {% endif %}
  302. </div>
  303. {% else %}
  304. <div class="text-muted">
  305. None
  306. {% if g.authenticated and (g.repo_user or g.fas_user.username == issue.user.user or open_access) and issue.status|lower == 'open'
  307. and (not issue.assignee or issue.assignee.username != g.fas_user.username)
  308. and not repo.settings.get('issue_tracker_read_only', False) %}
  309. &mdash; <a class="pointer" id="take-btn"
  310. title="assign this issue to you">
  311. Take
  312. </a>
  313. {% endif %}
  314. </div>
  315. {% endif %}
  316. </div>
  317. </div>
  318. </fieldset>
  319. {% if g.authenticated and (g.repo_user or open_access) %}
  320. <fieldset class="form-group issue-metadata-form hidden">
  321. <label for="assignee"><strong>Assignee</strong></label>
  322. <input class="form-control" name="assignee" id="assignee"
  323. placeholder="username"
  324. value="{{ issue.assignee.username or '' }}" />
  325. </fieldset>
  326. {% endif%}
  327. <fieldset class="form-group issue-metadata-display mt-4">
  328. <label class="mb-1"><i class="fa fa-fw fa-tag"></i> <strong>Tags</strong></label>
  329. {% if issue.tags %}
  330. <h4 class="ml-2" id="taglist">
  331. {% for tag in issue.tags %}
  332. <a id="tag-{{ tag.tag }}" title="{{ tag.tag_description }}"
  333. data-bg-color="{{ tag.tag_color }}"
  334. class="badge badge-secondary text-left my-1 p-2 badge-tag"
  335. href="{{ url_for('ui_ns.view_issues',
  336. repo=repo.name,
  337. username=username,
  338. namespace=repo.namespace,
  339. tags=tag.tag) }}">
  340. {{ tag.tag }}
  341. </a>
  342. {% endfor %}
  343. </h4>
  344. {% else %}
  345. <div class="text-muted">None</div>
  346. {% endif%}
  347. </fieldset>
  348. {% if g.authenticated and (g.repo_user or open_access) %}
  349. <fieldset class="form-group issue-metadata-form hidden">
  350. <label for="tag"><strong>Tags</strong></label>
  351. <input id="tag" type="text" placeholder="tag1, tag2" name="tag"
  352. title="comma separated list of tags"
  353. value="{{ issue.tags_text | join(',') }}" />
  354. </fieldset>
  355. {% endif%}
  356. {%macro blocks_item(ticket, itemtype="block") %}
  357. {% if ticket.status|lower == 'open' %}
  358. {% set status_color = "success" %}
  359. {% elif ticket.status|lower == 'merged' %}
  360. {% set status_color = "info" %}
  361. {% else %}
  362. {% set status_color = "danger" %}
  363. {% endif %}
  364. <div class="d-flex align-items-center">
  365. <div class="nowrap">
  366. <span class="fa fa-fw text-{{status_color}} fa-exclamation-circle pt-1"></span>
  367. <span class="text-{{status_color}} font-weight-bold">#{{ticket.id}}</span>
  368. </div>
  369. <div class="ellipsis pl-2 font-size-09">
  370. <a id="{{itemtype}}-{{ ticket.id }}"
  371. href="{{ url_for('ui_ns.view_issue',
  372. repo=repo.name,
  373. username=username,
  374. namespace=repo.namespace,
  375. issueid=ticket.id)
  376. }}" class="notblue">{{ticket.title}}</a>
  377. </div>
  378. </div>
  379. {% endmacro %}
  380. <fieldset class="form-group issue-metadata-display mt-4">
  381. <label class="mb-1 pl-1"> <i class="fa fa-fw fa-ban"></i> <strong>Blocking</strong></label>
  382. <div class="ml-2" id="blocklist">
  383. {% if issue.children %}
  384. {% for ticket in issue.children %}
  385. {{blocks_item(ticket, itemtype="block")}}
  386. {% endfor %}
  387. {% else %}
  388. <div class="text-muted">None</div>
  389. {% endif%}
  390. </div>
  391. </fieldset>
  392. {% if g.authenticated and (g.repo_user or open_access) %}
  393. <fieldset class="form-group issue-metadata-form hidden">
  394. <label for="blocking"><strong>Blocking</strong></label>
  395. <input class="form-control" id="blocking" type="text"
  396. placeholder="issue blocking" name="blocking"
  397. value="{{ issue.blocking_text | join(',') }}" />
  398. </fieldset>
  399. {% endif%}
  400. <fieldset class="form-group issue-metadata-display mt-4">
  401. <label class="mb-1 pl-1"> <i class="fa fa-fw fa-check-circle-o"></i> <strong>Depending on</strong></label>
  402. <div class="ml-2" id="dependlist">
  403. {% if issue.parents %}
  404. {% for ticket in issue.parents %}
  405. {{blocks_item(ticket, itemtype="depend")}}
  406. {% endfor %}
  407. {% else %}
  408. <div class="text-muted">None</div>
  409. {% endif %}
  410. </div>
  411. </fieldset>
  412. {% if g.authenticated and (g.repo_user or open_access) %}
  413. <fieldset class="form-group issue-metadata-form hidden">
  414. <label for="depending"><strong>Depending on</strong></label>
  415. <input class="form-control" id="depending" type="text"
  416. placeholder="issue depending" name="depending"
  417. value="{{ issue.depending_text | join(',') }}" />
  418. </fieldset>
  419. {% endif%}
  420. {% if repo.priorities %}
  421. <fieldset class="form-group issue-metadata-display mt-4">
  422. <label class="mb-1 pl-1"> <i class="fa fa-bolt"></i> <strong>Priority</strong></label>
  423. <div class="ml-2" id="priority_plain">
  424. {% if issue.priority is not none %}
  425. <span >{{ repo.priorities[issue.priority | string] }}</span>
  426. {% else %}
  427. <div class="text-muted">None</div>
  428. {% endif %}
  429. </div>
  430. </fieldset>
  431. {% if g.authenticated and (g.repo_user or open_access) %}
  432. {{ render_bootstrap_field(form.priority,
  433. formclass="issue-metadata-form hidden") }}
  434. {% endif%}
  435. {% endif %}
  436. {% if repo.milestones %}
  437. <fieldset class="form-group issue-metadata-display mt-4">
  438. <label class="mb-1 pl-1"> <i class="fa fa-fw fa-map-signs"></i> <strong>Milestone</strong></label>
  439. <div class="ml-2" id="milestone_plain">
  440. {% if issue.milestone %}
  441. <span>
  442. <a href="{{ url_for(
  443. 'ui_ns.view_milestone',
  444. repo=repo.name,
  445. username=username,
  446. namespace=repo.namespace,
  447. milestone=issue.milestone) }}">
  448. {{ issue.milestone }}
  449. </a>
  450. </span>
  451. {% else %}
  452. <span class="text-muted">None</span>
  453. {% endif %}
  454. </div>
  455. </fieldset>
  456. {% if g.authenticated and (g.repo_user or open_access) %}
  457. {{ render_bootstrap_field(form.milestone,
  458. formclass="issue-metadata-form hidden") }}
  459. {% endif%}
  460. {% endif %}
  461. {% if g.authenticated and (g.repo_user or open_access) %}
  462. {{ render_bootstrap_field(form.private,
  463. formclass="issue-metadata-form hidden") }}
  464. {% endif%}
  465. {% if repo.issue_keys %}
  466. {% for field in repo.issue_keys %}
  467. <fieldset class="form-group issue-metadata-display mt-4">
  468. <label class="mb-0"> <i class="fa fa-fw fa-circle-o"></i> <strong>{{ field.name }}</strong></label>
  469. <div class="pl-2" id="{{ field.name | replace(' ', '_') }}_plain">
  470. {% if field.name in knowns_keys %}
  471. {% if field.key_type == 'link' %}
  472. {% for link in knowns_keys[field.name].value.split(',') %}
  473. <a target="_blank" rel="noopener noreferrer" href="{{ link }}">{{ link }}</a>
  474. <br>
  475. {% endfor %}
  476. {% else %}
  477. {{ knowns_keys[field.name].value }}
  478. {% endif %}
  479. {% else %}
  480. <div class="text-muted">None</div>
  481. {% endif %}
  482. </div>
  483. </fieldset>
  484. {% if g.authenticated and (g.repo_user or open_access) %}
  485. <fieldset class="form-group issue-metadata-form hidden">
  486. <label for="field"><strong> <i class="fa fa-fw fa-circle-o"></i>{{ field.name }}</strong></label>
  487. {% if field.key_type == 'list' %}
  488. <select class="form-control"
  489. name="{{ field.name }}"
  490. id="{{ field.name | replace(' ', '_') }}">
  491. <option value="None">None</option>
  492. {% for item in field.data or [] %}
  493. <option value="{{item}}" {% if field.name in knowns_keys and item == knowns_keys[field.name].value %} selected {% endif %}>
  494. {{ item }}
  495. </option>
  496. {% endfor %}
  497. </select>
  498. {% else %}
  499. <input
  500. {%- if field.key_type == 'boolean' %} type="checkbox" {% endif %}
  501. {%- if field.key_type == 'date'%} type="date" {% endif %}
  502. class="form-control" name="{{ field.name }}" id="{{ field.name }}"
  503. {%- if field.name in knowns_keys %}
  504. {% if field.key_type == 'boolean'%}
  505. {% if knowns_keys[field.name].value in ['true', 'on', '1'] %}checked{% endif %}
  506. {% else %} value="{{ knowns_keys[field.name].value }}"
  507. {% endif %}
  508. {%- endif -%} />
  509. {% endif %}
  510. </fieldset>
  511. {% endif %}
  512. {% endfor %}
  513. {% endif %}
  514. <input type="submit" class="btn btn-primary issue-metadata-form hidden" value="Update">
  515. </div>
  516. </div>
  517. {% if attachments %}
  518. <div class="mt-3">
  519. <h5 class="d-flex align-items-center font-weight-bold border-bottom">
  520. <div class="py-2 text-uppercase font-size-09">
  521. Attachments
  522. <span class="badge badge-secondary badge-pill font-size-09 ml-1" id="attachments-count">{{attachments|count}}</span>
  523. </div>
  524. {#<div class="ml-auto">
  525. <a href="#" class="btn btn-sm btn-link" id="subcribe-btn"
  526. {% if g.fas_user.username in subscribers -%}
  527. title="Unsubscribe from this issue">Unsubscribe
  528. {%- else -%}
  529. title="Subscribe to this issue">Subscribe
  530. {%- endif -%}
  531. </a>
  532. </div>#}
  533. </h5>
  534. {{ show_attachments(attachments) }}
  535. </div>
  536. {% endif %}
  537. {% if g.authenticated %}
  538. <div class="mt-3">
  539. <h5 class="d-flex align-items-center font-weight-bold border-bottom">
  540. <div class="py-2 text-uppercase font-size-09">
  541. Subscribers
  542. <span class="badge badge-secondary badge-pill font-size-09 ml-1" id="subscribers-count">{{subscribers|count}}</span>
  543. </div>
  544. <div class="ml-auto">
  545. <a href="#" class="btn btn-sm btn-link" id="subcribe-btn"
  546. {% if g.fas_user.username in subscribers -%}
  547. title="Unsubscribe from this issue">Unsubscribe
  548. {%- else -%}
  549. title="Subscribe to this issue">Subscribe
  550. {%- endif -%}
  551. </a>
  552. </div>
  553. </h5>
  554. {% if subscribers %}
  555. <div id="subscribers_list" class="p-2">
  556. {% for subscriber in subscribers %}
  557. <a href="{{ url_for('ui_ns.view_user', username=subscriber)
  558. }}" title="{{ subscriber }}" id="sub-avatar-{{subscriber}}">{{
  559. subscriber |avatar(size=30, css_class="pb-1") | safe
  560. }}</a>
  561. {% endfor %}
  562. </div>
  563. {% else %}
  564. <div class="text-center text-muted pt-2">No Subscribers</div>
  565. {% endif %}
  566. </div>
  567. {% endif %}
  568. {% if issue.related_prs %}
  569. <div class="mt-3">
  570. <h5 class="d-flex align-items-center font-weight-bold border-bottom">
  571. <div class="py-2 text-uppercase font-size-09">
  572. Related Pull Requests
  573. </div>
  574. </h5>
  575. <div id="pr_list">
  576. <ul class="list-unstyled">
  577. {% for pr in issue.related_prs %}
  578. <li>
  579. <a class="badge badge-secondary" href="{{ url_for(
  580. 'ui_ns.request_pull',
  581. repo=pr.project.name,
  582. username=pr.project.user.user if pr.project.is_fork else none,
  583. namespace=pr.project.namespace,
  584. requestid=pr.id) }}">#{{pr.id}}</a>
  585. {{ pr.status if pr.status != 'Open' else 'Last updated'
  586. }} {{ pr.last_updated | humanize }}
  587. </li>
  588. {% endfor %}
  589. </ul>
  590. </div>
  591. </div>
  592. {% endif %}
  593. </div>
  594. </div>
  595. </form>
  596. {% endblock %}
  597. {% block jscripts %}
  598. {{ super() }}
  599. <script type="text/javascript" nonce="{{ g.nonce }}">
  600. var UPLOAD_URL = "{{ url_for('ui_ns.upload_issue', repo=repo.name, username=username, namespace=repo.namespace, issueid=issue.id) }}";
  601. </script>
  602. <script type="text/javascript" nonce="{{ g.nonce }}"src="{{
  603. url_for('static', filename='vendor/jquery.textcomplete/jquery.textcomplete.min.js') }}?version={{ g.version}}"></script>
  604. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  605. url_for('static', filename='vendor/emojione/emojione.min.js') }}?version={{ g.version}}"></script>
  606. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  607. url_for('static', filename='emoji/emojicomplete.js') }}?version={{ g.version}}"></script>
  608. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  609. url_for('static', filename='upload.js') }}?version={{ g.version}}"></script>
  610. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  611. url_for('static', filename='vendor/selectize/selectize.min.js') }}?version={{ g.version}}"></script>
  612. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  613. url_for('static', filename='vendor/jquery.caret/jquery.caret.min.js') }}?version={{ g.version}}"></script>
  614. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  615. url_for('static', filename='vendor/jquery.atwho/jquery.atwho.min.js') }}?version={{ g.version}}"></script>
  616. <script type="text/javascript" nonce="{{ g.nonce }}">
  617. {% if g.authenticated and form %}
  618. $(document).ready(function() {
  619. // Set up the drag/drop zone.
  620. initDropbox("{{ form.csrf_token.current_token }}", "#comment");
  621. // Set up the handler for the file input box.
  622. $("#file-picker").on("change", function() {
  623. doUpload("{{ form.csrf_token.current_token }}", this.files);
  624. });
  625. $('.delete_comment_btn').click(function() {
  626. return confirm('Do you really want to remove this comment?');
  627. });
  628. $('.mainform').submit(function() {
  629. return try_async_comment($(this));
  630. });
  631. $('.mainform #assignee').selectize({
  632. valueField: 'user',
  633. labelField: 'user',
  634. searchField: 'user',
  635. maxItems: 1,
  636. create: false,
  637. load: function(query, callback) {
  638. if (!query.length) return callback();
  639. $.getJSON(
  640. "{{ url_for('api_ns.api_users') }}", {
  641. pattern: "*"+query+"*"
  642. },
  643. function( data ) {
  644. callback( data.users.map(function(x) { return { user: x }; }) );
  645. }
  646. );
  647. }
  648. });
  649. $.get("{{ url_for('api_ns.api_users') }}", {
  650. pattern: '*'
  651. }).done(function(resp) {
  652. var userConfig = {
  653. at: '@',
  654. data: resp['mention'],
  655. insertTpl: '@${username}',
  656. displayTpl: "<li><img src=\"${image}\"> ${username} <small>${name}</small></li>",
  657. searchKey: "username"
  658. }
  659. $("#comment").atwho(userConfig);
  660. });
  661. $.when($.get("{{ url_for('api_ns.api_view_issues',
  662. repo=repo.name,
  663. username=username,
  664. namespace=repo.namespace,
  665. status='all') }}"),
  666. $.get("{{ url_for('api_ns.api_pull_request_views',
  667. repo=repo.name,
  668. username=username,
  669. namespace=repo.namespace,
  670. status='all') }}")
  671. ).done(function(issuesResp, prResp) {
  672. // 0 is the api response
  673. var issuesAndPrs = issuesResp[0]['issues'].concat(prResp[0]['requests']);
  674. var data = $.map(issuesAndPrs, function(ticket, idx) {
  675. return {
  676. name: ticket.id.toString(),
  677. title: $('<div>').text(ticket.title).html()
  678. }
  679. });
  680. var issueAndPrConfig = {
  681. at: '#',
  682. data: data,
  683. insertTpl: '#${name}',
  684. displayTpl: "<li>#${name}<small> ${title}</small></li>",
  685. }
  686. $("#comment").atwho(issueAndPrConfig);
  687. })
  688. });
  689. {% endif %}
  690. function setup_edit_btns() {
  691. $(".edit_btn").unbind();
  692. $(".edit_btn").click(function() {
  693. var commentid = $( this ).attr('data-comment');
  694. var _url = '{{ request.base_url }}' + '/comment/' + commentid + '/edit';
  695. $.ajax({
  696. url: _url + '?js=1',
  697. type: 'GET',
  698. dataType: 'html',
  699. success: function(res) {
  700. var el = $('#comment-' + commentid);
  701. var sec = el.parent().find('.issue_comment');
  702. $(sec).hide();
  703. el.parent().find('.issue_actions').hide();
  704. $(sec).after(res);
  705. cancel_edit_btn();
  706. },
  707. error: function() {
  708. alert('Could not make edit work');
  709. }
  710. });
  711. return false;
  712. });
  713. };
  714. function cancel_edit_btn() {
  715. $("#comment_update_cancel").unbind();
  716. $("#comment_update_cancel").click(
  717. function() {
  718. $(this).closest('#comments').find('.issue_comment').show();
  719. $(this).closest('#comments').find('.issue_actions').show();
  720. $(this).closest('.edit_comment').remove();
  721. return false;
  722. });
  723. };
  724. function setup_reply_btns() {
  725. $(".reply").unbind();
  726. $( ".reply" ).click(
  727. function() {
  728. var _section = $(this).closest('.card');
  729. if (!_section.length) {
  730. var _section = $(this).closest('#original_comment_box');
  731. }
  732. var _comment = _section.find('.comment_body');
  733. var _text = _comment.text().split("\n");
  734. var _output = new Array();
  735. for (cnt = 0; cnt < _text.length ; cnt ++) {
  736. _output[cnt] = '> ' + $.trim(_text[cnt]);
  737. }
  738. var _prev = $.trim($( "#comment" ).val());
  739. if (_prev.length > 0){
  740. _prev += "\n\n";
  741. }
  742. $( "#comment" ).val(_prev + _output.join("\n"));
  743. }
  744. ).click(function(){
  745. $('html, body').animate({
  746. scrollTop: $("#comment").offset().top
  747. }, 2000);
  748. });
  749. };
  750. function color_tags() {
  751. $(".badge-tag").each(function(ind, obj) {
  752. $(obj).css('background-color', $(obj).attr('data-bg-color'));
  753. });
  754. }
  755. $(document).ready(function() {
  756. var cur_hash = null;
  757. highlight_comment = function() {
  758. var _hash = window.location.hash;
  759. if (_hash != cur_hash) {
  760. $( cur_hash ).css(
  761. "background", "linear-gradient(to bottom, #ededed 0%, #fff 100%)"
  762. );
  763. };
  764. cur_hash = _hash;
  765. if ( _hash ) {
  766. $( _hash ).css(
  767. "background", "linear-gradient(to bottom, #eded98 0%, #fff 100%)"
  768. );
  769. };
  770. return false;
  771. };
  772. {% if g.repo_user %}
  773. $('#closeticket').click(function(event){
  774. event.preventDefault();
  775. var closeForm = $('<form>', {
  776. 'method': 'POST',
  777. 'action': '{{
  778. url_for('ui_ns.delete_issue',
  779. repo=repo.name,
  780. username=username,
  781. namespace=repo.namespace,
  782. issueid=issueid) }}',
  783. }).append($('<input>', {
  784. 'name': 'csrf_token',
  785. 'value': '{{ form.csrf_token.current_token }}',
  786. 'type': 'hidden'
  787. })).appendTo('body');
  788. if (confirm('Are you sure to delete this ticket? \nThis is final and cannot be un-done.')){
  789. closeForm.submit();
  790. }
  791. return false;
  792. });
  793. {% endif %}
  794. $(window.onload=highlight_comment());
  795. $(window).on('hashchange', highlight_comment);
  796. cancel_edit_btn();
  797. setup_edit_btns();
  798. setup_reply_btns();
  799. color_tags();
  800. });
  801. </script>
  802. {% if config['EVENTSOURCE_SOURCE'] and not issue.private %}
  803. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  804. url_for('static', filename='issue_ev.js') }}?version={{ g.version}}"></script>
  805. {% endif %}
  806. <script type="text/javascript" nonce="{{ g.nonce }}">
  807. var source = null;
  808. var sse = true;
  809. {% if config['EVENTSOURCE_SOURCE'] and not issue.private %}
  810. if (!!window.EventSource) {
  811. source = new EventSource('{{ config["EVENTSOURCE_SOURCE"]
  812. + request.script_root + request.path }}');
  813. source.addEventListener('error', function(e) {
  814. sse = false;
  815. }, false);
  816. }
  817. window.onbeforeunload = function() {
  818. source.close()
  819. };
  820. source.addEventListener('message', function(e) {
  821. console.log(e.data);
  822. var data = $.parseJSON(e.data);
  823. var _issues_url ='{{
  824. url_for('ui_ns.view_issues',
  825. repo=repo.name,
  826. username=username,
  827. namespace=repo.namespace)}}';
  828. var _api_issues_url ='{{
  829. url_for('api_ns.api_view_issue',
  830. repo=repo.name,
  831. username=username,
  832. namespace=repo.namespace,
  833. issueid='-123456789')}}';
  834. var _issue_url ='{{
  835. url_for('ui_ns.view_issue',
  836. repo=repo.name,
  837. username=username,
  838. namespace=repo.namespace,
  839. issueid='-123456789')}}';
  840. var _roadmap_url ='{{
  841. url_for('ui_ns.view_roadmap',
  842. repo=repo.name,
  843. username=username,
  844. namespace=repo.namespace,
  845. milestone='-123456789')}}';
  846. process_event(data, "{{ issue.uid }}", _issue_url,
  847. _issues_url, _api_issues_url, _roadmap_url,
  848. "{{ g.fas_user.username if g.authenticated or '' }}");
  849. setup_edit_btns();
  850. setup_reply_btns();
  851. }, false);
  852. {% else %}
  853. sse = false;
  854. {% endif %}
  855. {% if g.authenticated and form %}
  856. function set_ui_for_comment(setting){
  857. if (setting == false) {
  858. $(document.body).find('input[type="submit"]').removeAttr("disabled");
  859. document.body.style.cursor = 'default';
  860. } else {
  861. $(document.body).find('input[type="submit"]').attr("disabled", "disabled");
  862. document.body.style.cursor = 'wait';
  863. }
  864. }
  865. function try_async_comment(form) {
  866. console.log('Submitting form:');
  867. console.log(form);
  868. set_ui_for_comment(true);
  869. var _data = $(form).serialize();
  870. var btn = $(document.activeElement);
  871. if (btn[0].name == 'drop_comment'){
  872. _data += '&drop_comment=' + btn[0].value;
  873. set_ui_for_comment(false);
  874. return true;
  875. }
  876. if (!sse || source.readyState != 1) {
  877. $(form).off('submit');
  878. form.submit();
  879. return false;
  880. }
  881. var _url = form.attr("action") + "?js=1";
  882. $.post( _url, _data )
  883. .done(function(data) {
  884. if(data == 'ok') {
  885. {# The event-source server will automatically refresh the UI #}
  886. $('#comment').val('');
  887. $('#preview').html('');
  888. $('#previewinmarkdown').addClass('inactive');
  889. $('#previewinmarkdown').removeClass('active');
  890. $('#preview').hide();
  891. $('#comment').show();
  892. $('#comments').find('.comment_body').show();
  893. $('#comments').find('.edit_comment').remove();
  894. $( ".issue-metadata-form" ).hide();
  895. $( ".issue-metadata-display" ).show();
  896. set_ui_for_comment(false);
  897. } else {
  898. // Make the browser submit the form sync
  899. $(form).off('submit');
  900. form.submit();
  901. }
  902. })
  903. .fail(function() {
  904. // Make the browser submit the form sync
  905. $(form).off('submit');
  906. form.submit();
  907. })
  908. return false;
  909. };
  910. {% endif %}
  911. </script>
  912. <script type="text/javascript" nonce="{{ g.nonce }}">
  913. {% if g.authenticated and (g.repo_user or issue.user.user == g.fas_user.username or open_access) %}
  914. function take_issue(){
  915. var _url = "{{ url_for('api_ns.api_assign_issue',
  916. repo=repo.name, namespace=repo.namespace, username=username,
  917. issueid=issueid) }}";
  918. var _data = {assignee: "{{ g.fas_user.username }}"};
  919. $.post (_url, _data ).done(
  920. function(data) {
  921. var _user_url = '\n<div class="ml-2"><div class="mt-1">{{g.fas_user.username| avatar(size=24) | safe}} '
  922. + '<a href="{{ url_for("ui_ns.view_issues", repo=repo.name, username=username, namespace=repo.namespace) }}'
  923. + '?assignee={{ g.fas_user.username }}">'
  924. + '{{ g.fas_user.username }}</a>'
  925. + ' &mdash; <a id="drop-btn" title="drop the assignment of this issue" '
  926. + 'class="pointer">Drop</a></div></div>';
  927. $('#assignee_plain').html(_user_url);
  928. $('#assignee').val("{{ g.fas_user.username }}");
  929. setup_btn_take_drop();
  930. }
  931. ).fail(function() {
  932. alert( "An error occured, could not assign this ticket to you." );
  933. })
  934. return false;
  935. }
  936. {% endif %}
  937. {% if g.authenticated and (
  938. g.repo_user
  939. or issue.user.user == g.fas_user.username
  940. or issue.assignee.user == g.fas_user.username) %}
  941. function drop_issue(){
  942. var _url = "{{ url_for('api_ns.api_assign_issue',
  943. repo=repo.name, namespace=repo.namespace, username=username,
  944. issueid=issueid) }}";
  945. var _data = {assignee: ""};
  946. $.post( _url, _data ).done(
  947. function(data) {
  948. var _user_url = '<div class="ml-2">\n<span class="text-muted">None</span>'
  949. + ' &mdash; <a id="take-btn" title="assign this issue to you" '
  950. + 'class="pointer">Take</a></div>';
  951. $('#assignee_plain').html(_user_url);
  952. $('#assignee').val("");
  953. setup_btn_take_drop();
  954. }
  955. ).fail(function() {
  956. alert( "An error occured, could not drop the current assignee." );
  957. })
  958. return false;
  959. }
  960. {% endif %}
  961. function setup_btn_take_drop(){
  962. {% if g.authenticated and (g.repo_user or open_access) %}
  963. $("#take-btn").click(take_issue)
  964. {% endif %}
  965. {% if g.authenticated and (
  966. g.repo_user
  967. or issue.user.user == g.fas_user.username
  968. or issue.assignee.user == g.fas_user.username) %}
  969. $("#drop-btn").click(drop_issue);
  970. {% endif %}
  971. }
  972. $( document ).ready(function() {
  973. $(".close_status_dropdown_action").click(function(event){
  974. var status = "{{issue.status}}";
  975. if (status == "Open") {
  976. $("#changestatusform #statusform_status").val("Closed");
  977. $("#changestatusform #statusform_close_status").val($(this).attr("data-value"));
  978. } else {
  979. $("#changestatusform #statusform_status").val("Open");
  980. }
  981. $("#changestatusform #statusform_assignee").val($("#assignee").val());
  982. $("#changestatusform #statusform_tag").val($("#tag").val());
  983. $("#changestatusform #statusform_priority").val($("#priority").val());
  984. $("#changestatusform #statusform_milestone").val($("#milestone").val());
  985. $("#changestatusform #statusform_blocking").val($("#blocking").val());
  986. $("#changestatusform #statusform_depending").val($("#depending").val());
  987. {% if repo.issue_keys %}
  988. {% for field in repo.issue_keys %}
  989. $("#changestatusform #statusform_{{ field.name | replace(' ', '_') }}").val($("#{{ field.name | replace(' ', '_') }}").val());
  990. {% endfor %}
  991. {% endif %}
  992. $("#changestatusform").submit();
  993. });
  994. $(".comment_and_close_action").click(function(event){
  995. var status = "{{issue.status}}";
  996. if (status == "Open") {
  997. $(".mainform #status").val("Closed");
  998. $(".mainform #close_status").val($(this).attr("data-value"));
  999. } else {
  1000. $(".mainform #status").val("Open");
  1001. }
  1002. $(".mainform").submit();
  1003. });
  1004. var emojiStrategy;
  1005. $.getJSON(
  1006. '{{ url_for("static", filename="vendor/emojione/emoji_strategy.json") }}',
  1007. function( data ) {
  1008. emojiStrategy = data;
  1009. }
  1010. );
  1011. var folder = '{{url_for("static", filename="emoji/png/") }}?version={{ g.version}}';
  1012. var json_url = '{{ url_for("static", filename="vendor/emojione/emoji_strategy.json") }}?version={{ g.version}}';
  1013. emoji_complete(json_url, folder);
  1014. $(".comment_body").each(function(ind, obj) {
  1015. var source = $(obj).html();
  1016. var preview = emojione.toImage(source);
  1017. $(obj).html(preview);
  1018. });
  1019. $( ".editmetadatatoggle" ).click(
  1020. function() {
  1021. $( ".issue-metadata-form" ).toggle();
  1022. $( ".issue-metadata-display" ).toggle();
  1023. }
  1024. );
  1025. function _get_issues(url, callback){
  1026. $.getJSON(
  1027. url,
  1028. function( data ) {
  1029. issues = data.issues.filter(function(el) {
  1030. return el.id !== {{issue.id}};
  1031. });
  1032. callback(issues);
  1033. if (data.pagination.next){
  1034. _get_issues(data.pagination.next, callback)
  1035. }
  1036. }
  1037. );
  1038. }
  1039. $('.mainform #blocking').selectize({
  1040. plugins: ['remove_button'],
  1041. valueField: 'id',
  1042. labelField: 'id',
  1043. searchField: ['id', 'title'],
  1044. preload: 'focus',
  1045. render: {
  1046. option: function(item, escape) {
  1047. return '<div><span>'+escape(item.id)+'</span> <span>'+escape(item.title)+'</span></div>';
  1048. },
  1049. item: function(item, escape) {
  1050. return '<div><span>#'+escape(item.id)+'</span></div>';
  1051. },
  1052. },
  1053. create: false,
  1054. load: function(query, callback) {
  1055. if (!query){
  1056. callback();
  1057. return;
  1058. };
  1059. var _url = "{{ url_for('api_ns.api_view_issues',
  1060. repo=repo.name,
  1061. username=username,
  1062. namespace=repo.namespace) }}" + "?query_id=" + query;
  1063. _get_issues(_url, callback);
  1064. }
  1065. });
  1066. $('.mainform #depending').selectize({
  1067. plugins: ['remove_button'],
  1068. valueField: 'id',
  1069. labelField: 'id',
  1070. searchField: ['id', 'title'],
  1071. preload: 'focus',
  1072. render: {
  1073. option: function(item, escape) {
  1074. return '<div><span>'+escape(item.id)+'</span> <span>'+escape(item.title)+'</span></div>';
  1075. },
  1076. item: function(item, escape) {
  1077. return '<div><span>#'+escape(item.id)+'</span></div>';
  1078. },
  1079. },
  1080. create: false,
  1081. load: function(query, callback) {
  1082. if (!query){
  1083. callback();
  1084. return;
  1085. };
  1086. var _url = "{{ url_for('api_ns.api_view_issues',
  1087. repo=repo.name,
  1088. username=username,
  1089. namespace=repo.namespace) }}" + "?query_id=" + query;
  1090. _get_issues(_url, callback);
  1091. }
  1092. });
  1093. var available_tags = [];
  1094. {%for tog in tag_list %}
  1095. available_tags.push("{{tog.tag}}");
  1096. {%endfor%}
  1097. var items = available_tags.map(function(x) { return { item: x }; });
  1098. $('.mainform #tag').selectize({
  1099. delimiter: ',',
  1100. options: items,
  1101. persist: false,
  1102. create: false,
  1103. labelField: "item",
  1104. valueField: "item",
  1105. searchField: ["item"],
  1106. });
  1107. $( "#preview" ).hide();
  1108. $( "#previewinmarkdown" ).click(
  1109. function(event, ui) {
  1110. var _text = $( "#comment" ).val();
  1111. var _url = "{{ url_for('ui_ns.markdown_preview',
  1112. repo=repo.name,
  1113. user=repo.user.user if repo.is_fork,
  1114. namespace=repo.namespace) | safe}}";
  1115. $.ajax({
  1116. url: _url ,
  1117. type: 'POST',
  1118. data: {
  1119. content: _text,
  1120. csrf_token: "{{ g.confirmationform.csrf_token.current_token }}",
  1121. },
  1122. dataType: 'html',
  1123. success: function(res) {
  1124. var preview = emojione.toImage(res);
  1125. $( "#preview" ).html(preview);
  1126. $( "#previewinmarkdown" ).addClass("active");
  1127. $( "#editinmarkdown" ).removeClass("active");
  1128. $( "#comment" ).hide();
  1129. $( "#preview" ).show();
  1130. },
  1131. error: function() {
  1132. alert('Unable to generate preview!'+error);
  1133. }
  1134. });
  1135. return false;
  1136. }
  1137. );
  1138. $( "#editinmarkdown" ).click(
  1139. function(event, ui) {
  1140. $( "#editinmarkdown" ).addClass("active");
  1141. $( "#previewinmarkdown" ).removeClass("active");
  1142. $( "#comment" ).show();
  1143. $( "#preview" ).hide();
  1144. }
  1145. );
  1146. function submitFormOnCtrlKey(event) {
  1147. if (event.ctrlKey && event.keyCode == 13) {
  1148. var form = event.target.form;
  1149. form.submit();
  1150. event.preventDefault();
  1151. }
  1152. }
  1153. $('#comment').keydown(function(e) {
  1154. submitFormOnCtrlKey(e);
  1155. });
  1156. {% if g.authenticated and (
  1157. g.repo_user
  1158. or open_access
  1159. or issue.user.user == g.fas_user.username
  1160. or issue.assignee.user == g.fas_user.username) %}
  1161. setup_btn_take_drop();
  1162. {% endif %}
  1163. {% if g.authenticated %}
  1164. function set_up_subcribed() {
  1165. $("#subcribe-btn").click(function(){
  1166. var _url = "{{ url_for(
  1167. 'api_ns.api_subscribe_issue',
  1168. repo=repo.name,
  1169. username=username,
  1170. namespace=repo.namespace,
  1171. issueid=issueid
  1172. ) }}";
  1173. var _btn = $("#subcribe-btn");
  1174. var _data = {};
  1175. if (_btn.text() == 'Unsubscribe'){
  1176. _data.status = false;
  1177. } else {
  1178. _data.status = true;
  1179. }
  1180. $.post( _url, _data ).done(
  1181. function(data) {
  1182. var _btn = $("#subcribe-btn");
  1183. var _countlabel = $("#subscribers-count")
  1184. var _count = parseInt(_countlabel.text())
  1185. if (_btn.text() == 'Subscribe'){
  1186. _btn.text('Unsubscribe');
  1187. _countlabel.text(_count+1)
  1188. var _html = '<a href="/user/' + data.user + '"'
  1189. + 'title="'+data.user+'" id="sub-avatar-'+data.user+'">'
  1190. + '<img src="'+data.avatar_url+'" class="pb-1"></a>';
  1191. $('#subscribers_list').prepend(_html);
  1192. } else {
  1193. _btn.text('Subscribe');
  1194. _countlabel.text(_count-1);
  1195. $('#sub-avatar-'+data.user).remove();
  1196. }
  1197. return false;
  1198. }
  1199. )
  1200. return false;
  1201. });
  1202. };
  1203. set_up_subcribed();
  1204. {% endif %}
  1205. });
  1206. </script>
  1207. {% if repo.quick_replies %}
  1208. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  1209. url_for('static', filename='quick_reply.js') }}?version={{ g.version}}"></script>
  1210. {% endif %}
  1211. <script type="text/javascript" nonce="{{ g.nonce }}" src="{{
  1212. url_for('static', filename='reactions.js') }}?version={{ g.version}}"></script>
  1213. {% endblock %}