From 710e7747d2fdf234a89c94b11854012f3476637c Mon Sep 17 00:00:00 2001 From: vfilippov Date: Tue, 23 Aug 2011 15:27:12 +0000 Subject: [PATCH] Bug 46221, Bug 70605 - use Lingua::Stem::Snowball instead of hard-coded russian stemmer, store tsvectors in PostgreSQL fulltext tables for ts_rank() performance git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@1344 6955db30-a419-402b-8a0d-67ecbb4d7f56 --- Bugzilla/Bug.pm | 36 ++- Bugzilla/Config/Query.pm | 9 +- Bugzilla/Constants.pm | 28 +- Bugzilla/DB.pm | 28 ++ Bugzilla/DB/Mysql.pm | 4 + Bugzilla/DB/Pg.pm | 18 +- Bugzilla/DB/Schema.pm | 22 +- Bugzilla/DB/Schema/Pg.pm | 52 +++- Bugzilla/Install/DB.pm | 83 +++--- Bugzilla/Install/Localconfig.pm | 8 - Bugzilla/Install/Requirements.pm | 8 + Bugzilla/Search.pm | 17 +- Bugzilla/Util.pm | 49 ++-- Lingua/Stem/RuUTF8.pm | 252 ------------------ checksetup.sh | 2 +- extensions/Example/code/auth-login_methods.pl | 27 ++ .../Example/code/auth-verify_methods.pl | 27 ++ extensions/Example/code/bug-columns.pl | 27 ++ extensions/Example/code/bug-end_of_create.pl | 35 +++ extensions/Example/code/bug-end_of_update.pl | 56 ++++ extensions/Example/code/bug-fields.pl | 27 ++ extensions/Example/code/buglist-columns.pl | 26 ++ extensions/Example/code/colchange-columns.pl | 27 ++ extensions/Example/code/config-add_panels.pl | 25 ++ .../Example/code/config-modify_panels.pl | 32 +++ extensions/Example/code/config.pl | 26 ++ extensions/Example/code/flag-end_of_update.pl | 42 +++ .../code/install-before_final_checks.pl | 28 ++ extensions/Example/code/mailer-before_send.pl | 28 ++ .../Example/code/page-before_template.pl | 33 +++ .../Example/code/product-confirm_delete.pl | 26 ++ .../Example/code/webservice-error_codes.pl | 25 ++ extensions/Example/code/webservice.pl | 25 ++ extensions/Example/disabled | 0 extensions/custis/lib/CustisDBHooks.pm | 2 +- qa/t/webservice_bug_add_comment.t | 2 +- .../en/default/admin/params/query.html.tmpl | 55 ++-- template/en/default/setup/strings.txt.pl | 1 + 38 files changed, 829 insertions(+), 389 deletions(-) delete mode 100644 Lingua/Stem/RuUTF8.pm create mode 100644 extensions/Example/code/auth-login_methods.pl create mode 100644 extensions/Example/code/auth-verify_methods.pl create mode 100644 extensions/Example/code/bug-columns.pl create mode 100644 extensions/Example/code/bug-end_of_create.pl create mode 100644 extensions/Example/code/bug-end_of_update.pl create mode 100644 extensions/Example/code/bug-fields.pl create mode 100644 extensions/Example/code/buglist-columns.pl create mode 100644 extensions/Example/code/colchange-columns.pl create mode 100644 extensions/Example/code/config-add_panels.pl create mode 100644 extensions/Example/code/config-modify_panels.pl create mode 100644 extensions/Example/code/config.pl create mode 100644 extensions/Example/code/flag-end_of_update.pl create mode 100644 extensions/Example/code/install-before_final_checks.pl create mode 100644 extensions/Example/code/mailer-before_send.pl create mode 100644 extensions/Example/code/page-before_template.pl create mode 100644 extensions/Example/code/product-confirm_delete.pl create mode 100644 extensions/Example/code/webservice-error_codes.pl create mode 100644 extensions/Example/code/webservice.pl create mode 100644 extensions/Example/disabled diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index 1429a8207..65ca20cf6 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -1141,42 +1141,38 @@ sub _extract_multi_selects { # Should be called any time you update short_desc or change a comment. sub _sync_fulltext { + use utf8; my ($self, $new_bug) = @_; my $dbh = Bugzilla->dbh; my ($short_desc) = $dbh->selectrow_array( "SELECT short_desc FROM bugs WHERE bug_id=?", undef, $self->id ); - my @comments = @{ $dbh->selectall_arrayref( + my ($nopriv, $priv) = ([], []); + for (@{ $dbh->selectall_arrayref( "SELECT thetext, isprivate FROM longdescs WHERE bug_id=?", undef, $self->id - ) || [] }; - my @no_private = grep { !$_->[1] } @comments; - my $all = join "\n", map { $_->[0] } @comments; - my $nopriv = join "\n", map { $_->[0] } @no_private; - # Bug 46221 - Russian Snowball Stemmer in Bugzilla fulltext search - # Not for PostgreSQL: it has built-in Snowball stemmer - if (!$dbh->isa('Bugzilla::DB::Pg')) + ) || [] }) { - use utf8; - $short_desc = stem_text($short_desc); - $all = stem_text($all); - $nopriv = stem_text($nopriv); + $_->[1] ? push @$priv, $_->[0] : push @$nopriv, $_->[0]; } - # O_o как оно может быть здесь tainted - непонятно, но иногда стреляет - trick_taint($short_desc); - trick_taint($all); - trick_taint($nopriv); - my @bind = ($short_desc, $all, $nopriv, $self->id); + $nopriv = join "\n", @$nopriv; + $priv = join "\n", @$priv; + my $row = [ $short_desc, $nopriv, $priv ]; + $_ = $dbh->quote_fulltext($_) for @$row; + ## O_o Don't know how can it be tainted here, sometimes it was. Checking if it goes away. + #trick_taint($row); my $sql; if ($new_bug) { - $sql = "INSERT INTO bugs_fulltext (short_desc, comments, comments_noprivate, bug_id) VALUES (?, ?, ?, ?)"; + $sql = "INSERT INTO bugs_fulltext (bug_id, short_desc, comments, comments_private)". + " VALUES (".join(',', $self->id, @$row).")"; } else { - $sql = "UPDATE bugs_fulltext SET short_desc=?, comments=?, comments_noprivate=? WHERE bug_id=?"; + $sql = "UPDATE bugs_fulltext SET short_desc=$row->[0],". + " comments=$row->[1], comments_private=$row->[2] WHERE bug_id=".$self->id; } - return $dbh->do($sql, undef, @bind); + return $dbh->do($sql); } # This is the correct way to delete bugs from the DB. diff --git a/Bugzilla/Config/Query.pm b/Bugzilla/Config/Query.pm index 808a9a102..544088810 100644 --- a/Bugzilla/Config/Query.pm +++ b/Bugzilla/Config/Query.pm @@ -71,8 +71,13 @@ sub get_param_list { name => 'specific_search_allow_empty_words', type => 'b', default => 1 - } - + }, + + { + name => 'stem_language', + type => 't', + default => 'en', + }, ); return @param_list; } diff --git a/Bugzilla/Constants.pm b/Bugzilla/Constants.pm index 92696b9cc..449752197 100644 --- a/Bugzilla/Constants.pm +++ b/Bugzilla/Constants.pm @@ -176,8 +176,11 @@ use Cwd qw(abs_path); PASSWORD_DIGEST_ALGORITHM PASSWORD_SALT_LENGTH - + CGI_URI_LIMIT + + LANG_ISO_FULL + LANG_FULL_ISO ); @Bugzilla::Constants::EXPORT_OK = qw(contenttypes); @@ -510,6 +513,29 @@ use constant PASSWORD_SALT_LENGTH => 8; # can be safely done or not based on the web server's URI length setting. use constant CGI_URI_LIMIT => 8000; +# Full language names corresponding to 2-letter ISO codes +# Used to select stemming language in fulltext search +use constant LANG_ISO_FULL => { + da => 'danish', + nl => 'dutch', + en => 'english', + fi => 'finnish', + fr => 'french', + de => 'german', + hu => 'hungarian', + it => 'italian', + no => 'norwegian', + pt => 'portuguese', + ro => 'romanian', + ru => 'russian', + es => 'spanish', + sv => 'swedish', + tr => 'turkish', +}; + +# The reverse of LANG_ISO_FULL +use constant LANG_FULL_ISO => { reverse %{LANG_ISO_FULL()} }; + sub bz_locations { # We know that Bugzilla/Constants.pm must be in %INC at this point. # So the only question is, what's the name of the directory diff --git a/Bugzilla/DB.pm b/Bugzilla/DB.pm index 16478b38a..50fe48aa7 100644 --- a/Bugzilla/DB.pm +++ b/Bugzilla/DB.pm @@ -96,6 +96,20 @@ sub connect_main { $lc->{db_sock}, $lc->{db_user}, $lc->{db_pass}); } +sub connect_sphinx +{ + return undef unless Bugzilla->params->{use_sphinx}; + my $host = Bugzilla->params->{sphinxql_host}; + my $port = Bugzilla->params->{sphinxql_port}; + my $socket = Bugzilla->params->{sphinxql_socket}; + $host = [ $host ? "host=$host" : () ]; + push @$host, "port=$port" if $port; + push @$host, "mysql_socket=$socket" if $socket; + my $sphinx = DBI->connect("DBI:mysql:".join(';', @$host), undef, undef, { mysql_auto_reconnect => 1 }); + die $DBI::errstr if !$sphinx; + return $sphinx; +} + sub _connect { my ($driver, $host, $dbname, $port, $sock, $user, $pass) = @_; @@ -361,6 +375,10 @@ sub sql_fulltext_search # standard ANSI SQL, without real full text search support. DB specific # modules should override this, as this will be always much slower. + # stem text + my $lang = LANG_FULL_ISO->{lc(Bugzilla->params->{stem_language}||'')} || 'en'; + $text = stem_text($text, $lang); + # make the string lowercase to do case insensitive search my $lower_text = lc($text); @@ -396,6 +414,16 @@ sub sql_fulltext_search return ($term, $term); } +# Prepare string for inserting into full-text table and return the SQL expression +# Individual DB implementations should override this if they have built-in stemmer +sub quote_fulltext +{ + my $self = shift; + my ($a) = @_; + my $lang = LANG_FULL_ISO->{lc(Bugzilla->params->{stem_language}||'')} || 'en'; + return $self->quote(stem_text($a, $lang)); +} + ##################################################################### # General Info Methods ##################################################################### diff --git a/Bugzilla/DB/Mysql.pm b/Bugzilla/DB/Mysql.pm index 49af08b10..bb3ccfb88 100644 --- a/Bugzilla/DB/Mysql.pm +++ b/Bugzilla/DB/Mysql.pm @@ -178,6 +178,10 @@ sub sql_fulltext_search { my ($self, $column, $text) = @_; + # stem text + my $lang = LANG_FULL_ISO->{lc(Bugzilla->params->{stem_language}||'')} || 'en'; + $text = stem_text($text, $lang); + # quote un-quoted compound words my @words = quotewords('[\s()]+', 'delimiters', $text); if ($text =~ /(?:^|\W)[+\-<>~"()]/ || $text =~ /[()"*](?:$|\W)/) diff --git a/Bugzilla/DB/Pg.pm b/Bugzilla/DB/Pg.pm index e0ee9fd94..db4d0b9e3 100644 --- a/Bugzilla/DB/Pg.pm +++ b/Bugzilla/DB/Pg.pm @@ -44,6 +44,7 @@ package Bugzilla::DB::Pg; use strict; use Bugzilla::Error; +use Bugzilla::Constants qw(LANG_ISO_FULL); use DBD::Pg; # This module extends the DB interface via inheritance @@ -187,19 +188,26 @@ sub sql_fulltext_search { my $self = shift; my ($column, $text) = @_; - my $language = Bugzilla->localconfig->{postgres_fulltext_language} || 'english'; - $language = $self->quote($language).','; $text = $self->quote($text); # Try to_tsquery, and use plainto_tsquery if the syntax is incorrect - # FIXME reporting errors to user would be useful here + # FIXME reporting query parse errors to user would be useful here eval { $self->do("SELECT to_tsquery($language$text)") }; my $op = $@ ? 'plainto_tsquery' : 'to_tsquery'; return ( - "(to_tsvector($language$column) \@\@ $op($language$text))", - "(ts_rank(to_tsvector($language$column), $op($language$text)))", + "($column \@\@ $op($language$text))", + "(ts_rank($column, $op($language$text)))", ); } +# PostgreSQL has built-in stemmers +sub quote_fulltext +{ + my $self = shift; + my ($a) = @_; + my $lang = LANG_ISO_FULL->{Bugzilla->params->{stem_language}} || 'english'; + return "to_tsvector('$lang',".$self->quote($a).")"; +} + sub real_table_list { my $self = shift; diff --git a/Bugzilla/DB/Schema.pm b/Bugzilla/DB/Schema.pm index b01757835..b859c80a9 100644 --- a/Bugzilla/DB/Schema.pm +++ b/Bugzilla/DB/Schema.pm @@ -315,24 +315,30 @@ use constant ABSTRACT_SCHEMA => { bugs_fulltext => { FIELDS => [ - bug_id => {TYPE => 'INT3', NOTNULL => 1, PRIMARYKEY => 1, - REFERENCES => {TABLE => 'bugs', - COLUMN => 'bug_id', - DELETE => 'CASCADE'}}, + bug_id => {TYPE => 'INT3', NOTNULL => 1, PRIMARYKEY => 1, + REFERENCES => {TABLE => 'bugs', + COLUMN => 'bug_id', + DELETE => 'CASCADE'}}, short_desc => {TYPE => 'varchar(255)', NOTNULL => 1}, # Comments are stored all together in one column for searching. # This allows us to examine all comments together when deciding # the relevance of a bug in fulltext search. - comments => {TYPE => 'LONGTEXT'}, - comments_noprivate => {TYPE => 'LONGTEXT'}, + comments => {TYPE => 'LONGTEXT'}, + # Original Bugzilla stored each non-private comment 2 times: + # one time in comments and one time in comments_noprivate. + # As most of comments are non-private, this leads to fulltext + # index being approx. 2 times larger than it must be. That's + # not really good. So we store non-private comments in 'comments' + # fields and private ones in 'comments_private'. + comments_private => {TYPE => 'LONGTEXT'}, ], INDEXES => [ bugs_fulltext_short_desc_idx => {FIELDS => ['short_desc'], TYPE => 'FULLTEXT'}, bugs_fulltext_comments_idx => {FIELDS => ['comments'], TYPE => 'FULLTEXT'}, - bugs_fulltext_comments_noprivate_idx => { - FIELDS => ['comments_noprivate'], TYPE => 'FULLTEXT'}, + bugs_fulltext_comments_private_idx => { + FIELDS => ['comments_private'], TYPE => 'FULLTEXT'}, ], }, diff --git a/Bugzilla/DB/Schema/Pg.pm b/Bugzilla/DB/Schema/Pg.pm index f1e042fd4..79eeb0f64 100644 --- a/Bugzilla/DB/Schema/Pg.pm +++ b/Bugzilla/DB/Schema/Pg.pm @@ -72,15 +72,61 @@ sub _initialize { } #eosub--_initialize #-------------------------------------------------------------------- +sub _get_create_table_ddl +{ + my ($self, $table) = @_; + + my $thash = $self->{schema}{$table}; + die "Table $table does not exist in the database schema." + unless ref $thash; + + # Find fulltext fields + my %is_ft; + for (my $i = 1; $i < @{$thash->{INDEXES}}; $i += 2) + { + if ($thash->{INDEXES}->[$i]->{TYPE} eq 'FULLTEXT') + { + $is_ft{$_} = 1 for @{$thash->{INDEXES}->[$i]->{FIELDS}}; + } + } + + my $create_table = "CREATE TABLE $table \(\n"; + + my @fields = @{ $thash->{FIELDS} }; + while (@fields) + { + my $field = shift @fields; + my $finfo = shift @fields; + $create_table .= "\t$field\t"; + if ($is_ft{$field}) + { + # Don't store the contents of fulltext fields in PostgreSQL, + # just store the real tsvector's. This allows optimal performance + # while ranking results. + $create_table .= 'tsvector'; + } + else + { + $create_table .= $self->get_type_ddl($finfo); + } + $create_table .= "," if @fields; + $create_table .= "\n"; + } + + $create_table .= "\)"; + + return $create_table; +} + sub _get_create_index_ddl { my $self = shift; my ($table, $name, $index_fields, $index_type) = @_; if ($index_type && $index_type eq 'FULLTEXT') { - $index_fields = @$index_fields > 1 ? join(" || ' ' || ", @$index_fields) : $index_fields->[0]; - my $language = Bugzilla->localconfig->{postgres_fulltext_language} || 'english'; - return "CREATE INDEX $name ON $table USING gin(to_tsvector('$language', $index_fields))"; + # Override fulltext index creation clause + $index_fields = join(" || ", @$index_fields); + return "CREATE INDEX $name ON $table USING gin($index_fields)"; } return $self->SUPER::_get_create_index_ddl(@_); } diff --git a/Bugzilla/Install/DB.pm b/Bugzilla/Install/DB.pm index 7f7a9267a..a11fca587 100644 --- a/Bugzilla/Install/DB.pm +++ b/Bugzilla/Install/DB.pm @@ -33,6 +33,7 @@ use Bugzilla::Series; use Date::Parse; use Date::Format; use IO::File; +use Time::HiRes qw(time); # NOTE: This is NOT the function for general table updates. See # update_table_definitions for that. This is only for the fielddefs table. @@ -444,7 +445,7 @@ sub update_table_definitions { {TYPE => 'varchar(16)', PRIMARYKEY => 1, NOTNULL => 1}); _clean_control_characters_from_short_desc(); - + # 2005-12-07 altlst@sonic.net -- Bug 225221 $dbh->bz_add_column('longdescs', 'comment_id', {TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1}); @@ -532,7 +533,7 @@ sub update_table_definitions { # 2007-08-21 wurblzap@gmail.com - Bug 365378 _make_lang_setting_dynamic(); - + # 2007-11-29 xiaoou.wu@oracle.com - Bug 153129 _change_text_types(); @@ -544,6 +545,16 @@ sub update_table_definitions { {TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0}); $dbh->bz_drop_index('longdescs', 'longdescs_thetext_idx'); + + # Change fulltext index type to new version (comments/comments_private) + # We'll have to reindex anyway, so recreate the table + if ($dbh->bz_column_info(bugs_fulltext => 'comments_noprivate')) + { + $dbh->bz_drop_table('bugs_fulltext'); + $dbh->bz_add_table('bugs_fulltext'); + } + + # Populate fulltext index _populate_bugs_fulltext(); # 2008-01-18 xiaoou.wu@oracle.com - Bug 414292 @@ -572,7 +583,7 @@ sub update_table_definitions { # 2009-01-16 oreomike@gmail.com - Bug 302420 $dbh->bz_add_column('whine_events', 'mailifnobugs', { TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'}); - + _convert_disallownew_to_isactive(); $dbh->bz_alter_column('bugs_activity', 'added', @@ -3236,7 +3247,7 @@ sub _add_foreign_keys_to_multiselects { $dbh->bz_add_fk("bug_$name", "bug_id", {TABLE => 'bugs', COLUMN => 'bug_id', DELETE => 'CASCADE',}); - + $dbh->bz_add_fk("bug_$name", "value", {TABLE => $name, COLUMN => 'value', DELETE => 'RESTRICT',}); @@ -3252,8 +3263,11 @@ sub _populate_bugs_fulltext my $bug_ids = shift; $bug_ids = undef if $bug_ids && !@$bug_ids; my $dbh = Bugzilla->dbh; - my $fulltext = $dbh->selectrow_array - ("SELECT 1 FROM bugs_fulltext ".$dbh->sql_limit(1)); + my $sphinx; + my ($table, $limit1, $id) = ('bugs_fulltext', $dbh->sql_limit(1), 'bug_id'); + my $fulltext = $dbh->selectrow_array("SELECT $id FROM $table $limit1"); + my ($datasize, $time) = (0, time); + my ($lastdata, $lasttime) = ($datasize, $time); # We only populate the table if it's empty or if we've been given a # set of bug ids. if ($bug_ids || !$fulltext) @@ -3262,13 +3276,10 @@ sub _populate_bugs_fulltext $bug_ids ||= $dbh->selectcol_arrayref("SELECT bug_id FROM bugs"); return if !$bug_ids; - # Bug 46221 - Russian Stemming in Bugzilla fulltext search - # We can't use GROUP_CONCAT because we need to stem each word - # And there could be tons of bugs, so we'll use N-bug portions - print "Populating bugs_fulltext... (this can take a long time.)\n"; + # There could be tons of bugs, so we'll use 256-bug portions + print "Populating full-text index... (this can take a long time.)\n"; my ($portion, $done, $total) = (256, 0, scalar @$bug_ids); my ($short, $all, $nopriv, $wh, $rows); - my ($sth, $sth_del, $sthn) = (undef, undef, 0); while (my @ids = splice @$bug_ids, 0, $portion) { $rows = {}; @@ -3280,35 +3291,43 @@ sub _populate_bugs_fulltext "SELECT bug_id, thetext, isprivate FROM longdescs WHERE $wh", undef, @ids ); - $rows->{$_->[0]} = [ $_->[1], '', '' ] for @$short; - for (@$all) + # Local block with 'use bytes' for counting data size in MB { - $rows->{$_->[0]}->[1] .= $_->[1] . "\n"; - $rows->{$_->[0]}->[2] .= $_->[1] . "\n" - unless $_->[2]; - } - if (!$dbh->isa('Bugzilla::DB::Pg')) - { - for (keys %$rows) + use bytes; + for (@$short) { - $_ = stem_text($_) for @{$rows->{$_}}; + $rows->{$_->[0]} = [ $_->[1], '', '' ]; + $datasize += length $_->[1]; + } + # Comments divide into non-private and private + for (@$all) + { + $rows->{$_->[0]}->[$_->[2] ? 2 : 1] .= $_->[1] . "\n"; + $datasize += length($_->[1])+1; } } - if ($sthn != @ids) + for (keys %$rows) { - # Optimization: cache prepared statements - $sthn = @ids; - $sth_del = $dbh->prepare("DELETE FROM bugs_fulltext WHERE bug_id IN (".join(",", ("?") x $sthn).")"); - $sth = $dbh->prepare( - "INSERT INTO bugs_fulltext (bug_id, short_desc, comments, comments_noprivate)" . - " VALUES " . join(",", ("(?,?,?,?)") x $sthn) - ); + Encode::_utf8_off($_) for @{$rows->{$_}}; } - $sth_del->execute(@ids); - $sth->execute(map { ($_, @{$rows->{$_}}) } @ids); + for (keys %$rows) + { + # CustIS Bug 46221 - Snowball stemmers in Bugzilla fulltext search + $rows->{$_} = join ', ', map { $dbh->quote_fulltext($_) } @{$rows->{$_}}; + } + $dbh->do("DELETE FROM $table WHERE $id IN (".join(',', @ids).')'); + $dbh->do( + "INSERT INTO $table ($id, short_desc, comments, comments_private) VALUES ". + join(", ", map { "($_, $rows->{$_})" } @ids) + ); $done += @ids; - print "\r$done / $total ..."; + print "\r$done / $total, ".sprintf("%.2f MB, %d KB/s", $datasize/1048576, ($datasize-$lastdata)/1024/(time-$lasttime)); + print " ..."; + ($lastdata, $lasttime) = ($datasize, time); } + $time = time-$time; + print sprintf("\nTime: %02d min %02d sec, %d KB/s", + int($time/60), $time % 60, $datasize/1024/$time); print "\n"; } } diff --git a/Bugzilla/Install/Localconfig.pm b/Bugzilla/Install/Localconfig.pm index e0b74a100..e15e23507 100644 --- a/Bugzilla/Install/Localconfig.pm +++ b/Bugzilla/Install/Localconfig.pm @@ -214,14 +214,6 @@ EOT # such as bug changes. A random string is generated by default. # It's very important that this key is kept secret. It also must be # very long. -EOT - }, - { - name => 'postgres_fulltext_language', - default => 'english', - desc => < '0.05', feature => ['rand_security'], }, + + # Snowball stemmers in full-text search + { + package => 'Lingua-Stem-Snowball', + module => 'Lingua::Stem::Snowball', + version => 0, + feature => ['fulltext_stem'], + }, ); my $extra_modules = _get_extension_requirements('OPTIONAL_MODULES'); diff --git a/Bugzilla/Search.pm b/Bugzilla/Search.pm index 8b3e85eaa..30bc0d20d 100644 --- a/Bugzilla/Search.pm +++ b/Bugzilla/Search.pm @@ -2000,20 +2000,21 @@ sub _content_matches my $dbh = Bugzilla->dbh; my $table = "bugs_fulltext_".$self->{sequence}; - my $comments_col = "comments"; - $comments_col = "comments_noprivate" unless $self->{user}->is_insider; # Create search terms to add to the SELECT and WHERE clauses. - my $text = stem_text($self->{value}); - my ($term1, $rterm1) = $dbh->sql_fulltext_search("bugs_fulltext.$comments_col", $text); - my ($term2, $rterm2) = $dbh->sql_fulltext_search("bugs_fulltext.short_desc", $text); + # These are (search term, rank term, search term, rank term, ...) + my $text = $self->{value}; + my @terms = ( + $dbh->sql_fulltext_search("bugs_fulltext.short_desc", $text), + $dbh->sql_fulltext_search("bugs_fulltext.comments", $text), + ); + push @terms, $dbh->sql_fulltext_search("bugs_fulltext.comments_private", $text) if $self->{user}->is_insider; # Bug 46221 - Russian Stemming in Bugzilla fulltext search # MATCH(...) OR MATCH(...) is very slow in MySQL (and probably in other DBs): # -- it does no fulltext index merge optimization. So use JOIN to UNION. $self->{term} = { - table => "(SELECT bug_id FROM bugs_fulltext WHERE $term1 UNION ". - "SELECT bug_id FROM bugs_fulltext WHERE $term2) $table", + table => "(".join(" UNION ", map { "SELECT bug_id FROM bugs_fulltext WHERE $terms[$_]" } grep { !($_&1) } 0..$#terms).") $table", bugid_field => "$table.bug_id", }; @@ -2025,7 +2026,7 @@ sub _content_matches # this adds more terms to the relevance sql. if (!$self->{negated}) { - push @{COLUMNS->{relevance}->{bits}}, $rterm1, $rterm2; + push @{COLUMNS->{relevance}->{bits}}, @terms[grep { $_&1 } 0..$#terms]; COLUMNS->{relevance}->{name} = "(SELECT ".join("+", @{COLUMNS->{relevance}->{bits}}). " FROM bugs_fulltext WHERE bugs_fulltext.bug_id=bugs.bug_id)"; } diff --git a/Bugzilla/Util.pm b/Bugzilla/Util.pm index 990ad0726..6b03a3ded 100644 --- a/Bugzilla/Util.pm +++ b/Bugzilla/Util.pm @@ -63,7 +63,7 @@ use Text::Wrap; use Text::TabularDisplay::Utf8; use JSON; -use Lingua::Stem::RuUTF8; +eval { require 'Lingua/Stem/Snowball.pm' }; sub trick_taint { @@ -733,30 +733,34 @@ sub disable_utf8 { } } -# Bug 46221 - Russian Stemming in Bugzilla fulltext search +# CustIS Bug 46221 - Snowball Stemmers in MySQL fulltext search +my $snowballs = {}; sub stem_text { - my ($text, $allow_verbatim) = @_; + my ($text, $lang, $allow_verbatim) = @_; + return '' if !defined $text || $text =~ /^\s*$/so; + return $text if !$INC{'Lingua/Stem/Snowball.pm'}; + $lang ||= 'en'; Encode::_utf8_on($text) if Bugzilla->params->{utf8}; - $text = [ split /(?<=\w)(?=\W)|(?<=\W)(?=\w)/, $text ]; + # CustIS Bug 66033 - _ is wanted to also be a delimiter + $text = [ split /(\PL+)/, $text ]; + my $word = 1; + if ($text->[0] eq '') + { + $word = 0; + shift @$text; + } my $q = 0; + my $cache = (Bugzilla->request_cache->{stem_cache} ||= {}); + %$cache = () if keys(%$cache) > 65536; + my $stem = ($snowballs->{$lang} ||= Lingua::Stem::Snowball->new(lang => $lang, encoding => 'UTF-8')); + my $r = ''; for (@$text) { - unless (/\W/) + if ($word) { - # $q = 1 means verbatim - unless ($q) - { - if (/_/) - { - # CustIS Bug 66033 - $_ = join ' ', map { Lingua::Stem::RuUTF8::stem_word($_) } ($_, split(/_/, $_)); - } - else - { - $_ = Lingua::Stem::RuUTF8::stem_word($_); - } - } + # $q = 1 means we're inside quotes + $r .= ($cache->{$_} ||= $stem->stem($_)) unless $q; } else { @@ -765,13 +769,12 @@ sub stem_text # If $allow_verbatim is TRUE then text in "double quotes" doesn't stem $q = ($q + tr/\"/\"/) % 2; } - if (!/\s$/so) - { - $_ .= ' '; - } + $r .= $_; + $r .= ' ' if !/\s$/o; } + $word = !$word; } - return join '', @$text; + return $r; } sub intersect diff --git a/Lingua/Stem/RuUTF8.pm b/Lingua/Stem/RuUTF8.pm deleted file mode 100644 index 9b83ad14d..000000000 --- a/Lingua/Stem/RuUTF8.pm +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/perl -# Lingua::Stem::Ru - UTF-8 стеммер Портера - -package Lingua::Stem::RuUTF8; - -use utf8; -use strict; -use Exporter; -use Carp; -use vars qw (@ISA @EXPORT_OK @EXPORT %EXPORT_TAGS $VERSION); - -BEGIN { - @ISA = qw (Exporter); - @EXPORT = (); - @EXPORT_OK = qw (stem stem_word clear_stem_cache stem_caching); - %EXPORT_TAGS = (); -} -$VERSION = "0.01"; - -my $Stem_Caching = 0; -my $Stem_Cache = {}; - -my $VOWEL = qr/аеиоуыэюя/; -my $PERFECTIVEGROUND = qr/((ив|ивши|ившись|ыв|ывши|ывшись)|((?<=[ая])(в|вши|вшись)))$/; -my $REFLEXIVE = qr/(с[яь])$/; -my $ADJECTIVE = qr/(ее|ие|ые|ое|ими|ыми|ей|ий|ый|ой|ем|им|ым|ом|его|ого|ему|ому|их|ых|ую|юю|ая|яя|ою|ею)$/; -my $PARTICIPLE = qr/((ивш|ывш|ующ)|((?<=[ая])(ем|нн|вш|ющ|щ)))$/; -my $VERB = qr/((ила|ыла|ена|ейте|уйте|ите|или|ыли|ей|уй|ил|ыл|им|ым|ен|ило|ыло|ено|ят|ует|уют|ит|ыт|ены|ить|ыть|ишь|ую|ю)|((?<=[ая])(ла|на|ете|йте|ли|й|л|ем|н|ло|но|ет|ют|ны|ть|ешь|нно)))$/; -my $NOUN = qr/(а|ев|ов|ие|ье|е|иями|ями|ами|еи|ии|и|ией|ей|ой|ий|й|иям|ям|ием|ем|ам|ом|о|у|ах|иях|ях|ы|ь|ию|ью|ю|ия|ья|я)$/; -my $RVRE = qr/^(.*?[$VOWEL])(.*)$/; -my $DERIVATIONAL = qr/[^$VOWEL][$VOWEL]+[^$VOWEL]+[$VOWEL].*(?<=о)сть?$/; - -sub stem { - return [] if ($#_ == -1); - my $parm_ref; - if (ref $_[0]) { - $parm_ref = shift; - } else { - $parm_ref = { @_ }; - } - - my $words = []; - my $locale = 'ru'; - my $exceptions = {}; - foreach (keys %$parm_ref) { - my $key = lc ($_); - if ($key eq '-words') { - @$words = @{$parm_ref->{$key}}; - } elsif ($key eq '-exceptions') { - $exceptions = $parm_ref->{$key}; - } elsif ($key eq '-locale') { - $locale = $parm_ref->{$key}; - } else { - croak (__PACKAGE__ . "::stem() - Unknown parameter '$key' with value '$parm_ref->{$key}'\n"); - } - } - - local( $_ ); - foreach (@$words) { - # Flatten case - $_ = lc $_; - - # Check against exceptions list - if (exists $exceptions->{$_}) { - $_ = $exceptions->{$_}; - next; - } - - # Check against cache of stemmed words - my $original_word = $_; - if ($Stem_Caching && exists $Stem_Cache->{$original_word}) { - $_ = $Stem_Cache->{$original_word}; - next; - } - - $_ = stem_word($_); - - $Stem_Cache->{$original_word} = $_ if $Stem_Caching; - } - $Stem_Cache = {} if ($Stem_Caching < 2); - - return $words; -} - -sub stem_word { - my $word = lc shift; - - # Check against cache of stemmed words - if ($Stem_Caching && exists $Stem_Cache->{$word}) { - return $Stem_Cache->{$word}; - } - - my ($start, $RV) = $word =~ /$RVRE/; - return $word unless $RV; - - # Step 1 - unless ($RV =~ s/$PERFECTIVEGROUND//) { - $RV =~ s/$REFLEXIVE//; - - if ($RV =~ s/$ADJECTIVE//) { - $RV =~ s/$PARTICIPLE//; - } else { - $RV =~ s/$NOUN// unless $RV =~ s/$VERB//; - } - } - - # Step 2 - $RV =~ s/и$//; - - # Step 3 - $RV =~ s/ость?$// if $RV =~ /$DERIVATIONAL/; - - # Step 4 - unless ($RV =~ s/ь$//) { - $RV =~ s/ейше?//; - $RV =~ s/нн$/н/; - } - - return $start.$RV; -} - -sub stem_caching { - my $parm_ref; - if (ref $_[0]) { - $parm_ref = shift; - } else { - $parm_ref = { @_ }; - } - my $caching_level = $parm_ref->{-level}; - if (defined $caching_level) { - if ($caching_level !~ m/^[012]$/) { - croak(__PACKAGE__ . "::stem_caching() - Legal values are '0','1' or '2'. '$caching_level' is not a legal value"); - } - $Stem_Caching = $caching_level; - } - return $Stem_Caching; -} - -sub clear_stem_cache { - $Stem_Cache = {}; -} - -1; -__END__ - -=head1 NAME - -Lingua::Stem::RuUTF8 - Porter's stemming algorithm for Russian (UTF-8 only) - -=head1 SYNOPSIS - - use Lingua::Stem::RuUTF8; - my $stems = Lingua::Stem::RuUTF8::stem({ - -words => $word_list_reference, - -locale => 'ru', - -exceptions => $exceptions_hash, - }); - - my $stem = Lingua::Stem::RuUTF8::stem_word( $word ); - -=head1 DESCRIPTION - -This module applies the Porter Stemming Algorithm to its parameters, -returning the stemmed words. - -The algorithm is implemented exactly as described in: - - http://snowball.tartarus.org/russian/stemmer.html - -The code is carefully crafted to work in conjunction with the L -module by Benjamin Franz. This stemmer is also based -on the work of Aldo Capini, see L. - -=head1 METHODS - -=over 4 - -=item stem({ -words => \@words, -locale => 'ru', -exceptions => \%exceptions }); - -Stems a list of passed words. Returns an anonymous list reference to the stemmed -words. - -Example: - - my $stemmed_words = Lingua::Stem::RuUTF8::stem({ - -words => \@words, - -locale => 'ru', - -exceptions => \%exceptions, - }); - -=item stem_word( $word ); - -Stems a single word and returns the stem directly. - -Example: - - my $stem = Lingua::Stem::RuUTF8::stem_word( $word ); - -=item stem_caching({ -level => 0|1|2 }); - -Sets the level of stem caching. - -'0' means 'no caching'. This is the default level. - -'1' means 'cache per run'. This caches stemming results during a single - call to 'stem'. - -'2' means 'cache indefinitely'. This caches stemming results until - either the process exits or the 'clear_stem_cache' method is called. - -=item clear_stem_cache; - -Clears the cache of stemmed words - -=back - -=cut - -=head2 EXPORT - -None by default. - -=head1 HISTORY - -=over 8 - -=item * - -0.01 (2004-05-21) - -=back - -=head1 AUTHOR - -Aleksandr Guidrevitch - -=head1 SEE ALSO - - Lingua::Stem - -=head1 COPYRIGHT - -Copyright (C) 2003 by Aldo Calpini - -Copyright (C) 2004 by Aleksandr Guidrevitch - -This software may be freely copied and distributed under the same -terms and conditions as Perl itself, either Perl version 5.8.3 -or, at your option, any later version of Perl 5 you may -have available.. - -=cut diff --git a/checksetup.sh b/checksetup.sh index 588deabae..a24fb6c7c 100755 --- a/checksetup.sh +++ b/checksetup.sh @@ -1,2 +1,2 @@ #!/bin/sh -perl checksetup.pl --no-chmod --no-templates +LD_PRELOAD=/usr/lib/i386-linux-gnu/libstdc++.so.6:/lib/libuuid.so.1 perl checksetup.pl --no-chmod --no-templates diff --git a/extensions/Example/code/auth-login_methods.pl b/extensions/Example/code/auth-login_methods.pl new file mode 100644 index 000000000..0ae12aa6b --- /dev/null +++ b/extensions/Example/code/auth-login_methods.pl @@ -0,0 +1,27 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +my $modules = Bugzilla->hook_args->{modules}; +if (exists $modules->{Example}) { + $modules->{Example} = 'extensions/example/lib/AuthLogin.pm'; +} diff --git a/extensions/Example/code/auth-verify_methods.pl b/extensions/Example/code/auth-verify_methods.pl new file mode 100644 index 000000000..7ae52f012 --- /dev/null +++ b/extensions/Example/code/auth-verify_methods.pl @@ -0,0 +1,27 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +my $modules = Bugzilla->hook_args->{modules}; +if (exists $modules->{Example}) { + $modules->{Example} = 'extensions/example/lib/AuthVerify.pm'; +} diff --git a/extensions/Example/code/bug-columns.pl b/extensions/Example/code/bug-columns.pl new file mode 100644 index 000000000..92ccf6d23 --- /dev/null +++ b/extensions/Example/code/bug-columns.pl @@ -0,0 +1,27 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Elliotte Martin + + +use strict; +use warnings; +use Bugzilla; + +my $columns = Bugzilla->hook_args->{'columns'}; +push (@$columns, "delta_ts AS example") diff --git a/extensions/Example/code/bug-end_of_create.pl b/extensions/Example/code/bug-end_of_create.pl new file mode 100644 index 000000000..0325ddd59 --- /dev/null +++ b/extensions/Example/code/bug-end_of_create.pl @@ -0,0 +1,35 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is ITA Software +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander +# Bradley Baetz + +use strict; +use warnings; +use Bugzilla; + +# This code doesn't actually *do* anything, it's just here to show you +# how to use this hook. +my $args = Bugzilla->hook_args; +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 +# you file a bug. +# warn "Bug $bug_id has been filed!"; diff --git a/extensions/Example/code/bug-end_of_update.pl b/extensions/Example/code/bug-end_of_update.pl new file mode 100644 index 000000000..036563517 --- /dev/null +++ b/extensions/Example/code/bug-end_of_update.pl @@ -0,0 +1,56 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Everything Solved, Inc. +# Portions created by Everything Solved are Copyright (C) 2008 +# Everything Solved, Inc. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +use Bugzilla::Status; + +# This code doesn't actually *do* anything, it's just here to show you +# how to use this hook. +my $args = Bugzilla->hook_args; +my $bug = $args->{'bug'}; +my $timestamp = $args->{'timestamp'}; +my $changes = $args->{'changes'}; + +foreach my $field (keys %$changes) { + my $used_to_be = $changes->{$field}->[0]; + my $now_it_is = $changes->{$field}->[1]; +} + +my $status_message; +if (my $status_change = $changes->{'bug_status'}) { + my $old_status = new Bugzilla::Status({ name => $status_change->[0] }); + my $new_status = new Bugzilla::Status({ name => $status_change->[1] }); + if ($new_status->is_open && !$old_status->is_open) { + $status_message = "Bug re-opened!"; + } + if (!$new_status->is_open && $old_status->is_open) { + $status_message = "Bug closed!"; + } +} + +my $bug_id = $bug->id; +my $num_changes = scalar keys %$changes; +my $result = "There were $num_changes changes to fields on bug $bug_id" + . " at $timestamp."; +# Uncomment this line to see $result in your webserver's error log whenever +# you update a bug. +# warn $result; diff --git a/extensions/Example/code/bug-fields.pl b/extensions/Example/code/bug-fields.pl new file mode 100644 index 000000000..f8475426d --- /dev/null +++ b/extensions/Example/code/bug-fields.pl @@ -0,0 +1,27 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Elliotte Martin + + +use strict; +use warnings; +use Bugzilla; + +my $fields = Bugzilla->hook_args->{'fields'}; +push (@$fields, "example") diff --git a/extensions/Example/code/buglist-columns.pl b/extensions/Example/code/buglist-columns.pl new file mode 100644 index 000000000..4487b2dc8 --- /dev/null +++ b/extensions/Example/code/buglist-columns.pl @@ -0,0 +1,26 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Elliotte Martin + +use strict; +use warnings; +use Bugzilla; + +my $columns = Bugzilla->hook_args->{'columns'}; +$columns->{'example'} = { 'name' => 'bugs.delta_ts' , 'title' => 'Example' }; diff --git a/extensions/Example/code/colchange-columns.pl b/extensions/Example/code/colchange-columns.pl new file mode 100644 index 000000000..6174d3940 --- /dev/null +++ b/extensions/Example/code/colchange-columns.pl @@ -0,0 +1,27 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Elliotte Martin + + +use strict; +use warnings; +use Bugzilla; + +my $columns = Bugzilla->hook_args->{'columns'}; +push (@$columns, "example") diff --git a/extensions/Example/code/config-add_panels.pl b/extensions/Example/code/config-add_panels.pl new file mode 100644 index 000000000..5f4f5bdd4 --- /dev/null +++ b/extensions/Example/code/config-add_panels.pl @@ -0,0 +1,25 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Bradley Baetz + +use strict; +use warnings; +use Bugzilla; +my $modules = Bugzilla->hook_args->{panel_modules}; +$modules->{Example} = "extensions::example::lib::ConfigExample"; diff --git a/extensions/Example/code/config-modify_panels.pl b/extensions/Example/code/config-modify_panels.pl new file mode 100644 index 000000000..bd93962bf --- /dev/null +++ b/extensions/Example/code/config-modify_panels.pl @@ -0,0 +1,32 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +my $panels = Bugzilla->hook_args->{panels}; + +# Add the "Example" auth methods. +my $auth_params = $panels->{'auth'}->{params}; +my ($info_class) = grep($_->{name} eq 'user_info_class', @$auth_params); +my ($verify_class) = grep($_->{name} eq 'user_verify_class', @$auth_params); + +push(@{ $info_class->{choices} }, 'CGI,Example'); +push(@{ $verify_class->{choices} }, 'Example'); diff --git a/extensions/Example/code/config.pl b/extensions/Example/code/config.pl new file mode 100644 index 000000000..1da490cc2 --- /dev/null +++ b/extensions/Example/code/config.pl @@ -0,0 +1,26 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander +# Bradley Baetz + +use strict; +use warnings; +use Bugzilla; +my $config = Bugzilla->hook_args->{config}; +$config->{Example} = "extensions::example::lib::ConfigExample"; diff --git a/extensions/Example/code/flag-end_of_update.pl b/extensions/Example/code/flag-end_of_update.pl new file mode 100644 index 000000000..6371bd154 --- /dev/null +++ b/extensions/Example/code/flag-end_of_update.pl @@ -0,0 +1,42 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Everything Solved, Inc. +# Portions created by Everything Solved are Copyright (C) 2008 +# Everything Solved, Inc. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +use Bugzilla::Util qw(diff_arrays); + +# This code doesn't actually *do* anything, it's just here to show you +# how to use this hook. +my $args = Bugzilla->hook_args; +my ($bug, $timestamp, $old_flags, $new_flags) = + @$args{qw(bug timestamp old_flags new_flags)}; +my ($removed, $added) = diff_arrays($old_flags, $new_flags); +my ($granted, $denied) = (0, 0); +foreach my $new_flag (@$added) { + $granted++ if $new_flag =~ /\+$/; + $denied++ if $new_flag =~ /-$/; +} +my $bug_id = $bug->id; +my $result = "$granted flags were granted and $denied flags were denied" + . " on bug $bug_id at $timestamp."; +# Uncomment this line to see $result in your webserver's error log whenever +# you update flags. +# warn $result; diff --git a/extensions/Example/code/install-before_final_checks.pl b/extensions/Example/code/install-before_final_checks.pl new file mode 100644 index 000000000..ef1bee22c --- /dev/null +++ b/extensions/Example/code/install-before_final_checks.pl @@ -0,0 +1,28 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2008 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): Elliotte Martin + + +use strict; +use warnings; +use Bugzilla; + +my $silent = Bugzilla->hook_args->{'silent'}; + +print "Install-before_final_checks hook\n" unless $silent; diff --git a/extensions/Example/code/mailer-before_send.pl b/extensions/Example/code/mailer-before_send.pl new file mode 100644 index 000000000..322c78088 --- /dev/null +++ b/extensions/Example/code/mailer-before_send.pl @@ -0,0 +1,28 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Everything Solved, Inc. +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +my $email = Bugzilla->hook_args->{email}; +# If you add a header to an email, it's best to start it with +# 'X-Bugzilla-' so that you don't conflict with +# other extensions. +$email->header_set('X-Bugzilla-Example-Header', 'Example'); diff --git a/extensions/Example/code/page-before_template.pl b/extensions/Example/code/page-before_template.pl new file mode 100644 index 000000000..dcf059367 --- /dev/null +++ b/extensions/Example/code/page-before_template.pl @@ -0,0 +1,33 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Example Plugin. +# +# The Initial Developer of the Original Code is Canonical Ltd. +# Portions created by Canonical Ltd. are Copyright (C) 2009 +# Canonical Ltd. All Rights Reserved. +# +# Contributor(s): +# Max Kanat-Alexander + + +use strict; +use warnings; +use Bugzilla; + +my %args = %{ Bugzilla->hook_args }; +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') { + $vars->{cgi_variables} = { Bugzilla->cgi->Vars }; +} diff --git a/extensions/Example/code/product-confirm_delete.pl b/extensions/Example/code/product-confirm_delete.pl new file mode 100644 index 000000000..1f4c3740e --- /dev/null +++ b/extensions/Example/code/product-confirm_delete.pl @@ -0,0 +1,26 @@ +#!/usr/bin/perl -w +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Testopia System. +# +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2008 +# Novell. All Rights Reserved. +# +# Contributor(s): Greg Hendricks + +use strict; + +my $vars = Bugzilla->hook_args->{vars}; + +$vars->{'example'} = 1 diff --git a/extensions/Example/code/webservice-error_codes.pl b/extensions/Example/code/webservice-error_codes.pl new file mode 100644 index 000000000..94c4c52fc --- /dev/null +++ b/extensions/Example/code/webservice-error_codes.pl @@ -0,0 +1,25 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Everything Solved, Inc. are Copyright (C) 2008 +# Everything Solved, Inc. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +my $error_map = Bugzilla->hook_args->{error_map}; +$error_map->{'example_my_error'} = 10001; diff --git a/extensions/Example/code/webservice.pl b/extensions/Example/code/webservice.pl new file mode 100644 index 000000000..ff503be39 --- /dev/null +++ b/extensions/Example/code/webservice.pl @@ -0,0 +1,25 @@ +# -*- Mode: perl; indent-tabs-mode: nil -*- +# +# 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 Everything Solved, Inc. are Copyright (C) 2007 +# Everything Solved, Inc. All Rights Reserved. +# +# Contributor(s): Max Kanat-Alexander + +use strict; +use warnings; +use Bugzilla; +my $dispatch = Bugzilla->hook_args->{dispatch}; +$dispatch->{Example} = "extensions::example::lib::WSExample"; diff --git a/extensions/Example/disabled b/extensions/Example/disabled new file mode 100644 index 000000000..e69de29bb diff --git a/extensions/custis/lib/CustisDBHooks.pm b/extensions/custis/lib/CustisDBHooks.pm index 7f47bee32..87cedb6df 100644 --- a/extensions/custis/lib/CustisDBHooks.pm +++ b/extensions/custis/lib/CustisDBHooks.pm @@ -320,7 +320,7 @@ sub install_update_db $dbh->do('INSERT INTO setting_value (name, value, sortindex) VALUES (\'silent_affects_flags\', \'send\', 10), (\'silent_affects_flags\', \'do_not_send\', 20)'); } - # Специальные группы + # New system groups my $special_groups = [ [ 'bz_editcheckers', 'Users who can edit Bugzilla Correctness Checkers', [ 'admin' ] ], [ 'editfields', 'Users who can edit Bugzilla field parameters', [ 'admin' ] ], diff --git a/qa/t/webservice_bug_add_comment.t b/qa/t/webservice_bug_add_comment.t index 7bb800445..63de4d31c 100644 --- a/qa/t/webservice_bug_add_comment.t +++ b/qa/t/webservice_bug_add_comment.t @@ -11,7 +11,7 @@ my ($xmlrpc, $jsonrpc, $config) = get_rpc_clients(); use constant INVALID_BUG_ID => -1; use constant INVALID_BUG_ALIAS => 'aaaaaaa12345'; -use constant PRIVATE_BUG => 40933; +use constant PRIVATE_BUG => 40934; use constant PUBLIC_BUG => 58878; use constant TEST_COMMENT => '--- Test Comment From QA Tests ---'; diff --git a/template/en/default/admin/params/query.html.tmpl b/template/en/default/admin/params/query.html.tmpl index 34ea04381..835b3c3bb 100644 --- a/template/en/default/admin/params/query.html.tmpl +++ b/template/en/default/admin/params/query.html.tmpl @@ -24,35 +24,42 @@ %] [% param_descs = { - quip_list_entry_control => "Controls how easily users can add entries to the quip list. -
    -
  • - open - Users may freely add to the quip list, and - their entries will immediately be available for viewing. -
  • -
  • - moderated - quips can be entered, but need to be approved - by an admin before they will be shown. -
  • -
  • - closed - no new additions to the quips list are allowed. -
  • -
", + quip_list_entry_control => + "Controls how easily users can add entries to the quip list. +
    +
  • + open - Users may freely add to the quip list, and + their entries will immediately be available for viewing. +
  • +
  • + moderated - quips can be entered, but need to be approved + by an admin before they will be shown. +
  • +
  • + closed - no new additions to the quips list are allowed. +
  • +
", - mostfreqthreshold => "The minimum number of duplicates $terms.abug needs to show up on the " _ - "most frequently reported $terms.bugs page. " _ - "If you have a large database and this page takes a long time to " _ - "load, try increasing this number.", + mostfreqthreshold => + "The minimum number of duplicates $terms.abug needs to show up on the " _ + "most frequently reported $terms.bugs page. " _ + "If you have a large database and this page takes a long time to " _ + "load, try increasing this number.", - mybugstemplate => "This is the URL to use to bring up a simple 'all of my $terms.bugs' " _ - "list for a user. %userid% will get replaced with the login name of a user.", + mybugstemplate => + "This is the URL to use to bring up a simple 'all of my $terms.bugs' " _ + "list for a user. %userid% will get replaced with the login name of a user.", - defaultquery => "This is the default query that initially comes up when you " _ - "access the advanced query page. It's in URL parameter " _ - "format, which makes it hard to read. Sorry!", + defaultquery => + "This is the default query that initially comes up when you " _ + "access the advanced query page. It's in URL parameter " _ + "format, which makes it hard to read. Sorry!", - specific_search_allow_empty_words => + specific_search_allow_empty_words => "Whether to allow a search on the 'Simple Search' page with an empty" _ " 'Words' field.", + stem_language => "Language for stemming words in full-text search, 2-letter code" _ + " (one of: da, de, en, es, fi, fr, hu, it, nl, no, pt, ro, ru, sv, tr)", + } %] diff --git a/template/en/default/setup/strings.txt.pl b/template/en/default/setup/strings.txt.pl index d4f49f102..0507b9f08 100644 --- a/template/en/default/setup/strings.txt.pl +++ b/template/en/default/setup/strings.txt.pl @@ -66,6 +66,7 @@ END feature_smtp_auth => 'SMTP Authentication', feature_updates => 'Automatic Update Notifications', feature_xmlrpc => 'XML-RPC Interface', + feature_fulltext_stem => 'Snowball stemmers in full-text search', header => "* This is Bugzilla ##bz_ver## on perl ##perl_ver##\n" . "* Running on ##os_name## ##os_ver##",