You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@steve.apache.org by ad...@apache.org on 2015/03/23 03:10:32 UTC
svn commit: r1668509 [3/3] - in /steve/steve-web: ./ bin/ docs/ docs/source/
docs/source/_static/ docs/source/_templates/ src/ src/asf/ src/asf/steve/
src/asf/steve/admin/ src/asf/steve/admin/templates/ src/asf/steve/api/
src/asf/steve/api/templates/ s...
Added: steve/steve-web/src/asf/steve/static/js/steve_stv.js
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/static/js/steve_stv.js?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/static/js/steve_stv.js (added)
+++ steve/steve-web/src/asf/steve/static/js/steve_stv.js Mon Mar 23 02:10:31 2015
@@ -0,0 +1,612 @@
+/* WARNING: This script contains Voodoo! */
+/*
+#####
+# 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 candidates = []
+var statements = []
+var ballotNames = []
+var ballotChars = []
+var chars;
+var fading = false
+var seats = 0;
+
+// Make copies for reset
+var candidates_copy = []
+var chars_copy = []
+
+var failover = null;
+
+
+
+// Set transfer data during drag'n'drop
+function dragVote(ev) {
+ ev.dataTransfer.setData("Text", ev.target.getAttribute("data"));
+ failover = ev.target.getAttribute("data")
+ if (ballotNames.indexOf(failover) == -1) {
+ document.getElementById('candidates').style.backgroundImage = "url(/images/dragright.png)"
+ document.getElementById('candidates').style.backgroundRepeat = "no-repeat"
+ } else {
+ document.getElementById('candidates').style.backgroundImage = "url(/images/dragleft.png)"
+ document.getElementById('candidates').style.backgroundRepeat = "no-repeat"
+ }
+}
+
+var source, dest
+
+function cancel(ev) {
+ ev.preventDefault()
+}
+
+function resetList() {
+ candidates = []
+ chars = []
+ for (i in candidates_copy) candidates.push(candidates_copy[i])
+ for (i in chars_copy) chars.push(chars_copy[i])
+ ballotNames = []
+ ballotChars = []
+ shuffleCandidates();
+
+ drawCandidates()
+ fading = false
+ document.getElementById('ballot').innerHTML = '<img src="/images/target.png" style="margin-left: 100px;" ondrop="event.preventDefault(); dropCandidate(event);"/>';
+ drawList();
+}
+
+// Did we drop a vote on top of another?
+function dropVote(ev, parent) {
+
+ //ev.preventDefault();
+ if (parent || fading) return;
+
+ // Get who we dragged and who we dropped it on
+ source = ev.dataTransfer.getData("Text");
+ dest = parent ? ev.target.parentNode.getAttribute("data") : ev.target.getAttribute("data")
+ if (dest == "UPPER") { dest = ballotNames[0]}
+ if (dest == "LOWER") { dest = ballotNames[ballotNames.length -1] }
+ if (candidates.indexOf(dest) != -1) {
+ alert("Back to school!")
+ }
+
+ // If we didn't drag this onto ourselves, let's initiate the fade-out and swap
+ if (source != dest) {
+ fadeOut(1, "ballot");
+ }
+
+}
+
+function dropComplete(z) {
+ if (fading) {
+ return;
+ }
+ // Get array indices
+ var sid = ballotNames.indexOf(source);
+ var did = ballotNames.indexOf(dest)
+
+ // Splice!
+ if (sid >= 0 && did >= 0) {
+ ballotNames.splice(did, 0, ballotNames.splice(sid, 1)[0])
+ ballotChars.splice(did, 0, ballotChars.splice(sid, 1)[0])
+ } else {
+ alert(source + ":" + dest)
+ }
+ //ev.preventDefault();
+ // Redraw and carry on
+
+ drawList()
+ fadeIn(0, z, Math.random())
+}
+
+
+// A little shuffle, so we don't all get the same order at first
+function shuffleCandidates() {
+ for (var i = 0; i < candidates.length; i++) {
+
+ // Pick some numbers
+ var sid = parseInt(Math.random()*candidates.length-0.01);
+ var did = parseInt(Math.random()*candidates.length-0.01);
+
+ // Splice!
+ if (sid >= 0 && did >= 0) {
+ candidates.splice(did, 0, candidates.splice(sid, 1)[0])
+ chars.splice(did, 0, chars.splice(sid, 1)[0])
+ }
+ }
+}
+
+
+function drawCandidates() {
+ var box = document.getElementById('candidates')
+ box.innerHTML = "<h3>Candidates:</h3>"
+ for (i in candidates) {
+ var name = candidates[i]
+ var char = chars[i]
+ // Add element and set drag'n'drop + data
+ var outer = document.createElement('div')
+ var inner = document.createElement('span')
+ inner.style.fontFamily = "monospace"
+ inner.innerHTML = char + ": " + name;
+ inner.setAttribute("ondrop", "dropCandidate(event, true)")
+ outer.setAttribute("class", "ballotbox_clist")
+ outer.setAttribute("id", name)
+ outer.setAttribute("data", name)
+ inner.setAttribute("data", name)
+ outer.setAttribute("draggable", "true")
+ outer.setAttribute("ondragstart", "dragVote(event)")
+ outer.appendChild(inner)
+ outer.setAttribute("title", "Drag to move " + name + " to the ballot box")
+ outer.setAttribute("ondrop", "dropCandidate(event, false)")
+ outer.setAttribute("ondragover", "event.preventDefault();")
+ outer.setAttribute("ondragend", "event.preventDefault();")
+ outer.setAttribute("ondragenter", "event.preventDefault();")
+
+ // Does the candidate have a statement? if so, put it on there
+ if (statements[char]) {
+ var statement = document.createElement('div')
+ statement.setAttribute("class", "statement_marker")
+ statement.setAttribute("title", "Click to read " + name + "'s statement")
+ statement.innerHTML = "<a href='#statement_"+char+"'>Statement</a>"
+
+ outer.appendChild(statement)
+
+ var popup = document.createElement("div")
+ popup.setAttribute("class", "modal")
+ popup.setAttribute("id", "statement_" + char)
+ popup.setAttribute("aria-hidden", "true")
+
+ var popupd = document.createElement("div")
+ popupd.setAttribute("class", "modal-dialog")
+ popup.appendChild(popupd)
+
+ var popuph = document.createElement("div")
+ popuph.setAttribute("class", "modal-header")
+ popuph.innerHTML = '<h2>Statement from ' + name + '</h2><a href="#close" class="btn-close" aria-hidden="true">�</a>'
+
+ var popupb = document.createElement("div")
+ popupb.setAttribute("class", "modal-body")
+ popupb.innerHTML = '<pre>' + (statements[char] ? statements[char] : "This candidate has not prepared a statement") +'</pre>'
+
+ var popupf = document.createElement("div")
+ popupf.setAttribute("class", "modal-footer")
+ popupf.innerHTML = '<a href="#close" class="btn">Close statement</a>'
+
+ popupd.appendChild(popuph)
+ popupd.appendChild(popupb)
+ popupd.appendChild(popupf)
+
+ document.getElementsByTagName('body')[0].appendChild(popup)
+ }/* else {
+ var statement = document.createElement('div')
+ statement.setAttribute("class", "statement_marker")
+ statement.style = "background: linear-gradient(to bottom, #e2e2e2 0%,#dbdbdb 50%,#d1d1d1 51%,#fefefe 100%) !important;"
+ statement.style.color = "#666";
+ statement.innerHTML = "<i>No statement</i>"
+
+ outer.appendChild(statement)
+ }*/
+ box.appendChild(outer)
+
+ }
+}
+
+// Did we drop a vote on top of another?
+function dropCandidate(ev) {
+
+ ev.preventDefault();
+ source = ev.dataTransfer.getData("Text");
+ dest = ev.target.getAttribute("data")
+ var z = 0;
+ if (dest == "UPPER") { dest = ballotNames[0]; z = 0}
+ if (dest == "LOWER") { dest = ballotNames[ballotNames.length -1]; z = 1;}
+ if (dest && candidates.indexOf(dest) != -1) {
+ return;
+ }
+ if (ballotNames.indexOf(source) == -1 && candidates.indexOf(source) != -1) {
+ var x = ballotNames.indexOf(dest)
+ x += z
+ if (ballotNames.indexOf(dest) != -1) {
+ ballotNames.splice(x,0,source);
+ ballotChars.splice(x,0,chars[candidates.indexOf(source)]);
+ } else {
+ ballotNames.push(source)
+ ballotChars.push(chars[candidates.indexOf(source)])
+ }
+ chars.splice(candidates.indexOf(source), 1)
+ candidates.splice(candidates.indexOf(source), 1)
+
+ fadeIn(0, "ballot", Math.random())
+ //ev.preventDefault()
+ drawCandidates();
+ drawList();
+
+ }
+
+}
+
+// Did we drop a vote on top of another?
+function dropBack(ev) {
+ ev.preventDefault();
+ source = ev.dataTransfer.getData("Text");
+ dest = ev.target.getAttribute("data")
+
+ if (dest == "UPPER") { dest = ballotNames[0]}
+ if (dest == "LOWER") { dest = ballotNames[ballotNames.length -1] }
+
+ if ((!dest || candidates.indexOf(dest) != -1) && ballotNames.indexOf(source) != -1) {
+
+ candidates.push(source)
+ chars.push(ballotChars[ballotNames.indexOf(source)])
+ ballotChars.splice(ballotNames.indexOf(source), 1)
+ ballotNames.splice(ballotNames.indexOf(source), 1)
+ drawList();
+ drawCandidates();
+ fadeIn(0, "candidates", Math.random())
+
+ } else {
+ dest = null
+ source = null
+ }
+}
+
+function showLines(ev) {
+
+ source = ev.dataTransfer.getData("Text");
+ source = source ? source : failover;
+ ev.preventDefault();
+ if (ev.target) {
+ var above = false
+ dest = ev.target.getAttribute("data")
+ var odest = dest;
+ var override = false
+ if (dest == "UPPER") { dest = ballotNames[0]; override = true; above = true;}
+ if (dest == "LOWER") { dest = ballotNames[ballotNames.length-1]; override = true; above= false; }
+ for (i=0;i< document.getElementById('ballot').childNodes.length;i++) {
+ var el = document.getElementById('ballot').childNodes[i]
+ el.style.borderTop = ""
+ el.style.borderBottom = ""
+ }
+ document.getElementById('UPPER').style.borderTop = "none"
+ document.getElementById('LOWER').style.borderBottom = "none"
+ document.getElementById('UPPER').style.borderBottom = "none"
+ document.getElementById('LOWER').style.borderTop = "none"
+ if (ballotNames.indexOf(dest) != -1 && dest != source) {
+ a = ballotNames.indexOf(source);
+ b = ballotNames.indexOf(dest);
+
+ override = false
+ if (a != -1 && !override) {
+
+ if (a > b) {
+ above = true;
+ } else {
+ above = false;
+ }
+ } else {
+ b--;
+ if (b == -1) {
+ above = false;
+ } if (b == ballotNames.length-1) {
+ above = false;
+ }
+ }
+
+ if (((a == -1 || above == true) && odest != "UPPER") || odest == "LOWER") {
+ document.getElementById(odest).style.borderTop = "16px solid #0AF";
+ } else {
+ document.getElementById(odest).style.borderBottom = "16px solid #0AF";
+ }
+ }
+ }
+
+}
+
+function insertAfter(newNode, referenceNode) {
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
+}
+
+function insertBefore(newNode, referenceNode) {
+ referenceNode.parentNode.insertBefore(newNode, referenceNode);
+}
+
+function drawList() {
+
+
+ // Remove drag helper
+ document.getElementById('candidates').style.background = "";
+
+ // Fetch ballot master and clear it
+ var ballot = document.getElementById('ballot')
+ ballot.innerHTML = ""
+ var s = 0;
+
+ // For each nominee, do...
+ for (i in ballotNames) {
+ s++;
+ var el = ballotNames[i];
+ var outer = document.createElement('li');
+ // Set style
+ outer.setAttribute("class", "ballotbox")
+
+ var no = document.createElement('div');
+ no.setAttribute("class", "ballotNumber")
+ no.innerHTML = (s)
+
+
+ // Above/below cutaway line? If so, draw it
+ if (s == seats) {
+ outer.style.borderBottom = "1px solid #A00"
+ }
+ if (s == seats+1) {
+ outer.style.borderTop = "1px solid #A00"
+ }
+
+ // 'grey out' people below cutaway line
+ if (s > seats) {
+ outer.style.opacity = "0.75"
+ }
+
+ // Add element and set drag'n'drop + data
+ var inner = document.createElement('span')
+ inner.style.left = "35px"
+ inner.style.maxWidth = "300px"
+ inner.style.maxHeight = "60px"
+ inner.style.overflow = "hidden"
+ inner.innerHTML = ballotChars[i] + ": " + el;
+ inner.setAttribute("ondrop", "dropVote(event, true)")
+ outer.setAttribute("id", el)
+ outer.setAttribute("data", el)
+ inner.setAttribute("data", el)
+ outer.setAttribute("draggable", "true")
+ outer.setAttribute("ondragstart", "dragVote(event)")
+ outer.setAttribute("ondragenter", "showLines(event)")
+ outer.appendChild(no)
+ outer.appendChild(inner)
+ outer.setAttribute("title", "Drag to move " + el + " up or down on the list")
+ outer.setAttribute("ondrop", "dropVote(event, false)")
+
+
+ if (el == source) {
+ outer.style.opacity = "0"
+ }
+
+ // Add to box
+ ballot.appendChild(outer)
+ }
+
+ // Drop upper and lower filler boxes, so people can drag to the top/bottom of the list as well
+ if (!document.getElementById('UPPER')) {
+ var d = document.createElement('div');
+ d.setAttribute("class", "fillerbox")
+ d.setAttribute("data", "UPPER");
+ d.setAttribute("id", "UPPER");
+ d.setAttribute("ondragenter", "showLines(event)")
+ d.setAttribute("ondrop", "dropVote(event, false)")
+ insertBefore(d, ballot);
+
+ var d = document.createElement('div');
+ d.setAttribute("class", "fillerbox")
+ d.setAttribute("id", "LOWER")
+ d.setAttribute("data", "LOWER");
+ d.setAttribute("ondrop", "dropVote(event, false)")
+ d.setAttribute("ondragenter", "showLines(event)")
+ insertAfter(d, ballot);
+ }
+
+ // Clear any bad lines
+ document.getElementById('UPPER').style.borderTop = "none"
+ document.getElementById('LOWER').style.borderBottom = "none"
+ document.getElementById('UPPER').style.borderBottom = "none"
+ document.getElementById('LOWER').style.borderTop = "none"
+
+}
+
+
+// Fade in/out maneuvres
+function fadeOut(x) {
+ if (source) {
+ if (!x) {
+ x = 1
+ }
+ if (fading) {
+ return;
+ }
+ x -= 0.1
+ document.getElementById(source).setAttribute("class", "ballotSelected")
+ document.getElementById(source).style.opacity = String(x)
+ if (x > 0) {
+ window.setTimeout(function() { fadeOut(x)}, 20)
+ } else {
+ dropComplete("candidates");
+ }
+ }
+}
+
+var gz = 0;
+function fadeIn(x, y, z) {
+ if (source) {
+ if (x == 0) {
+ gz = z;
+ }
+ if (z != gz) {
+ return;
+ }
+ x += 0.1
+ if (x >= 1) {
+ x = 1
+ }
+ document.getElementById(source).style.opacity = String(x)
+
+
+ document.getElementById(source).setAttribute("class", "ballotSelected")
+ if (x < 1) {
+ fading = true
+ window.setTimeout(function() { fadeIn(x, y, z)}, 25)
+
+ } else {
+ window.setTimeout(function() {fading = false }, 250)
+ if (y == "ballot") {
+ document.getElementById(source).setAttribute("class", "ballotbox")
+
+ } else {
+ document.getElementById(source).setAttribute("class", "ballotbox_clist")
+
+ }
+ source = null
+ drawList();
+
+
+ }
+ }
+}
+
+var step = -1
+
+function loadIssue(election, issue, uid, callback) {
+
+ var messages = ["Herding cats...", "Shaving yaks...", "Shooing some cows away...", "Fetching election data...", "Loading issues..."]
+ if (!election || !uid) {
+ var l = document.location.search.substr(1).split("/");
+ election = l[0];
+ issue = l.length > 1 ? l[l.length-2] : "";
+ uid = l.length > 2 ? l[l.length-1] : "";
+ }
+ if (step == -1) {
+ getJSON("/steve/voter/view/" + election + "/" + issue + "?uid=" + uid, [election, issue, uid], callback)
+ }
+
+ var obj = document.getElementById('preloader');
+ step++;
+ if (!election_data && obj) {
+ if (step % 2 == 1) obj.innerHTML = messages[parseInt(Math.random()*messages.length-0.01)]
+ } else if (obj && (step % 2 == 1)) {
+ obj.innerHTML = "Ready..!"
+ }
+ if (step % 2 == 1) {
+ obj.style.transform = "translate(0,0)"
+ } else if (obj) {
+ obj.style.transform = "translate(0,-500%)"
+ }
+ if (!election_data|| (step % 2 == 0) ) {
+ window.setTimeout(loadElection, 750, election, uid, callback);
+ }
+}
+
+function displayIssueSTV(code, response, state) {
+ chars = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'] // Corresponding STV letters, in same order as nominees
+ election_data = response
+ if (code != 200) {
+ document.getElementById('preloaderWrapper').innerHTML = "<h1>Could not load issue:</h1><h2>" + response.message + "</h2>";
+ } else {
+ candidates = []
+ statements = {}
+ var m = response.issue.type.match(/(\d+)/);
+ if (m) {
+ seats = parseInt(m[1])
+ }
+ for (c in response.issue.candidates) {
+ var candidate = response.issue.candidates[c];
+ candidates.push(candidate.name);
+ statements[chars[c]] = candidate.statement;
+ }
+ document.getElementById('cnum').innerHTML = candidates.length
+ document.getElementById('snum').innerHTML = seats
+ while (chars.length > candidates.length) chars.splice(-1,1)
+
+ for (i in candidates) candidates_copy.push(candidates[i])
+ for (i in chars) chars_copy.push(chars[i])
+
+
+ var obj = document.getElementById('preloaderWrapper')
+ obj.innerHTML = ""
+ obj.setAttribute("style", "min-width: 100%; min-height: 400px;")
+ obj.setAttribute("id", "ballotWrapper")
+
+
+ var c = document.createElement('div')
+ c.setAttribute("id", "candidates")
+ c.setAttribute("ondragover", "event.preventDefault();")
+ c.setAttribute("ondragenter", "event.preventDefault();")
+ c.setAttribute("ondragend", "event.preventDefault();")
+ c.setAttribute("ondrop", "dropBack(event);")
+ obj.appendChild(c)
+
+ var b = document.createElement('div')
+ b.setAttribute("id", "ballotbox")
+ b.setAttribute("ondragover", "event.preventDefault();")
+ b.setAttribute("ondragenter", "event.preventDefault();")
+ b.setAttribute("ondragend", "event.preventDefault();")
+ b.setAttribute("ondrop", "dropCandidate(event);")
+ b.innerHTML = "<font color='red'><h3>Drag candidates over here:</h3</font>"
+
+ var l = document.createElement('ol')
+ l.setAttribute("id", "ballot")
+ b.appendChild(l)
+ obj.appendChild(b)
+ l.innerHTML = "<img src='/images/target.png'/>"
+
+
+ var stvdiv = document.createElement('div')
+ stvdiv.setAttribute("id", "stv")
+ b.appendChild(stvdiv)
+
+ var vote = document.createElement('input')
+ vote.setAttribute("type", "button")
+ vote.setAttribute("class", "btn-green")
+ vote.setAttribute("value", "Cast votes")
+ vote.setAttribute("onclick", "castVotes();")
+
+ var reset = document.createElement('input')
+ reset.setAttribute("type", "button")
+ reset.setAttribute("class", "btn-red")
+ reset.setAttribute("value", "Reset")
+ reset.setAttribute("onclick", "resetList();")
+
+ stvdiv.appendChild(vote)
+ stvdiv.appendChild(reset)
+
+ shuffleCandidates();
+ drawCandidates();
+
+ document.getElementById('title').innerHTML = response.issue.title
+ document.title = response.issue.title + " - Apache STeVe"
+
+ }
+
+}
+
+function castVotes(args) {
+ var l = document.location.search.substr(1).split("/");
+ election = l[0];
+ issue = l.length > 1 ? l[l.length-2] : "";
+ uid = l.length > 2 ? l[l.length-1] : "";
+ postREST("/steve/voter/vote/" + election + "/" + issue, {
+ uid: uid,
+ vote: ballotChars.join("")
+ },
+ undefined,
+ castVotesCallback,
+ null)
+}
+
+function castVotesCallback(code, response, state) {
+ if (code != 200) {
+ alert(response.message)
+ } else {
+ document.getElementById('votebox').innerHTML = "<h2>Your vote has been registered!</h2><p style='text-align:center;'><big>Should you reconsider, you can always reload this page and vote again.<br/><br/><a href=\"javascript:void(location.href='election.html'+document.location.search);\">Back to election front page</a></big></p>"
+ }
+}
\ No newline at end of file
Added: steve/steve-web/src/asf/steve/templates/error.html
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/templates/error.html?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/templates/error.html (added)
+++ steve/steve-web/src/asf/steve/templates/error.html Mon Mar 23 02:10:31 2015
@@ -0,0 +1,17 @@
+{% extends 'layout.html' %}
+
+{% block content %}
+{{ super() }}
+
+<div class="container">
+ <div class="row">
+ <h1 class="alert-heading">500 Internal Server Error</h1>
+
+ <p>
+ The server encountered an internal error and was unable to complete your
+ request. Either the server is overloaded or there is an error in the
+ application.
+ </p>
+ </div>
+</div>
+{% endblock %}
Added: steve/steve-web/src/asf/steve/templates/forbidden.html
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/templates/forbidden.html?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/templates/forbidden.html (added)
+++ steve/steve-web/src/asf/steve/templates/forbidden.html Mon Mar 23 02:10:31 2015
@@ -0,0 +1,32 @@
+{% extends "layout.html" %}
+
+
+{% block content %}
+ {{ super() }}
+
+ {% if g.identity.is_authenticated %}
+ <div class="alert alert-block alert-error">
+ <p><strong>You do not have permission to access this page</strong></p>
+
+ <p>You can <a href="{{ url_for('logout') }}">sign out</a> to log in as a different user.</p>
+ </div>
+ {% elif g.login_allowed %}
+ <p class="alert alert-warning">
+ <strong>Please sign in to access this page</strong>
+ </p>
+
+ <form action="{{ url_for('login') }}" method="POST">
+ <p>
+ <input name="username" class="span3" type="text" placeholder="Username">
+ <input name="password" class="span3" type="password" placeholder="Password">
+ <button class="btn" type="submit">Sign in</button>
+ </p>
+ </form>
+ {% else %}
+ <div class="alert alert-block alert-error">
+ <p><strong>Login disabled over insecure connection</strong></p>
+
+ <p>For security reasons, login is only enabled over HTTPS.</p>
+ </div>
+ {% endif %}
+{% endblock %}
Added: steve/steve-web/src/asf/steve/templates/index.html
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/templates/index.html?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/templates/index.html (added)
+++ steve/steve-web/src/asf/steve/templates/index.html Mon Mar 23 02:10:31 2015
@@ -0,0 +1,16 @@
+{% extends "layout.html" %}
+
+{% block onload %}onload="getJSON('/steve/admin/list', null, showElections);"{% endblock %}
+
+{% block content %}
+ <div id="popups"></div>
+ <p style="text-align: center;">
+ <img src="{{ url_for('static', filename='images/steve_logo.png') }}"/>
+ </p>
+ <div class="formbox">
+ <h1 id="title">STeVe administration</h1>
+ <a href="create_election.html" class="btn-green">Create a new election</a><br/>
+ <div id="preloaderWrapper">
+ </div>
+ </div>
+{% endblock %}
Added: steve/steve-web/src/asf/steve/templates/layout.html
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/templates/layout.html?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/templates/layout.html (added)
+++ steve/steve-web/src/asf/steve/templates/layout.html Mon Mar 23 02:10:31 2015
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ {% block head %}
+ <meta charset="utf-8">
+ <meta name="apple-mobile-web-app-capable" content="yes"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
+ <meta name="apple-mobile-web-app-status-bar-style" content="black"/>
+ <title>{% block title %}Apache STeVe{% endblock title %}</title>
+ <meta name="description" content="">
+ <meta name="author" content="">
+
+ <!-- Le styles -->
+ <link href="{{ url_for('static', filename='css/steve.css') }}" rel="stylesheet">
+ <link href="{{ url_for('static', filename='css/jquery-ui-1.11.4.css') }}" rel="stylesheet">
+
+ <!-- Le fav and touch icons -->
+ <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
+ <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
+
+ {% endblock head %}
+</head>
+
+<body {% block onload %}{% endblock onload %}>
+
+{% block header %}
+ <div id="popups"></div>
+ <p style="text-align: center;">
+ <img src="{{ url_for('static', filename='images/steve_logo.png') }}"/>
+ </p>
+{% endblock header %}
+
+
+{% block content %}
+{% endblock %}
+
+{% block footer %}
+{% endblock footer %}
+
+<script src="//code.jquery.com/jquery-2.0.0.min.js"></script>
+<script src="//code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
+<script src="{{ url_for('static', filename='js/steve_rest.js') }}"></script>
+
+{% block script %}
+{% endblock script %}
+
+</body>
+</html>
Added: steve/steve-web/src/asf/steve/templates/not_found.html
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/templates/not_found.html?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/templates/not_found.html (added)
+++ steve/steve-web/src/asf/steve/templates/not_found.html Mon Mar 23 02:10:31 2015
@@ -0,0 +1,17 @@
+{% extends 'layout.html' %}
+
+{% block content %}
+{{ super() }}
+
+<div class="container">
+ <div class="row">
+ <div class="col-md-6">
+ <h1><b>404 Not Found</b></h1>
+
+ <p>The requested URL was not found on the server.</p>
+
+ <p>If you entered the URL manually please check your spelling and try again.</p>
+ </div>
+ </div>
+</div>
+{% endblock %}
Added: steve/steve-web/src/asf/steve/voter/__init__.py
URL: http://svn.apache.org/viewvc/steve/steve-web/src/asf/steve/voter/__init__.py?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/src/asf/steve/voter/__init__.py (added)
+++ steve/steve-web/src/asf/steve/voter/__init__.py Mon Mar 23 02:10:31 2015
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+import logging
+
+import flask
+from flask.ext import principal
+
+
+voter = flask.Blueprint('voter', __name__, template_folder='templates')
+
+log = logging.getLogger(__name__)
+
+authenticated_permission = principal.Permission(principal.RoleNeed('authenticated'))
+
+
+@voter.route('/v1/admin/list')
+@authenticated_permission.require()
+def user_info():
+ return flask.jsonify([{
+ 'id': '',
+ 'owner': 'foo'
+ }])
+
+
+@voter.errorhandler(401)
+@voter.errorhandler(403)
+@voter.errorhandler(principal.PermissionDenied)
+def forbidden_handler(error):
+ return flask.render_template('api_forbidden.html', error=error), 403
Added: steve/steve-web/tests/data/steve-flask.properties
URL: http://svn.apache.org/viewvc/steve/steve-web/tests/data/steve-flask.properties?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/tests/data/steve-flask.properties (added)
+++ steve/steve-web/tests/data/steve-flask.properties Mon Mar 23 02:10:31 2015
@@ -0,0 +1,4 @@
+DEBUG=True
+SECRET_KEY='\xd0\xe8\x13\x08\xfd\xa3\x97*\x161FY\x9d\xdd\xbb\xff\xacWF\xd4}\t\x03\xf2'
+SESSION_COOKIE_NAME='_ps'
+
Added: steve/steve-web/tests/test_steve.py
URL: http://svn.apache.org/viewvc/steve/steve-web/tests/test_steve.py?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/tests/test_steve.py (added)
+++ steve/steve-web/tests/test_steve.py Mon Mar 23 02:10:31 2015
@@ -0,0 +1,3 @@
+
+def test_steve():
+ pass
\ No newline at end of file
Added: steve/steve-web/tox.ini
URL: http://svn.apache.org/viewvc/steve/steve-web/tox.ini?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/tox.ini (added)
+++ steve/steve-web/tox.ini Mon Mar 23 02:10:31 2015
@@ -0,0 +1,13 @@
+[tox]
+deps=-rrequirements.txt
+envlist=flake8,pytest
+
+[testenv:pytest]
+deps=pytest
+ mock
+ -rrequirements.txt
+commands=py.test {posargs} # substitute with tox' positional arguments
+
+[testenv:flake8]
+commands = flake8 {posargs}
+deps = flake8
Added: steve/steve-web/uwsgi.ini
URL: http://svn.apache.org/viewvc/steve/steve-web/uwsgi.ini?rev=1668509&view=auto
==============================================================================
--- steve/steve-web/uwsgi.ini (added)
+++ steve/steve-web/uwsgi.ini Mon Mar 23 02:10:31 2015
@@ -0,0 +1,10 @@
+[uwsgi]
+http = 127.0.0.1:8080
+env = STEVE_FLASK_CONFIG=../../../tests/data/steve-flask.properties
+wsgi-file = ./bin/steve.uwsgi.wsgi
+callable = app
+pythonpath = src
+processes = 4
+threads = 2
+stats = 127.0.0.1:9191
+