View | Details | Raw Unified | Return to bug 16994
Collapse All | Expand All

(-)a/C4/MarcModificationTemplates.pm (+191 lines)
Lines 34-39 use Koha::SimpleMARC qw( Link Here
34
);
34
);
35
use Koha::MoreUtils;
35
use Koha::MoreUtils;
36
use Koha::DateUtils qw( dt_from_string );
36
use Koha::DateUtils qw( dt_from_string );
37
use JSON            qw( encode_json decode_json );
37
38
38
use vars qw(@ISA @EXPORT);
39
use vars qw(@ISA @EXPORT);
39
40
Lines 54-59 BEGIN { Link Here
54
55
55
        ModifyRecordsWithTemplate
56
        ModifyRecordsWithTemplate
56
        ModifyRecordWithTemplate
57
        ModifyRecordWithTemplate
58
59
        ExportModificationTemplates
60
        ImportModificationTemplates
57
    );
61
    );
58
}
62
}
59
63
Lines 735-740 sub ModifyRecordWithTemplate { Link Here
735
739
736
    return;
740
    return;
737
}
741
}
742
743
=head2
744
  ExportModificationTemplates
745
746
  my $json = ExportModificationTemplates( $template_ids );
747
748
  Exports MARC modification templates to JSON format.
749
  If $template_ids is provided, only export those templates.
750
  If not provided, export all templates.
751
752
  Returns a JSON string containing the exported templates.
753
=cut
754
755
sub ExportModificationTemplates {
756
    my ($template_ids) = @_;
757
758
    my $dbh = C4::Context->dbh;
759
760
    my @templates;
761
    if ( $template_ids && @$template_ids ) {
762
        my $placeholders = join( ',', ('?') x @$template_ids );
763
        my $sth          = $dbh->prepare(
764
            "SELECT * FROM marc_modification_templates WHERE template_id IN ($placeholders) ORDER BY name");
765
        $sth->execute(@$template_ids);
766
        while ( my $template = $sth->fetchrow_hashref() ) {
767
            push( @templates, $template );
768
        }
769
    } else {
770
        my $sth = $dbh->prepare("SELECT * FROM marc_modification_templates ORDER BY name");
771
        $sth->execute();
772
        while ( my $template = $sth->fetchrow_hashref() ) {
773
            push( @templates, $template );
774
        }
775
    }
776
777
    my $export_data = {
778
        version   => 1,
779
        templates => [],
780
    };
781
782
    foreach my $template (@templates) {
783
        my $template_id = $template->{'template_id'};
784
        my @actions     = GetModificationTemplateActions($template_id);
785
786
        my $template_export = {
787
            template_id => $template_id,
788
            name        => $template->{'name'},
789
            actions     => [],
790
        };
791
792
        foreach my $action (@actions) {
793
            my $action_export = {
794
                mmta_id                => $action->{'mmta_id'},
795
                ordering               => $action->{'ordering'},
796
                action                 => $action->{'action'},
797
                field_number           => $action->{'field_number'},
798
                from_field             => $action->{'from_field'},
799
                from_subfield          => $action->{'from_subfield'},
800
                field_value            => $action->{'field_value'},
801
                to_field               => $action->{'to_field'},
802
                to_subfield            => $action->{'to_subfield'},
803
                to_regex_search        => $action->{'to_regex_search'},
804
                to_regex_replace       => $action->{'to_regex_replace'},
805
                to_regex_modifiers     => $action->{'to_regex_modifiers'},
806
                conditional            => $action->{'conditional'},
807
                conditional_field      => $action->{'conditional_field'},
808
                conditional_subfield   => $action->{'conditional_subfield'},
809
                conditional_comparison => $action->{'conditional_comparison'},
810
                conditional_value      => $action->{'conditional_value'},
811
                conditional_regex      => $action->{'conditional_regex'},
812
                description            => $action->{'description'},
813
            };
814
815
            push( @{ $template_export->{actions} }, $action_export );
816
        }
817
818
        push( @{ $export_data->{templates} }, $template_export );
819
    }
820
821
    return encode_json($export_data);
822
}
823
824
=head2
825
  ImportModificationTemplates
826
827
  my $result = ImportModificationTemplates( $json, $options );
828
829
  Imports MARC modification templates from JSON format.
830
  $json is the JSON string to import.
831
  $options is a hashref with import options:
832
    - skip_existing: if true, skip templates that already exist (default: false)
833
    - update_existing: if true, update templates that already exist (default: false)
834
835
  Returns a hashref with:
836
    - success: number of successfully imported templates
837
    - skipped: number of skipped templates
838
    - errors: arrayref of error messages
839
=cut
840
841
sub ImportModificationTemplates {
842
    my ( $json, $skip_existing ) = @_;
843
844
    my $result = {
845
        success => 0,
846
        skipped => 0,
847
        errors  => [],
848
    };
849
850
    my $data;
851
    eval { $data = decode_json($json); };
852
    if ($@) {
853
        push( @{ $result->{errors} }, "Failed to parse JSON: $@" );
854
        return $result;
855
    }
856
857
    my $version = $data->{version} || 0;
858
    if ( $version != 1 ) {
859
        push( @{ $result->{errors} }, "Unsupported export version: $version" );
860
        return $result;
861
    }
862
863
    my $templates = $data->{templates} || [];
864
    my $dbh       = C4::Context->dbh;
865
866
    foreach my $template_data (@$templates) {
867
        my $template_name = $template_data->{name};
868
        my $actions       = $template_data->{actions} || [];
869
870
        # Check if template already exists
871
        my $sth = $dbh->prepare("SELECT template_id FROM marc_modification_templates WHERE name = ?");
872
        $sth->execute($template_name);
873
        my $existing = $sth->fetchrow_hashref();
874
875
        if ( $existing && $skip_existing ) {
876
            $result->{skipped}++;
877
            next;
878
        }
879
880
        my $template_id;
881
        if ($existing) {
882
            $template_id = $existing->{template_id};
883
884
            # Delete existing actions for this template
885
            $sth = $dbh->prepare("DELETE FROM marc_modification_template_actions WHERE template_id = ?");
886
            $sth->execute($template_id);
887
            $result->{overwrite}++;
888
        } else {
889
890
            # Create new template
891
            $sth = $dbh->prepare("INSERT INTO marc_modification_templates (name) VALUES (?)");
892
            $sth->execute($template_name);
893
894
            $sth = $dbh->prepare("SELECT template_id FROM marc_modification_templates WHERE name = ?");
895
            $sth->execute($template_name);
896
            my $row = $sth->fetchrow_hashref();
897
            $template_id = $row->{template_id};
898
        }
899
900
        # Add new actions
901
        foreach my $action_data (@$actions) {
902
            AddModificationTemplateAction(
903
                $template_id,
904
                $action_data->{action},
905
                $action_data->{field_number},
906
                $action_data->{from_field},
907
                $action_data->{from_subfield},
908
                $action_data->{field_value},
909
                $action_data->{to_field},
910
                $action_data->{to_subfield},
911
                $action_data->{to_regex_search},
912
                $action_data->{to_regex_replace},
913
                $action_data->{to_regex_modifiers},
914
                $action_data->{conditional},
915
                $action_data->{conditional_field},
916
                $action_data->{conditional_subfield},
917
                $action_data->{conditional_comparison},
918
                $action_data->{conditional_value},
919
                $action_data->{conditional_regex},
920
                $action_data->{description},
921
            );
922
        }
923
924
        $result->{success}++;
925
    }
926
927
    return $result;
928
}
738
1;
929
1;
739
__END__
930
__END__
740
931
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/marc_modification_templates.tt (+81 lines)
Lines 74-79 Link Here
74
                <input type="hidden" name="op" value="cud-delete_template" />
74
                <input type="hidden" name="op" value="cud-delete_template" />
75
                <button type="submit" class="btn btn-default confirm-delete-template"><i class="fa fa-fw fa-trash-can"></i> Delete template</button>
75
                <button type="submit" class="btn btn-default confirm-delete-template"><i class="fa fa-fw fa-trash-can"></i> Delete template</button>
76
            </form>
76
            </form>
77
        [% ELSE %]
78
            <div class="btn-group">
79
                <a href="#" class="btn btn-default" data-bs-toggle="dropdown"><i class="fa fa-download"></i> Export</a>
80
                <div class="dropdown-menu">
81
                    <a class="dropdown-item" href="/cgi-bin/koha/tools/marc_modification_templates.pl?op=export&export_all=1">Export all templates</a>
82
                    [% FOREACH TemplatesLoo IN TemplatesLoop %]
83
                        <a class="dropdown-item" href="/cgi-bin/koha/tools/marc_modification_templates.pl?op=export&template_ids=[% TemplatesLoo.template_id | html %]">Export [% TemplatesLoo.name | html %]</a>
84
                    [% END %]
85
                </div>
86
            </div>
87
            <a href="#" class="btn btn-default" data-bs-toggle="modal" data-bs-target="#importTemplate"><i class="fa fa-upload"></i> Import</a>
77
        [% END %]
88
        [% END %]
78
    </div>
89
    </div>
79
90
Lines 375-380 Link Here
375
            </div>
386
            </div>
376
        </div>
387
        </div>
377
    </div>
388
    </div>
389
390
    <!-- Modal to import templates -->
391
    <div class="modal" id="importTemplate" tabindex="-1" role="dialog" aria-labelledby="LabelimportTemplate" aria-hidden="true">
392
        <div class="modal-dialog">
393
            <div class="modal-content">
394
                <div class="modal-header">
395
                    <h1 class="modal-title" id="LabelimportTemplate">Import MARC modification templates</h1>
396
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
397
                </div>
398
                <form method="post" action="/cgi-bin/koha/tools/marc_modification_templates.pl" enctype="multipart/form-data" id="import_template" class="validated">
399
                    [% INCLUDE 'csrf-token.inc' %]
400
                    <div class="modal-body">
401
                        <fieldset>
402
                            <p>
403
                                <label for="import_file" class="required">JSON file:</label>
404
                                <input type="file" name="import_file" id="import_file" required="required" class="required" />
405
                                <span class="required">Required</span>
406
                            </p>
407
408
                            <p>
409
                                <label>
410
                                    <input type="checkbox" name="skip_existing" id="skip_existing" value="1" checked />
411
                                    Skip existing templates (do not overwrite)
412
                                </label>
413
                            </p>
414
415
                            <input type="hidden" name="op" value="cud-import" />
416
417
                            [% IF import_error %]
418
                                [% IF import_error == 'no_file_uploaded' %]
419
                                    <div class="alert alert-danger">Error: No file was uploaded.</div>
420
                                [% END %]
421
                            [% END %]
422
423
                            [% IF import_success || import_skipped || import_overwrite %]
424
                                <div class="alert alert-success">
425
                                    [% IF import_success %]
426
                                        <p>Successfully imported [% import_success | html %] template(s).</p>
427
                                    [% END %]
428
                                    [% IF import_skipped %]
429
                                        <p>Skipped [% import_skipped | html %] template(s) that already exist.</p>
430
                                    [% END %]
431
                                    [% IF import_overwrite %]
432
                                        <p>Overwrite [% import_overwrite | html %] template(s) that already exist.</p>
433
                                    [% END %]
434
                                </div>
435
                            [% END %]
436
437
                            [% IF import_errors %]
438
                                [% FOREACH error IN import_errors %]
439
                                    <div class="alert alert-danger">Error: [% error | html %]</div>
440
                                [% END %]
441
                            [% END %]
442
                        </fieldset>
443
                    </div>
444
                    <div class="modal-footer">
445
                        <button type="submit" class="btn btn-primary">Import</button>
446
                        <button type="button" class="btn btn-default" data-bs-dismiss="modal">Close</button>
447
                    </div>
448
                </form>
449
            </div>
450
        </div>
451
    </div>
378
[% END %]
452
[% END %]
379
453
380
[% MACRO jsinclude BLOCK %]
454
[% MACRO jsinclude BLOCK %]
Lines 384-389 Link Here
384
        [% IF ActionsLoop %]
458
        [% IF ActionsLoop %]
385
            var mmtas = [% ActionsLoop.json | $raw %]
459
            var mmtas = [% ActionsLoop.json | $raw %]
386
        [% END %]
460
        [% END %]
461
462
        // Show import modal if there are import results
463
        [% IF show_import_modal %]
464
            $(document).ready(function() {
465
                $('#importTemplate').modal('show');
466
            });
467
        [% END %]
387
    </script>
468
    </script>
388
    [% Asset.js("js/marc_modification_templates.js") | $raw %]
469
    [% Asset.js("js/marc_modification_templates.js") | $raw %]
389
[% END %]
470
[% END %]
(-)a/tools/marc_modification_templates.pl (-1 / +57 lines)
Lines 27-34 use C4::MarcModificationTemplates qw( Link Here
27
    AddModificationTemplateAction
27
    AddModificationTemplateAction
28
    DelModificationTemplate
28
    DelModificationTemplate
29
    DelModificationTemplateAction
29
    DelModificationTemplateAction
30
    ExportModificationTemplates
30
    GetModificationTemplateActions
31
    GetModificationTemplateActions
31
    GetModificationTemplates
32
    GetModificationTemplates
33
    ImportModificationTemplates
32
    ModModificationTemplateAction
34
    ModModificationTemplateAction
33
    MoveModificationTemplateAction
35
    MoveModificationTemplateAction
34
);
36
);
Lines 114-119 if ( $op eq "cud-create_template" ) { Link Here
114
116
115
    MoveModificationTemplateAction( scalar $cgi->param('mmta_id'), scalar $cgi->param('where') );
117
    MoveModificationTemplateAction( scalar $cgi->param('mmta_id'), scalar $cgi->param('where') );
116
118
119
} elsif ( $op eq "export" ) {
120
121
    my @template_ids;
122
    if ( $cgi->param('export_all') ) {
123
124
        # Export all templates
125
        my @templates = GetModificationTemplates();
126
        @template_ids = map { $_->{template_id} } @templates;
127
    } else {
128
129
        # Export selected templates
130
        @template_ids = $cgi->multi_param('template_ids');
131
    }
132
133
    my $json = ExportModificationTemplates( \@template_ids );
134
135
    print $cgi->header(
136
        -type       => 'application/json',
137
        -attachment => 'marc_modification_templates.json',
138
        -charset    => 'utf-8',
139
    );
140
    print $json;
141
142
    exit;
143
144
} elsif ( $op eq "cud-import" ) {
145
146
    my $upload = $cgi->upload('import_file');
147
    if ($upload) {
148
        my $json;
149
        {
150
            local $/;
151
            while ( my $line = <$upload> ) {
152
                $json .= $line;
153
            }
154
        }
155
156
        my $skip_existing = $cgi->param('skip_existing') ? 1 : 0;
157
158
        my $result = ImportModificationTemplates( $json, $skip_existing );
159
160
        $template->param(
161
            import_success    => $result->{success},
162
            import_skipped    => $result->{skipped},
163
            import_overwrite  => $result->{overwrite},
164
            import_errors     => $result->{errors},
165
            show_import_modal => 1,
166
        );
167
    } else {
168
        $template->param(
169
            import_error      => 'no_file_uploaded',
170
            show_import_modal => 1,
171
        );
172
    }
173
117
}
174
}
118
175
119
my @templates = GetModificationTemplates($template_id);
176
my @templates = GetModificationTemplates($template_id);
120
- 

Return to bug 16994