You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ja...@apache.org on 2013/07/31 18:56:05 UTC
[1/4] git commit: updated refs/heads/1867-feature-plugins to 7789506
Updated Branches:
refs/heads/1867-feature-plugins [created] 778950622
add couch_plugins
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/66faf3a8
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/66faf3a8
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/66faf3a8
Branch: refs/heads/1867-feature-plugins
Commit: 66faf3a899f0b8b13c3cc774c629154f9e001a30
Parents: 198f936
Author: Jan Lehnardt <ja...@apache.org>
Authored: Wed Jul 31 15:12:30 2013 +0200
Committer: Jan Lehnardt <ja...@apache.org>
Committed: Wed Jul 31 15:12:30 2013 +0200
----------------------------------------------------------------------
src/couch_plugins/ebin/couch_plugins.app | 9 +
src/couch_plugins/src/couch_plugins.app.src | 12 +
src/couch_plugins/src/couch_plugins.erl | 248 +++++++++++++++++++++
src/couch_plugins/src/couch_plugins_httpd.erl | 10 +
4 files changed, 279 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/66faf3a8/src/couch_plugins/ebin/couch_plugins.app
----------------------------------------------------------------------
diff --git a/src/couch_plugins/ebin/couch_plugins.app b/src/couch_plugins/ebin/couch_plugins.app
new file mode 100644
index 0000000..b67645e
--- /dev/null
+++ b/src/couch_plugins/ebin/couch_plugins.app
@@ -0,0 +1,9 @@
+{application,couch_plugins,
+ [{description,"A CouchDB Plugin Installer"},
+ {vsn,"1"},
+ {registered,[]},
+ {applications,[kernel,stdlib]},
+ {mod,{couch_plugins_app,[]}},
+ {env,[]},
+ {modules,[couch_plugins,couch_plugins_app,couch_plugins_httpd,
+ couch_plugins_sup]}]}.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/66faf3a8/src/couch_plugins/src/couch_plugins.app.src
----------------------------------------------------------------------
diff --git a/src/couch_plugins/src/couch_plugins.app.src b/src/couch_plugins/src/couch_plugins.app.src
new file mode 100644
index 0000000..059663c
--- /dev/null
+++ b/src/couch_plugins/src/couch_plugins.app.src
@@ -0,0 +1,12 @@
+{application, couch_plugins,
+ [
+ {description, "A CouchDB Plugin Installer"},
+ {vsn, "1"},
+ {registered, []},
+ {applications, [
+ kernel,
+ stdlib
+ ]},
+ {mod, { couch_plugins_app, []}},
+ {env, []}
+ ]}.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/66faf3a8/src/couch_plugins/src/couch_plugins.erl
----------------------------------------------------------------------
diff --git a/src/couch_plugins/src/couch_plugins.erl b/src/couch_plugins/src/couch_plugins.erl
new file mode 100644
index 0000000..e406b2a
--- /dev/null
+++ b/src/couch_plugins/src/couch_plugins.erl
@@ -0,0 +1,248 @@
+-module(couch_plugins).
+-include("couch_db.hrl").
+%% Application callbacks
+-export([install/1]).
+
+
+% couch_plugins:install({"geocouch", "http://127.0.0.1:8000", "1.0.0", [{"R15B03", "+XOJP6GSzmuO2qKdnjO+mWckXVs="}]}).
+% couch_plugins:install({"geocouch", "http://people.apache.org/~jan/", "couchdb1.2.x_v0.3.0-11-gd83ba22", [{"R15B03", "Z9xK+OKLRvqKx3uoQHsiTuv6mrY="}]}).
+
+
+-define(PLUGIN_DIR, "/tmp/couchdb_plugins").
+
+log(T) ->
+ ?LOG_DEBUG("[couch_plugins] ~p ~n", [T]).
+
+%% "geocouch", "http://localhost:8000/dist", "1.0.0"
+-type plugin() :: {string(), string(), string(), list()}.
+-spec install(plugin()) -> ok | {error, string()}.
+install({Name, _BaseUrl, Version, Checksums}=Plugin) ->
+ log("Installing " ++ Name),
+
+ {ok, LocalFilename} = download(Plugin),
+ log("downloaded to " ++ LocalFilename),
+
+ ok = verify_checksum(LocalFilename, Checksums),
+ log("checksum verified"),
+
+ ok = untargz(LocalFilename),
+ log("extraction done"),
+
+ ok = add_code_path(Name, Version),
+ log("added code path"),
+
+ ok = load_config(Name, Version),
+ load_plugin(Name),
+
+ log("loaded plugin"),
+ ok.
+
+-spec load_config(string(), string()) -> ok | {error, string()}.
+load_config(Name, Version) ->
+ ConfigFile = ?PLUGIN_DIR ++ "/" ++ get_file_slug(Name, Version) ++ "/priv/config.erlt",
+ load_config_file(file_exists(ConfigFile), ConfigFile).
+
+-spec load_config_file(boolean(), string()) -> ok | {error, string()}.
+load_config_file(false, _) -> ok;
+load_config_file(true, ConfigFile) ->
+ % read file
+ {ok, ConfigFileData} = file:read_file(ConfigFile),
+ % split by \n
+ Lines = binary:split(ConfigFileData, <<"\n">>, [global]),
+ % feed each line...
+ lists:foreach(
+ fun(<<>>) ->
+ ok; % skip empty lines
+ (<<";", _Rest/binary>>) ->
+ ok; % ignore comments
+ (Line) ->
+ % ...to couch_util:parse_term()...
+ case couch_util:parse_term(Line) of
+ {ok, {{Section, Key}, Value}} ->
+ % ...and set the configs
+ ?LOG_DEBUG("parsed Line correctly: ~p", [Line]),
+ couch_config:set(Section, Key, Value);
+ Else ->
+ ?LOG_ERROR("Error parsing plugin config from line ~s", [Line]),
+ Else
+ end
+ end, Lines),
+ ok.
+
+-spec add_code_path(string(), string()) -> ok | {error, bad_directory}.
+add_code_path(Name, Version) ->
+ PluginPath = ?PLUGIN_DIR ++ "/" ++ get_file_slug(Name, Version) ++ "/ebin",
+ case code:add_path(PluginPath) of
+ true -> ok;
+ Else ->
+ ?LOG_ERROR("Failed to add PluginPath: '~s'", [PluginPath]),
+ Else
+ end.
+
+load_plugin(NameList) ->
+ Name = list_to_atom(NameList),
+ application:load(Name).
+
+
+-spec untargz(string()) -> {ok, string()} | {error, string()}.
+untargz(Filename) ->
+ % read .gz file
+ {ok, GzData} = file:read_file(Filename),
+ % gunzip
+ log("unzipped"),
+ TarData = zlib:gunzip(GzData),
+ ok = filelib:ensure_dir(?PLUGIN_DIR),
+ % untar
+ erl_tar:extract({binary, TarData}, [{cwd, ?PLUGIN_DIR}, keep_old_files]).
+
+
+% downloads a pluygin .tar.gz into a local plugins directory
+-spec download(string()) -> ok | {error, string()}.
+download({Name, _BaseUrl, Version, _Checksums}=Plugin) ->
+ TargetFile = "/tmp/" ++ get_filename(Name, Version),
+ case file_exists(TargetFile) of
+ %% wipe and redownload
+ true -> file:delete(TargetFile);
+ _Else -> ok
+ end,
+ Url = get_url(Plugin),
+ HTTPOptions = [
+ {connect_timeout, 30*1000}, % 30 seconds
+ {timeout, 30*1000} % 30 seconds
+ ],
+ % todo: windows
+ Options = [
+ {stream, TargetFile}, % /tmp/something
+ {body_format, binary},
+ {full_result, false}
+ ],
+ % todo: reduce to just httpc:request()
+ case httpc:request(get, {Url, []}, HTTPOptions, Options) of
+ {ok, _Result} ->
+ log("downloading " ++ Url),
+ {ok, TargetFile};
+ Error -> Error
+ end.
+
+-spec verify_checksum(string(), list()) -> ok | {error, string()}.
+verify_checksum(Filename, Checksums) ->
+ OTPRelease = erlang:system_info(otp_release),
+ case proplists:get_value(OTPRelease, Checksums) of
+ undefined ->
+ ?LOG_ERROR("[couch_plugins] Can't find checksum for OTP Release '~s'", [OTPRelease]),
+ {error, no_checksum};
+ Checksum ->
+ do_verify_checksum(Filename, Checksum)
+ end.
+
+-spec do_verify_checksum(string(), string()) -> ok | {error, string()}.
+do_verify_checksum(Filename, Checksum) ->
+ case file:read_file(Filename) of
+ {ok, Data} ->
+ ComputedChecksum = binary_to_list(base64:encode(crypto:sha(Data))),
+ case ComputedChecksum of
+ Checksum -> ok;
+ _Else ->
+ ?LOG_ERROR("Checksum mismatch. Wanted: '~p'. Got '~p'", [Checksum, ComputedChecksum]),
+ {error, checksum_mismatch}
+ end;
+ Error -> Error
+ end.
+
+
+
+
+-spec get_url(plugin()) -> string().
+get_url({Name, BaseUrl, Version, _Checksums}) ->
+ BaseUrl ++ "/" ++ get_filename(Name, Version).
+
+-spec get_filename(string(), string()) -> string().
+get_filename(Name, Version) ->
+ get_file_slug(Name, Version) ++ ".tar.gz".
+
+-spec get_file_slug(string(), string()) -> string().
+get_file_slug(Name, Version) ->
+ % OtpRelease does not include patch levels like the -1 in R15B03-1
+ OTPRelease = erlang:system_info(otp_release),
+ Name ++ "-" ++ Version ++ "-" ++ OTPRelease.
+
+-spec file_exists(string()) -> boolean().
+file_exists(Filename) ->
+ does_file_exist(file:read_file_info(Filename)).
+-spec does_file_exist(term()) -> boolean().
+does_file_exist({error, enoent}) -> false;
+does_file_exist(_Else) -> true.
+
+% installing a plugin:
+% - POST /_plugins -d {plugin-def}
+% - get plugin definition
+% - get download URL (matching erlang version)
+% - download archive
+% - match checksum
+% - untar-gz archive into a plugins dir
+% - code:add_path(“geocouch-{geocouch_version}-{erlang_version}/ebin”)
+% - [cp geocouch-{geocouch_version}-{erlang_version}/etc/ ]
+% - application:start(geocouch)
+% - register plugin in plugin registry
+
+% Plugin registry impl:
+% - _plugins database
+% - pro: known db ops
+% - con: no need for replication, needs to be system db etc.
+% - _config/plugins namespace in config
+% - pro: lightweight, fits rarely-changing nature better
+% - con: potentially not flexible enough
+
+
+
+% /geocouch
+% /geocouch/dist/
+% /geocouch/dist/geocouch-{geocouch_version}-{erlang_version}.tar.gz
+
+% tar.gz includes:
+% geocouch-{geocouch_version}-{erlang_version}/
+% geocouch-{geocouch_version}-{erlang_version}/ebin
+% [geocouch-{geocouch_version}-{erlang_version}/config/config.erlt]
+% [geocouch-{geocouch_version}-{erlang_version}/share/]
+
+
+
+% config.erlt:
+% // {{Section, Key}, Value}
+% {{"httpd_db_handlers", "_spatial_cleanup"}, "{couch_spatial_http, handle_cleanup_req}"}
+% {{"httpd_design_handlers", "_spatial"}, "{couch_spatial_http, handle_spatial_req}"}
+% {{"httpd_design_handlers", "_list"}, "{couch_spatial_list, handle_view_list_req}"}
+% {{"httpd_design_handlers", "_info"}, "{couch_spatial_http, handle_info_req}"}
+% {{"httpd_design_handlers", "_compact"}, "{couch_spatial_http, handle_compact_req}"}
+
+% milestones
+% 1. MVP
+% - erlang plugins only
+% - no c deps
+% - install via futon (admin only)
+% - uninstall via futon (admin only)
+% - load plugin.tgz from the web
+% - no security checking
+% - no identity checking
+% - hardcoded list of plugins in futon
+% - must publish on *.apache.org/*
+
+% 2. Creator friendly
+% - couchdb plugin template
+% - easy to publish
+
+% 3. Public registry
+% - plugin authors can publish stuff independently, shows up in futon
+%
+
+% XXX Later
+% - signing of plugin releases
+% - signing verification of plugin releases
+
+
+% Questions:
+% - where should the downloaded .beam files put?
+% - in couch 1.x.x context
+% - in bigcouch context
+% - what is a server-user owned data/ dir we can use for this, that isn’t db_dir or index_dir or log or var/run or /tmp
+
http://git-wip-us.apache.org/repos/asf/couchdb/blob/66faf3a8/src/couch_plugins/src/couch_plugins_httpd.erl
----------------------------------------------------------------------
diff --git a/src/couch_plugins/src/couch_plugins_httpd.erl b/src/couch_plugins/src/couch_plugins_httpd.erl
new file mode 100644
index 0000000..1e61aa2
--- /dev/null
+++ b/src/couch_plugins/src/couch_plugins_httpd.erl
@@ -0,0 +1,10 @@
+-module(couch_plugins_httpd).
+
+-export([handle_req/1]).
+
+-include_lib("couch_db.hrl").
+
+handle_req(#httpd{method='PUT'}=Req) ->
+ couch_httpd:send_json(Req, 202, {[{ok, true}]});
+handle_req(Req) ->
+ couch_httpd:send_method_not_allowed(Req, "PUT").
[3/4] git commit: updated refs/heads/1867-feature-plugins to 7789506
Posted by ja...@apache.org.
add docs/debug output
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/449b458f
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/449b458f
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/449b458f
Branch: refs/heads/1867-feature-plugins
Commit: 449b458f26b632bb9f99258583cb9880282fbe31
Parents: f823b77
Author: Jan Lehnardt <ja...@apache.org>
Authored: Wed Jul 31 18:48:19 2013 +0200
Committer: Jan Lehnardt <ja...@apache.org>
Committed: Wed Jul 31 18:48:19 2013 +0200
----------------------------------------------------------------------
src/couch_plugins/src/couch_plugins.erl | 8 +++----
src/couch_plugins/src/couch_plugins_httpd.erl | 25 +++++++++++++++++++---
2 files changed, 26 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/449b458f/src/couch_plugins/src/couch_plugins.erl
----------------------------------------------------------------------
diff --git a/src/couch_plugins/src/couch_plugins.erl b/src/couch_plugins/src/couch_plugins.erl
index 0a65bf7..7dd3bd2 100644
--- a/src/couch_plugins/src/couch_plugins.erl
+++ b/src/couch_plugins/src/couch_plugins.erl
@@ -1,13 +1,10 @@
-module(couch_plugins).
-include("couch_db.hrl").
-%% Application callbacks
-export([install/1]).
-
% couch_plugins:install({"geocouch", "http://127.0.0.1:8000", "1.0.0", [{"R15B03", "+XOJP6GSzmuO2qKdnjO+mWckXVs="}]}).
% couch_plugins:install({"geocouch", "http://people.apache.org/~jan/", "couchdb1.2.x_v0.3.0-11-gd83ba22", [{"R15B03", "ZetgdHj2bY2w37buulWVf3USOZs="}]}).
-
-define(PLUGIN_DIR, "/tmp/couchdb_plugins").
log(T) ->
@@ -79,6 +76,7 @@ add_code_path(Name, Version) ->
Else
end.
+-spec load_plugin(string()) -> ok | {error, atom()}.
load_plugin(NameList) ->
Name = list_to_atom(NameList),
application:load(Name).
@@ -126,6 +124,7 @@ download({Name, _BaseUrl, Version, _Checksums}=Plugin) ->
-spec verify_checksum(string(), list()) -> ok | {error, string()}.
verify_checksum(Filename, Checksums) ->
+
OTPRelease = erlang:system_info(otp_release),
case proplists:get_value(OTPRelease, Checksums) of
undefined ->
@@ -137,6 +136,7 @@ verify_checksum(Filename, Checksums) ->
-spec do_verify_checksum(string(), string()) -> ok | {error, string()}.
do_verify_checksum(Filename, Checksum) ->
+ ?LOG_DEBUG("Filename: ~s", [Filename]),
case file:read_file(Filename) of
{ok, Data} ->
ComputedChecksum = binary_to_list(base64:encode(crypto:sha(Data))),
@@ -150,7 +150,7 @@ do_verify_checksum(Filename, Checksum) ->
end.
-
+%% utils
-spec get_url(plugin()) -> string().
get_url({Name, BaseUrl, Version, _Checksums}) ->
http://git-wip-us.apache.org/repos/asf/couchdb/blob/449b458f/src/couch_plugins/src/couch_plugins_httpd.erl
----------------------------------------------------------------------
diff --git a/src/couch_plugins/src/couch_plugins_httpd.erl b/src/couch_plugins/src/couch_plugins_httpd.erl
index 1e61aa2..6d987ae 100644
--- a/src/couch_plugins/src/couch_plugins_httpd.erl
+++ b/src/couch_plugins/src/couch_plugins_httpd.erl
@@ -4,7 +4,26 @@
-include_lib("couch_db.hrl").
-handle_req(#httpd{method='PUT'}=Req) ->
- couch_httpd:send_json(Req, 202, {[{ok, true}]});
+handle_req(#httpd{method='POST'}=Req) ->
+ ok = couch_httpd:verify_is_server_admin(Req),
+ couch_httpd:validate_ctype(Req, "application/json"),
+
+ {PluginSpec} = couch_httpd:json_body_obj(Req),
+ ?LOG_DEBUG("Plugin Spec: ~p", [PluginSpec]),
+ Url = binary_to_list(couch_util:get_value(<<"url">>, PluginSpec)),
+ Name = binary_to_list(couch_util:get_value(<<"name">>, PluginSpec)),
+ Version = binary_to_list(couch_util:get_value(<<"version">>, PluginSpec)),
+ {Checksums0} = couch_util:get_value(<<"checksums">>, PluginSpec),
+ Checksums = lists:map(fun({K, V}) ->
+ {binary_to_list(K), binary_to_list(V)}
+ end, Checksums0),
+
+ case couch_plugins:install({Name, Url, Version, Checksums}}) of
+ ok ->
+ couch_httpd:send_json(Req, 202, {[{ok, true}]});
+ Error ->
+ ?LOG_DEBUG("Plugin Spec: ~p", [PluginSpec]),
+ couch_httpd:send_error(Req, {bad_request, Error})
+ end;
handle_req(Req) ->
- couch_httpd:send_method_not_allowed(Req, "PUT").
+ couch_httpd:send_method_not_allowed(Req, "POST").
[2/4] git commit: updated refs/heads/1867-feature-plugins to 7789506
Posted by ja...@apache.org.
update comment
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/f823b773
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/f823b773
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/f823b773
Branch: refs/heads/1867-feature-plugins
Commit: f823b773e2c6320ec05f63925831d1f8ba04f214
Parents: 66faf3a
Author: Jan Lehnardt <ja...@apache.org>
Authored: Wed Jul 31 15:14:55 2013 +0200
Committer: Jan Lehnardt <ja...@apache.org>
Committed: Wed Jul 31 15:14:55 2013 +0200
----------------------------------------------------------------------
src/couch_plugins/src/couch_plugins.erl | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/f823b773/src/couch_plugins/src/couch_plugins.erl
----------------------------------------------------------------------
diff --git a/src/couch_plugins/src/couch_plugins.erl b/src/couch_plugins/src/couch_plugins.erl
index e406b2a..0a65bf7 100644
--- a/src/couch_plugins/src/couch_plugins.erl
+++ b/src/couch_plugins/src/couch_plugins.erl
@@ -5,7 +5,7 @@
% couch_plugins:install({"geocouch", "http://127.0.0.1:8000", "1.0.0", [{"R15B03", "+XOJP6GSzmuO2qKdnjO+mWckXVs="}]}).
-% couch_plugins:install({"geocouch", "http://people.apache.org/~jan/", "couchdb1.2.x_v0.3.0-11-gd83ba22", [{"R15B03", "Z9xK+OKLRvqKx3uoQHsiTuv6mrY="}]}).
+% couch_plugins:install({"geocouch", "http://people.apache.org/~jan/", "couchdb1.2.x_v0.3.0-11-gd83ba22", [{"R15B03", "ZetgdHj2bY2w37buulWVf3USOZs="}]}).
-define(PLUGIN_DIR, "/tmp/couchdb_plugins").
[4/4] git commit: updated refs/heads/1867-feature-plugins to 7789506
Posted by ja...@apache.org.
hook up futon to /_plugins
Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/77895062
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/77895062
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/77895062
Branch: refs/heads/1867-feature-plugins
Commit: 778950622d0682c161a350df34a89d44efc28844
Parents: 449b458
Author: Jan Lehnardt <ja...@apache.org>
Authored: Wed Jul 31 18:49:32 2013 +0200
Committer: Jan Lehnardt <ja...@apache.org>
Committed: Wed Jul 31 18:49:32 2013 +0200
----------------------------------------------------------------------
share/www/_sidebar.html | 1 +
share/www/plugins.html | 82 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/couchdb/blob/77895062/share/www/_sidebar.html
----------------------------------------------------------------------
diff --git a/share/www/_sidebar.html b/share/www/_sidebar.html
index e68bf73..26a1bc8 100644
--- a/share/www/_sidebar.html
+++ b/share/www/_sidebar.html
@@ -23,6 +23,7 @@ specific language governing permissions and limitations under the License.
<li><a href="config.html">Configuration</a></li>
<li><a href="replicator.html">Replicator</a></li>
<li><a href="status.html">Status</a></li>
+ <li><a href="plugins.html">Plugins</a></li>
</ul></li>
<li><span>Documentation</span><ul>
<li><a href="docs/">Manual</a></li>
http://git-wip-us.apache.org/repos/asf/couchdb/blob/77895062/share/www/plugins.html
----------------------------------------------------------------------
diff --git a/share/www/plugins.html b/share/www/plugins.html
new file mode 100644
index 0000000..a99826c
--- /dev/null
+++ b/share/www/plugins.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!--
+
+Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+
+-->
+<html lang="en">
+ <head>
+ <title>Plugins</title>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+ <link rel="stylesheet" href="style/layout.css?0.11.0" type="text/css">
+ <script src="script/json2.js"></script>
+ <script src="script/sha1.js"></script>
+ <script src="script/jquery.js"></script>
+ <script src="script/jquery.couch.js"></script>
+ <script src="script/jquery.dialog.js"></script>
+ <script src="script/futon.js"></script>
+ </head>
+ <body><div id="wrap">
+ <h1>
+ <a href="index.html">Overview</a>
+ <strong>Plugins</strong>
+ </h1>
+ <div id="content">
+ <div class="row">
+ <h2>GeoCouch</h2>
+ <p>Version: <strong>couchdb1.2.x_v0.3.0-11-gd83ba22</strong></p>
+ <p>
+ Available Erlang Versions:
+ <ul>
+ <li>R15B01</li>
+ </ul>
+ </p>
+ <p>
+ <button href="#" id="install_plugin" data-url="http://people.apache.org/~jan" data-checksums='{"R15B03":"mw7RWJtbt7WMOF/ypwpgkRHT0Wo="}' data-name="geocouch" data-version="couchdb1.2.x_v0.3.0-12-g4ea0bea">Install GeoCouch Now</button>
+ </p>
+ </div>
+
+ </div>
+ </div></body>
+ <script>
+ $('#install_plugin').click(function(event) {
+ var button = $(this);
+ var plugin_spec = JSON.stringify({
+ name: button.data('name'),
+ url: button.data('url'),
+ version: button.data('version'),
+ checksums: button.data('checksums')
+ });
+ var url = '/_plugins'
+ $.ajax({
+ url: url,
+ type: 'POST',
+ data: plugin_spec,
+ contentType: 'application/json', // what we send to the server
+ dataType: 'json', // expected from the server
+ processData: false, // keep our precious JSON
+ success: function(data, textStatus, jqXhr) {
+ button.html(textStatus);
+ },
+ beforeSend: function(xhr) {
+ xhr.setRequestHeader('Accept', 'application/json');
+ },
+ });
+ });
+ </script>
+ <style type="text/css">
+ .row {
+ background-color: #EEE;
+ padding:1em;
+ }
+ </style>
+</html>