Revision 9ffd10ce

b/ui/static/main.css
4656 4656
    font-weight: bold;
4657 4657
}
4658 4658

  
4659
.overlay-error .indicator {
4660
    background-color: #880000;
4661
    color: #fff;
4662
    padding: 4px;
4663
    display: block;
4664
    float: left;
4665
    border: 1px solid #444;
4666
}
4667

  
4668
.overlay-error .nav-btn:hover {
4669
    
4670
}
4671

  
4672
.overlay-error .nav-btn {
4673
    margin-left: 5px;
4674
    color: #fff;
4675
    display: block;
4676
    float: left;
4677
    padding: 4px;
4678
    background-color: #AAA;
4679
    border: 1px solid #444;
4680
}
4681

  
4682
.overlay-error .error-nav {
4683
    position: absolute;
4684
    right: 0px;
4685
    bottom: -25px;
4686
    font-size: 0.8em;
4687
}
4688

  
4659 4689
.overlay-error span.value, .overlay-error div.value {
4660 4690
    padding: 0.4em;
4661 4691
    display: block;
b/ui/static/snf/js/models.js
423 423
        },
424 424

  
425 425
        rename: function(name, callback) {
426
            return this.api.call(this.api_path(), "update", {network:{name:name}}, callback);
426
            return this.api.call(this.api_path(), "update", {
427
                network:{name:name}, 
428
                _options:{
429
                    critical: false, 
430
                    error_params:{
431
                        title: "Network action failed",
432
                        ns: "Networks",
433
                        extra_details: {"Network id": this.id},
434
                    }
435
                }}, callback);
427 436
        },
428 437

  
429 438
        get_connectable_vms: function() {
......
805 814
            var url = this.api_path() + "/meta/" + meta.key;
806 815
            var payload = {meta:{}};
807 816
            payload.meta[meta.key] = meta.value;
817
            payload._options = {
818
                critical:false, 
819
                error_params: {
820
                    title: "Machine metadata error",
821
                    extra_details: {"Machine id": this.id}
822
            }};
808 823

  
809
            // inject error settings
810
            payload._options = {critical: false};
811

  
812
            this.api.call(url, "update", payload, complete, error)
824
            this.api.call(url, "update", payload, complete, error);
813 825
        },
814 826

  
815 827
        set_firewall: function(net_id, value, callback, error, options) {
......
976 988
        rename: function(new_name) {
977 989
            //this.set({'name': new_name});
978 990
            this.sync("update", this, {
991
                critical: true,
979 992
                data: {
980 993
                    'server': {
981 994
                        'name': new_name
......
1081 1094
                success: function(){ self.handle_action_succeed.apply(self, arguments); success.apply(this, arguments)},
1082 1095
                error: function(){ self.handle_action_fail.apply(self, arguments); error.apply(this, arguments)},
1083 1096
                error_params: { ns: "Machines actions", 
1084
                                message: "'" + this.get("name") + "'" + " action failed", 
1097
                                title: "'" + this.get("name") + "'" + " " + action + " failed", 
1085 1098
                                extra_details: { 'Machine ID': this.id, 'URL': url, 'Action': action || "undefined" },
1086 1099
                                allow_reload: false
1087 1100
                              },
b/ui/static/snf/js/sync.js
413 413

  
414 414
    // on api error update the api error_state
415 415
    api.bind("error", function() {
416
        if (snf.api.error_state == snf.api.STATES.ERROR) { return };
417

  
416 418
        var args = _.toArray(_.toArray(arguments)[0]);
417 419
        var params = _.last(args);
418 420
        
......
420 422
            snf.api.error_state = api.STATES.ERROR;
421 423
            snf.api.stop_calls = true;
422 424
        } else {
423
            snf.api.error_state = api.STATES.WARN;
425
            snf.api.error_state = api.STATES.ERROR;
424 426
        }
425 427
        snf.api.trigger("change:error_state", snf.api.error_state);
426 428
    });
b/ui/static/snf/js/ui/web/ui_error_view.js
52 52

  
53 53
            this.$(".reload-app").click(function(){
54 54
                window.location.reload(true);
55
            })
55
            });
56

  
57
            this.$(".show-next").click(_.bind(function(){
58
                this.show_next_error();
59
            }, this));
60

  
61
            this.$(".show-prev").click(_.bind(function(){
62
                this.show_prev_error();
63
            }, this));
64

  
65
            this.displaying_error = false;
66
            this.error_stack_index = [];
67
            this.error_stack = {};
56 68
        },
57 69

  
58 70
        error_object: function() {
......
62 74
        report_error: function() {
63 75
            this.feedback_view = this.feedback_view || ui.main.feedback_view;
64 76
            this.hide(false);
77
            this.displaying_error = true;
78

  
65 79
            window.setTimeout(_.bind(function() {
66 80
                this.feedback_view.show(this.get_report_message(), true, {error: this.error_object()});
67 81
            }, this), 400);
......
81 95
        },
82 96
        
83 97
        show_error: function(ns, code, message, type, details, error_options) {
84
            if (snf.api.error_state == snf.api.STATES.NORMAL) { this.error_stack = {} };
85
                
98
            
86 99
            var error_entry = [ns, code, message, type, details, error_options];
87
            this.error_stack[new Date()] = error_entry;
88
            this.display_error.apply(this, error_entry);
89
            this.show();
100
            var last_error_key = this.update_errors_stack(error_entry);
101
            
102
            if (!this.is_visible && !this.displaying_error) {
103
                this.current_error = last_error_key;
104
                this.display_error.call(this, last_error_key);
105
                this.show();
106
            }
107

  
108
            this.update_errors_stack();
109
        },
110

  
111
        update_errors_stack: function(entry) {
112
            if (snf.api.error_state != snf.api.STATES.ERROR) { 
113
                this.error_stack = {};
114
                this.error_stack_index = [];
115
            };
116

  
117
            var stack_key = (new Date()).getTime();
118
            this.error_stack[stack_key] = entry;
119
            this.error_stack_index.push(stack_key);
120
            this.errors_occured = this.error_stack_index.length;
121
            
122
            this.$(".error-nav").hide();
123
            //this.update_errors_stack_layout();
124
            return stack_key;
90 125
        },
91 126

  
92
        display_error: function(ns, code, message, type, details, error_options) {
127
        is_last_error: function(stack_key) {
128
            return this.error_stack_index.indexOf(stack_key) == this.error_stack_index.length - 1;
129
        },
130

  
131
        is_first_error: function(stack_key) {
132
            return this.error_stack_index.indexOf(stack_key) == 0;
133
        },
134

  
135
        update_errors_stack_layout: function() {
136
            if (!this.current_error) { return };
137

  
138
            if (this.errors_occured <= 1) {
139
                this.$(".error-nav").hide();
140
            } else {
141
                this.$(".error-nav").show();
142
            };
143
            
144
            if (this.is_last_error(this.current_error)) {
145
                this.$(".show-next").hide();
146
            } else {
147
                this.$(".show-next").show();
148
            }
149

  
150
            if (this.is_first_error(this.current_error)) {
151
                this.$(".show-prev").hide();
152
            } else {
153
                this.$(".show-prev").show();
154
            }
155
        },
156

  
157
        show_next_error: function() {
158
        },
159

  
160
        show_prev_error: function() {
161
        },
162

  
163
        display_error: function(stack_key) {
164
            var err = this.error_stack[stack_key];
165
            var ns = err[0], code = err[1], message = err[2], type = err[3], details = err[4], error_options = err[5]
166

  
93 167
            this.error_options = {'allow_report': true, 'allow_reload': true, 
94 168
                'extra_details': {}, 'non_critical': false, 
95
                'allow_details': false };
169
                'allow_details': false,
170
                'allow_close': true };
96 171
            
97 172
            if (error_options) {
98 173
                this.error_options = _.extend(this.error_options, error_options);
......
120 195
            }
121 196
            
122 197
            this.$(".actions .show-details").click();
198
            this.$(".error-details").hide();
123 199
            this.$(".key.details").click();
124 200
            this.$(".error-more-details").hide();
125 201
        },
......
170 246
                this.$(".reload-app").hide();
171 247
            }
172 248

  
249
            if (this.error_options.allow_close) {
250
                this.$(".closeme").show();
251
            } else {
252
                this.$(".closeme").hide();
253
            }
254

  
173 255
        },
174 256

  
175 257
        onOpen: function() {
258
            this.displaying_error = true;
176 259
            var self = this;
177 260

  
178 261
            this.$(".closeme").unbind("click");
179 262
            this.$(".closeme").bind("click", function(){
180
                self.hide("reset")
263
                self.hide("reset");
181 264
            })
182 265
        },
183 266

  
184 267
        hide: function(reset_state) {
185
            if (reset_state === "reset") { snf.api.trigger("reset") };
268
            if (reset_state === "reset") {
269
                // delay reset error state for fade out
270
                window.setTimeout(_.bind(function(){
271
                    this.displaying_error = false;
272
                    this.error_stack = {};
273
                    snf.api.trigger("reset");
274
                }, this), 500);
275
            } else {
276
                this.displaying_error = false;
277
            }
186 278
            views.ErrorView.__super__.hide.apply(this);
187 279
        },
188 280

  
b/ui/static/snf/js/ui/web/ui_feedback_view.js
141 141
                // report error feedback form
142 142
                if (snf.api.error_state == snf.api.STATES.ERROR) {
143 143
                    snf.api.trigger("reset");
144
                    ui.main.error_view.displaying_error = false;
145
                    ui.main.error_view.is_visible = false;
144 146
                }
145 147
            }
148
            ui.main.error_view.is_visible = false;
146 149
            views.FeedbackView.__super__.hide.apply(this);
147 150
        },
148 151

  
149 152
        show: function(data, collect_data, extra_data, cb) {
153
            // proxy error view visibility to avoid showing
154
            // errors while user sees feedback overlay
155
            ui.main.error_view.is_visible = true;
156

  
150 157
            this.data = data || "";
151 158
            this.cb = cb || function () {};
152 159
            this.collect_data = collect_data || false;
b/ui/static/snf/js/ui/web/ui_main_view.js
393 393
            this.error_view.show_error.apply(this.error_view, error_entry);
394 394
        },
395 395

  
396
        handle_ui_error: function(error) {
397
            error = error + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
398
            this.error_view.show_error("Application", -1, "Something went wrong", "JS Exception", error);
396
        handle_ui_error: function(data) {
397
            var msg = data.msg, code = data.code, err_obj = data.error;
398
            error = msg + "<br /><br />" + snf.util.stacktrace().replace("at", "<br /><br />at");
399
            params = { title: "UI error", extra_details: data.extra };
400
            params.allow_close = data.extra.allow_close === undefined ? true : data.extra.allow_close;
401
            this.error_view.show_error("UI", -1, msg, "JS Exception", error, params);
399 402
        },
400 403

  
401 404
        init_overlays: function() {
......
622 625
                            window.positionFooter();
623 626
                            this.multiple_actions_view.fix_position();
624 627
                        }, this));
625
                    } catch (err) {snf.ui.trigger("error", err)}
628
                    } catch (err) {snf.ui.trigger_error(-1, "Cannot add view", err)}
626 629
                }
627 630
            } else {
628 631
            }
......
690 693
        },
691 694

  
692 695
        show_view: function(view_id) {
693
            // same view, visible
694
            // get out of here asap
695
            if (this.current_view && 
696
                this.current_view.id == view_id && 
697
                this.current_view.visible()) {
698
                return;
699
            }
700
            
701
            // choose proper view_id
702
            view_id = this.identify_view(view_id);
703

  
704
            // add/create view and update current view
705
            var view = this.add_view(view_id);
706
            
707
            // set current view
708
            this.current_view = view;
709
            this.current_view_id = view_id;
710

  
711
            // hide all other views
712
            this.hide_views([this.current_view]);
713
            
714
            // FIXME: depricated
715
            $(".large-spinner").remove();
716

  
717
            storage.vms.reset_pending_actions();
718
            storage.vms.stop_stats_update();
719

  
720
            // show current view
721
            this.show_view_pane();
722
            view.show();
723
            
724
            // update menus
725
            if (this.select_view) {
726
                this.select_view.update_layout();
727
            }
728
            this.current_view.__update_layout();
696
            try {
697
                // same view, visible
698
                // get out of here asap
699
                if (this.current_view && 
700
                    this.current_view.id == view_id && 
701
                    this.current_view.visible()) {
702
                    return;
703
                }
704
                
705
                // choose proper view_id
706
                view_id = this.identify_view(view_id);
707

  
708
                // add/create view and update current view
709
                var view = this.add_view(view_id);
710
                
711
                // set current view
712
                this.current_view = view;
713
                this.current_view_id = view_id;
714

  
715
                // hide all other views
716
                this.hide_views([this.current_view]);
717
                
718
                // FIXME: depricated
719
                $(".large-spinner").remove();
720

  
721
                storage.vms.reset_pending_actions();
722
                storage.vms.stop_stats_update();
723

  
724
                // show current view
725
                this.show_view_pane();
726
                view.show();
727
                
728
                // update menus
729
                if (this.select_view) {
730
                    this.select_view.update_layout();
731
                }
732
                this.current_view.__update_layout();
733

  
734
                // update cookies
735
                this.update_session();
736
                
737
                // machines view subnav
738
                if (this.current_view.vms_view) {
739
                    $("#machines-pane").show();
740
                } else {
741
                    $("#machines-pane").hide();
742
                }
743
                
744
                // fix footer position
745
                // TODO: move footer handlers in
746
                // main view (from home.html)
747
                if (window.positionFooter) {
748
                    window.positionFooter();
749
                }
729 750

  
730
            // update cookies
731
            this.update_session();
732
            
733
            // machines view subnav
734
            if (this.current_view.vms_view) {
735
                $("#machines-pane").show();
736
            } else {
737
                $("#machines-pane").hide();
751
                // trigger view change event
752
                this.trigger("view:change", this.current_view.view_id);
753
                $(window).trigger("view:change");
754
                return view;
755
            } catch (err) {
756
                snf.ui.trigger_error(-2, "Cannot show view: " + view_id, err);
738 757
            }
739
            
740
            // fix footer position
741
            // TODO: move footer handlers in
742
            // main view (from home.html)
743
            if (window.positionFooter) {
744
                window.positionFooter();
745
            }
746
            
747
            // trigger view change event
748
            this.trigger("view:change", this.current_view.view_id);
749
            $(window).trigger("view:change");
750
            return view;
751 758
        },
752 759

  
753 760
        reset_vm_actions: function() {
......
781 788
    }
782 789

  
783 790
    snf.ui.init = function() {
791
        window.onerror = function(msg, file, line) {
792
            snf.ui.trigger_error("CRITICAL", msg, {}, { file:file + ":" + line, allow_close: false });
793
        };
784 794
        snf.ui.main.load();
785 795
    }
786 796

  
b/ui/static/snf/js/ui/web/ui_vms_base_view.js
550 550
       return os_icon_tag(vm.get_os(), size, vm.is_active(), attrs);
551 551
    }
552 552
    
553

  
553 554
    snf.ui = _.extend(snf.ui, bb.Events);
555
    snf.ui.trigger_error = function(code, msg, error, extra) {
556
        snf.ui.trigger("error", { code:code, msg:msg, error:error, extra:extra || {} })
557
    };
554 558

  
555 559
})(this);
b/ui/static/snf/js/utils.js
56 56
        var data = {}
57 57
        
58 58
        try {
59
            data.client = {'browser': $.browser, 'screen': $.extend({}, screen), 'client': $.client}
60
        } catch (err) { data.client = err }
61
        try {
59 62
            data.calls = synnefo.api.requests;
60 63
        } catch (err) { data.calls = err }
61 64
        try {
......
68 71
            data.data.vms = synnefo.storage.vms.toJSON();
69 72
        } catch (err) { data.data.vms = err }
70 73
        try {
71
            data.data.networks = synnefo.storage.vms.toJSON();
74
            data.data.networks = synnefo.storage.networks.toJSON();
72 75
        } catch (err) { data.data.networks = err }
73
        try {
74
            data.data.images = synnefo.storage.images.toJSON();
75
        } catch (err) { data.data.images = err }
76
        try {
77
            data.data.flavors = synnefo.storage.flavors.toJSON();
78
        } catch (err) { data.data.flavors = err }
76
        //try {
77
            //data.data.images = synnefo.storage.images.toJSON();
78
        //} catch (err) { data.data.images = err }
79
        //try {
80
            //data.data.flavors = synnefo.storage.flavors.toJSON();
81
        //} catch (err) { data.data.flavors = err }
79 82
        try {
80 83
            data.date = new Date;
81 84
        } catch (err) { data.date = err }
b/ui/static/snf/js/views.js
119 119

  
120 120
            this.overlay = $(this.el).overlay();
121 121
            this.append_css = this.options ? this.options.css_class ? this.options.css_class : "" : "";
122

  
123
            this.is_visible = false;
122 124
            return this;
123 125
        },
124 126

  
......
169 171
        },
170 172

  
171 173
        _beforeOpen: function() {
174
            this.is_visible = true;
172 175
            if (this.append_css) {
173 176
                $(this.el).addClass(this.append_css);
174 177
            }
......
194 197
        },
195 198

  
196 199
        _beforeClose: function() {
200
            this.is_visible = false;
197 201
            this.beforeClose.apply(this, arguments);
198 202
            this.options.beforeClose.apply(this, arguments);
199 203
        },
......
224 228

  
225 229
            // do we need to wait for other overlays to close ???
226 230
            if (hidden) { delay = 300; } else { delay = 0; }
231

  
232
            this.is_visible = true;
227 233
            window.setTimeout(_.bind(function(){ this.overlay.load() }, this), delay)
228 234
            return this;
229 235
        },
b/ui/templates/home.html
419 419
    </div>
420 420

  
421 421
    <div id="error-overlay-content" class="hidden">
422
        <div class="error-nav hidden clearfix">
423
            <span class="indicator"><span class="num">1</span> <span
424
                    class="ind-msg">new error occured</span></span>
425
            <span class="show-next nav-btn">{% trans "next" %}</span>
426
            <span class="show-prev nav-btn">{% trans "previous" %}</span>
427
        </div>
422 428
        <div class="message"><p></p></div>
423 429
        <div class="error-details">
424 430
            <span class="key">{% trans "Module" %}</span>

Also available in: Unified diff