Commit 0777ee56 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 530746: Create a script that converts extensions from the old format to the new format

Patch by Max Kanat-Alexander <mkanat@bugzilla.org> (module owner) a=mkanat
parent 823e5969
......@@ -281,8 +281,8 @@ For example, here's an implementation of a hook named C<foo_start>
that gets an argument named C<bar>:
sub foo_start {
my ($self, $params) = @_;
my $bar = $params->{bar};
my ($self, $args) = @_;
my $bar = $args->{bar};
print "I got $bar!\n";
}
......
#!/usr/bin/perl -w
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Everything Solved, Inc.
# Portions created by the Initial Developer are Copyright (C) 2009 the
# Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Max Kanat-Alexander <mkanat@bugzilla.org>
use strict;
use warnings;
use lib qw(. lib);
use Bugzilla;
use Bugzilla::Constants;
use Bugzilla::Util qw(trim);
use File::Basename;
use File::Copy qw(move);
use File::Find;
use File::Path qw(mkpath rmtree);
my $from = $ARGV[0]
or die <<END;
You must specify the name of the extension you are converting from,
as the first argument.
END
my $extension_name = ucfirst($from);
my $extdir = bz_locations()->{'extensionsdir'};
my $from_dir = "$extdir/$from";
if (!-d $from_dir) {
die "$from_dir does not exist.\n";
}
my $to_dir = "$extdir/$extension_name";
if (-d $to_dir) {
die "$to_dir already exists, not converting.\n";
}
if (ON_WINDOWS) {
# There's no easy way to recursively copy a directory on Windows.
print "WARNING: This will modify the contents of $from_dir.\n",
"Press Ctrl-C to stop or any other key to continue...\n";
getc;
move($from_dir, $to_dir)
|| die "rename of $from_dir to $to_dir failed: $!";
}
else {
print "Copying $from_dir to $to_dir...\n";
system("cp", "-r", $from_dir, $to_dir);
}
# Make sure we don't accidentally modify the $from_dir anywhere else
# in this script.
undef $from_dir;
if (!-d $to_dir) {
die "$to_dir was not created.\n";
}
my $version = get_version($to_dir);
move_template_hooks($to_dir);
rename_module_packages($to_dir, $extension_name);
my $install_requirements = get_install_requirements($to_dir);
my ($modules, $subs) = code_files_to_subroutines($to_dir);
my $config_pm = <<END;
package Bugzilla::Extension::$extension_name;
use strict;
use constant NAME => '$extension_name';
$install_requirements
__PACKAGE__->NAME;
END
my $extension_pm = <<END;
package Bugzilla::Extension::$extension_name;
use strict;
use base qw(Bugzilla::Extension);
$modules
our \$VERSION = '$version';
$subs
__PACKAGE__->NAME;
END
open(my $config_fh, '>', "$to_dir/Config.pm") || die "$to_dir/Config.pm: $!";
print $config_fh $config_pm;
close($config_fh);
open(my $extension_fh, '>', "$to_dir/Extension.pm")
|| die "$to_dir/Extension.pm: $!";
print $extension_fh $extension_pm;
close($extension_fh);
rmtree("$to_dir/code");
unlink("$to_dir/info.pl");
###############
# Subroutines #
###############
sub rename_module_packages {
my ($dir, $name) = @_;
my $lib_dir = "$dir/lib";
# We don't want things like Bugzilla::Extension::Testopia::Testopia.
if (-d "$lib_dir/$name") {
print "Moving contents of $lib_dir/$name into $lib_dir...\n";
foreach my $file (glob("$lib_dir/$name/*")) {
my $dirname = dirname($file);
my $basename = basename($file);
rename($file, "$dirname/../$basename") || warn "$file: $!\n";
}
}
my @modules;
find({ wanted => sub { $_ =~ /\.pm$/i and push(@modules, $_) },
no_chdir => 1 }, $lib_dir);
my %module_rename;
foreach my $file (@modules) {
open(my $fh, '<', $file) || die "$file: $!";
my $content = do { local $/ = undef; <$fh> };
close($fh);
if ($content =~ /^package (\S+);/m) {
my $package = $1;
my $new_name = $file;
$new_name =~ s/^$lib_dir\///;
$new_name =~ s/\.pm$//;
$new_name = join('::', File::Spec->splitdir($new_name));
$new_name = "Bugzilla::Extension::${name}::$new_name";
print "Renaming $package to $new_name...\n";
$content =~ s/^package \Q$package\E;/package \Q$new_name\E;/;
open(my $write_fh, '>', $file) || die "$file: $!";
print $write_fh $content;
close($write_fh);
$module_rename{$package} = $new_name;
}
}
print "Renaming module names inside of library and code files...\n";
my @code_files = glob("$dir/code/*.pl");
rename_modules_internally(\%module_rename, [@modules, @code_files]);
}
sub rename_modules_internally {
my ($rename, $files) = @_;
# We can't use \b because :: matches \b.
my $break = qr/^|[^\w:]|$/;
foreach my $file (@$files) {
open(my $fh, '<', $file) || die "$file: $!";
my $content = do { local $/ = undef; <$fh> };
close($fh);
foreach my $old_name (keys %$rename) {
my $new_name = $rename->{$old_name};
$content =~ s/($break)\Q$old_name\E($break)/$1$new_name$2/gms;
}
open(my $write_fh, '>', $file) || die "$file: $!";
print $write_fh $content;
close($write_fh);
}
}
sub get_version {
my ($dir) = @_;
print "Getting version info from info.pl...\n";
my $info;
{
local @INC = ("$dir/lib", @INC);
$info = do "$dir/info.pl"; die $@ if $@;
}
return $info->{version};
}
sub get_install_requirements {
my ($dir) = @_;
my $file = "$dir/code/install-requirements.pl";
return '' if !-f $file;
print "Moving install-requirements.pl code into Config.pm...\n";
my ($modules, $code) = process_code_file($file);
$modules = join('', @$modules);
$code = join('', @$code);
if ($modules) {
return "$modules\n\n$code";
}
return $code;
}
sub process_code_file {
my ($file) = @_;
open(my $fh, '<', $file) || die "$file: $!";
my $stuff_started;
my (@modules, @code);
foreach my $line (<$fh>) {
$stuff_started = 1 if $line !~ /^#/;
next if !$stuff_started;
next if $line =~ /^use (warnings|strict|lib|Bugzilla)[^\w:]/;
if ($line =~ /^(?:use|require)\b/) {
push(@modules, $line);
}
else {
push(@code, $line);
}
}
close $fh;
return (\@modules, \@code);
}
sub code_files_to_subroutines {
my ($dir) = @_;
my @dir_files = glob("$dir/code/*.pl");
my (@all_modules, @subroutines);
foreach my $file (@dir_files) {
next if $file =~ /install-requirements/;
print "Moving $file code into Extension.pm...\n";
my ($modules, $code) = process_code_file($file);
my @code_lines = map { " $_" } @$code;
my $code_string = join('', @code_lines);
$code_string =~ s/Bugzilla->hook_args/\$args/g;
$code_string =~ s/my\s+\$args\s+=\s+\$args;//gs;
chomp($code_string);
push(@all_modules, @$modules);
my $name = basename($file);
$name =~ s/-/_/;
$name =~ s/\.pl$//;
my $subroutine = <<END;
sub $name {
my (\$self, \$args) = \@_;
$code_string
}
END
push(@subroutines, $subroutine);
}
my %seen_modules = map { trim($_) => 1 } @all_modules;
my $module_string = join("\n", sort keys %seen_modules);
my $subroutine_string = join("\n", @subroutines);
return ($module_string, $subroutine_string);
}
sub move_template_hooks {
my ($dir) = @_;
foreach my $lang (glob("$dir/template/*")) {
next if !_file_matters($lang);
mkpath("$lang/hook") || die "$lang/hook: $!";
# Hooks can be in all sorts of weird places, including
# template/default/hook.
foreach my $hooks_container ($lang, "$lang/default/hook") {
foreach my $file (glob("$hooks_container/*")) {
next if !_file_matters($file, 1);
my $dirname = basename($file);
print "Moving $file to $lang/hook/$dirname...\n";
rename($file, "$lang/hook/$dirname") || die "move failed: $!";
}
}
}
}
sub _file_matters {
my ($path, $tmpl) = @_;
my @ignore = qw(default custom CVS hook);
my $file = basename($path);
return 0 if grep(lc($_) eq lc($file), @ignore);
# Hidden files
return 0 if $file =~ /^\./;
if ($tmpl) {
return 1 if $file =~ /\.tmpl$/;
}
return 0 if !-d $path;
return 1;
}
__END__
=head1 NAME
extension-convert.pl - Convert extensions from the pre-3.6 format to the
3.6 format.
=head1 SYNOPSIS
contrib/extension-convert.pl name
Converts an extension in the F<extensions/> directory into the new
extension layout for Bugzilla 3.6.
......@@ -29,10 +29,10 @@ use Image::Magick;
our $VERSION = '1.0';
sub attachment_process_data {
my ($self, $params) = @_;
return unless $params->{attributes}->{mimetype} eq 'image/bmp';
my ($self, $args) = @_;
return unless $args->{attributes}->{mimetype} eq 'image/bmp';
my $data = ${$params->{data}};
my $data = ${$args->{data}};
my $img = Image::Magick->new(magick => 'bmp');
# $data is a filehandle.
......@@ -49,9 +49,9 @@ sub attachment_process_data {
}
undef $img;
${$params->{data}} = $data;
$params->{attributes}->{mimetype} = 'image/png';
$params->{attributes}->{filename} =~ s/^(.+)\.bmp$/$1.png/i;
${$args->{data}} = $data;
$args->{attributes}->{mimetype} = 'image/png';
$args->{attributes}->{filename} =~ s/^(.+)\.bmp$/$1.png/i;
}
__PACKAGE__->NAME;
......@@ -35,9 +35,9 @@ use Data::Dumper;
our $VERSION = '1.0';
sub attachment_process_data {
my ($self, $params) = @_;
my $type = $params->{attributes}->{mimetype};
my $filename = $params->{attributes}->{filename};
my ($self, $args) = @_;
my $type = $args->{attributes}->{mimetype};
my $filename = $args->{attributes}->{filename};
# Make sure images have the correct extension.
# Uncomment the two lines below to make this check effective.
......@@ -45,44 +45,44 @@ sub attachment_process_data {
my $format = $1;
if ($filename =~ /^(.+)(:?\.[^\.]+)$/) {
my $name = $1;
#$params->{attributes}->{filename} = "${name}.$format";
#$args->{attributes}->{filename} = "${name}.$format";
}
else {
# The file has no extension. We append it.
#$params->{attributes}->{filename} .= ".$format";
#$args->{attributes}->{filename} .= ".$format";
}
}
}
sub auth_login_methods {
my ($self, $params) = @_;
my $modules = $params->{modules};
my ($self, $args) = @_;
my $modules = $args->{modules};
if (exists $modules->{Example}) {
$modules->{Example} = 'Bugzilla/Extension/Example/Auth/Login.pm';
}
}
sub auth_verify_methods {
my ($self, $params) = @_;
my $modules = $params->{modules};
my ($self, $args) = @_;
my $modules = $args->{modules};
if (exists $modules->{Example}) {
$modules->{Example} = 'Bugzilla/Extension/Example/Auth/Verify.pm';
}
}
sub bug_columns {
my ($self, $params) = @_;
my $columns = $params->{'columns'};
my ($self, $args) = @_;
my $columns = $args->{'columns'};
push (@$columns, "delta_ts AS example")
}
sub bug_end_of_create {
my ($self, $params) = @_;
my ($self, $args) = @_;
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
my $bug = $params->{'bug'};
my $timestamp = $params->{'timestamp'};
my $bug = $args->{'bug'};
my $timestamp = $args->{'timestamp'};
my $bug_id = $bug->id;
# Uncomment this line to see a line in your webserver's error log whenever
......@@ -91,11 +91,11 @@ sub bug_end_of_create {
}
sub bug_end_of_create_validators {
my ($self, $params) = @_;
my ($self, $args) = @_;
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
my $bug_params = $params->{'params'};
my $bug_params = $args->{'params'};
# Uncomment this line below to see a line in your webserver's error log
# containing all validated bug field values every time you file a bug.
......@@ -107,11 +107,11 @@ sub bug_end_of_create_validators {
}
sub bug_end_of_update {
my ($self, $params) = @_;
my ($self, $args) = @_;
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
my ($bug, $timestamp, $changes) = @$params{qw(bug timestamp changes)};
my ($bug, $timestamp, $changes) = @$args{qw(bug timestamp changes)};
foreach my $field (keys %$changes) {
my $used_to_be = $changes->{$field}->[0];
......@@ -140,19 +140,19 @@ sub bug_end_of_update {
}
sub bug_fields {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $fields = $params->{'fields'};
my $fields = $args->{'fields'};
push (@$fields, "example")
}
sub bug_format_comment {
my ($self, $params) = @_;
my ($self, $args) = @_;
# This replaces every occurrence of the word "foo" with the word
# "bar"
my $regexes = $params->{'regexes'};
my $regexes = $args->{'regexes'};
push(@$regexes, { match => qr/\bfoo\b/, replace => 'bar' });
# And this links every occurrence of the word "bar" to example.com,
......@@ -168,48 +168,48 @@ sub bug_format_comment {
# Used by bug_format_comment--see its code for an explanation.
sub _replace_bar {
my $params = shift;
my $args = shift;
# $match is the first parentheses match in the $bar_match regex
# in bug-format_comment.pl. We get up to 10 regex matches as
# arguments to this function.
my $match = $params->{matches}->[0];
my $match = $args->{matches}->[0];
# Remember, you have to HTML-escape any data that you are returning!
$match = html_quote($match);
return qq{<a href="http://example.com/">$match</a>};
};
sub buglist_columns {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $columns = $params->{'columns'};
my $columns = $args->{'columns'};
$columns->{'example'} = { 'name' => 'bugs.delta_ts' , 'title' => 'Example' };
}
sub colchange_columns {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $columns = $params->{'columns'};
my $columns = $args->{'columns'};
push (@$columns, "example")
}
sub config {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $config = $params->{config};
my $config = $args->{config};
$config->{Example} = "Bugzilla::Extension::Example::Config";
}
sub config_add_panels {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $modules = $params->{panel_modules};
my $modules = $args->{panel_modules};
$modules->{Example} = "Bugzilla::Extension::Example::Config";
}
sub config_modify_panels {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $panels = $params->{panels};
my $panels = $args->{panels};
# Add the "Example" auth methods.
my $auth_params = $panels->{'auth'}->{params};
......@@ -221,11 +221,11 @@ sub config_modify_panels {
}
sub flag_end_of_update {
my ($self, $params) = @_;
my ($self, $args) = @_;
# This code doesn't actually *do* anything, it's just here to show you
# how to use this hook.
my $flag_params = $params;
my $flag_params = $args;
my ($object, $timestamp, $old_flags, $new_flags) =
@$flag_params{qw(object timestamp old_flags new_flags)};
my ($removed, $added) = diff_arrays($old_flags, $new_flags);
......@@ -244,14 +244,14 @@ sub flag_end_of_update {
}
sub install_before_final_checks {
my ($self, $params) = @_;
print "Install-before_final_checks hook\n" unless $params->{silent};
my ($self, $args) = @_;
print "Install-before_final_checks hook\n" unless $args->{silent};
}
sub mailer_before_send {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $email = $params->{email};
my $email = $args->{email};
# If you add a header to an email, it's best to start it with
# 'X-Bugzilla-<Extension>' so that you don't conflict with
# other extensions.
......@@ -259,10 +259,10 @@ sub mailer_before_send {
}
sub object_before_create {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $class = $params->{'class'};
my $object_params = $params->{'params'};
my $class = $args->{'class'};
my $object_params = $args->{'params'};
# Note that this is a made-up class, for this example.
if ($class->isa('Bugzilla::ExampleObject')) {
......@@ -273,9 +273,9 @@ sub object_before_create {
}
sub object_before_set {
my ($self, $params) = @_;
my ($self, $args) = @_;
my ($object, $field, $value) = @$params{qw(object field value)};
my ($object, $field, $value) = @$args{qw(object field value)};
# Note that this is a made-up class, for this example.
if ($object->isa('Bugzilla::ExampleObject')) {
......@@ -285,10 +285,10 @@ sub object_before_set {
}
sub object_end_of_create_validators {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $class = $params->{'class'};
my $object_params = $params->{'params'};
my $class = $args->{'class'};
my $object_params = $args->{'params'};
# Note that this is a made-up class, for this example.
if ($class->isa('Bugzilla::ExampleObject')) {
......@@ -299,10 +299,10 @@ sub object_end_of_create_validators {
}
sub object_end_of_set_all {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $object = $params->{'class'};
my $object_params = $params->{'params'};
my $object = $args->{'class'};
my $object_params = $args->{'params'};
# Note that this is a made-up class, for this example.
if ($object->isa('Bugzilla::ExampleObject')) {
......@@ -314,10 +314,10 @@ sub object_end_of_set_all {
}
sub object_end_of_update {
my ($self, $params) = @_;
my ($self, $args) = @_;
my ($object, $old_object, $changes) =
@$params{qw(object old_object changes)};
@$args{qw(object old_object changes)};
# Note that this is a made-up class, for this example.
if ($object->isa('Bugzilla::ExampleObject')) {
......@@ -329,9 +329,9 @@ sub object_end_of_update {
}
sub page_before_template {
my ($self, $params) = @_;
my ($self, $args) = @_;
my ($vars, $page) = @$params{qw(vars page_id)};
my ($vars, $page) = @$args{qw(vars page_id)};
# You can see this hook in action by loading page.cgi?id=example.html
if ($page eq 'example.html') {
......@@ -340,19 +340,19 @@ sub page_before_template {
}
sub product_confirm_delete {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $vars = $params->{vars};
my $vars = $args->{vars};
$vars->{'example'} = 1;
}
sub sanitycheck_check {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $dbh = Bugzilla->dbh;
my $sth;
my $status = $params->{'status'};
my $status = $args->{'status'};
# Check that all users are Australian
$status->('example_check_au_user');
......@@ -374,12 +374,12 @@ sub sanitycheck_check {
}
sub sanitycheck_repair {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $cgi = Bugzilla->cgi;
my $dbh = Bugzilla->dbh;
my $status = $params->{'status'};
my $status = $args->{'status'};
if ($cgi->param('example_repair_au_user')) {
$status->('example_repair_au_user_start');
......@@ -393,9 +393,9 @@ sub sanitycheck_repair {
}
sub template_before_create {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $config = $params->{'config'};
my $config = $args->{'config'};
# This will be accessible as "example_global_variable" in every
# template in Bugzilla. See Bugzilla/Template.pm's create() function
# for more things that you can set.
......@@ -403,9 +403,9 @@ sub template_before_create {
}
sub template_before_process {
my ($self, $params) = @_;
my ($self, $args) = @_;
my ($vars, $file, $template) = @$params{qw(vars file template)};
my ($vars, $file, $template) = @$args{qw(vars file template)};
$vars->{'example'} = 1;
......@@ -415,16 +415,16 @@ sub template_before_process {
}
sub webservice {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $dispatch = $params->{dispatch};
my $dispatch = $args->{dispatch};
$dispatch->{Example} = "Bugzilla::Extension::Example::WebService";
}
sub webservice_error_codes {
my ($self, $params) = @_;
my ($self, $args) = @_;
my $error_map = $params->{error_map};
my $error_map = $args->{error_map};
$error_map->{'example_my_error'} = 10001;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment