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-67ecbb4d7f56master
parent
3ed6d7df0e
commit
710e7747d2
|
@ -1141,42 +1141,38 @@ sub _extract_multi_selects {
|
||||||
# Should be called any time you update short_desc or change a comment.
|
# Should be called any time you update short_desc or change a comment.
|
||||||
sub _sync_fulltext
|
sub _sync_fulltext
|
||||||
{
|
{
|
||||||
|
use utf8;
|
||||||
my ($self, $new_bug) = @_;
|
my ($self, $new_bug) = @_;
|
||||||
my $dbh = Bugzilla->dbh;
|
my $dbh = Bugzilla->dbh;
|
||||||
my ($short_desc) = $dbh->selectrow_array(
|
my ($short_desc) = $dbh->selectrow_array(
|
||||||
"SELECT short_desc FROM bugs WHERE bug_id=?", undef, $self->id
|
"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=?",
|
"SELECT thetext, isprivate FROM longdescs WHERE bug_id=?",
|
||||||
undef, $self->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;
|
$_->[1] ? push @$priv, $_->[0] : push @$nopriv, $_->[0];
|
||||||
$short_desc = stem_text($short_desc);
|
|
||||||
$all = stem_text($all);
|
|
||||||
$nopriv = stem_text($nopriv);
|
|
||||||
}
|
}
|
||||||
# O_o как оно может быть здесь tainted - непонятно, но иногда стреляет
|
$nopriv = join "\n", @$nopriv;
|
||||||
trick_taint($short_desc);
|
$priv = join "\n", @$priv;
|
||||||
trick_taint($all);
|
my $row = [ $short_desc, $nopriv, $priv ];
|
||||||
trick_taint($nopriv);
|
$_ = $dbh->quote_fulltext($_) for @$row;
|
||||||
my @bind = ($short_desc, $all, $nopriv, $self->id);
|
## O_o Don't know how can it be tainted here, sometimes it was. Checking if it goes away.
|
||||||
|
#trick_taint($row);
|
||||||
my $sql;
|
my $sql;
|
||||||
if ($new_bug)
|
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
|
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.
|
# This is the correct way to delete bugs from the DB.
|
||||||
|
|
|
@ -71,8 +71,13 @@ sub get_param_list {
|
||||||
name => 'specific_search_allow_empty_words',
|
name => 'specific_search_allow_empty_words',
|
||||||
type => 'b',
|
type => 'b',
|
||||||
default => 1
|
default => 1
|
||||||
}
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name => 'stem_language',
|
||||||
|
type => 't',
|
||||||
|
default => 'en',
|
||||||
|
},
|
||||||
);
|
);
|
||||||
return @param_list;
|
return @param_list;
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,8 +176,11 @@ use Cwd qw(abs_path);
|
||||||
|
|
||||||
PASSWORD_DIGEST_ALGORITHM
|
PASSWORD_DIGEST_ALGORITHM
|
||||||
PASSWORD_SALT_LENGTH
|
PASSWORD_SALT_LENGTH
|
||||||
|
|
||||||
CGI_URI_LIMIT
|
CGI_URI_LIMIT
|
||||||
|
|
||||||
|
LANG_ISO_FULL
|
||||||
|
LANG_FULL_ISO
|
||||||
);
|
);
|
||||||
|
|
||||||
@Bugzilla::Constants::EXPORT_OK = qw(contenttypes);
|
@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.
|
# can be safely done or not based on the web server's URI length setting.
|
||||||
use constant CGI_URI_LIMIT => 8000;
|
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 {
|
sub bz_locations {
|
||||||
# We know that Bugzilla/Constants.pm must be in %INC at this point.
|
# 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
|
# So the only question is, what's the name of the directory
|
||||||
|
|
|
@ -96,6 +96,20 @@ sub connect_main {
|
||||||
$lc->{db_sock}, $lc->{db_user}, $lc->{db_pass});
|
$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 {
|
sub _connect {
|
||||||
my ($driver, $host, $dbname, $port, $sock, $user, $pass) = @_;
|
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
|
# standard ANSI SQL, without real full text search support. DB specific
|
||||||
# modules should override this, as this will be always much slower.
|
# 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
|
# make the string lowercase to do case insensitive search
|
||||||
my $lower_text = lc($text);
|
my $lower_text = lc($text);
|
||||||
|
|
||||||
|
@ -396,6 +414,16 @@ sub sql_fulltext_search
|
||||||
return ($term, $term);
|
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
|
# General Info Methods
|
||||||
#####################################################################
|
#####################################################################
|
||||||
|
|
|
@ -178,6 +178,10 @@ sub sql_fulltext_search
|
||||||
{
|
{
|
||||||
my ($self, $column, $text) = @_;
|
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
|
# quote un-quoted compound words
|
||||||
my @words = quotewords('[\s()]+', 'delimiters', $text);
|
my @words = quotewords('[\s()]+', 'delimiters', $text);
|
||||||
if ($text =~ /(?:^|\W)[+\-<>~"()]/ || $text =~ /[()"*](?:$|\W)/)
|
if ($text =~ /(?:^|\W)[+\-<>~"()]/ || $text =~ /[()"*](?:$|\W)/)
|
||||||
|
|
|
@ -44,6 +44,7 @@ package Bugzilla::DB::Pg;
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
use Bugzilla::Error;
|
use Bugzilla::Error;
|
||||||
|
use Bugzilla::Constants qw(LANG_ISO_FULL);
|
||||||
use DBD::Pg;
|
use DBD::Pg;
|
||||||
|
|
||||||
# This module extends the DB interface via inheritance
|
# This module extends the DB interface via inheritance
|
||||||
|
@ -187,19 +188,26 @@ sub sql_fulltext_search
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($column, $text) = @_;
|
my ($column, $text) = @_;
|
||||||
my $language = Bugzilla->localconfig->{postgres_fulltext_language} || 'english';
|
|
||||||
$language = $self->quote($language).',';
|
|
||||||
$text = $self->quote($text);
|
$text = $self->quote($text);
|
||||||
# Try to_tsquery, and use plainto_tsquery if the syntax is incorrect
|
# 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)") };
|
eval { $self->do("SELECT to_tsquery($language$text)") };
|
||||||
my $op = $@ ? 'plainto_tsquery' : 'to_tsquery';
|
my $op = $@ ? 'plainto_tsquery' : 'to_tsquery';
|
||||||
return (
|
return (
|
||||||
"(to_tsvector($language$column) \@\@ $op($language$text))",
|
"($column \@\@ $op($language$text))",
|
||||||
"(ts_rank(to_tsvector($language$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
|
sub real_table_list
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
|
@ -315,24 +315,30 @@ use constant ABSTRACT_SCHEMA => {
|
||||||
|
|
||||||
bugs_fulltext => {
|
bugs_fulltext => {
|
||||||
FIELDS => [
|
FIELDS => [
|
||||||
bug_id => {TYPE => 'INT3', NOTNULL => 1, PRIMARYKEY => 1,
|
bug_id => {TYPE => 'INT3', NOTNULL => 1, PRIMARYKEY => 1,
|
||||||
REFERENCES => {TABLE => 'bugs',
|
REFERENCES => {TABLE => 'bugs',
|
||||||
COLUMN => 'bug_id',
|
COLUMN => 'bug_id',
|
||||||
DELETE => 'CASCADE'}},
|
DELETE => 'CASCADE'}},
|
||||||
short_desc => {TYPE => 'varchar(255)', NOTNULL => 1},
|
short_desc => {TYPE => 'varchar(255)', NOTNULL => 1},
|
||||||
# Comments are stored all together in one column for searching.
|
# Comments are stored all together in one column for searching.
|
||||||
# This allows us to examine all comments together when deciding
|
# This allows us to examine all comments together when deciding
|
||||||
# the relevance of a bug in fulltext search.
|
# the relevance of a bug in fulltext search.
|
||||||
comments => {TYPE => 'LONGTEXT'},
|
comments => {TYPE => 'LONGTEXT'},
|
||||||
comments_noprivate => {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 => [
|
INDEXES => [
|
||||||
bugs_fulltext_short_desc_idx => {FIELDS => ['short_desc'],
|
bugs_fulltext_short_desc_idx => {FIELDS => ['short_desc'],
|
||||||
TYPE => 'FULLTEXT'},
|
TYPE => 'FULLTEXT'},
|
||||||
bugs_fulltext_comments_idx => {FIELDS => ['comments'],
|
bugs_fulltext_comments_idx => {FIELDS => ['comments'],
|
||||||
TYPE => 'FULLTEXT'},
|
TYPE => 'FULLTEXT'},
|
||||||
bugs_fulltext_comments_noprivate_idx => {
|
bugs_fulltext_comments_private_idx => {
|
||||||
FIELDS => ['comments_noprivate'], TYPE => 'FULLTEXT'},
|
FIELDS => ['comments_private'], TYPE => 'FULLTEXT'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -72,15 +72,61 @@ sub _initialize {
|
||||||
} #eosub--_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
|
sub _get_create_index_ddl
|
||||||
{
|
{
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($table, $name, $index_fields, $index_type) = @_;
|
my ($table, $name, $index_fields, $index_type) = @_;
|
||||||
if ($index_type && $index_type eq 'FULLTEXT')
|
if ($index_type && $index_type eq 'FULLTEXT')
|
||||||
{
|
{
|
||||||
$index_fields = @$index_fields > 1 ? join(" || ' ' || ", @$index_fields) : $index_fields->[0];
|
# Override fulltext index creation clause
|
||||||
my $language = Bugzilla->localconfig->{postgres_fulltext_language} || 'english';
|
$index_fields = join(" || ", @$index_fields);
|
||||||
return "CREATE INDEX $name ON $table USING gin(to_tsvector('$language', $index_fields))";
|
return "CREATE INDEX $name ON $table USING gin($index_fields)";
|
||||||
}
|
}
|
||||||
return $self->SUPER::_get_create_index_ddl(@_);
|
return $self->SUPER::_get_create_index_ddl(@_);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ use Bugzilla::Series;
|
||||||
use Date::Parse;
|
use Date::Parse;
|
||||||
use Date::Format;
|
use Date::Format;
|
||||||
use IO::File;
|
use IO::File;
|
||||||
|
use Time::HiRes qw(time);
|
||||||
|
|
||||||
# NOTE: This is NOT the function for general table updates. See
|
# NOTE: This is NOT the function for general table updates. See
|
||||||
# update_table_definitions for that. This is only for the fielddefs table.
|
# 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});
|
{TYPE => 'varchar(16)', PRIMARYKEY => 1, NOTNULL => 1});
|
||||||
|
|
||||||
_clean_control_characters_from_short_desc();
|
_clean_control_characters_from_short_desc();
|
||||||
|
|
||||||
# 2005-12-07 altlst@sonic.net -- Bug 225221
|
# 2005-12-07 altlst@sonic.net -- Bug 225221
|
||||||
$dbh->bz_add_column('longdescs', 'comment_id',
|
$dbh->bz_add_column('longdescs', 'comment_id',
|
||||||
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
|
{TYPE => 'MEDIUMSERIAL', NOTNULL => 1, PRIMARYKEY => 1});
|
||||||
|
@ -532,7 +533,7 @@ sub update_table_definitions {
|
||||||
|
|
||||||
# 2007-08-21 wurblzap@gmail.com - Bug 365378
|
# 2007-08-21 wurblzap@gmail.com - Bug 365378
|
||||||
_make_lang_setting_dynamic();
|
_make_lang_setting_dynamic();
|
||||||
|
|
||||||
# 2007-11-29 xiaoou.wu@oracle.com - Bug 153129
|
# 2007-11-29 xiaoou.wu@oracle.com - Bug 153129
|
||||||
_change_text_types();
|
_change_text_types();
|
||||||
|
|
||||||
|
@ -544,6 +545,16 @@ sub update_table_definitions {
|
||||||
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
|
{TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 0});
|
||||||
|
|
||||||
$dbh->bz_drop_index('longdescs', 'longdescs_thetext_idx');
|
$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();
|
_populate_bugs_fulltext();
|
||||||
|
|
||||||
# 2008-01-18 xiaoou.wu@oracle.com - Bug 414292
|
# 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
|
# 2009-01-16 oreomike@gmail.com - Bug 302420
|
||||||
$dbh->bz_add_column('whine_events', 'mailifnobugs',
|
$dbh->bz_add_column('whine_events', 'mailifnobugs',
|
||||||
{ TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
|
{ TYPE => 'BOOLEAN', NOTNULL => 1, DEFAULT => 'FALSE'});
|
||||||
|
|
||||||
_convert_disallownew_to_isactive();
|
_convert_disallownew_to_isactive();
|
||||||
|
|
||||||
$dbh->bz_alter_column('bugs_activity', 'added',
|
$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',
|
$dbh->bz_add_fk("bug_$name", "bug_id", {TABLE => 'bugs',
|
||||||
COLUMN => 'bug_id',
|
COLUMN => 'bug_id',
|
||||||
DELETE => 'CASCADE',});
|
DELETE => 'CASCADE',});
|
||||||
|
|
||||||
$dbh->bz_add_fk("bug_$name", "value", {TABLE => $name,
|
$dbh->bz_add_fk("bug_$name", "value", {TABLE => $name,
|
||||||
COLUMN => 'value',
|
COLUMN => 'value',
|
||||||
DELETE => 'RESTRICT',});
|
DELETE => 'RESTRICT',});
|
||||||
|
@ -3252,8 +3263,11 @@ sub _populate_bugs_fulltext
|
||||||
my $bug_ids = shift;
|
my $bug_ids = shift;
|
||||||
$bug_ids = undef if $bug_ids && !@$bug_ids;
|
$bug_ids = undef if $bug_ids && !@$bug_ids;
|
||||||
my $dbh = Bugzilla->dbh;
|
my $dbh = Bugzilla->dbh;
|
||||||
my $fulltext = $dbh->selectrow_array
|
my $sphinx;
|
||||||
("SELECT 1 FROM bugs_fulltext ".$dbh->sql_limit(1));
|
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
|
# We only populate the table if it's empty or if we've been given a
|
||||||
# set of bug ids.
|
# set of bug ids.
|
||||||
if ($bug_ids || !$fulltext)
|
if ($bug_ids || !$fulltext)
|
||||||
|
@ -3262,13 +3276,10 @@ sub _populate_bugs_fulltext
|
||||||
$bug_ids ||= $dbh->selectcol_arrayref("SELECT bug_id FROM bugs");
|
$bug_ids ||= $dbh->selectcol_arrayref("SELECT bug_id FROM bugs");
|
||||||
return if !$bug_ids;
|
return if !$bug_ids;
|
||||||
|
|
||||||
# Bug 46221 - Russian Stemming in Bugzilla fulltext search
|
# There could be tons of bugs, so we'll use 256-bug portions
|
||||||
# We can't use GROUP_CONCAT because we need to stem each word
|
print "Populating full-text index... (this can take a long time.)\n";
|
||||||
# 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";
|
|
||||||
my ($portion, $done, $total) = (256, 0, scalar @$bug_ids);
|
my ($portion, $done, $total) = (256, 0, scalar @$bug_ids);
|
||||||
my ($short, $all, $nopriv, $wh, $rows);
|
my ($short, $all, $nopriv, $wh, $rows);
|
||||||
my ($sth, $sth_del, $sthn) = (undef, undef, 0);
|
|
||||||
while (my @ids = splice @$bug_ids, 0, $portion)
|
while (my @ids = splice @$bug_ids, 0, $portion)
|
||||||
{
|
{
|
||||||
$rows = {};
|
$rows = {};
|
||||||
|
@ -3280,35 +3291,43 @@ sub _populate_bugs_fulltext
|
||||||
"SELECT bug_id, thetext, isprivate FROM longdescs WHERE $wh",
|
"SELECT bug_id, thetext, isprivate FROM longdescs WHERE $wh",
|
||||||
undef, @ids
|
undef, @ids
|
||||||
);
|
);
|
||||||
$rows->{$_->[0]} = [ $_->[1], '', '' ] for @$short;
|
# Local block with 'use bytes' for counting data size in MB
|
||||||
for (@$all)
|
|
||||||
{
|
{
|
||||||
$rows->{$_->[0]}->[1] .= $_->[1] . "\n";
|
use bytes;
|
||||||
$rows->{$_->[0]}->[2] .= $_->[1] . "\n"
|
for (@$short)
|
||||||
unless $_->[2];
|
|
||||||
}
|
|
||||||
if (!$dbh->isa('Bugzilla::DB::Pg'))
|
|
||||||
{
|
|
||||||
for (keys %$rows)
|
|
||||||
{
|
{
|
||||||
$_ = 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
|
Encode::_utf8_off($_) for @{$rows->{$_}};
|
||||||
$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)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
$sth_del->execute(@ids);
|
for (keys %$rows)
|
||||||
$sth->execute(map { ($_, @{$rows->{$_}}) } @ids);
|
{
|
||||||
|
# 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;
|
$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";
|
print "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,14 +214,6 @@ EOT
|
||||||
# such as bug changes. A random string is generated by default.
|
# 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
|
# It's very important that this key is kept secret. It also must be
|
||||||
# very long.
|
# very long.
|
||||||
EOT
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name => 'postgres_fulltext_language',
|
|
||||||
default => 'english',
|
|
||||||
desc => <<EOT
|
|
||||||
# Value of this variable is used as the language for full-text search
|
|
||||||
# morphology, only for PostgreSQL database.
|
|
||||||
EOT
|
EOT
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -294,6 +294,14 @@ sub OPTIONAL_MODULES {
|
||||||
version => '0.05',
|
version => '0.05',
|
||||||
feature => ['rand_security'],
|
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');
|
my $extra_modules = _get_extension_requirements('OPTIONAL_MODULES');
|
||||||
|
|
|
@ -2000,20 +2000,21 @@ sub _content_matches
|
||||||
my $dbh = Bugzilla->dbh;
|
my $dbh = Bugzilla->dbh;
|
||||||
|
|
||||||
my $table = "bugs_fulltext_".$self->{sequence};
|
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.
|
# Create search terms to add to the SELECT and WHERE clauses.
|
||||||
my $text = stem_text($self->{value});
|
# These are (search term, rank term, search term, rank term, ...)
|
||||||
my ($term1, $rterm1) = $dbh->sql_fulltext_search("bugs_fulltext.$comments_col", $text);
|
my $text = $self->{value};
|
||||||
my ($term2, $rterm2) = $dbh->sql_fulltext_search("bugs_fulltext.short_desc", $text);
|
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
|
# Bug 46221 - Russian Stemming in Bugzilla fulltext search
|
||||||
# MATCH(...) OR MATCH(...) is very slow in MySQL (and probably in other DBs):
|
# 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.
|
# -- it does no fulltext index merge optimization. So use JOIN to UNION.
|
||||||
$self->{term} = {
|
$self->{term} = {
|
||||||
table => "(SELECT bug_id FROM bugs_fulltext WHERE $term1 UNION ".
|
table => "(".join(" UNION ", map { "SELECT bug_id FROM bugs_fulltext WHERE $terms[$_]" } grep { !($_&1) } 0..$#terms).") $table",
|
||||||
"SELECT bug_id FROM bugs_fulltext WHERE $term2) $table",
|
|
||||||
bugid_field => "$table.bug_id",
|
bugid_field => "$table.bug_id",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2025,7 +2026,7 @@ sub _content_matches
|
||||||
# this adds more terms to the relevance sql.
|
# this adds more terms to the relevance sql.
|
||||||
if (!$self->{negated})
|
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}}).
|
COLUMNS->{relevance}->{name} = "(SELECT ".join("+", @{COLUMNS->{relevance}->{bits}}).
|
||||||
" FROM bugs_fulltext WHERE bugs_fulltext.bug_id=bugs.bug_id)";
|
" FROM bugs_fulltext WHERE bugs_fulltext.bug_id=bugs.bug_id)";
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ use Text::Wrap;
|
||||||
use Text::TabularDisplay::Utf8;
|
use Text::TabularDisplay::Utf8;
|
||||||
use JSON;
|
use JSON;
|
||||||
|
|
||||||
use Lingua::Stem::RuUTF8;
|
eval { require 'Lingua/Stem/Snowball.pm' };
|
||||||
|
|
||||||
sub trick_taint
|
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
|
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};
|
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 $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)
|
for (@$text)
|
||||||
{
|
{
|
||||||
unless (/\W/)
|
if ($word)
|
||||||
{
|
{
|
||||||
# $q = 1 means verbatim
|
# $q = 1 means we're inside quotes
|
||||||
unless ($q)
|
$r .= ($cache->{$_} ||= $stem->stem($_)) unless $q;
|
||||||
{
|
|
||||||
if (/_/)
|
|
||||||
{
|
|
||||||
# CustIS Bug 66033
|
|
||||||
$_ = join ' ', map { Lingua::Stem::RuUTF8::stem_word($_) } ($_, split(/_/, $_));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$_ = Lingua::Stem::RuUTF8::stem_word($_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -765,13 +769,12 @@ sub stem_text
|
||||||
# If $allow_verbatim is TRUE then text in "double quotes" doesn't stem
|
# If $allow_verbatim is TRUE then text in "double quotes" doesn't stem
|
||||||
$q = ($q + tr/\"/\"/) % 2;
|
$q = ($q + tr/\"/\"/) % 2;
|
||||||
}
|
}
|
||||||
if (!/\s$/so)
|
$r .= $_;
|
||||||
{
|
$r .= ' ' if !/\s$/o;
|
||||||
$_ .= ' ';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$word = !$word;
|
||||||
}
|
}
|
||||||
return join '', @$text;
|
return $r;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub intersect
|
sub intersect
|
||||||
|
|
|
@ -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<Lingua::Stem>
|
|
||||||
module by Benjamin Franz. This stemmer is also based
|
|
||||||
on the work of Aldo Capini, see L<Lingua::Stem::It>.
|
|
||||||
|
|
||||||
=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 <pillgrim@mail.ru>
|
|
||||||
|
|
||||||
=head1 SEE ALSO
|
|
||||||
|
|
||||||
Lingua::Stem
|
|
||||||
|
|
||||||
=head1 COPYRIGHT
|
|
||||||
|
|
||||||
Copyright (C) 2003 by Aldo Calpini <dada@perl.it>
|
|
||||||
|
|
||||||
Copyright (C) 2004 by Aleksandr Guidrevitch <pillgrim@mail.ru>
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,2 +1,2 @@
|
||||||
#!/bin/sh
|
#!/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
|
||||||
|
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
my $modules = Bugzilla->hook_args->{modules};
|
||||||
|
if (exists $modules->{Example}) {
|
||||||
|
$modules->{Example} = 'extensions/example/lib/AuthLogin.pm';
|
||||||
|
}
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
my $modules = Bugzilla->hook_args->{modules};
|
||||||
|
if (exists $modules->{Example}) {
|
||||||
|
$modules->{Example} = 'extensions/example/lib/AuthVerify.pm';
|
||||||
|
}
|
|
@ -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 <elliotte_martin@yahoo.com>
|
||||||
|
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
|
||||||
|
my $columns = Bugzilla->hook_args->{'columns'};
|
||||||
|
push (@$columns, "delta_ts AS example")
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
# Bradley Baetz <bbaetz@acm.org>
|
||||||
|
|
||||||
|
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!";
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
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;
|
|
@ -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 <elliotte_martin@yahoo.com>
|
||||||
|
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
|
||||||
|
my $fields = Bugzilla->hook_args->{'fields'};
|
||||||
|
push (@$fields, "example")
|
|
@ -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 <elliotte_martin@yahoo.com>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
|
||||||
|
my $columns = Bugzilla->hook_args->{'columns'};
|
||||||
|
$columns->{'example'} = { 'name' => 'bugs.delta_ts' , 'title' => 'Example' };
|
|
@ -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 <elliotte_martin@yahoo.com>
|
||||||
|
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
|
||||||
|
my $columns = Bugzilla->hook_args->{'columns'};
|
||||||
|
push (@$columns, "example")
|
|
@ -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 <bbaetz@acm.org>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
my $modules = Bugzilla->hook_args->{panel_modules};
|
||||||
|
$modules->{Example} = "extensions::example::lib::ConfigExample";
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
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');
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
# Bradley Baetz <bbaetz@acm.org>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
my $config = Bugzilla->hook_args->{config};
|
||||||
|
$config->{Example} = "extensions::example::lib::ConfigExample";
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
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;
|
|
@ -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 <elliotte_martin@yahoo.com>
|
||||||
|
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
|
||||||
|
my $silent = Bugzilla->hook_args->{'silent'};
|
||||||
|
|
||||||
|
print "Install-before_final_checks hook\n" unless $silent;
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
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-<Extension>' so that you don't conflict with
|
||||||
|
# other extensions.
|
||||||
|
$email->header_set('X-Bugzilla-Example-Header', 'Example');
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
|
@ -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 <ghendricks@novell.com>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
my $vars = Bugzilla->hook_args->{vars};
|
||||||
|
|
||||||
|
$vars->{'example'} = 1
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
my $error_map = Bugzilla->hook_args->{error_map};
|
||||||
|
$error_map->{'example_my_error'} = 10001;
|
|
@ -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 <mkanat@bugzilla.org>
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Bugzilla;
|
||||||
|
my $dispatch = Bugzilla->hook_args->{dispatch};
|
||||||
|
$dispatch->{Example} = "extensions::example::lib::WSExample";
|
|
@ -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)');
|
$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 = [
|
my $special_groups = [
|
||||||
[ 'bz_editcheckers', 'Users who can edit Bugzilla Correctness Checkers', [ 'admin' ] ],
|
[ 'bz_editcheckers', 'Users who can edit Bugzilla Correctness Checkers', [ 'admin' ] ],
|
||||||
[ 'editfields', 'Users who can edit Bugzilla field parameters', [ 'admin' ] ],
|
[ 'editfields', 'Users who can edit Bugzilla field parameters', [ 'admin' ] ],
|
||||||
|
|
|
@ -11,7 +11,7 @@ my ($xmlrpc, $jsonrpc, $config) = get_rpc_clients();
|
||||||
|
|
||||||
use constant INVALID_BUG_ID => -1;
|
use constant INVALID_BUG_ID => -1;
|
||||||
use constant INVALID_BUG_ALIAS => 'aaaaaaa12345';
|
use constant INVALID_BUG_ALIAS => 'aaaaaaa12345';
|
||||||
use constant PRIVATE_BUG => 40933;
|
use constant PRIVATE_BUG => 40934;
|
||||||
use constant PUBLIC_BUG => 58878;
|
use constant PUBLIC_BUG => 58878;
|
||||||
|
|
||||||
use constant TEST_COMMENT => '--- Test Comment From QA Tests ---';
|
use constant TEST_COMMENT => '--- Test Comment From QA Tests ---';
|
||||||
|
|
|
@ -24,35 +24,42 @@
|
||||||
%]
|
%]
|
||||||
|
|
||||||
[% param_descs = {
|
[% param_descs = {
|
||||||
quip_list_entry_control => "Controls how easily users can add entries to the quip list.
|
quip_list_entry_control =>
|
||||||
<ul>
|
"Controls how easily users can add entries to the quip list.
|
||||||
<li>
|
<ul>
|
||||||
open - Users may freely add to the quip list, and
|
<li>
|
||||||
their entries will immediately be available for viewing.
|
open - Users may freely add to the quip list, and
|
||||||
</li>
|
their entries will immediately be available for viewing.
|
||||||
<li>
|
</li>
|
||||||
moderated - quips can be entered, but need to be approved
|
<li>
|
||||||
by an admin before they will be shown.
|
moderated - quips can be entered, but need to be approved
|
||||||
</li>
|
by an admin before they will be shown.
|
||||||
<li>
|
</li>
|
||||||
closed - no new additions to the quips list are allowed.
|
<li>
|
||||||
</li>
|
closed - no new additions to the quips list are allowed.
|
||||||
</ul>",
|
</li>
|
||||||
|
</ul>",
|
||||||
|
|
||||||
mostfreqthreshold => "The minimum number of duplicates $terms.abug needs to show up on the " _
|
mostfreqthreshold =>
|
||||||
"<a href=\"duplicates.cgi\">most frequently reported $terms.bugs page</a>. " _
|
"The minimum number of duplicates $terms.abug needs to show up on the " _
|
||||||
"If you have a large database and this page takes a long time to " _
|
"<a href=\"duplicates.cgi\">most frequently reported $terms.bugs page</a>. " _
|
||||||
"load, try increasing this number.",
|
"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' " _
|
mybugstemplate =>
|
||||||
"list for a user. %userid% will get replaced with the login name of a user.",
|
"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 " _
|
defaultquery =>
|
||||||
"access the advanced query page. It's in URL parameter " _
|
"This is the default query that initially comes up when you " _
|
||||||
"format, which makes it hard to read. Sorry!",
|
"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"
|
"Whether to allow a search on the 'Simple Search' page with an empty"
|
||||||
_ " 'Words' field.",
|
_ " '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)",
|
||||||
|
|
||||||
} %]
|
} %]
|
||||||
|
|
|
@ -66,6 +66,7 @@ END
|
||||||
feature_smtp_auth => 'SMTP Authentication',
|
feature_smtp_auth => 'SMTP Authentication',
|
||||||
feature_updates => 'Automatic Update Notifications',
|
feature_updates => 'Automatic Update Notifications',
|
||||||
feature_xmlrpc => 'XML-RPC Interface',
|
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"
|
header => "* This is Bugzilla ##bz_ver## on perl ##perl_ver##\n"
|
||||||
. "* Running on ##os_name## ##os_ver##",
|
. "* Running on ##os_name## ##os_ver##",
|
||||||
|
|
Loading…
Reference in New Issue