Author: justi9
Date: 2008-09-30 00:12:35 -0400 (Tue, 30 Sep 2008)
New Revision: 2551
Modified:
mgmt/trunk/cumin/python/cumin/__init__.py
mgmt/trunk/cumin/python/cumin/page.py
mgmt/trunk/cumin/python/cumin/page.strings
mgmt/trunk/cumin/python/cumin/user.py
mgmt/trunk/cumin/python/cumin/util.py
mgmt/trunk/cumin/python/cumin/widgets.py
mgmt/trunk/cumin/python/cumin/widgets.strings
Log:
Make CuminPage a common cumin widget, and move it to widgets.py.
Add a home tab to the main view.
Add a change password form.
Fix crypt_password to take a salt.
Modified: mgmt/trunk/cumin/python/cumin/__init__.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/__init__.py 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/__init__.py 2008-09-30 04:12:35 UTC (rev 2551)
@@ -13,7 +13,7 @@
from model import CuminModel, ModelPage
from demo import DemoData
-from page import CuminPage
+from page import MainPage
from stat import StatChartPage
from action import ActionPage
from user import LoginPage
@@ -38,7 +38,7 @@
self.model.data.setCloseListener(closeListener)
- self.main_page = CuminPage(self, "index.html")
+ self.main_page = MainPage(self, "index.html")
self.add_page(self.main_page)
self.set_default_page(self.main_page)
Modified: mgmt/trunk/cumin/python/cumin/page.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/page.py 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/page.py 2008-09-30 04:12:35 UTC (rev 2551)
@@ -10,6 +10,7 @@
from pool import *
from job import *
from system import *
+from user import *
from action import *
from widgets import *
from util import *
@@ -20,16 +21,10 @@
strings = StringCatalog(__file__)
-class CuminPage(Page, ModeSet):
+class MainPage(CuminPage, ModeSet):
def __init__(self, app, name):
- super(CuminPage, self).__init__(app, name)
+ super(MainPage, self).__init__(app, name)
- self.__frames = self.FramesAttribute(app, "frames")
- self.add_attribute(self.__frames)
-
- self.__modal = Attribute(app, "modal")
- self.add_attribute(self.__modal)
-
self.__main = MainFrame(app, "main")
self.add_mode(self.__main)
self.set_default_frame(self.__main)
@@ -38,26 +33,10 @@
frame = self.show_mode(session, self.__main)
return self.set_current_frame(session, frame)
- def save_session(self, session):
- if self.app.debug:
- self.app.debug.sessions.append(session)
-
- def set_modal(self, session, modal):
- self.__modal.set(session, modal)
-
- class FramesAttribute(Attribute):
- def get_default(self, session):
- return list()
-
- def get_frames(self, session):
- return self.__frames.get(session)
-
- def render_class(self, session):
- return self.__modal.get(session) and "modal" or "modeless"
-
def render_title(self, session):
return "MRG Management"
+# XXX merge this into the above
class MainFrame(CuminFrame):
def __init__(self, app, name):
super(MainFrame, self).__init__(app, name)
@@ -71,6 +50,9 @@
self.add_mode(view)
self.set_view_mode(view)
+ self.__password = ChangePasswordForm(app, "password")
+ self.add_mode(self.__password)
+
self.__broker = BrokerFrame(app, "broker")
self.add_mode(self.__broker)
@@ -139,6 +121,10 @@
ccount = self.app.model.count_invocations("OK")
return len(self.app.model.invocations) - pcount - ccount
+ def show_change_password(self, session):
+ frame = self.show_mode(session, self.__password)
+ return self.page.set_current_frame(session, frame)
+
def show_broker(self, session, reg):
assert isinstance(reg, BrokerRegistration)
frame = self.show_mode(session, self.__broker)
@@ -183,9 +169,10 @@
super(MainFrameTabs, self).__init__(app, name)
self.selection = Parameter(app, "sel")
- self.selection.default = "mtab"
+ self.selection.default = "htab"
self.add_parameter(self.selection)
+ self.add_link(self.HomeTab(app, "htab"))
self.add_link(self.MessagingTab(app, "mtab"))
self.add_link(self.GridTab(app, "gtab"))
self.add_link(self.SystemsTab(app, "stab"))
@@ -195,8 +182,18 @@
class Tab(Link):
def render_class(self, session):
- return (self.parent.selection.get(session) == self.name) and
"selected" or "not-selected"
+ return (self.parent.selection.get(session) == self.name) \
+ and "selected" or "not-selected"
+ class HomeTab(Tab):
+ def render_content(self, session):
+ return "Home"
+
+ def edit_session(self, session):
+ self.parent.selection.set(session, self.name)
+ frame = self.page.show_main(session).show_view(session)
+ frame.show_home(session)
+
class MessagingTab(Tab):
def render_content(self, session):
return "Messaging"
@@ -228,6 +225,9 @@
def __init__(self, app, name):
super(MainView, self).__init__(app, name)
+ self.__home = HomeView(app, "home")
+ self.add_mode(self.__home)
+
self.__messaging = MessagingView(app, "msg")
self.add_mode(self.__messaging)
@@ -237,6 +237,9 @@
self.__systems = SystemsView(app, "sys")
self.add_mode(self.__systems)
+ def show_home(self, session):
+ return self.show_mode(session, self.__home)
+
def show_messaging(self, session):
return self.show_mode(session, self.__messaging)
@@ -246,6 +249,22 @@
def show_systems(self, session):
return self.show_mode(session, self.__systems)
+class HomeView(TabbedModeSet):
+ def __init__(self, app, name):
+ super(HomeView, self).__init__(app, name)
+
+ heading = self.Heading(app, "heading")
+ self.add_child(heading)
+
+ def render_change_password_href(self, session):
+ branch = session.branch()
+ self.frame.show_change_password(branch)
+ return branch.marshal()
+
+ class Heading(CuminHeading):
+ def render_title(self, session):
+ return "MRG Management"
+
class MessagingView(TabbedModeSet):
def __init__(self, app, name):
super(MessagingView, self).__init__(app, name)
@@ -296,4 +315,3 @@
class Heading(CuminHeading):
def render_title(self, session):
return "Systems"
-
Modified: mgmt/trunk/cumin/python/cumin/page.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/page.strings 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/page.strings 2008-09-30 04:12:35 UTC (rev 2551)
@@ -1,522 +1,3 @@
-[CuminPage.css]
-body {
- margin: 0;
- padding: 0;
- background-color: #fff;
- font-family: "DejaVu LGC Sans", "Bitstream Vera Sans", "Lucida
Grande",
- "Trebuchet MS", verdana, helvetica, arial, sans-serif;
- font-size: 0.9em;
-}
-
-body.modal {
- background-color: #f7f7f7;
-}
-
-img {
- border: none;
-}
-
-a {
- color: #06c;
- text-decoration: none;
-}
-
-a.selected {
- color: black;
-}
-
-ul {
- list-style: none;
- padding: 0;
- margin: 0;
-}
-
-ul > ul {
- padding: 1em;
-}
-
-h1 {
- font-size: 1.1em;
-}
-
-h2 {
- font-size: 1em;
- margin: 0;
-}
-
-span.none {
- font-style: italic;
- color: #999;
-}
-
-.oblock {
- padding: 0;
- background-color: white;
-}
-
-.iblock {
- margin: 1em 0;
- padding: 0 1em;
-}
-
-.notice {
- margin: 2em;
- padding: 2em;
- border: 1px dotted black;
- width: 66%;
- text-align: center;
-}
-
-ul.actions {
- padding: 0;
- margin: 0 0 1em 0;
- list-style: none;
-}
-
-ul.actions li {
- display: inline;
-}
-
-a.nav:before {
- content: "\00BB \0020";
- font-weight: bold;
- color: #dc9f2e;
-}
-
-div.sactions {
- margin: 0;
- padding: 0.35em 0.75em;
- background-color: #e7e7f7;
-}
-
-div.sactions h2 {
- display: inline;
- font-size: 0.9em;
- font-weight: normal;
- margin: 0 0.5em 0 0;
-}
-
-div.sactions select {
- margin: 0 0.5em 0 0;
-}
-
-div.mobject div.mactions h2 {
- display: inline;
- font-size: 0.9em;
- font-weight: normal;
- margin: 0 0.5em 0 0;
-}
-
-button, ul.actions a, a.action {
- margin: 0;
- border: 1px solid #ddd;
- padding: 0.25em 0.5em 0.15em 0.5em;
- background-color: #f7f7f7;
- color: #000;
- font-size: 0.9em;
-}
-
-button:hover, ul.actions a:hover, a.action:hover {
- background-color: white;
-}
-
-button.disabled {
- color: #666;
- border: 1px solid #EAEAEA;
-}
-button.disabled:hover {
- background-color: #f7f7f7;
-}
-
-ul.mobjects {
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-ul.mobjects li {
- margin: 0;
- border-top: 1px solid #ccc;
- padding: 0.5em 0;
-}
-
-ul.mobjects li:first-child {
- margin: 0;
- border: none;
-}
-
-ul.mobjects li a.action {
- float: right;
-}
-
-ul.mobjects .flags {
- font-size: small;
- font-style: italic;
-}
-
-ul.mobjects .config {
- padding: 0 0 0 2em;
-}
-
-ul.mobjects .status {
- padding: 0 0 0 2em;
- color: #936;
-}
-
-table.mobjects {
- width: 100%;
- border-collapse: collapse;
- margin: 0;
-}
-
-table.mobjects tr {
- border-top: 1px dotted #ccc;
- vertical-align: top;
-}
-
-table.mobjects td {
- padding: 0.35em 0.5em;
-}
-
-table.mobjects th {
- padding: 0.35em 0.5em;
-}
-
-table.mobjects th {
- text-align: left;
- font-weight: normal;
- background-color: #f7f7f7;
-}
-
-form.mform {
- width: 50em;
- border: 1px solid #ddd;
- background-color: #fff;
-}
-
-form.mform fieldset {
- border: none;
- padding: 0.75em;
-}
-
-form.mform .legend {
- font-weight: bold;
-}
-
-form.mform .head, .mform .body, .mform .foot {
- padding: 0.5em 0.75em;
- margin: 0;
-}
-
-form.mform .head {
- font-weight: bold;
- color: white;
- background-color: #685b8a;
- /* background-color: #564979; */
-}
-
-form.mform .head h1 {
- margin: 0;
- font-size: 1.1em;
-}
-
-form.mform .foot {
- text-align: right;
- border-top: 1px solid #ddd;
-}
-
-form.mform .field {
- margin: 0.25em 0;
-}
-
-form.mform .field input {
- border-style: groove;
-}
-
-form.mform ul.errors {
- list-style: none;
- display: block;
- float: right;
- color: red;
- padding: 0.25em 0.5em;
- border: 1px solid red;
- margin: 0 0.5em;
- max-width: 20em;
-}
-
-form.mform button, form.mform a.help {
- margin: 0.5em;
- padding: 0.25em 0.25em 0 0.25em;
-}
-
-form.mform a.help {
- float: left;
- margin: 0.5em;
- padding: 0.25em 0.35em 0 0.5em;
-}
-
-form.mform a.help:before {
- content: url(resource?name=help-20.png);
- padding: 0 0.25em 0 0;
- vertical-align: -35%;
-}
-
-form.mform button.cancel:before {
- content: url(resource?name=cancel-20.png);
- padding: 0 0.25em 0 0;
- vertical-align: -35%;
-}
-
-form.mform button.submit:before {
- content: url(resource?name=submit-20.png);
- padding: 0 0.25em 0 0;
- vertical-align: -35%;
-}
-
-form.mform td {
- vertical-align: top;
-}
-
-ul.radiotabs {
- list-style: none;
- margin: 0 0 1em 0;
- padding: 0;
-}
-
-ul.radiotabs li {
- display: inline;
- padding: 0 0.75em 0 0;
-}
-
-ul.radiotabs li:last-child {
- display: inline;
- margin: 0;
-}
-
-ul.radiotabs li a:before {
- content: url(resource?name=radio-button.png);
- margin: 0 0.5em 0 0;
- vertical-align: -15%;
-}
-
-ul.radiotabs li a.selected {
- color: black;
-}
-
-ul.radiotabs li a.selected:before {
- content: url(resource?name=radio-button-checked.png);
-}
-
-ul.radiotabs li a.disabled:before {
- content: url(resource?name=radio-button.png);
-}
-ul.radiotabs li a.disabled {
- color: #666666;
-}
-
-div.statuslight {
- float: left;
- width: 1em;
- height: 1em;
- margin: 0.25em 1px 0 0;
- padding: 0.15em;
- font-size: 0.8em;
- text-align: center;
- line-height: 1.1em;
-}
-
-div.statuslight.red {
- background-color: #e33;
- color: #fd3;
-}
-
-div.statuslight.yellow {
- background-color: #fd3;
- color: #e33;
-}
-
-div.statuslight.green {
- background-color: #9e9;
-}
-
-pre.code {
- background-color: #f7f7f7;
- padding: 1em;
- border: 1px dotted #ddd;
-}
-
-ul.comma {
- list-style: none;
-}
-
-ul.comma li {
- display: inline;
-}
-
-ul.comma li:after {
- content: ", "
-}
-
-ul.comma li:last-child:after {
- content: ""
-}
-
-ul.slist a:before {
- content: url(resource?name=radio-button.png);
- vertical-align: -10%;
- margin: 0 0.5em 0 0;
-}
-
-ul.slist a.selected:before {
- content: url(resource?name=radio-button-checked.png);
-}
-
-table.twocol {
- width: 100%;
-}
-
-table.twocol > tbody > tr > td {
- width: 50%;
- vertical-align: top;
-}
-
-table.twocol > tbody > tr > td:first-child {
- padding: 0 1.5em 0 0;
-}
-
-table.twocol > tbody > tr > td:last-child {
- padding: 0 0 0 1.5em;
-}
-
-.ralign, table.mobjects .ralign, div.mstatus table .ralign {
- text-align: right;
-}
-
-form.inline {
- display: inline;
-}
-
-span.count {
- font-size: 0.9em;
- color: #999;
-}
-
-.rfloat {
- float: right;
-}
-
-.rclear {
- font-size:0.01em;
- width: 0.01px;
- clear:right;
- line-height:0.01px;
-}
-
-table.Editable input.edit_string,
-table.Editable textarea.edit_bigstring,
-div.inline_help span.edit_string {
- border: 1px solid #CCCCCC;
-}
-table.Editable input.edit_number,
-div.inline_help span.edit_number {
- border: 1px dashed #66CCFF;
-}
-table.Editable input.numeric_error,
-div.inline_help span.numeric_error {
- border: 1px dashed #FF0000;
-}
-table.Editable span.edit_readonly {
- background-color: #FFFFFF;
- color: #444444;
-}
-
-table.Editable input.edit_string,
-table.Editable input.edit_number,
-table.Editable input.numeric_error {
- width: 20em;
-}
-table.Editable textarea.edit_bigstring {
- width: 25em;
-}
-table.Editable div.error {
- color: #FF0000;
- font-size: 0.9em;
-}
-
-form.editform {
- border:0 none !important;
- width: 100% !important;
-}
-
-div.inline_help {
- margin: 1em;
-}
-
-div.inline_help span.edit_string,
-div.inline_help span.edit_number,
-div.inline_help span.numeric_error {
- color: #444444;
- font-weight: normal;
- margin-right: 0.5em;
- padding:0.05em 0.2em;
-}
-div.inline_help h2 {
- margin-right: 1em;
-}
-
-span.prop_example {
- font-size: 0.9em;
-}
-span.prop_example:before {
- content: "Example:";
- padding-right: 0.25em;
-}
-
-[CuminPage.javascript]
-var cumin;
-
-(function() {
- cumin = new Cumin();
-
- function Cumin() {
- this.modelListeners = new Object();
- this.objectListeners = new Object();
-
- this.runModelListeners = function(model) {
- for (var id in this.modelListeners) {
- this.modelListeners[id](id, model);
- }
- }
-
- this.runObjectListeners = function(object) {
- for (var id in this.objectListeners) {
- this.objectListeners[id](id, object);
- }
- }
- }
-}())
-function addEvent(obj, event_type, funct) {
- if (obj.addEventListener)
- obj.addEventListener(event_type, funct, false);
- else if (obj.attachEvent)
- obj.attachEvent("on"+event_type, funct);
-}
-[CuminPage.html]
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html
xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
- <head>
- <title>{title}</title>
- <link rel="stylesheet" type="text/css"
href="cumin.css"/>
- <link rel="shortcut icon" href="resource?name=favicon.ico"
type="image/x-icon"/>
- <script src="resource?name=wooly.js"
type="text/javascript"> </script>
- <script src="cumin.js" type="text/javascript">
</script>
- </head>
- <body class="{class}">
- {mode}
- </body>
-</html>
-
[MainFrame.css]
#head {
padding: 0;
@@ -692,11 +173,20 @@
[MainFrame.frame_html]
<li><a href="{frame_href}">{frame_title}</a></li>
-[MessagingView.css]
-img#msg_logo {
- margin: 0 0 1em 0;
-}
+[HomeView.html]
+<script type="text/javascript">
+<![CDATA[
+ wooly.setIntervalUpdate("{data_url}", updateMain, 3000);
+]]>
+</script>
+<div class="oblock">
+ {heading}
+ <ul class="actions">
+ <a class="nav" href="{change_password_href}">Change
Password</a>
+ </ul>
+</div>
+
[MessagingView.html]
<script type="text/javascript">
<![CDATA[
Modified: mgmt/trunk/cumin/python/cumin/user.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/user.py 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/user.py 2008-09-30 04:12:35 UTC (rev 2551)
@@ -120,3 +120,80 @@
class Submit(FormButton):
def render_content(self, session):
return "Submit"
+
+class ChangePasswordForm(CuminFieldForm):
+ def __init__(self, app, name):
+ super(ChangePasswordForm, self).__init__(app, name)
+
+ self.__errors = self.Errors(app, "errors")
+ self.add_attribute(self.__errors)
+
+ self.__current = self.Current(app, "current", self)
+ self.add_field(self.__current)
+ self.__current.get_input().set_size(20)
+
+ self.__new0 = self.New0(app, "new0", self)
+ self.add_field(self.__new0)
+ self.__new0.get_input().set_size(20)
+
+ self.__new1 = self.New1(app, "new1", self)
+ self.add_field(self.__new1)
+ self.__new1.get_input().set_size(20)
+
+ def process_cancel(self, session):
+ branch = session.branch()
+ self.page.show_main(branch).show_view(branch)
+ self.page.set_redirect_url(session, branch.marshal())
+
+ def process_submit(self, session):
+ curr = self.__current.get(session)
+ new0 = self.__new0.get(session)
+ new1 = self.__new1.get(session)
+
+ errors = self.validate(session)
+
+ subject = session.user_session.subject
+ crypted = subject.password
+
+ if crypt_password(curr, crypted) != crypted:
+ msg = "The current password you entered is incorrect"
+ self.__errors.get(session).append(msg)
+ errors.append(msg)
+
+ if new0 != new1:
+ msg = "The new passwords do not match"
+ self.__errors.get(session).append(msg)
+ errors.append(msg)
+
+ if not errors:
+ subject.password = crypt_password(new0)
+ subject.syncUpdate()
+
+ self.process_cancel(session)
+
+ def render_title(self, session):
+ return "Change Password"
+
+ def render_form_error(self, session):
+ errors = self.__errors.get(session)
+
+ if errors:
+ return "<ul class=\"errors\" style=\"margin:0;
float:left;\"><li>" + \
+ ("</li><li>".join(errors)) + \
+ "</li></ul>"
+
+ class Current(PasswordField):
+ def render_title(self, session):
+ return "Current Password"
+
+ class New0(PasswordField):
+ def render_title(self, session):
+ return "New Password"
+
+ class New1(PasswordField):
+ def render_title(self, session):
+ return "Repeat New Password"
+
+ class Errors(Attribute):
+ def get_default(self, session):
+ return list()
Modified: mgmt/trunk/cumin/python/cumin/util.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/util.py 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/util.py 2008-09-30 04:12:35 UTC (rev 2551)
@@ -202,5 +202,8 @@
password_chars =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-def crypt_password(password):
- return crypt(password, "".join(sample(password_chars, 2)))
+def crypt_password(password, salt=None):
+ if not salt:
+ salt = "".join(sample(password_chars, 2))
+
+ return crypt(password, salt)
Modified: mgmt/trunk/cumin/python/cumin/widgets.py
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.py 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/widgets.py 2008-09-30 04:12:35 UTC (rev 2551)
@@ -14,9 +14,35 @@
import time
-
strings = StringCatalog(__file__)
+class CuminPage(Page):
+ def __init__(self, app, name):
+ super(CuminPage, self).__init__(app, name)
+
+ self.__frames = self.FramesAttribute(app, "frames")
+ self.add_attribute(self.__frames)
+
+ self.__modal = Attribute(app, "modal")
+ self.add_attribute(self.__modal)
+
+ def save_session(self, session):
+ if self.app.debug:
+ self.app.debug.sessions.append(session)
+
+ def set_modal(self, session, modal):
+ self.__modal.set(session, modal)
+
+ def get_frames(self, session):
+ return self.__frames.get(session)
+
+ def render_class(self, session):
+ return self.__modal.get(session) and "modal" or "modeless"
+
+ class FramesAttribute(Attribute):
+ def get_default(self, session):
+ return list()
+
class CuminFrame(Frame, ModeSet):
def __init__(self, app, name):
super(CuminFrame, self).__init__(app, name)
@@ -128,7 +154,7 @@
class CuminForm(Form):
def __init__(self, app, name):
super(CuminForm, self).__init__(app, name)
-
+
self.__cancel = self.Cancel(app, "cancel", self)
self.__cancel.set_tab_index(201)
self.add_child(self.__cancel)
Modified: mgmt/trunk/cumin/python/cumin/widgets.strings
===================================================================
--- mgmt/trunk/cumin/python/cumin/widgets.strings 2008-09-29 13:35:20 UTC (rev 2550)
+++ mgmt/trunk/cumin/python/cumin/widgets.strings 2008-09-30 04:12:35 UTC (rev 2551)
@@ -1,3 +1,523 @@
+[CuminPage.css]
+body {
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ font-family: "DejaVu LGC Sans", "Bitstream Vera Sans", "Lucida
Grande",
+ "Trebuchet MS", verdana, helvetica, arial, sans-serif;
+ font-size: 0.9em;
+}
+
+body.modal {
+ background-color: #f7f7f7;
+}
+
+img {
+ border: none;
+}
+
+a {
+ color: #06c;
+ text-decoration: none;
+}
+
+a.selected {
+ color: black;
+}
+
+ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+ul > ul {
+ padding: 1em;
+}
+
+h1 {
+ font-size: 1.1em;
+}
+
+h2 {
+ font-size: 1em;
+ margin: 0;
+}
+
+span.none {
+ font-style: italic;
+ color: #999;
+}
+
+.oblock {
+ padding: 0;
+ background-color: white;
+}
+
+.iblock {
+ margin: 1em 0;
+ padding: 0 1em;
+}
+
+.notice {
+ margin: 2em;
+ padding: 2em;
+ border: 1px dotted black;
+ width: 66%;
+ text-align: center;
+}
+
+ul.actions {
+ padding: 0;
+ margin: 0 0 1em 0;
+ list-style: none;
+}
+
+ul.actions li {
+ display: inline;
+}
+
+a.nav:before {
+ content: "\00BB \0020";
+ font-weight: bold;
+ color: #dc9f2e;
+}
+
+div.sactions {
+ margin: 0;
+ padding: 0.35em 0.75em;
+ background-color: #e7e7f7;
+}
+
+div.sactions h2 {
+ display: inline;
+ font-size: 0.9em;
+ font-weight: normal;
+ margin: 0 0.5em 0 0;
+}
+
+div.sactions select {
+ margin: 0 0.5em 0 0;
+}
+
+div.mobject div.mactions h2 {
+ display: inline;
+ font-size: 0.9em;
+ font-weight: normal;
+ margin: 0 0.5em 0 0;
+}
+
+button, ul.actions a, a.action {
+ margin: 0;
+ border: 1px solid #ddd;
+ padding: 0.25em 0.5em 0.15em 0.5em;
+ background-color: #f7f7f7;
+ color: #000;
+ font-size: 0.9em;
+}
+
+button:hover, ul.actions a:hover, a.action:hover {
+ background-color: white;
+}
+
+button.disabled {
+ color: #666;
+ border: 1px solid #EAEAEA;
+}
+button.disabled:hover {
+ background-color: #f7f7f7;
+}
+
+ul.mobjects {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+ul.mobjects li {
+ margin: 0;
+ border-top: 1px solid #ccc;
+ padding: 0.5em 0;
+}
+
+ul.mobjects li:first-child {
+ margin: 0;
+ border: none;
+}
+
+ul.mobjects li a.action {
+ float: right;
+}
+
+ul.mobjects .flags {
+ font-size: small;
+ font-style: italic;
+}
+
+ul.mobjects .config {
+ padding: 0 0 0 2em;
+}
+
+ul.mobjects .status {
+ padding: 0 0 0 2em;
+ color: #936;
+}
+
+table.mobjects {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 0;
+}
+
+table.mobjects tr {
+ border-top: 1px dotted #ccc;
+ vertical-align: top;
+}
+
+table.mobjects td {
+ padding: 0.35em 0.5em;
+}
+
+table.mobjects th {
+ padding: 0.35em 0.5em;
+}
+
+table.mobjects th {
+ text-align: left;
+ font-weight: normal;
+ background-color: #f7f7f7;
+}
+
+form.mform {
+ width: 50em;
+ border: 1px solid #ddd;
+ background-color: #fff;
+}
+
+form.mform fieldset {
+ border: none;
+ padding: 0.75em;
+}
+
+form.mform .legend {
+ font-weight: bold;
+}
+
+form.mform .head, .mform .body, .mform .foot {
+ padding: 0.5em 0.75em;
+ margin: 0;
+}
+
+form.mform .head {
+ font-weight: bold;
+ color: white;
+ background-color: #685b8a;
+ /* background-color: #564979; */
+}
+
+form.mform .head h1 {
+ margin: 0;
+ font-size: 1.1em;
+}
+
+form.mform .foot {
+ text-align: right;
+ border-top: 1px solid #ddd;
+}
+
+form.mform .field {
+ margin: 0.25em 0;
+}
+
+form.mform .field input {
+ border-style: groove;
+}
+
+form.mform ul.errors {
+ list-style: none;
+ display: block;
+ float: right;
+ color: red;
+ padding: 0.25em 0.5em;
+ border: 1px solid red;
+ margin: 0 0.5em;
+ max-width: 20em;
+}
+
+form.mform button, form.mform a.help {
+ margin: 0.5em;
+ padding: 0.25em 0.25em 0 0.25em;
+}
+
+form.mform a.help {
+ float: left;
+ margin: 0.5em;
+ padding: 0.25em 0.35em 0 0.5em;
+}
+
+form.mform a.help:before {
+ content: url(resource?name=help-20.png);
+ padding: 0 0.25em 0 0;
+ vertical-align: -35%;
+}
+
+form.mform button.cancel:before {
+ content: url(resource?name=cancel-20.png);
+ padding: 0 0.25em 0 0;
+ vertical-align: -35%;
+}
+
+form.mform button.submit:before {
+ content: url(resource?name=submit-20.png);
+ padding: 0 0.25em 0 0;
+ vertical-align: -35%;
+}
+
+form.mform td {
+ vertical-align: top;
+}
+
+ul.radiotabs {
+ list-style: none;
+ margin: 0 0 1em 0;
+ padding: 0;
+}
+
+ul.radiotabs li {
+ display: inline;
+ padding: 0 0.75em 0 0;
+}
+
+ul.radiotabs li:last-child {
+ display: inline;
+ margin: 0;
+}
+
+ul.radiotabs li a:before {
+ content: url(resource?name=radio-button.png);
+ margin: 0 0.5em 0 0;
+ vertical-align: -15%;
+}
+
+ul.radiotabs li a.selected {
+ color: black;
+}
+
+ul.radiotabs li a.selected:before {
+ content: url(resource?name=radio-button-checked.png);
+}
+
+ul.radiotabs li a.disabled:before {
+ content: url(resource?name=radio-button.png);
+}
+ul.radiotabs li a.disabled {
+ color: #666666;
+}
+
+div.statuslight {
+ float: left;
+ width: 1em;
+ height: 1em;
+ margin: 0.25em 1px 0 0;
+ padding: 0.15em;
+ font-size: 0.8em;
+ text-align: center;
+ line-height: 1.1em;
+}
+
+div.statuslight.red {
+ background-color: #e33;
+ color: #fd3;
+}
+
+div.statuslight.yellow {
+ background-color: #fd3;
+ color: #e33;
+}
+
+div.statuslight.green {
+ background-color: #9e9;
+}
+
+pre.code {
+ background-color: #f7f7f7;
+ padding: 1em;
+ border: 1px dotted #ddd;
+}
+
+ul.comma {
+ list-style: none;
+}
+
+ul.comma li {
+ display: inline;
+}
+
+ul.comma li:after {
+ content: ", "
+}
+
+ul.comma li:last-child:after {
+ content: ""
+}
+
+ul.slist a:before {
+ content: url(resource?name=radio-button.png);
+ vertical-align: -10%;
+ margin: 0 0.5em 0 0;
+}
+
+ul.slist a.selected:before {
+ content: url(resource?name=radio-button-checked.png);
+}
+
+table.twocol {
+ width: 100%;
+}
+
+table.twocol > tbody > tr > td {
+ width: 50%;
+ vertical-align: top;
+}
+
+table.twocol > tbody > tr > td:first-child {
+ padding: 0 1.5em 0 0;
+}
+
+table.twocol > tbody > tr > td:last-child {
+ padding: 0 0 0 1.5em;
+}
+
+.ralign, table.mobjects .ralign, div.mstatus table .ralign {
+ text-align: right;
+}
+
+form.inline {
+ display: inline;
+}
+
+span.count {
+ font-size: 0.9em;
+ color: #999;
+}
+
+.rfloat {
+ float: right;
+}
+
+.rclear {
+ font-size:0.01em;
+ width: 0.01px;
+ clear:right;
+ line-height:0.01px;
+}
+
+table.Editable input.edit_string,
+table.Editable textarea.edit_bigstring,
+div.inline_help span.edit_string {
+ border: 1px solid #CCCCCC;
+}
+table.Editable input.edit_number,
+div.inline_help span.edit_number {
+ border: 1px dashed #66CCFF;
+}
+table.Editable input.numeric_error,
+div.inline_help span.numeric_error {
+ border: 1px dashed #FF0000;
+}
+table.Editable span.edit_readonly {
+ background-color: #FFFFFF;
+ color: #444444;
+}
+
+table.Editable input.edit_string,
+table.Editable input.edit_number,
+table.Editable input.numeric_error {
+ width: 20em;
+}
+table.Editable textarea.edit_bigstring {
+ width: 25em;
+}
+table.Editable div.error {
+ color: #FF0000;
+ font-size: 0.9em;
+}
+
+form.editform {
+ border:0 none !important;
+ width: 100% !important;
+}
+
+div.inline_help {
+ margin: 1em;
+}
+
+div.inline_help span.edit_string,
+div.inline_help span.edit_number,
+div.inline_help span.numeric_error {
+ color: #444444;
+ font-weight: normal;
+ margin-right: 0.5em;
+ padding:0.05em 0.2em;
+}
+div.inline_help h2 {
+ margin-right: 1em;
+}
+
+span.prop_example {
+ font-size: 0.9em;
+}
+span.prop_example:before {
+ content: "Example:";
+ padding-right: 0.25em;
+}
+
+[CuminPage.javascript]
+var cumin;
+
+(function() {
+ cumin = new Cumin();
+
+ function Cumin() {
+ this.modelListeners = new Object();
+ this.objectListeners = new Object();
+
+ this.runModelListeners = function(model) {
+ for (var id in this.modelListeners) {
+ this.modelListeners[id](id, model);
+ }
+ }
+
+ this.runObjectListeners = function(object) {
+ for (var id in this.objectListeners) {
+ this.objectListeners[id](id, object);
+ }
+ }
+ }
+}())
+function addEvent(obj, event_type, funct) {
+ if (obj.addEventListener)
+ obj.addEventListener(event_type, funct, false);
+ else if (obj.attachEvent)
+ obj.attachEvent("on"+event_type, funct);
+}
+
+[CuminPage.html]
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html
xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
+ <head>
+ <title>{title}</title>
+ <link rel="stylesheet" type="text/css"
href="cumin.css"/>
+ <link rel="shortcut icon" href="resource?name=favicon.ico"
type="image/x-icon"/>
+ <script src="resource?name=wooly.js"
type="text/javascript"> </script>
+ <script src="cumin.js" type="text/javascript">
</script>
+ </head>
+ <body class="{class}">
+ {mode}
+ </body>
+</html>
+
[BindingSet.sql]
select
b.id,