Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b58f288eec | ||
|
|
140372a5cf | ||
|
|
d97431cb4f | ||
|
|
1e14b0d068 | ||
|
|
4ff0fcdd82 | ||
|
|
696fbfb8b9 | ||
|
|
be47390baf | ||
|
|
0d5a6eeeff | ||
|
|
6ddab02769 | ||
|
|
091aa62084 | ||
|
|
2058cdf662 | ||
|
|
6bc6bc7f60 | ||
|
|
370df47042 | ||
|
|
9ae49cbf31 | ||
|
|
822d384746 | ||
|
|
ce31726af5 | ||
|
|
494a8ac3fe | ||
|
|
50738db2b8 | ||
|
|
6fa6939f52 | ||
|
|
ef0e53e8fc | ||
|
|
fd2c2c4ff7 | ||
|
|
d6c4911135 | ||
|
|
6991c06937 | ||
|
|
3f16a0c52c | ||
|
|
4a6426d3e7 | ||
|
|
235a75202d | ||
|
|
55cf0cf2f9 | ||
|
|
0565f8dd78 | ||
|
|
bdf90e67a7 | ||
|
|
7144db7f5a | ||
|
|
c66840bc8e | ||
|
|
26ad6d4c5d | ||
|
|
87b64cf3c0 | ||
|
|
0f1651686b | ||
|
|
528ea88dec | ||
|
|
44ef501283 | ||
|
|
eb27fa29b1 | ||
|
|
a89ab88097 |
No files matched your search
@@ -113,6 +113,8 @@ When you’ve specified the details that will be incorporated into the pg_dump c
|
||||
.. image:: images/backup_messages.png
|
||||
:alt: Backup success notification popup
|
||||
|
||||
Use the **Stop Process** button to stop the Backup process.
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_process_watcher.png
|
||||
|
||||
@@ -24,6 +24,8 @@ Click the *Backup* button to build and execute a command based on your selection
|
||||
.. image:: images/backup_globals_messages.png
|
||||
:alt: Backup globals success notification popup
|
||||
|
||||
Use the **Stop Process** button to stop the Backup process.
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_globals_process_watcher.png
|
||||
|
||||
@@ -78,6 +78,8 @@ Click the *Backup* button to build and execute a command based on your selection
|
||||
.. image:: images/backup_server_messages.png
|
||||
:alt: Backup server success notification popup
|
||||
|
||||
Use the **Stop Process** button to stop the Backup process.
|
||||
|
||||
If the backup is successful, a popup window will confirm success. Click *Click here for details* on the popup window to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the backup and provides additional information for troubleshooting.
|
||||
|
||||
.. image:: images/backup_server_process_watcher.png
|
||||
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 228 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 233 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 259 KiB |
@@ -53,6 +53,8 @@ After completing the *Import/Export data* dialog, click the *OK* button to perfo
|
||||
.. image:: images/import_export_complete.png
|
||||
:alt: Import Export data completion notification
|
||||
|
||||
Use the **Stop Process** button to stop the Import/Export process.
|
||||
|
||||
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
||||
|
||||
.. image:: images/import_export_pw.png
|
||||
|
||||
@@ -34,6 +34,8 @@ pgAdmin will inform you when the background process completes:
|
||||
.. image:: images/maintenance_complete.png
|
||||
:alt: Maintenance completion notification
|
||||
|
||||
Use the **Stop Process** button to stop the Maintenance process.
|
||||
|
||||
Use the *Click here for details* link on the notification to open the *Process Watcher* and review detailed information about the execution of the command that performed the import or export:
|
||||
|
||||
.. image:: images/maintenance_pw.png
|
||||
|
||||
@@ -127,9 +127,9 @@ Use the fields on the *Help* panel to specify the location of help files.
|
||||
|
||||
Please note: the default help paths include the *VERSION* placeholder; the $VERSION$ placeholder will be replaced by the current database version.
|
||||
|
||||
**The SQL Editor Node**
|
||||
**The Query Tool Node**
|
||||
|
||||
Expand the *SQL Editor* node to access panels that allow you to specify your preferences for the SQL Editor tool.
|
||||
Expand the *Query Tool* node to access panels that allow you to specify your preferences for the Query Editor tool.
|
||||
|
||||
.. image:: images/preferences_sql_auto_completion.png
|
||||
:alt: Preferences dialog sqleditor auto completion option
|
||||
@@ -150,13 +150,13 @@ Use the fields on the *CSV Output* panel to control the CSV output.
|
||||
.. image:: images/preferences_sql_display.png
|
||||
:alt: Preferences dialog sqleditor display options
|
||||
|
||||
Use the fields on the *Display* panel to specify your preferences for the SQL Editor display.
|
||||
Use the fields on the *Display* panel to specify your preferences for the Query Tool display.
|
||||
|
||||
* When the *Connection status* switch is set to *True*, each new instance of the SQL Editor will display connection and transaction status.
|
||||
* When the *Connection status* switch is set to *True*, each new instance of the Query Tool will display connection and transaction status.
|
||||
|
||||
* Use the *Connection status refresh rate* field to specify the number of seconds between connection/transaction status updates.
|
||||
|
||||
* When the *Open in new browser tab* switch is set to *True*, each new instance of the SQL Editor will open in a new browser tab.
|
||||
* When the *Open in new browser tab* switch is set to *True*, each new instance of the Query Tool will open in a new browser tab.
|
||||
|
||||
* Use the *Query info notifier timeout* field to control the behaviour of the notifier that is displayed when query execution completes. A value of *-1* will disable the notifier, and a value of 0 will display it until clicked. If a positive value above zero is specified, the notifier will be displayed for the specified number of seconds. The default is *5*.
|
||||
|
||||
@@ -207,7 +207,7 @@ Use the fields on the *Results grid* panel to specify your formatting preference
|
||||
* Use the *Result copy quote character* drop-down listbox to select the quote character for copied data.
|
||||
* Use the *Result copy quoting* drop-down listbox to select which type of fields require quoting; select *All*, *None*, or *Strings*.
|
||||
|
||||
Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the sql editor window navigation:
|
||||
Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the query tool window navigation:
|
||||
|
||||
.. image:: images/preferences_sql_keyboard_shortcuts.png
|
||||
:alt: Preferences dialog sql keyboard shortcuts section
|
||||
|
||||
@@ -9,6 +9,7 @@ for it.
|
||||
|
||||
.. toctree::
|
||||
|
||||
release_notes_3_5
|
||||
release_notes_3_4
|
||||
release_notes_3_3
|
||||
release_notes_3_2
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
***********
|
||||
Version 3.5
|
||||
***********
|
||||
|
||||
Release date: 2018-11-01
|
||||
|
||||
This release contains a number of features and fixes reported since the release of pgAdmin4 3.4
|
||||
|
||||
|
||||
Features
|
||||
********
|
||||
|
||||
| `Feature #1253 <https://redmine.postgresql.org/issues/1253>`_ - Save the treeview state periodically, and restore it automatically when reconnecting.
|
||||
| `Feature #3562 <https://redmine.postgresql.org/issues/3562>`_ - Migrate from Bootstrap 3 to Bootstrap 4.
|
||||
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Bug #3232 <https://redmine.postgresql.org/issues/3232>`_ - Ensure that Utilities(Backup/Restore/Maintenence/Import-Export) should not be started if binary path is wrong and also added 'Stop Process' button to cancel the process.
|
||||
| `Bug #3638 <https://redmine.postgresql.org/issues/3638>`_ - Fix syntax error when creating new pgAgent schedules with a start date/time and exception.
|
||||
| `Bug #3674 <https://redmine.postgresql.org/issues/3674>`_ - Cleanup session files periodically.
|
||||
| `Bug #3660 <https://redmine.postgresql.org/issues/3660>`_ - Rename the 'SQL Editor' section of the Preferences to 'Query Tool' as it applies to the whole tool, not just the editor.
|
||||
| `Bug #3676 <https://redmine.postgresql.org/issues/3676>`_ - Fix CREATE Script functionality for EDB-Wrapped functions.
|
||||
| `Bug #3700 <https://redmine.postgresql.org/issues/3700>`_ - Fix connection garbage collector.
|
||||
| `Bug #3703 <https://redmine.postgresql.org/issues/3703>`_ - Purge connections from the cache on logout.
|
||||
| `Bug #3722 <https://redmine.postgresql.org/issues/3722>`_ - Ensure that utility existence check should work for schema and other child objects while taking Backup/Restore.
|
||||
| `Bug #3730 <https://redmine.postgresql.org/issues/3730>`_ - Fixed fatal error while launching the pgAdmin4 3.5. Update the version of the Flask to 0.12.4 for release.
|
||||
@@ -84,6 +84,8 @@ When you’ve specified the details that will be incorporated into the pg_restor
|
||||
.. image:: images/restore_messages.png
|
||||
:alt: Restore dialog notifications
|
||||
|
||||
Use the **Stop Process** button to stop the Restore process.
|
||||
|
||||
Click *Click here for details* on the popup to launch the *Process Watcher*. The *Process Watcher* logs all the activity associated with the restore, and provides additional information for troubleshooting should the restore command encounter problems.
|
||||
|
||||
.. image:: images/restore_process_watcher.png
|
||||
|
||||
@@ -45,7 +45,8 @@ ENV PYTHONPATH=/pgadmin4
|
||||
# so that deps do not increase the size of resulting image by remaining in layers
|
||||
COPY ./pgadmin4/requirements.txt /pgadmin4
|
||||
RUN set -ex && \
|
||||
apk add --no-cache --virtual build-deps build-base postgresql-dev libffi-dev && \
|
||||
apk add --no-cache --virtual build-deps build-base postgresql-dev libffi-dev linux-headers && \
|
||||
pip install --upgrade pip && \
|
||||
pip install --no-cache-dir -r requirements.txt && \
|
||||
apk del --no-cache build-deps
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
blinker==1.4
|
||||
extras==1.0.0
|
||||
fixtures==3.0.0
|
||||
Flask==0.12.2
|
||||
Flask==0.12.4
|
||||
Flask-Gravatar==0.5.0
|
||||
Flask-Login==0.3.2
|
||||
Flask-Mail==0.9.1
|
||||
@@ -28,6 +28,7 @@ speaklater==1.3
|
||||
sqlparse==0.2.4
|
||||
WTForms==2.1
|
||||
Flask-Paranoid==0.2.0
|
||||
psutil==5.4.7
|
||||
|
||||
################################################################
|
||||
# Modules specifically requires for Python2.7 or greater version
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>pgAdmin 4</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.4.0</string>
|
||||
<string>3.5.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.4.0</string>
|
||||
<string>3.5.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.10</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION = 3.4.0.0
|
||||
VERSION = 3.5.0.0
|
||||
QMAKE_TARGET_COMPANY = "The pgAdmin Development Team"
|
||||
QMAKE_TARGET_PRODUCT = "pgAdmin 4"
|
||||
QMAKE_TARGET_DESCRIPTION = "pgAdmin 4 Desktop Runtime"
|
||||
|
||||
@@ -50,7 +50,7 @@ APP_ICON = 'pg-icon'
|
||||
|
||||
# Application version number components
|
||||
APP_RELEASE = 3
|
||||
APP_REVISION = 4
|
||||
APP_REVISION = 5
|
||||
|
||||
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
|
||||
# for GA releases.
|
||||
@@ -59,7 +59,7 @@ APP_SUFFIX = ''
|
||||
# Numeric application version for upgrade checks. Should be in the format:
|
||||
# [X]XYYZZ, where X is the release version, Y is the revision, with a leading
|
||||
# zero if needed, and Z represents the suffix, with a leading zero if needed
|
||||
APP_VERSION_INT = 30400
|
||||
APP_VERSION_INT = 30500
|
||||
|
||||
# DO NOT CHANGE!
|
||||
# The application version string, constructed from the components
|
||||
@@ -387,6 +387,17 @@ SESSION_SKIP_PATHS = [
|
||||
'/misc/ping'
|
||||
]
|
||||
|
||||
##########################################################################
|
||||
# Session expiration support
|
||||
##########################################################################
|
||||
# SESSION_EXPIRATION_TIME is the interval in Days. Session will be
|
||||
# expire after the specified number of *days*.
|
||||
SESSION_EXPIRATION_TIME = 1
|
||||
|
||||
# CHECK_SESSION_FILES_INTERVAL is interval in Hours. Application will check
|
||||
# the session files for cleanup after specified number of *hours*.
|
||||
CHECK_SESSION_FILES_INTERVAL = 24
|
||||
|
||||
##########################################################################
|
||||
# SSH Tunneling supports only for Python 2.7 and 3.4+
|
||||
##########################################################################
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
"""Added state of the utility process
|
||||
|
||||
Revision ID: b5b87fdfcb30
|
||||
Revises: ece2e76bf60e
|
||||
Create Date: 2018-10-24 12:37:59.487969
|
||||
|
||||
"""
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b5b87fdfcb30'
|
||||
down_revision = 'ece2e76bf60e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE process ADD COLUMN process_state INTEGER DEFAULT 0'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -0,0 +1,34 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
""" Added utility pid to stop process
|
||||
|
||||
Revision ID: ece2e76bf60e
|
||||
Revises: ca00ec32581b
|
||||
Create Date: 2018-10-18 14:45:13.483068
|
||||
|
||||
"""
|
||||
|
||||
from pgadmin.model import db
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ece2e76bf60e'
|
||||
down_revision = 'ca00ec32581b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
db.engine.execute(
|
||||
'ALTER TABLE process ADD COLUMN utility_pid INTEGER'
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
pass
|
||||
@@ -33,6 +33,7 @@
|
||||
"karma-webpack": "~2.0.13",
|
||||
"node-sass": "^4.5.3",
|
||||
"optimize-css-assets-webpack-plugin": "^2.0.0",
|
||||
"popper.js": "^1.14.4",
|
||||
"raw-loader": "^0.5.1",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sass-resources-loader": "^1.3.3",
|
||||
@@ -56,7 +57,7 @@
|
||||
"backgrid-select-all": "^0.3.5",
|
||||
"backgrid-sizeable-columns": "^0.1.1",
|
||||
"bignumber.js": "^6.0.0",
|
||||
"bootstrap": "^3.3.7",
|
||||
"bootstrap": "^4.1.3",
|
||||
"bootstrap-datepicker": "^1.7.0",
|
||||
"bootstrap-switch": "3.3.4",
|
||||
"bowser": "1.6.1",
|
||||
@@ -65,7 +66,6 @@
|
||||
"css-loader": "0.14.0",
|
||||
"cssnano": "^3.10.0",
|
||||
"dropzone": "^5.1.1",
|
||||
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
|
||||
"exports-loader": "~0.6.4",
|
||||
"flotr2": "git+https://github.com/EnterpriseDB/Flotr2.git",
|
||||
"font-awesome": "^4.7.0",
|
||||
@@ -78,6 +78,7 @@
|
||||
"jquery-ui": "^1.12.1",
|
||||
"leaflet": "^1.3.3",
|
||||
"moment": "^2.20.1",
|
||||
"moment-timezone": "^0.5.21",
|
||||
"mousetrap": "^1.6.1",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.2.0",
|
||||
@@ -91,6 +92,8 @@
|
||||
"spectrum-colorpicker": "^1.8.0",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"tablesorter": "^2.30.6",
|
||||
"tempusdominus-bootstrap-4": "^5.1.2",
|
||||
"tempusdominus-core": "^5.0.3",
|
||||
"underscore": "^1.8.3",
|
||||
"underscore.string": "^3.3.4",
|
||||
"watchify": "~3.9.0",
|
||||
|
||||
@@ -32,6 +32,7 @@ from pgadmin.utils import PgAdminModule, driver
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
from pgadmin.utils.session import create_session_interface, pga_unauthorised
|
||||
from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader
|
||||
from datetime import timedelta
|
||||
|
||||
# If script is running under python3, it will not have the xrange function
|
||||
# defined
|
||||
@@ -354,6 +355,10 @@ def create_app(app_name=None):
|
||||
# register custom unauthorised handler.
|
||||
app.login_manager.unauthorized_handler(pga_unauthorised)
|
||||
|
||||
# Set the permanent session lifetime to the specified value in config file.
|
||||
app.permanent_session_lifetime = timedelta(
|
||||
days=config.SESSION_EXPIRATION_TIME)
|
||||
|
||||
app.session_interface = create_session_interface(
|
||||
app, config.SESSION_SKIP_PATHS
|
||||
)
|
||||
@@ -545,6 +550,13 @@ def create_app(app_name=None):
|
||||
def force_session_write(app, user):
|
||||
session.force_write = True
|
||||
|
||||
@user_logged_out.connect_via(app)
|
||||
def clear_current_user_connections(app, user):
|
||||
from config import PG_DEFAULT_DRIVER
|
||||
from pgadmin.utils.driver import get_driver
|
||||
_driver = get_driver(PG_DEFAULT_DRIVER)
|
||||
_driver.gc_own()
|
||||
|
||||
##########################################################################
|
||||
# Load plugin modules
|
||||
##########################################################################
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="col-sm-9">{{ info.current_user }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-right"><img
|
||||
<div class="col-12 text-right"><img
|
||||
src="{{ url_for('static', filename='img/logo-right-128.png') }}"
|
||||
alt="{{ config.APP_NAME }} {{ _('logo') }}"
|
||||
></div>
|
||||
|
||||
@@ -28,6 +28,16 @@ def register_browser_preferences(self):
|
||||
True, category_label=gettext('Display')
|
||||
)
|
||||
|
||||
self.preference.register(
|
||||
'display', 'browser_tree_state_save_interval',
|
||||
gettext("Browser tree state saving interval"), 'integer',
|
||||
30, category_label=gettext('Display'),
|
||||
help_str=gettext(
|
||||
'Browser tree state saving interval in seconds.'
|
||||
'Use -1 to disable the tree saving mechanism.'
|
||||
)
|
||||
)
|
||||
|
||||
self.table_row_count_threshold = self.preference.register(
|
||||
'properties', 'table_row_count_threshold',
|
||||
gettext("Count rows if estimated less than"), 'integer', 2000,
|
||||
|
||||
@@ -585,7 +585,8 @@ class ServerNode(PGChildNodeView):
|
||||
server_icon_and_background(connected, manager, server),
|
||||
True,
|
||||
self.node_type,
|
||||
connected=False,
|
||||
connected=connected,
|
||||
user=manager.user_info if connected else None,
|
||||
server_type='pg' # default server type
|
||||
)
|
||||
)
|
||||
|
||||
@@ -197,12 +197,12 @@ define('pgadmin.node.fts_configuration', [
|
||||
'<div class="subnode-header-form">',
|
||||
' <div class="container-fluid">',
|
||||
' <div class="row">',
|
||||
' <div class="col-xs-3">',
|
||||
' <div class="col-3">',
|
||||
' <label class="control-label"><%-token_label%></label>',
|
||||
' </div>',
|
||||
' <div class="col-xs-6" header="token"></div>',
|
||||
' <div class="col-xs-2">',
|
||||
' <button class="btn-sm btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
|
||||
' <div class="col-6" header="token"></div>',
|
||||
' <div class="col-2">',
|
||||
' <button class="btn btn-sm-sq btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
@@ -257,7 +257,7 @@ define('pgadmin.node.fts_configuration', [
|
||||
var self = this,
|
||||
titleTmpl = _.template('<div class=\'subnode-header\'></div>'),
|
||||
$gridBody = $('<div></div>', {
|
||||
class:'pgadmin-control-group backgrid form-group col-xs-12 object subnode',
|
||||
class:'pgadmin-control-group backgrid form-group col-12 object subnode',
|
||||
}).append(
|
||||
titleTmpl({label: data.label})
|
||||
);
|
||||
|
||||
@@ -29,9 +29,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif %};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif %};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -36,9 +36,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -33,9 +33,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -33,9 +33,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -36,9 +36,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -29,9 +29,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif %};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif %};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -33,9 +33,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -31,9 +31,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -34,9 +34,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -29,9 +29,7 @@ CREATE{% if query_type is defined %}{{' OR REPLACE'}}{% endif %} FUNCTION {{ con
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif %};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif %};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args_without}})
|
||||
|
||||
@@ -23,9 +23,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.proa
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -23,9 +23,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}()
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -23,9 +23,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({% if data.proa
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -20,9 +20,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}()
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif %};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif %};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -23,9 +23,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}()
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -23,9 +23,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}()
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif -%};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif -%};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -20,9 +20,7 @@ CREATE FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}()
|
||||
AS {% if data.lanname == 'c' %}
|
||||
{{ data.probin|qtLiteral }}, {{ data.prosrc_c|qtLiteral }}
|
||||
{% else %}
|
||||
$BODY$
|
||||
{{ data.prosrc }}
|
||||
$BODY${% endif %};
|
||||
$BODY${{ data.prosrc }}$BODY${% endif %};
|
||||
{% if data.funcowner %}
|
||||
|
||||
ALTER FUNCTION {{ conn|qtIdent(data.pronamespace, data.name) }}({{data.func_args}})
|
||||
|
||||
@@ -64,7 +64,7 @@ define('pgadmin.node.schema', [
|
||||
'<div class="subnode-header">',
|
||||
' <label class="control-label col-sm-4"><%-label%></label>',
|
||||
'</div>'].join('\n')),
|
||||
gridBody = $('<div class="pgadmin-control-group backgrid form-group col-xs-12 object subnode"></div>').append(
|
||||
gridBody = $('<div class="pgadmin-control-group backgrid form-group col-12 object subnode"></div>').append(
|
||||
gridHeader(attributes)
|
||||
);
|
||||
|
||||
|
||||
@@ -391,12 +391,12 @@ define('pgadmin.node.exclusion_constraint', [
|
||||
'<div class="subnode-header-form">',
|
||||
' <div class="container-fluid">',
|
||||
' <div class="row">',
|
||||
' <div class="col-xs-4">',
|
||||
' <div class="col-4">',
|
||||
' <label class="control-label"><%-column_label%></label>',
|
||||
' </div>',
|
||||
' <div class="col-xs-4" header="column"></div>',
|
||||
' <div class="col-xs-4">',
|
||||
' <button class="btn-sm btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
|
||||
' <div class="col-4" header="column"></div>',
|
||||
' <div class="col-4">',
|
||||
' <button class="btn btn-sm-sq btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
@@ -443,7 +443,7 @@ define('pgadmin.node.exclusion_constraint', [
|
||||
var self = this,
|
||||
titleTmpl = _.template('<div class=\'subnode-header\'></div>'),
|
||||
$gridBody =
|
||||
$('<div class=\'pgadmin-control-group backgrid form-group col-xs-12 object subnode\'></div>').append(
|
||||
$('<div class=\'pgadmin-control-group backgrid form-group col-12 object subnode\'></div>').append(
|
||||
// Append titleTmpl only if create/edit mode
|
||||
data.mode !== 'properties' ? titleTmpl({label: data.label}) : ''
|
||||
);
|
||||
|
||||
@@ -309,7 +309,7 @@ define('pgadmin.node.foreign_key', [
|
||||
' </div>',
|
||||
' <div class="col-md-6" header="local_column"></div>',
|
||||
' <div class="col-md-2">',
|
||||
' <button class="btn-sm btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
|
||||
' <button class="btn btn-sm-sq btn-default add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> ></button>',
|
||||
' </div>',
|
||||
' </div>',
|
||||
' <div class="row">',
|
||||
@@ -374,7 +374,7 @@ define('pgadmin.node.foreign_key', [
|
||||
'<label class=\'control-label\'><%-label%></label>',
|
||||
'</div>'].join('\n')),
|
||||
$gridBody =
|
||||
$('<div class=\'pgadmin-control-group backgrid form-group col-xs-12 object subnode\'></div>').append(
|
||||
$('<div class=\'pgadmin-control-group backgrid form-group col-12 object subnode\'></div>').append(
|
||||
// Append titleTmpl only if create/edit mode
|
||||
data.mode !== 'properties' ? titleTmpl({label: data.label}) : ''
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ define('pgadmin.node.trigger', [
|
||||
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
||||
'<% } %>',
|
||||
].join('\n')),
|
||||
className: 'pgadmin-control-group form-group col-xs-6',
|
||||
className: 'pgadmin-control-group form-group col-6',
|
||||
});
|
||||
|
||||
if (!pgBrowser.Nodes['coll-trigger']) {
|
||||
|
||||
@@ -148,9 +148,11 @@ define('pgadmin.node.pga_schedule', [
|
||||
},{
|
||||
id: 'jextime', type: 'text', placeholder: gettext('<any>'),
|
||||
label: gettext('Time'), editable: true, cell: AnyDatetimeCell,
|
||||
options: {format: 'HH:mm'}, displayFormat: 'HH:mm',
|
||||
modelFormat: 'HH:mm:ss', displayInUTC: false, allowEmpty: true,
|
||||
cellHeaderClasses:'width_percent_50', modalInUTC: false,
|
||||
options: {format: 'HH:mm', buttons: {
|
||||
showToday: false,
|
||||
}}, displayFormat: 'HH:mm',
|
||||
modelFormat: 'HH:mm:ss', displayInUTC: true, allowEmpty: true,
|
||||
cellHeaderClasses:'width_percent_50', modalInUTC: true,
|
||||
}],
|
||||
validate: function() {
|
||||
var self = this, exceptions = this.collection,
|
||||
@@ -343,7 +345,7 @@ define('pgadmin.node.pga_schedule', [
|
||||
);
|
||||
|
||||
this.$el.prepend(
|
||||
'<div class="set-group pg-el-xs-12 ' +
|
||||
'<div class="set-group c ' +
|
||||
Backform.helpMessageClassName + '">' +
|
||||
gettext('Schedules are specified using a <b>cron-style</b> format.<br/><ul><li>For each selected time or date element, the schedule will execute.<br/>e.g. To execute at 5 minutes past every hour, simply select ‘05’ in the Minutes list box.<br/></li><li>Values from more than one field may be specified in order to further control the schedule.<br/>e.g. To execute at 12:05 and 14:05 every Monday and Thursday, you would click minute 05, hours 12 and 14, and weekdays Monday and Thursday.</li><li>For additional flexibility, the Month Days check list includes an extra Last Day option. This matches the last day of the month, whether it happens to be the 28th, 29th, 30th or 31st.</li></ul>') +
|
||||
'</div>'
|
||||
@@ -354,7 +356,7 @@ define('pgadmin.node.pga_schedule', [
|
||||
}),
|
||||
schema:[{
|
||||
id: 'jscweekdays', label: gettext('Week Days'), cell: 'select2',
|
||||
group: gettext('Days'), control: 'select2',
|
||||
group: gettext('Days'), control: 'select2', type: 'array',
|
||||
select2: {
|
||||
first_empty: false,
|
||||
multiple: true,
|
||||
@@ -370,7 +372,7 @@ define('pgadmin.node.pga_schedule', [
|
||||
options: BooleanArrayOptions,
|
||||
},{
|
||||
id: 'jscmonthdays', label: gettext('Month Days'), cell: 'select2',
|
||||
group: gettext('Days'), control: 'select2',
|
||||
group: gettext('Days'), control: 'select2', type: 'array',
|
||||
select2: {
|
||||
first_empty: false,
|
||||
multiple: true,
|
||||
@@ -385,7 +387,7 @@ define('pgadmin.node.pga_schedule', [
|
||||
selector: monthdays, options: BooleanArrayOptions,
|
||||
},{
|
||||
id: 'jscmonths', label: gettext('Months'), cell: 'select2',
|
||||
group: gettext('Days'), control: 'select2',
|
||||
group: gettext('Days'), control: 'select2', type: 'array',
|
||||
select2: {
|
||||
first_empty: false,
|
||||
multiple: true,
|
||||
@@ -404,7 +406,7 @@ define('pgadmin.node.pga_schedule', [
|
||||
group: gettext('Repeat'), mode: ['create', 'edit'],
|
||||
schema:[{
|
||||
id: 'jschours', label: gettext('Hours'), cell: 'select2',
|
||||
group: gettext('Times'), control: 'select2',
|
||||
group: gettext('Times'), control: 'select2', type: 'array',
|
||||
select2: {
|
||||
first_empty: false,
|
||||
multiple: true,
|
||||
@@ -419,7 +421,7 @@ define('pgadmin.node.pga_schedule', [
|
||||
selector: hours, options: BooleanArrayOptions,
|
||||
},{
|
||||
id: 'jscminutes', label: gettext('Minutes'), cell: 'select2',
|
||||
group: gettext('Times'), control: 'select2',
|
||||
group: gettext('Times'), control: 'select2', type: 'array',
|
||||
select2: {
|
||||
first_empty: false,
|
||||
multiple: true,
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
INSERT INTO pgagent.pga_exception (
|
||||
jexscid, jexdate, jextime
|
||||
) VALUES (
|
||||
{% if jscid %}{{ jscid|qtLiteral }}{% else %}scid{% endif %}, {% if data.jexdate %}to_date({{ data.jexdate|qtLiteral }}, 'MM/DD/YYYY')::date{% else %}NULL::date{% endif %}, {% if data.jextime %}{{ data.jextime|qtLiteral }}::time without time zone{% else %}NULL::time without time zone{% endif %}
|
||||
{% if jscid %}{{ jscid|qtLiteral }}{% else %}scid{% endif %}, {% if data.jexdate %}to_date({{ data.jexdate|qtLiteral }}, 'YYYY-MM-DD')::date{% else %}NULL::date{% endif %}, {% if data.jextime %}{{ data.jextime|qtLiteral }}::time without time zone{% else %}NULL::time without time zone{% endif %}
|
||||
|
||||
);
|
||||
{%- endmacro %}
|
||||
{% macro UPDATE(jscid, data) -%}
|
||||
-- Updating an existing schedule exception (id: {{ data.jexid|qtLiteral }}, schedule: {{ jscid|qtLiteral }})
|
||||
UPDATE pgagent.pga_exception SET
|
||||
{% if 'jexdate' in data %}jexdate={% if data.jexdate %}to_date({{ data.jexdate|qtLiteral }}, 'MM/DD/YYYY')::date{% else %}NULL::date{% endif %}{% endif %}{% if 'jextime' in data%}{% if 'jexdate' in data %}, {% endif %}jextime={% if data.jextime %}{{ data.jextime|qtLiteral }}::time without time zone{% else %}NULL::time without time zone{% endif %}{% endif %}
|
||||
{% if 'jexdate' in data %}jexdate={% if data.jexdate %}to_date({{ data.jexdate|qtLiteral }}, 'YYYY-MM-DD')::date{% else %}NULL::date{% endif %}{% endif %}{% if 'jextime' in data%}{% if 'jexdate' in data %}, {% endif %}jextime={% if data.jextime %}{{ data.jextime|qtLiteral }}::time without time zone{% else %}NULL::time without time zone{% endif %}{% endif %}
|
||||
|
||||
WHERE jexid={{ data.jexid|qtLiteral }}::integer AND jscid={{ jscid|qtLiteral }}::integer;
|
||||
WHERE jexid={{ data.jexid|qtLiteral }}::integer AND jexscid={{ jscid|qtLiteral }}::integer;
|
||||
{%- endmacro %}
|
||||
{% macro DELETE(jscid, data) -%}
|
||||
-- Deleting a schedule exception (id: {{ data.jexid|qtLiteral }}, schedule: {{ jscid|qtLiteral }})
|
||||
DELETE FROM pgagent.pga_exception WHERE jexid={{ data.jexid|qtLiteral }}::integer AND jscid={{ jscid|qtLiteral }}::integer;
|
||||
DELETE FROM pgagent.pga_exception WHERE jexid={{ data.jexid|qtLiteral }}::integer AND jexscid={{ jscid|qtLiteral }}::integer;
|
||||
{%- endmacro %}
|
||||
@@ -23,7 +23,7 @@ INSERT INTO pgagent.pga_schedule(
|
||||
{{ data.jscmonthdays|qtLiteral }}::boolean[],
|
||||
-- Months
|
||||
{{ data.jscmonths|qtLiteral }}::boolean[]
|
||||
) RETURNING jscid {% if not jid %}INTO scid;{% endif %}{% if 'jscexceptions' in data %}
|
||||
) RETURNING jscid INTO scid;{% if 'jscexceptions' in data %}
|
||||
{% for exc in data.jscexceptions %}
|
||||
|
||||
{{ EXCEPTIONS.INSERT(None, exc) }}{% endfor %}{% endif %}
|
||||
|
||||
@@ -25,6 +25,6 @@
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
div[role=tabpanel] > .pgadmin-control-group.form-group.pg-el-xs-12.jscexceptions {
|
||||
div[role=tabpanel] > .pgadmin-control-group.form-group.c.jscexceptions {
|
||||
min-height: 400px;
|
||||
}
|
||||
@@ -1,2 +1,8 @@
|
||||
{% import 'macros/pga_schedule.macros' as SCHEDULE %}
|
||||
DO $$
|
||||
DECLARE
|
||||
scid integer;
|
||||
BEGIN
|
||||
{{ SCHEDULE.INSERT(jid, data) }}
|
||||
END
|
||||
$$;
|
||||
@@ -0,0 +1,80 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import simplejson as json
|
||||
import uuid
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as pgagent_utils
|
||||
|
||||
|
||||
class PgAgentAddScheduleTestCase(BaseTestGenerator):
|
||||
"""This class will test the add schedule in the pgAgent job API"""
|
||||
scenarios = [
|
||||
('Create schedule with exception in pgAgent job', dict(
|
||||
url='/browser/pga_schedule/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
name = "test_job_get%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = pgagent_utils.create_pgagent_job(self, name)
|
||||
|
||||
def runTest(self):
|
||||
# Check and Delete entry for pga_exception table for the above
|
||||
# date and time as no primary key is defined for pga_exception table
|
||||
# and there is a unique constraint for date and time. So when we run
|
||||
# the test cases multiple time then it will fail with unique
|
||||
# constraint error.
|
||||
jexdate = '2050-01-01'
|
||||
jextime = '12:00:00'
|
||||
pgagent_utils.delete_pgagent_exception(self, jexdate, jextime)
|
||||
|
||||
self.pgagent_schedule_name = "test_sch_add%s" % str(uuid.uuid4())[1:8]
|
||||
data = {
|
||||
'jscjobid': self.job_id,
|
||||
'jscenabled': True,
|
||||
'jscdesc': '',
|
||||
'jscname': self.pgagent_schedule_name,
|
||||
'jscexceptions': [{'jexdate': jexdate,
|
||||
'jextime': jextime}],
|
||||
'jscstart': '2050-01-01 12:14:21 +05:30',
|
||||
'jscend': '2050-03-01 12:14:21 +05:30',
|
||||
'jscminutes': [False] * 60,
|
||||
'jscweekdays': [True] * 7,
|
||||
'jscmonthdays': [True] * 32,
|
||||
'jschours': [False] * 24,
|
||||
'jscmonths': [True] * 12
|
||||
}
|
||||
|
||||
response = self.tester.post(
|
||||
'{0}{1}/{2}/{3}/'.format(
|
||||
self.url, str(utils.SERVER_GROUP), str(self.server_id),
|
||||
str(self.job_id)
|
||||
),
|
||||
data=json.dumps(data),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
response_data = json.loads(response.data)
|
||||
self.schedule_id = response_data['node']['_id']
|
||||
is_present = pgagent_utils.verify_pgagent_schedule(self)
|
||||
self.assertTrue(
|
||||
is_present, "pgAgent schedule was not created successfully"
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up code"""
|
||||
pgagent_utils.delete_pgagent_job(self)
|
||||
@@ -0,0 +1,69 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import simplejson as json
|
||||
import uuid
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as pgagent_utils
|
||||
|
||||
|
||||
class PgAgentAddStepTestCase(BaseTestGenerator):
|
||||
"""This class will test the add step in the pgAgent job API"""
|
||||
scenarios = [
|
||||
('Create step for pgAgent job', dict(
|
||||
url='/browser/pga_jobstep/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
name = "test_job_get%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = pgagent_utils.create_pgagent_job(self, name)
|
||||
|
||||
def runTest(self):
|
||||
self.pgagent_step_name = "test_step_add%s" % str(uuid.uuid4())[1:8]
|
||||
data = {
|
||||
'jstjobid': self.job_id,
|
||||
'jstname': self.pgagent_step_name,
|
||||
'jstdesc': '',
|
||||
'jstenabled': True,
|
||||
'jstkind': True,
|
||||
'jstconntype': True,
|
||||
'jstcode': 'SELECT 1;',
|
||||
'jstconnstr': None,
|
||||
'jstdbname': 'postgres',
|
||||
'jstonerror': 'f',
|
||||
'jstnextrun': '',
|
||||
}
|
||||
|
||||
response = self.tester.post(
|
||||
'{0}{1}/{2}/{3}/'.format(
|
||||
self.url, str(utils.SERVER_GROUP), str(self.server_id),
|
||||
str(self.job_id)
|
||||
),
|
||||
data=json.dumps(data),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
response_data = json.loads(response.data)
|
||||
self.step_id = response_data['node']['_id']
|
||||
is_present = pgagent_utils.verify_pgagent_step(self)
|
||||
self.assertTrue(
|
||||
is_present, "pgAgent step was not created successfully"
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up code"""
|
||||
pgagent_utils.delete_pgagent_job(self)
|
||||
@@ -0,0 +1,52 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as pgagent_utils
|
||||
|
||||
|
||||
class PgAgentDeleteScheduleTestCase(BaseTestGenerator):
|
||||
"""This class will test the delete pgAgent job schedule API"""
|
||||
scenarios = [
|
||||
('Delete pgAgent Schedule', dict(url='/browser/pga_schedule/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
name = "test_job_delete%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = pgagent_utils.create_pgagent_job(self, name)
|
||||
sch_name = "test_schedule_delete%s" % str(uuid.uuid4())[1:8]
|
||||
self.schedule_id = pgagent_utils.create_pgagent_schedule(
|
||||
self, sch_name, self.job_id)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will deletes pgAgent job schedule"""
|
||||
response = self.tester.delete(
|
||||
'{0}{1}/{2}/{3}/{4}'.format(
|
||||
self.url, str(utils.SERVER_GROUP), str(self.server_id),
|
||||
str(self.job_id), str(self.schedule_id)
|
||||
),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
is_present = pgagent_utils.verify_pgagent_schedule(self)
|
||||
self.assertFalse(
|
||||
is_present, "pgAgent schedule was not deleted successfully"
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up code"""
|
||||
pgagent_utils.delete_pgagent_job(self)
|
||||
@@ -0,0 +1,52 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as pgagent_utils
|
||||
|
||||
|
||||
class PgAgentDeleteStepTestCase(BaseTestGenerator):
|
||||
"""This class will test the delete pgAgent job step API"""
|
||||
scenarios = [
|
||||
('Delete pgAgent Step', dict(url='/browser/pga_jobstep/obj/'))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
name = "test_job_delete%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = pgagent_utils.create_pgagent_job(self, name)
|
||||
step_name = "test_step_delete%s" % str(uuid.uuid4())[1:8]
|
||||
self.step_id = pgagent_utils.create_pgagent_step(
|
||||
self, step_name, self.job_id)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will deletes pgAgent job step"""
|
||||
response = self.tester.delete(
|
||||
'{0}{1}/{2}/{3}/{4}'.format(
|
||||
self.url, str(utils.SERVER_GROUP), str(self.server_id),
|
||||
str(self.job_id), str(self.step_id)
|
||||
),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
is_present = pgagent_utils.verify_pgagent_step(self)
|
||||
self.assertFalse(
|
||||
is_present, "pgAgent step was not deleted successfully"
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up code"""
|
||||
pgagent_utils.delete_pgagent_job(self)
|
||||
@@ -0,0 +1,131 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import simplejson as json
|
||||
import uuid
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as pgagent_utils
|
||||
|
||||
|
||||
class PgAgentPutScheduleTestCase(BaseTestGenerator):
|
||||
"""This class will test the update pgAgent schedule API"""
|
||||
scenarios = [
|
||||
('Update schedule with start and end time', dict(
|
||||
url='/browser/pga_schedule/obj/',
|
||||
data={
|
||||
'jscdesc': 'Test Schedule',
|
||||
'jscstart': '2050-01-01 12:00:00 +05:30',
|
||||
'jscend': '2050-01-20 12:00:00 +05:30',
|
||||
})),
|
||||
('Update schedule with repeat', dict(
|
||||
url='/browser/pga_schedule/obj/',
|
||||
data={
|
||||
'jscmonthdays': '[true,false,true,false,true,false,false,'
|
||||
'false,false,false,false,false,false,false,'
|
||||
'false,false,false,false,false,false,false,'
|
||||
'false,false,false,false,false,false,false,'
|
||||
'false,false,false,false]',
|
||||
'jscweekdays': '[true,false,false,true,false,false,false]',
|
||||
'jscmonths': '[true,false,false,true,false,false,false,false,'
|
||||
'false,false,false,false]',
|
||||
'jschours': '[false,false,false,false,true,false,false,false,'
|
||||
'false,false,false,false,false,false,false,false,'
|
||||
'false,false,false,false,false,false,false,false]'
|
||||
})),
|
||||
('Update schedule add exception', dict(
|
||||
url='/browser/pga_schedule/obj/',
|
||||
data={
|
||||
'jscexceptions': {
|
||||
'added': [{'jexdate': '2050-01-01',
|
||||
'jextime': '12:00:00'}]
|
||||
}},
|
||||
delete_existing_exception=True)),
|
||||
('Update schedule change exception date and time', dict(
|
||||
url='/browser/pga_schedule/obj/',
|
||||
data={
|
||||
'jscexceptions': {
|
||||
'changed': [{'jexdate': '2050-01-31',
|
||||
'jextime': '20:00:00'}]
|
||||
}},
|
||||
create_exception=True)),
|
||||
('Update schedule delete exception', dict(
|
||||
url='/browser/pga_schedule/obj/',
|
||||
data={
|
||||
'jscexceptions': {
|
||||
'deleted': [{'jexdate': '2050-01-01',
|
||||
'jextime': '12:00:00'}]
|
||||
}},
|
||||
create_exception=True,
|
||||
is_delete=True)),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
name = "test_job_update%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = pgagent_utils.create_pgagent_job(self, name)
|
||||
sch_name = "test_schedule_update%s" % str(uuid.uuid4())[1:8]
|
||||
self.schedule_id = pgagent_utils.create_pgagent_schedule(
|
||||
self, sch_name, self.job_id)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will update pgAgent schedule"""
|
||||
|
||||
# Check and Delete entry for pga_exception table for the specified
|
||||
# date and time as no primary key is defined for pga_exception table
|
||||
# and there is a unique constraint for date and time. So when we run
|
||||
# the test cases multiple time then it will fail with unique
|
||||
# constraint error.
|
||||
if hasattr(self, 'delete_existing_exception'):
|
||||
pgagent_utils.delete_pgagent_exception(
|
||||
self, self.data['jscexceptions']['added'][0]['jexdate'],
|
||||
self.data['jscexceptions']['added'][0]['jextime'])
|
||||
|
||||
# Create exception for update and delete
|
||||
if hasattr(self, 'create_exception'):
|
||||
date = None
|
||||
time = None
|
||||
if hasattr(self, 'is_delete'):
|
||||
date = self.data['jscexceptions']['deleted'][0]['jexdate']
|
||||
time = self.data['jscexceptions']['deleted'][0]['jextime']
|
||||
else:
|
||||
date = self.data['jscexceptions']['changed'][0]['jexdate']
|
||||
time = self.data['jscexceptions']['changed'][0]['jextime']
|
||||
|
||||
self.excp_id = pgagent_utils.create_pgagent_exception(
|
||||
self, self.schedule_id, date, time)
|
||||
|
||||
# Add created exception id in data
|
||||
if hasattr(self, 'is_delete'):
|
||||
self.data['jscexceptions']['deleted'][0]['jexid'] = \
|
||||
self.excp_id
|
||||
else:
|
||||
self.data['jscexceptions']['changed'][0]['jexid'] = \
|
||||
self.excp_id
|
||||
|
||||
self.data['jscid'] = str(self.schedule_id)
|
||||
response = self.tester.put(
|
||||
'{0}{1}/{2}/{3}/{4}'.format(
|
||||
self.url, str(utils.SERVER_GROUP), str(self.server_id),
|
||||
str(self.job_id), str(self.schedule_id)
|
||||
),
|
||||
data=json.dumps(self.data),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up code"""
|
||||
pgagent_utils.delete_pgagent_schedule(self)
|
||||
pgagent_utils.delete_pgagent_job(self)
|
||||
@@ -0,0 +1,67 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2018, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import simplejson as json
|
||||
import uuid
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from regression.python_test_utils import test_utils as utils
|
||||
from . import utils as pgagent_utils
|
||||
|
||||
|
||||
class PgAgentPutStepTestCase(BaseTestGenerator):
|
||||
"""This class will test the update pgAgent steps API"""
|
||||
scenarios = [
|
||||
('Update step with kind, description, code and error', dict(
|
||||
url='/browser/pga_jobstep/obj/',
|
||||
data={
|
||||
'jstdesc': 'Test Steps',
|
||||
'jstkind': False,
|
||||
'jstcode': 'SELECT 12345',
|
||||
'jstonerror': 'i'
|
||||
})),
|
||||
('Update step with connection type and string', dict(
|
||||
url='/browser/pga_jobstep/obj/',
|
||||
data={
|
||||
'jstconntype': False,
|
||||
'jstconnstr':
|
||||
'host=localhost port=5432 dbname=mydb connect_timeout=10'
|
||||
}))
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
flag, msg = pgagent_utils.is_valid_server_to_run_pgagent(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
flag, msg = pgagent_utils.is_pgagent_installed_on_server(self)
|
||||
if not flag:
|
||||
self.skipTest(msg)
|
||||
name = "test_job_update%s" % str(uuid.uuid4())[1:8]
|
||||
self.job_id = pgagent_utils.create_pgagent_job(self, name)
|
||||
step_name = "test_step_update%s" % str(uuid.uuid4())[1:8]
|
||||
self.step_id = pgagent_utils.create_pgagent_step(
|
||||
self, step_name, self.job_id)
|
||||
|
||||
def runTest(self):
|
||||
"""This function will update pgAgent steps"""
|
||||
|
||||
self.data['jstid'] = str(self.step_id)
|
||||
response = self.tester.put(
|
||||
'{0}{1}/{2}/{3}/{4}'.format(
|
||||
self.url, str(utils.SERVER_GROUP), str(self.server_id),
|
||||
str(self.job_id), str(self.step_id)
|
||||
),
|
||||
data=json.dumps(self.data),
|
||||
content_type='html/json'
|
||||
)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up code"""
|
||||
pgagent_utils.delete_pgagent_step(self)
|
||||
pgagent_utils.delete_pgagent_job(self)
|
||||
@@ -151,7 +151,7 @@ def delete_pgagent_job(self):
|
||||
|
||||
def verify_pgagent_job(self):
|
||||
"""
|
||||
This function deletes the pgAgent job.
|
||||
This function verifies the pgAgent job.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
@@ -173,3 +173,236 @@ def verify_pgagent_job(self):
|
||||
return count is not None and int(count) != 0
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def create_pgagent_schedule(self, sch_name, jobid):
|
||||
"""
|
||||
This function create the pgAgent schedule.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
query = """
|
||||
INSERT INTO pgagent.pga_schedule(
|
||||
jscname, jscjobid, jscenabled, jscdesc, jscstart, jscend
|
||||
) VALUES (
|
||||
'{0}'::text, {1}::int, true, '',
|
||||
'2050-01-01 12:14:21 +05:30'::timestamp with time zone,
|
||||
'2050-01-30 12:14:21 +05:30'::timestamp with time zone
|
||||
) RETURNING jscid;
|
||||
""".format(sch_name, jobid)
|
||||
pg_cursor.execute(query)
|
||||
sch_id = pg_cursor.fetchone()
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
return sch_id[0]
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def delete_pgagent_schedule(self):
|
||||
"""
|
||||
This function deletes the pgAgent schedule.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute(
|
||||
"DELETE FROM pgagent.pga_schedule "
|
||||
"WHERE jscid = '%s'::integer;" % self.schedule_id
|
||||
)
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def verify_pgagent_schedule(self):
|
||||
"""
|
||||
This function verifies the pgAgent schedule.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute(
|
||||
"SELECT COUNT(*) FROM pgagent.pga_schedule "
|
||||
"WHERE jscid = '%s'::integer;" % self.schedule_id
|
||||
)
|
||||
result = pg_cursor.fetchone()
|
||||
count = result[0]
|
||||
connection.close()
|
||||
return count is not None and int(count) != 0
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def delete_pgagent_exception(self, date, time):
|
||||
"""
|
||||
This function deletes the pgAgent exception.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
pg_cursor = connection.cursor()
|
||||
query = "DELETE FROM pgagent.pga_exception " \
|
||||
"WHERE jexdate = to_date('{0}', 'YYYY-MM-DD') AND " \
|
||||
"jextime = '{1}'::time without time zone;".format(date, time)
|
||||
pg_cursor.execute(query)
|
||||
connection.close()
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def create_pgagent_exception(self, schid, date, time):
|
||||
"""
|
||||
This function create the pgAgent exception.
|
||||
"""
|
||||
try:
|
||||
# Delete existing exception if already exists
|
||||
delete_pgagent_exception(self, date, time)
|
||||
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
query = """
|
||||
INSERT INTO pgagent.pga_exception(jexscid, jexdate, jextime
|
||||
) VALUES ({0},
|
||||
to_date('{1}', 'YYYY-MM-DD'), '{2}'::time without time zone
|
||||
) RETURNING jexid;
|
||||
""".format(schid, date, time)
|
||||
pg_cursor.execute(query)
|
||||
excep_id = pg_cursor.fetchone()
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
return excep_id[0]
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def create_pgagent_step(self, step_name, jobid):
|
||||
"""
|
||||
This function create the pgAgent step.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
query = """
|
||||
INSERT INTO pgagent.pga_jobstep(
|
||||
jstname, jstjobid, jstenabled, jstkind,
|
||||
jstcode, jstdbname
|
||||
) VALUES (
|
||||
'{0}'::text, {1}::int, true, 's', 'SELECT 1', 'postgres'
|
||||
) RETURNING jstid;
|
||||
""".format(step_name, jobid)
|
||||
pg_cursor.execute(query)
|
||||
step_id = pg_cursor.fetchone()
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
return step_id[0]
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def delete_pgagent_step(self):
|
||||
"""
|
||||
This function deletes the pgAgent step.
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
old_isolation_level = connection.isolation_level
|
||||
connection.set_isolation_level(0)
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute(
|
||||
"DELETE FROM pgagent.pga_jobstep "
|
||||
"WHERE jstid = '%s'::integer;" % self.step_id
|
||||
)
|
||||
connection.set_isolation_level(old_isolation_level)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
||||
def verify_pgagent_step(self):
|
||||
"""
|
||||
This function verifies the pgAgent step .
|
||||
"""
|
||||
try:
|
||||
connection = utils.get_db_connection(
|
||||
self.server['db'],
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port'],
|
||||
self.server['sslmode']
|
||||
)
|
||||
pg_cursor = connection.cursor()
|
||||
pg_cursor.execute(
|
||||
"SELECT COUNT(*) FROM pgagent.pga_jobstep "
|
||||
"WHERE jstid = '%s'::integer;" % self.step_id
|
||||
)
|
||||
result = pg_cursor.fetchone()
|
||||
count = result[0]
|
||||
connection.close()
|
||||
return count is not None and int(count) != 0
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
@@ -66,8 +66,8 @@ define('pgadmin.node.role', [
|
||||
|
||||
var RoleCustomSwitchControl = Backform.SwitchControl.extend({
|
||||
template: _.template([
|
||||
'<label class="control-label pg-el-sm-9 pg-el-xs-12"><%=label%></label>',
|
||||
'<div class="pgadmin-controls pg-el-sm-3 pg-el-xs-12">',
|
||||
'<label class="control-label"><%=label%></label>',
|
||||
'<div class="pgadmin-controls">',
|
||||
' <div class="checkbox" style="float:right">',
|
||||
' <label>',
|
||||
' <input type="checkbox" class="<%=extraClasses.join(\' \')%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
||||
@@ -82,7 +82,7 @@ define('pgadmin.node.role', [
|
||||
|
||||
var RoleMembersControl = Backform.Control.extend({
|
||||
defaults: _.defaults(
|
||||
{extraClasses: ['col-xs-12 col-sm-12 col-md-12']},
|
||||
{extraClasses: ['col-12 col-sm-12 col-md-12']},
|
||||
Backform.NodeListByNameControl.prototype.defaults
|
||||
),
|
||||
initialize: function() {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.icon-server-connecting {
|
||||
background-image: url('~top/static/img/load-node.gif') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
}
|
||||
@@ -328,10 +328,10 @@ define([
|
||||
titleTmpl = _.template([
|
||||
'<div class=\'subnode-header\'>',
|
||||
'<label class=\'control-label\'><%-label%></label>',
|
||||
'<button class=\'btn-sm btn-default add fa fa-plus\' title=\'' + _('Add new row') + '\' <%=canAdd ? \'\' : \'disabled="disabled"\'%>></button>',
|
||||
'<button class=\'btn btn-sm-sq btn-default add fa fa-plus\' title=\'' + _('Add new row') + '\' <%=canAdd ? \'\' : \'disabled="disabled"\'%>></button>',
|
||||
'</div>'].join('\n')),
|
||||
$gridBody =
|
||||
$('<div class=\'pgadmin-control-group backgrid form-group col-xs-12 object subnode\'></div>').append(
|
||||
$('<div class=\'pgadmin-control-group backgrid form-group col-12 object subnode\'></div>').append(
|
||||
titleTmpl(data)
|
||||
);
|
||||
|
||||
|
||||
@@ -16,15 +16,6 @@
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.icon-server-connecting {
|
||||
background-image: url('{{ url_for('static', filename='js/generated/img/load-node.gif')}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
.change_password {
|
||||
padding-left: 7px;
|
||||
}
|
||||
@@ -9,7 +9,7 @@ define('pgadmin.browser', [
|
||||
'pgadmin.browser.error', 'pgadmin.browser.frame',
|
||||
'pgadmin.browser.node', 'pgadmin.browser.collection',
|
||||
'sources/codemirror/addon/fold/pgadmin-sqlfoldcode',
|
||||
'pgadmin.browser.keyboard',
|
||||
'pgadmin.browser.keyboard', 'sources/tree/pgadmin_tree_save_state',
|
||||
], function(
|
||||
tree,
|
||||
gettext, url_for, require, $, _, S, Bootstrap, pgAdmin, Alertify,
|
||||
@@ -132,7 +132,7 @@ define('pgadmin.browser', [
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
elContainer: true,
|
||||
content: '<div class="obj_properties"><div class="alert alert-info pg-panel-message">' + select_object_msg + '</div></div>',
|
||||
content: '<div class="obj_properties container-fluid"><div class="alert alert-info pg-panel-message">' + select_object_msg + '</div></div>',
|
||||
events: panelEvents,
|
||||
onCreate: function(myPanel, $container) {
|
||||
$container.addClass('pg-no-overflow');
|
||||
@@ -146,7 +146,7 @@ define('pgadmin.browser', [
|
||||
width: 500,
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
content: '<div><div class="alert alert-info pg-panel-message pg-panel-statistics-message">' + select_object_msg + '</div><div class="pg-panel-statistics-container hidden"></div></div>',
|
||||
content: '<div><div class="alert alert-info pg-panel-message pg-panel-statistics-message">' + select_object_msg + '</div><div class="pg-panel-statistics-container d-none"></div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
// Reversed engineered SQL for the object
|
||||
@@ -167,7 +167,7 @@ define('pgadmin.browser', [
|
||||
width: 500,
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
content: '<div><div class="alert alert-info pg-panel-message pg-panel-depends-message">' + select_object_msg + '</div><div class="pg-panel-depends-container hidden"></div></div>',
|
||||
content: '<div><div class="alert alert-info pg-panel-message pg-panel-depends-message">' + select_object_msg + '</div><div class="pg-panel-depends-container d-none"></div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
// Dependents of the object
|
||||
@@ -178,7 +178,7 @@ define('pgadmin.browser', [
|
||||
width: 500,
|
||||
isCloseable: false,
|
||||
isPrivate: true,
|
||||
content: '<div><div class="alert alert-info pg-panel-message pg-panel-depends-message">' + select_object_msg + '</div><div class="pg-panel-depends-container hidden"></div></div>',
|
||||
content: '<div><div class="alert alert-info pg-panel-message pg-panel-depends-message">' + select_object_msg + '</div><div class="pg-panel-depends-container d-none"></div></div>',
|
||||
events: panelEvents,
|
||||
}),
|
||||
},
|
||||
@@ -279,7 +279,7 @@ define('pgadmin.browser', [
|
||||
// menu navigation bar
|
||||
navbar = $('#navbar-menu > ul').first(),
|
||||
// Drop down menu for objects
|
||||
$obj_mnu = navbar.find('li#mnu_obj > ul.dropdown-menu').first(),
|
||||
$obj_mnu = navbar.find('li#mnu_obj .dropdown-menu').first(),
|
||||
// data for current selected object
|
||||
d = obj.tree.itemData(item),
|
||||
update_menuitem = function(m) {
|
||||
@@ -320,7 +320,7 @@ define('pgadmin.browser', [
|
||||
// Create a dummy 'no object seleted' menu
|
||||
var create_submenu = pgAdmin.Browser.MenuGroup(
|
||||
obj.menu_categories['create'], [{
|
||||
$el: $('<li class="menu-item disabled"><a href="#">' + gettext('No object selected') + '</a></li>'),
|
||||
$el: $('<li><a class="dropdown-item text-white disabled" href="#">' + gettext('No object selected') + '</a></li>'),
|
||||
priority: 1,
|
||||
category: 'create',
|
||||
update: function() {},
|
||||
@@ -513,9 +513,11 @@ define('pgadmin.browser', [
|
||||
.done(function() {})
|
||||
.fail(function() {});
|
||||
}, 300000);
|
||||
|
||||
obj.Events.on('pgadmin:browser:tree:add', obj.onAddTreeNode, obj);
|
||||
obj.Events.on('pgadmin:browser:tree:update', obj.onUpdateTreeNode, obj);
|
||||
obj.Events.on('pgadmin:browser:tree:refresh', obj.onRefreshTreeNode, obj);
|
||||
|
||||
},
|
||||
|
||||
add_menu_category: function(
|
||||
@@ -609,11 +611,11 @@ define('pgadmin.browser', [
|
||||
if (pgAdmin.Browser.MenuCreator(
|
||||
$dropdown, obj.menus[o.menu], obj.menu_categories
|
||||
)) {
|
||||
$mnu.removeClass('hide');
|
||||
$mnu.removeClass('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
navbar.children('#mnu_obj').removeClass('hide');
|
||||
navbar.children('#mnu_obj').removeClass('d-none');
|
||||
obj.enable_disable_menus();
|
||||
},
|
||||
// General function to handle callbacks for object or dialog help.
|
||||
@@ -1942,19 +1944,10 @@ define('pgadmin.browser', [
|
||||
pgAdmin.Browser.editor_shortcut_keys.Tab = 'insertSoftTab';
|
||||
}
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
var e = window.event,
|
||||
msg = S(gettext('Are you sure you wish to close the %s browser?')).sprintf(pgBrowser.utils.app_name).value();
|
||||
|
||||
// For IE and Firefox prior to version 4
|
||||
if (e) {
|
||||
e.returnValue = msg;
|
||||
}
|
||||
|
||||
// For Safari
|
||||
return msg;
|
||||
};
|
||||
|
||||
$(window).on('beforeunload', function() {
|
||||
if (pgBrowser.get_preference('browser', 'browser_tree_state_save_interval').value !== -1)
|
||||
pgAdmin.Browser.browserTreeState.save_state();
|
||||
});
|
||||
|
||||
return pgAdmin.Browser;
|
||||
});
|
||||
@@ -59,7 +59,7 @@ define([
|
||||
j = panel.$container.find('.obj_properties').first(),
|
||||
view = j.data('obj-view'),
|
||||
content = $('<div></div>')
|
||||
.addClass('pg-prop-content col-xs-12'),
|
||||
.addClass('pg-prop-content col-12'),
|
||||
node = pgBrowser.Nodes[that.node],
|
||||
// This will be the URL, used for object manipulation.
|
||||
urlBase = this.generate_url(item, 'properties', data),
|
||||
|
||||
@@ -13,7 +13,7 @@ function(gettext, _, alertify, pgAdmin) {
|
||||
' + gettext('Error message') + '</a>\
|
||||
</h4>\
|
||||
</div>\
|
||||
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">\
|
||||
<div id="collapseOne" class="panel-collapse collapse show" role="tabpanel" aria-labelledby="headingOne">\
|
||||
<div class="panel-body" style="overflow: auto;">' + unescape(message) + '</div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
@@ -4,6 +4,7 @@ import $ from 'jquery';
|
||||
import Mousetrap from 'mousetrap';
|
||||
import * as commonUtils from '../../../static/js/utils';
|
||||
import dialogTabNavigator from '../../../static/js/dialog_tab_navigator';
|
||||
import {default as keyboardFunc} from 'sources/keyboard_shortcuts';
|
||||
|
||||
const pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {};
|
||||
|
||||
@@ -37,9 +38,7 @@ _.extend(pgBrowser.keyboardNavigation, {
|
||||
this.keyboardShortcut.help_shortcut],
|
||||
}, // Main menu
|
||||
'bindRightPanel': {'shortcuts': [this.keyboardShortcut.tabbed_panel_backward, this.keyboardShortcut.tabbed_panel_forward]}, // Main window panels
|
||||
'bindMainMenuLeft': {'shortcuts': 'left', 'bindElem': '.pg-navbar'}, // Main menu
|
||||
'bindMainMenuRight': {'shortcuts': 'right', 'bindElem': '.pg-navbar'}, // Main menu
|
||||
'bindMainMenuUpDown': {'shortcuts': ['up', 'down']}, // Main menu
|
||||
'bindSubMenuClose': {'shortcuts': ['esc','enter']},
|
||||
'bindLeftTree': {'shortcuts': this.keyboardShortcut.left_tree_shortcut}, // Main menu,
|
||||
'bindSubMenuQueryTool': {'shortcuts': this.keyboardShortcut.sub_menu_query_tool}, // Sub menu - Open Query Tool,
|
||||
'bindSubMenuViewData': {'shortcuts': this.keyboardShortcut.sub_menu_view_data}, // Sub menu - Open View Data,
|
||||
@@ -58,9 +57,37 @@ _.extend(pgBrowser.keyboardNavigation, {
|
||||
_.each(self.shortcutMethods, (keyCombo, callback) => {
|
||||
self._bindWithMousetrap(keyCombo.shortcuts, self[callback], keyCombo.bindElem);
|
||||
});
|
||||
|
||||
|
||||
/* Dropdown submenu was not working properly for up/down arrow keys.
|
||||
* So up/down/right/left events for dropdown were removed from Mousetrap and were
|
||||
* handled manually.
|
||||
*/
|
||||
const LEFT_KEY = 37,
|
||||
UP_KEY = 38,
|
||||
RIGHT_KEY = 39,
|
||||
DOWN_KEY = 40;
|
||||
|
||||
$('.pg-navbar').on('keydown', (event)=> {
|
||||
switch(event.keyCode) {
|
||||
case LEFT_KEY:
|
||||
self.bindMainMenuLeft(event);
|
||||
break;
|
||||
case UP_KEY:
|
||||
self.bindMainMenuUpDown(event, 'up');
|
||||
break;
|
||||
case RIGHT_KEY:
|
||||
self.bindMainMenuRight(event);
|
||||
break;
|
||||
case DOWN_KEY:
|
||||
self.bindMainMenuUpDown(event, 'down');
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
_bindWithMousetrap: function(shortcuts, callback, bindElem) {
|
||||
const self = this;
|
||||
Mousetrap.unbind(shortcuts);
|
||||
if (bindElem) {
|
||||
const elem = document.querySelector(bindElem);
|
||||
Mousetrap(elem).bind(shortcuts, function() {
|
||||
@@ -72,6 +99,15 @@ _.extend(pgBrowser.keyboardNavigation, {
|
||||
});
|
||||
}
|
||||
},
|
||||
unbindShortcuts: function() {
|
||||
// Reset previous events on each instance
|
||||
const self = this;
|
||||
_.each(self.mousetrapInstances, (obj) => {
|
||||
obj['instance'].reset();
|
||||
});
|
||||
// Clear already processed events
|
||||
self.mousetrapInstances = [];
|
||||
},
|
||||
bindMainMenu: function(event, combo) {
|
||||
const shortcut_obj = this.keyboardShortcut;
|
||||
if (combo === shortcut_obj.file_shortcut) $('#mnu_file a.dropdown-toggle').dropdown('toggle');
|
||||
@@ -106,48 +142,85 @@ _.extend(pgBrowser.keyboardNavigation, {
|
||||
}, 1000);
|
||||
},
|
||||
bindMainMenuLeft: function(event) {
|
||||
let prevMenu;
|
||||
if ($(event.target).hasClass('menu-link')) { // Menu items
|
||||
prevMenu = $(event.target).parent().parent().parent().prev('.dropdown');
|
||||
}
|
||||
else if ($(event.target).parent().hasClass('dropdown-submenu')) { // Sub menu
|
||||
$(event.target).parent().toggleClass('open');
|
||||
return;
|
||||
}
|
||||
else { //Menu headers
|
||||
prevMenu = $(event.target).parent().prev('.dropdown');
|
||||
}
|
||||
if ($(event.target).hasClass('nav-link')) { // Menu items
|
||||
let currNavMenu = $(event.target).closest('.nav-item');
|
||||
keyboardFunc._stopEventPropagation(event);
|
||||
|
||||
if (prevMenu.hasClass('hide')) prevMenu = prevMenu.prev('.dropdown'); // Skip hidden menus
|
||||
currNavMenu = currNavMenu.prev('.nav-item');
|
||||
// Skip hidden menus
|
||||
while(currNavMenu.hasClass('d-none')) {
|
||||
currNavMenu = currNavMenu.prev('.nav-item');
|
||||
}
|
||||
|
||||
prevMenu.find('a:first').dropdown('toggle');
|
||||
currNavMenu.find('.dropdown-toggle').first().dropdown('toggle');
|
||||
|
||||
} else if($(event.target).closest('.dropdown-menu').length > 0) {
|
||||
let currLi = $(event.target).closest('li');
|
||||
keyboardFunc._stopEventPropagation(event);
|
||||
/*close submenu*/
|
||||
let currMenu = currLi.closest('.dropdown-menu');
|
||||
if(currMenu.closest('.dropdown-submenu').length > 0) {
|
||||
currMenu.removeClass('show');
|
||||
currLi = currMenu.closest('.dropdown-submenu');
|
||||
currLi.find('.dropdown-item').trigger('focus');
|
||||
}
|
||||
}
|
||||
},
|
||||
bindMainMenuRight: function(event) {
|
||||
let nextMenu;
|
||||
if ($(event.target).hasClass('menu-link')) { // Menu items
|
||||
nextMenu = $(event.target).parent().parent().parent().next('.dropdown');
|
||||
}
|
||||
else if ($(event.target).parent().hasClass('dropdown-submenu')) { // Sub menu
|
||||
$(event.target).parent().toggleClass('open');
|
||||
return;
|
||||
}
|
||||
else { //Menu headers
|
||||
nextMenu = $(event.target).parent().next('.dropdown');
|
||||
}
|
||||
if ($(event.target).hasClass('nav-link')) { // Menu items
|
||||
let currNavMenu = $(event.target).closest('.nav-item');
|
||||
keyboardFunc._stopEventPropagation(event);
|
||||
|
||||
if (nextMenu.hasClass('hide')) nextMenu = nextMenu.next('.dropdown'); // Skip hidden menus
|
||||
currNavMenu = currNavMenu.next('.nav-item');
|
||||
// Skip hidden menus
|
||||
while(currNavMenu.hasClass('d-none')) {
|
||||
currNavMenu = currNavMenu.next('.nav-item');
|
||||
}
|
||||
|
||||
nextMenu.find('a:first').dropdown('toggle');
|
||||
currNavMenu.find('.dropdown-toggle').first().dropdown('toggle');
|
||||
} else if($(event.target).closest('.dropdown-menu').length > 0) {
|
||||
let currLi = $(event.target).closest('li');
|
||||
keyboardFunc._stopEventPropagation(event);
|
||||
|
||||
/*open submenu if any*/
|
||||
if(currLi.hasClass('dropdown-submenu')){
|
||||
currLi.find('.dropdown-menu').addClass('show');
|
||||
currLi = currLi.find('.dropdown-menu .dropdown-item').first().trigger('focus');
|
||||
}
|
||||
}
|
||||
},
|
||||
bindMainMenuUpDown: function(event, combo) {
|
||||
// Handle Sub-menus
|
||||
if (combo === 'up' && $(event.target).parent().prev().prev('.dropdown-submenu').length > 0) {
|
||||
$(event.target).parent().prev().prev('.dropdown-submenu').find('a:first').trigger('focus');
|
||||
} else {
|
||||
if ($(event.target).parent().hasClass('dropdown-submenu')) {
|
||||
$(event.target).parent().parent().parent().find('a:first').dropdown('toggle');
|
||||
$(event.target).parent().parent().children().eq(2).find('a:first').trigger('focus');
|
||||
if($(event.target).closest('.dropdown-menu').length > 0) {
|
||||
keyboardFunc._stopEventPropagation(event);
|
||||
let currLi = $(event.target).closest('li');
|
||||
/*close all the submenus on movement*/
|
||||
$(event.target).closest('.dropdown-menu').find('.show').removeClass('show');
|
||||
|
||||
if(combo === 'up') {
|
||||
currLi = currLi.prev();
|
||||
}
|
||||
else if(combo === 'down'){
|
||||
currLi = currLi.next();
|
||||
}
|
||||
|
||||
/*do not focus on divider and disabled */
|
||||
while(currLi.hasClass('dropdown-divider')
|
||||
|| currLi.find('.dropdown-item').first().hasClass('disabled')) {
|
||||
if(combo === 'up') {
|
||||
currLi = currLi.prev();
|
||||
}
|
||||
else if(combo === 'down'){
|
||||
currLi = currLi.next();
|
||||
}
|
||||
}
|
||||
currLi.find('.dropdown-item').trigger('focus');
|
||||
}
|
||||
},
|
||||
bindSubMenuClose: function() {
|
||||
if($(event.target).hasClass('dropdown-item')
|
||||
&& $(event.target).closest('.dropdown-submenu').length > 0) {
|
||||
$(event.target).closest('.dropdown').find('.dropdown-submenu .dropdown-menu').removeClass('show');
|
||||
}
|
||||
},
|
||||
bindLeftTree: function() {
|
||||
|
||||
@@ -55,7 +55,7 @@ define([
|
||||
module: this.module || pgAdmin.Browser,
|
||||
cb: this.callback,
|
||||
data: this.data,
|
||||
}).addClass('menu-link');
|
||||
}).addClass('dropdown-item');
|
||||
|
||||
this.is_disabled = this.disabled(node, item);
|
||||
if (this.icon) {
|
||||
@@ -64,53 +64,14 @@ define([
|
||||
}));
|
||||
}
|
||||
|
||||
url.addClass((this.is_disabled ? ' disabled' : ''));
|
||||
|
||||
var textSpan = $('<span data-test="menu-item-text"></span>').text(' ' + this.label);
|
||||
|
||||
url.append(textSpan);
|
||||
|
||||
this.$el = $('<li/>')
|
||||
.addClass('menu-item' + (this.is_disabled ? ' disabled' : ''))
|
||||
.append(url);
|
||||
|
||||
this.applyStyle();
|
||||
this.$el = $('<li/>').append(url);
|
||||
},
|
||||
|
||||
applyDisabledStyle: function() {
|
||||
var span = this.$el.find('span');
|
||||
var icon = this.$el.find('i');
|
||||
|
||||
|
||||
span.addClass('text-gray');
|
||||
span.removeClass('text-fg-inverse');
|
||||
icon.addClass('text-gray');
|
||||
icon.removeClass('text-fg-inverse');
|
||||
},
|
||||
|
||||
applyEnabledStyle: function() {
|
||||
var element = this.$el;
|
||||
var span = this.$el.find('span');
|
||||
|
||||
span.addClass('text-fg-inverse');
|
||||
span.removeClass('text-gray');
|
||||
element.find('i').addClass('text-fg-inverse');
|
||||
element.find('i').removeClass('text-gray');
|
||||
|
||||
span.on('mouseover',() => {
|
||||
element.addClass('bg-gray-dark');
|
||||
});
|
||||
span.on('mouseout',() => {
|
||||
element.removeClass('bg-gray-dark');
|
||||
});
|
||||
},
|
||||
|
||||
applyStyle: function() {
|
||||
if (this.is_disabled) {
|
||||
this.applyDisabledStyle();
|
||||
} else {
|
||||
this.applyEnabledStyle();
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Updates the enable/disable state of the menu-item based on the current
|
||||
* selection using the disabled function. This also creates a object
|
||||
@@ -118,17 +79,13 @@ define([
|
||||
*/
|
||||
update: function(node, item) {
|
||||
|
||||
if (this.$el && !this.$el.hasClass('disabled')) {
|
||||
this.$el.addClass('disabled');
|
||||
if (this.$el && !this.$el.find('.dropdown-item').hasClass('disabled')) {
|
||||
this.$el.find('.dropdown-item').addClass('disabled');
|
||||
}
|
||||
|
||||
this.is_disabled = this.disabled(node, item);
|
||||
if (this.$el && !this.is_disabled) {
|
||||
this.$el.removeClass('disabled');
|
||||
}
|
||||
|
||||
if (this.$el) {
|
||||
this.applyStyle();
|
||||
this.$el.find('.dropdown-item').removeClass('disabled');
|
||||
}
|
||||
|
||||
this.context = {
|
||||
@@ -208,16 +165,16 @@ define([
|
||||
|
||||
pgAdmin.Browser.MenuGroup = function(opts, items, prev, ctx) {
|
||||
var template = _.template([
|
||||
'<% if (above) { %><hr><% } %>',
|
||||
'<li class="menu-item dropdown dropdown-submenu">',
|
||||
' <a href="#" class="dropdown-toggle" data-toggle="dropdown">',
|
||||
'<% if (above) { %><li class="dropdown-divider"></li><% } %>',
|
||||
'<li class="dropdown-submenu">',
|
||||
' <a href="#" class="dropdown-item">',
|
||||
' <% if (icon) { %><i class="<%= icon %>"></i><% } %>',
|
||||
' <span><%= label %></span>',
|
||||
' <span class="text-white"><%= label %></span>',
|
||||
' </a>',
|
||||
' <ul class="dropdown-menu navbar-inverse">',
|
||||
' <ul class="dropdown-menu bg-dark">',
|
||||
' </ul>',
|
||||
'</li>',
|
||||
'<% if (below) { %><hr><% } %>',
|
||||
'<% if (below) { %><li class="dropdown-divider"></li><% } %>',
|
||||
].join('\n')),
|
||||
data = {
|
||||
'label': opts.label,
|
||||
@@ -393,7 +350,7 @@ define([
|
||||
Menu.DEFAULTS = {};
|
||||
|
||||
Menu.prototype.toggle = function(ev) {
|
||||
var $parent = this.$element.closest('.menu-item');
|
||||
var $parent = this.$element.closest('.dropdown-item');
|
||||
if ($parent.hasClass('disabled')) {
|
||||
ev.preventDefault();
|
||||
return false;
|
||||
@@ -446,8 +403,8 @@ define([
|
||||
$(document)
|
||||
.on('click.pg.menu.data-api', '[data-toggle^="pg-menu"]', function(ev) {
|
||||
var $menu = $(ev.target);
|
||||
if (!$menu.hasClass('menu-link'))
|
||||
$menu = $menu.closest('.menu-link');
|
||||
if (!$menu.hasClass('dropdown-item'))
|
||||
$menu = $menu.closest('.dropdown-item');
|
||||
Plugin.call($menu, 'toggle', ev);
|
||||
})
|
||||
.on(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
define('pgadmin.browser.node', [
|
||||
'sources/tree/pgadmin_tree_node',
|
||||
'sources/tree/pgadmin_tree_node', 'sources/url_for',
|
||||
'sources/gettext', 'jquery', 'underscore', 'underscore.string', 'sources/pgadmin',
|
||||
'pgadmin.browser.menu', 'backbone', 'pgadmin.alertifyjs', 'pgadmin.browser.datamodel',
|
||||
'backform', 'sources/browser/generate_url', 'sources/utils', 'pgadmin.browser.utils',
|
||||
'pgadmin.backform',
|
||||
], function(
|
||||
pgadminTreeNode,
|
||||
pgadminTreeNode, url_for,
|
||||
gettext, $, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform, generateUrl, commonUtils
|
||||
) {
|
||||
|
||||
@@ -395,7 +395,7 @@ define('pgadmin.browser.node', [
|
||||
success: function() {
|
||||
// Clear timeout and remove message
|
||||
clearTimeout(timer);
|
||||
$msgDiv.addClass('hidden');
|
||||
$msgDiv.addClass('d-none');
|
||||
|
||||
// We got the latest attributes of the object. Render the view
|
||||
// now.
|
||||
@@ -479,7 +479,7 @@ define('pgadmin.browser.node', [
|
||||
isCloseable: true,
|
||||
isPrivate: true,
|
||||
elContainer: true,
|
||||
content: '<div class="obj_properties"><div class="alert alert-info pg-panel-message">' + gettext('Please wait while we fetch information about the node from the server...') + '</div></div>',
|
||||
content: '<div class="obj_properties container-fluid"><div class="alert alert-info pg-panel-message">' + gettext('Please wait while we fetch information about the node from the server...') + '</div></div>',
|
||||
onCreate: function(myPanel, $container) {
|
||||
$container.addClass('pg-no-overflow');
|
||||
},
|
||||
@@ -849,7 +849,6 @@ define('pgadmin.browser.node', [
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
added: function(item, data, browser) {
|
||||
var b = browser || pgBrowser,
|
||||
t = b.tree,
|
||||
@@ -876,6 +875,8 @@ define('pgadmin.browser.node', [
|
||||
);
|
||||
}
|
||||
|
||||
pgBrowser.Events.trigger('pgadmin:browser:tree:expand-from-previous-tree-state',
|
||||
item);
|
||||
pgBrowser.Node.callbacks.change_server_background(item, data);
|
||||
},
|
||||
// Callback called - when a node is selected in browser tree.
|
||||
@@ -905,6 +906,8 @@ define('pgadmin.browser.node', [
|
||||
}
|
||||
}
|
||||
|
||||
pgBrowser.Events.trigger('pgadmin:browser:tree:update-tree-state',
|
||||
item);
|
||||
return true;
|
||||
},
|
||||
removed: function(item) {
|
||||
@@ -958,6 +961,14 @@ define('pgadmin.browser.node', [
|
||||
},
|
||||
});
|
||||
},
|
||||
opened: function(item) {
|
||||
pgBrowser.Events.trigger('pgadmin:browser:tree:update-tree-state',
|
||||
item);
|
||||
},
|
||||
closed: function(item) {
|
||||
pgBrowser.Events.trigger('pgadmin:browser:tree:remove-from-tree-state',
|
||||
item);
|
||||
},
|
||||
},
|
||||
/**********************************************************************
|
||||
* A hook (not a callback) to show object properties in given HTML
|
||||
@@ -972,7 +983,7 @@ define('pgadmin.browser.node', [
|
||||
j = panel.$container.find('.obj_properties').first(),
|
||||
view = j.data('obj-view'),
|
||||
content = $('<div tabindex="1"></div>')
|
||||
.addClass('pg-prop-content col-xs-12');
|
||||
.addClass('pg-prop-content col-12');
|
||||
|
||||
// Handle key press events for Cancel, save and help button
|
||||
var handleKeyDown = function(event, context) {
|
||||
|
||||
@@ -38,7 +38,7 @@ define([
|
||||
].join(''));
|
||||
|
||||
let $btnContainer = $(
|
||||
'<div style="">'
|
||||
'<div class="select2-select-all-adapter-container">'
|
||||
).append($selectAll).append($unselectAll);
|
||||
|
||||
if (!this.$element.prop('multiple')) {
|
||||
|
||||
@@ -94,6 +94,9 @@ _.extend(pgBrowser, {
|
||||
modifyAnimation.modifyAlertifyAnimation(self);
|
||||
}
|
||||
|
||||
// Initialize Tree saving/reloading
|
||||
pgBrowser.browserTreeState.init();
|
||||
|
||||
/* Once the cache is loaded after changing the preferences,
|
||||
* notify the modules of the change
|
||||
*/
|
||||
|
||||
@@ -101,7 +101,7 @@ define([
|
||||
' <div class="wizard-progress-bar"><% if (show_progress_bar) { %>' +
|
||||
' <p class="alert alert-info col-sm-12"><%= show_progress_bar %></p><% } %>' +
|
||||
' </div>' +
|
||||
' <div class="wizard-right-panel_content col-xs-12">' +
|
||||
' <div class="wizard-right-panel_content col-12">' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
' </div>' +
|
||||
|
||||
@@ -45,8 +45,8 @@ samp,
|
||||
}
|
||||
|
||||
.pg-toolbar-btn:hover {
|
||||
border: 1px solid $color-gray-light !important;
|
||||
background-color: $color-gray-lighter !important;
|
||||
border: none;
|
||||
background-color: $color-bg !important;
|
||||
}
|
||||
|
||||
.pg-toolbar-btn {
|
||||
@@ -55,5 +55,5 @@ samp,
|
||||
background-color: $color-bg !important;
|
||||
font-size: 14px !important;
|
||||
color: $color-gray-darker !important;
|
||||
border: 1px solid $color-gray-light !important;
|
||||
border: none;
|
||||
}
|
||||
@@ -34,7 +34,7 @@ require.onResourceLoad = function (context, map, depMaps) {
|
||||
if (loadingStatusEl) {
|
||||
if (!context) {
|
||||
// we will call onResourceLoad(false) by ourselves when requirejs
|
||||
// is not loading anything hide the indicator and exit
|
||||
// is not loading anything d-none the indicator and exit
|
||||
setTimeout(function() {
|
||||
if (panel != null) {
|
||||
try{
|
||||
@@ -80,10 +80,9 @@ window.onload = function(e){
|
||||
setTimeout(function() {
|
||||
var gravatarImg = {{ IMG.PREPARE_HTML()|safe }}
|
||||
//$('#navbar-menu .navbar-right > li > a').html(gravatarImg);
|
||||
var navbarRight = document.getElementById("navbar-menu").getElementsByClassName("navbar-right")[0];
|
||||
if (navbarRight) {
|
||||
var list = navbarRight.getElementsByTagName("LI")[0];
|
||||
list.getElementsByTagName("a")[0].innerHTML = gravatarImg;
|
||||
var navbarUser = document.getElementById("navbar-user");
|
||||
if (navbarUser) {
|
||||
navbarUser.innerHTML = gravatarImg;
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
@@ -93,85 +92,77 @@ window.onload = function(e){
|
||||
{% block body %}
|
||||
<div id="pg-spinner">
|
||||
<div class="pg-sp-content">
|
||||
<div class="row"><div class="col-xs-12 pg-sp-icon fa fa-spinner fa-pulse"></div></div>
|
||||
<div class="row"><div class="col-xs-12 pg-sp-text">{{ _('Loading {0} v{1}...').format(config.APP_NAME, config.APP_VERSION) }}</div></div>
|
||||
<div class="row"><div class="col-12 pg-sp-icon fa fa-spinner fa-pulse"></div></div>
|
||||
<div class="row"><div class="col-12 pg-sp-text">{{ _('Loading {0} v{1}...').format(config.APP_NAME, config.APP_VERSION) }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="navbar-inverse navbar-fixed-top pg-navbar">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-menu">
|
||||
<span class="sr-only">{{ _('Toggle navigation') }}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand pgadmin_header_logo" onClick="return false;" href="{{ '#' }}"
|
||||
title="{{ config.APP_NAME }} {{ _('logo') }}">
|
||||
<i class="app-icon {{ config.APP_ICON }}"></i>
|
||||
<span> {{ config.APP_NAME }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbar-menu" role="navigation">
|
||||
|
||||
<ul class="nav navbar-nav">
|
||||
|
||||
<li id="mnu_file" class="dropdown hide">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('File') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-inverse" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_edit" class="dropdown hide">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Edit') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-inverse" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_obj" class="dropdown ">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Object') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-inverse" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_management" class="dropdown hide">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Management') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-inverse" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_tools" class="dropdown hide">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Tools') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-inverse" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_help" class="dropdown hide">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Help') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu navbar-inverse" role="menu"></ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% if config.SERVER_MODE %}
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"></a>
|
||||
<ul class="dropdown-menu navbar-inverse">
|
||||
<li>
|
||||
<a href="#" onclick="pgAdmin.Browser.UserManagement.change_password(
|
||||
'{{ url_for('browser.change_password') }}'
|
||||
)">
|
||||
{{ _('Change Password') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
{% if is_admin %}
|
||||
<li><a href="#" onclick="pgAdmin.Browser.UserManagement.show_users()">{{ _('Users') }}</a></li>
|
||||
<li class="divider"></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ url_for('security.logout') }}?next={{url_for('browser.index')}}">{{ _('Logout') }}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark pg-navbar">
|
||||
<a class="navbar-brand pgadmin_header_logo" onClick="return false;" href="{{ '#' }}"
|
||||
title="{{ config.APP_NAME }} {{ _('logo') }}">
|
||||
<i class="app-icon {{ config.APP_ICON }}"></i>
|
||||
<span> {{ config.APP_NAME }}</span>
|
||||
</a>
|
||||
<button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#navbar-menu" aria-controls="navbar-menu">
|
||||
<span class="sr-only">{{ _('Toggle navigation') }}</span>
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbar-menu" role="navigation">
|
||||
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li id="mnu_file" class="nav-item active dropdown d-none">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('File') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_edit" class="nav-item active dropdown d-none">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Edit') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_obj" class="nav-item active dropdown ">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Object') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_management" class="nav-item active dropdown d-none">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Management') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_tools" class="nav-item active dropdown d-none">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Tools') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</li>
|
||||
<li id="mnu_help" class="nav-item active dropdown d-none">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{{
|
||||
_('Help') }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% if config.SERVER_MODE %}
|
||||
<ul class="navbar-nav mx-2">
|
||||
<li class="nav-item active dropdown">
|
||||
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false" id="navbar-user"></a>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" onclick="pgAdmin.Browser.UserManagement.change_password(
|
||||
'{{ url_for('browser.change_password') }}'
|
||||
)">
|
||||
{{ _('Change Password') }}
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown-divider"></li>
|
||||
{% if is_admin %}
|
||||
<li><a class="dropdown-item" href="#" onclick="pgAdmin.Browser.UserManagement.show_users()">{{ _('Users') }}</a></li>
|
||||
<li class="dropdown-divider"></li>
|
||||
{% endif %}
|
||||
<li><a class="dropdown-item" href="{{ url_for('security.logout') }}?next={{url_for('browser.index')}}">{{ _('Logout') }}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -94,13 +94,13 @@ class ChangePasswordTestCase(BaseTestGenerator):
|
||||
if 'valid_password' in dir(self):
|
||||
response = self.tester.post(
|
||||
'/user_management/user/',
|
||||
data=dict(
|
||||
data=json.dumps(dict(
|
||||
email=self.username,
|
||||
newPassword=self.password,
|
||||
confirmPassword=self.password,
|
||||
active=1,
|
||||
active=True,
|
||||
role="2"
|
||||
),
|
||||
)),
|
||||
follow_redirects=True
|
||||
)
|
||||
user_id = json.loads(response.data.decode('utf-8'))['id']
|
||||
|
||||
@@ -43,6 +43,10 @@ class TestLoginUserImage(BaseTestGenerator):
|
||||
"Logout first if already logged in"
|
||||
utils.logout_tester_account(cls.tester)
|
||||
|
||||
# No need to call baseclass setup function
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def runTest(self):
|
||||
# Login and check type of image in response
|
||||
response = self.tester.post(
|
||||
|
||||
@@ -15,6 +15,7 @@ from pgadmin import create_app
|
||||
|
||||
class InternalVersionTestCase(BaseTestGenerator):
|
||||
"""
|
||||
This test case verifies application creation with new versions parameters
|
||||
"""
|
||||
scenarios = [
|
||||
('TestCase with INTERNAL_VERSION_PARAM none', dict(
|
||||
@@ -47,12 +48,14 @@ class InternalVersionTestCase(BaseTestGenerator):
|
||||
self.config_bak = config
|
||||
|
||||
def runTest(self):
|
||||
# assign config file parameters new values
|
||||
config.APP_VERSION_INT = self.app_version
|
||||
config.INTERNAL_VERSION_PARAM = self.version_param
|
||||
config.INTERNAL_VERSION_EXTN = self.version_extn
|
||||
config.APP_VERSION_PARAM = self.version_param
|
||||
config.APP_VERSION_EXTN = self.version_extn
|
||||
|
||||
version_string = "?{0}={1}".format(config.INTERNAL_VERSION_PARAM,
|
||||
config.APP_VERSION_INT)
|
||||
version_string = "?{0}={1}".format(self.version_param,
|
||||
self.app_version)
|
||||
# create application
|
||||
app = create_app()
|
||||
with app.app_context(), app.test_request_context():
|
||||
url = url_for(self.endpoint, filename=self.filename)
|
||||
|
||||
@@ -86,6 +86,10 @@ class LoginTestCase(BaseTestGenerator):
|
||||
"""
|
||||
utils.logout_tester_account(cls.tester)
|
||||
|
||||
# No need to call base class setup function
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def runTest(self):
|
||||
"""This function checks login functionality."""
|
||||
response = self.tester.post(
|
||||
|
||||
@@ -28,6 +28,10 @@ class LogoutTest(BaseTestGenerator):
|
||||
def setUpClass(cls):
|
||||
pass
|
||||
|
||||
# No need to call base class setup function
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def runTest(self):
|
||||
"""This function checks the logout functionality."""
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ class ResetPasswordTestCase(BaseTestGenerator):
|
||||
def setUpClass(cls):
|
||||
logout_tester_account(cls.tester)
|
||||
|
||||
# No need to call baseclass setup function
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def runTest(self):
|
||||
"""This function checks reset password functionality."""
|
||||
|
||||
|
||||
@@ -57,6 +57,10 @@ class VersionInRangeTestCase(BaseTestGenerator):
|
||||
def setUpClass(cls):
|
||||
pass
|
||||
|
||||
# No need to call base class setup function
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def runTest(self):
|
||||
"""This function will check version in range functionality."""
|
||||
if self.scenario == 1:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
.dashboard-container {
|
||||
padding: 15px;
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.dashboard-row {
|
||||
|
||||
@@ -27,27 +27,13 @@
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .subnode-dialog {
|
||||
background-color: $color-bg;
|
||||
border: 0px solid $color-gray-lighter;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .sub-node-form {
|
||||
padding: 0px 0px 0px 1px;
|
||||
background-color: $color-gray-lighter;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .subnode-body {
|
||||
background-color: $color-gray-lighter;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .sub-node-form > .nav-tabs {
|
||||
background-color: $color-gray-lighter !important;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .subnode-dialog .nav > li > a {
|
||||
font-weight: bold;
|
||||
border-color: $color-gray-lighter;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .subnode-dialog .nav-tabs > li.active > a,
|
||||
@@ -63,7 +49,3 @@
|
||||
background-color: $color-gray-lighter !important;
|
||||
border-bottom-color: $color-gray-lighter;
|
||||
}
|
||||
|
||||
.dashboard-tab-container .subnode-dialog .tab-pane {
|
||||
border: 1px solid $color-gray-lighter;
|
||||
}
|
||||