From ed3e015a1c21061ed9f30cfc3fe3c3e83c0d2fb1 Mon Sep 17 00:00:00 2001 From: Tiago Mello Date: Mon, 20 Dec 2010 20:49:10 -0200 Subject: Bug 593539: Refactor See Also to use separate modules for each type of URL r/a=mkanat --- Bugzilla/BugUrl.pm | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 Bugzilla/BugUrl.pm (limited to 'Bugzilla/BugUrl.pm') diff --git a/Bugzilla/BugUrl.pm b/Bugzilla/BugUrl.pm new file mode 100644 index 000000000..521ee8193 --- /dev/null +++ b/Bugzilla/BugUrl.pm @@ -0,0 +1,173 @@ +# -*- 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 Tiago Mello +# Portions created by Tiago Mello are Copyright (C) 2010 +# Tiago Mello. All Rights Reserved. +# +# Contributor(s): Tiago Mello + +package Bugzilla::BugUrl; +use strict; +use base qw(Bugzilla::Object); + +use Bugzilla::Util; +use Bugzilla::Error; +use Bugzilla::Constants; + +use URI::QueryParam; + +############################### +#### Initialization #### +############################### + +use constant DB_TABLE => 'bug_see_also'; +use constant NAME_FIELD => 'value'; +use constant LIST_ORDER => 'id'; + +use constant DB_COLUMNS => qw( + id + bug_id + value +); + +# This must be strings with the names of the validations, +# instead of coderefs, because subclasses override these +# validators with their own. +use constant VALIDATORS => { + value => '_check_value', + bug_id => '_check_bug_id', +}; + +# This is the order we go through all of subclasses and +# pick the first one that should handle the url. New +# subclasses should be added at the end of the list. +use constant SUB_CLASSES => qw( + Bugzilla::BugUrl::Bugzilla::Local + Bugzilla::BugUrl::Bugzilla + Bugzilla::BugUrl::Launchpad + Bugzilla::BugUrl::Google + Bugzilla::BugUrl::Debian +); + +############################### +#### Methods #### +############################### + +sub new { + my $class = shift; + my $param = shift; + + if (ref $param) { + my $bug_id = $param->{bug_id}; + my $name = $param->{name} || $param->{value}; + if (!defined $bug_id) { + ThrowCodeError('bad_arg', + { argument => 'bug_id', + function => "${class}::new" }); + } + if (!defined $name) { + ThrowCodeError('bad_arg', + { argument => 'name', + function => "${class}::new" }); + } + + my $condition = 'bug_id = ? AND value = ?'; + my @values = ($bug_id, $name); + $param = { condition => $condition, values => \@values }; + } + + unshift @_, $param; + return $class->SUPER::new(@_); +} + +# This is an abstract method. It must be overridden +# in every subclass. +sub should_handle { + my ($class, $input) = @_; + ThrowCodeError('unknown_method', + { method => "${class}::should_handle" }); +} + +sub class_for { + my ($class, $value) = @_; + + my $uri = URI->new($value); + foreach my $subclass ($class->SUB_CLASSES) { + eval "use $subclass"; + die $@ if $@; + return wantarray ? ($subclass, $uri) : $subclass + if $subclass->should_handle($uri); + } + + ThrowUserError('bug_url_invalid', { url => $value, + reason => 'show_bug' }); +} + +sub _check_bug_id { + my ($class, $bug_id) = @_; + + my $bug; + if (blessed $bug_id) { + # We got a bug object passed in, use it + $bug = $bug_id; + $bug->check_is_visible; + } + else { + # We got a bug id passed in, check it and get the bug object + $bug = Bugzilla::Bug->check({ id => $bug_id }); + } + + return $bug->id; +} + +sub _check_value { + my ($class, $uri) = @_; + + my $value = $uri->as_string; + + if (!$value) { + ThrowCodeError('param_required', + { function => 'add_see_also', param => '$value' }); + } + + # We assume that the URL is an HTTP URL if there is no (something):// + # in front. + if (!$uri->scheme) { + # This works better than setting $uri->scheme('http'), because + # that creates URLs like "http:domain.com" and doesn't properly + # differentiate the path from the domain. + $uri = new URI("http://$value"); + } + elsif ($uri->scheme ne 'http' && $uri->scheme ne 'https') { + ThrowUserError('bug_url_invalid', { url => $value, reason => 'http' }); + } + + # This stops the following edge cases from being accepted: + # * show_bug.cgi?id=1 + # * /show_bug.cgi?id=1 + # * http:///show_bug.cgi?id=1 + if (!$uri->authority or $uri->path !~ m{/}) { + ThrowUserError('bug_url_invalid', + { url => $value, reason => 'path_only' }); + } + + if (length($uri->path) > MAX_BUG_URL_LENGTH) { + ThrowUserError('bug_url_too_long', { url => $uri->path }); + } + + return $uri; +} + +1; -- cgit v1.2.3-24-g4f1b