mirror of
https://github.com/golang/go
synced 2024-11-21 22:34:48 -07:00
godashboard: add Projects page
R=rsc, r, gri CC=golang-dev https://golang.org/cl/1476041
This commit is contained in:
parent
57e1888741
commit
6e83100ae5
@ -1,5 +1,5 @@
|
|||||||
application: godashboard
|
application: godashboard
|
||||||
version: 4
|
version: 5
|
||||||
runtime: python
|
runtime: python
|
||||||
api_version: 1
|
api_version: 1
|
||||||
|
|
||||||
@ -10,5 +10,8 @@ handlers:
|
|||||||
- url: /package.*
|
- url: /package.*
|
||||||
script: package.py
|
script: package.py
|
||||||
|
|
||||||
|
- url: /project.*
|
||||||
|
script: package.py
|
||||||
|
|
||||||
- url: /.*
|
- url: /.*
|
||||||
script: gobuild.py
|
script: gobuild.py
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li><a href="/">Build Status</a></li>
|
<li><a href="/">Build Status</a></li>
|
||||||
<li><a href="/package">Packages</a></li>
|
<li><a href="/package">Packages</a></li>
|
||||||
|
<li><a href="/project">Projects</a></li>
|
||||||
<li><a href="/benchmarks">Benchmarks</a></li>
|
<li><a href="/benchmarks">Benchmarks</a></li>
|
||||||
<li><a href="http://golang.org/">golang.org</a></li>
|
<li><a href="http://golang.org/">golang.org</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li><a href="/">Build Status</a></li>
|
<li><a href="/">Build Status</a></li>
|
||||||
<li><a href="/package">Packages</a></li>
|
<li><a href="/package">Packages</a></li>
|
||||||
|
<li><a href="/project">Projects</a></li>
|
||||||
<li>Benchmarks</li>
|
<li>Benchmarks</li>
|
||||||
<li><a href="http://golang.org/">golang.org</a></li>
|
<li><a href="http://golang.org/">golang.org</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -23,6 +23,17 @@ indexes:
|
|||||||
- name: __key__
|
- name: __key__
|
||||||
direction: desc
|
direction: desc
|
||||||
|
|
||||||
|
- kind: Project
|
||||||
|
properties:
|
||||||
|
- name: approved
|
||||||
|
- name: category
|
||||||
|
- name: name
|
||||||
|
|
||||||
|
- kind: Project
|
||||||
|
properties:
|
||||||
|
- name: category
|
||||||
|
- name: name
|
||||||
|
|
||||||
# AUTOGENERATED
|
# AUTOGENERATED
|
||||||
|
|
||||||
# This index.yaml is automatically updated whenever the dev_appserver
|
# This index.yaml is automatically updated whenever the dev_appserver
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li>Build Status</li>
|
<li>Build Status</li>
|
||||||
<li><a href="/package">Packages</a></li>
|
<li><a href="/package">Packages</a></li>
|
||||||
|
<li><a href="/project">Projects</a></li>
|
||||||
<li><a href="/benchmarks">Benchmarks</a></li>
|
<li><a href="/benchmarks">Benchmarks</a></li>
|
||||||
<li><a href="http://golang.org/">golang.org</a></li>
|
<li><a href="http://golang.org/">golang.org</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<ul class="menu">
|
<ul class="menu">
|
||||||
<li><a href="/">Build Status</a></li>
|
<li><a href="/">Build Status</a></li>
|
||||||
<li>Packages</li>
|
<li>Packages</li>
|
||||||
|
<li><a href="/project">Projects</a></li>
|
||||||
<li><a href="/benchmarks">Benchmarks</a></li>
|
<li><a href="/benchmarks">Benchmarks</a></li>
|
||||||
<li><a href="http://golang.org/">golang.org</a></li>
|
<li><a href="http://golang.org/">golang.org</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -22,24 +23,34 @@
|
|||||||
|
|
||||||
<h2>Recently Installed Packages</h2>
|
<h2>Recently Installed Packages</h2>
|
||||||
<table class="alternate" cellpadding="0" cellspacing="0">
|
<table class="alternate" cellpadding="0" cellspacing="0">
|
||||||
<tr><th>last install</th><th>count</th><th>path</th></tr>
|
<tr><th>last install</th><th>count</th><th>path</th><th>project</th></tr>
|
||||||
{% for r in by_time %}
|
{% for r in by_time %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
|
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
|
||||||
<td class="count">{{r.count}}</td>
|
<td class="count">{{r.count}}</td>
|
||||||
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
|
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
|
||||||
|
<td class="project">
|
||||||
|
{% for p in r.project_set %}
|
||||||
|
<a href="{{p.web_url}}">{{p.name}}</a> - {{p.descr}}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h2>Most Installed Packages</h2>
|
<h2>Most Installed Packages</h2>
|
||||||
<table class="alternate" cellpadding="0" cellspacing="0">
|
<table class="alternate" cellpadding="0" cellspacing="0">
|
||||||
<tr><th>last install</th><th>count</th><th>path</th></tr>
|
<tr><th>last install</th><th>count</th><th>path</th><th>project</th></tr>
|
||||||
{% for r in by_count %}
|
{% for r in by_count %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
|
<td class="time">{{r.last_install|date:"Y-M-d H:i"}}</td>
|
||||||
<td class="count">{{r.count}}</td>
|
<td class="count">{{r.count}}</td>
|
||||||
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
|
<td class="path"><a href="{{r.web_url}}">{{r.path}}</a></td>
|
||||||
|
<td class="project">
|
||||||
|
{% for p in r.project_set %}
|
||||||
|
<a href="{{p.web_url}}">{{p.name}}</a> - {{p.descr}}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
@ -5,12 +5,18 @@
|
|||||||
# This is the server part of the package dashboard.
|
# This is the server part of the package dashboard.
|
||||||
# It must be run by App Engine.
|
# It must be run by App Engine.
|
||||||
|
|
||||||
|
mail_to = "adg@golang.org"
|
||||||
|
mail_from = "Go Dashboard <adg@golang.org>"
|
||||||
|
mail_subject = "New Project Submitted"
|
||||||
|
|
||||||
from google.appengine.api import memcache
|
from google.appengine.api import memcache
|
||||||
from google.appengine.runtime import DeadlineExceededError
|
from google.appengine.runtime import DeadlineExceededError
|
||||||
from google.appengine.ext import db
|
from google.appengine.ext import db
|
||||||
from google.appengine.ext import webapp
|
from google.appengine.ext import webapp
|
||||||
from google.appengine.ext.webapp import template
|
from google.appengine.ext.webapp import template
|
||||||
from google.appengine.ext.webapp.util import run_wsgi_app
|
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 binascii
|
||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
@ -21,6 +27,7 @@ import re
|
|||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
import urllib2
|
import urllib2
|
||||||
|
import sets
|
||||||
|
|
||||||
# Storage model for package info recorded on server.
|
# Storage model for package info recorded on server.
|
||||||
# Just path, count, and time of last install.
|
# Just path, count, and time of last install.
|
||||||
@ -30,6 +37,15 @@ class Package(db.Model):
|
|||||||
count = db.IntegerProperty()
|
count = db.IntegerProperty()
|
||||||
last_install = db.DateTimeProperty()
|
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_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_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_.\-]+$')
|
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.set_status(500)
|
||||||
self.response.out.write('not ok')
|
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():
|
def main():
|
||||||
app = webapp.WSGIApplication([('/package', PackagePage)], debug=True)
|
app = webapp.WSGIApplication([
|
||||||
|
('/package', PackagePage),
|
||||||
|
('/project.*', ProjectPage),
|
||||||
|
], debug=True)
|
||||||
run_wsgi_app(app)
|
run_wsgi_app(app)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
45
misc/dashboard/godashboard/project-edit.html
Normal file
45
misc/dashboard/godashboard/project-edit.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
|
||||||
|
<script>
|
||||||
|
google.load("jquery", "1");
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="/static/jquery.autocomplete.min.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/static/jquery.autocomplete.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form action="/project/edit?orig_name={{p.name}}" method="POST">
|
||||||
|
Name:<br/>
|
||||||
|
<input type="text" name="name" value="{{p.name|escape}}"><br/>
|
||||||
|
Description:<br/>
|
||||||
|
<input type="text" name="descr" value="{{p.descr|escape}}"><br/>
|
||||||
|
Category:<br/>
|
||||||
|
<input type="text" id="cats" name="category" value="{{p.category|escape}}"><br/>
|
||||||
|
Tags: (comma-separated)<br/>
|
||||||
|
<input type="text" id="tags" name="tags" value="{{tags}}"><br/>
|
||||||
|
Web URL:<br/>
|
||||||
|
<input type="text" name="web_url" value="{{p.web_url|escape}}"><br/>
|
||||||
|
Package URL: (to link to a goinstall'd package)<br/>
|
||||||
|
<input type="text" name="package" value="{{p.package.path|escape}}"><br/>
|
||||||
|
Approved: <input type="checkbox" name="approved" value="1" {% if p.approved %}checked{% endif %}><br/>
|
||||||
|
<br/>
|
||||||
|
<input type="submit" name="do" value="Save">
|
||||||
|
<input type="submit" name="do" value="Delete" onClick="javascript:return confirm('Delete this?');">
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
var tags = [
|
||||||
|
{% for t in taglist %}
|
||||||
|
"{{t}}"{% if not forloop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
];
|
||||||
|
var cats = [
|
||||||
|
{% for c in catlist %}
|
||||||
|
"{{c}}"{% if not forloop.last %},{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
];
|
||||||
|
|
||||||
|
$('#tags').autocomplete(tags);
|
||||||
|
$('#cats').autocomplete(cats);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
9
misc/dashboard/godashboard/project-notify.txt
Normal file
9
misc/dashboard/godashboard/project-notify.txt
Normal file
@ -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}}
|
||||||
|
|
86
misc/dashboard/godashboard/project.html
Normal file
86
misc/dashboard/godashboard/project.html
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Projects - Go Dashboard</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="static/style.css">
|
||||||
|
<style>
|
||||||
|
.unapproved a.name { color: red }
|
||||||
|
.tag { font-size: 0.8em; color: #666 }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<ul class="menu">
|
||||||
|
<li><a href="/">Build Status</a></li>
|
||||||
|
<li><a href="/package">Packages</a></li>
|
||||||
|
<li>Projects</li>
|
||||||
|
<li><a href="/benchmarks">Benchmarks</a></li>
|
||||||
|
<li><a href="http://golang.org/">golang.org</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1>Go Dashboard</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
These are external projects and not endorsed or supported by the Go project.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Projects</h2>
|
||||||
|
|
||||||
|
<div class="submit">
|
||||||
|
<h3>Submit a Project</h3>
|
||||||
|
<p>
|
||||||
|
Using this form you can submit a project to be included in the list.
|
||||||
|
</p>
|
||||||
|
<form action="/project" method="POST">
|
||||||
|
<table>
|
||||||
|
<tr><td>Name:<td><input type="text" name="name">
|
||||||
|
<tr><td>Description:<td><input type="text" name="descr">
|
||||||
|
<tr><td>URL:<td><input type="text" name="web_url">
|
||||||
|
<tr><td> <td><input type="submit" value="Send">
|
||||||
|
{% if submitMsg %}
|
||||||
|
<tr><td class="msg" colspan="2">{{ submitMsg }}</td></tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Filter by tag:
|
||||||
|
{% if tag %}
|
||||||
|
<a href="/project">all</a>
|
||||||
|
{% else %}
|
||||||
|
<b>all</b>
|
||||||
|
{% endif %}
|
||||||
|
{% for t in tags %}
|
||||||
|
{% ifequal t tag %}
|
||||||
|
<b>{{t}}</b>
|
||||||
|
{% else %}
|
||||||
|
<a href="?tag={{t}}">{{t}}</a>
|
||||||
|
{% endifequal %}
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% for r in projects %}
|
||||||
|
{% ifchanged r.category %}
|
||||||
|
{% if not forloop.first %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
<h3>{{r.category}}</h3>
|
||||||
|
<ul>
|
||||||
|
{% endifchanged %}
|
||||||
|
<li{% if not r.approved %} class="unapproved"{% endif %}>
|
||||||
|
{% if admin %}[<a href="/project/edit?name={{r.name}}">edit</a>]{% endif %}
|
||||||
|
<a class="name" href="{{r.web_url}}">{{r.name}}</a> - {{r.descr}}
|
||||||
|
{% for tag in r.tags %}
|
||||||
|
<span class="tag">{{tag}}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</li>
|
||||||
|
{% if forloop.last %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -3,8 +3,8 @@ body {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
h1, h2, ul, table, p {
|
h1, h2, h3, ul.menu, table, p {
|
||||||
padding: 0 0.2em;
|
padding: 0 0.5em;
|
||||||
}
|
}
|
||||||
h1, h2 {
|
h1, h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -19,6 +19,25 @@ h1 {
|
|||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
border-top: 1px solid #ccc;
|
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 {
|
table.alternate {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
Loading…
Reference in New Issue
Block a user