Merge remote-tracking branch 'origin/master' into cve-9.16
# Conflicts: # docs/en_US/release_notes_9_16.rst
This commit is contained in:
@@ -63,7 +63,7 @@ and secure.
|
||||
"OAUTH2_API_BASE_URL", "Oauth2 base URL endpoint to make requests simple, ex: *https://api.github.com/*"
|
||||
"OAUTH2_USERINFO_ENDPOINT", "User Endpoint, ex: *user* (for github, or *user/emails* if the user's email address is private) and *userinfo* (for google). **For OIDC providers**, this is optional if the ID token contains sufficient claims (email, preferred_username, or sub)."
|
||||
"OAUTH2_SCOPE", "Oauth scope, ex: 'openid email profile'. **For OIDC providers**, include 'openid' scope to receive an ID token."
|
||||
"OAUTH2_ICON", "The Font-awesome icon to be placed on the oauth2 button, ex: fa-github"
|
||||
"OAUTH2_ICON", "The Font Awesome icon to be placed on the oauth2 button. A brand icon name (e.g. fa-github) uses the brands style by default; to use another style, include the style class explicitly (e.g. 'fas fa-key')."
|
||||
"OAUTH2_BUTTON_COLOR", "Oauth2 button color"
|
||||
"OAUTH2_USERNAME_CLAIM", "The claim which is used for the username. If the value is empty, **for OIDC providers** pgAdmin will use: 1) email, 2) preferred_username, or 3) sub (in that order). **For OAuth2 providers** without OIDC, email is required. Ex: *oid* (for AzureAD), *email* (for Github), *preferred_username* (for Keycloak)"
|
||||
"OAUTH2_AUTO_CREATE_USER", "Set the value to *True* if you want to automatically
|
||||
|
||||
@@ -27,6 +27,7 @@ New features
|
||||
************
|
||||
|
||||
| `Issue #2431 <https://github.com/pgadmin-org/pgadmin4/issues/2431>`_ - Added an option to colourize panel and tab headers based on the connected server's colour, making it easier to tell which server a tab is connected to at a glance.
|
||||
| `Issue #7641 <https://github.com/pgadmin-org/pgadmin4/issues/7641>`_ - Allow the OAuth2 login button icon to use any Font Awesome style (e.g. ``fas fa-key``), not only brand icons.
|
||||
| `Issue #9301 <https://github.com/pgadmin-org/pgadmin4/issues/9301>`_ - Added a "Back to login" link to the Forgot Password and Reset Password pages.
|
||||
| `Issue #9626 <https://github.com/pgadmin-org/pgadmin4/issues/9626>`_ - Add support for the TOAST tuple target storage parameter in the Materialized View dialog.
|
||||
| `Issue #9646 <https://github.com/pgadmin-org/pgadmin4/issues/9646>`_ - Make the init container security context in the Helm chart configurable via containerSecurityContext, consistent with the main container.
|
||||
@@ -46,11 +47,13 @@ Housekeeping
|
||||
| `Issue #10049 <https://github.com/pgadmin-org/pgadmin4/issues/10049>`_ - Bump vulnerable transitive dependencies (tar, flatted) to clear security advisories.
|
||||
| `Issue #10050 <https://github.com/pgadmin-org/pgadmin4/issues/10050>`_ - Rebase the version-specific SQL templates so the default targets PostgreSQL 14, the oldest supported server version, dropping the obsolete sub-14 template buckets.
|
||||
| `Issue #10063 <https://github.com/pgadmin-org/pgadmin4/issues/10063>`_ - Strip the foreign-architecture slice from the macOS bundle so single-arch builds no longer ship the universal2 Python framework's unused arm64/x86_64 code.
|
||||
| `Issue #10094 <https://github.com/pgadmin-org/pgadmin4/issues/10094>`_ - Update the Italian translation for v9.16.
|
||||
|
||||
Bug fixes
|
||||
*********
|
||||
|
||||
| `Issue #6308 <https://github.com/pgadmin-org/pgadmin4/issues/6308>`_ - Fix the infinite loading spinner after an idle database connection is silently dropped, by detecting stale connections and offering a reconnect dialog.
|
||||
| `Issue #7346 <https://github.com/pgadmin-org/pgadmin4/issues/7346>`_ - Fixed an issue where preferences set via the CLI (setup.py set-prefs) were not validated, so invalid values were stored silently; CLI preference values are now validated against the preference type and rejected (and reported) if invalid.
|
||||
| `Issue #7596 <https://github.com/pgadmin-org/pgadmin4/issues/7596>`_ - Fix the Query Tool turning into a blank white screen when the runtime has a malformed default locale, by guarding the Query History date/time formatting against the resulting RangeError.
|
||||
| `Issue #8318 <https://github.com/pgadmin-org/pgadmin4/issues/8318>`_ - Fixed an error ("i.default.find(...) is undefined") that prevented deleting a table or relationship link in the ERD tool when a foreign key referenced a column that had been renamed.
|
||||
| `Issue #9060 <https://github.com/pgadmin-org/pgadmin4/issues/9060>`_ - Fixed an issue in the Create Table dialog where renaming a column did not update the column references in foreign key and unique constraint definitions for the new table.
|
||||
@@ -59,6 +62,7 @@ Bug fixes
|
||||
| `Issue #9595 <https://github.com/pgadmin-org/pgadmin4/issues/9595>`_ - Fix missing ALTER ... SET DEFAULT statements for inherited columns in the generated table SQL/EDIT script.
|
||||
| `Issue #9677 <https://github.com/pgadmin-org/pgadmin4/issues/9677>`_ - Fix the Unlogged table toggle in table properties not generating any ALTER TABLE ... SET LOGGED/UNLOGGED statement.
|
||||
| `Issue #9701 <https://github.com/pgadmin-org/pgadmin4/issues/9701>`_ - Ensure pgAdmin uses psycopg3 rather than psycopg2 when connecting to the configuration database via PGADMIN_CONFIG_CONFIG_DATABASE_URI.
|
||||
| `Issue #9744 <https://github.com/pgadmin-org/pgadmin4/issues/9744>`_ - Fix a View/Edit Data crash when the session contains a transaction object that is not filter-capable (e.g. left by the Query Tool or persisted by an older version), which could prevent the desktop application from loading after an upgrade.
|
||||
| `Issue #9762 <https://github.com/pgadmin-org/pgadmin4/issues/9762>`_ - Fix the "Cannot read properties of undefined (reading 'map')" crash in the desktop runtime when a menu refresh is triggered before the application menus have been received.
|
||||
| `Issue #9766 <https://github.com/pgadmin-org/pgadmin4/issues/9766>`_ - Fixed an issue where a server's custom foreground colour was not applied to the object counts (children count) and column type labels shown in the object explorer.
|
||||
| `Issue #9782 <https://github.com/pgadmin-org/pgadmin4/issues/9782>`_ - Optimise pgAdmin startup time by replacing the fixed server-ping interval with adaptive pinging that backs off when no servers are configured.
|
||||
@@ -106,7 +110,7 @@ Non-breaking ``dependabot`` updates aggregated for v9.16.
|
||||
Python:
|
||||
|
||||
| ``certifi`` 2026.4.22 -> 2026.5.20
|
||||
| ``cryptography`` 47.0 -> 48.0
|
||||
| ``cryptography`` 47.0 -> 49.0
|
||||
| ``Flask-Security-Too`` 5.4 -> 5.6 (``python_version <= '3.9'``)
|
||||
| ``google-auth-oauthlib`` 1.3.1 -> 1.4.0 (``python_version > '3.9'``; Python 3.9 stays on 1.3.1)
|
||||
| ``requests`` >=2.33.1 -> >=2.34.2 (``python_version > '3.9'``)
|
||||
@@ -124,6 +128,6 @@ JavaScript (``web/``):
|
||||
|
||||
JavaScript (``runtime/``):
|
||||
|
||||
| ``axios`` 1.16.0 -> 1.17.0
|
||||
| ``axios`` 1.16.0 -> 1.18.0
|
||||
| ``electron`` 41.5.0 -> 42.3.3
|
||||
| ``eslint`` 10.3.0 -> 10.4.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: v2
|
||||
name: pgadmin4-helm
|
||||
version: 9.15.0
|
||||
appVersion: 9.15.0
|
||||
version: 9.16.0
|
||||
appVersion: 9.16.0
|
||||
description: pgAdmin is the most popular and feature rich Open Source administration and development platform for PostgreSQL, the most advanced Open Source database in the world.
|
||||
home: https://pgadmin.org
|
||||
sources: ["https://github.com/pgadmin-org/pgadmin4/tree/master/helm"]
|
||||
|
||||
@@ -22,7 +22,7 @@ bcrypt==5.0.*
|
||||
boto3==1.42.*; python_version <= '3.9'
|
||||
boto3==1.43.*; python_version > '3.9'
|
||||
certifi==2026.5.20
|
||||
cryptography==48.0.*
|
||||
cryptography==49.0.*
|
||||
Flask-Babel==4.0.*
|
||||
Flask-Compress==1.*
|
||||
Flask-Login==0.*
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"globals": "^17.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.17.0",
|
||||
"axios": "^1.18.0",
|
||||
"electron-context-menu": "^4.1.2",
|
||||
"electron-store": "^11.0.2"
|
||||
}
|
||||
|
||||
@@ -288,15 +288,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^1.17.0":
|
||||
version: 1.17.0
|
||||
resolution: "axios@npm:1.17.0"
|
||||
"axios@npm:^1.18.0":
|
||||
version: 1.18.0
|
||||
resolution: "axios@npm:1.18.0"
|
||||
dependencies:
|
||||
follow-redirects: "npm:^1.16.0"
|
||||
form-data: "npm:^4.0.5"
|
||||
https-proxy-agent: "npm:^5.0.1"
|
||||
proxy-from-env: "npm:^2.1.0"
|
||||
checksum: 10c0/c4fa19ff3a3a63bde48beec03ad816b133b9a6385cccffffe172577ab18c6a70e299280d57f12c80c867fe25df41f92cb91d3a8258708a6d2be3e9e085f92650
|
||||
checksum: 10c0/f88aae13fd9dd395dea3053c7299178fee62f436f20424a1d6d79ec11ffb7656ba664b4630defbadab2c387b6ea07e29afb9f28b26d8e0e49d45be766983290d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1200,7 +1200,7 @@ __metadata:
|
||||
resolution: "pgadmin4@workspace:."
|
||||
dependencies:
|
||||
"@eslint/js": "npm:^10.0.1"
|
||||
axios: "npm:^1.17.0"
|
||||
axios: "npm:^1.18.0"
|
||||
electron: "npm:^42.3.3"
|
||||
electron-context-menu: "npm:^4.1.2"
|
||||
electron-store: "npm:^11.0.2"
|
||||
|
||||
@@ -46,9 +46,16 @@ export default function LoginPage({userLanguage, langOptions, forgotPassUrl, csr
|
||||
}
|
||||
{authSources?.includes?.(authSourcesEnum.OAUTH2) &&
|
||||
oauth2Config.map((oauth)=>{
|
||||
// Allow the configured icon to specify its own Font Awesome style
|
||||
// (e.g. 'fas fa-key'). Default to the brands style ('fab') when
|
||||
// only an icon name is given, for backward compatibility.
|
||||
const iconStyles = ['fab', 'fas', 'far', 'fal', 'fat', 'fad',
|
||||
'fa-brands', 'fa-solid', 'fa-regular', 'fa-light', 'fa-thin', 'fa-duotone'];
|
||||
const hasStyle = oauth.OAUTH2_ICON?.split(/\s+/).some((c)=>iconStyles.includes(c));
|
||||
const iconClassName = hasStyle ? oauth.OAUTH2_ICON : 'fab '+oauth.OAUTH2_ICON;
|
||||
return (
|
||||
<SecurityButton key={oauth.OAUTH2_NAME} name="oauth2_button" value={oauth.OAUTH2_NAME} style={{backgroundColor: oauth.OAUTH2_BUTTON_COLOR}}>
|
||||
<Icon className={'fab '+oauth.OAUTH2_ICON} style={{ fontSize: '1.5em', marginRight: '8px' }} />{gettext('Login with %s', oauth.OAUTH2_DISPLAY_NAME)}
|
||||
<Icon className={iconClassName} style={{ fontSize: '1.5em', marginRight: '8px' }} />{gettext('Login with %s', oauth.OAUTH2_DISPLAY_NAME)}
|
||||
</SecurityButton>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -321,7 +321,15 @@ def initialize_viewdata(trans_id, cmd_type, obj_type, sgid, sid, did, obj_id):
|
||||
if str(trans_id) in sql_grid_data:
|
||||
old_trans_obj = pickle.loads(
|
||||
sql_grid_data[str(trans_id)]['command_obj'])
|
||||
if old_trans_obj.did == did and old_trans_obj.obj_id == obj_id:
|
||||
# Only restore the filter/sorting when the previously stored object is
|
||||
# a filter-capable (View/Edit Data) command. The same trans_id may
|
||||
# have been used by a non-filter command such as the Query Tool, or by
|
||||
# an incompatible object persisted by an older version - neither
|
||||
# carries the _row_filter/_data_sorting attributes, and blindly
|
||||
# accessing them raises an AttributeError that prevents the tool (and,
|
||||
# in desktop mode, the application) from loading.
|
||||
if isinstance(old_trans_obj, SQLFilter) and \
|
||||
old_trans_obj.did == did and old_trans_obj.obj_id == obj_id:
|
||||
command_obj.set_filter(old_trans_obj._row_filter)
|
||||
command_obj.set_data_sorting(
|
||||
dict(data_sorting=old_trans_obj._data_sorting), True)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
import uuid
|
||||
import pickle
|
||||
import secrets
|
||||
from pgadmin.utils.route import BaseTestGenerator
|
||||
from pgadmin.browser.server_groups.servers.databases.tests import utils as \
|
||||
database_utils
|
||||
from regression import parent_node_dict
|
||||
from regression.python_test_utils import test_utils
|
||||
from pgadmin.tools.sqleditor.command import QueryToolCommand
|
||||
|
||||
|
||||
class TestViewDataRestoreStaleTransObj(BaseTestGenerator):
|
||||
"""
|
||||
Regression test for issue #9744.
|
||||
|
||||
When View/Edit Data initialises a transaction it tries to restore the
|
||||
filter/sorting from any command object previously stored in the session
|
||||
under the same trans_id. That stored object is not always a filter-capable
|
||||
(View/Edit Data) command - the same trans_id may have been used by the
|
||||
Query Tool, or the session may contain an incompatible object persisted by
|
||||
an older version after an upgrade. In those cases the object has no
|
||||
_row_filter/_data_sorting attributes, and blindly accessing them raised an
|
||||
AttributeError that returned a 500 and, in desktop mode, prevented the
|
||||
application from loading.
|
||||
|
||||
This test seeds the session with a pickled QueryToolCommand (which is what
|
||||
a restored Query Tool transaction leaves in session['gridData']) under the
|
||||
trans_id used to initialise View/Edit Data, and asserts the request
|
||||
succeeds instead of crashing.
|
||||
"""
|
||||
scenarios = [
|
||||
('Initialize View/Edit Data over a stale non-filter trans object',
|
||||
dict())
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
self.server_id = self.server_information['server_id']
|
||||
self.database_info = parent_node_dict["database"][-1]
|
||||
self.db_name = self.database_info["db_name"]
|
||||
self.db_id = self.database_info["db_id"]
|
||||
|
||||
self.connection = test_utils.get_db_connection(
|
||||
self.db_name,
|
||||
self.server['username'],
|
||||
self.server['db_password'],
|
||||
self.server['host'],
|
||||
self.server['port']
|
||||
)
|
||||
|
||||
db_con = database_utils.connect_database(self, test_utils.SERVER_GROUP,
|
||||
self.server_id, self.db_id)
|
||||
if not db_con["info"] == "Database connected.":
|
||||
raise Exception("Could not connect to the database.")
|
||||
|
||||
def runTest(self):
|
||||
self.table = "test_table_%s" % (str(uuid.uuid4())[1:8])
|
||||
# Note: do not give the primary key an explicit constraint name -
|
||||
# other tests in this package create tables in the same database, and
|
||||
# a hard-coded constraint name would collide across the suite. Letting
|
||||
# PostgreSQL auto-name it (<table>_pkey) keeps it unique.
|
||||
table_sql = """Create Table %s(
|
||||
id integer Not Null Primary Key
|
||||
);""" % self.table
|
||||
test_utils.create_table_with_query(self.server, self.db_name,
|
||||
table_sql)
|
||||
|
||||
# Fetch the table OID
|
||||
pg_cursor = self.connection.cursor()
|
||||
pg_cursor.execute("""Select oid FROM pg_catalog.pg_class WHERE
|
||||
relname = '%s' AND relkind IN ('r','s','t')""" % self.table)
|
||||
table_id = pg_cursor.fetchall()[0][0]
|
||||
|
||||
trans_id = str(secrets.choice(range(1, 9999999)))
|
||||
|
||||
# Build a QueryToolCommand - the kind of object the Query Tool stores
|
||||
# in session['gridData'] - and give it the same did/obj_id as the
|
||||
# table so the restore guard in initialize_viewdata is reached. A
|
||||
# QueryToolCommand has no _row_filter attribute, which is what
|
||||
# previously triggered the AttributeError.
|
||||
stale_obj = QueryToolCommand(
|
||||
sgid=test_utils.SERVER_GROUP, sid=self.server_id, did=self.db_id)
|
||||
stale_obj.obj_id = table_id
|
||||
|
||||
with self.tester.session_transaction() as sess:
|
||||
grid_data = sess.get('gridData', {})
|
||||
grid_data[trans_id] = {
|
||||
'command_obj': pickle.dumps(stale_obj, -1)
|
||||
}
|
||||
sess['gridData'] = grid_data
|
||||
|
||||
url = '/sqleditor/initialize/viewdata/{0}/3/table/{1}/{2}/{3}/{4}' \
|
||||
.format(trans_id, test_utils.SERVER_GROUP, self.server_id,
|
||||
self.db_id, table_id)
|
||||
response = self.tester.post(url)
|
||||
|
||||
# Before the fix this returned 500 with:
|
||||
# 'QueryToolCommand' object has no attribute '_row_filter'
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def tearDown(self):
|
||||
self.connection.cursor().execute(
|
||||
"DROP TABLE IF EXISTS %s" % self.table)
|
||||
self.connection.commit()
|
||||
self.connection.close()
|
||||
database_utils.disconnect_database(self, self.server_id,
|
||||
self.db_id)
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -165,12 +165,15 @@ class _Preference():
|
||||
|
||||
return False, None
|
||||
|
||||
def set(self, value):
|
||||
def set(self, value, user_id=None):
|
||||
"""
|
||||
set
|
||||
Set the value into the configuration table for this current user.
|
||||
Set the value into the configuration table for this current user
|
||||
(or the given user_id, when called outside a request context).
|
||||
|
||||
:param value: Value to be set
|
||||
:param user_id: User to set the preference for; defaults to the
|
||||
current user.
|
||||
|
||||
:returns: nothing.
|
||||
"""
|
||||
@@ -217,14 +220,15 @@ class _Preference():
|
||||
"Invalid value for {0} option.".format(
|
||||
error_map.get(self._type, self._type)))
|
||||
|
||||
uid = user_id if user_id is not None else current_user.id
|
||||
pref = UserPrefTable.query.filter_by(
|
||||
pid=self.pid
|
||||
).filter_by(uid=current_user.id).first()
|
||||
).filter_by(uid=uid).first()
|
||||
|
||||
value = "{}".format(value)
|
||||
if pref is None:
|
||||
pref = UserPrefTable(
|
||||
uid=current_user.id, pid=self.pid, value=value
|
||||
uid=uid, pid=self.pid, value=value
|
||||
)
|
||||
db.session.add(pref)
|
||||
else:
|
||||
@@ -597,30 +601,53 @@ class Preferences():
|
||||
@classmethod
|
||||
def save_cli(cls, mid, cid, pid, user_id, value):
|
||||
"""
|
||||
save
|
||||
Update the value for the preference in the configuration database.
|
||||
save_cli
|
||||
Validate and update the value for the preference in the
|
||||
configuration database for the given user (used by the CLI).
|
||||
|
||||
:param mid: Module ID
|
||||
:param cid: Category ID
|
||||
:param pid: Preference ID
|
||||
:param user_id: User to set the preference for
|
||||
:param value: Value for the options
|
||||
"""
|
||||
# Find the entry for this module in the configuration database.
|
||||
module = ModulePrefTable.query.filter_by(id=mid).first()
|
||||
|
||||
pref = UserPrefTable.query.filter_by(
|
||||
pid=pid
|
||||
).filter_by(uid=user_id).first()
|
||||
if module is None:
|
||||
return False, gettext("Could not find the specified module.")
|
||||
|
||||
value = "{}".format(value)
|
||||
if pref is None:
|
||||
pref = UserPrefTable(
|
||||
uid=user_id, pid=pid, value=value
|
||||
)
|
||||
db.session.add(pref)
|
||||
else:
|
||||
pref.value = value
|
||||
db.session.commit()
|
||||
m = cls.modules.get(module.name)
|
||||
if m is None:
|
||||
return False, gettext(
|
||||
"Module '{0}' is no longer in use."
|
||||
).format(module.name)
|
||||
|
||||
return True, None
|
||||
category = None
|
||||
for c in m.categories:
|
||||
cat = m.categories[c]
|
||||
if cid == cat['id']:
|
||||
category = cat
|
||||
break
|
||||
|
||||
if category is None:
|
||||
return False, gettext(
|
||||
"Module '{0}' does not have category with id '{1}'"
|
||||
).format(module.name, cid)
|
||||
|
||||
preference = None
|
||||
for p in category['preferences']:
|
||||
pref = (category['preferences'])[p]
|
||||
if pref.pid == pid:
|
||||
preference = pref
|
||||
break
|
||||
|
||||
if preference is None:
|
||||
return False, gettext("Could not find the specified preference.")
|
||||
|
||||
# Delegate to set() so the value is validated against the
|
||||
# preference type, just like the GUI path.
|
||||
return preference.set(value, user_id=user_id)
|
||||
|
||||
@classmethod
|
||||
def save(cls, mid, cid, pid, value):
|
||||
|
||||
16
web/setup.py
16
web/setup.py
@@ -728,6 +728,7 @@ class ManagePreferences:
|
||||
prefs = ManagePreferences.fetch_prefs(True)
|
||||
app = create_app(config.APP_NAME + '-cli')
|
||||
invalid_prefs = []
|
||||
invalid_value_prefs = []
|
||||
valid_prefs = []
|
||||
with app.app_context():
|
||||
from pgadmin.preferences import save_pref
|
||||
@@ -749,11 +750,13 @@ class ManagePreferences:
|
||||
'name': final_opt[2],
|
||||
'user_id': user_id,
|
||||
'value': val}
|
||||
save_pref(_row)
|
||||
valid_prefs.append(_row)
|
||||
if save_pref(_row):
|
||||
valid_prefs.append(_row)
|
||||
|
||||
if not json:
|
||||
table.add_row(jsonlib.dumps(_row))
|
||||
if not json:
|
||||
table.add_row(jsonlib.dumps(_row))
|
||||
else:
|
||||
invalid_value_prefs.append(f)
|
||||
else:
|
||||
invalid_prefs.append(f)
|
||||
|
||||
@@ -762,6 +765,11 @@ class ManagePreferences:
|
||||
(', ').join(
|
||||
invalid_prefs)))
|
||||
|
||||
if len(invalid_value_prefs) >= 1:
|
||||
print("Invalid value provided for preference(s) "
|
||||
"[red]{0}[/red].".format(
|
||||
(', ').join(invalid_value_prefs)))
|
||||
|
||||
if not json and console:
|
||||
print(table)
|
||||
elif json and console:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
# Application version number components
|
||||
APP_RELEASE = 9
|
||||
APP_REVISION = 15
|
||||
APP_REVISION = 16
|
||||
|
||||
# Application version suffix, e.g. 'beta1', 'dev'. Usually an empty string
|
||||
# for GA releases.
|
||||
@@ -27,7 +27,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 = 91500
|
||||
APP_VERSION_INT = 91600
|
||||
|
||||
# DO NOT CHANGE!
|
||||
# The application version string, constructed from the components
|
||||
|
||||
Reference in New Issue
Block a user