summaryrefslogtreecommitdiffstats
path: root/extensions/Bitly
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/Bitly')
-rw-r--r--extensions/Bitly/Config.pm45
-rw-r--r--extensions/Bitly/Extension.pm31
-rw-r--r--extensions/Bitly/lib/WebService.pm141
-rw-r--r--extensions/Bitly/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl13
-rw-r--r--extensions/Bitly/template/en/default/hook/global/header-start.html.tmpl13
-rw-r--r--extensions/Bitly/template/en/default/hook/global/user-error-errors.html.tmpl17
-rw-r--r--extensions/Bitly/template/en/default/hook/list/list-links.html.tmpl24
-rw-r--r--extensions/Bitly/web/js/bitly.js100
-rw-r--r--extensions/Bitly/web/styles/bitly.css23
9 files changed, 407 insertions, 0 deletions
diff --git a/extensions/Bitly/Config.pm b/extensions/Bitly/Config.pm
new file mode 100644
index 000000000..aff9d4b4b
--- /dev/null
+++ b/extensions/Bitly/Config.pm
@@ -0,0 +1,45 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Bitly;
+use strict;
+
+use Bugzilla::Install::Util qw(vers_cmp);
+
+use constant NAME => 'Bitly';
+
+sub REQUIRED_MODULES {
+ my @required;
+ push @required, {
+ package => 'LWP',
+ module => 'LWP',
+ version => 5,
+ };
+ # LWP 6 split https support into a separate package
+ if (Bugzilla::Install::Requirements::have_vers({
+ package => 'LWP',
+ module => 'LWP',
+ version => 6,
+ })) {
+ push @required, {
+ package => 'LWP-Protocol-https',
+ module => 'LWP::Protocol::https',
+ version => 0
+ };
+ }
+ return \@required;
+}
+
+use constant OPTIONAL_MODULES => [
+ {
+ package => 'Mozilla-CA',
+ module => 'Mozilla::CA',
+ version => 0
+ },
+];
+
+__PACKAGE__->NAME;
diff --git a/extensions/Bitly/Extension.pm b/extensions/Bitly/Extension.pm
new file mode 100644
index 000000000..a368b20fe
--- /dev/null
+++ b/extensions/Bitly/Extension.pm
@@ -0,0 +1,31 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Bitly;
+use strict;
+use warnings;
+
+use base qw(Bugzilla::Extension);
+our $VERSION = '1';
+
+use Bugzilla;
+
+sub webservice {
+ my ($self, $args) = @_;
+ $args->{dispatch}->{Bitly} = "Bugzilla::Extension::Bitly::WebService";
+}
+
+sub config_modify_panels {
+ my ($self, $args) = @_;
+ push @{ $args->{panels}->{advanced}->{params} }, {
+ name => 'bitly_token',
+ type => 't',
+ default => '',
+ };
+}
+
+__PACKAGE__->NAME;
diff --git a/extensions/Bitly/lib/WebService.pm b/extensions/Bitly/lib/WebService.pm
new file mode 100644
index 000000000..e721103b0
--- /dev/null
+++ b/extensions/Bitly/lib/WebService.pm
@@ -0,0 +1,141 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::Extension::Bitly::WebService;
+
+use strict;
+use warnings;
+
+use base qw(Bugzilla::WebService);
+
+use Bugzilla::CGI;
+use Bugzilla::Constants;
+use Bugzilla::Error;
+use Bugzilla::Search;
+use Bugzilla::Search::Quicksearch;
+use Bugzilla::Util 'correct_urlbase';
+use Bugzilla::WebService::Util 'validate';
+use JSON;
+use LWP::UserAgent;
+use URI;
+use URI::Escape;
+use URI::QueryParam;
+
+sub _validate_uri {
+ my ($self, $params) = @_;
+
+ # extract url from params
+ if (!defined $params->{url}) {
+ ThrowCodeError(
+ 'param_required',
+ { function => 'Bitly.shorten', param => 'url' }
+ );
+ }
+ my $url = ref($params->{url}) ? $params->{url}->[0] : $params->{url};
+
+ # only allow buglist queries for this bugzilla install
+ my $uri = URI->new($url);
+ $uri->query(undef);
+ $uri->fragment(undef);
+ if ($uri->as_string ne correct_urlbase() . 'buglist.cgi') {
+ ThrowUserError('bitly_unsupported');
+ }
+
+ return URI->new($url);
+}
+
+sub shorten {
+ my ($self) = shift;
+ my $uri = $self->_validate_uri(@_);
+
+ # the list_id is user-specific, remove it
+ $uri->query_param_delete('list_id');
+
+ return $self->_bitly($uri);
+}
+
+sub list {
+ my ($self) = shift;
+ my $uri = $self->_validate_uri(@_);
+
+ # map params to cgi vars, converting quicksearch if required
+ my $params = $uri->query_param('quicksearch')
+ ? Bugzilla::CGI->new(quicksearch($uri->query_param('quicksearch')))->Vars
+ : Bugzilla::CGI->new($uri->query)->Vars;
+
+ # execute the search
+ my $search = Bugzilla::Search->new(
+ params => $params,
+ fields => ['bug_id'],
+ limit => Bugzilla->params->{max_search_results},
+ );
+ my $data = $search->data;
+
+ # form a bug_id only url, sanity check the length
+ $uri = URI->new(correct_urlbase() . 'buglist.cgi?bug_id=' . join(',', map { $_->[0] } @$data));
+ if (length($uri->as_string) > CGI_URI_LIMIT) {
+ ThrowUserError('bitly_failure', { message => "Too many bugs returned by search" });
+ }
+
+ # shorten
+ return $self->_bitly($uri);
+}
+
+sub _bitly {
+ my ($self, $uri) = @_;
+
+ # form request url
+ # http://dev.bitly.com/links.html#v3_shorten
+ my $bitly_url = sprintf(
+ 'https://api-ssl.bitly.com/v3/shorten?access_token=%s&longUrl=%s',
+ Bugzilla->params->{bitly_token},
+ uri_escape($uri->as_string)
+ );
+
+ # is Mozilla::CA isn't installed, skip certificate verification
+ eval { require Mozilla::CA };
+ $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = $@ ? 0 : 1;
+
+ # request
+ my $ua = LWP::UserAgent->new(agent => 'Bugzilla');
+ $ua->timeout(10);
+ $ua->protocols_allowed(['http', 'https']);
+ if (my $proxy_url = Bugzilla->params->{proxy_url}) {
+ $ua->proxy(['http', 'https'], $proxy_url);
+ }
+ else {
+ $ua->env_proxy();
+ }
+ my $response = $ua->get($bitly_url);
+ if ($response->is_error) {
+ ThrowUserError('bitly_failure', { message => $response->message });
+ }
+ my $result = decode_json($response->decoded_content);
+ if ($result->{status_code} != 200) {
+ ThrowUserError('bitly_failure', { message => $result->{status_txt} });
+ }
+
+ # return just the short url
+ return { url => $result->{data}->{url} };
+}
+
+sub rest_resources {
+ return [
+ qr{^/bitly/shorten$}, {
+ GET => {
+ method => 'shorten',
+ },
+ },
+ qr{^/bitly/list$}, {
+ GET => {
+ method => 'list',
+ },
+ },
+ ]
+}
+
+1;
diff --git a/extensions/Bitly/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl b/extensions/Bitly/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
new file mode 100644
index 000000000..2e0f58bc4
--- /dev/null
+++ b/extensions/Bitly/template/en/default/hook/admin/params/editparams-current_panel.html.tmpl
@@ -0,0 +1,13 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF panel.name == "advanced" %]
+ [% panel.param_descs.bitly_token =
+ 'Bitly Generic Access Token'
+ %]
+[% END -%]
diff --git a/extensions/Bitly/template/en/default/hook/global/header-start.html.tmpl b/extensions/Bitly/template/en/default/hook/global/header-start.html.tmpl
new file mode 100644
index 000000000..12ab7b20f
--- /dev/null
+++ b/extensions/Bitly/template/en/default/hook/global/header-start.html.tmpl
@@ -0,0 +1,13 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% RETURN UNLESS template.name == 'list/list.html.tmpl' %]
+
+[% style_urls.push('extensions/Bitly/web/styles/bitly.css') %]
+[% javascript_urls.push('extensions/Bitly/web/js/bitly.js') %]
+[% yui.push('container') %]
diff --git a/extensions/Bitly/template/en/default/hook/global/user-error-errors.html.tmpl b/extensions/Bitly/template/en/default/hook/global/user-error-errors.html.tmpl
new file mode 100644
index 000000000..edf0b0724
--- /dev/null
+++ b/extensions/Bitly/template/en/default/hook/global/user-error-errors.html.tmpl
@@ -0,0 +1,17 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% IF error == "bitly_failure" %]
+ [% title = "Failed to generate short URL" %]
+ [% message FILTER html %]
+
+[% ELSIF error == "bitly_unsupported" %]
+ [% title = "Unsupported URL" %]
+ The requested URL is not supported.
+
+[% END %]
diff --git a/extensions/Bitly/template/en/default/hook/list/list-links.html.tmpl b/extensions/Bitly/template/en/default/hook/list/list-links.html.tmpl
new file mode 100644
index 000000000..836c017ed
--- /dev/null
+++ b/extensions/Bitly/template/en/default/hook/list/list-links.html.tmpl
@@ -0,0 +1,24 @@
+[%# This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ #
+ # This Source Code Form is "Incompatible With Secondary Licenses", as
+ # defined by the Mozilla Public License, v. 2.0.
+ #%]
+
+[% RETURN UNLESS user.id && Bugzilla.params.bitly_token %]
+
+<div id="bitly_overlay">
+ <div class="bd">
+ <select id="bitly_type" onchange="YAHOO.bitly.execute()">
+ <option value="shorten">Share a link to this search</option>
+ <option value="list">Share a link to this list of [% terms.bugs %]</option>
+ </select>
+ <input id="bitly_url" readonly placeholder="Generating short link...">
+ </div>
+ <div class="ft">
+ <button id="bitly_close" class="notransition">Close</button>
+ </div>
+</div>
+<a id="bitly_shorten" href="#" onclick="YAHOO.bitly.toggle();return false">Short URL</a>
+|&nbsp; [%# using nbsp because tt always trims trailing whitespace from templates %]
diff --git a/extensions/Bitly/web/js/bitly.js b/extensions/Bitly/web/js/bitly.js
new file mode 100644
index 000000000..62c49b650
--- /dev/null
+++ b/extensions/Bitly/web/js/bitly.js
@@ -0,0 +1,100 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+(function() {
+ 'use strict';
+ var Dom = YAHOO.util.Dom;
+ YAHOO.namespace('bitly');
+ var bitly = YAHOO.bitly;
+
+ bitly.dialog = false;
+ bitly.url = { shorten: '', list: '' };
+
+ bitly.shorten = function() {
+ if (this.dialog) {
+ this.dialog.show();
+ var el = Dom.get('bitly_url');
+ el.select();
+ el.focus();
+ return;
+ }
+ this.dialog = new YAHOO.widget.Overlay('bitly_overlay', {
+ visible: true,
+ close: false,
+ underlay: 'shadow',
+ width: '400px',
+ context: [ 'bitly_shorten', 'bl', 'tl', ['windowResize'], [0, -10] ]
+ });
+ this.dialog.render(document.body);
+
+ YAHOO.util.Event.addListener('bitly_close', 'click', function() {
+ YAHOO.bitly.dialog.hide();
+ });
+ YAHOO.util.Event.addListener('bitly_url', 'keypress', function(o) {
+ if (o.keyCode == 27 || o.keyCode == 13)
+ YAHOO.bitly.dialog.hide();
+ });
+ this.execute();
+ Dom.get('bitly_url').focus();
+ };
+
+ bitly.execute = function() {
+ Dom.get('bitly_url').value = '';
+
+ var type = Dom.get('bitly_type').value;
+ if (this.url[type]) {
+ this.set(this.url[type]);
+ return;
+ }
+
+ var url = 'rest/bitly/' + type + '?url=' + encodeURIComponent(document.location);
+ YAHOO.util.Connect.initHeader("Accept", "application/json");
+ YAHOO.util.Connect.asyncRequest('GET', url, {
+ success: function(o) {
+ var response = YAHOO.lang.JSON.parse(o.responseText);
+ if (response.error) {
+ bitly.set(response.message);
+ }
+ else {
+ bitly.url[type] = response.url;
+ bitly.set(response.url);
+ }
+ },
+ failure: function(o) {
+ try {
+ var response = YAHOO.lang.JSON.parse(o.responseText);
+ if (response.error) {
+ bitly.set(response.message);
+ }
+ else {
+ bitly.set(o.statusText);
+ }
+ } catch (ex) {
+ bitly.set(o.statusText);
+ }
+ }
+ });
+ };
+
+ bitly.set = function(value) {
+ var el = Dom.get('bitly_url');
+ el.value = value;
+ el.select();
+ el.focus();
+ };
+
+ bitly.toggle = function() {
+ if (this.dialog
+ && YAHOO.util.Dom.get('bitly_overlay').style.visibility == 'visible')
+ {
+ this.dialog.hide();
+ }
+ else {
+ this.shorten();
+ }
+ };
+})();
diff --git a/extensions/Bitly/web/styles/bitly.css b/extensions/Bitly/web/styles/bitly.css
new file mode 100644
index 000000000..110a6bef4
--- /dev/null
+++ b/extensions/Bitly/web/styles/bitly.css
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Source Code Form is "Incompatible With Secondary Licenses", as
+ * defined by the Mozilla Public License, v. 2.0. */
+
+#bitly_overlay {
+ position: absolute;
+ background: #eee;
+ border: 1px solid black;
+ padding: 5px;
+ margin: 10px;
+ visibility: collapse;
+ box-shadow: 3px 3px 6px #888;
+ -moz-box-shadow: 3px 3px 6px #888;
+}
+
+#bitly_url {
+ margin: 2px 0;
+ display: block;
+ width: 100%;
+}