Add support for creating dialogs, and add an About dialog.

This adds bootstrap-dialog to help with creation of nice dialogs,
and adds the ability for modules to render Javascript into the
browser, and specify onclick handlers in the menu system.

Also add a basic About dialog, using the new infrastructure and
showing some useful info about the application.
This commit is contained in:
Dave Page committed 2015-01-27 16:54:39 +00:00
1 parent d86c90fa73
commit 53d649de70
15 files changed
+1457 -14

No files matched your search

+3
View File
@@ -28,6 +28,9 @@ APP_REVISION = 0
# for GA releases.
APP_SUFFIX = 'dev'
# Copyright string for display in the app
APP_COPYRIGHT = 'Copyright 2014 - 2015, The pgAdmin Development Team'
# DO NOT CHANGE!
# The application version string, constructed from the components
APP_VERSION = '%s.%s.%s-%s' % (APP_MAJOR, APP_MINOR, APP_REVISION, APP_SUFFIX)
+12 -3
View File
@@ -9,10 +9,19 @@
"""Browser integration functions for the About module."""
from flask import url_for
from flask import render_template, url_for
import config
def get_help_menu_items():
"""Return a (set) of dicts of help menu items, with name, priority and URL."""
return [{'name': 'About %s' % (config.APP_NAME), 'priority': 999, 'url': url_for('about.index')}]
"""Return a (set) of dicts of help menu items, with name, priority, URL and
onclick code."""
return [{'name': 'About %s' % (config.APP_NAME),
'priority': 999,
'url': "#",
'onclick': "about_show()"}]
def get_javascript_code():
"""Render from the template and return any Javascript code snippets required
in the browser"""
return render_template("about/browser.js")
@@ -0,0 +1,16 @@
{#
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2014, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
#}
function about_show() {
BootstrapDialog.show({
title: 'About {{ config.APP_NAME }}',
message: $('<div></div>').load('{{ url_for('about.index') }}')});
}
+28 -1
View File
@@ -1 +1,28 @@
About {{ config.APP_NAME }}
<div class="row">
<div class="col-sm-3"><b>Version</b></div>
<div class="col-sm-9">{{ config.APP_VERSION }}</div>
</div>
<div class="row">
<div class="col-sm-3"><b>Copyright</b></div>
<div class="col-sm-9">{{ config.APP_COPYRIGHT }}</div>
</div>
<div class="row">
<div class="col-sm-3"><b>Python Version</b></div>
<div class="col-sm-9">{{ info.python_version }}</div>
</div>
<div class="row">
<div class="col-sm-3"><b>Flask Version</b></div>
<div class="col-sm-9">{{ info.flask_version }}</div>
</div>
<div class="row">
<div class="col-sm-3"><b>Application Mode</b></div>
<div class="col-sm-9">{{ info.app_mode }}</div>
</div>
<div class="row">
<div class="col-sm-3"><b>Current User</b></div>
<div class="col-sm-9">{{ info.current_user }}</div>
</div>
<div class="row">
<div class="col-sm-3 pull-right"><img src="{{ url_for('static', filename='img/logo-right-128.png') }}" alt="{{ config.APP_NAME }}"></div>
</div>
+15 -3
View File
@@ -10,9 +10,12 @@
"""A blueprint module implementing the about box."""
MODULE_NAME = 'about'
from flask import Blueprint, current_app, render_template, __version__
from flask.ext.security import current_user, login_required
import sys
import config
from flask import Blueprint, current_app, render_template
from flask.ext.security import login_required
# Initialise the module
blueprint = Blueprint(MODULE_NAME, __name__, static_folder='static', static_url_path='', template_folder='templates', url_prefix='/' + MODULE_NAME)
@@ -24,4 +27,13 @@ blueprint = Blueprint(MODULE_NAME, __name__, static_folder='static', static_url
@login_required
def index():
"""Render the about box."""
return render_template(MODULE_NAME + '/index.html')
info = { }
info['python_version'] = sys.version
info['flask_version'] = __version__
if config.SERVER_MODE == True:
info['app_mode'] = 'Server'
else:
info['app_mode'] = 'Desktop'
info['current_user'] = current_user.email
return render_template(MODULE_NAME + '/index.html', info=info)
@@ -21,7 +21,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">File <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
{% for file_item in file_items %}
<li><a href="{{ file_item.url }}">{{ file_item.name|safe }}</a></li>
<li><a href="{{ file_item.url }}" onclick="{{ file_item.onclick|safe }}">{{ file_item.name }}</a></li>
{% endfor %}
</ul>
</li>
@@ -30,7 +30,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Edit <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
{% for edit_item in edit_items %}
<li><a href="{{ edit_item.url }}">{{ edit_item.name|safe }}</a></li>
<li><a href="{{ edit_item.url }}" onclick="{{ edit_item.onclick|safe }}">{{ edit_item.name }}</a></li>
{% endfor %}
</ul>
</li>
@@ -39,7 +39,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Tools <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
{% for tools_item in tools_items %}
<li><a href="{{ tools_item.url }}">{{ tools_item.name|safe }}</a></li>
<li><a href="{{ tools_item.url }}" onclick="{{ tools_item.onclick|safe }}">{{ tools_item.name }}</a></li>
{% endfor %}
</ul>
</li>
@@ -48,7 +48,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Help <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
{% for help_item in help_items %}
<li><a href="{{ help_item.url }}">{{ help_item.name|safe }}</a></li>
<li><a href="{{ help_item.url }}" onclick="{{ help_item.onclick|safe }}">{{ help_item.name }}</a></li>
{% endfor %}
</ul>
</li>
@@ -70,6 +70,12 @@
</div>
</nav>
{% include 'browser/body.html' %}
{% include 'browser/messages.html' %}
<script language="javascript">
{{ js_code|safe }}
</script>
{% endblock %}
+9 -2
View File
@@ -39,11 +39,12 @@ def index():
use_ssl=False,
base_url=None)
# Get the menu items from the module
# Get the plugin elements from the module
file_items = [ ]
edit_items = [ ]
tools_items = [ ]
help_items = [ ]
js_code = ''
for module in modules:
# Get the edit menu items
@@ -61,15 +62,21 @@ def index():
# Get the help menu items
if 'browser' in dir(module) and 'get_help_menu_items' in dir(module.browser):
help_items.extend(module.browser.get_help_menu_items())
# Get any Javascript code
if 'browser' in dir(module) and 'get_javascript_code' in dir(module.browser):
js_code += (module.browser.get_javascript_code())
file_items = sorted(file_items, key=lambda k: k['priority'])
edit_items = sorted(edit_items, key=lambda k: k['priority'])
tools_items = sorted(tools_items, key=lambda k: k['priority'])
help_items = sorted(help_items, key=lambda k: k['priority'])
# Get any Javascript snippets
return render_template(MODULE_NAME + '/index.html',
username=current_user.email,
file_items=file_items,
edit_items=edit_items,
tools_items=tools_items,
help_items=help_items)
help_items=help_items,
js_code = js_code)
+120
View File
@@ -0,0 +1,120 @@
.bootstrap-dialog {
}
.bootstrap-dialog .modal-header {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.bootstrap-dialog .bootstrap-dialog-title {
color: #fff;
display: inline-block;
}
.bootstrap-dialog.type-default .bootstrap-dialog-title {
color: #333;
}
.bootstrap-dialog .bootstrap-dialog-title {
font-size: 16px;
}
.bootstrap-dialog.size-large .bootstrap-dialog-title {
font-size: 24px;
}
.bootstrap-dialog .bootstrap-dialog-close-button {
float: right;
filter:alpha(opacity=90);
-moz-opacity:0.9;
-khtml-opacity: 0.9;
opacity: 0.9;
}
.bootstrap-dialog .bootstrap-dialog-close-button {
font-size: 20px;
}
.bootstrap-dialog.size-large .bootstrap-dialog-close-button {
font-size: 30px;
}
.bootstrap-dialog .bootstrap-dialog-close-button:hover {
cursor: pointer;
filter: alpha(opacity=100);
-moz-opacity: 1;
-khtml-opacity: 1;
opacity: 1;
}
.bootstrap-dialog .bootstrap-dialog-message {
font-size: 14px;
}
.bootstrap-dialog.size-large .bootstrap-dialog-message {
font-size: 18px;
}
.bootstrap-dialog.type-default .modal-header {
background-color: #fff;
}
.bootstrap-dialog.type-info .modal-header {
background-color: #5bc0de;
}
.bootstrap-dialog.type-primary .modal-header {
background-color: #428bca;
}
.bootstrap-dialog.type-success .modal-header {
background-color: #5cb85c;
}
.bootstrap-dialog.type-warning .modal-header {
background-color: #f0ad4e;
}
.bootstrap-dialog.type-danger .modal-header {
background-color: #d9534f;
}
.bootstrap-dialog .bootstrap-dialog-button-icon {
margin-right: 3px;
}
/**
* Icon animation
* Copied from font-awesome: http://fontawesome.io/
**/
.icon-spin {
display: inline-block;
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
-webkit-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
}
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
}
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
}
}
@-o-keyframes spin {
0% {
-o-transform: rotate(0deg);
}
100% {
-o-transform: rotate(359deg);
}
}
@-ms-keyframes spin {
0% {
-ms-transform: rotate(0deg);
}
100% {
-ms-transform: rotate(359deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(359deg);
}
}
/** End of icon animation **/
+1
View File
@@ -0,0 +1 @@
.bootstrap-dialog .modal-header{border-top-left-radius:4px;border-top-right-radius:4px}.bootstrap-dialog .bootstrap-dialog-title{color:#fff;display:inline-block}.bootstrap-dialog.type-default .bootstrap-dialog-title{color:#333}.bootstrap-dialog .bootstrap-dialog-title{font-size:16px}.bootstrap-dialog.size-large .bootstrap-dialog-title{font-size:24px}.bootstrap-dialog .bootstrap-dialog-close-button{float:right;filter:alpha(opacity=90);-moz-opacity:.9;-khtml-opacity:.9;opacity:.9}.bootstrap-dialog .bootstrap-dialog-close-button{font-size:20px}.bootstrap-dialog.size-large .bootstrap-dialog-close-button{font-size:30px}.bootstrap-dialog .bootstrap-dialog-close-button:hover{cursor:pointer;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}.bootstrap-dialog .bootstrap-dialog-message{font-size:14px}.bootstrap-dialog.size-large .bootstrap-dialog-message{font-size:18px}.bootstrap-dialog.type-default .modal-header{background-color:#fff}.bootstrap-dialog.type-info .modal-header{background-color:#5bc0de}.bootstrap-dialog.type-primary .modal-header{background-color:#428bca}.bootstrap-dialog.type-success .modal-header{background-color:#5cb85c}.bootstrap-dialog.type-warning .modal-header{background-color:#f0ad4e}.bootstrap-dialog.type-danger .modal-header{background-color:#d9534f}.bootstrap-dialog .bootstrap-dialog-button-icon{margin-right:3px}.icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0{-moz-transform:rotate(0)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0{-o-transform:rotate(0)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0{-ms-transform:rotate(0)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0{transform:rotate(0)}100%{transform:rotate(359deg)}}
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+1239
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
(function(a,b){if(typeof module!=="undefined"&&module.exports){module.exports=b(require("jquery")(a))}else{if(typeof define==="function"&&define.amd){define("bootstrap-dialog",["jquery"],function(c){return b(c)})}else{a.BootstrapDialog=b(a.jQuery)}}}(this,function(d){var b=d.fn.modal.Constructor;var c=function(f,e){b.call(this,f,e)};c.getModalVersion=function(){var e=null;if(typeof d.fn.modal.Constructor.VERSION==="undefined"){e="v3.1"}else{if(/3\.2\.\d+/.test(d.fn.modal.Constructor.VERSION)){e="v3.2"}else{e="v3.3"}}return e};c.ORIGINAL_BODY_PADDING=d("body").css("padding-right")||0;c.METHODS_TO_OVERRIDE={};c.METHODS_TO_OVERRIDE["v3.1"]={};c.METHODS_TO_OVERRIDE["v3.2"]={hide:function(g){if(g){g.preventDefault()}g=d.Event("hide.bs.modal");this.$element.trigger(g);if(!this.isShown||g.isDefaultPrevented()){return}this.isShown=false;var f=this.getGlobalOpenedDialogs();if(f.length===0){this.$body.removeClass("modal-open")}this.resetScrollbar();this.escape();d(document).off("focusin.bs.modal");this.$element.removeClass("in").attr("aria-hidden",true).off("click.dismiss.bs.modal");d.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",d.proxy(this.hideModal,this)).emulateTransitionEnd(300):this.hideModal()}};c.METHODS_TO_OVERRIDE["v3.3"]={setScrollbar:function(){var e=c.ORIGINAL_BODY_PADDING;if(this.bodyIsOverflowing){this.$body.css("padding-right",e+this.scrollbarWidth)}},resetScrollbar:function(){var e=this.getGlobalOpenedDialogs();if(e.length===0){this.$body.css("padding-right",c.ORIGINAL_BODY_PADDING)}},hideModal:function(){this.$element.hide();this.backdrop(d.proxy(function(){var e=this.getGlobalOpenedDialogs();if(e.length===0){this.$body.removeClass("modal-open")}this.resetAdjustments();this.resetScrollbar();this.$element.trigger("hidden.bs.modal")},this))}};c.prototype={constructor:c,getGlobalOpenedDialogs:function(){var e=[];d.each(a.dialogs,function(g,f){if(f.isRealized()&&f.isOpened()){e.push(f)}});return e}};c.prototype=d.extend(c.prototype,b.prototype,c.METHODS_TO_OVERRIDE[c.getModalVersion()]);var a=function(e){this.defaultOptions=d.extend(true,{id:a.newGuid(),buttons:[],data:{},onshow:null,onshown:null,onhide:null,onhidden:null},a.defaultOptions);this.indexedButtons={};this.registeredButtonHotkeys={};this.draggableData={isMouseDown:false,mouseOffset:{}};this.realized=false;this.opened=false;this.initOptions(e);this.holdThisInstance()};a.NAMESPACE="bootstrap-dialog";a.TYPE_DEFAULT="type-default";a.TYPE_INFO="type-info";a.TYPE_PRIMARY="type-primary";a.TYPE_SUCCESS="type-success";a.TYPE_WARNING="type-warning";a.TYPE_DANGER="type-danger";a.DEFAULT_TEXTS={};a.DEFAULT_TEXTS[a.TYPE_DEFAULT]="Information";a.DEFAULT_TEXTS[a.TYPE_INFO]="Information";a.DEFAULT_TEXTS[a.TYPE_PRIMARY]="Information";a.DEFAULT_TEXTS[a.TYPE_SUCCESS]="Success";a.DEFAULT_TEXTS[a.TYPE_WARNING]="Warning";a.DEFAULT_TEXTS[a.TYPE_DANGER]="Danger";a.DEFAULT_TEXTS.OK="OK";a.DEFAULT_TEXTS.CANCEL="Cancel";a.SIZE_NORMAL="size-normal";a.SIZE_SMALL="size-small";a.SIZE_WIDE="size-wide";a.SIZE_LARGE="size-large";a.BUTTON_SIZES={};a.BUTTON_SIZES[a.SIZE_NORMAL]="";a.BUTTON_SIZES[a.SIZE_SMALL]="";a.BUTTON_SIZES[a.SIZE_WIDE]="";a.BUTTON_SIZES[a.SIZE_LARGE]="btn-lg";a.ICON_SPINNER="glyphicon glyphicon-asterisk";a.defaultOptions={type:a.TYPE_PRIMARY,size:a.SIZE_NORMAL,cssClass:"",title:null,message:null,nl2br:true,closable:true,closeByBackdrop:true,closeByKeyboard:true,spinicon:a.ICON_SPINNER,autodestroy:true,draggable:false,animate:true,description:""};a.configDefaultOptions=function(e){a.defaultOptions=d.extend(true,a.defaultOptions,e)};a.dialogs={};a.openAll=function(){d.each(a.dialogs,function(f,e){e.open()})};a.closeAll=function(){d.each(a.dialogs,function(f,e){e.close()})};a.moveFocus=function(){var e=null;d.each(a.dialogs,function(g,f){e=f});if(e!==null&&e.isRealized()){e.getModal().focus()}};a.METHODS_TO_OVERRIDE={};a.METHODS_TO_OVERRIDE["v3.1"]={handleModalBackdropEvent:function(){this.getModal().on("click",{dialog:this},function(e){e.target===this&&e.data.dialog.isClosable()&&e.data.dialog.canCloseByBackdrop()&&e.data.dialog.close()});return this},updateZIndex:function(){var g=1040;var h=1050;var i=0;d.each(a.dialogs,function(j,k){i++});var f=this.getModal();var e=f.data("bs.modal").$backdrop;f.css("z-index",h+(i-1)*20);e.css("z-index",g+(i-1)*20);return this},open:function(){!this.isRealized()&&this.realize();this.getModal().modal("show");this.updateZIndex();this.setOpened(true);return this}};a.METHODS_TO_OVERRIDE["v3.2"]={handleModalBackdropEvent:a.METHODS_TO_OVERRIDE["v3.1"]["handleModalBackdropEvent"],updateZIndex:a.METHODS_TO_OVERRIDE["v3.1"]["updateZIndex"],open:a.METHODS_TO_OVERRIDE["v3.1"]["open"]};a.METHODS_TO_OVERRIDE["v3.3"]={};a.prototype={constructor:a,initOptions:function(e){this.options=d.extend(true,this.defaultOptions,e);return this},holdThisInstance:function(){a.dialogs[this.getId()]=this;return this},initModalStuff:function(){this.setModal(this.createModal()).setModalDialog(this.createModalDiaLine truncated
+2
View File
@@ -15,6 +15,7 @@
<meta name="dcterms.dateCopyrighted" content="2014 - 2015">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-dialog.min.css') }}">
<style>
body {
padding-top: 50px;
@@ -36,6 +37,7 @@
<script src="{{ url_for('static', filename='js/vendor/jquery-1.11.1.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/vendor/bootstrap.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/vendor/bootstrap-dialog.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
</body>
</html>
@@ -1,5 +1,5 @@
{% block watermark %}
<div style="position: fixed; bottom: 0; right: 0;">
<img src="img/logo-right-256.png" alt="pgAdmin 4">
<img src="{{ url_for('static', filename='img/logo-right-256.png') }}" alt="pgAdmin 4">
</div>
{% endblock %}