[Jifty-commit] r5111 - in apps/CASPlus/trunk: lib/CASPlus lib/CASPlus/Action lib/CASPlus/Model lib/CASPlus/View share/web/static/js

Jifty commits jifty-commit at lists.jifty.org
Thu Feb 14 15:44:47 EST 2008


Author: sterling
Date: Thu Feb 14 15:44:46 2008
New Revision: 5111

Added:
   apps/CASPlus/trunk/lib/CASPlus/Action/CreateColumnLink.pm
   apps/CASPlus/trunk/lib/CASPlus/Action/ExecuteBulkImport.pm
   apps/CASPlus/trunk/lib/CASPlus/Model/BulkUpload.pm
   apps/CASPlus/trunk/lib/CASPlus/Model/CSVBulkUpload.pm
   apps/CASPlus/trunk/lib/CASPlus/Model/ColumnLink.pm
   apps/CASPlus/trunk/lib/CASPlus/View/BulkUpload.pm
   apps/CASPlus/trunk/share/web/static/js/
   apps/CASPlus/trunk/share/web/static/js/app_behaviour.js
Modified:
   apps/CASPlus/trunk/   (props changed)
   apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm
   apps/CASPlus/trunk/lib/CASPlus/View.pm

Log:
 r15312 at riddle:  andrew | 2008-02-14 14:43:16 -0600
 Added the ability to bulk upload profile records and added the dispatcher rule for handling the roles view of the user view.


Added: apps/CASPlus/trunk/lib/CASPlus/Action/CreateColumnLink.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Action/CreateColumnLink.pm	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,61 @@
+use strict;
+use warnings;
+
+package CASPlus::Action::CreateColumnLink;
+use base qw/ CASPlus::Action::Record::Create Jifty::Action::Record::Create /;
+
+sub record_class { 'CASPlus::Model::ColumnLink' }
+
+sub _bulk_upload {
+    my $self = shift;
+    my $id   = $self->argument_value('bulk_upload');
+    my $bulk_upload = CASPlus::Model::BulkUpload->new;
+    $bulk_upload->load($id);
+    return $bulk_upload;
+}
+
+sub arguments {
+    my $self = shift;
+    my $arguments = $self->SUPER::arguments(@_);
+
+    my $bulk_upload = $self->_bulk_upload;
+
+    if ($bulk_upload->id) {
+        my $model = Jifty->app_class('Model', $bulk_upload->model);
+        my @columns = map { 
+            { display => $_->label || $_->name, value => $_->name }
+        } $model->columns;
+        unshift @columns, { display => '- do not import -', value => '' };
+        $arguments->{model_column}{valid_values} = \@columns;
+
+        my @all_columns;
+        for my $column ($model->columns) {
+            if ($column->refers_to && $column->refers_to->isa('Jifty::Record')) {
+                push @all_columns, map {
+                    {
+                        display => $_->label || $_->name,
+                        value   => $_->name,
+                    }
+                } $column->refers_to->columns;
+            }
+        }
+        $arguments->{referenced_key_column}{valid_values} = \@all_columns;
+    }
+
+    return $arguments;
+}
+
+sub take_action {
+    my $self = shift;
+
+    # Just quietly skip it if set to "- do not import -"
+    if ($self->argument_value('model_column') eq '') {
+        $self->result->success(1);
+        return;
+    }
+
+    return $self->SUPER::take_action(@_);
+}
+
+
+1;

Added: apps/CASPlus/trunk/lib/CASPlus/Action/ExecuteBulkImport.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Action/ExecuteBulkImport.pm	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,134 @@
+use strict;
+use warnings;
+
+=head1 NAME
+
+CASPlus::Action::ExecuteBulkImport
+
+=cut
+
+package CASPlus::Action::ExecuteBulkImport;
+use base qw/CASPlus::Action Jifty::Action/;
+
+use Jifty::Param::Schema;
+use Jifty::Action schema {
+    param 'bulk_upload';
+};
+
+sub _bulk_upload {
+    my $self = shift;
+    my $id   = $self->argument_value('bulk_upload');
+    my $bulk_upload = CASPlus::Model::BulkUpload->new;
+    $bulk_upload->load($id);
+    return $bulk_upload;
+}
+
+=head2 take_action
+
+=cut
+
+sub take_action {
+    my $self = shift;
+
+    my $bulk_upload = $self->_bulk_upload;
+
+    unless ($bulk_upload->id) {
+        $self->result->error('Not a valid bulk upload.');
+        return;
+    }
+
+    my $csv_bulk_upload = CASPlus::Model::CSVBulkUpload->new;
+    $csv_bulk_upload->load_by_cols(bulk_upload => $bulk_upload);
+
+    unless ($csv_bulk_upload->id) {
+        $self->result->error('Not a valid CSV import configuration.');
+        return;
+    }
+
+    my $created_rows = 0;
+    my $updated_rows = 0;
+
+    my $create_errors    = 0;
+    my $update_errors    = 0;
+    my $reference_errors = 0;
+
+    my @error_messages;
+
+    my $model_class = Jifty->app_class('Model', $bulk_upload->model);
+    $csv_bulk_upload->parse_import_file(sub {
+        my $count   = shift;
+        my $columns = shift;
+
+        my %lookup;
+        my %record;
+        my %extra_lookup;
+
+        my $column_links = $bulk_upload->column_links;
+        while (my $column_link = $column_links->next) {
+            $lookup{ $column_link->model_column }
+                = $columns->[ $column_link->import_column_id ]
+                    if $column_link->use_as_pk;
+
+            $record{ $column_link->model_column }
+                = $columns->[ $column_link->import_column_id ];
+
+            $extra_lookup{ $column_link->model_column }
+                = $column_link->referenced_key_column
+                    if $column_link->referenced_key_column;
+        }
+
+        for my $field (keys %extra_lookup) {
+            my $column = $model_class->column( $field );
+            my $referenced_model = $column->refers_to;
+            
+            my $ref_obj = $referenced_model->new;
+            $ref_obj->load_by_cols( 
+                $extra_lookup{ $field } => $record{ $field } 
+            );
+
+            if ($ref_obj->id) {
+                $record{ $field } = $ref_obj;
+                $lookup{ $field } = $ref_obj if defined $lookup{ $field };
+            }
+
+            else {
+                $reference_errors++;
+                push @error_messages, "Failed on referenced key column lookup: there is no $referenced_model with a $field set to $record{$field}. Skipping this record.";
+                return;
+            }
+        }
+
+        my $model = $model_class->new;
+        $model->load_by_cols( %lookup ) if keys %lookup;
+        if ($model->id) {
+            for my $field (keys %record) {
+                unless ($model->$field( $record{ $field } )) {
+                    push @error_messages, "Failed to set field $field on $model_class with ID ".$model->id.' matching ('.join('; ', values %lookup).').';
+                    $update_errors++;
+                }
+            }
+
+            $updated_rows++;
+        }
+
+        else {
+            unless ($model->create( %record )) {
+                push @error_messages, "Failed to create a new record in $model_class with values (".join('; ', values %record).').';
+                $create_errors++;
+            }
+            $created_rows++;
+        }
+    });
+
+    $self->result->message(
+        _('Created %1 rows (%2 errors) and updated %3 rows (%4 errors).',
+            $created_rows, $create_errors,
+            $updated_rows, $update_errors)
+    );
+    $self->result->content( error_messages => \@error_messages );
+    
+    return 1;
+}
+
+1;
+

Modified: apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus/Dispatcher.pm	Thu Feb 14 15:44:46 2008
@@ -9,6 +9,7 @@
 use CASPlus::Action::Logout;
 use CASPlus::Action::Validate;
 
+use List::MoreUtils qw/ any /;
 use Scalar::Util qw/ looks_like_number /;
 
 =head1 NAME
@@ -23,6 +24,17 @@
 
 The following dispatcher rules are related to the CAS 2.0 protocol.
 
+=head2 before **
+
+Make sure to reconnect to the database in case the connection was lost.
+
+=cut
+
+before '**' => run {
+#    eval { Jifty->handle->dbh->disconnect };
+    Jifty->handle->connect();
+};
+
 =head2 before GET /login
 
 Performs the login check action. Calls the L<CASPlus::Action::LoginCheck> action.
@@ -727,6 +739,13 @@
         show '/user/password';
     },
 
+    on 'roles' => run {
+        my $id = get 'id';
+        _load_user($id);
+        show '/user/roles';
+    },
+
+
     under '=' => [
         on '#' => run {
             if (_load_user($1)) {
@@ -861,6 +880,181 @@
     show '/error/unknown';
 };
 
+=head2 before [ /bulkupload/wizard, /bulkupload/wizard/* ]
+
+Redirects to /bulkupload/wizard/config/# if the upload action was already submitted.
+
+=cut
+
+on [ '/bulkupload/wizard', '/bulkupload/wizard/*' ] => run {
+    my $result = Jifty->web->response->result('bulk_upload');
+
+    if (defined $result && $result->success 
+            && defined $result->content('id')) {
+
+        redirect '/bulkupload/wizard/config/' . $result->content('id');
+    }
+};
+
+=head2 on /bulkupload/wizard
+
+Handle the initial upload and model selection for bulk uploads.
+
+=cut
+
+on '/bulkupload/wizard' => run {
+    my $action = Jifty->web->new_action(
+        class   => 'CreateBulkUpload',
+        moniker => 'bulk_upload',
+    );
+
+    set action => $action;
+    show '/bulkupload/wizard/upload';
+};
+
+=head2 on /bulkupload/wizard/*
+
+Handle the initial upload, but instead of letting the user select the model to use, it is preset.
+
+=cut
+
+on '/bulkupload/wizard/*' => run {
+    my $action = Jifty->web->new_action(
+        class     => 'CreateBulkUpload',
+        moniker   => 'bulk_upload',
+        arguments => {
+            model => $1,
+        },
+    );
+
+    set action => $action;
+    show '/bulkupload/wizard/upload';
+};
+
+=head2 on /bulkupload/wizard/#
+
+Handle the rest of the configuration and data import tasks.
+
+=cut
+
+on '/bulkupload/wizard/config/#' => run {
+    my $bulk_upload_id = $1;
+    my $bulk_upload = CASPlus::Model::BulkUpload->new;
+    $bulk_upload->load($bulk_upload_id);
+
+    my $wizard_path = '/bulkupload/page2-configure';
+    my $csv_bulk_upload = CASPlus::Model::CSVBulkUpload->new;
+    $csv_bulk_upload->load_by_cols(bulk_upload => $bulk_upload);
+
+    if ($csv_bulk_upload->id) {
+        $wizard_path = '/bulkupload/page3-columns';
+
+        my $column_links = $bulk_upload->column_links;
+        if ($column_links->count > 0) {
+            $wizard_path = '/bulkupload/page4-review';
+        }
+    }
+
+    set bulk_upload => $bulk_upload;
+    set wizard_path => $wizard_path;
+    show '/bulkupload/wizard/config';
+};
+
+=head2 on /bulkupload/page2-configure
+
+Handles the CSV configuration.
+
+=cut
+
+on '/bulkupload/page2-configure' => run {
+    my $bulk_upload_id = get 'id';
+    my $bulk_upload = CASPlus::Model::BulkUpload->new;
+    $bulk_upload->load($bulk_upload_id);
+
+    my $csv_bulk_upload = CASPlus::Model::CSVBulkUpload->new;
+    $csv_bulk_upload->load_by_cols(bulk_upload => $bulk_upload);
+
+    # Next step if we already have one
+    if ($csv_bulk_upload->id) {
+        dispatch '/bulkupload/page3-columns';
+    }
+
+    my $action = Jifty->web->new_action(
+        class     => 'CreateCSVBulkUpload',
+        moniker   => 'bulk_upload2',
+        arguments => {
+            bulk_upload => $bulk_upload->id,
+        },
+    );
+
+    set action => $action;
+};
+
+=head2 on /bulkupload/page3-columns
+
+Handles the column matching between CSV columns and the Jifty file.
+
+=cut
+
+on '/bulkupload/page3-columns' => run {
+    my $bulk_upload_id = get 'id';
+    my $bulk_upload = CASPlus::Model::BulkUpload->new;
+    $bulk_upload->load($bulk_upload_id);
+
+    # Move on if we already did this step
+    my $column_links = $bulk_upload->column_links;
+    if ($column_links->count > 0) {
+        dispatch '/bulkupload/page4-review';
+    }
+
+    my $csv_bulk_upload = CASPlus::Model::CSVBulkUpload->new;
+    $csv_bulk_upload->load_by_cols( bulk_upload => $bulk_upload );
+
+    my @actions;
+    for my $column ($csv_bulk_upload->column_metadata) {
+        my $action = Jifty->web->new_action(
+            class     => 'CreateColumnLink',
+            moniker   => 'bulk_upload3-'.$column->{id},
+            arguments => {
+                bulk_upload      => $bulk_upload->id,
+                import_column_id => $column->{id},
+                import_column    => $column->{title},
+                samples          => $column->{samples},
+            },
+        );
+
+        push @actions, $action;
+    }
+
+    set bulk_upload => $bulk_upload;
+    set actions     => \@actions;
+};
+
+=head2 on /bulkupload/page4-review
+
+Shows a review screen so that the user can confirm the settings for the import.
+
+=cut
+
+on '/bulkupload/page4-review' => run {
+    my $bulk_upload_id = get 'id';
+    my $bulk_upload = CASPlus::Model::BulkUpload->new;
+    $bulk_upload->load($bulk_upload_id);
+
+    my $action = Jifty->web->new_action(
+        class => 'ExecuteBulkImport',
+        moniker => 'bulk_upload4',
+        arguments => { bulk_upload => $bulk_upload->id },
+    );
+
+    my $csv_bulk_upload = CASPlus::Model::CSVBulkUpload->new;
+    $csv_bulk_upload->load_by_cols( bulk_upload => $bulk_upload );
+
+    set bulk_upload => $bulk_upload->as_update_action;
+    set csv_bulk_upload => $csv_bulk_upload->as_update_action;
+    set action => $action;
+};
+
 =head1 CAS+ BACK-END RULES
 
 The following dispatcher rules are related to CAS+ customizations seen by services and not, generally, but end-users or administrators.

Added: apps/CASPlus/trunk/lib/CASPlus/Model/BulkUpload.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/BulkUpload.pm	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,85 @@
+use strict;
+use warnings;
+
+package CASPlus::Model::BulkUpload;
+use Jifty::DBI::Schema;
+
+use constant CLASS_UUID => '2CD842A4-8853-11DC-ADF0-BD38C9EB023F';
+
+use CASPlus::Record schema {
+    column import_file =>
+        type is 'blob',
+        label is 'File upload',
+        render as 'Upload',
+        is mandatory,
+        ;
+
+    column uploaded_on =>
+        type is 'timestamp',
+        label is 'Uploaded on',
+        filters are qw/ Jifty::DBI::Filter::DateTime /,
+        ;
+
+    column model =>
+        type is 'text',
+        label is 'Model',
+        is mandatory,
+        valid_values are defer {
+            [ 
+                map  { s/^CASPlus::Model:://; $_ } 
+                grep { eval 
+                        { 
+                            my $o = $_->new;
+                            $o->current_user_can('create') and
+                            $o->current_user_can('update')
+                        } 
+                     } 
+                grep { !/^CASPlus::Model::BulkUpload$/ }
+                     Jifty->class_loader->models 
+            ]
+        },
+        ;
+
+    column column_links =>
+        references CASPlus::Model::ColumnLinkCollection by 'bulk_upload';
+};
+
+use DateTime;
+
+sub since { '0.0.2' }
+
+sub before_create {
+    my $self = shift;
+    my $args = shift;
+
+    $args->{uploaded_on} = DateTime->now;
+
+    return 1;
+}
+
+sub current_user_can {
+    my $self  = shift;
+    my $right = shift;
+    my %args  = @_;
+
+    if ($right eq 'create') {
+        return 1 if eval {
+            my $o = Jifty->app_class('Model', $args{model})->new;
+            $o->current_user_can('create') and
+            $o->current_user_can('update')
+        };
+    }
+
+    if ($right eq 'read') {
+        return 1;
+    }
+
+    if ($right eq 'delete') {
+        return 1;
+    }
+
+    return $self->SUPER::current_user_can($right, @_);
+}
+
+1;
+

Added: apps/CASPlus/trunk/lib/CASPlus/Model/CSVBulkUpload.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/CSVBulkUpload.pm	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,229 @@
+use strict;
+use warnings;
+
+package CASPlus::Model::CSVBulkUpload;
+use Jifty::DBI::Schema;
+
+use English;
+
+use constant CLASS_UUID => '8719F028-8853-11DC-8F63-C838C9EB023F';
+
+our (%escaped, %unescaped);
+BEGIN {
+    our %escaped = (  
+        "\a" => "\\a",
+        "\b" => "\\b",
+        "\t" => "\\t",
+        "\n" => "\\n",
+        "\f" => "\\f",
+        "\r" => "\\r",
+        "\e" => "\\e",
+    );
+    our %unescaped = reverse %escaped;
+}
+
+use CASPlus::Record schema {
+    column bulk_upload =>
+        references CASPlus::Model::BulkUpload,
+        label is 'Bulk Upload',
+        is mandatory,
+        is distinct,
+        ;
+
+    column first_row_titles =>
+        type is 'boolean',
+        label is 'First row contains column names?',
+        is mandatory,
+        default is 0,
+        ;
+
+    column eol =>
+        type is 'varchar(5)',
+        label is 'End of Line Marker',
+        hints are q{The most common values here are "\r\n" for DOS/Windows exports (such as generated by Excel), "\n" for Unix exports, and "\r" for Macintosh exports.},
+        is mandatory,
+        default is lazy { 
+            local $_ = "\r\n"; #$INPUT_RECORD_SEPARATOR;
+            s/([\a\b\t\n\f\r\e])/$escaped{$1}/g;
+            $_;
+        },
+        ;
+
+    column sep_char =>
+        type is 'varchar(2)',
+        label is 'Separatar Character',
+        is mandatory,
+        max_length is 1,
+        default is ',',
+        ;
+
+    column allow_whitespace =>
+        type is 'boolean',
+        label is 'Allow extra whitespace?',
+        is mandatory,
+        default is 0,
+        ;
+
+    column quote_char =>
+        type is 'varchar(1)',
+        label is 'Quote Character',
+        is mandatory,
+        max_length is 1,
+        default is '"',
+        ;
+
+    column allow_loose_quotes =>
+        type is 'boolean',
+        label is 'Allow loose quotes?',
+        is mandatory,
+        default is 0,
+        ;
+
+    column escape_char =>
+        type is 'varchar(1)',
+        label is 'Escape Character',
+        is mandatory,
+        max_length is 1,
+        default is '"',
+        ;
+
+    column allow_loose_escapes =>
+        type is 'boolean',
+        label is 'Allow loose escapes?',
+        is mandatory,
+        default is 0,
+        ;
+
+    column allow_binary =>
+        type is 'boolean',
+        label is 'Allow Binary in Values?',
+        hints is 'Check this box if your exported file contains multiline columns (such as descriptions or comments) or other binary data (such as image files).',
+        is mandatory,
+        default is 0,
+        ;
+};
+
+use Text::CSV_XS;
+
+sub since { '0.0.2' }
+
+sub _csv_xs {
+    my $self = shift;
+
+    my $eol = $self->eol;
+    $eol =~ s/(\\[abtnfre])/$unescaped{$1}/g;
+
+    my $sep_char = $self->sep_char;
+    $sep_char =~ s/(\\[abtnfre])/$unescaped{$1}/g;
+
+    my $csv = Text::CSV_XS->new({
+        eol                 => $eol,
+        sep_char            => $sep_char,
+        allow_whitespace    => $self->allow_whitespace,
+        quote_char          => $self->quote_char,
+        allow_loose_quotes  => $self->allow_loose_quotes,
+        escape_char         => $self->escape_char,
+        allow_loose_escapes => $self->allow_loose_escapes,
+        binary              => $self->allow_binary,
+    });
+
+    return $csv;
+}
+
+sub parse_import_file {
+    my $self     = shift;
+    my $callback = shift;
+    my $limit    = shift;
+
+    my $import_file = $self->bulk_upload->import_file;
+    my $import_csv = IO::String->new($import_file);
+
+    my $csv = $self->_csv_xs;
+
+    my $count = 0;
+    while (my $columns = $csv->getline($import_csv)) {
+        $callback->($count++, $columns);
+        return if defined $limit and $count >= $limit;
+    }
+}
+
+sub column_metadata {
+    my $self = shift;
+
+    my @columns;
+    my $save_metadata = sub {
+        my $count   = shift;
+        my $columns = shift;
+
+        if ($count == 0) {
+            if ($self->first_row_titles) {
+                my $index = 0;
+                @columns = map { 
+                    { 
+                        id    => $index++,
+                        title => $_ 
+                    } 
+                } @$columns;
+            }
+            else {
+                my $index = 1;
+                @columns = map { 
+                    { 
+                        id      => $index - 1,
+                        title   => 'column '.$index++, 
+                        samples => [ defined $_ ? $_ : () ],
+                    }
+                } @$columns;
+            }
+        }
+
+        else {
+            my $index = 0;
+            for my $value (@$columns) {
+                push @{ $columns[ $index ]->{'samples'} }, $value
+                    if defined $value and $value =~ /\S/;
+
+                $index++;
+            }
+        }
+    };
+
+    $self->parse_import_file($save_metadata, 100);
+
+    return @columns;
+}
+
+sub current_user_can {
+    my $self  = shift;
+    my $right = shift;
+    my %args  = @_;
+
+    if ($right eq 'create' and $args{bulk_upload}) {
+        my $bulk_upload = $args{bulk_upload};
+
+        unless (ref $bulk_upload) {
+            my $bulk_upload_obj = CASPlus::Model::BulkUpload->new;
+            $bulk_upload_obj->load($bulk_upload);
+            $bulk_upload = $bulk_upload_obj;
+        }
+
+        if (defined $bulk_upload) {
+            return $bulk_upload->current_user_can('create', 
+                model => $bulk_upload->model
+            );
+        }
+    }
+
+    if ($right eq 'read') {
+        return 1;
+    }
+
+    if ($right eq 'delete') {
+        return 1;
+    }
+
+    return $self->SUPER::current_user_can($right, @_);
+}
+
+1;
+

Added: apps/CASPlus/trunk/lib/CASPlus/Model/ColumnLink.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/ColumnLink.pm	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,83 @@
+use strict;
+use warnings;
+
+package CASPlus::Model::ColumnLink;
+use Jifty::DBI::Schema;
+
+use constant CLASS_UUID => '47671D48-8853-11DC-9D40-BF38C9EB023F';
+
+use CASPlus::Record schema {
+    column bulk_upload =>
+        references CASPlus::Model::BulkUpload,
+        label is 'Bulk Upload',
+        is mandatory,
+        ;
+    
+    column import_column_id =>
+        type is 'int',
+        label is 'Import column ID',
+        is mandatory,
+        ;
+
+    column import_column =>
+        type is 'varchar(100)',
+        label is 'Import column',
+        is mandatory,
+        ;
+
+    column model_column =>
+        type is 'varchar(100)',
+        label is 'Model column',
+        ;
+
+    column use_as_pk =>
+        type is 'boolean',
+        label is 'Use as primary key?',
+        hints is 'If checked, this column will be used to match existing values. If a match is found already in the database, that record will be updated instead of a new record being created.',
+        is mandatory,
+        default is 0,
+        ;
+
+    column referenced_key_column =>
+        type is 'varchar(100)',
+        label is 'Referenced key column',
+        ;
+};
+
+sub since { '0.0.2' }
+
+sub current_user_can {
+    my $self  = shift;
+    my $right = shift;
+    my %args  = @_;
+
+    if ($right eq 'create' and $args{bulk_upload}) {
+        my $bulk_upload = $args{bulk_upload};
+
+        unless (ref $bulk_upload) {
+            my $bulk_upload_obj = CASPlus::Model::BulkUpload->new;
+            $bulk_upload_obj->load($bulk_upload);
+            $bulk_upload = $bulk_upload_obj;
+        }
+
+        if (defined $bulk_upload) {
+            return $bulk_upload->current_user_can('create', 
+                model => $bulk_upload->model
+            );
+        }
+    }
+
+    if ($right eq 'read') {
+        return 1;
+    }
+
+    if ($right eq 'delete') {
+        return 1;
+    }
+
+    return $self->SUPER::current_user_can($right, @_);
+}
+
+
+1;
+

Modified: apps/CASPlus/trunk/lib/CASPlus/View.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/View.pm	(original)
+++ apps/CASPlus/trunk/lib/CASPlus/View.pm	Thu Feb 14 15:44:46 2008
@@ -185,6 +185,12 @@
         };
 
         p { hyperlink( label => 'Logout', url => '/logout' ); };
+
+        h3 { _('Change your password') };
+
+        p { outs_raw _('If you would like to update your password, you can do so by clicking on the link below and then click on the <strong>Password</strong> tab.') };
+
+        p { hyperlink label => _('Update my password.'), url => '/user/me' };
     }
 
     else {
@@ -377,6 +383,15 @@
 use CASPlus::View::Error;
 alias CASPlus::View::Error under '/error';
 
+=head2 bulkupload/**
+
+See L<CASPlus::View::BulkUpload>
+
+=cut
+
+use CASPlus::View::BulkUpload;
+alias CASPlus::View::BulkUpload under '/bulkupload';
+
 =head2 admin/**
 
 A number of administrative views are available under the C<CASPlus::View::Admin>.
@@ -385,7 +400,7 @@
 
 use Jifty::View::Declare::CRUD;
 while (my ($view, $model) = each %ADMIN_CRUD_VIEWS) {
-    my $view_class  = 'CASPlus::View::Admin::' . $view;
+    my $view_class = 'CASPlus::View::Admin::' . $view;
 
     my $path = lc $view;
     $path =~ s{::}{/}g;

Added: apps/CASPlus/trunk/lib/CASPlus/View/BulkUpload.pm
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/lib/CASPlus/View/BulkUpload.pm	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,227 @@
+use strict;
+use warnings;
+
+package CASPlus::View::BulkUpload;
+use Jifty::View::Declare -base;
+
+template 'wizard/upload' => page {
+    my $action = get 'action';
+
+    { title is 'Bulk Upload' }
+
+    form {
+
+        render_param $action, 'import_file';
+        render_param $action, 'model';
+
+        form_submit
+            label  => _('Next >'),
+            submit => $action,
+            ;
+    };
+};
+
+template 'wizard/config' => page {
+    my $bulk_upload = get 'bulk_upload';
+    my $wizard_path = get 'wizard_path';
+
+    { title is 'Bulk Upload' }
+
+    form {
+        render_region
+            name     => 'wizard-page',
+            path     => $wizard_path,
+            defaults => { id => $bulk_upload->id },
+            ;
+    };
+};
+
+template 'page2-configure' => sub {
+    my $action = get 'action';
+
+    p {
+        _('Use this form to select advanced options for your CSV import. If you do not know what to do here, leave everything as is since these cover the most common situations.');
+    };
+
+    outs_raw($action->hidden(
+        bulk_upload => $action->argument_value('bulk_upload')
+    ));
+
+    for my $field ($action->argument_names) {
+        next if $field eq 'bulk_upload';
+        render_param $action, $field;
+    }
+
+    form_submit
+        label => _('Next >'),
+        onclick => [
+            {
+                submit       => $action,
+                refresh_self => 1,
+            },
+        ],
+        ;
+};
+
+private template 'column_link' => sub {
+    my $self  = shift;
+    my $action = shift;
+
+    fieldset {
+        outs_raw($action->hidden(
+            bulk_upload => $action->argument_value('bulk_upload'),
+        ));
+
+        outs_raw($action->hidden(
+            import_column_id => $action->argument_value('import_column_id'),
+        ));
+
+        outs_raw($action->hidden(
+            import_column => $action->argument_value('import_column'),
+        ));
+
+        render_param $action, 'import_column_id', render_as => 'Text', render_mode => 'read';
+        render_param $action, 'import_column', render_as => 'Text', render_mode => 'read';
+        render_param $action, 'model_column', 
+            render_as => 'Select';
+        render_param $action, 'referenced_key_column', 
+            render_as => 'Select';
+        render_param $action, 'use_as_pk';
+
+        my @samples = @{ $action->argument_value('samples') || [] };
+        if (@samples) {
+            div {
+                { class is 'form_field' };
+
+                span {
+                    { class is 'label text argument-samples' }
+                    _('Sample Values: ');
+                };
+                span {
+                    { class is 'text argument-samples' }
+
+                    if (@samples > 10) {
+                        @samples = @samples[0 .. 9];
+                    }
+                    join ', ', @samples;
+                };
+            };
+        }
+    };
+};
+
+template 'page3-columns' => sub {
+    my $actions = get 'actions';
+    my $bulk_upload = get 'bulk_upload';
+
+    p { 
+        _('Please select how you would like the imported columns mapped into the local table. If you do not check any primary key checkboxes, you are instructing the importer to only create new records, even if it means creating duplicates.');
+    };
+
+    my %reference_columns;
+    my $model = Jifty->app_class('Model', $bulk_upload->model);
+    for my $column ($model->columns) {
+        if ($column->refers_to && $column->refers_to->isa('Jifty::Record')) {
+            $reference_columns{ $column->name } = [ map {
+                {
+                    display => $_->label || $_->name,
+                    value   => $_->name,
+                }
+            } $column->refers_to->columns ];
+        }
+    }
+
+    script {
+        attr { type => 'text/javascript' };
+
+        outs_raw('reference_columns = '.
+            JSON::Syck::Dump(\%reference_columns).';');
+    };
+
+    for my $action (@$actions) {
+        show '/bulkupload/column_link', $action;
+    }
+
+    script {
+        attr { type => 'text/javascript' };
+
+        outs_raw('document.getElementsByClassName("argument-referenced_key_column").each(function(node){if(node.tagName=="DIV"){Element.hide(node);}})');
+    };
+
+    form_submit
+        label => _('Next >'),
+        onclick => [
+            {
+                submit       => $actions,
+                refresh_self => 1,
+            },
+        ],
+        ;
+};
+
+template 'page4-review' => sub {
+    my $action = get 'action';
+    my $bulk_upload = get 'bulk_upload';
+    my $csv_bulk_upload = get 'csv_bulk_upload';
+
+    p { _('You have requested the following import configuration.') };
+
+    render_param $bulk_upload, 'uploaded_on', render_mode => 'read';
+    render_param $bulk_upload, 'model', render_mode => 'read';
+
+    my $column_links = $bulk_upload->record->column_links;
+    while (my $link = $column_links->next) {
+        my $column_link = $link->as_update_action;
+        fieldset {
+            render_action $column_link, [ 
+                qw/ import_column_id import_column model_column /,
+            ], { render_mode => 'read', render_as => 'text' };
+
+            if ($link->referenced_key_column) {
+                render_param $column_link, 'referenced_key_column',
+                    render_mode => 'read', render_as => 'text';
+            }
+        };
+    }
+
+    p { outs_raw(_('If everything looks okay, click <strong>Import Data</strong> to finish.')) };
+
+    outs_raw($action->hidden('bulk_upload', $action->argument_value('bulk_upload')));
+    form_submit
+        label => _('Import Data >'),
+        onclick => [
+            {
+                submit       => $action,
+                refresh_self => 1,
+                replace_with => '/bulkupload/page5-status',
+            },
+        ],
+        ;
+};
+
+template 'page5-status' => sub {
+    if (Jifty->web->response->result('bulk_upload4')) {
+        my $result = Jifty->web->response->result('bulk_upload4');
+
+        if ($result->success) {
+            p { $result->message };
+        }
+        else {
+            p { $result->error };
+            
+            my $error_messages = $result->content('error_messages') || [];
+            if (@$error_messages) {
+                ul {
+                    for my $error_message (@$error_messages) {
+                        li { $error_message };
+                    }
+                };
+            }
+        }
+    }
+
+
+    p { 'Finished. See the above messages for success or errors.' };
+};
+
+1;

Added: apps/CASPlus/trunk/share/web/static/js/app_behaviour.js
==============================================================================
--- (empty file)
+++ apps/CASPlus/trunk/share/web/static/js/app_behaviour.js	Thu Feb 14 15:44:46 2008
@@ -0,0 +1,40 @@
+var reference_columns = [];
+
+function update_referenced_key_column(model_column, referenced_key) {
+    referenced_key.options.length = 0;
+
+    if (reference_columns[ model_column.value ] != null) {
+
+        var columns = reference_columns[ model_column.value ];
+        for (var index in columns) {
+            referenced_key.options[index] = new Option(
+                columns[index].display,
+                columns[index].value
+            );
+        }
+
+        Element.show(referenced_key.parentNode);
+    }
+
+    else {
+        Element.hide(referenced_key.parentNode);
+    }
+}
+
+Behaviour.register({
+    'select.argument-model_column': function(node) {
+        if (reference_columns) {
+            var init_action = Form.Element.getAction(node);
+            var init_referenced_key = init_action.getField('referenced_key_column');
+
+            update_referenced_key_column(node, init_referenced_key);
+        }
+
+        node.onclick = function() {
+            var action = Form.Element.getAction(this);
+            var referenced_key = action.getField('referenced_key_column');
+
+            update_referenced_key_column(this, referenced_key);
+        };
+    },
+});


More information about the Jifty-commit mailing list