diff --git a/misc/dashboard/godashboard/app.yaml b/misc/dashboard/godashboard/app.yaml index fb742d8edcb..aec559dccd5 100644 --- a/misc/dashboard/godashboard/app.yaml +++ b/misc/dashboard/godashboard/app.yaml @@ -1,5 +1,5 @@ application: godashboard -version: 4 +version: 5 runtime: python api_version: 1 @@ -10,5 +10,8 @@ handlers: - url: /package.* script: package.py +- url: /project.* + script: package.py + - url: /.* script: gobuild.py diff --git a/misc/dashboard/godashboard/benchmark1.html b/misc/dashboard/godashboard/benchmark1.html index 66e9830a219..2d49e7204e9 100644 --- a/misc/dashboard/godashboard/benchmark1.html +++ b/misc/dashboard/godashboard/benchmark1.html @@ -9,6 +9,7 @@ diff --git a/misc/dashboard/godashboard/benchmarks.html b/misc/dashboard/godashboard/benchmarks.html index 14026f5dac1..d42fcfe4836 100644 --- a/misc/dashboard/godashboard/benchmarks.html +++ b/misc/dashboard/godashboard/benchmarks.html @@ -9,6 +9,7 @@ diff --git a/misc/dashboard/godashboard/index.yaml b/misc/dashboard/godashboard/index.yaml index 573abfb0970..148824bb6af 100644 --- a/misc/dashboard/godashboard/index.yaml +++ b/misc/dashboard/godashboard/index.yaml @@ -23,6 +23,17 @@ indexes: - name: __key__ direction: desc +- kind: Project + properties: + - name: approved + - name: category + - name: name + +- kind: Project + properties: + - name: category + - name: name + # AUTOGENERATED # This index.yaml is automatically updated whenever the dev_appserver diff --git a/misc/dashboard/godashboard/main.html b/misc/dashboard/godashboard/main.html index dba7951b85a..8eb27869e53 100644 --- a/misc/dashboard/godashboard/main.html +++ b/misc/dashboard/godashboard/main.html @@ -11,6 +11,7 @@ diff --git a/misc/dashboard/godashboard/package.html b/misc/dashboard/godashboard/package.html index 64d86d7b80a..08dd6a31d64 100644 --- a/misc/dashboard/godashboard/package.html +++ b/misc/dashboard/godashboard/package.html @@ -9,6 +9,7 @@ @@ -22,24 +23,34 @@

Recently Installed Packages

- + {% for r in by_time %} + {% endfor %}
last installcountpath
last installcountpathproject
{{r.last_install|date:"Y-M-d H:i"}} {{r.count}} {{r.path}} + {% for p in r.project_set %} + {{p.name}} - {{p.descr}} + {% endfor %} +

Most Installed Packages

- + {% for r in by_count %} + {% endfor %}
last installcountpath
last installcountpathproject
{{r.last_install|date:"Y-M-d H:i"}} {{r.count}} {{r.path}} + {% for p in r.project_set %} + {{p.name}} - {{p.descr}} + {% endfor %} +
diff --git a/misc/dashboard/godashboard/package.py b/misc/dashboard/godashboard/package.py index 351a1fadc8d..6c3bd999569 100644 --- a/misc/dashboard/godashboard/package.py +++ b/misc/dashboard/godashboard/package.py @@ -5,12 +5,18 @@ # This is the server part of the package dashboard. # It must be run by App Engine. +mail_to = "adg@golang.org" +mail_from = "Go Dashboard " +mail_subject = "New Project Submitted" + from google.appengine.api import memcache from google.appengine.runtime import DeadlineExceededError from google.appengine.ext import db from google.appengine.ext import webapp from google.appengine.ext.webapp import template from google.appengine.ext.webapp.util import run_wsgi_app +from google.appengine.api import users +from google.appengine.api import mail import binascii import datetime import hashlib @@ -21,6 +27,7 @@ import re import struct import time import urllib2 +import sets # Storage model for package info recorded on server. # Just path, count, and time of last install. @@ -30,6 +37,15 @@ class Package(db.Model): count = db.IntegerProperty() last_install = db.DateTimeProperty() +class Project(db.Model): + name = db.StringProperty(indexed=True) + descr = db.StringProperty() + web_url = db.StringProperty() + package = db.ReferenceProperty(Package) + category = db.StringProperty(indexed=True) + tags = db.ListProperty(str) + approved = db.BooleanProperty(indexed=True) + re_bitbucket = re.compile(r'^bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$') re_googlecode = re.compile(r'^[a-z0-9\-]+\.googlecode\.com/(svn|hg)$') re_github = re.compile(r'^github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+$') @@ -124,8 +140,118 @@ class PackagePage(webapp.RequestHandler): self.response.set_status(500) self.response.out.write('not ok') +class ProjectPage(webapp.RequestHandler): + + def get(self): + admin = users.is_current_user_admin() + if self.request.path == "/project/login": + self.redirect(users.create_login_url("/project")) + elif self.request.path == "/project/logout": + self.redirect(users.create_logout_url("/project")) + elif self.request.path == "/project/edit" and admin: + self.edit() + else: + self.list() + + def post(self): + if self.request.path == "/project/edit": + self.edit(True) + else: + data = dict(map(lambda x: (x, self.request.get(x)), ["name","descr","web_url"])) + if reduce(lambda x, y: x or not y, data.values(), False): + data["submitMsg"] = "You must complete all the fields." + self.list(data) + return + p = Project.get_by_key_name("proj-"+data["name"]) + if p is not None: + data["submitMsg"] = "A project by this name already exists." + self.list(data) + return + p = Project(key_name="proj-"+data["name"], **data) + p.put() + + path = os.path.join(os.path.dirname(__file__), 'project-notify.txt') + mail.send_mail( + sender=mail_from, to=mail_to, subject=mail_subject, + body=template.render(path, {'project': p})) + + self.list({"submitMsg": "Your project has been submitted."}) + + def list(self, data={}): + projects = Project.all().order('category').order('name') + + admin = users.is_current_user_admin() + if not admin: + projects = projects.filter('approved =', True) + + projects = list(projects) + + tags = sets.Set() + for p in projects: + for t in p.tags: + tags.add(t) + + tag = self.request.get("tag", None) + if tag: + projects = filter(lambda x: tag in x.tags, projects) + + self.response.headers['Content-Type'] = 'text/html; charset=utf-8' + path = os.path.join(os.path.dirname(__file__), 'project.html') + data["tag"] = tag + data["tags"] = tags + data["projects"] = projects + data["admin"] = admin + self.response.out.write(template.render(path, data)) + + def edit(self, save=False): + if save: + name = self.request.get("orig_name") + else: + name = self.request.get("name") + + p = Project.get_by_key_name("proj-"+name) + if not p: + self.response.out.write("Couldn't find that Project.") + return + + if save: + if self.request.get("do") == "Delete": + p.delete() + else: + pkg_name = self.request.get("package", None) + if pkg_name: + pkg = Package.get_by_key_name("pkg-"+pkg_name) + if pkg: + p.package = pkg.key() + for f in ['name', 'descr', 'web_url', 'category']: + setattr(p, f, self.request.get(f, None)) + p.approved = self.request.get("approved") == "1" + p.tags = filter(lambda x: x, self.request.get("tags", "").split(",")) + p.put() + self.redirect("/project") + return + + # get all project categories and tags + cats, tags = sets.Set(), sets.Set() + for r in Project.all(): + cats.add(r.category) + for t in r.tags: + tags.add(t) + + self.response.headers['Content-Type'] = 'text/html; charset=utf-8' + path = os.path.join(os.path.dirname(__file__), 'project-edit.html') + self.response.out.write(template.render(path, { + "taglist": tags, "catlist": cats, "p": p, "tags": ",".join(p.tags) })) + + def redirect(self, url): + self.response.set_status(302) + self.response.headers.add_header("Location", url) + def main(): - app = webapp.WSGIApplication([('/package', PackagePage)], debug=True) + app = webapp.WSGIApplication([ + ('/package', PackagePage), + ('/project.*', ProjectPage), + ], debug=True) run_wsgi_app(app) if __name__ == '__main__': diff --git a/misc/dashboard/godashboard/project-edit.html b/misc/dashboard/godashboard/project-edit.html new file mode 100644 index 00000000000..5f1ca3b1189 --- /dev/null +++ b/misc/dashboard/godashboard/project-edit.html @@ -0,0 +1,45 @@ + + + + + + + + +
+Name:
+
+Description:
+
+Category:
+
+Tags: (comma-separated)
+
+Web URL:
+
+Package URL: (to link to a goinstall'd package)
+
+Approved:
+
+ + +
+ + + diff --git a/misc/dashboard/godashboard/project-notify.txt b/misc/dashboard/godashboard/project-notify.txt new file mode 100644 index 00000000000..3a165908ca1 --- /dev/null +++ b/misc/dashboard/godashboard/project-notify.txt @@ -0,0 +1,9 @@ +A new project has been submitted: + +Name: {{project.name}} +Description: {{project.descr}} +URL: {{project.web_url}} + +To edit/approve/delete: +http://godashboard.appspot.com/project/edit?name={{project.name|urlencode}} + diff --git a/misc/dashboard/godashboard/project.html b/misc/dashboard/godashboard/project.html new file mode 100644 index 00000000000..a9363806fbb --- /dev/null +++ b/misc/dashboard/godashboard/project.html @@ -0,0 +1,86 @@ + + + + Projects - Go Dashboard + + + + + + + +

Go Dashboard

+ +

+ These are external projects and not endorsed or supported by the Go project. +

+ +

Projects

+ +
+

Submit a Project

+

+ Using this form you can submit a project to be included in the list. +

+
+ + + {% endif %} +
Name: +
Description: +
URL: +
  + {% if submitMsg %} +
{{ submitMsg }}
+
+
+ +

+ Filter by tag: + {% if tag %} + all + {% else %} + all + {% endif %} + {% for t in tags %} + {% ifequal t tag %} + {{t}} + {% else %} + {{t}} + {% endifequal %} + {% endfor %} +

+ + {% for r in projects %} + {% ifchanged r.category %} + {% if not forloop.first %} + + {% endif %} +

{{r.category}}

+ + {% endif %} + {% endfor %} + + + + + diff --git a/misc/dashboard/godashboard/static/style.css b/misc/dashboard/godashboard/static/style.css index 882b854aba5..481af36d7d6 100644 --- a/misc/dashboard/godashboard/static/style.css +++ b/misc/dashboard/godashboard/static/style.css @@ -3,8 +3,8 @@ body { margin: 0; padding: 0; } -h1, h2, ul, table, p { - padding: 0 0.2em; +h1, h2, h3, ul.menu, table, p { + padding: 0 0.5em; } h1, h2 { margin: 0; @@ -19,6 +19,25 @@ h1 { } h2 { border-top: 1px solid #ccc; + padding-left: 0.2em; +} +.submit { + float: right; + border: 1px solid #ccc; + width: 350px; + padding-bottom: 1em; + margin: 0.5em; + background: #eee; +} +.submit table { + width: 100%; +} +.submit input[type=text] { + width: 200px; +} +.submit .msg { + text-align: center; + color: red; } table.alternate { white-space: nowrap;