cone.tokens 0.2__tar.gz → 0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cone.tokens might be problematic. Click here for more details.

Files changed (47) hide show
  1. {cone.tokens-0.2 → cone.tokens-0.3}/CHANGES.rst +10 -0
  2. {cone.tokens-0.2/src/cone.tokens.egg-info → cone.tokens-0.3}/PKG-INFO +12 -7
  3. {cone.tokens-0.2 → cone.tokens-0.3}/setup.py +1 -1
  4. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/api.py +7 -6
  5. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/api.py +36 -0
  6. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/static/cone.tokens.js +84 -10
  7. cone.tokens-0.3/src/cone/tokens/browser/static/cone.tokens.min.js +1 -0
  8. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/templates/token.pt +1 -1
  9. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/templates/tokens_overview.pt +13 -2
  10. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/tokens.py +3 -0
  11. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/locale/cone.tokens.pot +17 -6
  12. cone.tokens-0.3/src/cone/tokens/locale/de/LC_MESSAGES/cone.tokens.mo +0 -0
  13. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/locale/de/LC_MESSAGES/cone.tokens.po +18 -7
  14. cone.tokens-0.3/src/cone/tokens/locale/en/LC_MESSAGES/cone.tokens.mo +0 -0
  15. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/locale/en/LC_MESSAGES/cone.tokens.po +18 -6
  16. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/model.py +1 -0
  17. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/tests/test_api.py +1 -0
  18. {cone.tokens-0.2 → cone.tokens-0.3/src/cone.tokens.egg-info}/PKG-INFO +12 -7
  19. cone.tokens-0.2/src/cone/tokens/browser/static/cone.tokens.min.js +0 -1
  20. cone.tokens-0.2/src/cone/tokens/locale/de/LC_MESSAGES/cone.tokens.mo +0 -0
  21. cone.tokens-0.2/src/cone/tokens/locale/en/LC_MESSAGES/cone.tokens.mo +0 -0
  22. {cone.tokens-0.2 → cone.tokens-0.3}/LICENSE.rst +0 -0
  23. {cone.tokens-0.2 → cone.tokens-0.3}/MANIFEST.in +0 -0
  24. {cone.tokens-0.2 → cone.tokens-0.3}/README.rst +0 -0
  25. {cone.tokens-0.2 → cone.tokens-0.3}/setup.cfg +0 -0
  26. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/__init__.py +0 -0
  27. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/__init__.py +0 -0
  28. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/__init__.py +0 -0
  29. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/settings.py +0 -0
  30. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/static/cone.tokens.css +0 -0
  31. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/static/cone.tokens.min.css +0 -0
  32. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/templates/token_settings.pt +0 -0
  33. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/templates/tokens.pt +0 -0
  34. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/browser/token.py +0 -0
  35. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/exceptions.py +0 -0
  36. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/settings.py +0 -0
  37. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/tests/__init__.py +0 -0
  38. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/tests/test_json_api.py +0 -0
  39. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/tests/test_model.py +0 -0
  40. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/tests/test_settings.py +0 -0
  41. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone/tokens/tests/test_token.py +0 -0
  42. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone.tokens.egg-info/SOURCES.txt +0 -0
  43. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone.tokens.egg-info/dependency_links.txt +0 -0
  44. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone.tokens.egg-info/namespace_packages.txt +0 -0
  45. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone.tokens.egg-info/not-zip-safe +0 -0
  46. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone.tokens.egg-info/requires.txt +0 -0
  47. {cone.tokens-0.2 → cone.tokens-0.3}/src/cone.tokens.egg-info/top_level.txt +0 -0
@@ -1,6 +1,16 @@
1
1
  Changes
2
2
  =======
3
3
 
4
+ 0.3 (2023-09-28)
5
+ ----------------
6
+
7
+ - Tokens can be bulk deleted.
8
+ [lenadax]
9
+
10
+ - Tokens can be created from scanned value.
11
+ [rnix]
12
+
13
+
4
14
  0.2 (2023-09-27)
5
15
  ----------------
6
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cone.tokens
3
- Version: 0.2
3
+ Version: 0.3
4
4
  Summary: cone token api
5
5
  Home-page: http://github.com/conestack/cone.tokens
6
6
  Author: Cone Contributors
@@ -10,13 +10,8 @@ Keywords: node pyramid cone web
10
10
  Classifier: Environment :: Web Environment
11
11
  Classifier: Programming Language :: Python
12
12
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
13
- License-File: LICENSE.rst
14
- Requires-Dist: setuptools
15
- Requires-Dist: cone.sql
16
- Requires-Dist: python-dateutil
17
- Requires-Dist: qrcode[pil]
18
13
  Provides-Extra: test
19
- Requires-Dist: zope.testrunner; extra == "test"
14
+ License-File: LICENSE.rst
20
15
 
21
16
  .. image:: https://img.shields.io/pypi/v/cone.tokens.svg
22
17
  :target: https://pypi.python.org/pypi/cone.tokens
@@ -170,6 +165,16 @@ Contributors
170
165
  Changes
171
166
  =======
172
167
 
168
+ 0.3 (2023-09-28)
169
+ ----------------
170
+
171
+ - Tokens can be bulk deleted.
172
+ [lenadax]
173
+
174
+ - Tokens can be created from scanned value.
175
+ [rnix]
176
+
177
+
173
178
  0.2 (2023-09-27)
174
179
  ----------------
175
180
 
@@ -9,7 +9,7 @@ def read_file(name):
9
9
  return f.read()
10
10
 
11
11
 
12
- version = '0.2'
12
+ version = '0.3'
13
13
  shortdesc = 'cone token api'
14
14
  longdesc = '\n\n'.join([read_file(name) for name in [
15
15
  'README.rst',
@@ -74,10 +74,11 @@ class TokenAPI(object):
74
74
  if request:
75
75
  return request.authenticated_userid
76
76
 
77
- def _log_usage(self, token_uid, error_code=None):
77
+ def _log_usage(self, token_uid, token_value, error_code=None):
78
78
  record = TokenUsageRecord()
79
79
  record.uid = uuid.uuid4()
80
80
  record.token_uid = token_uid
81
+ record.token_value = token_value
81
82
  record.timestamp = datetime.now()
82
83
  record.error_code = error_code
83
84
  record.user = self._authenticated_userid
@@ -88,28 +89,28 @@ class TokenAPI(object):
88
89
  token = self.get_token(token_uid)
89
90
  if token.usage_count == 0:
90
91
  exc = TokenUsageCountExceeded(token_uid)
91
- self._log_usage(token_uid, error_code=exc.error_code)
92
+ self._log_usage(token_uid, token.value, error_code=exc.error_code)
92
93
  raise exc
93
94
  now = datetime.now()
94
95
  if token.last_used:
95
96
  if token.last_used + timedelta(0, token.lock_time) > now:
96
97
  exc = TokenLockTimeViolation(token_uid)
97
- self._log_usage(token_uid, error_code=exc.error_code)
98
+ self._log_usage(token_uid, token.value, error_code=exc.error_code)
98
99
  raise exc
99
100
  valid_from = token.valid_from
100
101
  valid_to = token.valid_to
101
102
  if valid_from and now < valid_from:
102
103
  exc = TokenTimeRangeViolation(token_uid)
103
- self._log_usage(token_uid, error_code=exc.error_code)
104
+ self._log_usage(token_uid, token.value, error_code=exc.error_code)
104
105
  raise exc
105
106
  if valid_to and now > valid_to:
106
107
  exc = TokenTimeRangeViolation(token_uid)
107
- self._log_usage(token_uid, error_code=exc.error_code)
108
+ self._log_usage(token_uid, token.value, error_code=exc.error_code)
108
109
  raise exc
109
110
  if token.usage_count != -1:
110
111
  token.usage_count -= 1
111
112
  token.last_used = now
112
- self._log_usage(token_uid)
113
+ self._log_usage(token_uid, token.value)
113
114
  return True
114
115
 
115
116
  def add(
@@ -4,11 +4,17 @@ from cone.tokens.exceptions import TokenValueError
4
4
  from cone.tokens.model import TokenContainer
5
5
  from cone.tokens.model import TokenNode
6
6
  from cone.tokens.settings import get_settings_node
7
+ from pyramid.i18n import get_localizer
8
+ from pyramid.i18n import TranslationStringFactory
7
9
  from pyramid.view import view_config
8
10
  import dateutil.parser
11
+ import json
9
12
  import uuid
10
13
 
11
14
 
15
+ _ = TranslationStringFactory('cone.tokens')
16
+
17
+
12
18
  def read_string(request, param, kw, default=None):
13
19
  if param in request.params:
14
20
  value = request.params[param]
@@ -140,6 +146,36 @@ def add_token(model, request):
140
146
  return dict(success=True, token_uid=str(uid))
141
147
 
142
148
 
149
+ @view_config(
150
+ name='delete_tokens',
151
+ request_method='POST',
152
+ accept='application/json',
153
+ renderer='json',
154
+ context=TokenContainer,
155
+ permission='delete')
156
+ def delete_tokens(model, request):
157
+ api = TokenAPI(request)
158
+ token_uids = request.params.get('token_uids', None)
159
+ if not token_uids:
160
+ return
161
+ token_uids = json.loads(token_uids)
162
+ token_count = len(token_uids)
163
+ for t_uid in token_uids:
164
+ uid = uuid.UUID(t_uid)
165
+ try:
166
+ api.delete(uid)
167
+ except Exception as e:
168
+ return dict(success=False, message=str(e))
169
+ ts = _(
170
+ 'deleted_tokens',
171
+ default='Successfully deleted ${count} Tokens.',
172
+ mapping={'count': token_count}
173
+ )
174
+ localizer = get_localizer(request)
175
+ message = localizer.translate(ts)
176
+ return dict(success=True, message=message)
177
+
178
+
143
179
  @view_config(
144
180
  name='update_token',
145
181
  request_method='POST',
@@ -112,8 +112,15 @@ var cone_tokens = (function (exports, $, ts) {
112
112
  this.filter = $('button[name="filter"]', this.tokens_title);
113
113
  this.filter_tokens = this.filter_tokens.bind(this);
114
114
  this.filter.on('click', this.filter_tokens);
115
+ this.delete_tokens_container = $('.delete-tokens', this.tokens_title);
116
+ this.delete_tokens_btn = $(
117
+ 'button[name="delete-tokens"]',
118
+ this.delete_tokens_container
119
+ );
120
+ this.delete_tokens = this.delete_tokens.bind(this);
121
+ this.delete_tokens_btn.on('click', this.delete_tokens);
115
122
  this.set_token_size = this.set_token_size.bind(this);
116
- this.size_input = $('input[name="token-size"]');
123
+ this.size_input = $('input[name="token-size"]', this.tokens_title);
117
124
  this.size_input.on('change', this.set_token_size);
118
125
  if (this.tokens.length) {
119
126
  this.original_size = parseInt($(this.tokens[0]).attr('width'));
@@ -189,9 +196,51 @@ var cone_tokens = (function (exports, $, ts) {
189
196
  }
190
197
  });
191
198
  }
192
- let amount = parseInt(this.add_tokens_input.val());
199
+ let amount = this.add_tokens_input.val();
200
+ if (!amount) {
201
+ return;
202
+ } else {
203
+ amount = parseInt(amount);
204
+ }
193
205
  add(amount, 0);
194
206
  }
207
+ delete_tokens(evt) {
208
+ const base_url = this.token_settings.base_url;
209
+ ts.show_dialog({
210
+ title: 'Delete tokens?',
211
+ message: 'Do you really want to delete selected tokens?',
212
+ on_confirm: () => {
213
+ let uids = [];
214
+ for (let token of this.tokens) {
215
+ let uid = $(token).data('token-uid');
216
+ uids.push(uid);
217
+ }
218
+ ts.ajax.request({
219
+ url: `${base_url}/delete_tokens`,
220
+ params: {token_uids: JSON.stringify(uids)},
221
+ type: 'json',
222
+ method: 'POST',
223
+ success: (data, status, request) => {
224
+ if (data.success) {
225
+ ts.ajax.action({
226
+ name: 'tokens_overview',
227
+ selector: '#content',
228
+ mode: 'inner',
229
+ url: base_url,
230
+ params: {}
231
+ });
232
+ ts.show_message({message: data.message, flavor:''});
233
+ } else {
234
+ ts.show_error(data.message);
235
+ }
236
+ },
237
+ error: (request, status, error) => {
238
+ ts.show_error(`Failed to request JSON API: ${error}`);
239
+ }
240
+ });
241
+ }
242
+ });
243
+ }
195
244
  set_token_size(evt) {
196
245
  evt.preventDefault();
197
246
  let size = this.size_input.val();
@@ -246,6 +295,16 @@ var cone_tokens = (function (exports, $, ts) {
246
295
  this.query_token(input.val());
247
296
  });
248
297
  }
298
+ load_token(token_uid) {
299
+ console.log('load token: ' + token_uid);
300
+ ts.ajax.action({
301
+ name: 'layout',
302
+ selector: '#layout',
303
+ mode: 'replace',
304
+ url: `${this.base_url}/${token_uid}`,
305
+ params: {}
306
+ });
307
+ }
249
308
  query_token(value) {
250
309
  ts.ajax.request({
251
310
  url: `${this.base_url}/query_token`,
@@ -255,15 +314,30 @@ var cone_tokens = (function (exports, $, ts) {
255
314
  if (data.success) {
256
315
  if (!data.token) {
257
316
  this.active = false;
258
- ts.show_error('Token not exists');
259
- } else {
260
- ts.ajax.action({
261
- name: 'layout',
262
- selector: '#layout',
263
- mode: 'replace',
264
- url: `${this.base_url}/${data.token.uid}`,
265
- params: {}
317
+ ts.show_dialog({
318
+ title: 'Token not exists?',
319
+ message: 'Do you want to create it?',
320
+ on_confirm: () => {
321
+ ts.ajax.request({
322
+ url: `${this.base_url}/add_token`,
323
+ params: {value: value},
324
+ type: 'json',
325
+ method: 'POST',
326
+ success: (data, status, request) => {
327
+ if (data.success) {
328
+ this.load_token(data.token_uid);
329
+ } else {
330
+ ts.show_error(data.message);
331
+ }
332
+ },
333
+ error: (request, status, error) => {
334
+ ts.show_error(`Failed to request JSON API: ${error}`);
335
+ }
336
+ });
337
+ }
266
338
  });
339
+ } else {
340
+ this.load_token(data.token.uid);
267
341
  }
268
342
  } else {
269
343
  ts.show_error(data.message);
@@ -0,0 +1 @@
1
+ var cone_tokens=function(t,e,s){"use strict";class n{static initialize(t){e(".token",t).each((function(){new n(e(this))}))}constructor(t){this.elem=t,this.settings=t.data("token-settings");const s=this;e(".btn-group.timeranges button",t).each((function(){const t=e(this);t.on("click",(function(e){s.set_timerange(t.data("timerange-scope"))}))})),e(".btn-group.usage-count button",t).each((function(){const t=e(this);t.on("click",(function(e){s.set_usage_count(t.data("usage-count"))}))}))}request_api(t){const e=this.settings;s.ajax.request({url:`${e.base_url}/update_token`,params:t,type:"json",method:"POST",success:(t,n,i)=>{t.success?s.ajax.action({name:"content",selector:"#content",mode:"inner",url:`${e.base_url}`,params:{}}):s.show_error(t.message)},error:(t,e,n)=>{s.show_error(`Failed to request JSON API: ${n}`)}})}set_timerange(t){const e=this.settings.timeranges;let s,n,i;if(Object.keys(e).includes(t)){s=e[t];let o=s.start.split(":");n=new Date,n.setHours(o[0],o[1],0);let a=s.end.split(":");i=new Date,i.setHours(a[0],a[1],0)}function o(t){return t&&(t=new Date(t.getTime()-6e4*t.getTimezoneOffset())),t?t.toISOString():""}this.request_api({valid_from:o(n),valid_to:o(i)})}set_usage_count(t){this.request_api({usage_count:t})}}class i{static initialize(t){e(".tokens-overview-container",t).each((function(){new i(e(this))}))}constructor(t){this.container=t,this.tokens_elem=e("#tokens-overview",t),this.tokens=e("object.token_qr",this.tokens_elem),this.tokens_title=e("#tokens-overview-title",t),this.token_settings=this.container.data("token-settings"),this.add_tokens_container=e(".add-tokens",this.tokens_title),this.add_tokens_input=e('input[name="amount"]',this.add_tokens_container),this.add_tokens_btn=e('button[name="add-tokens"]',this.add_tokens_container),this.add_tokens=this.add_tokens.bind(this),this.add_tokens_btn.on("click",this.add_tokens),this.start=e('input[name="start"]',this.tokens_title).addClass("datepicker").data("date-locale","de"),this.end=e('input[name="end"]',this.tokens_title).addClass("datepicker").data("date-locale","de"),this.filter=e('button[name="filter"]',this.tokens_title),this.filter_tokens=this.filter_tokens.bind(this),this.filter.on("click",this.filter_tokens),this.delete_tokens_container=e(".delete-tokens",this.tokens_title),this.delete_tokens_btn=e('button[name="delete-tokens"]',this.delete_tokens_container),this.delete_tokens=this.delete_tokens.bind(this),this.delete_tokens_btn.on("click",this.delete_tokens),this.set_token_size=this.set_token_size.bind(this),this.size_input=e('input[name="token-size"]',this.tokens_title),this.size_input.on("change",this.set_token_size),this.tokens.length?this.original_size=parseInt(e(this.tokens[0]).attr("width")):this.original_size=256,this.token_size=100}get token_size(){return this._token_size}set token_size(t){t||(t=100);let s=this.original_size*(t/100);this.tokens_elem.css("grid-template-columns",`repeat( auto-fit, minmax(${s}px, 1fr) )`),this.tokens.each((function(){let t=e(this);t.attr("width",`${s}px`),t.attr("height",`${s}px`)})),this.size_input.val()||this.size_input.val(t),this._token_size=t}filter_tokens(t){t.preventDefault();let e={start:this.start.val(),end:this.end.val()};s.ajax.action({name:"tokens_overview",mode:"inner",selector:"#content",url:this.token_settings.base_url,params:e})}add_tokens(t){const e=this.token_settings.base_url;let n=this.add_tokens_input.val();n&&(n=parseInt(n),function t(n,i){s.ajax.request({url:`${e}/add_token`,params:{},type:"json",method:"POST",success:(o,a,r)=>{o.success?n===(i+=1)?s.ajax.action({name:"tokens_overview",selector:"#content",mode:"inner",url:e,params:{}}):t(n,i):s.show_error(o.message)},error:(t,e,n)=>{s.show_error(`Failed to request JSON API: ${n}`)}})}(n,0))}delete_tokens(t){const n=this.token_settings.base_url;s.show_dialog({title:"Delete tokens?",message:"Do you really want to delete selected tokens?",on_confirm:()=>{let t=[];for(let s of this.tokens){let n=e(s).data("token-uid");t.push(n)}s.ajax.request({url:`${n}/delete_tokens`,params:{token_uids:JSON.stringify(t)},type:"json",method:"POST",success:(t,e,i)=>{t.success?(s.ajax.action({name:"tokens_overview",selector:"#content",mode:"inner",url:n,params:{}}),s.show_message({message:t.message,flavor:""})):s.show_error(t.message)},error:(t,e,n)=>{s.show_error(`Failed to request JSON API: ${n}`)}})}})}set_token_size(t){t.preventDefault();let e=this.size_input.val();this.token_size=e}}class o{static initialize(t){e(".tokens-container",t).each((function(){new o(e(this))}))}constructor(t){this.elem=t,this.base_url=t.data("base-url"),this.button=e(".scan-token",t),this._input_wrapper=null,this._input=null,this.scan_token=this.scan_token.bind(this),this.button.on("click",(t=>{this.scan_token()}))}get active(){return null!==this._input}set active(t){if(t==this.value)return;let s=this.button;if(t){let t=this._input_wrapper=e("<div/>").css("width",0).css("overflow","hidden"),n=this._input=e('<input type="text">');t.append(n),this.elem.append(t),s.removeClass("inactive").addClass("active"),n[0].focus()}else this._input_wrapper.remove(),this._input_wrapper=null,this._input=null,s.removeClass("active").addClass("inactive")}scan_token(){this.active=!0;let t=this._input;t.one("change",(()=>{this.query_token(t.val())}))}load_token(t){console.log("load token: "+t),s.ajax.action({name:"layout",selector:"#layout",mode:"replace",url:`${this.base_url}/${t}`,params:{}})}query_token(t){s.ajax.request({url:`${this.base_url}/query_token`,params:{value:t},type:"json",success:(e,n,i)=>{e.success?e.token?this.load_token(e.token.uid):(this.active=!1,s.show_dialog({title:"Token not exists?",message:"Do you want to create it?",on_confirm:()=>{s.ajax.request({url:`${this.base_url}/add_token`,params:{value:t},type:"json",method:"POST",success:(t,e,n)=>{t.success?this.load_token(t.token_uid):s.show_error(t.message)},error:(t,e,n)=>{s.show_error(`Failed to request JSON API: ${n}`)}})}})):s.show_error(e.message)},error:(t,e,n)=>{s.show_error(`Failed to request JSON API: ${n}`)}})}}return e((function(){s.ajax.register(n.initialize,!0),s.ajax.register(o.initialize,!0),s.ajax.register(i.initialize,!0)})),t.TokenScanner=o,t.TokensOverview=i,Object.defineProperty(t,"__esModule",{value:!0}),t}({},jQuery,ts);
@@ -60,7 +60,7 @@
60
60
 
61
61
  <li class="list-group-item flex-group">
62
62
  <div>
63
- <strong>Usage Count</strong>:
63
+ <strong i18n:translate="usage_count">Usage Count</strong>:
64
64
  <span tal:condition="model.attrs['usage_count'] > -1"
65
65
  tal:content="model.attrs['usage_count']">
66
66
  Usage Count
@@ -16,8 +16,9 @@
16
16
  <div class="form-inline add-tokens">
17
17
  <label for="amount" i18n:translate="amount">Amount</label>
18
18
  <input type="number"
19
- name="amount"
20
- class="form-control add-tokens-amount" />
19
+ name="amount"
20
+ min=0
21
+ class="form-control add-tokens-amount" />
21
22
  <button name="add-tokens"
22
23
  class="btn btn-default"
23
24
  i18n:translate="add_token">
@@ -46,10 +47,19 @@
46
47
  </button>
47
48
  </div>
48
49
 
50
+ <div class="form-inline delete-tokens">
51
+ <button name="delete-tokens"
52
+ class="btn btn-danger"
53
+ i18n:translate="delete_token">
54
+ Delete selected Tokens
55
+ </button>
56
+ </div>
57
+
49
58
  <div class="form-inline">
50
59
  <label for="token-size" i18n:translate="token_size">Token Size</label>
51
60
  <input type="number"
52
61
  name="token-size"
62
+ min=0
53
63
  class="form-control token-button"
54
64
  value="100" />
55
65
  %
@@ -61,6 +71,7 @@
61
71
  <tal:token repeat="token tokens">
62
72
  <object class="token_qr"
63
73
  data="${context.qrcode(token.value)}"
74
+ data-token-uid="${token.uid}"
64
75
  type="image/png"
65
76
  height="256px"
66
77
  width="256px">
@@ -83,6 +83,9 @@ class TokensOverview(ProtectedContentTile):
83
83
 
84
84
  @property
85
85
  def tokens(self):
86
+ action = self.request.params.get('ajax.action', None)
87
+ if not action:
88
+ return
86
89
  start = self.start
87
90
  end = self.end
88
91
  session = get_session(self.request)
@@ -6,7 +6,7 @@
6
6
  msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: PACKAGE 1.0\n"
9
- "POT-Creation-Date: 2023-09-27 13:37+0200\n"
9
+ "POT-Creation-Date: 2023-09-28 13:05+0200\n"
10
10
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11
11
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12
12
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -74,6 +74,7 @@ msgstr ""
74
74
 
75
75
  #. Default: Usage Count
76
76
  #: ./src/cone/tokens/browser/token.py:202
77
+ #: ./src/cone/tokens/browser/templates/token.pt:63
77
78
  msgid "usage_count"
78
79
  msgstr ""
79
80
 
@@ -93,6 +94,11 @@ msgstr ""
93
94
  msgid "cancel"
94
95
  msgstr ""
95
96
 
97
+ #. Default: Successfully deleted ${count} Tokens.
98
+ #: ./src/cone/tokens/browser/api.py:169
99
+ msgid "deleted_tokens"
100
+ msgstr ""
101
+
96
102
  #. Default: Token Settings
97
103
  #: ./src/cone/tokens/browser/settings.py:37
98
104
  msgid "token_settings"
@@ -123,7 +129,7 @@ msgstr ""
123
129
  #: ./src/cone/tokens/browser/settings.py:95
124
130
  #: ./src/cone/tokens/browser/settings.py:125
125
131
  #: ./src/cone/tokens/browser/settings.py:155
126
- #: ./src/cone/tokens/browser/templates/tokens_overview.pt:29
132
+ #: ./src/cone/tokens/browser/templates/tokens_overview.pt:30
127
133
  msgid "start"
128
134
  msgstr ""
129
135
 
@@ -131,7 +137,7 @@ msgstr ""
131
137
  #: ./src/cone/tokens/browser/settings.py:105
132
138
  #: ./src/cone/tokens/browser/settings.py:135
133
139
  #: ./src/cone/tokens/browser/settings.py:165
134
- #: ./src/cone/tokens/browser/templates/tokens_overview.pt:37
140
+ #: ./src/cone/tokens/browser/templates/tokens_overview.pt:38
135
141
  msgid "end"
136
142
  msgstr ""
137
143
 
@@ -217,16 +223,21 @@ msgid "amount"
217
223
  msgstr ""
218
224
 
219
225
  #. Default: Add Tokens
220
- #: ./src/cone/tokens/browser/templates/tokens_overview.pt:23
226
+ #: ./src/cone/tokens/browser/templates/tokens_overview.pt:24
221
227
  msgid "add_token"
222
228
  msgstr ""
223
229
 
224
230
  #. Default: Filter
225
- #: ./src/cone/tokens/browser/templates/tokens_overview.pt:44
231
+ #: ./src/cone/tokens/browser/templates/tokens_overview.pt:45
226
232
  msgid "filter"
227
233
  msgstr ""
228
234
 
235
+ #. Default: Delete selected Tokens
236
+ #: ./src/cone/tokens/browser/templates/tokens_overview.pt:53
237
+ msgid "delete_token"
238
+ msgstr ""
239
+
229
240
  #. Default: Token Size
230
- #: ./src/cone/tokens/browser/templates/tokens_overview.pt:50
241
+ #: ./src/cone/tokens/browser/templates/tokens_overview.pt:59
231
242
  msgid "token_size"
232
243
  msgstr ""
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: cone.tokens 1.0\n"
4
- "POT-Creation-Date: 2023-09-27 13:37+0200\n"
4
+ "POT-Creation-Date: 2023-09-28 13:05+0200\n"
5
5
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
6
6
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
7
7
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -44,7 +44,7 @@ msgstr "Tokens"
44
44
  #. Default: Valid from date must be before valid to date
45
45
  #: src/cone/tokens/browser/token.py:120
46
46
  msgid "timerange_error"
47
- msgstr ""
47
+ msgstr "Anfangsdatum muss vor Enddatum liegen"
48
48
 
49
49
  #. Default: Value already used by another token
50
50
  #: src/cone/tokens/browser/token.py:136
@@ -69,6 +69,7 @@ msgstr "Gültig bis"
69
69
 
70
70
  #. Default: Usage Count
71
71
  #: src/cone/tokens/browser/token.py:202
72
+ #: src/cone/tokens/browser/templates/token.pt:63
72
73
  msgid "usage_count"
73
74
  msgstr "Benutzungen"
74
75
 
@@ -88,6 +89,11 @@ msgstr "Speichern"
88
89
  msgid "cancel"
89
90
  msgstr "Abbrechen"
90
91
 
92
+ #. Default: Successfully deleted ${count} Tokens.
93
+ #: src/cone/tokens/browser/api.py:169
94
+ msgid "deleted_tokens"
95
+ msgstr "${count} Tokens gelöscht."
96
+
91
97
  #. Default: Token Settings
92
98
  #: src/cone/tokens/browser/settings.py:37
93
99
  msgid "token_settings"
@@ -118,7 +124,7 @@ msgstr "Endzeit muss angegeben werden."
118
124
  #: src/cone/tokens/browser/settings.py:95
119
125
  #: src/cone/tokens/browser/settings.py:125
120
126
  #: src/cone/tokens/browser/settings.py:155
121
- #: src/cone/tokens/browser/templates/tokens_overview.pt:29
127
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:30
122
128
  msgid "start"
123
129
  msgstr "Start"
124
130
 
@@ -126,7 +132,7 @@ msgstr "Start"
126
132
  #: src/cone/tokens/browser/settings.py:105
127
133
  #: src/cone/tokens/browser/settings.py:135
128
134
  #: src/cone/tokens/browser/settings.py:165
129
- #: src/cone/tokens/browser/templates/tokens_overview.pt:37
135
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:38
130
136
  msgid "end"
131
137
  msgstr "Ende"
132
138
 
@@ -212,16 +218,21 @@ msgid "amount"
212
218
  msgstr ""
213
219
 
214
220
  #. Default: Add Tokens
215
- #: src/cone/tokens/browser/templates/tokens_overview.pt:23
221
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:24
216
222
  msgid "add_token"
217
223
  msgstr "Tokens erstellen"
218
224
 
219
225
  #. Default: Filter
220
- #: src/cone/tokens/browser/templates/tokens_overview.pt:44
226
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:45
221
227
  msgid "filter"
222
228
  msgstr "Filter"
223
229
 
230
+ #. Default: Delete selected Tokens
231
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:53
232
+ msgid "delete_token"
233
+ msgstr "Ausgewählte Tokens löschen"
234
+
224
235
  #. Default: Token Size
225
- #: src/cone/tokens/browser/templates/tokens_overview.pt:50
236
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:59
226
237
  msgid "token_size"
227
238
  msgstr "Token Größe"
@@ -1,7 +1,7 @@
1
1
  msgid ""
2
2
  msgstr ""
3
3
  "Project-Id-Version: cone.tokens 1.0\n"
4
- "POT-Creation-Date: 2023-09-27 13:37+0200\n"
4
+ "POT-Creation-Date: 2023-09-28 13:05+0200\n"
5
5
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
6
6
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
7
7
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -69,6 +69,7 @@ msgstr "Valid to"
69
69
 
70
70
  #. Default: Usage Count
71
71
  #: src/cone/tokens/browser/token.py:202
72
+ #: src/cone/tokens/browser/templates/token.pt:63
72
73
  msgid "usage_count"
73
74
  msgstr "Usage Count"
74
75
 
@@ -88,6 +89,12 @@ msgstr "Save"
88
89
  msgid "cancel"
89
90
  msgstr "Cancel"
90
91
 
92
+ #. Default: Successfully deleted ${count} Tokens.
93
+ #: src/cone/tokens/browser/api.py:169
94
+ #, fuzzy
95
+ msgid "deleted_tokens"
96
+ msgstr "Add Tokens"
97
+
91
98
  #. Default: Token Settings
92
99
  #: src/cone/tokens/browser/settings.py:37
93
100
  msgid "token_settings"
@@ -118,7 +125,7 @@ msgstr "End Time is required."
118
125
  #: src/cone/tokens/browser/settings.py:95
119
126
  #: src/cone/tokens/browser/settings.py:125
120
127
  #: src/cone/tokens/browser/settings.py:155
121
- #: src/cone/tokens/browser/templates/tokens_overview.pt:29
128
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:30
122
129
  msgid "start"
123
130
  msgstr "Start"
124
131
 
@@ -126,7 +133,7 @@ msgstr "Start"
126
133
  #: src/cone/tokens/browser/settings.py:105
127
134
  #: src/cone/tokens/browser/settings.py:135
128
135
  #: src/cone/tokens/browser/settings.py:165
129
- #: src/cone/tokens/browser/templates/tokens_overview.pt:37
136
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:38
130
137
  msgid "end"
131
138
  msgstr "End"
132
139
 
@@ -212,16 +219,21 @@ msgid "amount"
212
219
  msgstr "Amount"
213
220
 
214
221
  #. Default: Add Tokens
215
- #: src/cone/tokens/browser/templates/tokens_overview.pt:23
222
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:24
216
223
  msgid "add_token"
217
224
  msgstr "Add Tokens"
218
225
 
219
226
  #. Default: Filter
220
- #: src/cone/tokens/browser/templates/tokens_overview.pt:44
227
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:45
221
228
  msgid "filter"
222
229
  msgstr "Filter"
223
230
 
231
+ #. Default: Delete selected Tokens
232
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:53
233
+ msgid "delete_token"
234
+ msgstr "Delete selected Tokens"
235
+
224
236
  #. Default: Token Size
225
- #: src/cone/tokens/browser/templates/tokens_overview.pt:50
237
+ #: src/cone/tokens/browser/templates/tokens_overview.pt:59
226
238
  msgid "token_size"
227
239
  msgstr "Token Size"
@@ -104,6 +104,7 @@ class TokenUsageRecord(SQLBase):
104
104
 
105
105
  uid = Column(GUID, primary_key=True)
106
106
  token_uid = Column(GUID)
107
+ token_value = Column(String)
107
108
  timestamp = Column(DateTime)
108
109
  error_code = Column(Integer)
109
110
  user = Column(String)
@@ -114,6 +114,7 @@ class TestTokenAPI(NodeTestCase):
114
114
 
115
115
  usage_record = session.query(TokenUsageRecord).one()
116
116
  self.assertEqual(usage_record.token_uid, token_uid)
117
+ self.assertEqual(usage_record.token_value, 'token value')
117
118
  self.assertIsInstance(usage_record.timestamp, datetime)
118
119
  self.assertEqual(usage_record.error_code, None)
119
120
  self.assertEqual(usage_record.user, None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cone.tokens
3
- Version: 0.2
3
+ Version: 0.3
4
4
  Summary: cone token api
5
5
  Home-page: http://github.com/conestack/cone.tokens
6
6
  Author: Cone Contributors
@@ -10,13 +10,8 @@ Keywords: node pyramid cone web
10
10
  Classifier: Environment :: Web Environment
11
11
  Classifier: Programming Language :: Python
12
12
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
13
- License-File: LICENSE.rst
14
- Requires-Dist: setuptools
15
- Requires-Dist: cone.sql
16
- Requires-Dist: python-dateutil
17
- Requires-Dist: qrcode[pil]
18
13
  Provides-Extra: test
19
- Requires-Dist: zope.testrunner; extra == "test"
14
+ License-File: LICENSE.rst
20
15
 
21
16
  .. image:: https://img.shields.io/pypi/v/cone.tokens.svg
22
17
  :target: https://pypi.python.org/pypi/cone.tokens
@@ -170,6 +165,16 @@ Contributors
170
165
  Changes
171
166
  =======
172
167
 
168
+ 0.3 (2023-09-28)
169
+ ----------------
170
+
171
+ - Tokens can be bulk deleted.
172
+ [lenadax]
173
+
174
+ - Tokens can be created from scanned value.
175
+ [rnix]
176
+
177
+
173
178
  0.2 (2023-09-27)
174
179
  ----------------
175
180
 
@@ -1 +0,0 @@
1
- var cone_tokens=function(t,e,s){"use strict";class i{static initialize(t){e(".token",t).each((function(){new i(e(this))}))}constructor(t){this.elem=t,this.settings=t.data("token-settings");const s=this;e(".btn-group.timeranges button",t).each((function(){const t=e(this);t.on("click",(function(e){s.set_timerange(t.data("timerange-scope"))}))})),e(".btn-group.usage-count button",t).each((function(){const t=e(this);t.on("click",(function(e){s.set_usage_count(t.data("usage-count"))}))}))}request_api(t){const e=this.settings;s.ajax.request({url:`${e.base_url}/update_token`,params:t,type:"json",method:"POST",success:(t,i,n)=>{t.success?s.ajax.action({name:"content",selector:"#content",mode:"inner",url:`${e.base_url}`,params:{}}):s.show_error(t.message)},error:(t,e,i)=>{s.show_error(`Failed to request JSON API: ${i}`)}})}set_timerange(t){const e=this.settings.timeranges;let s,i,n;if(Object.keys(e).includes(t)){s=e[t];let a=s.start.split(":");i=new Date,i.setHours(a[0],a[1],0);let o=s.end.split(":");n=new Date,n.setHours(o[0],o[1],0)}function a(t){return t&&(t=new Date(t.getTime()-6e4*t.getTimezoneOffset())),t?t.toISOString():""}this.request_api({valid_from:a(i),valid_to:a(n)})}set_usage_count(t){this.request_api({usage_count:t})}}class n{static initialize(t){e(".tokens-overview-container",t).each((function(){new n(e(this))}))}constructor(t){this.container=t,this.tokens_elem=e("#tokens-overview",t),this.tokens=e("object.token_qr",this.tokens_elem),this.tokens_title=e("#tokens-overview-title",t),this.token_settings=this.container.data("token-settings"),this.add_tokens_container=e(".add-tokens",this.tokens_title),this.add_tokens_input=e('input[name="amount"]',this.add_tokens_container),this.add_tokens_btn=e('button[name="add-tokens"]',this.add_tokens_container),this.add_tokens=this.add_tokens.bind(this),this.add_tokens_btn.on("click",this.add_tokens),this.start=e('input[name="start"]',this.tokens_title).addClass("datepicker").data("date-locale","de"),this.end=e('input[name="end"]',this.tokens_title).addClass("datepicker").data("date-locale","de"),this.filter=e('button[name="filter"]',this.tokens_title),this.filter_tokens=this.filter_tokens.bind(this),this.filter.on("click",this.filter_tokens),this.set_token_size=this.set_token_size.bind(this),this.size_input=e('input[name="token-size"]'),this.size_input.on("change",this.set_token_size),this.tokens.length?this.original_size=parseInt(e(this.tokens[0]).attr("width")):this.original_size=256,this.token_size=100}get token_size(){return this._token_size}set token_size(t){t||(t=100);let s=this.original_size*(t/100);this.tokens_elem.css("grid-template-columns",`repeat( auto-fit, minmax(${s}px, 1fr) )`),this.tokens.each((function(){let t=e(this);t.attr("width",`${s}px`),t.attr("height",`${s}px`)})),this.size_input.val()||this.size_input.val(t),this._token_size=t}filter_tokens(t){t.preventDefault();let e={start:this.start.val(),end:this.end.val()};s.ajax.action({name:"tokens_overview",mode:"inner",selector:"#content",url:this.token_settings.base_url,params:e})}add_tokens(t){const e=this.token_settings.base_url;!function t(i,n){s.ajax.request({url:`${e}/add_token`,params:{},type:"json",method:"POST",success:(a,o,r)=>{a.success?i===(n+=1)?s.ajax.action({name:"tokens_overview",selector:"#content",mode:"inner",url:e,params:{}}):t(i,n):s.show_error(a.message)},error:(t,e,i)=>{s.show_error(`Failed to request JSON API: ${i}`)}})}(parseInt(this.add_tokens_input.val()),0)}set_token_size(t){t.preventDefault();let e=this.size_input.val();this.token_size=e}}class a{static initialize(t){e(".tokens-container",t).each((function(){new a(e(this))}))}constructor(t){this.elem=t,this.base_url=t.data("base-url"),this.button=e(".scan-token",t),this._input_wrapper=null,this._input=null,this.scan_token=this.scan_token.bind(this),this.button.on("click",(t=>{this.scan_token()}))}get active(){return null!==this._input}set active(t){if(t==this.value)return;let s=this.button;if(t){let t=this._input_wrapper=e("<div/>").css("width",0).css("overflow","hidden"),i=this._input=e('<input type="text">');t.append(i),this.elem.append(t),s.removeClass("inactive").addClass("active"),i[0].focus()}else this._input_wrapper.remove(),this._input_wrapper=null,this._input=null,s.removeClass("active").addClass("inactive")}scan_token(){this.active=!0;let t=this._input;t.one("change",(()=>{this.query_token(t.val())}))}query_token(t){s.ajax.request({url:`${this.base_url}/query_token`,params:{value:t},type:"json",success:(t,e,i)=>{t.success?t.token?s.ajax.action({name:"layout",selector:"#layout",mode:"replace",url:`${this.base_url}/${t.token.uid}`,params:{}}):(this.active=!1,s.show_error("Token not exists")):s.show_error(t.message)},error:(t,e,i)=>{s.show_error(`Failed to request JSON API: ${i}`)}})}}return e((function(){s.ajax.register(i.initialize,!0),s.ajax.register(a.initialize,!0),s.ajax.register(n.initialize,!0)})),t.TokenScanner=a,t.TokensOverview=n,Object.defineProperty(t,"__esModule",{value:!0}),t}({},jQuery,ts);
File without changes
File without changes
File without changes
File without changes