You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@community.apache.org by hu...@apache.org on 2016/02/07 16:33:01 UTC

svn commit: r1728977 - in /comdev/helpwanted.apache.org/site: ./ admin/ css/ js/ lib/

Author: humbedooh
Date: Sun Feb  7 15:33:01 2016
New Revision: 1728977

URL: http://svn.apache.org/viewvc?rev=1728977&view=rev
Log:
Add initial PoC code for helpwanted.apache.org, a simple task directory for Apache projects.
See https://helpwanted.apache.org/ 

Added:
    comdev/helpwanted.apache.org/site/
    comdev/helpwanted.apache.org/site/admin/
    comdev/helpwanted.apache.org/site/admin/close.lua
    comdev/helpwanted.apache.org/site/admin/elastic.lua
    comdev/helpwanted.apache.org/site/admin/index.html
    comdev/helpwanted.apache.org/site/admin/newtask.lua
    comdev/helpwanted.apache.org/site/css/
    comdev/helpwanted.apache.org/site/css/hw.css
    comdev/helpwanted.apache.org/site/css/hw2.css
    comdev/helpwanted.apache.org/site/favicon.ico   (with props)
    comdev/helpwanted.apache.org/site/index.html
    comdev/helpwanted.apache.org/site/js/
    comdev/helpwanted.apache.org/site/js/hw.js
    comdev/helpwanted.apache.org/site/lib/
    comdev/helpwanted.apache.org/site/lib/elastic.lua
    comdev/helpwanted.apache.org/site/listitems.lua
    comdev/helpwanted.apache.org/site/task.html
    comdev/helpwanted.apache.org/site/widget.js
    comdev/helpwanted.apache.org/site/wtest.html

Added: comdev/helpwanted.apache.org/site/admin/close.lua
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/admin/close.lua?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/admin/close.lua (added)
+++ comdev/helpwanted.apache.org/site/admin/close.lua Sun Feb  7 15:33:01 2016
@@ -0,0 +1,42 @@
+--[[
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+]]--
+
+local JSON = require 'cjson'
+local elastic = require 'elastic'
+
+function handle(r)
+    r.content_type = "text/html"
+    local t = {}
+    local now = r:clock()
+    local tnow = now
+    local get = r:parseargs()
+    local id = get.id
+    local doc = elastic.get('item', id)
+    if doc then
+        if get.reopen then
+            elastic.update('item', id, { closed = false })
+            r:puts("Task reopened!")
+        else
+            elastic.update('item', id, { closed = true })
+            r:puts("Task closed!")
+        end
+    else
+        r:puts("No such task :(")
+    end
+    return apache2.OK
+end
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/admin/elastic.lua
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/admin/elastic.lua?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/admin/elastic.lua (added)
+++ comdev/helpwanted.apache.org/site/admin/elastic.lua Sun Feb  7 15:33:01 2016
@@ -0,0 +1,148 @@
+--[[
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+]]--
+
+-- This is elastic.lua - ElasticSearch library
+
+local http = require 'socket.http'
+local JSON = require 'cjson'
+local config = {
+    es_url = "http://localhost:9200/helpwanted/",
+    maxResults = 100
+}
+local default_doc = "item"
+
+-- Standard ES query, returns $size results of any doc of type $doc, sorting by $sitem
+function getHits(query, size, doc, sitem)
+    doc = doc or "mbox"
+    sitem = sitem or "epoch"
+    size = size or 10
+    query = query:gsub(" ", "+")
+    local url = config.es_url .. doc .. "/_search?q="..query.."&sort=" .. sitem .. ":desc&size=" .. size
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    local out = {}
+    if json and json.hits and json.hits.hits then
+        for k, v in pairs(json.hits.hits) do
+            v._source.request_id = v._id
+            table.insert(out, v._source)
+        end
+    end
+    return out
+end
+
+-- Get a single document
+function getDoc (ty, id)
+    local url = config.es_url  .. ty .. "/" .. id
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    if json and json._source then
+        json._source.request_id = json._id
+    end
+    return (json and json._source) and json._source or {}
+end
+
+-- Get results (a'la getHits), but only return email headers, not the body
+-- provides faster transport when we don't need everything
+function getHeaders(query, size, doc)
+    doc = doc or "mbox"
+    size = size or 10
+    query = query:gsub(" ", "+")
+    local url = config.es_url  .. doc .. "/_search?_source_exclude=body&q="..query.."&sort=date:desc&size=" .. size
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    local out = {}
+    if json and json.hits and json.hits.hits then
+        for k, v in pairs(json.hits.hits) do
+            v._source.request_id = v._id
+            table.insert(out, v._source)
+        end
+    end
+    return out
+end
+
+-- Same as above, but reverse return order
+function getHeadersReverse(query, size, doc)
+    doc = doc or "mbox"
+    size = size or 10
+    query = query:gsub(" ", "+")
+    local url = config.es_url .. doc .. "/_search?_source_exclude=body&q="..query.."&sort=epoch:desc&size=" .. size
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    local out = {}
+    if json and json.hits and json.hits.hits then
+        for k, v in pairs(json.hits.hits) do
+            v._source.request_id = v._id
+            table.insert(out, 1, v._source)
+        end
+    end
+    return out
+end
+
+-- Do a raw ES query with a JSON query
+function raw(query, doctype)
+    local js = JSON.encode(query)
+    doctype = doctype or default_doc
+    local url = config.es_url .. doctype .. "/_search"
+    local result = http.request(url, js)
+    local out = {}
+    local json = JSON.decode(result)
+    return json or {}, url
+end
+
+-- Update a document
+function update(doctype, id, query)
+    local js = JSON.encode({doc = query })
+    doctype = doctype or default_doc
+    local url = config.es_url .. doctype .. "/" .. id .. "/_update"
+    local result = http.request(url, js)
+    local out = {}
+    local json = JSON.decode(result)
+    return json or {}, url
+end
+
+-- Put a new document somewhere
+function index(r, id, ty, body)
+    local js = JSON.encode(query)
+    if not id then
+        id = r:sha1(ty .. (math.random(1,99999999)*os.time()) .. ':' .. r:clock())
+    end
+    local url = config.es_url .. ty .. "/" .. id
+    local result = http.request(url, body)
+    local out = {}
+    local json = JSON.decode(result)
+    return json or {}
+end
+
+function setDefault(typ)
+    default_doc = typ
+end
+
+-- module defs
+return {
+    find = getHits,
+    findFast = getHeaders,
+    findFastReverse = getHeadersReverse,
+    get = getDoc,
+    raw = raw,
+    index = index,
+    default = setDefault,
+    update = update
+}

Added: comdev/helpwanted.apache.org/site/admin/index.html
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/admin/index.html?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/admin/index.html (added)
+++ comdev/helpwanted.apache.org/site/admin/index.html Sun Feb  7 15:33:01 2016
@@ -0,0 +1,62 @@
+ <!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="stylesheet" href="/css/hw.css">
+<link rel="stylesheet" href="/css/hw2.css">
+<script src="/js/hw.js" type="text/javascript"></script>
+<title>Help Wanted!</title>
+
+</head>
+<body onload="populateAdminForm(); fetchItemsAdmin();">
+    
+<div style="text-align: center;">
+    <h2><img src='/images/logo.png' title="Help Wanted!"/></h2>
+</div>
+
+<div id="innerpicker">
+    <form action="newtask.lua" method="post">
+        <h3>Create a new task:</h3>
+        <b>Project:</b> <select name="project" id="project"></select><br/>
+        <b>Task type:</b>
+            <select name="type">
+                <option value="programming">Programming task</option>
+                <option value="web design">Web design task</option>
+                <option value="documentation">Documentation task</option>
+                <option value="marketing">Marketing task</option>
+                <option value="community">Community task</option>
+            </select><br/>
+        <b>Title: </b> <input type="text" name="title"/><br/>
+        <b>Languages (optional): </b>
+            <select name="languages" id="languages" multiple="multiple" size="5">
+                
+            </select>
+            <br/>
+        <b>Difficulty:</b>
+            <select name="difficulty">
+                <option value="0">Beginner</option>
+                <option value="1">Journeyman</option>
+                <option value="2">Intermediate</option>
+                <option value="3">Advanced</option>
+                <option value="4">Expert</option>
+            </select>
+            <br/>
+        <b>Description:</b>
+            <textarea name="description" placeholder="Description goes here..." style="width: 500px; height: 200px;"></textarea><br/>
+        <b>Additional info URL: </b> <input type="text" name="url"/><br/>
+        <b>Collaborative?: </b> <input type="checkbox" name="collaboration" value="yes"/><br/>
+        <input type="submit" value="Save task"/>
+    </form>
+</div>
+
+<div id="hwitems" class="hwitems">
+    
+</div>
+
+<p style="font-size: 12px; font-style: italic; text-align: center;">
+    Managed by the <a href="https://community.apache.org/">Apache Community Development Project</a>.
+    Copyright 2016, the Apache Software Foundation.
+    Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
+</p>
+</body>
+</html>
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/admin/newtask.lua
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/admin/newtask.lua?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/admin/newtask.lua (added)
+++ comdev/helpwanted.apache.org/site/admin/newtask.lua Sun Feb  7 15:33:01 2016
@@ -0,0 +1,61 @@
+--[[
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+]]--
+
+local JSON = require 'cjson'
+local elastic = require 'elastic'
+
+function handle(r)
+    r.content_type = "text/html"
+    local t = {}
+    local now = r:clock()
+    local tnow = now
+    local get = r:parseargs()
+    local post, postm = r:parsebody()
+    
+    local project = r:escape_html(post.project or "")
+    local typ = r:escape_html(post.type)
+    local title = r:escape_html(post.title)
+    local lingos = r:escape_html(table.concat(postm.languages or {'n/a'}, ","))
+    local difficulty = tonumber(post.difficulty) or 0
+    local desc = r:escape_html(post.description)
+    local url = r:escape_html(post.url)
+    local collab = (post.collaboration and post.collaboration == 'yes') and true or false
+    
+    
+    if project and #project > 2 and typ and title and lingos and difficulty and desc then
+        elastic.index(r, nil, 'item', JSON.encode{
+            project = project,
+            ['type'] = typ,
+            languages = lingos,
+            title = title,
+            difficulty = difficulty,
+            description = desc,
+            url = url,
+            collaboration = collab,
+            created = os.time(),
+            author = r.user or "unknown",
+            estimate = 0,
+            timeout = 0,
+        }
+            )
+        r:puts("Task saved!")
+    else
+        r:puts("Something was missing, go back!")
+    end
+    return apache2.OK
+end

Added: comdev/helpwanted.apache.org/site/css/hw.css
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/css/hw.css?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/css/hw.css (added)
+++ comdev/helpwanted.apache.org/site/css/hw.css Sun Feb  7 15:33:01 2016
@@ -0,0 +1,42 @@
+.itemNumber-yellow {
+  background: linear-gradient(to bottom, #e8cc43 0%,#cc9004 100%);
+  border-radius: 12px;
+  border: 3px solid #FFF;
+  color: #FFF;
+  box-shadow: 0px 2px 2px 0px rgba(153,153,153,1);
+  width: 20px;
+  float: left;
+  text-align: center;
+  font-family: sans-serif;
+  margin-right: 8px;
+  height: 16px;
+  font-size: 12px;
+  margin-bottom: 4px;
+  padding-top: 3px;
+  position: relative;
+  top: 0px;
+  left: 0px;
+}
+
+#picker {
+  border: 1px solid #666;
+  border-radius: 8px;
+  background: #EEE;
+}
+
+.hwitems {
+  
+  margin: 0 auto;
+  width: 1000px;
+  padding: 5px;
+  text-align: center;
+}
+
+.details {
+  border: 1px solid #666;
+  border-radius: 5px;
+  background: #FFE;
+  margin: 10px;
+  padding: 5px;
+  font-size: 10pt;
+}
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/css/hw2.css
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/css/hw2.css?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/css/hw2.css (added)
+++ comdev/helpwanted.apache.org/site/css/hw2.css Sun Feb  7 15:33:01 2016
@@ -0,0 +1,295 @@
+
+
+.stepbutton {
+	text-align: center;
+	background: linear-gradient(to bottom, rgba(183,222,237,1) 0%,rgba(113,206,239,1) 50%,rgba(33,180,226,1) 51%,rgba(183,222,237,1) 100%);
+	border-radius: 15px;
+	padding: 10px 10px;
+	display: block;
+	float: left;
+	margin: 0 auto;
+	margin-right: 30px;
+	font-family: arial;
+	font-weight: bold;
+	color:#333;
+	text-decoration: none;
+	cursor: pointer;
+	border:1px solid #a7a7a7;
+	box-shadow: 0px 2px 1px white inset, 0px -2px 8px white, 0px 2px 5px rgba(0, 0, 0, 0.1), 0px 8px 10px rgba(0, 0, 0, 0.1);
+}
+
+.finishbutton {
+	text-align: center;
+	background: linear-gradient(to bottom, rgba(157,213,58,1) 0%,rgba(161,213,79,1) 50%,rgba(128,194,23,1) 51%,rgba(124,188,10,1) 100%);
+	border-radius: 15px;
+	padding: 10px 10px;
+	display: block;
+	margin: 0 auto;
+	font-family: arial;
+	font-weight: bold;
+	color:#333;
+	text-decoration: none;
+	cursor: pointer;
+	border:1px solid #a7a7a7;
+	box-shadow: 0px 2px 1px white inset, 0px -2px 8px white, 0px 2px 5px rgba(0, 0, 0, 0.1), 0px 8px 10px rgba(0, 0, 0, 0.1);
+}
+
+.dibbutton {
+    height: 24px;
+	text-align: center;
+	background: linear-gradient(to bottom, rgba(109,179,242,1) 0%,rgba(84,163,238,1) 50%,rgba(54,144,240,1) 51%,rgba(30,105,222,1) 100%);
+	border-radius: 8px;
+	padding: 2px 5px;
+	display: block;
+	margin: 10px auto;
+    font-size: 9pt;
+	font-family: arial;
+	font-weight: bold;
+	color:#EEE;
+	text-decoration: none;
+	cursor: pointer;
+	border:1px solid #a7a7a7;
+	box-shadow: 0px 2px 1px white inset, 0px -2px 8px white, 0px 2px 5px rgba(0, 0, 0, 0.1), 0px 8px 10px rgba(0, 0, 0, 0.1);
+}
+
+
+
+.infobox {
+	
+	background-repeat: no-repeat;
+	background: linear-gradient(to bottom, rgba(135,224,253,1) 0%,rgba(83,203,241,1) 40%,rgba(5,171,224,1) 100%);
+	width: 600px;
+	min-height: 70px;
+	padding: 5px;
+	border: 1px solid #083051;
+}
+
+ul {
+	margin: 0px;
+	font-size: 0;
+}
+
+li {
+	font-size: 12px;
+	margin: 0;
+    padding: 0.2em;
+}
+
+
+
+
+.hwitems table a:link {
+	color: #666;
+	font-weight: bold;
+	text-decoration:none;
+}
+.hwitems table a:visited {
+	color: #999999;
+	font-weight:bold;
+	text-decoration:none;
+}
+.hwitems table a:active,
+.hwitems table a:hover {
+	color: #bd5a35;
+	text-decoration:underline;
+}
+.hwitems table, .cs {
+	font-family:Arial, Helvetica, sans-serif;
+	color:#666;
+	font-size:12px;
+	text-shadow: 1px 1px 0px #fff;
+	background:#eaebec;
+	margin:20px;
+	border:#ccc 1px solid;
+
+	-moz-border-radius:3px;
+	-webkit-border-radius:3px;
+	border-radius:3px;
+
+	-moz-box-shadow: 0 1px 2px #d1d1d1;
+	-webkit-box-shadow: 0 1px 2px #d1d1d1;
+	box-shadow: 0 1px 2px #d1d1d1;
+}
+
+#innerpicker {
+    text-align: left;
+	font-family:Arial, Helvetica, sans-serif;
+	color:#666;
+	font-size:12px;
+	text-shadow: 1px 1px 0px #fff;
+	background:#fafbfc;
+	border:#ccc 1px solid;
+	width: 600px;
+	min-height: 300px;
+    padding: 5px;
+	margin: 0 auto;
+	-moz-border-radius:3px;
+	-webkit-border-radius:3px;
+	border-radius:3px;
+
+	-moz-box-shadow: 0 1px 2px #d1d1d1;
+	-webkit-box-shadow: 0 1px 2px #d1d1d1;
+	box-shadow: 0 1px 2px #d1d1d1;
+}
+.hwitems table th {
+	padding:21px 25px 22px 25px;
+	border-top:1px solid #fafafa;
+	border-bottom:1px solid #e0e0e0;
+
+	background: #ededed;
+	background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#ebebeb));
+	background: -moz-linear-gradient(top,  #ededed,  #ebebeb);
+}
+.hwitems table th:first-child {
+	text-align: left;
+	padding-left:20px;
+}
+.hwitems table tr:first-child th:first-child {
+	-moz-border-radius-topleft:3px;
+	-webkit-border-top-left-radius:3px;
+	border-top-left-radius:3px;
+}
+.hwitems table tr:first-child th:last-child {
+	-moz-border-radius-topright:3px;
+	-webkit-border-top-right-radius:3px;
+	border-top-right-radius:3px;
+}
+.hwitems table tr {
+	text-align: center;
+	padding-left:20px;
+}
+.hwitems table td:first-child {
+	text-align: left;
+	padding-left:20px;
+	border-left: 0;
+}
+
+.hwitems table td:last-child {
+	text-align: left;
+}
+
+.hwitems table td {
+	padding:12px;
+	border-top: 1px solid #ffffff;
+	border-bottom:1px solid #e0e0e0;
+	border-left: 1px solid #e0e0e0;
+
+	background: #fafafa;
+	background: -webkit-gradient(linear, left top, left bottom, from(#fbfbfb), to(#fafafa));
+	background: -moz-linear-gradient(top,  #fbfbfb,  #fafafa);
+	
+}
+.hwitems table tr.even td {
+	background: #f6f6f6;
+	background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#f6f6f6));
+	background: -moz-linear-gradient(top,  #f8f8f8,  #f6f6f6);
+}
+.hwitems table tr:last-child td {
+	border-bottom:0;
+}
+.hwitems table tr:last-child td:first-child {
+	-moz-border-radius-bottomleft:3px;
+	-webkit-border-bottom-left-radius:3px;
+	border-bottom-left-radius:3px;
+}
+.hwitems table tr:last-child td:last-child {
+	-moz-border-radius-bottomright:3px;
+	-webkit-border-bottom-right-radius:3px;
+	border-bottom-right-radius:3px;
+}
+.hwitems table tr:hover td {
+	background: #f2f2f2;
+	background: -webkit-gradient(linear, left top, left bottom, from(#f2f2f2), to(#f0f0f0));
+	background: -moz-linear-gradient(top,  #f2f2f2,  #f0f0f0);	
+}
+
+
+ .hwwidget table th {
+	padding:4px 5px 4px 5px;
+	border-top:1px solid #fafafa;
+	border-bottom:1px solid #e0e0e0;
+
+	background: #ededed;
+	background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#ebebeb));
+	background: -moz-linear-gradient(top,  #ededed,  #ebebeb);
+}
+ .hwwidget table th:first-child {
+	text-align: left;
+	padding-left:3px;
+}
+ .hwwidget table tr:first-child th:first-child {
+	-moz-border-radius-topleft:3px;
+	-webkit-border-top-left-radius:3px;
+	border-top-left-radius:3px;
+}
+ .hwwidget table tr:first-child th:last-child {
+	-moz-border-radius-topright:3px;
+	-webkit-border-top-right-radius:3px;
+	border-top-right-radius:3px;
+}
+ .hwwidget table tr {
+	text-align: center;
+	padding-left:3px;
+}
+ .hwwidget table td:first-child {
+	text-align: left;
+	padding-left:3px;
+	border-left: 0;
+}
+
+ .hwwidget table td:last-child {
+	text-align: left;
+}
+
+ .hwwidget table td {
+	padding:2px;
+	border-top: 1px solid #ffffff;
+	border-bottom:1px solid #e0e0e0;
+	border-left: 1px solid #e0e0e0;
+
+	background: #fafafa;
+	background: -webkit-gradient(linear, left top, left bottom, from(#fbfbfb), to(#fafafa));
+	background: -moz-linear-gradient(top,  #fbfbfb,  #fafafa);
+	
+}
+
+
+
+#step_menu { margin:0 auto; padding:5px; display:inline-table; list-style:none;text-shadow: none; }
+#step_menu li{ display:inline; float:left; background:url(/images/steps.png) no-repeat -50px bottom; height:41px; line-height:41px; font-size:11px; padding:0 30px 0 25px; position:relative;}
+#step_menu li span{ margin:0px; z-index:2000; background:url(/images/steps.png) no-repeat left bottom; display:block; position:absolute; height:41px; line-height:41px; width:13px; top:0; left:-10px; padding:0;}
+#step_menu li.inprogress { background-position:-50px 0px}
+#step_menu li.inprogress span, #step_menu li.lastinprogress span{background-position:left 0px }
+#step_menu li.done {background-position: -50px -41px}
+#step_menu li.done span, #step_menu li.ldone span {background-position:left -41px }
+#step_menu li.first, #step_menu li.done, #step_menu li.inprogress, #step_menu li.ldone, #step_menu li.lastinprogress { font-weight: bold; color:#333;}
+#step_menu li.first {background-position: -20px 0px }
+#step_menu li.fdone { background-position: -20px -41px }
+#step_menu li.last {background-position: right bottom }
+#step_menu li.lastinprogress { background-position: right 0}
+#step_menu li.ldone { background-position: right -41px}
+
+.itemNumber-widget {
+  background: linear-gradient(to bottom, #cce843 0%,#90cc04 100%);
+  border-radius: 12px;
+  border: 2px solid #FFF;
+  color: #FFF;
+  box-shadow: 0px 2px 2px 0px rgba(153,153,153,1);
+  width: 14px;
+  float: left;
+  text-align: center;
+  font-family: sans-serif;
+  margin-right: 7px;
+  height: 12px;
+  font-size: 9px;
+  margin-bottom: 4px;
+  padding-top: 0px;
+  position: relative;
+  top: 0px;
+  left: 0px;
+}
+
+.hwwidget img {
+    vertical-align: middle;
+    margin-right: 5px;
+}
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/favicon.ico
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/favicon.ico?rev=1728977&view=auto
==============================================================================
Binary file - no diff available.

Propchange: comdev/helpwanted.apache.org/site/favicon.ico
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: comdev/helpwanted.apache.org/site/index.html
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/index.html?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/index.html (added)
+++ comdev/helpwanted.apache.org/site/index.html Sun Feb  7 15:33:01 2016
@@ -0,0 +1,39 @@
+ <!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="stylesheet" href="css/hw.css">
+<link rel="stylesheet" href="css/hw2.css">
+<link rel="shortcut icon" href="/favicon.ico">
+<script src="js/hw.js" type="text/javascript"></script>
+<title>Help Wanted!</title>
+
+</head>
+<body onload="wizard(1);">
+    
+<div style="text-align: center;">
+    <p style="text-align: center;">
+        [<a href="/admin/">Edit tasks</a>] - [<a href="/wtest.html">Widget primer</a>]
+    </p>
+    <h2><img src='/images/logo.png' title="Help Wanted!"/></h2>
+    <p>This is in alpha test mode and what not, so...yeah.</p>
+    <p>Projects can now add a <a href='wtest.html'>widget</a> to their web sites with tasks from HW.</p>
+</div>
+
+<div id="pickerparent" style="text-align: center;">
+    <h4>Quickly find something to do:</h4>
+    <div id="innerpicker">
+        
+    </div>
+</div>
+<div id="hwitems" class="hwitems">
+    
+</div>
+
+<p style="font-size: 12px; font-style: italic; text-align: center;">
+    Managed by the <a href="https://community.apache.org/">Apache Community Development Project</a>.
+    Copyright 2016, the Apache Software Foundation.
+    Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
+</p>
+</body>
+</html>
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/js/hw.js
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/js/hw.js?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/js/hw.js (added)
+++ comdev/helpwanted.apache.org/site/js/hw.js Sun Feb  7 15:33:01 2016
@@ -0,0 +1,334 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+
+var diff = ['Beginner', 'Journeyman', 'Intermediate', 'Advanced', 'Expert']
+var langs = ['c','xml','c++','c-sharp','java','javascript','css','html','perl','ruby','lua','python','go','rust', 'erlang', 'swift', 'groovy', 'haskell']
+var types = ['programming', 'web design', 'marketing', 'documentation', 'community', 'translation']
+var types_long = {
+    programming: "Programming and Development",
+    'web design': "Web Design",
+    marketing: 'Marketing and Publicity',
+    documentation: 'Documentation and Guides',
+    community: 'Community Outreach',
+    translation: 'Translation'
+}
+
+var spoken_langs = ['english', 'french', 'german', 'spanish', 'russian', 'italian', 'japanese', 'chinese']
+var website_langs = ['css','javascript','html']
+var projects = ['all projects']
+var cjson = {}
+
+langs.sort()
+types.sort()
+
+function getAsyncJSON(theUrl, xstate, callback) {
+	var xmlHttp = null;
+	if (window.XMLHttpRequest) {
+		xmlHttp = new XMLHttpRequest();
+	} else {
+		xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+	}
+	xmlHttp.open("GET", theUrl, true);
+	xmlHttp.send(null);
+	xmlHttp.onreadystatechange = function(state) {
+
+		if (xmlHttp.readyState == 4 && xmlHttp.status == 200 || xmlHttp.status == 404) {
+			if (callback) {
+				if (xmlHttp.status == 404) {
+					callback({}, xstate);
+				} else {
+					window.setTimeout(callback, 0.05, JSON.parse(xmlHttp.responseText), xstate);
+				}
+			}
+		}
+	}
+}
+
+var wstate = {}
+
+function wizard(step, arg) {
+    var obj = document.getElementById('innerpicker')
+    if (!step) {
+        step = 1
+    }
+    if (step == 1) {
+        wstate = {}
+        obj.innerHTML = "<h2 style='text-align: center;'>What sort of work would you like to do?</h2>"
+        for (var i in types) {
+            obj.innerHTML += "<img style='vertical-align: middle;' src='images/icon_" + types[i].replace(/\s+/g, "") + ".png'/><big> <a href='javascript:void(0);' onclick='wizard(2, \"" + types[i] + "\");'>" + types_long[types[i]] + "</a></big><br/><br/>"
+        }
+        obj.innerHTML += "<br/><a href='javascript:void(0);' onclick='fetchItems();'><big>...Just show me everything</big></a>"
+    }
+    if (step == 2 && arg) {
+        wstate = {
+            type: arg
+        }
+        
+        obj.innerHTML = "<h2 style='text-align: center;'>Which languages are you proficient in?</h2>"
+        obj.innerHTML += "<small>You don't need to pick a language, but it will help narrow down the tasks available for you.</small><br/>"
+        
+        if (arg == 'programming' || arg == 'documentation') {
+            for (var i in langs) {
+                var div = document.createElement('div')
+                div.style.float = "left"
+                div.style.width = "100px"
+                var cb = document.createElement('input')
+                cb.setAttribute("type", "checkbox")
+                cb.setAttribute("id", "plang_" + langs[i])
+                var lbl = document.createElement('label')
+                lbl.setAttribute("for", "plang_" + langs[i])
+                var txt = document.createTextNode(langs[i])
+                lbl.appendChild(txt)
+                div.appendChild(cb)
+                div.appendChild(lbl)
+                obj.appendChild(div)
+            }
+            obj.appendChild(document.createElement('br'))
+        } else if (arg == 'web design') {
+            for (var i in website_langs) {
+                var div = document.createElement('div')
+                div.style.float = "left"
+                div.style.width = "100px"
+                var cb = document.createElement('input')
+                cb.setAttribute("type", "checkbox")
+                cb.setAttribute("id", "plang_" + website_langs[i])
+                var lbl = document.createElement('label')
+                lbl.setAttribute("for", "plang_" + website_langs[i])
+                var txt = document.createTextNode(website_langs[i])
+                lbl.appendChild(txt)
+                div.appendChild(cb)
+                div.appendChild(lbl)
+                obj.appendChild(div)
+            }
+            obj.appendChild(document.createElement('br'))
+        } else {
+            for (var i in spoken_langs) {
+                var div = document.createElement('div')
+                div.style.float = "left"
+                div.style.width = "100px"
+                var cb = document.createElement('input')
+                cb.setAttribute("type", "checkbox")
+                cb.setAttribute("id", "plang_" + spoken_langs[i])
+                var lbl = document.createElement('label')
+                lbl.setAttribute("for", "plang_" + spoken_langs[i])
+                var txt = document.createTextNode(spoken_langs[i].replace(/^([a-z])/, function(a) { return a.toUpperCase() }))
+                lbl.appendChild(txt)
+                div.appendChild(cb)
+                div.appendChild(lbl)
+                obj.appendChild(div)
+            }
+            obj.appendChild(document.createElement('br'))
+        }
+        obj.innerHTML += '<br/><div style="width: 100%; margin-top: 40px;"><input type="button" class="finishbutton" onclick="doForm()" value="Find me something to do!"/>' +
+        '<a onclick="wizard(1)" href="javascript:void(0);">Back to start</a></div>'
+    }
+}
+
+function populateForm() {
+    // languages
+    
+    var f = document.getElementById('plang')
+    for (var i in langs) {
+        var div = document.createElement('div')
+        div.style.float = "left"
+        div.style.width = "100px"
+        var cb = document.createElement('input')
+        cb.setAttribute("type", "checkbox")
+        cb.setAttribute("id", "plang_" + langs[i])
+        var lbl = document.createElement('label')
+        lbl.setAttribute("for", "plang_" + langs[i])
+        var txt = document.createTextNode(langs[i])
+        lbl.appendChild(txt)
+        div.appendChild(cb)
+        div.appendChild(lbl)
+        f.appendChild(div)
+    }
+    f.appendChild(document.createElement('br'))
+    
+    // task types
+
+    var f = document.getElementById('ptypes')
+    for (var i in types) {
+        var div = document.createElement('div')
+        div.style.float = "left"
+        div.style.width = "200px"
+        var cb = document.createElement('input')
+        cb.setAttribute("type", "checkbox")
+        cb.setAttribute("id", "ptype_" + types[i])
+        var lbl = document.createElement('label')
+        lbl.setAttribute("for", "ptype_" + types[i])
+        var txt = document.createTextNode(types[i])
+        lbl.appendChild(txt)
+        div.appendChild(cb)
+        div.appendChild(lbl)
+        f.appendChild(div)
+    }
+    f.appendChild(document.createElement('br'))
+    
+    // projects - none for now
+    var f = document.getElementById('pprojects')
+    f.appendChild(document.createTextNode('All projects'))
+}
+
+function doForm() {
+    var l = []
+    var t = []
+    var p = []
+    for (var i in langs) {
+        if (document.getElementById("plang_" + langs[i]) && document.getElementById("plang_" + langs[i]).checked) {
+            l.push(langs[i])
+        }
+    }
+    for (var i in types) {
+        if (document.getElementById("ptype_" + types[i]) && document.getElementById("ptype_" + types[i]).checked) {
+            t.push(types[i])
+        }
+    }
+    if (wstate.type) {
+        t = [wstate.type]
+    }
+    if (wstate.projects) {
+        p = wstate.projects
+    }
+    fetchItems(l, t, p)
+}
+
+function sw(id) {
+    var obj = document.getElementById(id)
+    var op = obj.style.display == 'none' ? 'table-row' : 'none'
+    obj.style.display = op
+}
+
+function reallyPopulate(json) {
+    var pro = []
+    var obj = document.getElementById('project')
+    for (var i in json.committees) {
+        pro.push(i)
+        var opt = document.createElement('option')
+        opt.text = i
+        opt.setAttribute("value", i)
+        obj.appendChild(opt)
+    }
+    
+    var obj = document.getElementById('languages')
+    for (var i in langs) {
+        var opt = document.createElement('option')
+        opt.text = langs[i]
+        opt.setAttribute("value", langs[i])
+        obj.appendChild(opt)
+    }
+    
+    for (var i in spoken_langs) {
+        var opt = document.createElement('option')
+        opt.text = spoken_langs[i].replace(/^([a-z])/, function(a) {return a.toUpperCase()})
+        opt.setAttribute("value", spoken_langs[i])
+        obj.appendChild(opt)
+    }
+}
+
+function populateAdminForm() {
+    getAsyncJSON('https://whimsy.apache.org/public/public_ldap_committees.json', null, reallyPopulate)
+}
+
+function displayItems(json, state) {
+    cjson = json
+    var numItems = 0
+    for (var i in json) {
+        var item = json[i]
+        if (item.closed) {
+            continue
+        }
+        numItems++
+    }
+    var obj = document.getElementById('hwitems')
+    
+    if (typeof(numItems) === 'undefined') {
+        obj.innerHTML = "Sorry, we couldn't find any tasks matching your criteria"
+        return
+    }
+    
+    obj.innerHTML = "Found " + numItems + " item" + (numItems != 1 ? "s" : "") + ":<br/>"
+    var tbl = "<table style='text-align: left;'><tr><th></th><th>Project</th><th>Title</th><th>Languages</th><th>Difficulty</th><th>Created</th></tr>"
+    for (var i in json) {
+        var item = json[i]
+        if (item.closed) {
+            continue
+        }
+        var z = parseInt(i)+1
+        var ptype = item.type.replace(/\s+/g, "")
+        var cdate = new Date(item.created*1000).toLocaleString()
+        var lingos = (item.languages != 'n/a') ? "(" + item.languages + ") " : ""
+        
+        // admin stuff
+        var add = ""
+        if (state.admin) {
+            add = " &nbsp; <a href='/admin/close.lua?id=" + item.request_id + "'>Mark as done</a>"
+        }
+        
+        tbl += "<tr style='cursor: pointer;' onclick=\"sw('details_" + i + "');\"><td><div class='itemNumber-yellow'>" + z + "</div><img title='" + item.type + "' style='float: left;' src='/images/icon_" + ptype + ".png'/></td>" +
+        "<td>" + item.project + "</td>"+
+        "<td style='text-align: left;'>" + item.title + "</td>" +
+        "<td>" + lingos + "</td><td style='text-align: left;'><img src='/images/level_" + (parseInt(item.difficulty)+1) + ".png'/> " + diff[item.difficulty] + add + "</td><td>" + cdate + "</td></tr>"
+        
+        tbl += "<tr style='display:none;' id='details_" + i + "'><td colspan='6'><b>Project:</b> " + item.project + "<br/><b>Requested by:</b> " + item.author + "@apache.org<br/><b>Created:</b> " + cdate + "<br/><b>Description:</b> <blockquote>" + item.description + "</blockquote><b>Further information: </b> <a href='" + item.url + "'>" + item.url + "</a><br/><input type='button' onclick='location.href=\"https://helpwanted.apache.org/task.html?" + item.request_id +"\";' value='I am interested in this'/></td></tr>"
+        
+    }
+    tbl += "</table>"
+    obj.innerHTML += tbl
+}
+
+function fetchItems(languages, types, projects, sortBy) {
+    if (!languages) languages = []
+    if (!types) types = []
+    if (!projects) projects = []
+    getAsyncJSON("/listitems.lua?lang=" + languages.join(",") + "&type=" + types.join(",") +"&project=" + projects.join(","),
+                 {
+                    languages: languages,
+                    types: types,
+                    projects: projects,
+                    sortBy: sortBy
+                    }, displayItems)
+}
+
+
+function fetchItemsAdmin() {
+    getAsyncJSON("/listitems.lua", {admin: true}, displayItems)
+}
+
+function renderItem(json, state) {
+    var obj = document.getElementById('item')
+    var cdate = new Date(json.created*1000).toDateString()
+    obj.innerHTML = "<h2>Item #" + state + ": <span style='color: #369;'>" + json.title + "</span></h2>"
+    obj.innerHTML += "<p style='text-align: left;'><b>Project: </b> " + json.project + "<br/>" +
+        "<b>Created by:</b> " + json.author + "@apache.org<br/>" +
+        "<b>Task added: </b>" + cdate + "<br/>" +
+        "<b>Difficulty: </b> <img style='width: 16px; height: 16px; vertical-align: middle;' src='/images/level_" + (parseInt(json.difficulty)+1) + ".png'/> " + diff[json.difficulty] + "<br/>" +
+        "<b>Task type:</b> " + types_long[json.type] + "<br/>" +
+        "<b>Additional information:</b> <a href='" + json.url + "'>" + json.url + "</a><br/>" +
+        ((json.estimate && json.estimate.length > 0) ? "<b>Estimated time to complete:</b> " + json.estimate : "") +
+        ((json.timeout && json.timeout > 0) ? "<b>Task expires:</b> " + new Date(json.timeout*1000).toDateString() : "") +
+        "<blockquote style='text-align: left;'><q>" + json.description + "</q></blockquote>" +
+        "<br/></p>" +
+        "<h3 style='text-align: left;'>How to help:</h3><p style='text-align: left;'>" +
+        "If you want to help with this task, please get in touch with the project at: dev@" + json.project + ".apache.org!" +
+        "<br/>You should also check out the additional information URL (if such is provided above) for more information."
+        "<br/>&nbsp;<br/>&nbsp;<br/></p>"
+}
+
+function displayItem(id) {
+    getAsyncJSON("/listitems.lua?id=" + id, id, renderItem)
+}
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/lib/elastic.lua
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/lib/elastic.lua?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/lib/elastic.lua (added)
+++ comdev/helpwanted.apache.org/site/lib/elastic.lua Sun Feb  7 15:33:01 2016
@@ -0,0 +1,148 @@
+--[[
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+]]--
+
+-- This is elastic.lua - ElasticSearch library
+
+local http = require 'socket.http'
+local JSON = require 'cjson'
+local config = {
+    es_url = "http://localhost:9200/helpwanted/",
+    maxResults = 100
+}
+local default_doc = "item"
+
+-- Standard ES query, returns $size results of any doc of type $doc, sorting by $sitem
+function getHits(query, size, doc, sitem)
+    doc = doc or "mbox"
+    sitem = sitem or "epoch"
+    size = size or 10
+    query = query:gsub(" ", "+")
+    local url = config.es_url .. doc .. "/_search?q="..query.."&sort=" .. sitem .. ":desc&size=" .. size
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    local out = {}
+    if json and json.hits and json.hits.hits then
+        for k, v in pairs(json.hits.hits) do
+            v._source.request_id = v._id
+            table.insert(out, v._source)
+        end
+    end
+    return out
+end
+
+-- Get a single document
+function getDoc (ty, id)
+    local url = config.es_url  .. ty .. "/" .. id
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    if json and json._source then
+        json._source.request_id = json._id
+    end
+    return (json and json._source) and json._source or {}
+end
+
+-- Get results (a'la getHits), but only return email headers, not the body
+-- provides faster transport when we don't need everything
+function getHeaders(query, size, doc)
+    doc = doc or "mbox"
+    size = size or 10
+    query = query:gsub(" ", "+")
+    local url = config.es_url  .. doc .. "/_search?_source_exclude=body&q="..query.."&sort=date:desc&size=" .. size
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    local out = {}
+    if json and json.hits and json.hits.hits then
+        for k, v in pairs(json.hits.hits) do
+            v._source.request_id = v._id
+            table.insert(out, v._source)
+        end
+    end
+    return out
+end
+
+-- Same as above, but reverse return order
+function getHeadersReverse(query, size, doc)
+    doc = doc or "mbox"
+    size = size or 10
+    query = query:gsub(" ", "+")
+    local url = config.es_url .. doc .. "/_search?_source_exclude=body&q="..query.."&sort=epoch:desc&size=" .. size
+    local result = http.request(url)
+    local out = {}
+    local json = JSON.decode(result)
+    local out = {}
+    if json and json.hits and json.hits.hits then
+        for k, v in pairs(json.hits.hits) do
+            v._source.request_id = v._id
+            table.insert(out, 1, v._source)
+        end
+    end
+    return out
+end
+
+-- Do a raw ES query with a JSON query
+function raw(query, doctype)
+    local js = JSON.encode(query)
+    doctype = doctype or default_doc
+    local url = config.es_url .. doctype .. "/_search"
+    local result = http.request(url, js)
+    local out = {}
+    local json = JSON.decode(result)
+    return json or {}, url
+end
+
+-- Update a document
+function update(doctype, id, query)
+    local js = JSON.encode({doc = query })
+    doctype = doctype or default_doc
+    local url = config.es_url .. doctype .. "/" .. id .. "/_update"
+    local result = http.request(url, js)
+    local out = {}
+    local json = JSON.decode(result)
+    return json or {}, url
+end
+
+-- Put a new document somewhere
+function index(r, id, ty, body)
+    local js = JSON.encode(query)
+    if not id then
+        id = r:sha1(ty .. (math.random(1,99999999)*os.time()) .. ':' .. r:clock())
+    end
+    local url = config.es_url .. ty .. "/" .. id
+    local result = http.request(url, body)
+    local out = {}
+    local json = JSON.decode(result)
+    return json or {}
+end
+
+function setDefault(typ)
+    default_doc = typ
+end
+
+-- module defs
+return {
+    find = getHits,
+    findFast = getHeaders,
+    findFastReverse = getHeadersReverse,
+    get = getDoc,
+    raw = raw,
+    index = index,
+    default = setDefault,
+    update = update
+}

Added: comdev/helpwanted.apache.org/site/listitems.lua
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/listitems.lua?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/listitems.lua (added)
+++ comdev/helpwanted.apache.org/site/listitems.lua Sun Feb  7 15:33:01 2016
@@ -0,0 +1,78 @@
+--[[
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+
+]]--
+
+local JSON = require 'cjson'
+local elastic = require 'lib/elastic'
+
+function handle(r)
+    r.content_type = "application/json"
+    local t = {}
+    local now = r:clock()
+    local tnow = now
+    local get = r:parseargs()
+    local qs = "*"
+    local domain = get.domain or ""
+    if #domain < 2 then
+        domain = "*"
+    end
+    local dd = 14
+    local maxresults = 10000
+    local listdata = {}
+
+    if get.id then
+        local doc = elastic.get('item', get.id)
+        if doc then
+            r:puts(JSON.encode(doc))
+            return apache2.OK
+        end
+        r:puts[[{}]]
+        return apache2.OK
+    end
+
+    -- Languages
+    local lingos = {}
+    for lang in (get.lang or ""):gmatch("([+#A-Za-z0-9+]+)") do
+        table.insert(lingos, lang)
+    end
+    
+    -- Types
+    local types = {}
+    for typ in (get.type or ""):gmatch("([+#A-Za-z0-9+]+)") do
+        table.insert(types, typ)
+    end
+    
+    -- Projects
+    local projects = {}
+    for project in (get.project or ""):gmatch("([+#A-Za-z0-9+]+)") do
+        table.insert(projects, project)
+    end
+    
+    local pl = #projects > 0 and table.concat(projects, " OR ") or "*"
+    local tl = #types > 0 and table.concat(types, " OR ") or "*"
+    local ll = #lingos > 0 and table.concat(lingos, " OR ") or "*"
+    
+    local dsl = ("languages:(%s) AND type:(%s) AND project:(%s)"):format(ll, tl,pl)
+    
+    local doc = elastic.find(dsl, 200, 'item', 'created')
+    if doc and #doc > 0 then
+        r:puts(JSON.encode(doc))
+    else
+        r:puts[[{}]]
+    end
+    return apache2.OK
+end

Added: comdev/helpwanted.apache.org/site/task.html
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/task.html?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/task.html (added)
+++ comdev/helpwanted.apache.org/site/task.html Sun Feb  7 15:33:01 2016
@@ -0,0 +1,27 @@
+ <!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf8">
+<link rel="stylesheet" href="css/hw.css">
+<link rel="stylesheet" href="css/hw2.css">
+<script src="js/hw.js" type="text/javascript"></script>
+<title>Help Wanted!</title>
+
+</head>
+<body onload="displayItem(document.location.search.substr(1));">
+    
+<div style="text-align: center;">
+    <h2><img src='/images/logo.png' title="Help Wanted!"/></h2>
+    <div id="item">
+        
+    </div>
+</div>
+
+
+<p style="font-size: 12px; font-style: italic; text-align: center;">
+    Managed by the <a href="https://community.apache.org/">Apache Community Development Project</a>.
+    Copyright 2016, the Apache Software Foundation.
+    Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License 2.0</a>
+</p>
+</body>
+</html>
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/widget.js
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/widget.js?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/widget.js (added)
+++ comdev/helpwanted.apache.org/site/widget.js Sun Feb  7 15:33:01 2016
@@ -0,0 +1,151 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+*/
+var widgetobj = null
+var widgetproject = "???"
+var widgettitle = null
+var hw_json = null
+
+function getAsyncJSON(theUrl, xstate, callback) {
+	var xmlHttp = null;
+	if (window.XMLHttpRequest) {
+		xmlHttp = new XMLHttpRequest();
+	} else {
+		xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+	}
+	xmlHttp.open("GET", theUrl, true);
+	xmlHttp.send(null);
+	xmlHttp.onreadystatechange = function(state) {
+
+		if (xmlHttp.readyState == 4 && xmlHttp.status == 200 || xmlHttp.status == 404) {
+			if (callback) {
+				if (xmlHttp.status == 404) {
+					callback({}, xstate);
+				} else {
+					window.setTimeout(callback, 0.05, JSON.parse(xmlHttp.responseText), xstate);
+				}
+			}
+		}
+	}
+}
+
+var hw_oldstate = ""
+function displayItemsWidget(json, state) {
+    hw_json = json ? json : hw_json
+    json = hw_json
+    var diff = ['Beginner', 'Journeyman', 'Intermediate', 'Advanced', 'Expert']
+    var numItems = 0
+    for (var i in json) {
+        var item = json[i]
+        if (item.closed) {
+            continue
+        }
+        numItems++
+    }
+    var obj = widgetobj
+    obj.setAttribute("class", "hwitems hwwidget")
+    
+    if (state && typeof(state) === "string") {
+        if (hw_oldstate == state) {
+            json.sort(function(a,b) { return a[state] < b[state] })
+            hw_oldstate = ""
+        } else {
+            json.sort(function(a,b) { return a[state] > b[state] })
+            hw_oldstate = state
+        }
+        
+    }
+    
+    obj.innerHTML = ""
+    var tbl = "<table style='width: 100%; text-align: left;'><tr><td colspan='5' style='text-align: center;'><img src='https://helpwanted.apache.org/images/cube.png'/> Tasks " + widgettitle + " would like help with:</td></tr>" +
+    "<tr style='cursor: pointer' title='Click on a column to sort'><th onclick='displayItemsWidget(null, \"title\");'>Title</th>" +
+    "<th onclick='displayItemsWidget(null, \"languages\");'>Languages</th>" +
+    "<th onclick='displayItemsWidget(null, \"difficulty\");'>Difficulty</th>" +
+    "<th onclick='displayItemsWidget(null, \"created\");'>Created</th></tr>"
+    
+    if (typeof(numItems) === 'undefined' || numItems == 0) {
+        obj.innerHTML = tbl + "<tr><td colspan='5'>Sorry, we couldn't find any tasks matching your criteria</td></tr>" +
+        "<tr><td colspan='4' style='text-align: center;'>Powered by <a href='https://helpwanted.apache.org/'>Help Wanted</a> - a task directory for Apache projects</td></tr>" +
+        "</table>"
+        return
+    }
+    for (var i in json) {
+        var item = json[i]
+        if (item.closed) {
+            continue
+        }
+        var z = parseInt(i)+1
+        var ptype = item.type.replace(/\s+/g, "")
+        var cdate = new Date(item.created*1000).toLocaleString()
+        var lingos = (item.languages != 'n/a') ? item.languages : ""
+        
+        // admin stuff
+        var add = ""
+        if (state.admin) {
+            add = " &nbsp; <a href='/admin/close.lua?id=" + item.request_id + "'>Mark as done</a>"
+        }
+        
+        tbl += "<tr style='cursor: pointer; ' onclick=\"sw('hw_details_" + i + "');\"><td style='text-align: left;'><div class='itemNumber-widget'>" + z + "</div><img title='" + item.type + "' style='width:16px; height: 16px;' float: left;' src='/images/icon_" + ptype + ".png'/>" +
+        item.title + "</td>" +
+        "<td>" + lingos + "</td><td style='text-align: left;'><img style='width:16px; height: 16px;' src='/images/level_" + (parseInt(item.difficulty)+1) + ".png'/> " + diff[item.difficulty] + add + "</td><td>" + cdate + "</td></tr>"
+        var fi = ""
+        if (item.url && item.url.length > 10) {
+            fi = "<b>Further information: </b> <a href='" + item.url + "'>" + item.url + "</a><br/>"
+        }
+        
+        tbl += "<tr style='display:none;' id='hw_details_" + i + "'><td colspan='6'><b>Project:</b> " + item.project + "<br/><b>Requested by:</b> " + item.author + "@apache.org<br/><b>Created:</b> " + cdate + "<br/><b>Description:</b> <blockquote>" + item.description + "</blockquote>" + fi + "<input type='button' class='dibbutton' onclick='location.href=\"https://helpwanted.apache.org/task.html?" + item.request_id + "\";' value='I am interested in this'/></td></tr>"
+        
+    }
+    tbl += "<tr><td colspan='4' style='text-align: center;'>Powered by <a href='https://helpwanted.apache.org/'>Help Wanted</a> - a task directory for Apache projects</td></tr></table>"
+    obj.innerHTML += tbl
+}
+
+function fetchItemsWidget(languages, types, projects, sortBy) {
+    if (!languages) languages = []
+    if (!types) types = []
+    if (!projects) projects = []
+    getAsyncJSON("https://helpwanted.apache.org/listitems.lua?lang=" + languages.join(",") + "&type=" + types.join(",") +"&project=" + projects.join(","),
+                 {
+                    languages: languages,
+                    types: types,
+                    projects: projects,
+                    sortBy: sortBy
+                    }, displayItemsWidget)
+}
+
+function sw(id) {
+    var obj = document.getElementById(id)
+    var op = obj.style.display == 'none' ? 'table-row' : 'none'
+    obj.style.display = op
+}
+
+var divs = document.getElementsByTagName('div')
+for (var i in divs) {
+    var dt = divs[i].getAttribute("type")
+    var dn = divs[i].getAttribute("project")
+    var dti = divs[i].getAttribute("description")
+    if (dt && dt == "helpwanted" && dn) {
+        widgetobj = divs[i]
+        widgetproject = dn
+        widgettitle = dti ? dti : dn
+        fetchItemsWidget([],[],[dn])
+        var css = document.createElement('link')
+        css.rel = "stylesheet";
+        css.href = "https://helpwanted.apache.org/css/hw2.css";
+        (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(css)
+        break
+    }
+}
\ No newline at end of file

Added: comdev/helpwanted.apache.org/site/wtest.html
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/wtest.html?rev=1728977&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/wtest.html (added)
+++ comdev/helpwanted.apache.org/site/wtest.html Sun Feb  7 15:33:01 2016
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf8">
+<title>Help Wanted!</title>
+
+</head>
+<body>
+    <h2><img src='/images/logo.png' title="Help Wanted!"/></h2>
+    <h2>Sample widget page</h2>
+    This is a sample widget page. Just an ordinary HTML page with a widget attached by inserting the following code:
+    <pre style='background: #FFE; border: 1px solid #666;'>
+&lt;div style="width: 700px;" type="helpwanted" project="httpd" description="the Apache HTTP Server">&lt;/div>
+&lt;script src="https://helpwanted.apache.org/widget.js" type="text/javascript">&lt;/script>
+    </pre>
+    <div style="width: 700px;" type="helpwanted" project="httpd" description="the Apache HTTP Server"></div>
+<script src="https://helpwanted.apache.org/widget.js" type="text/javascript"></script>
+</body>
+</html>