[Rt-commit] rt branch, 4.6/config-in-database, created. rt-4.4.1-255-g94b465b
Shawn Moore
shawn at bestpractical.com
Fri Sep 22 14:12:51 EDT 2017
The branch, 4.6/config-in-database has been created
at 94b465b1082b8fc4bfc2c958018e29991db919aa (commit)
- Log -----------------------------------------------------------------
commit 25b33fd335e241f75ac6cb9dcb233284a5e6a3b9
Merge: 3e15209 c566c73
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:18:12 2017 +0000
Merge branch '4.4/widget-improvements' into 4.6/config-in-database
commit 6b2146c091da3b44b13e6a9a67380f4b58018b73
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 18:59:45 2017 +0000
DatabaseSetting schema updates
diff --git a/etc/acl.Pg b/etc/acl.Pg
index 6558550..7c2a975 100644
--- a/etc/acl.Pg
+++ b/etc/acl.Pg
@@ -66,6 +66,8 @@ sub acl {
CustomRoles
objectcustomroles_id_seq
ObjectCustomRoles
+ databasesettings_id_seq
+ DatabaseSettings
);
my $db_user = RT->Config->Get('DatabaseUser');
diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index afa2c94..a54544a 100644
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -538,3 +538,20 @@ CREATE TABLE ObjectCustomRoles (
LastUpdated DATE
);
CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
+
+CREATE SEQUENCE DatabaseSettings_seq;
+CREATE TABLE DatabaseSettings (
+ id NUMBER(11,0) CONSTRAINT DatabaseSettings_key PRIMARY KEY,
+ Name VARCHAR2(255) CONSTRAINT DatabaseSettings_Name_Unique unique NOT NULL,
+ Content CLOB,
+ ContentType VARCHAR2(80),
+ Disabled NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (LOWER(Name));
+CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/schema.Pg b/etc/schema.Pg
index c758284..fb116da 100644
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -778,3 +778,21 @@ CREATE TABLE ObjectCustomRoles (
);
CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
+
+CREATE SEQUENCE databasesettings_id_seq;
+CREATE TABLE DatabaseSettings (
+ id integer DEFAULT nextval('databasesettings_id_seq'),
+ Name varchar(255) NOT NULL,
+ Content text NULL,
+ ContentType varchar(80) NULL,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (LOWER(Name));
+CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index 3288a57..007b1c4 100644
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -569,3 +569,19 @@ CREATE TABLE ObjectCustomRoles (
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
+
+CREATE TABLE DatabaseSettings (
+ id INTEGER PRIMARY KEY,
+ Name varchar(255) collate NOCASE NOT NULL,
+ Content longtext collate NOCASE NULL,
+ ContentType varchar(80) collate NOCASE NULL,
+ Disabled int2 NOT NULL DEFAULT 0,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL
+);
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (Name);
+CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/schema.mysql b/etc/schema.mysql
index 4baf28d..0cd1c69 100644
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -559,3 +559,20 @@ CREATE TABLE ObjectCustomRoles (
) ENGINE=InnoDB CHARACTER SET utf8;
CREATE UNIQUE INDEX ObjectCustomRoles1 ON ObjectCustomRoles (ObjectId, CustomRole);
+
+CREATE TABLE DatabaseSettings (
+ id int(11) NOT NULL AUTO_INCREMENT,
+ Name varchar(255) NOT NULL,
+ Content longblob NULL,
+ ContentType varchar(80) CHARACTER SET ascii NULL,
+ Disabled int2 NOT NULL DEFAULT 0,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created datetime DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated datetime DEFAULT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (Name);
+CREATE UNIQUE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/upgrade/4.5.0/acl.Pg b/etc/upgrade/4.5.0/acl.Pg
new file mode 100644
index 0000000..6a73f0c
--- /dev/null
+++ b/etc/upgrade/4.5.0/acl.Pg
@@ -0,0 +1,30 @@
+sub acl {
+ my $dbh = shift;
+
+ my @acls;
+ my @tables = qw (
+ databasesettings_id_seq
+ DatabaseSettings
+ );
+
+ my $db_user = RT->Config->Get('DatabaseUser');
+
+ my $sequence_right
+ = ( $dbh->{pg_server_version} >= 80200 )
+ ? "USAGE, SELECT, UPDATE"
+ : "SELECT, UPDATE";
+
+ foreach my $table (@tables) {
+ # Tables are upper-case, sequences are lowercase in @tables
+ if ( $table =~ /^[a-z]/ ) {
+ push @acls, "GRANT $sequence_right ON $table TO \"$db_user\";"
+ }
+ else {
+ push @acls, "GRANT SELECT, INSERT, UPDATE, DELETE ON $table TO \"$db_user\";"
+ }
+ }
+ return (@acls);
+}
+
+1;
+
diff --git a/etc/upgrade/4.5.0/schema.Oracle b/etc/upgrade/4.5.0/schema.Oracle
new file mode 100644
index 0000000..ad05bc7
--- /dev/null
+++ b/etc/upgrade/4.5.0/schema.Oracle
@@ -0,0 +1,16 @@
+CREATE SEQUENCE DatabaseSettings_seq;
+CREATE TABLE DatabaseSettings (
+ id NUMBER(11,0) CONSTRAINT DatabaseSettings_key PRIMARY KEY,
+ Name VARCHAR2(255) CONSTRAINT DatabaseSettings_Name_Unique unique NOT NULL,
+ Content CLOB,
+ ContentType VARCHAR2(80),
+ Disabled NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (LOWER(Name));
+CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/upgrade/4.5.0/schema.Pg b/etc/upgrade/4.5.0/schema.Pg
new file mode 100644
index 0000000..73f4d96
--- /dev/null
+++ b/etc/upgrade/4.5.0/schema.Pg
@@ -0,0 +1,17 @@
+CREATE SEQUENCE databasesettings_id_seq;
+CREATE TABLE DatabaseSettings (
+ id integer DEFAULT nextval('databasesettings_id_seq'),
+ Name varchar(255) NOT NULL,
+ Content text NULL,
+ ContentType varchar(80) NULL,
+ Disabled integer NOT NULL DEFAULT 0 ,
+ Creator integer NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL,
+ PRIMARY KEY (id)
+);
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (LOWER(Name));
+CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/upgrade/4.5.0/schema.SQLite b/etc/upgrade/4.5.0/schema.SQLite
new file mode 100644
index 0000000..a25a8c9
--- /dev/null
+++ b/etc/upgrade/4.5.0/schema.SQLite
@@ -0,0 +1,15 @@
+CREATE TABLE DatabaseSettings (
+ id INTEGER PRIMARY KEY,
+ Name varchar(255) collate NOCASE NOT NULL,
+ Content longtext collate NOCASE NULL,
+ ContentType varchar(80) collate NOCASE NULL,
+ Disabled int2 NOT NULL DEFAULT 0,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created timestamp DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated timestamp DEFAULT NULL
+);
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (Name);
+CREATE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
diff --git a/etc/upgrade/4.5.0/schema.mysql b/etc/upgrade/4.5.0/schema.mysql
new file mode 100644
index 0000000..177d01e
--- /dev/null
+++ b/etc/upgrade/4.5.0/schema.mysql
@@ -0,0 +1,16 @@
+CREATE TABLE DatabaseSettings (
+ id int(11) NOT NULL AUTO_INCREMENT,
+ Name varchar(255) NOT NULL,
+ Content longblob NULL,
+ ContentType varchar(80) CHARACTER SET ascii NULL,
+ Disabled int2 NOT NULL DEFAULT 0,
+ Creator int(11) NOT NULL DEFAULT 0,
+ Created datetime DEFAULT NULL,
+ LastUpdatedBy int(11) NOT NULL DEFAULT 0,
+ LastUpdated datetime DEFAULT NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE UNIQUE INDEX DatabaseSettings1 ON DatabaseSettings (Name);
+CREATE UNIQUE INDEX DatabaseSettings2 ON DatabaseSettings (Disabled);
+
commit f0352c3266e9ae96f97e86fdab8ab4cd3f9d1f11
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:01:37 2017 +0000
Add ORM classes for DatabaseSettings
diff --git a/lib/RT/DatabaseSetting.pm b/lib/RT/DatabaseSetting.pm
new file mode 100644
index 0000000..fc38e87
--- /dev/null
+++ b/lib/RT/DatabaseSetting.pm
@@ -0,0 +1,417 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
+# <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use strict;
+use warnings;
+use 5.10.1;
+
+package RT::DatabaseSetting;
+use base 'RT::Record';
+
+use Storable ();
+use MIME::Base64;
+use JSON ();
+
+=head1 NAME
+
+RT::DatabaseSetting - Represents a config setting
+
+=cut
+
+=head1 METHODS
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database. Available
+keys are:
+
+=over 4
+
+=item Name
+
+Must be unique.
+
+=item Content
+
+If you provide a reference, we will automatically serialize the data structure
+using L<Storable>. Otherwise any string is passed through as-is.
+
+=item ContentType
+
+Currently handles C<storable> or C<application/json>.
+
+=back
+
+Returns a tuple of (status, msg) on failure and (id, msg) on success.
+Also automatically propagates this config change to all server processes.
+
+=cut
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Name => '',
+ Content => '',
+ ContentType => '',
+ @_,
+ );
+
+ return (0, $self->loc("Permission Denied"))
+ unless $self->CurrentUserHasRight('SuperUser');
+
+ unless ( $args{'Name'} ) {
+ return ( 0, $self->loc("Must specify 'Name' attribute") );
+ }
+
+ my ( $id, $msg ) = $self->ValidateName( $args{'Name'} );
+ return ( 0, $msg ) unless $id;
+
+ my $meta = RT->Config->Meta($args{'Name'});
+ if ($meta->{Immutable}) {
+ return ( 0, $self->loc("You cannot update [_1] using database config; you must edit your site config", $args{'Name'}) );
+ }
+
+ if (ref ($args{'Content'}) ) {
+ ($args{'Content'}, my $error) = $self->_SerializeContent($args{'Content'}, $args{'Name'});
+ if ($error) {
+ return (0, $error);
+ }
+ $args{'ContentType'} = 'storable';
+ }
+
+ my $old_value = RT->Config->Get($args{Name});
+ unless (defined($old_value) && length($old_value)) {
+ $old_value = $self->loc('(no value)');
+ }
+
+ ( $id, $msg ) = $self->SUPER::Create(
+ map { $_ => $args{$_} } grep {exists $args{$_}}
+ qw(Name Content ContentType),
+ );
+ unless ($id) {
+ return (0, $self->loc("Setting [_1] to [_2] failed: [_3]", $args{Name}, $args{Content}, $msg));
+ }
+
+ RT->Config->ApplyConfigChangeToAllServerProcesses;
+
+ my ($content, $error) = $self->Content;
+ unless (defined($content) && length($content)) {
+ $content = $self->loc('(no value)');
+ }
+
+ if (!ref($content) && !ref($old_value)) {
+ RT->Logger->info($self->CurrentUser->Name . " changed " . $self->Name . " from " . $old_value . " to " . $content);
+ return ($id, $self->loc("[_1] changed from [_2] to [_3]", $self->Name, $old_value, $content));
+ }
+ else {
+ RT->Logger->info($self->CurrentUser->Name . " changed " . $self->Name);
+ return ($id, $self->loc("[_1] changed", $self->Name));
+ }
+}
+
+=head2 CurrentUserCanSee
+
+Returns true if the current user can see the database setting
+
+=cut
+
+sub CurrentUserCanSee {
+ my $self = shift;
+
+ return $self->CurrentUserHasRight('SuperUser');
+}
+
+=head2 Load
+
+Load a setting from the database. Takes a single argument. If the
+argument is numerical, load by the column 'id'. Otherwise, load by the
+"Name" column.
+
+=cut
+
+sub Load {
+ my $self = shift;
+ my $identifier = shift || return undef;
+
+ if ( $identifier !~ /\D/ ) {
+ return $self->SUPER::LoadById( $identifier );
+ } else {
+ return $self->LoadByCol( "Name", $identifier );
+ }
+}
+
+=head2 SetName
+
+Not permitted
+
+=cut
+
+sub SetName {
+ my $self = shift;
+ return (0, $self->loc("Permission Denied"));
+}
+
+=head2 ValidateName
+
+Returns either (0, "failure reason") or 1 depending on whether the given
+name is valid.
+
+=cut
+
+sub ValidateName {
+ my $self = shift;
+ my $name = shift;
+
+ return ( 0, $self->loc('empty name') ) unless defined $name && length $name;
+
+ my $TempSetting = RT::DatabaseSetting->new( RT->SystemUser );
+ $TempSetting->Load($name);
+
+ if ( $TempSetting->id && ( !$self->id || $TempSetting->id != $self->id ) ) {
+ return ( 0, $self->loc('Name in use') );
+ }
+ else {
+ return 1;
+ }
+}
+
+=head2 Delete
+
+Checks ACL, and on success propagates this config change to all server
+processes.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+ return (0, $self->loc("Permission Denied")) unless $self->CurrentUserCanSee;
+ my ($ok, $msg) = $self->SUPER::Delete(@_);
+ return ($ok, $msg) if !$ok;
+ RT->Config->ApplyConfigChangeToAllServerProcesses;
+ RT->Logger->info($self->CurrentUser->Name . " removed database setting for " . $self->Name);
+ return ($ok, $self->loc("Database setting removed."));
+}
+
+=head2 DecodedContent
+
+Returns a pair of this setting's content and any error.
+
+=cut
+
+sub DecodedContent {
+ my $self = shift;
+
+ # Here we call _Value to run the ACL check.
+ my $content = $self->_Value('Content');
+
+ my $type = $self->__Value('ContentType') || '';
+
+ if ($type eq 'storable') {
+ return $self->_DeserializeContent($content);
+ }
+ elsif ($type eq 'application/json') {
+ return $self->_DeJSONContent($content);
+ }
+
+ return ($content, "");
+}
+
+=head2 SetContent
+
+=cut
+
+sub SetContent {
+ my $self = shift;
+ my $value = shift;
+ my $content_type = shift || '';
+
+ return (0, $self->loc("Permission Denied")) unless $self->CurrentUserCanSee;
+
+ my ($old_value, $error) = $self->Content;
+ unless (defined($old_value) && length($old_value)) {
+ $old_value = $self->loc('(no value)');
+ }
+
+ if (ref $value) {
+ ($value, my $error) = $self->_SerializeContent($value);
+ if ($error) {
+ return (0, $error);
+ }
+ $content_type = 'storable';
+ }
+
+ $RT::Handle->BeginTransaction;
+
+ my ($ok, $msg) = $self->_Set( Field => 'Content', Value => $value );
+ if (!$ok) {
+ $RT::Handle->Rollback;
+ return ($ok, $self->loc("Unable to update [_1]: [_2]", $self->Name, $msg));
+ }
+
+ if ($self->ContentType ne $content_type) {
+ ($ok, $msg) = $self->_Set( Field => 'ContentType', Value => $content_type );
+ if (!$ok) {
+ $RT::Handle->Rollback;
+ return ($ok, $self->loc("Unable to update [_1]: [_2]", $self->Name, $msg));
+ }
+ }
+
+ $RT::Handle->Commit;
+ RT->Config->ApplyConfigChangeToAllServerProcesses;
+
+ unless (defined($value) && length($value)) {
+ $value = $self->loc('(no value)');
+ }
+
+ if (!ref($value) && !ref($old_value)) {
+ RT->Logger->info($self->CurrentUser->Name . " changed " . $self->Name . " from " . $old_value . " to " . $value);
+ return ($ok, $self->loc("[_1] changed from [_2] to [_3]", $self->Name, $old_value, $value));
+ } else {
+ RT->Logger->info($self->CurrentUser->Name . " changed " . $self->Name);
+ return ($ok, $self->loc("[_1] changed", $self->Name));
+ }
+}
+
+=head1 PRIVATE METHODS
+
+Documented for internal use only, do not call these from outside
+RT::DatabaseSetting itself.
+
+=head2 _Set
+
+Checks if the current user has I<SuperUser> before calling
+C<SUPER::_Set>, and then propagates this config change to all server processes.
+
+=cut
+
+sub _Set {
+ my $self = shift;
+ my %args = (
+ Field => undef,
+ Value => undef,
+ @_
+ );
+
+ return (0, $self->loc("Permission Denied"))
+ unless $self->CurrentUserCanSee;
+
+ my ($ok, $msg) = $self->SUPER::_Set(@_);
+ RT->Config->ApplyConfigChangeToAllServerProcesses;
+ return ($ok, $msg);
+}
+
+=head2 _Value
+
+Checks L</CurrentUserCanSee> before calling C<SUPER::_Value>.
+
+=cut
+
+sub _Value {
+ my $self = shift;
+ return unless $self->CurrentUserCanSee;
+ return $self->SUPER::_Value(@_);
+}
+
+sub _SerializeContent {
+ my $self = shift;
+ my $content = shift;
+ my $name = shift || $self->Name;
+ my $frozen = eval { encode_base64(Storable::nfreeze($content)) };
+
+ if (my $error = $@) {
+ $RT::Logger->error("Storable serialization of database setting $name failed: $error");
+ return (undef, $self->loc("Storable serialization of database setting [_1] failed: [_2]", $name, $error));
+ }
+
+ return $frozen;
+}
+
+sub _DeserializeContent {
+ my $self = shift;
+ my $content = shift;
+
+ my $thawed = eval { Storable::thaw(decode_base64($content)) };
+ if (my $error = $@) {
+ $RT::Logger->error("Storable deserialization of database setting " . $self->Name . " failed: $error");
+ return (undef, $self->loc("Storable deserialization of database setting [_1] failed: [_2]", $self->Name, $error));
+ }
+
+ return $thawed;
+}
+
+sub _DeJSONContent {
+ my $self = shift;
+ my $content = shift;
+
+ my $thawed = eval { JSON::from_json($content) };
+ if (my $error = $@) {
+ $RT::Logger->error("JSON deserialization of database setting " . $self->Name . " failed: $error");
+ return (undef, $self->loc("JSON deserialization of database setting [_1] failed: [_2]", $self->Name, $error));
+ }
+
+ return $thawed;
+}
+
+sub Table { "DatabaseSettings" }
+
+sub _CoreAccessible {
+ {
+ id => { read => 1, type => 'int(11)', default => '' },
+ Name => { read => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Content => { read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ ContentType => { read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ Disabled => { read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ Creator => { read => 1, type => 'int(11)', default => '0', auto => 1 },
+ Created => { read => 1, type => 'datetime', default => '', auto => 1 },
+ LastUpdatedBy => { read => 1, type => 'int(11)', default => '0', auto => 1 },
+ LastUpdated => { read => 1, type => 'datetime', default => '', auto => 1 },
+ }
+}
+
+1;
+
diff --git a/lib/RT/DatabaseSettings.pm b/lib/RT/DatabaseSettings.pm
new file mode 100644
index 0000000..3236bad
--- /dev/null
+++ b/lib/RT/DatabaseSettings.pm
@@ -0,0 +1,84 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
+# <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use strict;
+use warnings;
+
+package RT::DatabaseSettings;
+use base 'RT::SearchBuilder';
+
+=head1 NAME
+
+RT::DatabaseSettings - a collection of L<RT::DatabaseSettings> objects
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return RT::DatabaseSetting->new( $self->CurrentUser );
+}
+
+=head2 _Init
+
+Sets default ordering by id ascending.
+
+=cut
+
+sub _Init {
+ my $self = shift;
+
+ $self->{'with_disabled_column'} = 1;
+
+ $self->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+ return $self->SUPER::_Init( @_ );
+}
+
+sub Table { "DatabaseSettings" }
+
+1;
+
commit 8f42e1cfa868649a418caab535a223d4a0f9a88d
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:15:16 2017 +0000
Port database config loading and refreshing from extension
diff --git a/lib/RT.pm b/lib/RT.pm
index 95911ca..d80f3f1 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -195,6 +195,7 @@ sub Init {
ConnectToDatabase();
InitSystemObjects();
InitClasses(%args);
+ RT->Config->LoadConfigFromDatabase();
InitLogging();
InitPlugins();
_BuildTableAttributes();
@@ -485,6 +486,8 @@ sub InitClasses {
require RT::Asset;
require RT::Assets;
require RT::CustomFieldValues::Canonicalizer;
+ require RT::DatabaseSetting;
+ require RT::DatabaseSettings;
_BuildTableAttributes();
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index 3407796..f662824 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -55,6 +55,7 @@ use 5.010;
use File::Spec ();
use Symbol::Global::Name;
use List::MoreUtils 'uniq';
+use Storable;
=head1 NAME
@@ -1786,6 +1787,125 @@ sub EnableExternalAuth {
return;
}
+my $database_config_cache_time = 0;
+my %original_setting_from_files;
+my $in_config_change_txn = 0;
+
+sub BeginDatabaseConfigChanges {
+ $in_config_change_txn = $in_config_change_txn + 1;
+}
+
+sub EndDatabaseConfigChanges {
+ $in_config_change_txn = $in_config_change_txn - 1;
+ if (!$in_config_change_txn) {
+ shift->ApplyConfigChangeToAllServerProcesses();
+ }
+}
+
+sub ApplyConfigChangeToAllServerProcesses {
+ my $self = shift;
+
+ return if $in_config_change_txn;
+
+ # first apply locally
+ $self->LoadConfigFromDatabase();
+
+ # then notify other servers
+ RT->System->ConfigCacheNeedsUpdate($database_config_cache_time);
+}
+
+sub RefreshConfigFromDatabase {
+ my $self = shift;
+ if ($in_config_change_txn) {
+ RT->Logger->error("It appears that there were unbalanced calls to BeginDatabaseConfigChanges with EndDatabaseConfigChanges; this indicates a software fault");
+ $in_config_change_txn = 0;
+ }
+
+ my $needs_update = RT->System->ConfigCacheNeedsUpdate;
+ if ($needs_update > $database_config_cache_time) {
+ $self->LoadConfigFromDatabase();
+ $database_config_cache_time = $needs_update;
+ }
+}
+
+sub LoadConfigFromDatabase {
+ my $self = shift;
+
+ $database_config_cache_time = time;
+
+ my $settings = RT::DatabaseSettings->new(RT->SystemUser);
+ $settings->UnLimit;
+
+ my %seen;
+
+ while (my $setting = $settings->Next) {
+ my $name = $setting->Name;
+ my ($value, $error) = $setting->DecodedContent;
+ next if $error;
+
+ if (!exists $original_setting_from_files{$name}) {
+ $original_setting_from_files{$name} = [
+ scalar($self->Get($name)),
+ Storable::dclone(scalar($self->Meta($name))),
+ ];
+ }
+
+ $seen{$name}++;
+
+ # are we inadvertantly overriding RT_SiteConfig.pm?
+ my $meta = $META{$name};
+ if ($meta->{'Source'}) {
+ my %source = %{ $meta->{'Source'} };
+ if ($source{'SiteConfig'} && $source{'File'} ne 'database') {
+ RT->Logger->warning("Change of config option '$name' at $source{File} line $source{Line} has been overridden by the config setting from the database. Please remove it from $source{File} or from the database to avoid confusion.");
+ }
+ }
+
+ my $type = $meta->{Type} || 'SCALAR';
+
+ # hashes combine, but we don't want that behavior because the previous
+ # config settings will shadow any change that the database config makes
+ if ($type eq 'HASH') {
+ $self->Set($name, ());
+ }
+
+ my $val = $type eq 'ARRAY' ? $value
+ : $type eq 'HASH' ? [ %$value ]
+ : [ $value ];
+
+ $self->SetFromConfig(
+ Option => \$name,
+ Value => $val,
+ Package => 'N/A',
+ File => 'database',
+ Line => 'N/A',
+ SiteConfig => 1,
+ );
+ }
+
+ # anything that wasn't loaded from the database but has been set in
+ # %original_setting_from_files must have been disabled from the database,
+ # so we want to restore the original setting
+ for my $name (keys %original_setting_from_files) {
+ next if $seen{$name};
+
+ my ($value, $meta) = @{ $original_setting_from_files{$name} };
+ my $type = $meta->{Type} || 'SCALAR';
+
+ if ($type eq 'ARRAY') {
+ $self->Set($name, @$value);
+ }
+ elsif ($type eq 'HASH') {
+ $self->Set($name, %$value);
+ }
+ else {
+ $self->Set($name, $value);
+ }
+
+ %{ $META{$name} } = %$meta;
+ }
+}
+
RT::Base->_ImportOverlays();
1;
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index 734939a..bba3e3d 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -267,6 +267,8 @@ sub HandleRequest {
Module::Refresh->refresh;
}
+ RT->Config->RefreshConfigFromDatabase();
+
$HTML::Mason::Commands::r->content_type("text/html; charset=utf-8");
$HTML::Mason::Commands::m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];
diff --git a/lib/RT/System.pm b/lib/RT/System.pm
index f5c2d39..08d79dd 100644
--- a/lib/RT/System.pm
+++ b/lib/RT/System.pm
@@ -235,6 +235,27 @@ sub CustomRoleCacheNeedsUpdate {
}
}
+=head2 ConfigCacheNeedsUpdate ( 1 )
+
+Attribute to decide when we need to flush the database settings
+and re-register any changes. Set when settings are created, enabled/disabled, etc.
+
+If passed a true value, will update the attribute to be the current time.
+
+=cut
+
+sub ConfigCacheNeedsUpdate {
+ my $self = shift;
+ my $time = shift;
+
+ if ($time) {
+ return $self->SetAttribute(Name => 'ConfigCacheNeedsUpdate', Content => $time);
+ } else {
+ my $cache = $self->FirstAttribute('ConfigCacheNeedsUpdate');
+ return (defined $cache ? $cache->Content : 0 );
+ }
+}
+
=head2 AddUpgradeHistory package, data
Adds an entry to the upgrade history database. The package can be either C<RT>
commit 4dfa52c11c0492cdd2b744bf2687f673ae22379e
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:17:05 2017 +0000
Add Code and MultilineString widgets
diff --git a/share/html/Widgets/Form/Code b/share/html/Widgets/Form/Code
new file mode 100644
index 0000000..1c346d6
--- /dev/null
+++ b/share/html/Widgets/Form/Code
@@ -0,0 +1,57 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%DOC>
+see docs/extending/using_forms_widgets.pod
+</%DOC>
+<& /Widgets/Form/MultilineString, Class => 'code', %ARGS &>
+<%METHOD InputOnly>
+<& /Widgets/Form/MultilineString:InputOnly, %ARGS &>
+</%METHOD>
+<%METHOD Process>
+<& /Widgets/Form/MultilineString:Process, %ARGS &>
+</%METHOD>
diff --git a/share/html/Widgets/Form/MultilineString b/share/html/Widgets/Form/MultilineString
new file mode 100644
index 0000000..fa6a747
--- /dev/null
+++ b/share/html/Widgets/Form/MultilineString
@@ -0,0 +1,102 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%DOC>
+see docs/extending/using_forms_widgets.pod
+</%DOC>
+<div id="form-box-<% lc $Name %>" class="widget <% $Class %>">
+<span class="description label"><% $Description %></span>
+<span class="value"><& SELF:InputOnly, %ARGS &></span>
+% if ( $Default ) {
+<span class="comment"><% $DefaultLabel %></span>
+% }
+<span class="hints"><% $Hints %></span>
+</div>
+<%ARGS>
+$Name
+
+$Class => ''
+$Description => undef,
+$Hints => ''
+
+$CurrentValue => '',
+
+$Default => 0,
+$DefaultValue => '',
+$DefaultLabel => loc( 'Default: [_1]', $DefaultValue ),
+</%ARGS>
+
+<%METHOD InputOnly>
+<textarea name="<% $Name %>" cols="<% $Cols %>" rows="<% $Rows %>"><% $CurrentValue %></textarea>
+<%ARGS>
+$Name
+$Cols => 80
+$Rows => 6
+$CurrentValue => '',
+</%ARGS>
+</%METHOD>
+
+<%METHOD Process>
+<%ARGS>
+$Name
+
+$Arguments => {},
+
+$Default => 0,
+$DefaultValue => '',
+</%ARGS>
+<%INIT>
+my $value = $Arguments->{ $Name };
+$value = '' unless defined $value;
+
+if ( $value eq '' ) {
+ return $DefaultValue unless $Default;
+ return undef;
+}
+return $value;
+</%INIT>
+</%METHOD>
commit 538dd5e824cc6b0f087ba3f3b4a377b4ac83bea5
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:23:14 2017 +0000
Port EditConfig page from extension
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index d68ad6c..5043bf8 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -925,6 +925,11 @@ sub _BuildAdminMenu {
description => loc('Detailed information about your RT setup'),
path => '/Admin/Tools/Configuration.html',
);
+ $admin_tools->child( edit_config =>
+ title => loc('Edit Configuration'),
+ description => loc('Update your RT setup'),
+ path => '/Admin/Tools/EditConfig.html',
+ );
$admin_tools->child( theme =>
title => loc('Theme'),
description => loc('Customize the look of your RT'),
diff --git a/share/html/Admin/Tools/EditConfig.html b/share/html/Admin/Tools/EditConfig.html
new file mode 100644
index 0000000..c3185ed
--- /dev/null
+++ b/share/html/Admin/Tools/EditConfig.html
@@ -0,0 +1,270 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2016 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%INIT>
+my $title = loc('System Configuration');
+unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
+ Abort(loc('This feature is only available to system administrators'));
+}
+
+my $has_execute_code = $session{CurrentUser}->HasRight(Right => 'ExecuteCode', Object => RT->System);
+
+my @results;
+
+my $doc_version = $RT::VERSION;
+$doc_version =~ s/rc\d+//; # 4.4.2rc1 -> 4.4.2
+$doc_version =~ s/\.\d+-\d+-g\w+$//; # 4.4.3-1-g123 -> 4.4
+
+use Data::Dumper;
+my $stringify = sub {
+ my $value = shift;
+ return "" if !defined($value);
+
+ local $Data::Dumper::Terse = 1;
+ local $Data::Dumper::Indent = 2;
+ local $Data::Dumper::Sortkeys = 1;
+ my $output = Dumper $value;
+ chomp $output;
+ return $output;
+};
+
+if (delete $ARGS{Update}) {
+ RT->Config->BeginDatabaseConfigChanges;
+ $RT::Handle->BeginTransaction;
+ my $has_error;
+
+ eval {
+ for my $key (keys %ARGS) {
+ next if $key =~ /-Current$/;
+
+ my $meta = RT->Config->Meta( $key );
+ my $widget = $meta->{Widget} || '/Widgets/Form/Code';
+ my $is_code = $widget eq '/Widgets/Form/Code';
+
+ my $val = $ARGS{$key};
+ $val = '' if $val eq '__empty_value__';
+ my $prev = $ARGS{$key . '-Current'};
+ next if $val eq $prev;
+
+ # for bools, check for truthiness since 0, '', and undef are equivalent
+ if ($widget eq '/Widgets/Form/Boolean') {
+ next if !!$val eq !!$prev;
+ }
+
+ if ( $meta->{Immutable} || $meta->{Obfuscate} || ($key =~ /Password/i and $key !~ /MinimumPasswordLength|AllowLoginPasswordAutoComplete/ )) {
+ push @results, loc("Cannot change [_1]: Permission Denied", $key);
+ $has_error++;
+ next;
+ }
+
+ if ($is_code) {
+ if (!$has_execute_code) {
+ push @results, loc("Cannot change [_1]: Permission Denied", $key);
+ $has_error++;
+ next;
+ }
+
+ my $code = $val;
+ my $coderef;
+ # similar to RT::Scrip::CompileCheck
+ do {
+ no strict 'vars';
+ $coderef = eval "sub { $code \n }";
+ };
+ if ($@) {
+ my $error = $@;
+ push @results, loc("Couldn't compile [_1] codeblock '[_2]': [_3]", $key, $code, $error);
+ $has_error++;
+ next;
+ }
+
+ if ($coderef) {
+ $val = eval { $coderef->() };
+ if ($@) {
+ my $error = $@;
+ push @results, loc("Couldn't execute [_1] codeblock '[_2]': [_3]", $key, $code, $error);
+ $has_error++;
+ next;
+ }
+ }
+ }
+
+ my $setting = RT::DatabaseSetting->new($session{CurrentUser});
+ $setting->Load($key);
+ if ($setting->Id) {
+ if ($setting->Disabled) {
+ $setting->SetDisabled(0);
+ }
+
+ my ($ok, $msg) = $setting->SetContent($val);
+ push @results, $msg;
+ $has_error++ if !$ok;
+ }
+ else {
+ my ($ok, $msg) = $setting->Create(
+ Name => $key,
+ Content => $val,
+ );
+ push @results, $msg;
+ $has_error++ if !$ok;
+ }
+ }
+ };
+
+ if ($@) {
+ push @results, $@;
+ $has_error++;
+ }
+
+ if ($has_error) {
+ push @results, loc("No changes made.");
+ $RT::Handle->Rollback;
+ }
+ else {
+ $RT::Handle->Commit;
+ }
+ RT->Config->EndDatabaseConfigChanges;
+}
+
+</%INIT>
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form id="EditConfig" method="post" action="EditConfig.html">
+<input type="hidden" name="Update" value=1></input>
+
+<&|/Widgets/TitleBox, title => loc("RT Configuration") &>
+<table border="0" cellspacing="0" cellpadding="5" width="100%" class="collection">
+<tr class="collection-as-table">
+<th class="collection-as-table"><&|/l&>Option</&></th>
+<th class="collection-as-table"><&|/l&>Value</&></th>
+</tr>
+<%PERL>
+my $index_conf;
+foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
+ my $meta = RT->Config->Meta( $key );
+
+ next if $meta->{Invisible};
+
+ my $raw_value = RT->Config->Get( $key );
+ my $val = $stringify->($raw_value);
+
+ $index_conf++;
+
+ my $doc_url = "https://docs.bestpractical.com/rt/$doc_version/RT_Config.html#$key";
+
+ my $widget = $meta->{'Widget'} || '/Widgets/Form/Code';
+ my $is_code = $widget eq '/Widgets/Form/Code';
+ my $is_password = ($key =~ /Password/i and $key !~ /MinimumPasswordLength|AllowLoginPasswordAutoComplete/ );
+ my $is_immutable = $meta->{Immutable}
+ || $meta->{Obfuscate}
+ || ($is_code && $val =~ s/sub { "DUMMY" }/sub { ... }/g)
+ || ($is_code && !$has_execute_code);
+
+ my $current_value = $is_code ? $val : $raw_value;
+ my $args = $meta->{'WidgetArguments'} || {};
+
+ if ($widget eq '/Widgets/Form/Boolean') {
+ %$args = (
+ Default => 0,
+ RadioStyle => 1,
+ %$args,
+ );
+ }
+ elsif ($widget eq '/Widgets/Form/String' || $widget eq '/Widgets/Form/Integer') {
+ %$args = (
+ Size => 60,
+ %$args,
+ );
+ }
+
+</%PERL>
+<tr class="<% $key %> <% $index_conf%2 ? 'oddline' : 'evenline'%>">
+<td class="collection-as-table"><a href="<% $doc_url %>" target="_blank"><% $key %></a></td>
+<td class="collection-as-table">
+
+% if ( $meta->{EditLink} ) {
+<&|/l_unsafe, "<a href=\"$meta->{EditLink}\">", loc($meta->{EditLinkLabel}), "</a>" &>Visit [_1][_2][_3] to manage this setting</&>
+% } elsif ( $key =~ /Plugins/) {
+<ul class="plugins">
+% for my $plugin (RT->Config->Get($key)) {
+<li><a href="https://metacpan.org/search?q=<% $plugin |u %>" target="_blank"><% $plugin %></a></li>
+% }
+</ul>
+<br><em><% loc('Must modify in config file' ) %></em>
+% } elsif ( $is_password ) {
+<em><% loc('Must modify in config file' ) %></em>
+% } elsif ( $is_immutable ) {
+% if ($widget eq '/Widgets/Form/MultilineString' || $widget eq '/Widgets/Form/Code') {
+<textarea disabled class="<% $is_code ? 'code' : '' %>" rows="6" cols="80"><% $current_value %></textarea>
+% } else {
+<input type="text" disabled width="80" value="<% $current_value %>"></input>
+% }
+<br><em><% loc('Must modify in config file' ) %></em>
+% } else {
+ <& $widget,
+ Default => 1,
+ DefaultValue => '',
+ DefaultLabel => '(no value)',
+
+ %{ $m->comp('/Widgets/FinalizeWidgetArguments', WidgetArguments => $args ) },
+ Name => $key,
+ CurrentValue => $current_value,
+ Description => '',
+ Hints => '',
+ &>
+<textarea class="hidden" name="<% $key %>-Current"><% $current_value %></textarea>
+% }
+</td>
+</tr>
+% }
+</table>
+</&>
+<& /Elements/Submit, Label => loc('Save Changes') &>
+</form>
+
diff --git a/share/static/css/base/forms.css b/share/static/css/base/forms.css
index 2584ee0..3fe379e 100644
--- a/share/static/css/base/forms.css
+++ b/share/static/css/base/forms.css
@@ -280,3 +280,23 @@ ul.selectable a {
font-weight: bold;
text-decoration: underline;
}
+
+/* remove unnecessary left padding for radio options */
+#EditConfig div.widget .label {
+ width: auto;
+ float: none;
+}
+
+#EditConfig textarea:disabled,
+#EditConfig input:disabled {
+ background-color: #EEE;
+}
+
+.widget.code textarea,
+textarea.code {
+ font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
+}
+
+#EditConfig ul.plugins {
+ margin: 0;
+}:
commit a93c15b2d6f11729c3bfa90d7079c89b6ba76b97
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:33:14 2017 +0000
Annotate Immutable options
These options cannot be changed in database settings
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index f662824..e87a6ea 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -615,6 +615,7 @@ our %META;
# this tends to break extensions that stash links in ticket update pages
Organization => {
Type => 'SCALAR',
+ Immutable => 1,
PostLoadCheck => sub {
my ($self,$value) = @_;
$RT::Logger->error("your \$Organization setting ($value) appears to contain whitespace. Please fix this.")
@@ -622,9 +623,38 @@ our %META;
},
},
+ rtname => {
+ Immutable => 1,
+ },
+
# Internal config options
DatabaseExtraDSN => {
- Type => 'HASH',
+ Type => 'HASH',
+ Immutable => 1,
+ },
+ DatabaseAdmin => {
+ Immutable => 1,
+ },
+ DatabaseHost => {
+ Immutable => 1,
+ },
+ DatabaseName => {
+ Immutable => 1,
+ },
+ DatabasePassword => {
+ Immutable => 1,
+ },
+ DatabasePort => {
+ Immutable => 1,
+ },
+ DatabaseRTHost => {
+ Immutable => 1,
+ },
+ DatabaseType => {
+ Immutable => 1,
+ },
+ DatabaseUser => {
+ Immutable => 1,
},
FullTextSearch => {
@@ -718,8 +748,24 @@ our %META;
Type => 'SCALAR',
PostLoadCheck => sub { RT::Interface::Email->_HTMLFormatter },
},
+ Plugins => {
+ Immutable => 1,
+ },
+ RecordBaseClass => {
+ Immutable => 1,
+ },
+ WebSessionClass => {
+ Immutable => 1,
+ },
+ DevelMode => {
+ Immutable => 1,
+ },
+ DisallowExecuteCode => {
+ Immutable => 1,
+ },
MailPlugins => {
Type => 'ARRAY',
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
@@ -845,6 +891,7 @@ our %META;
EmailDashboardLanguageOrder => { Type => 'ARRAY' },
CustomFieldValuesCanonicalizers => { Type => 'ARRAY' },
WebPath => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -871,6 +918,7 @@ our %META;
},
},
WebDomain => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -897,6 +945,7 @@ our %META;
},
},
WebPort => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -912,6 +961,7 @@ our %META;
},
},
WebBaseURL => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -935,6 +985,7 @@ our %META;
},
},
WebURL => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -1049,7 +1100,12 @@ our %META;
},
},
+ ExternalAuth => {
+ Immutable => 1,
+ },
+
ExternalSettings => {
+ Immutable => 1,
Obfuscate => sub {
# Ensure passwords are obfuscated on the System Configuration page
my ($config, $sources, $user) = @_;
@@ -1113,6 +1169,7 @@ our %META;
},
ExternalAuthPriority => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my @values = @{ shift || [] };
@@ -1141,6 +1198,7 @@ our %META;
},
ExternalInfoPriority => {
+ Immutable => 1,
PostLoadCheck => sub {
my $self = shift;
my @values = @{ shift || [] };
commit f25747d25b8d8004c7f8bb480ffac43f93a46ebb
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:40:29 2017 +0000
Add widget metadata for config options
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index e87a6ea..806f3e2 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -616,6 +616,7 @@ our %META;
Organization => {
Type => 'SCALAR',
Immutable => 1,
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub {
my ($self,$value) = @_;
$RT::Logger->error("your \$Organization setting ($value) appears to contain whitespace. Please fix this.")
@@ -625,6 +626,7 @@ our %META;
rtname => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
# Internal config options
@@ -634,27 +636,35 @@ our %META;
},
DatabaseAdmin => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DatabaseHost => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DatabaseName => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DatabasePassword => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DatabasePort => {
Immutable => 1,
+ Widget => '/Widgets/Form/Integer',
},
DatabaseRTHost => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DatabaseType => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DatabaseUser => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
FullTextSearch => {
@@ -713,6 +723,7 @@ our %META;
},
DisableGraphViz => {
Type => 'SCALAR',
+ Widget => '/Widgets/Form/Boolean',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -724,6 +735,7 @@ our %META;
},
DisableGD => {
Type => 'SCALAR',
+ Widget => '/Widgets/Form/Boolean',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -735,6 +747,7 @@ our %META;
},
MailCommand => {
Type => 'SCALAR',
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub {
my $self = shift;
my $value = $self->Get('MailCommand');
@@ -746,6 +759,7 @@ our %META;
},
HTMLFormatter => {
Type => 'SCALAR',
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub { RT::Interface::Email->_HTMLFormatter },
},
Plugins => {
@@ -753,15 +767,19 @@ our %META;
},
RecordBaseClass => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
WebSessionClass => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
},
DevelMode => {
Immutable => 1,
+ Widget => '/Widgets/Form/Boolean',
},
DisallowExecuteCode => {
Immutable => 1,
+ Widget => '/Widgets/Form/Boolean',
},
MailPlugins => {
Type => 'ARRAY',
@@ -892,6 +910,7 @@ our %META;
CustomFieldValuesCanonicalizers => { Type => 'ARRAY' },
WebPath => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -918,7 +937,8 @@ our %META;
},
},
WebDomain => {
- Immutable => 1,
+ Immutable => 1,
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -946,6 +966,7 @@ our %META;
},
WebPort => {
Immutable => 1,
+ Widget => '/Widgets/Form/Integer',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -962,6 +983,7 @@ our %META;
},
WebBaseURL => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -986,6 +1008,7 @@ our %META;
},
WebURL => {
Immutable => 1,
+ Widget => '/Widgets/Form/String',
PostLoadCheck => sub {
my $self = shift;
my $value = shift;
@@ -1102,6 +1125,7 @@ our %META;
ExternalAuth => {
Immutable => 1,
+ Widget => '/Widgets/Form/Boolean',
},
ExternalSettings => {
@@ -1232,6 +1256,357 @@ our %META;
$self->Set( 'ExternalInfoPriority', \@values );
},
},
+
+ AllowUserAutocompleteForUnprivileged => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ AlwaysDownloadAttachments => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ AmbiguousDayInFuture => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ AmbiguousDayInPast => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ApprovalRejectionNotes => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ArticleOnTicketCreate => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ AutocompleteOwnersForSearch => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ CanonicalizeRedirectURLs => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ CanonicalizeURLsInFeeds => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ChartsTimezonesInDB => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ CheckMoreMSMailHeaders => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ DateDayBeforeMonth => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ DontSearchFileAttachments => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ DropLongAttachments => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ EditCustomFieldsSingleColumn => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ EnableReminders => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ExternalStorageDirectLink => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ForceApprovalsView => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ForwardFromUser => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ Framebusting => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ HideArticleSearchOnReplyCreate => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ HideResolveActionsWithDependencies => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ HideTimeFieldsFromUnprivilegedUsers => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ LoopsToRTOwner => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ MessageBoxIncludeSignature => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ MessageBoxIncludeSignatureOnComment => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ OnlySearchActiveTicketsInSimpleSearch => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ParseNewMessageForTicketCcs => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ PreferDateTimeFormatNatural => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ PreviewScripMessages => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ RecordOutgoingEmail => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ RestrictLoginReferrer => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ RestrictReferrer => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ SearchResultsAutoRedirect => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ShowBccHeader => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ShowMoreAboutPrivilegedUsers => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ShowRTPortal => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ShowRemoteImages => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ShowTransactionImages => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ StoreLoops => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ StrictLinkACL => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ SuppressInlineTextFiles => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ TruncateLongAttachments => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ TrustHTMLAttachments => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ UseFriendlyFromLine => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ UseFriendlyToLine => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ UseOriginatorHeader => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ UseSQLForACLChecks => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ UseTransactionBatch => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ValidateUserEmailAddresses => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebFallbackToRTLogin => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebFlushDbCacheEveryRequest => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebHttpOnlyCookies => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebRemoteUserAuth => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebRemoteUserAutocreate => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebRemoteUserContinuous => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebRemoteUserGecos => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WebSecureCookies => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ WikiImplicitLinks => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ HideOneTimeSuggestions => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ LinkArticlesOnInclude => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ SelfServiceCorrespondenceOnly => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+ ShowSearchResultCount => {
+ Widget => '/Widgets/Form/Boolean',
+ },
+
+ AttachmentListCount => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ AutoLogoff => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ BcryptCost => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ DefaultSummaryRows => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ ExternalStorageCutoffSize => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ LogoutRefresh => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ MaxAttachmentSize => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ MaxFulltextAttachmentSize => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ MinimumPasswordLength => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ MoreAboutRequestorGroupsLimit => {
+ Widget => '/Widgets/Form/Integer',
+ },
+ TicketsItemMapSize => {
+ Widget => '/Widgets/Form/Integer',
+ },
+
+ CommentAddress => {
+ Widget => '/Widgets/Form/String',
+ },
+ CorrespondAddress => {
+ Widget => '/Widgets/Form/String',
+ },
+ DashboardAddress => {
+ Widget => '/Widgets/Form/String',
+ },
+ DashboardSubject => {
+ Widget => '/Widgets/Form/String',
+ },
+ DefaultErrorMailPrecedence => {
+ Widget => '/Widgets/Form/String',
+ },
+ DefaultMailPrecedence => {
+ Widget => '/Widgets/Form/String',
+ },
+ DefaultSearchResultOrderBy => {
+ Widget => '/Widgets/Form/String',
+ },
+ EmailOutputEncoding => {
+ Widget => '/Widgets/Form/String',
+ },
+ FriendlyFromLineFormat => {
+ Widget => '/Widgets/Form/String',
+ },
+ FriendlyToLineFormat => {
+ Widget => '/Widgets/Form/String',
+ },
+ LogDir => {
+ Widget => '/Widgets/Form/String',
+ },
+ LogToFileNamed => {
+ Widget => '/Widgets/Form/String',
+ },
+ LogoAltText => {
+ Widget => '/Widgets/Form/String',
+ },
+ LogoLinkURL => {
+ Widget => '/Widgets/Form/String',
+ },
+ LogoURL => {
+ Widget => '/Widgets/Form/String',
+ },
+ OwnerEmail => {
+ Widget => '/Widgets/Form/String',
+ },
+ RedistributeAutoGeneratedMessages => {
+ Widget => '/Widgets/Form/String',
+ },
+ SendmailArguments => {
+ Widget => '/Widgets/Form/String',
+ },
+ SendmailBounceArguments => {
+ Widget => '/Widgets/Form/String',
+ },
+ SendmailPath => {
+ Widget => '/Widgets/Form/String',
+ },
+ SetOutgoingMailFrom => {
+ Widget => '/Widgets/Form/String',
+ },
+ Timezone => {
+ Widget => '/Widgets/Form/String',
+ },
+ WebImagesURL => {
+ Widget => '/Widgets/Form/String',
+ },
+
+ AssetSearchFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ AssetSummaryFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ AssetSummaryRelatedTicketsFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ DefaultSearchResultFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ DefaultSelfServiceSearchResultFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ MoreAboutRequestorExtraInfo => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ MoreAboutRequestorTicketListFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ UserSearchResultFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ UserSummaryExtraInfo => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+ UserSummaryTicketListFormat => {
+ Widget => '/Widgets/Form/MultilineString',
+ },
+
+ LogToSyslog => {
+ Widget => '/Widgets/Form/Select',
+ WidgetArguments => { Values => [qw(debug info notice warning error critical alert emergency)] },
+ },
+ LogToSTDERR => {
+ Widget => '/Widgets/Form/Select',
+ WidgetArguments => { Values => [qw(debug info notice warning error critical alert emergency)] },
+ },
+ LogToFile => {
+ Widget => '/Widgets/Form/Select',
+ WidgetArguments => { Values => [qw(debug info notice warning error critical alert emergency)] },
+ },
+ LogStackTraces => {
+ Widget => '/Widgets/Form/Select',
+ WidgetArguments => { Values => [qw(debug info notice warning error critical alert emergency)] },
+ },
+ StatementLog => {
+ Widget => '/Widgets/Form/Select',
+ WidgetArguments => { Values => [qw(debug info notice warning error critical alert emergency)] },
+ },
+
+ DefaultSearchResultOrder => {
+ Widget => '/Widgets/Form/Select',
+ WidgetArguments => { Values => [qw(ASC DESC)] },
+ },
);
my %OPTIONS = ();
my @LOADED_CONFIGS = ();
commit c4a4560825d8a1ebb785aff5f8d004ae44315d89
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:54:10 2017 +0000
Hide deprecated options
diff --git a/share/html/Admin/Tools/EditConfig.html b/share/html/Admin/Tools/EditConfig.html
index c3185ed..c606d5e 100644
--- a/share/html/Admin/Tools/EditConfig.html
+++ b/share/html/Admin/Tools/EditConfig.html
@@ -189,7 +189,7 @@ my $index_conf;
foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
my $meta = RT->Config->Meta( $key );
- next if $meta->{Invisible};
+ next if $meta->{Invisible} || $meta->{Deprecated};
my $raw_value = RT->Config->Get( $key );
my $val = $stringify->($raw_value);
commit 1c08a82bd4d49f56bacc7bbf22d1702f3cbe30e9
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 19:54:47 2017 +0000
Mark ConfigInDatabase as cored
diff --git a/lib/RT.pm b/lib/RT.pm
index d80f3f1..7aee53a 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -742,6 +742,8 @@ our %CORED_PLUGINS = (
'RT::Extension::SpawnLinkedTicketInQueue' => '4.4',
'RT::Extension::ParentTimeWorked' => '4.4',
'RT::Extension::FutureMailgate' => '4.4',
+
+ 'RT::Extension::ConfigInDatabase' => '4.6',
);
sub InitPlugins {
commit 8ce972c3ba4a8ad6507a3cc4e734d79eb78c767e
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Aug 15 20:08:02 2017 +0000
List Database as source of configuration on Sys Config page
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index 806f3e2..6b34afb 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -2062,7 +2062,7 @@ sub SetFromConfig {
}
$META{$name}->{'Type'} = $type;
- foreach (qw(Package File Line SiteConfig Extension)) {
+ foreach (qw(Package File Line SiteConfig Extension Database)) {
$META{$name}->{'Source'}->{$_} = $args{$_};
}
$self->Set( $name, @{ $args{'Value'} } );
@@ -2312,6 +2312,7 @@ sub LoadConfigFromDatabase {
Package => 'N/A',
File => 'database',
Line => 'N/A',
+ Database => 1,
SiteConfig => 1,
);
}
diff --git a/share/html/Admin/Tools/Configuration.html b/share/html/Admin/Tools/Configuration.html
index 26ffcc8..1cb918d 100644
--- a/share/html/Admin/Tools/Configuration.html
+++ b/share/html/Admin/Tools/Configuration.html
@@ -70,7 +70,10 @@ foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
my $meta = RT->Config->Meta( $key );
my $description = '';
- if ( $meta->{'Source'}{'Extension'} && $meta->{'Source'}{'SiteConfig'} ) {
+ if ( $meta->{'Source'}{'Database'}) {
+ $description = loc("database");
+ }
+ elsif ( $meta->{'Source'}{'Extension'} && $meta->{'Source'}{'SiteConfig'} ) {
$description = loc("[_1] site config", $meta->{'Source'}{'Extension'});
}
elsif ( $meta->{'Source'}{'Extension'} ) {
commit 94b465b1082b8fc4bfc2c958018e29991db919aa
Author: Shawn M Moore <shawn at bestpractical.com>
Date: Tue Sep 5 14:56:57 2017 -0400
Render config with EditLink as readonly
That way you can still easily export the settings to another RT
diff --git a/share/html/Admin/Tools/EditConfig.html b/share/html/Admin/Tools/EditConfig.html
index c606d5e..62adc78 100644
--- a/share/html/Admin/Tools/EditConfig.html
+++ b/share/html/Admin/Tools/EditConfig.html
@@ -229,6 +229,11 @@ foreach my $key ( RT->Config->Options( Overridable => undef, Sorted => 0 ) ) {
<td class="collection-as-table">
% if ( $meta->{EditLink} ) {
+% if ($widget eq '/Widgets/Form/MultilineString' || $widget eq '/Widgets/Form/Code') {
+<textarea disabled class="<% $is_code ? 'code' : '' %>" rows="6" cols="80"><% $current_value %></textarea><br>
+% } else {
+<input type="text" disabled width="80" value="<% $current_value %>"></input><br>
+% }
<&|/l_unsafe, "<a href=\"$meta->{EditLink}\">", loc($meta->{EditLinkLabel}), "</a>" &>Visit [_1][_2][_3] to manage this setting</&>
% } elsif ( $key =~ /Plugins/) {
<ul class="plugins">
-----------------------------------------------------------------------
More information about the rt-commit
mailing list