user_settings.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. {% extends "master.html" %}
  2. {% from "_formhelper.html" import render_bootstrap_field %}
  3. {% from "_projectstring.html" import projectstring, projecticon %}
  4. {% from "_api_token.html" import render_api_token %}
  5. {% block title %}{{ user.user }}'s settings{% endblock %}
  6. {% set tag = "users"%}
  7. {% macro render_email(email, form, validated=True) %}
  8. {% set random_number = range(0, 256) | random() %}
  9. <div class="list-group-item {% if not validated %}disabled{% endif %}">
  10. <span class="fa fa-envelope text-muted"></span> &nbsp;{{ email.email }}
  11. {% if validated %}
  12. <form class="float-right" method="POST"
  13. action="{{ url_for('ui_ns.remove_user_email') }}">
  14. <input type="hidden" value="{{ email.email }}" name="email" />
  15. {{ form.csrf_token }}
  16. <button title="Remove email" data-email="{{ email.email }}"
  17. class="btn btn btn-outline-danger delete-email-btn">
  18. <i class="fa fa-trash fa-fw"></i>
  19. </button>
  20. </form>
  21. {% if email.email == user.default_email %}
  22. <div class="btn button-outline-light text-warning float-right mr-1">
  23. <span class="fa fa-star" title="default email address" data-toggle="tooltip"></span>
  24. </div>
  25. {% else %}
  26. <form class="inline" method="POST"
  27. action="{{ url_for('ui_ns.set_default_email') }}" id="default_mail_{{ random_number }}">
  28. <input type="hidden" value="{{ email.email }}" name="email" />
  29. {{ form.csrf_token }}
  30. <a class="float-right p-r-1 btn btn-outline-warning border-0 text-secondary mr-1 pointer submit-btn"
  31. data-form-id="default_mail_{{ random_number }}" title="Set as default email address">
  32. <span class="fa fa-star" data-toggle="tooltip"></span>
  33. </a>
  34. </form>
  35. {% endif %}
  36. {% else %}
  37. <div class="float-right">
  38. <small>pending verification via email </small>
  39. <form class="inline" method="POST"
  40. action="{{ url_for('ui_ns.reconfirm_email') }}" id="reconfirm_mail">
  41. <input type="hidden" value="{{ email.email }}" name="email" />
  42. {{ form.csrf_token }}
  43. <button data-form-id="reconfirm_mail"
  44. title="Resend validation email" class="btn btn btn-outline-primary submit-btn">
  45. <span class="fa fa-retweet fa-fw"></span>
  46. </button>
  47. </form>
  48. </div>
  49. {% endif %}
  50. </div>
  51. {% endmacro %}
  52. {% block content %}
  53. <div class="container p-t-3">
  54. <div class="row">
  55. <div class="col">
  56. <nav>
  57. <div class="nav nav-tabs nav-sidetabs flex-column" id="nav-tab" role="tablist">
  58. <h5 class="pl-2 font-weight-bold text-muted">User Settings</h5>
  59. <a class="nav-item nav-link active" id="nav-basic-tab" data-toggle="tab" href="#nav-basic" role="tab" aria-controls="nav-basic" aria-selected="true">Profile</a>
  60. <a class="nav-item nav-link" id="nav-email-tab" data-toggle="tab" href="#nav-email" role="tab" aria-controls="nav-email" aria-selected="true">Email Addresses</a>
  61. <a class="nav-item nav-link" id="nav-api-tab" data-toggle="tab" href="#nav-api" role="tab" aria-controls="nav-api" aria-selected="true">API Keys</a>
  62. {% if config.get('LOCAL_SSH_KEY', True) %}
  63. <a class="nav-item nav-link" id="nav-ssh-tab" data-toggle="tab" href="#nav-ssh" role="tab" aria-controls="nav-ssh" aria-selected="true">SSH Keys</a>
  64. {% endif %}
  65. <a class="nav-item nav-link" id="nav-user-tab" data-toggle="tab" href="#nav-user" role="tab" aria-controls="nav-user" aria-selected="true">Preferences</a>
  66. <a class="nav-item nav-link" id="nav-force-tab" data-toggle="tab" href="#nav-force" role="tab" aria-controls="nav-force" aria-selected="true">Force Logout</a>
  67. </div>
  68. </nav>
  69. </div>
  70. <div class="col-9">
  71. <div class="tab-content mt-4" id="nav-tabContent">
  72. <div class="tab-pane fade active show" id="nav-basic" role="tabpanel" aria-labelledby="nav-basic-tab">
  73. <h3 class="font-weight-bold mb-3">
  74. Basic Information
  75. {% if config.get('PAGURE_AUTH')=='local' %}
  76. <a class="btn btn-sm btn-outline-primary float-right" href="{{ url_for('ui_ns.change_password', username=g.fas_user.username) }}">Change password</a>
  77. {% endif %}
  78. </h3>
  79. <div class="row">
  80. <div class="col-xs-auto ml-4">
  81. <fieldset class="form-group text-center">
  82. <div>
  83. <div class="p-2 mt-2 bg-light border border-secondary"> {{ g.fas_user.username | avatar(80) | safe }} </div>
  84. <a class="btn btn-outline-primary btn-sm mt-1" href="https://www.libravatar.org/accounts/login/">
  85. Change Avatar </a>
  86. </div>
  87. </fieldset>
  88. </div>
  89. <div class="col ml-4">
  90. <fieldset class="form-group">
  91. <label for="description"><strong>Username</strong></label>
  92. <input class="form-control" type="text" disabled value="{{ user.user }}"/>
  93. </fieldset>
  94. <fieldset class="form-group">
  95. <label for="description"><strong>Full Name</strong></label>
  96. <input class="form-control" type="text" disabled value="{{ user.fullname }}"/>
  97. </fieldset>
  98. </div>
  99. </div>
  100. </div>
  101. <div class="tab-pane fade" id="nav-email" role="tabpanel" aria-labelledby="nav-email-tab">
  102. <h3 class="font-weight-bold mb-3">
  103. Email Addresses
  104. <a class="btn btn-outline-primary btn-sm float-right" href="{{
  105. url_for('ui_ns.add_user_email') }}">
  106. Add Email
  107. </a>
  108. </h3>
  109. <div class="row">
  110. <div class="col">
  111. <div class="list-group">
  112. {% for email in user.emails %}
  113. {{ render_email(email, form) }}
  114. {% endfor %}
  115. {% for email in user.emails_pending %}
  116. {{ render_email(email, form, validated=False) }}
  117. {% endfor %}
  118. </div>
  119. </div>
  120. </div>
  121. </div>
  122. <div class="tab-pane fade" id="nav-api" role="tabpanel" aria-labelledby="nav-api-tab">
  123. <div class="row mb-3">
  124. <h3 class="col-6 font-weight-bold mb-3">
  125. API Keys
  126. </h3>
  127. <div class="col-6 text-right">
  128. <div class="btn-group">
  129. <a href="{{ url_for('ui_ns.add_api_user_token') }}"
  130. class="btn btn-sm btn-outline-primary"
  131. title="Generate a new API token">
  132. Create new API Key
  133. </a>
  134. <div class="btn-group">
  135. <a method="post" class="btn btn-sm btn-outline-primary class"
  136. title="Show old API token" id="show_old_keys">
  137. Show old API Keys
  138. </a>
  139. </div>
  140. </div>
  141. </div>
  142. </div>
  143. <div class="row">
  144. <div class="col">
  145. <p>
  146. API keys are tokens used to authenticate you on pagure. They can also
  147. be used to grant access to 3rd party applications to act on all
  148. {{projectstring(plural=True)}} in your name.
  149. </p>
  150. <p>
  151. These are your personal tokens; they are not visible to others.
  152. </p>
  153. <p>
  154. These keys are private. Be sure to store them in a safe place and
  155. do not share them.
  156. </p>
  157. {% if user.tokens %}
  158. {% for token in user.tokens %}
  159. {% if not token.project %}
  160. {{ render_api_token(token, repo, username, form) }}
  161. {% endif %}
  162. {% endfor %}
  163. {% endif %}
  164. </div>
  165. </div>
  166. </div>
  167. {% if config.get('LOCAL_SSH_KEY', True) %}
  168. <div class="tab-pane fade" id="nav-ssh" role="tabpanel" aria-labelledby="nav-ssh-tab">
  169. <h3 class="font-weight-bold mb-3">
  170. SSH Keys
  171. <a class="btn btn-outline-primary btn-sm float-right ssh_key_btn ssh_key_block pointer">
  172. Add SSH key
  173. </a>
  174. </h3>
  175. <div class="row">
  176. <div class="col">
  177. <p>Below are your SSH keys.</p>
  178. {% for key in user.sshkeys %}
  179. <div class="form-group">
  180. <div class="input-group">
  181. <div class="input-group-prepend">
  182. <span class="input-group-text"><span class="fa fa-key"></span></span>
  183. </div>
  184. <input class="form-control bg-white font-monospace" readonly
  185. type="text" value="{{ key.ssh_short_key }}"/>
  186. <form class="pull-xs-right" method="POST"
  187. action="{{ url_for(
  188. 'ui_ns.remove_user_sshkey',
  189. keyid=key.id) }}">
  190. <button title="Remove SSH key"
  191. class="btn btn-outline-danger delete-sshkey-btn">
  192. <i class="fa fa-trash"></i>
  193. </button>
  194. {{ form.csrf_token }}
  195. </form>
  196. </div>
  197. </div>
  198. {% endfor %}
  199. </div>
  200. </div>
  201. <div class="row justify-content-around ssh_key_block hidden">
  202. <div class="col">
  203. <div class="card mt-5">
  204. <div class="card-header">
  205. <strong>Add SSH key</strong>
  206. </div>
  207. <div class="card-body">
  208. <form action="{{ url_for('ui_ns.add_user_sshkey') }}" method="post">
  209. <fieldset class="form-group">
  210. <label for="ssh_key"><strong>SSH key</strong></label>
  211. <textarea class="form-control" name="ssh_key" id="ssh_key"></textarea>
  212. </fieldset>
  213. <p class="buttons indent">
  214. <input type="button" value="Cancel" class="btn btn-secondary ssh_key_btn">
  215. <input type="submit" class="btn btn-primary" value="Add">
  216. {{ form.csrf_token }}
  217. </p>
  218. </form>
  219. </div>
  220. </div>
  221. </div>
  222. </div>
  223. </div>
  224. {% endif %}
  225. <div class="tab-pane fade" id="nav-user" role="tabpanel" aria-labelledby="nav-ssh-tab">
  226. <h3 class="font-weight-bold mb-3">
  227. User Settings
  228. </h3>
  229. <div class="row">
  230. <div class="col">
  231. <form action="{{ url_for('ui_ns.update_user_settings') }}" method="post">
  232. <div class="list-group">
  233. {% for key in user.settings | sort %}
  234. {% if user.settings[key] in [True, False, 'y'] %}
  235. <div class="list-group-item">
  236. <label class="custom-input custom-checkbox">
  237. <input id="{{ key }}" type="checkbox" value="y" name="{{ key }}" {%
  238. if user.settings[key] -%}checked=""{%- endif -%}/>
  239. <span class="c-indicator"></span>
  240. Activate {{ key | replace('_', ' ') }}
  241. </label>
  242. </div>
  243. {% else %}
  244. <div class="list-group-item">
  245. <label for="{{ key }}">Activate {{ key | replace('_', ' ') }} :</label>
  246. <input width="4em" class="form-control" id="{{ key }}" type="text" {%
  247. if user.settings[key] %}value="{{ user.settings[key] }}"{%
  248. endif %} name="{{ key }}" />
  249. </div>
  250. {% endif %}
  251. {% endfor %}
  252. </div>
  253. <p class="mt-3">
  254. <input type="submit" class="btn btn-primary" value="Update">
  255. {{ form.csrf_token }}
  256. </p>
  257. </form>
  258. </div>
  259. </div>
  260. </div>
  261. <div class="tab-pane fade" id="nav-force" role="tabpanel" aria-labelledby="nav-force-tab">
  262. <h3 class="font-weight-bold mb-3">
  263. Force Logout
  264. </h3>
  265. <div class="row">
  266. <div class="col">
  267. <p>
  268. Forcefully log out from every currently open session.
  269. </p>
  270. <form action="{{ url_for('ui_ns.force_logout') }}" method="post">
  271. <input type="submit" class="btn btn-outline-danger"
  272. value="Log out all currently active sessions">
  273. {{ form.csrf_token }}
  274. </form>
  275. </div>
  276. </div>
  277. </div>
  278. </div>
  279. </div>
  280. </div>
  281. </div>
  282. {% endblock %}
  283. {% block jscripts %}
  284. {{ super() }}
  285. <script type="text/javascript" nonce="{{ g.nonce }}">
  286. $(document).ready(function() {
  287. $('.submit-btn').click(function() {
  288. var _form_name = $(this).attr('data-form-id');
  289. $('#' + _form_name).submit();
  290. });
  291. $('.remove-token-btn').click(function() {
  292. return confirm('Are you sure you want to revoke this token ?'
  293. + '\nThis will break all applications using it and '
  294. + 'cannot be undone.');
  295. })
  296. $('.delete-email-btn').click(function() {
  297. return confirm('Do you really want to remove the email: ' + $(this).attr('data-email') + '?');
  298. })
  299. $('.delete-sshkey-btn').click(function() {
  300. return confirm('Are you sure you want to remove this SSH key?');
  301. })
  302. $('#nav-tab a.nav-link').on('shown.bs.tab', function (e) {
  303. window.location.hash = e.target.hash+'-tab';
  304. window.scrollTo(0,0);
  305. });
  306. if (!window.location.hash){
  307. window.location.hash = "#nav-basic-tab"
  308. }
  309. window.onhashchange = function () {
  310. $(window.location.hash).tab('show');
  311. }
  312. const _anchor = window.location.hash;
  313. console.log(_anchor);
  314. $(_anchor).tab('show');
  315. window.scrollTo(0,0);
  316. $(".ssh_key_btn").click(function() {
  317. $(".ssh_key_block").toggle();
  318. });
  319. $('#show_old_keys').click(function(e) {
  320. var _el = $('.expired_api_keys')
  321. if (_el.css('display') == 'none'){
  322. _el.css('display', 'flex');
  323. $('#show_old_keys').text('Hide old API keys');
  324. } else {
  325. $('#show_old_keys').text('Show old API keys');
  326. _el.hide();
  327. }
  328. });
  329. });
  330. </script>
  331. {% endblock %}