Skip to content

Commit

Permalink
Merge branch 'lua' into menu
Browse files Browse the repository at this point in the history
Conflicts:
	lib/GADS/Instances.pm
	lib/GADS/View.pm
  • Loading branch information
Andy Beverley committed Sep 2, 2017
2 parents e054dae + c83f2b4 commit 9d1994e
Show file tree
Hide file tree
Showing 18 changed files with 875 additions and 275 deletions.
249 changes: 249 additions & 0 deletions bin/move-layout.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
#!/usr/bin/perl

=pod
GADS - Globally Accessible Data Store
Copyright (C) 2017 Ctrl O Ltd
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
=cut

use FindBin;
use lib "$FindBin::Bin/../lib";

use Algorithm::Dependency::Ordered;
use Algorithm::Dependency::Source::HoA;
use Dancer2;
use Dancer2::Plugin::DBIC;
use GADS::Config;
use GADS::Layout;
use GADS::Views;
use Getopt::Long qw(:config pass_through);
use String::CamelCase qw(camelize);
use YAML::XS qw/LoadFile DumpFile/;

GADS::Config->instance(
config => config,
);

my ($instance_id, $site_id, $load_file, $dump_file, $global_views);
GetOptions (
'instance-id=s' => \$instance_id,
'site-id=s' => \$site_id,
'load-file=s' => \$load_file,
'dump-file=s' => \$dump_file,
'global-views' => \$global_views,
) or exit;

$instance_id or die "Need --instance-id";
$site_id or die "Need --site-id";
$load_file || $dump_file or die "Need either --load-file or --dump-file";

schema->site_id($site_id);

my $layout = GADS::Layout->new(
user_permission_override => 1,
user => undef,
instance_id => $instance_id,
config => config,
schema => schema,
);

my $views = GADS::Views->new(
user => undef,
schema => schema,
layout => $layout,
instance_id => $instance_id,
);

sub write_props
{ my ($field, $new) = @_;
$field->name ($new->{name});
$field->name_short ($new->{name_short});
$field->type ($new->{type});
$field->optional ($new->{optional});
$field->remember ($new->{remember});
$field->isunique ($new->{isunique});
$field->textbox ($new->{textbox})
if $field->can('textbox');
$field->typeahead ($new->{typeahead})
if $field->can('typeahead');
$field->force_regex ($new->{force_regex} || '')
if $field->can('force_regex');
$field->position ($new->{position});
$field->ordering ($new->{ordering});
$field->end_node_only ($new->{end_node_only})
if $field->can('end_node_only');
$field->multivalue ($new->{multivalue});
$field->description ($new->{description});
$field->helptext ($new->{helptext});
$field->display_field ($new->{display_field});
$field->display_regex ($new->{display_regex});
$field->link_parent_id($new->{link_parent_id});
$field->filter->as_json($new->{filter});
$field->_set_options ($new->{options});
$field->enumvals ($new->{enumvals})
if $field->type eq 'enum';
if ($field->type eq 'curval')
{
$field->curval_field_ids($new->{curval_field_ids});
my ($random) = @{$field->curval_field_ids};
$field->refers_to_instance($layout->column($random)->layout->instance_id);
}
$field->write(no_cache_update => 1, create_missing_id => 1); # Create any new enums, with existing IDs
}

if ($load_file)
{
my $guard = schema->txn_scope_guard;

my $array = LoadFile $load_file;

if ($global_views)
{
foreach my $import (@$array)
{
schema->resultset('View')->search({
id => $import->{id},
instance_id => { '!=' => $instance_id },
})->count and die "View ID $import->{id} already exists but for wrong instance";
schema->resultset('View')->find_or_create({
id => $import->{id},
instance_id => $instance_id,
});
my $view = GADS::View->new(
id => $import->{id},
user => undef,
schema => schema,
layout => $layout,
instance_id => $instance_id,
);
$view->columns($import->{columns});
$view->global($import->{is_admin});
$view->is_admin($import->{is_admin});
$view->name($import->{name});
$view->filter->as_json($import->{filter});
$view->write;
my (@sort_fields, @sort_types);
foreach my $sort (@{$import->{sorts}})
{
push @sort_fields, $sort->{layout_id};
push @sort_types, $sort->{type};
}
$view->set_sorts(\@sort_fields, \@sort_types);
}
}
else {
my %loaded;
$loaded{$_->{id}} = $_ foreach @$array;

# Find new ones
my %missing = %loaded;
delete $missing{$_->id} foreach $layout->all;

# Create first in case they are referenced
if (%missing)
{
my %deps = map {
$_->{id} => $_->{display_field} && $missing{$_->{display_field}} ? [ $_->{display_field} ] : []
} values %missing;

my $source = Algorithm::Dependency::Source::HoA->new(\%deps);
my $dep = Algorithm::Dependency::Ordered->new(source => $source)
or die 'Failed to set up dependency algorithm';
my @order = @{$dep->schedule_all};
my @missing = map { $missing{$_} } @order;

foreach my $new (@missing)
{
say STDERR "Creating missing field $new->{id} ($new->{name})";
my $class = "GADS::Column::".camelize $new->{type};
my $field = $class->new(
id => $new->{id},
schema => schema,
user => undef,
layout => $layout,
);
write_props($field, $new);
}
}

foreach my $field ($layout->all)
{
if (my $new = $loaded{$field->id})
{
write_props($field, $new);
}
else {
say STDERR "Field ".$field->name." (ID ".$field->id.") not in updated layout - needs manual deletion";
}
}
}

$guard->commit;
}
else {

my @out;

if ($global_views)
{
foreach my $view (@{$views->all})
{
next unless $view->global || $view->is_admin;
push @out, {
id => $view->id,
name => $view->name,
global => $view->global,
is_admin => $view->is_admin,
filter => $view->filter->as_json,
columns => $view->columns,
sorts => $view->sorts,
};
}
}
else {
foreach my $field ($layout->all)
{
my $hash = {
id => $field->id,
name => $field->name,
name_short => $field->name_short,
type => $field->type,
optional => $field->optional,
remember => $field->remember,
isunique => $field->isunique,
textbox => $field->can('textbox') ? $field->textbox : 0,
typeahead => $field->can('typeahead') ? $field->typeahead : 0,
force_regex => $field->can('force_regex') ? $field->force_regex : '',
position => $field->position,
ordering => $field->ordering,
end_node_only => $field->can('end_node_only') ? $field->end_node_only : 0,
multivalue => $field->multivalue,
description => $field->description,
helptext => $field->helptext,
display_field => $field->display_field,
display_regex => $field->display_regex,
link_parent_id => $field->link_parent_id,
filter => $field->filter->as_json,
options => $field->options,
};
$hash->{enumvals} = $field->enumvals if $field->type eq 'enum';
$hash->{curval_field_ids} = $field->curval_field_ids if $field->type eq 'curval';
push @out, $hash;
}
}

DumpFile $dump_file, [@out];
}

4 changes: 2 additions & 2 deletions bin/onboard.pl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
my $layout = GADS::Layout->new(
user => undef,
schema => schema,
config => config,
config => GADS::Config->instance,
instance_id => $instance_id,
user_permission_override => 1
);
Expand Down Expand Up @@ -497,7 +497,7 @@ sub update_fields
if (ref $col && $col->userinput) # Not calculated fields
{
my $newv = $input->{$col->field};
if (!$record->current_id || $newv)
if (!$record->current_id || defined $newv)
{
if ($col->type eq "daterange" && $ignore_incomplete_dateranges)
{
Expand Down
14 changes: 12 additions & 2 deletions lib/GADS/Column.pm
Original file line number Diff line number Diff line change
Expand Up @@ -850,8 +850,18 @@ sub write
$self->_set__rset($rset);
}
else {
$rset = $self->schema->resultset('Layout')->find($self->id);
$rset->update($newitem);
if ($rset = $self->schema->resultset('Layout')->find($self->id))
{
# Check whether attempt to move between instances - this is a bug
$newitem->{instance_id} != $rset->instance_id
and panic "Attempt to move column between instances";
$rset->update($newitem);
}
else {
$newitem->{id} = $self->id;
$rset = $self->schema->resultset('Layout')->create($newitem);
$self->_set__rset($rset);
}
}

$self->write_special(rset => $rset, id => $new_id || $self->id, %options); # Write any column-specific params
Expand Down
40 changes: 38 additions & 2 deletions lib/GADS/Column/Curval.pm
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ sub _build_view
# Replace any "special" $short_name values with their actual value from the
# record. If sub_values fails (due to record not being ready yet), then the
# view is not built
return unless $view->filter->sub_values($self->layout->record);
return unless $view->filter->sub_values($self->layout);
return $view;
}

Expand Down Expand Up @@ -347,7 +347,9 @@ sub _build_has_subvalues

sub filter_value_to_text
{ my ($self, $id) = @_;
$id or return '';
# Check for valid ID (in case search filter is corrupted) - Pg will choke
# on invalid IDs
$id =~ /^[0-9]+$/ or return '';
my $rows = $self->ids_to_values([$id]);
$rows->[0]->{value};
}
Expand Down Expand Up @@ -619,6 +621,40 @@ sub _build_all_ids
];
}

sub validate
{ my ($self, $value, %options) = @_;
return 1 if !$value;
my $fatal = $options{fatal};
if ($value !~ /^[0-9]+$/)
{
return 0 if !$fatal;
error __x"Value for {column} must be an integer", column => $self->name;
}
if (!$self->schema->resultset('Current')->search({ instance_id => $self->refers_to_instance, id => $value })->next)
{
return 0 if !$fatal;
error __x"{id} is not a valid record ID for {column}", id => $value, column => $self->name;
}
1;
}

sub validate_search
{ my $self = shift;
my ($value, %options) = @_;
if (!$value)
{
return 0 unless $options{fatal};
error __x"Search value cannot be blank for {col}.",
col => $self->name;
}
elsif ($value !~ /^[0-9]+$/) {
return 0 unless $options{fatal};
error __x"Search value must be an ID number for {col}.",
col => $self->name;
}
1;
}

sub random
{ my $self = shift;
$self->all_ids->[rand @{$self->all_ids}];
Expand Down
6 changes: 4 additions & 2 deletions lib/GADS/Column/Enum.pm
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ sub write_special
unless $value =~ /^[ \S]+$/;
if ($en->{id})
{
my $enumval = $self->schema->resultset('Enumval')->find($en->{id})
or error __x"Bad ID {id} for multiple select update", id => $en->{id};
my $enumval = $options{create_missing_id}
? $self->schema->resultset('Enumval')->find_or_create({ id => $en->{id}, layout_id => $id })
: $self->schema->resultset('Enumval')->find($en->{id});
$enumval or error __x"Bad ID {id} for multiple select update", id => $en->{id};
$enumval->update({ value => $en->{value} });
}
else {
Expand Down
4 changes: 1 addition & 3 deletions lib/GADS/Datum/Curval.pm
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ sub set_value
{ my ($self, $value) = @_;
my $clone = $self->clone; # Copy before changing text
my @values = sort grep {$_} ref $value eq 'ARRAY' ? @$value : ($value);
grep {
$_ !~ /^[0-9]+$/;
} @values and panic "Invalid value for ID";
$self->column->validate($_, fatal => 1) foreach @values;
my @old = sort @{$self->ids};
my $changed = "@values" ne "@old";
$self->_set_written_valid(!!@values);
Expand Down
10 changes: 8 additions & 2 deletions lib/GADS/Datum/String.pm
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ sub set_value
$value =~ /\h+$/ if !ref $value && $value;
if (my $regex = !ref $value && $self->column->force_regex)
{
error __x"Invalid value \"{value}\" for {field}", value => $value, field => $self->column->name
if $value && $value !~ /^$regex$/;
my $msg = __x"Invalid value \"{value}\" for {field}", value => $value, field => $self->column->name;
# Empty values are not checked - these should be done in optional value for field
if ($value && $value !~ /^$regex$/)
{
# Changed code repeated below, but don't want to flag changed if
# resulting error
($self->value || '') ne ($value || '') ? error($msg) : warning($msg);
}
}
$self->changed(1)
if ($self->value || '') ne ($value || '');
Expand Down
Loading

0 comments on commit 9d1994e

Please sign in to comment.