root / ui / static / snf / js / ui / web / ui_public_keys_view.js @ 47276ec2
History | View | Annotate | Download (19.8 kB)
1 |
;(function(root){
|
---|---|
2 |
|
3 |
// root
|
4 |
var root = root;
|
5 |
|
6 |
// setup namepsaces
|
7 |
var snf = root.synnefo = root.synnefo || {};
|
8 |
var api = snf.api = snf.api || {};
|
9 |
var models = snf.models = snf.models || {}
|
10 |
var storage = snf.storage = snf.storage || {};
|
11 |
var ui = snf.ui = snf.ui || {};
|
12 |
var util = snf.util = snf.util || {};
|
13 |
|
14 |
var views = snf.views = snf.views || {}
|
15 |
|
16 |
// shortcuts
|
17 |
var bb = root.Backbone;
|
18 |
|
19 |
views.CollectionView = views.View.extend({ |
20 |
|
21 |
collection: undefined, |
22 |
|
23 |
id_tpl: 'model-item-{0}', |
24 |
list_tpl: '#model-item-tpl', |
25 |
|
26 |
force_reset_before_fetch: true, |
27 |
auto_append_actions: true, |
28 |
|
29 |
initialize: function(options) { |
30 |
views.CollectionView.__super__.initialize.apply(this, arguments); |
31 |
_.bindAll(this);
|
32 |
|
33 |
this.loading = this.$(".loading-models"); |
34 |
this.content = this.$(".content"); |
35 |
this.list = this.$(".model-list .items-list"); |
36 |
this.list_cont = this.$(".model-list"); |
37 |
this.form = this.$(".model-form"); |
38 |
this.empty_msg = this.$(".items-empty-msg"); |
39 |
|
40 |
this.init_collection_handlers();
|
41 |
this.init_handlers();
|
42 |
this.close_form();
|
43 |
this.content.hide();
|
44 |
this.loading.show();
|
45 |
this.update_models();
|
46 |
this.items = [];
|
47 |
this.submiting = false; |
48 |
}, |
49 |
|
50 |
update_models: function() { |
51 |
this.collection.fetch({success:this.handle_reset}); |
52 |
}, |
53 |
|
54 |
init_handlers: function() { |
55 |
this.$(".add-new").click(_.bind(this.show_form, this, undefined)); |
56 |
|
57 |
this.form.find(".form-action.cancel").click(this.close_form); |
58 |
this.form.find(".form-action.submit").click(this.submit_form); |
59 |
|
60 |
var self = this; |
61 |
this.form.find("form").submit(function(e){ |
62 |
e.preventDefault(); |
63 |
self.submit_form(); |
64 |
return false; |
65 |
}); |
66 |
|
67 |
this.$(".quick-add").click(_.bind(this.show_form, this, undefined)); |
68 |
}, |
69 |
|
70 |
init_collection_handlers: function() { |
71 |
this.collection.bind("reset", this.handle_reset); |
72 |
}, |
73 |
|
74 |
reset_handlers: function() { |
75 |
this.collection.unbind("reset", this.handle_reset); |
76 |
}, |
77 |
|
78 |
set_items: function(models) { |
79 |
this.items = _.map(models, function(m) { return m.id }); |
80 |
}, |
81 |
|
82 |
handle_reset: function(collection, models) { |
83 |
this.loading.hide();
|
84 |
this.content.show();
|
85 |
|
86 |
if (this.force_reset_before_update) { |
87 |
this.reset_list();
|
88 |
} |
89 |
|
90 |
this.update_list(this.collection.models); |
91 |
this.update_removed(this.items, this.collection.models); |
92 |
|
93 |
this.set_items(this.collection.models); |
94 |
}, |
95 |
|
96 |
show_form: function(model) { |
97 |
var create = (model === undefined ? true : false); |
98 |
|
99 |
if (create) {
|
100 |
this.form.find(".new-title").show(); |
101 |
this.form.find(".edit-title").hide(); |
102 |
} else {
|
103 |
this.form.find(".new-title").hide(); |
104 |
this.form.find(".edit-title").show(); |
105 |
} |
106 |
|
107 |
var model = model || new this.collection.model(); |
108 |
this.list_cont.hide();
|
109 |
|
110 |
this.reset_form(model);
|
111 |
if (!snf.util.canReadFile()) {
|
112 |
this.form.find(".fromfile-field").hide(); |
113 |
} |
114 |
this.form.show();
|
115 |
|
116 |
this.editing = true; |
117 |
this.editing_id = model.id;
|
118 |
this.creating = create ? true : false; |
119 |
|
120 |
$(this.form.find("input").get(0)).focus(); |
121 |
this.reset_form_errors();
|
122 |
}, |
123 |
|
124 |
reset_form: function(model) { |
125 |
if (!model) {
|
126 |
this.form.find("input, textarea").val(""); |
127 |
return;
|
128 |
} |
129 |
|
130 |
this.update_form_from_model(model);
|
131 |
}, |
132 |
|
133 |
show_list_msg: function(type, msg) { |
134 |
this.list_messages = this.list_messages || this.$(".list-messages"); |
135 |
var el = $('<div class="{0}">{1}</div>'.format(type, msg)); |
136 |
this.list_messages.append(el);
|
137 |
window.setTimeout(function(){
|
138 |
el.fadeOut(300).delay(300).remove(); |
139 |
}, this.message_timeout || 4000) |
140 |
}, |
141 |
|
142 |
get_fields_map: function() { |
143 |
return {};
|
144 |
}, |
145 |
|
146 |
reset_form_errors: function() { |
147 |
this.form.find(".form-field").removeClass("error"); |
148 |
this.form.find(".form-field .errors").empty(); |
149 |
}, |
150 |
|
151 |
show_form_errors: function(errors) { |
152 |
this.reset_form_errors();
|
153 |
var fields_map = this.get_fields_map(); |
154 |
this.form_messages = this.form_messages || this.$(".form-messages"); |
155 |
|
156 |
_.each(errors.errors, _.bind(function(error, key){
|
157 |
var field = this.form.find(fields_map[key]).closest(".form-field"); |
158 |
field.addClass("error");
|
159 |
_.each(error, function(error_msg) {
|
160 |
var error_el = $('<div class="error">{0}</div>'.format(error_msg)); |
161 |
field.find(".errors").append(error_el);
|
162 |
}); |
163 |
}, this));
|
164 |
//var el = $('<div class="error">{1}</div>'.format(type, msg));
|
165 |
//this.list_messages.append(el);
|
166 |
}, |
167 |
|
168 |
clean_form_errors: function() { |
169 |
}, |
170 |
|
171 |
submit_form: function() { |
172 |
if (this.submiting) { return }; |
173 |
var errlist = this.validate_data(this.get_form_data()); |
174 |
if (errlist.empty()) {
|
175 |
this.save_model(this.get_form_data()); |
176 |
} else {
|
177 |
this.show_form_errors(errlist);
|
178 |
} |
179 |
}, |
180 |
|
181 |
close_form: function() { |
182 |
this.editing = false; |
183 |
this.editing_id = undefined; |
184 |
this.creating = false; |
185 |
|
186 |
this.form.hide();
|
187 |
this.list_cont.show();
|
188 |
this.list_cont.find("h3").show(); |
189 |
}, |
190 |
|
191 |
create_model_element: function(model) { |
192 |
var el = this.$(this.list_tpl).clone(); |
193 |
|
194 |
el.removeClass("hidden");
|
195 |
el.addClass("model-item");
|
196 |
el.attr("id", "item-content-" + this.id_tpl.format(model.id)); |
197 |
|
198 |
if (this.auto_append_actions) { |
199 |
this.append_actions(el, model);
|
200 |
} |
201 |
|
202 |
return el;
|
203 |
}, |
204 |
|
205 |
append_actions: function(el, model) { |
206 |
var actions = $('<div class="item-actions">' + |
207 |
'<div class="item-action remove">remove</div>' +
|
208 |
'<div class="item-action confirm-remove confirm">' +
|
209 |
'<span class="text">confirm</span>' +
|
210 |
'<span class="cancel-remove cancel">cancel</span></div>' +
|
211 |
'<div class="item-action edit">edit</div>' +
|
212 |
'</div>');
|
213 |
el.append(actions); |
214 |
}, |
215 |
|
216 |
bind_list_item_actions: function(el, model) { |
217 |
el.find(".item-actions .edit").click(_.bind(this.show_form, this, model)); |
218 |
el.find(".item-actions .remove").click(_.bind(this.show_confirm_remove, this, el, model)); |
219 |
el.find(".item-actions .confirm-remove .do-confirm").click(_.bind(this.delete_model, this, model)).hide(); |
220 |
el.find(".item-actions .confirm-remove .cancel-remove").click(_.bind(this.cancel_confirm_remove, |
221 |
this, el, model)).hide();
|
222 |
|
223 |
// initialize download link
|
224 |
snf.util.promptSaveFile(el.find(".item-actions .download"), model.get_filename(), model.get("content")) |
225 |
}, |
226 |
|
227 |
show_confirm_remove: function(el, model) { |
228 |
var confirmed = confirm("Are you sure you want to delete this key ?"); |
229 |
if (confirmed) {
|
230 |
this.delete_model(model);
|
231 |
} |
232 |
//el.closest(".model-item").addClass("pending-delete");
|
233 |
}, |
234 |
|
235 |
cancel_confirm_remove: function(el, model) { |
236 |
el.closest(".model-item").removeClass("pending-delete"); |
237 |
}, |
238 |
|
239 |
new_list_el: function(model) { |
240 |
var list_el = $("<li></li>"); |
241 |
el = this.create_model_element(model);
|
242 |
list_el.attr("id", this.id_tpl.format(model.id)); |
243 |
list_el.addClass("model-item");
|
244 |
this.update_list_item(el, model, true); |
245 |
list_el.append(el); |
246 |
this.bind_list_item_actions(list_el, model);
|
247 |
return list_el;
|
248 |
}, |
249 |
|
250 |
item_el: function(id) { |
251 |
return this.$("#" + this.id_tpl.format(id)); |
252 |
}, |
253 |
|
254 |
item_exists: function(model) { |
255 |
return this.item_el(model.id).length > 0; |
256 |
}, |
257 |
|
258 |
reset_list: function() { |
259 |
this.list.find("model-item").remove(); |
260 |
}, |
261 |
|
262 |
save_model: function(data) { |
263 |
this.form.find("form-action.submit").addClass("in-progress"); |
264 |
this.submiting = true; |
265 |
|
266 |
var options = {
|
267 |
success: _.bind(function(){ |
268 |
this.update_models();
|
269 |
this.close_form();
|
270 |
this.show_list_msg("success", "Public key created"); |
271 |
}, this),
|
272 |
|
273 |
error: _.bind(function(){ |
274 |
this.show_form_errors({'':'Public key submition failed'}) |
275 |
}, this),
|
276 |
|
277 |
complete: _.bind(function(){ |
278 |
this.submiting = false; |
279 |
this.form.find("form-action.submit").addClass("in-progress"); |
280 |
}, this)
|
281 |
} |
282 |
|
283 |
if (this.editing_id && this.collection.get(this.editing_id)) { |
284 |
var model = this.collection.get(this.editing_id); |
285 |
model.save(data, options); |
286 |
} else {
|
287 |
this.collection.create(data, options);
|
288 |
} |
289 |
}, |
290 |
|
291 |
delete_model: function(model) { |
292 |
this.item_el(model.id).addClass("in-progress").addClass("deleting"); |
293 |
var self = this; |
294 |
model.destroy({success:this.update_models, error: function() { |
295 |
self.show_list_msg("error", "Remove failed"); |
296 |
self.item_el(model.id).removeClass("in-progress").removeClass("deleting"); |
297 |
}}); |
298 |
}, |
299 |
|
300 |
update_removed: function(ids, models) { |
301 |
var newids = _.map(models, function(m) { return m.id }); |
302 |
_.each(_.difference(ids, newids), _.bind(function(id){
|
303 |
this.item_el(id).remove();
|
304 |
}, this));
|
305 |
}, |
306 |
|
307 |
update_list: function(models, reset) { |
308 |
var reset = reset || false; |
309 |
if (reset) { this.reset_list() }; |
310 |
|
311 |
// handle removed items
|
312 |
_.each(models, _.bind(function(model) {
|
313 |
if (this.item_exists(model)) { |
314 |
this.update_list_item(this.item_el(model.id), model); |
315 |
return;
|
316 |
}; |
317 |
this.list.append(this.new_list_el(model)) |
318 |
}, this));
|
319 |
|
320 |
this.check_empty();
|
321 |
}, |
322 |
|
323 |
check_empty: function() { |
324 |
if (this.collection.length == 0) { |
325 |
this.empty_msg.show();
|
326 |
this.list.find(".header").hide(); |
327 |
this.el.addClass("empty"); |
328 |
} else {
|
329 |
this.empty_msg.hide();
|
330 |
this.list.find(".header").show(); |
331 |
this.el.removeClass("empty"); |
332 |
} |
333 |
}, |
334 |
|
335 |
reset: function (){} |
336 |
|
337 |
}); |
338 |
|
339 |
views.PublicKeysView = views.CollectionView.extend({ |
340 |
collection: storage.keys,
|
341 |
|
342 |
initialize: function(options) { |
343 |
views.PublicKeysView.__super__.initialize.apply(this, arguments); |
344 |
this.$(".private-cont").hide(); |
345 |
_.bindAll(this);
|
346 |
}, |
347 |
|
348 |
append_actions: function(el, model) { |
349 |
var actions = $('<div class="item-actions">' + |
350 |
'<div class="item-action remove">remove</div>' +
|
351 |
'<div class="item-action confirm-remove">' +
|
352 |
'<span class="text do-confirm">confirm</span>' +
|
353 |
'<span class="cancel-remove cancel">X</span></div>' +
|
354 |
'<div class="item-action edit">edit</div>' +
|
355 |
'<div class="item-action show">show key</div>' +
|
356 |
'</div>');
|
357 |
el.append(actions); |
358 |
}, |
359 |
|
360 |
init_handlers: function() { |
361 |
views.PublicKeysView.__super__.init_handlers.apply(this, arguments); |
362 |
|
363 |
this.$(".add-generate").click(_.bind(this.generate_new, this, undefined)); |
364 |
|
365 |
// browser compat check
|
366 |
if (snf.util.canReadFile()) {
|
367 |
var self = this; |
368 |
this.form.find(".fromfile").get(0).addEventListener("change", function(e){ |
369 |
var f = undefined; |
370 |
var files = e.target.files;
|
371 |
if (files.length == 0) { return }; |
372 |
|
373 |
f = files[0];
|
374 |
var data = snf.util.readFileContents(f, _.bind(function(data) { |
375 |
this.form.find("textarea").val(data); |
376 |
}, self)); |
377 |
}); |
378 |
} |
379 |
|
380 |
var self = this; |
381 |
this.$(".private-cont .close-private").live("click", function(e) { |
382 |
self.$(".private-cont").hide(); |
383 |
self.$(".private-cont textarea").val(""); |
384 |
}); |
385 |
|
386 |
this.$(".item-action.show, .item-action.hide").live("click", function(e) { |
387 |
var open = $(this).parent().parent().parent().hasClass("expanded"); |
388 |
if (open) {
|
389 |
$(this).text("show key"); |
390 |
$(this).addClass("show").removeClass("hide"); |
391 |
} else {
|
392 |
$(this).text("hide key"); |
393 |
$(this).removeClass("show").addClass("hide"); |
394 |
} |
395 |
$(this).parent().parent().parent().toggleClass("expanded"); |
396 |
}); |
397 |
}, |
398 |
|
399 |
__generate_new: function(generate_text) { |
400 |
var key = storage.keys.generate_new();
|
401 |
var self = this; |
402 |
|
403 |
storage.keys.add_crypto_key(key, |
404 |
_.bind(function(instance, data) {
|
405 |
self.update_models(); |
406 |
this.generating = false; |
407 |
this.$(".add-generate").text(generate_text).removeClass( |
408 |
"in-progress").addClass("download"); |
409 |
this.show_download_private(key, instance);
|
410 |
},this),
|
411 |
|
412 |
_.bind(function() {
|
413 |
self.show_list_msg("error", "Cannot generate public key, please try again later."); |
414 |
|
415 |
this.generating = false; |
416 |
this.download_private = false; |
417 |
|
418 |
this.$(".add-generate").text(generate_text).removeClass("in-progress").removeClass("download"); |
419 |
}, this)
|
420 |
); |
421 |
}, |
422 |
|
423 |
generate_new: function() { |
424 |
if (this.generating) { return false }; |
425 |
this.$(".private-cont").hide(); |
426 |
this.generating = true; |
427 |
this.download_private = false; |
428 |
|
429 |
var generate_text = this.$(".add-generate").text(); |
430 |
this.$(".add-generate").text("Generating...").addClass("in-progress").removeClass("download"); |
431 |
|
432 |
window.setTimeout(_.bind(this.__generate_new, this, generate_text), 400); |
433 |
|
434 |
}, |
435 |
|
436 |
show_download_private: function(key, data) { |
437 |
var download_cont = this.$(".private-cont"); |
438 |
download_cont.find(".key-contents textarea").val(""); |
439 |
download_cont.find(".private-msg, .down-button").hide();
|
440 |
|
441 |
var pr = snf.util.promptSaveFile(download_cont.find(".down-button"), |
442 |
"{0}_private.pem".format(data.get("name")), key.privatePEM()); |
443 |
//pr = false;
|
444 |
if (pr) {
|
445 |
download_cont.find(".private-msg.download").show();
|
446 |
download_cont.find(".down-button").show();
|
447 |
download_cont.find(".key-contents textarea").val("").hide(); |
448 |
} else {
|
449 |
download_cont.find(".key-contents textarea").val(key.privatePEM()).show();
|
450 |
download_cont.find(".private-msg.copy").show();
|
451 |
} |
452 |
|
453 |
download_cont.show(); |
454 |
}, |
455 |
|
456 |
update_list_item: function(el, model) { |
457 |
el.find(".name").text(model.get("name")); |
458 |
el.find(".key-type").text(model.identify_type() || "unknown"); |
459 |
el.find(".publicid .param-content textarea").val(model.get("content")); |
460 |
el.find(".publicid").attr("title", _(model.get("content")).truncate(1000, "...")); |
461 |
return el;
|
462 |
}, |
463 |
|
464 |
update_form_from_model: function(model) { |
465 |
this.form.find("input.input-name").val(model.get("name")); |
466 |
this.form.find("textarea.input-content").val(model.get("content")); |
467 |
}, |
468 |
|
469 |
get_form_data: function() { |
470 |
return {
|
471 |
'name': this.form.find("input.input-name").val(), |
472 |
'content': this.form.find("textarea.input-content").val() |
473 |
} |
474 |
}, |
475 |
|
476 |
get_fields_map: function() { |
477 |
return {'name': "input.input-name", 'content': "textarea.input-content"}; |
478 |
}, |
479 |
|
480 |
validate_data: function(data) { |
481 |
var user_data = _.clone(data)
|
482 |
var errors = new snf.util.errorList(); |
483 |
|
484 |
if (!data.name || _.clean(data.name) == "") { |
485 |
errors.add("name", "Provide a valid public key name"); |
486 |
} |
487 |
|
488 |
if (!data.content || _.clean(data.content) == "") { |
489 |
errors.add("content", "Provide valid public key content"); |
490 |
return errors;
|
491 |
} |
492 |
|
493 |
try {
|
494 |
var content = snf.util.validatePublicKey(data.content);
|
495 |
if (content) {
|
496 |
this.form.find("textarea.input-content").val(content); |
497 |
} |
498 |
} catch (err) {
|
499 |
errors.add("content", "Invalid key content (" + err + ")"); |
500 |
} |
501 |
|
502 |
return errors;
|
503 |
}, |
504 |
|
505 |
reset: function() { |
506 |
this.$(".private-cont").hide(); |
507 |
this.$(".list-messages").empty(); |
508 |
this.$(".form-messages").empty(); |
509 |
this.$(".model-item").removeClass("expanded"); |
510 |
this.close_form();
|
511 |
} |
512 |
|
513 |
}) |
514 |
|
515 |
views.PublicKeysOverlay = views.Overlay.extend({ |
516 |
|
517 |
view_id: "public_keys_view", |
518 |
content_selector: "#user_public_keys", |
519 |
css_class: 'overlay-public-keys overlay-info', |
520 |
overlay_id: "user_public_keys_overlay", |
521 |
|
522 |
title: "Manage your ssh keys", |
523 |
subtitle: "SSH keys", |
524 |
|
525 |
initialize: function(options) { |
526 |
views.PublicKeysOverlay.__super__.initialize.apply(this, arguments); |
527 |
this.subview = new views.PublicKeysView({el:this.$(".public-keys-view")}); |
528 |
|
529 |
var self = this; |
530 |
this.$(".previous-view-link").live('click', function(){ |
531 |
self.hide(); |
532 |
}) |
533 |
}, |
534 |
|
535 |
show: function(view) { |
536 |
this.from_view = view || undefined; |
537 |
|
538 |
if (this.from_view) { |
539 |
this.$(".previous-view-link").show(); |
540 |
} else {
|
541 |
this.$(".previous-view-link").hide(); |
542 |
} |
543 |
|
544 |
this.subview.reset();
|
545 |
views.PublicKeysOverlay.__super__.show.apply(this, arguments); |
546 |
}, |
547 |
|
548 |
onClose: function() { |
549 |
if (this.from_view) { |
550 |
this.hiding = true; |
551 |
this.from_view.skip_reset_on_next_open = true; |
552 |
this.from_view.show();
|
553 |
this.from_view = undefined; |
554 |
} |
555 |
}, |
556 |
|
557 |
init_handlers: function() { |
558 |
} |
559 |
|
560 |
}); |
561 |
})(this);
|
562 |
|
563 |
|