galaxy-dev
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
August 2009
- 12 participants
- 156 discussions
details: http://www.bx.psu.edu/hg/galaxy/rev/899a830754a3
changeset: 2620:899a830754a3
user: James Taylor <james(a)jamestaylor.org>
date: Mon Aug 24 16:38:24 2009 -0400
description:
Message for page change script
1 file(s) affected in this change:
lib/galaxy/model/migrate/versions/0014_pages.py
diffs (21 lines):
diff -r 3ca815c4be07 -r 899a830754a3 lib/galaxy/model/migrate/versions/0014_pages.py
--- a/lib/galaxy/model/migrate/versions/0014_pages.py Mon Aug 24 15:55:39 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0014_pages.py Mon Aug 24 16:38:24 2009 -0400
@@ -1,3 +1,9 @@
+"""
+Migration script to add support for "Pages".
+ 1) Creates Page and PageRevision tables
+ 2) Adds username column to User table
+"""
+
from sqlalchemy import *
from migrate import *
from migrate.changeset import *
@@ -31,6 +37,7 @@
)
def upgrade():
+ print __doc__
metadata.reflect()
try:
Page_table.create()
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/4ff4be2f436f
changeset: 2616:4ff4be2f436f
user: James Taylor <james(a)jamestaylor.org>
date: Mon Aug 24 15:27:27 2009 -0400
description:
Cache defeat for css/js
3 file(s) affected in this change:
lib/galaxy/web/framework/helpers/__init__.py
templates/base.mako
templates/workflow/editor.mako
diffs (70 lines):
diff -r 168a6ef6a99a -r 4ff4be2f436f lib/galaxy/web/framework/helpers/__init__.py
--- a/lib/galaxy/web/framework/helpers/__init__.py Sun Aug 23 17:13:04 2009 -0400
+++ b/lib/galaxy/web/framework/helpers/__init__.py Mon Aug 24 15:27:27 2009 -0400
@@ -27,14 +27,17 @@
Take a list of stylesheet names (no extension) and return appropriate string
of link tags.
- TODO: This has a hardcoded "?v=1" to defeat caching. This should be done
+ TODO: This has a hardcoded "?v=2" to defeat caching. This should be done
in a better way.
"""
- return "\n".join( [ stylesheet_link_tag( "/static/style/" + name + ".css?v=1" ) for name in args ] )
+ return "\n".join( [ stylesheet_link_tag( "/static/style/" + name + ".css?v=2" ) for name in args ] )
def js( *args ):
"""
Take a list of javascript names (no extension) and return appropriate
string of script tags.
+
+ TODO: This has a hardcoded "?v=2" to defeat caching. This should be done
+ in a better way.
"""
- return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js" ) for name in args ] )
\ No newline at end of file
+ return "\n".join( [ javascript_include_tag( "/static/scripts/" + name + ".js?v=2" ) for name in args ] )
diff -r 168a6ef6a99a -r 4ff4be2f436f templates/base.mako
--- a/templates/base.mako Sun Aug 23 17:13:04 2009 -0400
+++ b/templates/base.mako Mon Aug 24 15:27:27 2009 -0400
@@ -27,9 +27,6 @@
## <!--[if lt IE 7]>
## <script type='text/javascript' src="/static/scripts/IE7.js"> </script>
## <![endif]-->
- <script type="text/javascript" src="${h.url_for('/static/scripts/jquery.js')}"></script>
- <script type="text/javascript" src="${h.url_for('/static/scripts/galaxy.base.js')}"></script>
-
-
+ ${h.js( "jquery", "galaxy.base" )}
</%def>
diff -r 168a6ef6a99a -r 4ff4be2f436f templates/workflow/editor.mako
--- a/templates/workflow/editor.mako Sun Aug 23 17:13:04 2009 -0400
+++ b/templates/workflow/editor.mako Mon Aug 24 15:27:27 2009 -0400
@@ -25,17 +25,17 @@
<!--[if IE]>
<script type='text/javascript' src="${h.url_for('/static/scripts/excanvas.js')}"> </script>
<![endif]-->
- <script type='text/javascript' src="${h.url_for('/static/scripts/jquery.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.drag.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.drop.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.hover.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/jquery.form.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/jquery.jstore-all.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/json2.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/galaxy.base.js')}"> </script>
- <script type='text/javascript' src="${h.url_for('/static/scripts/galaxy.workflow_editor.canvas.js')}"> </script>
-
+ ${h.js( "jquery",
+ "jquery.event.drag",
+ "jquery.event.drop",
+ "jquery.event.hover",
+ "jquery.form",
+ "jquery.jstore-all",
+ "json2",
+ "galaxy.base",
+ "galaxy.workflow_editor.canvas" )}
+
<!--[if lt IE 7]>
<script type='text/javascript'>
window.lt_ie_7 = true;
1
0
25 Aug '09
details: http://www.bx.psu.edu/hg/galaxy/rev/9c325f3656b5
changeset: 2617:9c325f3656b5
user: James Taylor <james(a)jamestaylor.org>
date: Mon Aug 24 15:53:17 2009 -0400
description:
Support for user defined pages. Page content is stripped down XHTML. Pages can be edited using a WYM editor, and have stable public urls. This is VERY PRELIMINARY, but functional
67 file(s) affected in this change:
lib/galaxy/model/__init__.py
lib/galaxy/model/mapping.py
lib/galaxy/model/migrate/versions/0014_pages.py
lib/galaxy/util/sanitize_html.py
lib/galaxy/web/__init__.py
lib/galaxy/web/buildapp.py
lib/galaxy/web/controllers/page.py
lib/galaxy/web/controllers/user.py
lib/galaxy/web/framework/helpers/grids.py
static/scripts/jquery.wymeditor.js
static/wymeditor/iframe/default/lbl-blockquote.png
static/wymeditor/iframe/default/lbl-h1.png
static/wymeditor/iframe/default/lbl-h2.png
static/wymeditor/iframe/default/lbl-h3.png
static/wymeditor/iframe/default/lbl-h4.png
static/wymeditor/iframe/default/lbl-h5.png
static/wymeditor/iframe/default/lbl-h6.png
static/wymeditor/iframe/default/lbl-p.png
static/wymeditor/iframe/default/lbl-pre.png
static/wymeditor/iframe/default/wymiframe.css
static/wymeditor/iframe/default/wymiframe.html
static/wymeditor/iframe/galaxy/lbl-blockquote.png
static/wymeditor/iframe/galaxy/lbl-h1.png
static/wymeditor/iframe/galaxy/lbl-h2.png
static/wymeditor/iframe/galaxy/lbl-h3.png
static/wymeditor/iframe/galaxy/lbl-h4.png
static/wymeditor/iframe/galaxy/lbl-h5.png
static/wymeditor/iframe/galaxy/lbl-h6.png
static/wymeditor/iframe/galaxy/lbl-p.png
static/wymeditor/iframe/galaxy/lbl-pre.png
static/wymeditor/iframe/galaxy/wymiframe.css
static/wymeditor/iframe/galaxy/wymiframe.html
static/wymeditor/lang/bg.js
static/wymeditor/lang/ca.js
static/wymeditor/lang/cs.js
static/wymeditor/lang/de.js
static/wymeditor/lang/en.js
static/wymeditor/lang/es.js
static/wymeditor/lang/fa.js
static/wymeditor/lang/fi.js
static/wymeditor/lang/fr.js
static/wymeditor/lang/he.js
static/wymeditor/lang/hr.js
static/wymeditor/lang/hu.js
static/wymeditor/lang/it.js
static/wymeditor/lang/nb.js
static/wymeditor/lang/nl.js
static/wymeditor/lang/nn.js
static/wymeditor/lang/pl.js
static/wymeditor/lang/pt-br.js
static/wymeditor/lang/pt.js
static/wymeditor/lang/ru.js
static/wymeditor/lang/sv.js
static/wymeditor/lang/tr.js
static/wymeditor/lang/zh_cn.js
static/wymeditor/skins/default/icons.png
static/wymeditor/skins/default/skin.css
static/wymeditor/skins/default/skin.js
static/wymeditor/skins/galaxy/icons.png
static/wymeditor/skins/galaxy/skin.css
static/wymeditor/skins/galaxy/skin.js
templates/grid.mako
templates/page/create.mako
templates/page/display.mako
templates/page/editor.mako
templates/page/index.mako
templates/user/index.mako
diffs (truncated from 7896 to 3000 lines):
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Mon Aug 24 15:27:27 2009 -0400
+++ b/lib/galaxy/model/__init__.py Mon Aug 24 15:53:17 2009 -0400
@@ -33,6 +33,7 @@
self.external = False
self.deleted = False
self.purged = False
+ self.username = None
# Relationships
self.histories = []
@@ -1118,7 +1119,20 @@
self.country+'<br/>'+ \
'Phone: '+self.phone
+class Page( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.title = None
+ self.slug = None
+ self.latest_revision_id = None
+ self.revisions = []
+class PageRevision( object ):
+ def __init__( self ):
+ self.user = None
+ self.title = None
+ self.content = None
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Mon Aug 24 15:27:27 2009 -0400
+++ b/lib/galaxy/model/mapping.py Mon Aug 24 15:53:17 2009 -0400
@@ -42,6 +42,7 @@
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "email", TrimmedString( 255 ), nullable=False ),
+ Column( "username", TrimmedString( 255 ), index=True, unique=True ),
Column( "password", TrimmedString( 40 ), nullable=False ),
Column( "external", Boolean, default=False ),
Column( "deleted", Boolean, index=True, default=False ),
@@ -523,6 +524,26 @@
Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ),
Column( "comment", TEXT ) )
+Page.table = Table( "page", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
+ Column( "latest_revision_id", Integer,
+ ForeignKey( "page_revision.id", use_alter=True, name='page_latest_revision_id_fk' ), index=True ),
+ Column( "title", TEXT ),
+ Column( "slug", TEXT, unique=True, index=True ),
+ )
+
+PageRevision.table = Table( "page_revision", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "page_id", Integer, ForeignKey( "page.id" ), index=True, nullable=False ),
+ Column( "title", TEXT ),
+ Column( "content", TEXT )
+ )
+
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
@@ -905,6 +926,18 @@
assign_mapper( context, MetadataFile, MetadataFile.table,
properties=dict( history_dataset=relation( HistoryDatasetAssociation ), library_dataset=relation( LibraryDatasetDatasetAssociation ) ) )
+assign_mapper( context, PageRevision, PageRevision.table )
+
+assign_mapper( context, Page, Page.table,
+ properties=dict( user=relation( User ),
+ revisions=relation( PageRevision, backref='page',
+ cascade="all, delete-orphan",
+ primaryjoin=( Page.table.c.id == PageRevision.table.c.page_id ) ),
+ latest_revision=relation( PageRevision, post_update=True,
+ primaryjoin=( Page.table.c.latest_revision_id == PageRevision.table.c.id ),
+ lazy=False )
+ ) )
+
def db_next_hid( self ):
"""
Override __next_hid to generate from the database in a concurrency
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/model/migrate/versions/0014_pages.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0014_pages.py Mon Aug 24 15:53:17 2009 -0400
@@ -0,0 +1,55 @@
+from sqlalchemy import *
+from migrate import *
+from migrate.changeset import *
+
+import datetime
+now = datetime.datetime.utcnow
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+
+Page_table = Table( "page", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
+ Column( "latest_revision_id", Integer,
+ ForeignKey( "page_revision.id", use_alter=True, name='page_latest_revision_id_fk' ), index=True ),
+ Column( "title", TEXT ),
+ Column( "slug", TEXT, unique=True, index=True ),
+ )
+
+PageRevision_table = Table( "page_revision", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "page_id", Integer, ForeignKey( "page.id" ), index=True, nullable=False ),
+ Column( "title", TEXT ),
+ Column( "content", TEXT )
+ )
+
+def upgrade():
+ metadata.reflect()
+ try:
+ Page_table.create()
+ except:
+ log.debug( "Could not create page table" )
+ try:
+ PageRevision_table.create()
+ except:
+ log.debug( "Could not create page_revision table" )
+
+ # Add 1 column to the user table
+ User_table = Table( "galaxy_user", metadata, autoload=True )
+ col = Column( 'username', String(255), index=True, unique=True, default=False )
+ col.create( User_table )
+ assert col is User_table.c.username
+
+def downgrade():
+ metadata.reflect()
+ Page_table.drop()
+ PageRevision_table.drop()
+ User_table = Table( "galaxy_user", metadata, autoload=True )
+ User_table.c.username.drop()
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/util/sanitize_html.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/util/sanitize_html.py Mon Aug 24 15:53:17 2009 -0400
@@ -0,0 +1,439 @@
+"""
+HTML Sanitizer (ripped from feedparser)
+"""
+
+import re, sgmllib
+
+# reversable htmlentitydefs mappings for Python 2.2
+try:
+ from htmlentitydefs import name2codepoint, codepoint2name
+except:
+ import htmlentitydefs
+ name2codepoint={}
+ codepoint2name={}
+ for (name,codepoint) in htmlentitydefs.entitydefs.iteritems():
+ if codepoint.startswith('&#'): codepoint=unichr(int(codepoint[2:-1]))
+ name2codepoint[name]=ord(codepoint)
+ codepoint2name[ord(codepoint)]=name
+
+_cp1252 = {
+ unichr(128): unichr(8364), # euro sign
+ unichr(130): unichr(8218), # single low-9 quotation mark
+ unichr(131): unichr( 402), # latin small letter f with hook
+ unichr(132): unichr(8222), # double low-9 quotation mark
+ unichr(133): unichr(8230), # horizontal ellipsis
+ unichr(134): unichr(8224), # dagger
+ unichr(135): unichr(8225), # double dagger
+ unichr(136): unichr( 710), # modifier letter circumflex accent
+ unichr(137): unichr(8240), # per mille sign
+ unichr(138): unichr( 352), # latin capital letter s with caron
+ unichr(139): unichr(8249), # single left-pointing angle quotation mark
+ unichr(140): unichr( 338), # latin capital ligature oe
+ unichr(142): unichr( 381), # latin capital letter z with caron
+ unichr(145): unichr(8216), # left single quotation mark
+ unichr(146): unichr(8217), # right single quotation mark
+ unichr(147): unichr(8220), # left double quotation mark
+ unichr(148): unichr(8221), # right double quotation mark
+ unichr(149): unichr(8226), # bullet
+ unichr(150): unichr(8211), # en dash
+ unichr(151): unichr(8212), # em dash
+ unichr(152): unichr( 732), # small tilde
+ unichr(153): unichr(8482), # trade mark sign
+ unichr(154): unichr( 353), # latin small letter s with caron
+ unichr(155): unichr(8250), # single right-pointing angle quotation mark
+ unichr(156): unichr( 339), # latin small ligature oe
+ unichr(158): unichr( 382), # latin small letter z with caron
+ unichr(159): unichr( 376)} # latin capital letter y with diaeresis
+
+class _BaseHTMLProcessor(sgmllib.SGMLParser):
+ special = re.compile('''[<>'"]''')
+ bare_ampersand = re.compile("&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)")
+ elements_no_end_tag = ['area', 'base', 'basefont', 'br', 'col', 'frame', 'hr',
+ 'img', 'input', 'isindex', 'link', 'meta', 'param']
+
+ def __init__(self, encoding, type):
+ self.encoding = encoding
+ self.type = type
+ ## if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
+ sgmllib.SGMLParser.__init__(self)
+
+ def reset(self):
+ self.pieces = []
+ sgmllib.SGMLParser.reset(self)
+
+ def _shorttag_replace(self, match):
+ tag = match.group(1)
+ if tag in self.elements_no_end_tag:
+ return '<' + tag + ' />'
+ else:
+ return '<' + tag + '></' + tag + '>'
+
+ def parse_starttag(self,i):
+ j=sgmllib.SGMLParser.parse_starttag(self, i)
+ if self.type == 'application/xhtml+xml':
+ if j>2 and self.rawdata[j-2:j]=='/>':
+ self.unknown_endtag(self.lasttag)
+ return j
+
+ def feed(self, data):
+ data = re.compile(r'<!((?!DOCTYPE|--|\[))', re.IGNORECASE).sub(r'<!\1', data)
+ #data = re.sub(r'<(\S+?)\s*?/>', self._shorttag_replace, data) # bug [ 1399464 ] Bad regexp for _shorttag_replace
+ data = re.sub(r'<([^<>\s]+?)\s*/>', self._shorttag_replace, data)
+ data = data.replace(''', "'")
+ data = data.replace('"', '"')
+ if self.encoding and type(data) == type(u''):
+ data = data.encode(self.encoding)
+ sgmllib.SGMLParser.feed(self, data)
+ sgmllib.SGMLParser.close(self)
+
+ def normalize_attrs(self, attrs):
+ if not attrs: return attrs
+ # utility method to be called by descendants
+ attrs = dict([(k.lower(), v) for k, v in attrs]).items()
+ attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
+ attrs.sort()
+ return attrs
+
+ def unknown_starttag(self, tag, attrs):
+ # called for each start tag
+ # attrs is a list of (attr, value) tuples
+ # e.g. for <pre class='screen'>, tag='pre', attrs=[('class', 'screen')]
+ ## if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
+ uattrs = []
+ strattrs=''
+ if attrs:
+ for key, value in attrs:
+ value=value.replace('>','>').replace('<','<').replace('"','"')
+ value = self.bare_ampersand.sub("&", value)
+ # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
+ if type(value) != type(u''):
+ try:
+ value = unicode(value, self.encoding)
+ except:
+ value = unicode(value, 'iso-8859-1')
+ uattrs.append((unicode(key, self.encoding), value))
+ strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs])
+ if self.encoding:
+ try:
+ strattrs=strattrs.encode(self.encoding)
+ except:
+ pass
+ if tag in self.elements_no_end_tag:
+ self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
+ else:
+ self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
+
+ def unknown_endtag(self, tag):
+ # called for each end tag, e.g. for </pre>, tag will be 'pre'
+ # Reconstruct the original end tag.
+ if tag not in self.elements_no_end_tag:
+ self.pieces.append("</%(tag)s>" % locals())
+
+ def handle_charref(self, ref):
+ # called for each character reference, e.g. for ' ', ref will be '160'
+ # Reconstruct the original character reference.
+ if ref.startswith('x'):
+ value = unichr(int(ref[1:],16))
+ else:
+ value = unichr(int(ref))
+
+ if value in _cp1252.keys():
+ self.pieces.append('&#%s;' % hex(ord(_cp1252[value]))[1:])
+ else:
+ self.pieces.append('&#%(ref)s;' % locals())
+
+ def handle_entityref(self, ref):
+ # called for each entity reference, e.g. for '©', ref will be 'copy'
+ # Reconstruct the original entity reference.
+ if name2codepoint.has_key(ref):
+ self.pieces.append('&%(ref)s;' % locals())
+ else:
+ self.pieces.append('&%(ref)s' % locals())
+
+ def handle_data(self, text):
+ # called for each block of plain text, i.e. outside of any tag and
+ # not containing any character or entity references
+ # Store the original text verbatim.
+ ## if _debug: sys.stderr.write('_BaseHTMLProcessor, handle_text, text=%s\n' % text)
+ self.pieces.append(text)
+
+ def handle_comment(self, text):
+ # called for each HTML comment, e.g. <!-- insert Javascript code here -->
+ # Reconstruct the original comment.
+ self.pieces.append('<!--%(text)s-->' % locals())
+
+ def handle_pi(self, text):
+ # called for each processing instruction, e.g. <?instruction>
+ # Reconstruct original processing instruction.
+ self.pieces.append('<?%(text)s>' % locals())
+
+ def handle_decl(self, text):
+ # called for the DOCTYPE, if present, e.g.
+ # <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ # "http://www.w3.org/TR/html4/loose.dtd">
+ # Reconstruct original DOCTYPE
+ self.pieces.append('<!%(text)s>' % locals())
+
+ _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
+ def _scan_name(self, i, declstartpos):
+ rawdata = self.rawdata
+ n = len(rawdata)
+ if i == n:
+ return None, -1
+ m = self._new_declname_match(rawdata, i)
+ if m:
+ s = m.group()
+ name = s.strip()
+ if (i + len(s)) == n:
+ return None, -1 # end of buffer
+ return name.lower(), m.end()
+ else:
+ self.handle_data(rawdata)
+# self.updatepos(declstartpos, i)
+ return None, -1
+
+ def convert_charref(self, name):
+ return '&#%s;' % name
+
+ def convert_entityref(self, name):
+ return '&%s;' % name
+
+ def output(self):
+ '''Return processed HTML as a single string'''
+ return ''.join([str(p) for p in self.pieces])
+
+class _HTMLSanitizer(_BaseHTMLProcessor):
+ acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area', 'article',
+ 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button', 'canvas',
+ 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'command',
+ 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'dir',
+ 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset', 'figure', 'footer',
+ 'font', 'form', 'header', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i',
+ 'img', 'input', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map',
+ 'menu', 'meter', 'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup',
+ 'option', 'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
+ 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong', 'sub',
+ 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot', 'th', 'thead',
+ 'tr', 'tt', 'u', 'ul', 'var', 'video', 'noscript']
+
+ acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
+ 'action', 'align', 'alt', 'autocomplete', 'autofocus', 'axis',
+ 'background', 'balance', 'bgcolor', 'bgproperties', 'border',
+ 'bordercolor', 'bordercolordark', 'bordercolorlight', 'bottompadding',
+ 'cellpadding', 'cellspacing', 'ch', 'challenge', 'char', 'charoff',
+ 'choff', 'charset', 'checked', 'cite', 'class', 'clear', 'color', 'cols',
+ 'colspan', 'compact', 'contenteditable', 'controls', 'coords', 'data',
+ 'datafld', 'datapagesize', 'datasrc', 'datetime', 'default', 'delay',
+ 'dir', 'disabled', 'draggable', 'dynsrc', 'enctype', 'end', 'face', 'for',
+ 'form', 'frame', 'galleryimg', 'gutter', 'headers', 'height', 'hidefocus',
+ 'hidden', 'high', 'href', 'hreflang', 'hspace', 'icon', 'id', 'inputmode',
+ 'ismap', 'keytype', 'label', 'leftspacing', 'lang', 'list', 'longdesc',
+ 'loop', 'loopcount', 'loopend', 'loopstart', 'low', 'lowsrc', 'max',
+ 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'nohref',
+ 'noshade', 'nowrap', 'open', 'optimum', 'pattern', 'ping', 'point-size',
+ 'prompt', 'pqg', 'radiogroup', 'readonly', 'rel', 'repeat-max',
+ 'repeat-min', 'replace', 'required', 'rev', 'rightspacing', 'rows',
+ 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size', 'span', 'src',
+ 'start', 'step', 'summary', 'suppress', 'tabindex', 'target', 'template',
+ 'title', 'toppadding', 'type', 'unselectable', 'usemap', 'urn', 'valign',
+ 'value', 'variable', 'volume', 'vspace', 'vrml', 'width', 'wrap',
+ 'xml:lang']
+
+ unacceptable_elements_with_end_tag = ['script', 'applet', 'style']
+
+ acceptable_css_properties = ['azimuth', 'background-color',
+ 'border-bottom-color', 'border-collapse', 'border-color',
+ 'border-left-color', 'border-right-color', 'border-top-color', 'clear',
+ 'color', 'cursor', 'direction', 'display', 'elevation', 'float', 'font',
+ 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight',
+ 'height', 'letter-spacing', 'line-height', 'overflow', 'pause',
+ 'pause-after', 'pause-before', 'pitch', 'pitch-range', 'richness',
+ 'speak', 'speak-header', 'speak-numeral', 'speak-punctuation',
+ 'speech-rate', 'stress', 'text-align', 'text-decoration', 'text-indent',
+ 'unicode-bidi', 'vertical-align', 'voice-family', 'volume',
+ 'white-space', 'width']
+
+ # survey of common keywords found in feeds
+ acceptable_css_keywords = ['auto', 'aqua', 'black', 'block', 'blue',
+ 'bold', 'both', 'bottom', 'brown', 'center', 'collapse', 'dashed',
+ 'dotted', 'fuchsia', 'gray', 'green', '!important', 'italic', 'left',
+ 'lime', 'maroon', 'medium', 'none', 'navy', 'normal', 'nowrap', 'olive',
+ 'pointer', 'purple', 'red', 'right', 'solid', 'silver', 'teal', 'top',
+ 'transparent', 'underline', 'white', 'yellow']
+
+ valid_css_values = re.compile('^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|' +
+ '\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$')
+
+ mathml_elements = ['annotation', 'annotation-xml', 'maction', 'math',
+ 'merror', 'mfenced', 'mfrac', 'mi', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded',
+ 'mphantom', 'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle',
+ 'msub', 'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
+ 'munderover', 'none', 'semantics']
+
+ mathml_attributes = ['actiontype', 'align', 'columnalign', 'columnalign',
+ 'columnalign', 'close', 'columnlines', 'columnspacing', 'columnspan', 'depth',
+ 'display', 'displaystyle', 'encoding', 'equalcolumns', 'equalrows',
+ 'fence', 'fontstyle', 'fontweight', 'frame', 'height', 'linethickness',
+ 'lspace', 'mathbackground', 'mathcolor', 'mathvariant', 'mathvariant',
+ 'maxsize', 'minsize', 'open', 'other', 'rowalign', 'rowalign', 'rowalign',
+ 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'scriptlevel', 'selection',
+ 'separator', 'separators', 'stretchy', 'width', 'width', 'xlink:href',
+ 'xlink:show', 'xlink:type', 'xmlns', 'xmlns:xlink']
+
+ # svgtiny - foreignObject + linearGradient + radialGradient + stop
+ svg_elements = ['a', 'animate', 'animateColor', 'animateMotion',
+ 'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'foreignObject',
+ 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
+ 'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 'mpath',
+ 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'set', 'stop',
+ 'svg', 'switch', 'text', 'title', 'tspan', 'use']
+
+ # svgtiny + class + opacity + offset + xmlns + xmlns:xlink
+ svg_attributes = ['accent-height', 'accumulate', 'additive', 'alphabetic',
+ 'arabic-form', 'ascent', 'attributeName', 'attributeType',
+ 'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height',
+ 'class', 'color', 'color-rendering', 'content', 'cx', 'cy', 'd', 'dx',
+ 'dy', 'descent', 'display', 'dur', 'end', 'fill', 'fill-opacity',
+ 'fill-rule', 'font-family', 'font-size', 'font-stretch', 'font-style',
+ 'font-variant', 'font-weight', 'from', 'fx', 'fy', 'g1', 'g2',
+ 'glyph-name', 'gradientUnits', 'hanging', 'height', 'horiz-adv-x',
+ 'horiz-origin-x', 'id', 'ideographic', 'k', 'keyPoints', 'keySplines',
+ 'keyTimes', 'lang', 'mathematical', 'marker-end', 'marker-mid',
+ 'marker-start', 'markerHeight', 'markerUnits', 'markerWidth', 'max',
+ 'min', 'name', 'offset', 'opacity', 'orient', 'origin',
+ 'overline-position', 'overline-thickness', 'panose-1', 'path',
+ 'pathLength', 'points', 'preserveAspectRatio', 'r', 'refX', 'refY',
+ 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
+ 'restart', 'rotate', 'rx', 'ry', 'slope', 'stemh', 'stemv',
+ 'stop-color', 'stop-opacity', 'strikethrough-position',
+ 'strikethrough-thickness', 'stroke', 'stroke-dasharray',
+ 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin',
+ 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage',
+ 'target', 'text-anchor', 'to', 'transform', 'type', 'u1', 'u2',
+ 'underline-position', 'underline-thickness', 'unicode', 'unicode-range',
+ 'units-per-em', 'values', 'version', 'viewBox', 'visibility', 'width',
+ 'widths', 'x', 'x-height', 'x1', 'x2', 'xlink:actuate', 'xlink:arcrole',
+ 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type',
+ 'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y', 'y1',
+ 'y2', 'zoomAndPan']
+
+ svg_attr_map = None
+ svg_elem_map = None
+
+ acceptable_svg_properties = [ 'fill', 'fill-opacity', 'fill-rule',
+ 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin',
+ 'stroke-opacity']
+
+ def reset(self):
+ _BaseHTMLProcessor.reset(self)
+ self.unacceptablestack = 0
+ self.mathmlOK = 0
+ self.svgOK = 0
+
+ def unknown_starttag(self, tag, attrs):
+ acceptable_attributes = self.acceptable_attributes
+ keymap = {}
+ if not tag in self.acceptable_elements or self.svgOK:
+ if tag in self.unacceptable_elements_with_end_tag:
+ self.unacceptablestack += 1
+
+ # not otherwise acceptable, perhaps it is MathML or SVG?
+ if tag=='math' and ('xmlns','http://www.w3.org/1998/Math/MathML') in attrs:
+ self.mathmlOK += 1
+ if tag=='svg' and ('xmlns','http://www.w3.org/2000/svg') in attrs:
+ self.svgOK += 1
+
+ # chose acceptable attributes based on tag class, else bail
+ if self.mathmlOK and tag in self.mathml_elements:
+ acceptable_attributes = self.mathml_attributes
+ elif self.svgOK and tag in self.svg_elements:
+ # for most vocabularies, lowercasing is a good idea. Many
+ # svg elements, however, are camel case
+ if not self.svg_attr_map:
+ lower=[attr.lower() for attr in self.svg_attributes]
+ mix=[a for a in self.svg_attributes if a not in lower]
+ self.svg_attributes = lower
+ self.svg_attr_map = dict([(a.lower(),a) for a in mix])
+
+ lower=[attr.lower() for attr in self.svg_elements]
+ mix=[a for a in self.svg_elements if a not in lower]
+ self.svg_elements = lower
+ self.svg_elem_map = dict([(a.lower(),a) for a in mix])
+ acceptable_attributes = self.svg_attributes
+ tag = self.svg_elem_map.get(tag,tag)
+ keymap = self.svg_attr_map
+ elif not tag in self.acceptable_elements:
+ return
+
+ # declare xlink namespace, if needed
+ if self.mathmlOK or self.svgOK:
+ if filter(lambda (n,v): n.startswith('xlink:'),attrs):
+ if not ('xmlns:xlink','http://www.w3.org/1999/xlink') in attrs:
+ attrs.append(('xmlns:xlink','http://www.w3.org/1999/xlink'))
+
+ clean_attrs = []
+ for key, value in self.normalize_attrs(attrs):
+ if key in acceptable_attributes:
+ key=keymap.get(key,key)
+ clean_attrs.append((key,value))
+ elif key=='style':
+ pass
+ ## clean_value = self.sanitize_style(value)
+ ## if clean_value: clean_attrs.append((key,clean_value))
+ _BaseHTMLProcessor.unknown_starttag(self, tag, clean_attrs)
+
+ def unknown_endtag(self, tag):
+ if not tag in self.acceptable_elements:
+ if tag in self.unacceptable_elements_with_end_tag:
+ self.unacceptablestack -= 1
+ if self.mathmlOK and tag in self.mathml_elements:
+ if tag == 'math' and self.mathmlOK: self.mathmlOK -= 1
+ elif self.svgOK and tag in self.svg_elements:
+ tag = self.svg_elem_map.get(tag,tag)
+ if tag == 'svg' and self.svgOK: self.svgOK -= 1
+ else:
+ return
+ _BaseHTMLProcessor.unknown_endtag(self, tag)
+
+ def handle_pi(self, text):
+ pass
+
+ def handle_decl(self, text):
+ pass
+
+ def handle_data(self, text):
+ if not self.unacceptablestack:
+ _BaseHTMLProcessor.handle_data(self, text)
+
+ def sanitize_style(self, style):
+ # disallow urls
+ style=re.compile('url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ',style)
+
+ # gauntlet
+ if not re.match("""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): return ''
+ if not re.match("^(\s*[-\w]+\s*:\s*[^:;]*(;|$))*$", style): return ''
+
+ clean = []
+ for prop,value in re.findall("([-\w]+)\s*:\s*([^:;]*)",style):
+ if not value: continue
+ if prop.lower() in self.acceptable_css_properties:
+ clean.append(prop + ': ' + value + ';')
+ elif prop.split('-')[0].lower() in ['background','border','margin','padding']:
+ for keyword in value.split():
+ if not keyword in self.acceptable_css_keywords and \
+ not self.valid_css_values.match(keyword):
+ break
+ else:
+ clean.append(prop + ': ' + value + ';')
+ elif self.svgOK and prop.lower() in self.acceptable_svg_properties:
+ clean.append(prop + ': ' + value + ';')
+
+ return ' '.join(clean)
+
+
+def sanitize_html(htmlSource, encoding, type):
+ p = _HTMLSanitizer(encoding, type)
+ p.feed(htmlSource)
+ data = p.output()
+ data = data.strip().replace('\r\n', '\n')
+ return data
\ No newline at end of file
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/web/__init__.py
--- a/lib/galaxy/web/__init__.py Mon Aug 24 15:27:27 2009 -0400
+++ b/lib/galaxy/web/__init__.py Mon Aug 24 15:53:17 2009 -0400
@@ -3,4 +3,5 @@
"""
from framework import expose, json, require_login, require_admin, url_for, error, form, FormBuilder
+from framework.base import httpexceptions
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/web/buildapp.py
--- a/lib/galaxy/web/buildapp.py Mon Aug 24 15:27:27 2009 -0400
+++ b/lib/galaxy/web/buildapp.py Mon Aug 24 15:53:17 2009 -0400
@@ -74,6 +74,7 @@
webapp.add_route( '/:controller/:action', action='index' )
webapp.add_route( '/:action', controller='root', action='index' )
webapp.add_route( '/datasets/:dataset_id/:action/:filename', controller='dataset', action='index', dataset_id=None, filename=None)
+ webapp.add_route( '/u/:username/p/:slug', controller='page', action='display_by_username_and_slug' )
webapp.finalize_config()
# Wrap the webapp in some useful middleware
if kwargs.get( 'middleware', True ):
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/web/controllers/page.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/web/controllers/page.py Mon Aug 24 15:53:17 2009 -0400
@@ -0,0 +1,142 @@
+from galaxy.web.base.controller import *
+from galaxy.web.framework.helpers import *
+from galaxy.util.sanitize_html import sanitize_html
+
+import re
+
+VALID_SLUG_RE = re.compile( "^[a-z0-9\-]+$" )
+
+class PageListGrid( grids.Grid ):
+ class URLColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, item ):
+ username = trans.user.username or "???"
+ return username + "/" + item.slug
+ def get_link( self, trans, grid, item ):
+ if trans.user.username:
+ return dict( action='display_by_username_and_slug', username=trans.user.username, slug=item.slug )
+ else:
+ return None
+ # Grid definition
+ use_panels = True
+ title = "Your pages"
+ model_class = model.Page
+ default_sort_key = "-create_time"
+ columns = [
+ grids.GridColumn( "Title", key="title", attach_popup=True ),
+ URLColumn( "Public URL" ),
+ grids.GridColumn( "Created", key="create_time", format=time_ago ),
+ grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
+ ]
+ global_actions = [
+ grids.GridAction( "Add new page", dict( action='create' ) )
+ ]
+ operations = [
+ grids.GridOperation( "View", allow_multiple=False, url_args=dict( action='display') ),
+ grids.GridOperation( "Edit", allow_multiple=False, url_args=dict( action='edit') )
+ ]
+
+class PageController( BaseController ):
+
+ list = PageListGrid()
+
+ @web.expose
+ @web.require_admin
+ def index( self, trans, *args, **kwargs ):
+ grid = self.list( trans, *args, **kwargs )
+ return trans.fill_template( "page/index.mako", grid=grid )
+
+ @web.expose
+ @web.require_admin
+ @web.require_login( "create pages" )
+ def create( self, trans, page_title="", page_slug="" ):
+ """
+ Create a new page
+ """
+ user = trans.get_user()
+ page_title_err = page_slug_err = ""
+ if trans.request.method == "POST":
+ if not page_title:
+ page_title_err = "Page name is required"
+ elif not page_slug:
+ page_slug_err = "Page id is required"
+ elif not VALID_SLUG_RE.match( page_slug ):
+ page_slug_err = "Page identifier must consist of only lowercase letters, numbers, and the '-' character"
+ elif model.Page.filter_by( user=user, slug=page_slug ).first():
+ page_slug_err = "Page id must be unique"
+ else:
+ # Create the new stored workflow
+ page = model.Page()
+ page.title = page_title
+ page.slug = page_slug
+ page.user = user
+ # And the first (empty) workflow revision
+ page_revision = model.PageRevision()
+ page_revision.title = page_title
+ page_revision.page = page
+ page.latest_revision = page_revision
+ page_revision.content = ""
+ # Persist
+ session = trans.sa_session
+ session.save_or_update( page )
+ session.flush()
+ # Display the management page
+ trans.set_message( "Page '%s' created" % page.title )
+ return self.list( trans )
+ return trans.show_form(
+ web.FormBuilder( web.url_for(), "Create new page", submit_text="Submit" )
+ .add_text( "page_title", "Page title", value=page_title, error=page_title_err )
+ .add_text( "page_slug", "Page identifier", value=page_slug, error=page_slug_err,
+ help="""A unique identifier that will be used for
+ public links to this page. A default is generated
+ from the page title, but can be edited. This field
+ must contain only lowercase letters, numbers, and
+ the '-' character.""" ),
+ template="page/create.mako" )
+
+ @web.expose
+ @web.require_admin
+ @web.require_login( "edit pages" )
+ def edit( self, trans, id ):
+ """
+ Render the main page editor interface.
+ """
+ id = trans.security.decode_id( id )
+ page = trans.sa_session.query( model.Page ).get( id )
+ assert page.user == trans.user
+ return trans.fill_template( "page/editor.mako", page=page )
+
+ @web.expose
+ @web.require_admin
+ def save( self, trans, id, content ):
+ id = trans.security.decode_id( id )
+ page = trans.sa_session.query( model.Page ).get( id )
+ assert page.user == trans.user
+ # Sanitize content
+ content = sanitize_html( content, 'utf-8', 'text/html' )
+ # Add a new revision to the page with the provided content
+ page_revision = model.PageRevision()
+ page_revision.title = page.title
+ page_revision.page = page
+ page.latest_revision = page_revision
+ page_revision.content = content
+ trans.sa_session.flush()
+
+ @web.expose
+ @web.require_admin
+ def display( self, trans, id ):
+ id = trans.security.decode_id( id )
+ page = trans.sa_session.query( model.Page ).get( id )
+ return trans.fill_template( "page/display.mako", page=page )
+
+ @web.expose
+ def display_by_username_and_slug( self, trans, username, slug ):
+ session = trans.sa_session
+ user = session.query( model.User ).filter_by( username=username ).first()
+ if user is None:
+ raise web.httpexceptions.HTTPNotFound()
+ page = trans.sa_session.query( model.Page ).filter_by( user=user, slug=slug ).first()
+ if page is None:
+ raise web.httpexceptions.HTTPNotFound()
+ return trans.fill_template( "page/display.mako", page=page )
+
+
\ No newline at end of file
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/web/controllers/user.py
--- a/lib/galaxy/web/controllers/user.py Mon Aug 24 15:27:27 2009 -0400
+++ b/lib/galaxy/web/controllers/user.py Mon Aug 24 15:53:17 2009 -0400
@@ -4,7 +4,7 @@
from galaxy.web.base.controller import *
from galaxy.model.orm import *
from galaxy import util
-import logging, os, string
+import logging, os, string, re
from random import choice
log = logging.getLogger( __name__ )
@@ -19,6 +19,8 @@
"""
require_login_nocreation_template = require_login_template % ""
require_login_creation_template = require_login_template % " If you don't already have an account, <a href='%s'>you may create one</a>."
+
+VALID_USERNAME_RE = re.compile( "^[a-z0-9\-]+$" )
class User( BaseController ):
edit_address_id = None
@@ -78,6 +80,37 @@
.add_text( "email", "Email", value=email, error=email_err )
.add_text( "conf_email", "Confirm Email", value='', error=conf_email_err )
.add_password( "password", "Password", value='', error=pass_err ) )
+
+ @web.expose
+ def change_username(self, trans, username='', **kwd):
+ username_err = ''
+ user = trans.get_user()
+ if not user:
+ trans.response.send_redirect( web.url_for( action='login' ) )
+ if trans.request.method == "POST":
+ if len( username ) < 4:
+ username_err = "Username must be at least 4 characters in length"
+ elif len( username ) > 255:
+ username_err = "USername must be at most 255 characters in length"
+ elif not( VALID_USERNAME_RE.match( username ) ):
+ username_err = "Username must contain only letters, numbers, '-', and '_'"
+ elif trans.app.model.User.filter_by( username=username ).first():
+ username_err = "This username is not available"
+ else:
+ user.username = username
+ user.flush()
+ trans.log_event( "User change username" )
+ return trans.show_ok_message( "Username been set to: " + user.username )
+ else:
+ username = user.username or ''
+ return trans.show_form(
+ web.FormBuilder( web.url_for(), "Change username", submit_text="Submit" )
+ .add_text( "username", "Username", value=username, error=username_err,
+ help="""Your username is an optional identifier that
+ will be used to generate adresses for information
+ you share publicly. Usernames must be at least
+ four characters in length and contain only lowercase
+ letters, numbers, and the '-' character.""" ) )
@web.expose
def login( self, trans, email='', password='' ):
diff -r 4ff4be2f436f -r 9c325f3656b5 lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py Mon Aug 24 15:27:27 2009 -0400
+++ b/lib/galaxy/web/framework/helpers/grids.py Mon Aug 24 15:53:17 2009 -0400
@@ -15,6 +15,7 @@
exposed = True
model_class = None
template = "grid.mako"
+ global_actions = []
columns = []
operations = []
standard_filters = []
@@ -22,7 +23,12 @@
default_sort_key = None
pass_through_operations = {}
def __init__( self ):
- pass
+ # Determine if any multiple row operations are defined
+ self.has_multiple_item_operations = False
+ for operation in self.operations:
+ if operation.allow_multiple:
+ self.has_multiple_item_operations = True
+ break
def __call__( self, trans, **kwargs ):
status = kwargs.get( 'status', None )
message = kwargs.get( 'message', None )
@@ -65,7 +71,13 @@
current_item = self.get_current_item( trans )
# Render
def url( *args, **kwargs ):
- new_kwargs = dict( extra_url_args )
+ # Only include sort/filter arguments if not linking to another
+ # page. This is a bit of a hack.
+ if 'action' in kwargs:
+ new_kwargs = dict()
+ else:
+ new_kwargs = dict( extra_url_args )
+ # Extend new_kwargs with first argument if found
if len(args) > 0:
new_kwargs.update( args[0] )
new_kwargs.update( kwargs )
@@ -158,16 +170,31 @@
return query
class GridOperation( object ):
- def __init__( self, label, key=None, condition=None, allow_multiple=True ):
+ def __init__( self, label, key=None, condition=None, allow_multiple=True, target=None, url_args=None ):
self.label = label
self.key = key
self.allow_multiple = allow_multiple
self.condition = condition
+ self.target = target
+ self.url_args = url_args
+ def get_url_args( self, item ):
+ if self.url_args:
+ temp = dict( self.url_args )
+ temp['id'] = item.id
+ return temp
+ else:
+ return dict( operation=operation.label, id=item.id )
+
def allowed( self, item ):
if self.condition:
return self.condition( item )
else:
return True
+
+class GridAction( object ):
+ def __init__( self, label=None, url_args=None ):
+ self.label = label
+ self.url_args = url_args
class GridColumnFilter( object ):
def __init__( self, label, args=None ):
diff -r 4ff4be2f436f -r 9c325f3656b5 static/scripts/jquery.wymeditor.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/static/scripts/jquery.wymeditor.js Mon Aug 24 15:53:17 2009 -0400
@@ -0,0 +1,4707 @@
+/**
+ * @version 0.5-rc1
+ *
+ * WYMeditor : what you see is What You Mean web-based editor
+ * Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
+ * Dual licensed under the MIT (MIT-license.txt)
+ * and GPL (GPL-license.txt) licenses.
+ *
+ * For further information visit:
+ * http://www.wymeditor.org/
+ *
+ * File: jquery.wymeditor.js
+ *
+ * Main JS file with core classes and functions.
+ * See the documentation for more info.
+ *
+ * About: authors
+ *
+ * Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
+ * Volker Mische (vmx a-t gmx dotde)
+ * Scott Lewis (lewiscot a-t gmail dotcom)
+ * Bermi Ferrer (wymeditor a-t bermi dotorg)
+ * Daniel Reszka (d.reszka a-t wymeditor dotorg)
+ * Jonatan Lundin (jonatan.lundin _at_ gmail.com)
+ */
+
+/*
+ Namespace: WYMeditor
+ Global WYMeditor namespace.
+*/
+if(!WYMeditor) var WYMeditor = {};
+
+//Wrap the Firebug console in WYMeditor.console
+(function() {
+ if ( !window.console || !console.firebug ) {
+ var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
+ "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
+
+ WYMeditor.console = {};
+ for (var i = 0; i < names.length; ++i)
+ WYMeditor.console[names[i]] = function() {}
+
+ } else WYMeditor.console = window.console;
+})();
+
+jQuery.extend(WYMeditor, {
+
+/*
+ Constants: Global WYMeditor constants.
+
+ VERSION - Defines WYMeditor version.
+ INSTANCES - An array of loaded WYMeditor.editor instances.
+ STRINGS - An array of loaded WYMeditor language pairs/values.
+ SKINS - An array of loaded WYMeditor skins.
+ NAME - The "name" attribute.
+ INDEX - A string replaced by the instance index.
+ WYM_INDEX - A string used to get/set the instance index.
+ BASE_PATH - A string replaced by WYMeditor's base path.
+ SKIN_PATH - A string replaced by WYMeditor's skin path.
+ WYM_PATH - A string replaced by WYMeditor's main JS file path.
+ SKINS_DEFAULT_PATH - The skins default base path.
+ SKINS_DEFAULT_CSS - The skins default CSS file.
+ LANG_DEFAULT_PATH - The language files default path.
+ IFRAME_BASE_PATH - A string replaced by the designmode iframe's base path.
+ IFRAME_DEFAULT - The iframe's default base path.
+ JQUERY_PATH - A string replaced by the computed jQuery path.
+ DIRECTION - A string replaced by the text direction (rtl or ltr).
+ LOGO - A string replaced by WYMeditor logo.
+ TOOLS - A string replaced by the toolbar's HTML.
+ TOOLS_ITEMS - A string replaced by the toolbar items.
+ TOOL_NAME - A string replaced by a toolbar item's name.
+ TOOL_TITLE - A string replaced by a toolbar item's title.
+ TOOL_CLASS - A string replaced by a toolbar item's class.
+ CLASSES - A string replaced by the classes panel's HTML.
+ CLASSES_ITEMS - A string replaced by the classes items.
+ CLASS_NAME - A string replaced by a class item's name.
+ CLASS_TITLE - A string replaced by a class item's title.
+ CONTAINERS - A string replaced by the containers panel's HTML.
+ CONTAINERS_ITEMS - A string replaced by the containers items.
+ CONTAINER_NAME - A string replaced by a container item's name.
+ CONTAINER_TITLE - A string replaced by a container item's title.
+ CONTAINER_CLASS - A string replaced by a container item's class.
+ HTML - A string replaced by the HTML view panel's HTML.
+ IFRAME - A string replaced by the designmode iframe.
+ STATUS - A string replaced by the status panel's HTML.
+ DIALOG_TITLE - A string replaced by a dialog's title.
+ DIALOG_BODY - A string replaced by a dialog's HTML body.
+ BODY - The BODY element.
+ STRING - The "string" type.
+ BODY,DIV,P,
+ H1,H2,H3,H4,H5,H6,
+ PRE,BLOCKQUOTE,
+ A,BR,IMG,
+ TABLE,TD,TH,
+ UL,OL,LI - HTML elements string representation.
+ CLASS,HREF,SRC,
+ TITLE,ALT - HTML attributes string representation.
+ DIALOG_LINK - A link dialog type.
+ DIALOG_IMAGE - An image dialog type.
+ DIALOG_TABLE - A table dialog type.
+ DIALOG_PASTE - A 'Paste from Word' dialog type.
+ BOLD - Command: (un)set selection to <strong>.
+ ITALIC - Command: (un)set selection to <em>.
+ CREATE_LINK - Command: open the link dialog or (un)set link.
+ INSERT_IMAGE - Command: open the image dialog or insert an image.
+ INSERT_TABLE - Command: open the table dialog.
+ PASTE - Command: open the paste dialog.
+ INDENT - Command: nest a list item.
+ OUTDENT - Command: unnest a list item.
+ TOGGLE_HTML - Command: display/hide the HTML view.
+ FORMAT_BLOCK - Command: set a block element to another type.
+ PREVIEW - Command: open the preview dialog.
+ UNLINK - Command: unset a link.
+ INSERT_UNORDEREDLIST- Command: insert an unordered list.
+ INSERT_ORDEREDLIST - Command: insert an ordered list.
+ MAIN_CONTAINERS - An array of the main HTML containers used in WYMeditor.
+ BLOCKS - An array of the HTML block elements.
+ KEY - Standard key codes.
+ NODE - Node types.
+
+*/
+
+ VERSION : "0.5-rc1",
+ INSTANCES : [],
+ STRINGS : [],
+ SKINS : [],
+ NAME : "name",
+ INDEX : "{Wym_Index}",
+ WYM_INDEX : "wym_index",
+ BASE_PATH : "{Wym_Base_Path}",
+ CSS_PATH : "{Wym_Css_Path}",
+ WYM_PATH : "{Wym_Wym_Path}",
+ SKINS_DEFAULT_PATH : "skins/",
+ SKINS_DEFAULT_CSS : "skin.css",
+ SKINS_DEFAULT_JS : "skin.js",
+ LANG_DEFAULT_PATH : "lang/",
+ IFRAME_BASE_PATH : "{Wym_Iframe_Base_Path}",
+ IFRAME_DEFAULT : "iframe/default/",
+ JQUERY_PATH : "{Wym_Jquery_Path}",
+ DIRECTION : "{Wym_Direction}",
+ LOGO : "{Wym_Logo}",
+ TOOLS : "{Wym_Tools}",
+ TOOLS_ITEMS : "{Wym_Tools_Items}",
+ TOOL_NAME : "{Wym_Tool_Name}",
+ TOOL_TITLE : "{Wym_Tool_Title}",
+ TOOL_CLASS : "{Wym_Tool_Class}",
+ CLASSES : "{Wym_Classes}",
+ CLASSES_ITEMS : "{Wym_Classes_Items}",
+ CLASS_NAME : "{Wym_Class_Name}",
+ CLASS_TITLE : "{Wym_Class_Title}",
+ CONTAINERS : "{Wym_Containers}",
+ CONTAINERS_ITEMS : "{Wym_Containers_Items}",
+ CONTAINER_NAME : "{Wym_Container_Name}",
+ CONTAINER_TITLE : "{Wym_Containers_Title}",
+ CONTAINER_CLASS : "{Wym_Container_Class}",
+ HTML : "{Wym_Html}",
+ IFRAME : "{Wym_Iframe}",
+ STATUS : "{Wym_Status}",
+ DIALOG_TITLE : "{Wym_Dialog_Title}",
+ DIALOG_BODY : "{Wym_Dialog_Body}",
+ STRING : "string",
+ BODY : "body",
+ DIV : "div",
+ P : "p",
+ H1 : "h1",
+ H2 : "h2",
+ H3 : "h3",
+ H4 : "h4",
+ H5 : "h5",
+ H6 : "h6",
+ PRE : "pre",
+ BLOCKQUOTE : "blockquote",
+ A : "a",
+ BR : "br",
+ IMG : "img",
+ TABLE : "table",
+ TD : "td",
+ TH : "th",
+ UL : "ul",
+ OL : "ol",
+ LI : "li",
+ CLASS : "class",
+ HREF : "href",
+ SRC : "src",
+ TITLE : "title",
+ ALT : "alt",
+ DIALOG_LINK : "Link",
+ DIALOG_IMAGE : "Image",
+ DIALOG_TABLE : "Table",
+ DIALOG_PASTE : "Paste_From_Word",
+ BOLD : "Bold",
+ ITALIC : "Italic",
+ CREATE_LINK : "CreateLink",
+ INSERT_IMAGE : "InsertImage",
+ INSERT_TABLE : "InsertTable",
+ INSERT_HTML : "InsertHTML",
+ PASTE : "Paste",
+ INDENT : "Indent",
+ OUTDENT : "Outdent",
+ TOGGLE_HTML : "ToggleHtml",
+ FORMAT_BLOCK : "FormatBlock",
+ PREVIEW : "Preview",
+ UNLINK : "Unlink",
+ INSERT_UNORDEREDLIST: "InsertUnorderedList",
+ INSERT_ORDEREDLIST : "InsertOrderedList",
+
+ MAIN_CONTAINERS : new Array("p","h1","h2","h3","h4","h5","h6","pre","blockquote"),
+
+ BLOCKS : new Array("address", "blockquote", "div", "dl",
+ "fieldset", "form", "h1", "h2", "h3", "h4", "h5", "h6", "hr",
+ "noscript", "ol", "p", "pre", "table", "ul", "dd", "dt",
+ "li", "tbody", "td", "tfoot", "th", "thead", "tr"),
+
+ KEY : {
+ BACKSPACE: 8,
+ ENTER: 13,
+ END: 35,
+ HOME: 36,
+ LEFT: 37,
+ UP: 38,
+ RIGHT: 39,
+ DOWN: 40,
+ CURSOR: new Array(37, 38, 39, 40),
+ DELETE: 46
+ },
+
+ NODE : {
+ ELEMENT: 1,
+ ATTRIBUTE: 2,
+ TEXT: 3
+ },
+
+ /*
+ Class: WYMeditor.editor
+ WYMeditor editor main class, instanciated for each editor occurrence.
+ */
+
+ editor : function(elem, options) {
+
+ /*
+ Constructor: WYMeditor.editor
+
+ Initializes main values (index, elements, paths, ...)
+ and call WYMeditor.editor.init which initializes the editor.
+
+ Parameters:
+
+ elem - The HTML element to be replaced by the editor.
+ options - The hash of options.
+
+ Returns:
+
+ Nothing.
+
+ See Also:
+
+ <WYMeditor.editor.init>
+ */
+
+ //store the instance in the INSTANCES array and store the index
+ this._index = WYMeditor.INSTANCES.push(this) - 1;
+ //store the element replaced by the editor
+ this._element = elem;
+ //store the options
+ this._options = options;
+ //store the element's inner value
+ this._html = jQuery(elem).val();
+
+ //store the HTML option, if any
+ if(this._options.html) this._html = this._options.html;
+ //get or compute the base path (where the main JS file is located)
+ this._options.basePath = this._options.basePath
+ || this.computeBasePath();
+ //get or set the skin path (where the skin files are located)
+ this._options.skinPath = this._options.skinPath
+ || this._options.basePath + WYMeditor.SKINS_DEFAULT_PATH
+ + this._options.skin + '/';
+ //get or compute the main JS file location
+ this._options.wymPath = this._options.wymPath
+ || this.computeWymPath();
+ //get or set the language files path
+ this._options.langPath = this._options.langPath
+ || this._options.basePath + WYMeditor.LANG_DEFAULT_PATH;
+ //get or set the designmode iframe's base path
+ this._options.iframeBasePath = this._options.iframeBasePath
+ || this._options.basePath + WYMeditor.IFRAME_DEFAULT;
+ //get or compute the jQuery JS file location
+ this._options.jQueryPath = this._options.jQueryPath
+ || this.computeJqueryPath();
+
+ //initialize the editor instance
+ this.init();
+
+ }
+
+});
+
+
+/********** JQUERY **********/
+
+/**
+ * Replace an HTML element by WYMeditor
+ *
+ * @example jQuery(".wymeditor").wymeditor(
+ * {
+ *
+ * }
+ * );
+ * @desc Example description here
+ *
+ * @name WYMeditor
+ * @description WYMeditor is a web-based WYSIWYM XHTML editor
+ * @param Hash hash A hash of parameters
+ * @option Integer iExample Description here
+ * @option String sExample Description here
+ *
+ * @type jQuery
+ * @cat Plugins/WYMeditor
+ * @author Jean-Francois Hovinne
+ */
+jQuery.fn.wymeditor = function(options) {
+
+ options = jQuery.extend({
+
+ html: "",
+
+ basePath: false,
+
+ skinPath: false,
+
+ wymPath: false,
+
+ iframeBasePath: false,
+
+ jQueryPath: false,
+
+ styles: false,
+
+ stylesheet: false,
+
+ skin: "default",
+ initSkin: true,
+ loadSkin: true,
+
+ lang: "en",
+
+ direction: "ltr",
+
+ boxHtml: "<div class='wym_box'>"
+ + "<div class='wym_area_top'>"
+ + WYMeditor.TOOLS
+ + "</div>"
+ + "<div class='wym_area_left'></div>"
+ + "<div class='wym_area_right'>"
+ + WYMeditor.CONTAINERS
+ + WYMeditor.CLASSES
+ + "</div>"
+ + "<div class='wym_area_main'>"
+ + WYMeditor.HTML
+ + WYMeditor.IFRAME
+ + WYMeditor.STATUS
+ + "</div>"
+ + "<div class='wym_area_bottom'>"
+ + WYMeditor.LOGO
+ + "</div>"
+ + "</div>",
+
+ logoHtml: "<a class='wym_wymeditor_link' "
+ + "href='http://www.wymeditor.org/'>WYMeditor</a>",
+
+ iframeHtml:"<div class='wym_iframe wym_section'>"
+ + "<iframe "
+ + "src='"
+ + WYMeditor.IFRAME_BASE_PATH
+ + "wymiframe.html' "
+ + "onload='this.contentWindow.parent.WYMeditor.INSTANCES["
+ + WYMeditor.INDEX + "].initIframe(this)'"
+ + "></iframe>"
+ + "</div>",
+
+ editorStyles: [],
+
+ toolsHtml: "<div class='wym_tools wym_section'>"
+ + "<h2>{Tools}</h2>"
+ + "<ul>"
+ + WYMeditor.TOOLS_ITEMS
+ + "</ul>"
+ + "</div>",
+
+ toolsItemHtml: "<li class='"
+ + WYMeditor.TOOL_CLASS
+ + "'><a href='#' name='"
+ + WYMeditor.TOOL_NAME
+ + "' title='"
+ + WYMeditor.TOOL_TITLE
+ + "'>"
+ + WYMeditor.TOOL_TITLE
+ + "</a></li>",
+
+ toolsItems: [
+ {'name': 'Bold', 'title': 'Strong', 'css': 'wym_tools_strong'},
+ {'name': 'Italic', 'title': 'Emphasis', 'css': 'wym_tools_emphasis'},
+ {'name': 'Superscript', 'title': 'Superscript',
+ 'css': 'wym_tools_superscript'},
+ {'name': 'Subscript', 'title': 'Subscript',
+ 'css': 'wym_tools_subscript'},
+ {'name': 'InsertOrderedList', 'title': 'Ordered_List',
+ 'css': 'wym_tools_ordered_list'},
+ {'name': 'InsertUnorderedList', 'title': 'Unordered_List',
+ 'css': 'wym_tools_unordered_list'},
+ {'name': 'Indent', 'title': 'Indent', 'css': 'wym_tools_indent'},
+ {'name': 'Outdent', 'title': 'Outdent', 'css': 'wym_tools_outdent'},
+ {'name': 'Undo', 'title': 'Undo', 'css': 'wym_tools_undo'},
+ {'name': 'Redo', 'title': 'Redo', 'css': 'wym_tools_redo'},
+ {'name': 'CreateLink', 'title': 'Link', 'css': 'wym_tools_link'},
+ {'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
+ {'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
+ {'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
+ {'name': 'Paste', 'title': 'Paste_From_Word',
+ 'css': 'wym_tools_paste'},
+ {'name': 'ToggleHtml', 'title': 'HTML', 'css': 'wym_tools_html'},
+ {'name': 'Preview', 'title': 'Preview', 'css': 'wym_tools_preview'}
+ ],
+
+ containersHtml: "<div class='wym_containers wym_section'>"
+ + "<h2>{Containers}</h2>"
+ + "<ul>"
+ + WYMeditor.CONTAINERS_ITEMS
+ + "</ul>"
+ + "</div>",
+
+ containersItemHtml:"<li class='"
+ + WYMeditor.CONTAINER_CLASS
+ + "'>"
+ + "<a href='#' name='"
+ + WYMeditor.CONTAINER_NAME
+ + "'>"
+ + WYMeditor.CONTAINER_TITLE
+ + "</a></li>",
+
+ containersItems: [
+ {'name': 'P', 'title': 'Paragraph', 'css': 'wym_containers_p'},
+ {'name': 'H1', 'title': 'Heading_1', 'css': 'wym_containers_h1'},
+ {'name': 'H2', 'title': 'Heading_2', 'css': 'wym_containers_h2'},
+ {'name': 'H3', 'title': 'Heading_3', 'css': 'wym_containers_h3'},
+ {'name': 'H4', 'title': 'Heading_4', 'css': 'wym_containers_h4'},
+ {'name': 'H5', 'title': 'Heading_5', 'css': 'wym_containers_h5'},
+ {'name': 'H6', 'title': 'Heading_6', 'css': 'wym_containers_h6'},
+ {'name': 'PRE', 'title': 'Preformatted', 'css': 'wym_containers_pre'},
+ {'name': 'BLOCKQUOTE', 'title': 'Blockquote',
+ 'css': 'wym_containers_blockquote'},
+ {'name': 'TH', 'title': 'Table_Header', 'css': 'wym_containers_th'}
+ ],
+
+ classesHtml: "<div class='wym_classes wym_section'>"
+ + "<h2>{Classes}</h2><ul>"
+ + WYMeditor.CLASSES_ITEMS
+ + "</ul></div>",
+
+ classesItemHtml: "<li><a href='#' name='"
+ + WYMeditor.CLASS_NAME
+ + "'>"
+ + WYMeditor.CLASS_TITLE
+ + "</a></li>",
+
+ classesItems: [],
+
+ statusHtml: "<div class='wym_status wym_section'>"
+ + "<h2>{Status}</h2>"
+ + "</div>",
+
+ htmlHtml: "<div class='wym_html wym_section'>"
+ + "<h2>{Source_Code}</h2>"
+ + "<textarea class='wym_html_val'></textarea>"
+ + "</div>",
+
+ boxSelector: ".wym_box",
+ toolsSelector: ".wym_tools",
+ toolsListSelector: " ul",
+ containersSelector:".wym_containers",
+ classesSelector: ".wym_classes",
+ htmlSelector: ".wym_html",
+ iframeSelector: ".wym_iframe iframe",
+ iframeBodySelector:".wym_iframe",
+ statusSelector: ".wym_status",
+ toolSelector: ".wym_tools a",
+ containerSelector: ".wym_containers a",
+ classSelector: ".wym_classes a",
+ htmlValSelector: ".wym_html_val",
+
+ hrefSelector: ".wym_href",
+ srcSelector: ".wym_src",
+ titleSelector: ".wym_title",
+ altSelector: ".wym_alt",
+ textSelector: ".wym_text",
+
+ rowsSelector: ".wym_rows",
+ colsSelector: ".wym_cols",
+ captionSelector: ".wym_caption",
+ summarySelector: ".wym_summary",
+
+ submitSelector: ".wym_submit",
+ cancelSelector: ".wym_cancel",
+ previewSelector: "",
+
+ dialogTypeSelector: ".wym_dialog_type",
+ dialogLinkSelector: ".wym_dialog_link",
+ dialogImageSelector: ".wym_dialog_image",
+ dialogTableSelector: ".wym_dialog_table",
+ dialogPasteSelector: ".wym_dialog_paste",
+ dialogPreviewSelector: ".wym_dialog_preview",
+
+ updateSelector: ".wymupdate",
+ updateEvent: "click",
+
+ dialogFeatures: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ + ",width=560,height=300,top=0,left=0",
+ dialogFeaturesPreview: "menubar=no,titlebar=no,toolbar=no,resizable=no"
+ + ",scrollbars=yes,width=560,height=300,top=0,left=0",
+
+ dialogHtml: "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'"
+ + " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>"
+ + "<html dir='"
+ + WYMeditor.DIRECTION
+ + "'><head>"
+ + "<link rel='stylesheet' type='text/css' media='screen'"
+ + " href='"
+ + WYMeditor.CSS_PATH
+ + "' />"
+ + "<title>"
+ + WYMeditor.DIALOG_TITLE
+ + "</title>"
+ + "<script type='text/javascript'"
+ + " src='"
+ + WYMeditor.JQUERY_PATH
+ + "'></script>"
+ + "<script type='text/javascript'"
+ + " src='"
+ + WYMeditor.WYM_PATH
+ + "'></script>"
+ + "</head>"
+ + WYMeditor.DIALOG_BODY
+ + "</html>",
+
+ dialogLinkHtml: "<body class='wym_dialog wym_dialog_link'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_LINK
+ + "' />"
+ + "<legend>{Link}</legend>"
+ + "<div class='row'>"
+ + "<label>{URL}</label>"
+ + "<input type='text' class='wym_href' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Title}</label>"
+ + "<input type='text' class='wym_title' value='' size='40' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogImageHtml: "<body class='wym_dialog wym_dialog_image'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_IMAGE
+ + "' />"
+ + "<legend>{Image}</legend>"
+ + "<div class='row'>"
+ + "<label>{URL}</label>"
+ + "<input type='text' class='wym_src' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Alternative_Text}</label>"
+ + "<input type='text' class='wym_alt' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Title}</label>"
+ + "<input type='text' class='wym_title' value='' size='40' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogTableHtml: "<body class='wym_dialog wym_dialog_table'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<fieldset>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_TABLE
+ + "' />"
+ + "<legend>{Table}</legend>"
+ + "<div class='row'>"
+ + "<label>{Caption}</label>"
+ + "<input type='text' class='wym_caption' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Summary}</label>"
+ + "<input type='text' class='wym_summary' value='' size='40' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Number_Of_Rows}</label>"
+ + "<input type='text' class='wym_rows' value='3' size='3' />"
+ + "</div>"
+ + "<div class='row'>"
+ + "<label>{Number_Of_Cols}</label>"
+ + "<input type='text' class='wym_cols' value='2' size='3' />"
+ + "</div>"
+ + "<div class='row row-indent'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogPasteHtml: "<body class='wym_dialog wym_dialog_paste'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + ">"
+ + "<form>"
+ + "<input type='hidden' class='wym_dialog_type' value='"
+ + WYMeditor.DIALOG_PASTE
+ + "' />"
+ + "<fieldset>"
+ + "<legend>{Paste_From_Word}</legend>"
+ + "<div class='row'>"
+ + "<textarea class='wym_text' rows='10' cols='50'></textarea>"
+ + "</div>"
+ + "<div class='row'>"
+ + "<input class='wym_submit' type='button'"
+ + " value='{Submit}' />"
+ + "<input class='wym_cancel' type='button'"
+ + "value='{Cancel}' />"
+ + "</div>"
+ + "</fieldset>"
+ + "</form>"
+ + "</body>",
+
+ dialogPreviewHtml: "<body class='wym_dialog wym_dialog_preview'"
+ + " onload='WYMeditor.INIT_DIALOG(" + WYMeditor.INDEX + ")'"
+ + "></body>",
+
+ dialogStyles: [],
+
+ stringDelimiterLeft: "{",
+ stringDelimiterRight:"}",
+
+ preInit: null,
+ preBind: null,
+ postInit: null,
+
+ preInitDialog: null,
+ postInitDialog: null
+
+ }, options);
+
+ return this.each(function() {
+
+ new WYMeditor.editor(jQuery(this),options);
+ });
+};
+
+/* @name extend
+ * @description Returns the WYMeditor instance based on its index
+ */
+jQuery.extend({
+ wymeditors: function(i) {
+ return (WYMeditor.INSTANCES[i]);
+ }
+});
+
+
+/********** WYMeditor **********/
+
+/* @name Wymeditor
+ * @description WYMeditor class
+ */
+
+/* @name init
+ * @description Initializes a WYMeditor instance
+ */
+WYMeditor.editor.prototype.init = function() {
+
+ //load subclass - browser specific
+ //unsupported browsers: do nothing
+ if (jQuery.browser.msie) {
+ var WymClass = new WYMeditor.WymClassExplorer(this);
+ }
+ else if (jQuery.browser.mozilla) {
+ var WymClass = new WYMeditor.WymClassMozilla(this);
+ }
+ else if (jQuery.browser.opera) {
+ var WymClass = new WYMeditor.WymClassOpera(this);
+ }
+ else if (jQuery.browser.safari) {
+ var WymClass = new WYMeditor.WymClassSafari(this);
+ }
+
+ if(WymClass) {
+
+ if(jQuery.isFunction(this._options.preInit)) this._options.preInit(this);
+
+ var SaxListener = new WYMeditor.XhtmlSaxListener();
+ jQuery.extend(SaxListener, WymClass);
+ this.parser = new WYMeditor.XhtmlParser(SaxListener);
+
+ if(this._options.styles || this._options.stylesheet){
+ this.configureEditorUsingRawCss();
+ }
+
+ this.helper = new WYMeditor.XmlHelper();
+
+ //extend the Wymeditor object
+ //don't use jQuery.extend since 1.1.4
+ //jQuery.extend(this, WymClass);
+ for (var prop in WymClass) { this[prop] = WymClass[prop]; }
+
+ //load wymbox
+ this._box = jQuery(this._element).hide().after(this._options.boxHtml).next().addClass('wym_box_' + this._index);
+
+ //store the instance index in wymbox and element replaced by editor instance
+ //but keep it compatible with jQuery < 1.2.3, see #122
+ if( jQuery.isFunction( jQuery.fn.data ) ) {
+ jQuery.data(this._box.get(0), WYMeditor.WYM_INDEX, this._index);
+ jQuery.data(this._element.get(0), WYMeditor.WYM_INDEX, this._index);
+ }
+
+ var h = WYMeditor.Helper;
+
+ //construct the iframe
+ var iframeHtml = this._options.iframeHtml;
+ iframeHtml = h.replaceAll(iframeHtml, WYMeditor.INDEX, this._index);
+ iframeHtml = h.replaceAll(iframeHtml, WYMeditor.IFRAME_BASE_PATH, this._options.iframeBasePath);
+
+ //construct wymbox
+ var boxHtml = jQuery(this._box).html();
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.LOGO, this._options.logoHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS, this._options.toolsHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS,this._options.containersHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES, this._options.classesHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.HTML, this._options.htmlHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.IFRAME, iframeHtml);
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.STATUS, this._options.statusHtml);
+
+ //construct tools list
+ var aTools = eval(this._options.toolsItems);
+ var sTools = "";
+
+ for(var i = 0; i < aTools.length; i++) {
+ var oTool = aTools[i];
+ if(oTool.name && oTool.title)
+ var sTool = this._options.toolsItemHtml;
+ var sTool = h.replaceAll(sTool, WYMeditor.TOOL_NAME, oTool.name);
+ sTool = h.replaceAll(sTool, WYMeditor.TOOL_TITLE, this._options.stringDelimiterLeft
+ + oTool.title
+ + this._options.stringDelimiterRight);
+ sTool = h.replaceAll(sTool, WYMeditor.TOOL_CLASS, oTool.css);
+ sTools += sTool;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.TOOLS_ITEMS, sTools);
+
+ //construct classes list
+ var aClasses = eval(this._options.classesItems);
+ var sClasses = "";
+
+ for(var i = 0; i < aClasses.length; i++) {
+ var oClass = aClasses[i];
+ if(oClass.name && oClass.title)
+ var sClass = this._options.classesItemHtml;
+ sClass = h.replaceAll(sClass, WYMeditor.CLASS_NAME, oClass.name);
+ sClass = h.replaceAll(sClass, WYMeditor.CLASS_TITLE, oClass.title);
+ sClasses += sClass;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CLASSES_ITEMS, sClasses);
+
+ //construct containers list
+ var aContainers = eval(this._options.containersItems);
+ var sContainers = "";
+
+ for(var i = 0; i < aContainers.length; i++) {
+ var oContainer = aContainers[i];
+ if(oContainer.name && oContainer.title)
+ var sContainer = this._options.containersItemHtml;
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_NAME, oContainer.name);
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_TITLE,
+ this._options.stringDelimiterLeft
+ + oContainer.title
+ + this._options.stringDelimiterRight);
+ sContainer = h.replaceAll(sContainer, WYMeditor.CONTAINER_CLASS, oContainer.css);
+ sContainers += sContainer;
+ }
+
+ boxHtml = h.replaceAll(boxHtml, WYMeditor.CONTAINERS_ITEMS, sContainers);
+
+ //l10n
+ boxHtml = this.replaceStrings(boxHtml);
+
+ //load html in wymbox
+ jQuery(this._box).html(boxHtml);
+
+ //hide the html value
+ jQuery(this._box).find(this._options.htmlSelector).hide();
+
+ //enable the skin
+ this.loadSkin();
+
+ }
+};
+
+WYMeditor.editor.prototype.bindEvents = function() {
+
+ //copy the instance
+ var wym = this;
+
+ //handle click event on tools buttons
+ jQuery(this._box).find(this._options.toolSelector).click(function() {
+ wym._iframe.contentWindow.focus(); //See #154
+ wym.exec(jQuery(this).attr(WYMeditor.NAME));
+ return(false);
+ });
+
+ //handle click event on containers buttons
+ jQuery(this._box).find(this._options.containerSelector).click(function() {
+ wym.container(jQuery(this).attr(WYMeditor.NAME));
+ return(false);
+ });
+
+ //handle keyup event on html value: set the editor value
+ //handle focus/blur events to check if the element has focus, see #147
+ jQuery(this._box).find(this._options.htmlValSelector)
+ .keyup(function() { jQuery(wym._doc.body).html(jQuery(this).val());})
+ .focus(function() { jQuery(this).toggleClass('hasfocus'); })
+ .blur(function() { jQuery(this).toggleClass('hasfocus'); });
+
+ //handle click event on classes buttons
+ jQuery(this._box).find(this._options.classSelector).click(function() {
+
+ var aClasses = eval(wym._options.classesItems);
+ var sName = jQuery(this).attr(WYMeditor.NAME);
+
+ var oClass = WYMeditor.Helper.findByName(aClasses, sName);
+
+ if(oClass) {
+ var jqexpr = oClass.expr;
+ wym.toggleClass(sName, jqexpr);
+ }
+ wym._iframe.contentWindow.focus(); //See #154
+ return(false);
+ });
+
+ //handle event on update element
+ jQuery(this._options.updateSelector)
+ .bind(this._options.updateEvent, function() {
+ wym.update();
+ });
+};
+
+WYMeditor.editor.prototype.ready = function() {
+ return(this._doc != null);
+};
+
+
+/********** METHODS **********/
+
+/* @name box
+ * @description Returns the WYMeditor container
+ */
+WYMeditor.editor.prototype.box = function() {
+ return(this._box);
+};
+
+/* @name html
+ * @description Get/Set the html value
+ */
+WYMeditor.editor.prototype.html = function(html) {
+
+ if(typeof html === 'string') jQuery(this._doc.body).html(html);
+ else return(jQuery(this._doc.body).html());
+};
+
+/* @name xhtml
+ * @description Cleans up the HTML
+ */
+WYMeditor.editor.prototype.xhtml = function() {
+ return this.parser.parse(this.html());
+};
+
+/* @name exec
+ * @description Executes a button command
+ */
+WYMeditor.editor.prototype.exec = function(cmd) {
+
+ //base function for execCommand
+ //open a dialog or exec
+ switch(cmd) {
+ case WYMeditor.CREATE_LINK:
+ var container = this.container();
+ if(container || this._selected_image) this.dialog(WYMeditor.DIALOG_LINK);
+ break;
+
+ case WYMeditor.INSERT_IMAGE:
+ this.dialog(WYMeditor.DIALOG_IMAGE);
+ break;
+
+ case WYMeditor.INSERT_TABLE:
+ this.dialog(WYMeditor.DIALOG_TABLE);
+ break;
+
+ case WYMeditor.PASTE:
+ this.dialog(WYMeditor.DIALOG_PASTE);
+ break;
+
+ case WYMeditor.TOGGLE_HTML:
+ this.update();
+ this.toggleHtml();
+
+ //partially fixes #121 when the user manually inserts an image
+ if(!jQuery(this._box).find(this._options.htmlSelector).is(':visible'))
+ this.listen();
+ break;
+
+ case WYMeditor.PREVIEW:
+ this.dialog(WYMeditor.PREVIEW, this._options.dialogFeaturesPreview);
+ break;
+
+ default:
+ this._exec(cmd);
+ break;
+ }
+};
+
+/* @name container
+ * @description Get/Set the selected container
+ */
+WYMeditor.editor.prototype.container = function(sType) {
+
+ if(sType) {
+
+ var container = null;
+
+ if(sType.toLowerCase() == WYMeditor.TH) {
+
+ container = this.container();
+
+ //find the TD or TH container
+ switch(container.tagName.toLowerCase()) {
+
+ case WYMeditor.TD: case WYMeditor.TH:
+ break;
+ default:
+ var aTypes = new Array(WYMeditor.TD,WYMeditor.TH);
+ container = this.findUp(this.container(), aTypes);
+ break;
+ }
+
+ //if it exists, switch
+ if(container!=null) {
+
+ sType = (container.tagName.toLowerCase() == WYMeditor.TD)? WYMeditor.TH: WYMeditor.TD;
+ this.switchTo(container,sType);
+ this.update();
+ }
+ } else {
+
+ //set the container type
+ var aTypes=new Array(WYMeditor.P,WYMeditor.H1,WYMeditor.H2,WYMeditor.H3,WYMeditor.H4,WYMeditor.H5,
+ WYMeditor.H6,WYMeditor.PRE,WYMeditor.BLOCKQUOTE);
+ container = this.findUp(this.container(), aTypes);
+
+ if(container) {
+
+ var newNode = null;
+
+ //blockquotes must contain a block level element
+ if(sType.toLowerCase() == WYMeditor.BLOCKQUOTE) {
+
+ var blockquote = this.findUp(this.container(), WYMeditor.BLOCKQUOTE);
+
+ if(blockquote == null) {
+
+ newNode = this._doc.createElement(sType);
+ container.parentNode.insertBefore(newNode,container);
+ newNode.appendChild(container);
+ this.setFocusToNode(newNode.firstChild);
+
+ } else {
+
+ var nodes = blockquote.childNodes;
+ var lgt = nodes.length;
+ var firstNode = null;
+
+ if(lgt > 0) firstNode = nodes.item(0);
+ for(var x=0; x<lgt; x++) {
+ blockquote.parentNode.insertBefore(nodes.item(0),blockquote);
+ }
+ blockquote.parentNode.removeChild(blockquote);
+ if(firstNode) this.setFocusToNode(firstNode);
+ }
+ }
+
+ else this.switchTo(container,sType);
+
+ this.update();
+ }
+ }
+ }
+ else return(this.selected());
+};
+
+/* @name toggleClass
+ * @description Toggles class on selected element, or one of its parents
+ */
+WYMeditor.editor.prototype.toggleClass = function(sClass, jqexpr) {
+
+ var container = (this._selected_image
+ ? this._selected_image
+ : jQuery(this.selected()));
+ container = jQuery(container).parentsOrSelf(jqexpr);
+ jQuery(container).toggleClass(sClass);
+
+ if(!jQuery(container).attr(WYMeditor.CLASS)) jQuery(container).removeAttr(this._class);
+
+};
+
+/* @name findUp
+ * @description Returns the first parent or self container, based on its type
+ */
+WYMeditor.editor.prototype.findUp = function(node, filter) {
+
+ //filter is a string or an array of strings
+
+ if(node) {
+
+ var tagname = node.tagName.toLowerCase();
+
+ if(typeof(filter) == WYMeditor.STRING) {
+
+ while(tagname != filter && tagname != WYMeditor.BODY) {
+
+ node = node.parentNode;
+ tagname = node.tagName.toLowerCase();
+ }
+
+ } else {
+
+ var bFound = false;
+
+ while(!bFound && tagname != WYMeditor.BODY) {
+ for(var i = 0; i < filter.length; i++) {
+ if(tagname == filter[i]) {
+ bFound = true;
+ break;
+ }
+ }
+ if(!bFound) {
+ node = node.parentNode;
+ tagname = node.tagName.toLowerCase();
+ }
+ }
+ }
+
+ if(tagname != WYMeditor.BODY) return(node);
+ else return(null);
+
+ } else return(null);
+};
+
+/* @name switchTo
+ * @description Switch the node's type
+ */
+WYMeditor.editor.prototype.switchTo = function(node,sType) {
+
+ var newNode = this._doc.createElement(sType);
+ var html = jQuery(node).html();
+ node.parentNode.replaceChild(newNode,node);
+ jQuery(newNode).html(html);
+ this.setFocusToNode(newNode);
+};
+
+WYMeditor.editor.prototype.replaceStrings = function(sVal) {
+ //check if the language file has already been loaded
+ //if not, get it via a synchronous ajax call
+ if(!WYMeditor.STRINGS[this._options.lang]) {
+ try {
+ eval(jQuery.ajax({url:this._options.langPath
+ + this._options.lang + '.js', async:false}).responseText);
+ } catch(e) {
+ WYMeditor.console.error("WYMeditor: error while parsing language file.");
+ return sVal;
+ }
+ }
+
+ //replace all the strings in sVal and return it
+ for (var key in WYMeditor.STRINGS[this._options.lang]) {
+ sVal = WYMeditor.Helper.replaceAll(sVal, this._options.stringDelimiterLeft + key
+ + this._options.stringDelimiterRight,
+ WYMeditor.STRINGS[this._options.lang][key]);
+ };
+ return(sVal);
+};
+
+WYMeditor.editor.prototype.encloseString = function(sVal) {
+
+ return(this._options.stringDelimiterLeft
+ + sVal
+ + this._options.stringDelimiterRight);
+};
+
+/* @name status
+ * @description Prints a status message
+ */
+WYMeditor.editor.prototype.status = function(sMessage) {
+
+ //print status message
+ jQuery(this._box).find(this._options.statusSelector).html(sMessage);
+};
+
+/* @name update
+ * @description Updates the element and textarea values
+ */
+WYMeditor.editor.prototype.update = function() {
+
+ var html = this.xhtml();
+ jQuery(this._element).val(html);
+ jQuery(this._box).find(this._options.htmlValSelector).not('.hasfocus').val(html); //#147
+};
+
+/* @name dialog
+ * @description Opens a dialog box
+ */
+WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
+
+ var features = dialogFeatures || this._wym._options.dialogFeatures;
+ var wDialog = window.open('', 'dialog', features);
+
+ if(wDialog) {
+
+ var sBodyHtml = "";
+
+ switch( dialogType ) {
+
+ case(WYMeditor.DIALOG_LINK):
+ sBodyHtml = this._options.dialogLinkHtml;
+ break;
+ case(WYMeditor.DIALOG_IMAGE):
+ sBodyHtml = this._options.dialogImageHtml;
+ break;
+ case(WYMeditor.DIALOG_TABLE):
+ sBodyHtml = this._options.dialogTableHtml;
+ break;
+ case(WYMeditor.DIALOG_PASTE):
+ sBodyHtml = this._options.dialogPasteHtml;
+ break;
+ case(WYMeditor.PREVIEW):
+ sBodyHtml = this._options.dialogPreviewHtml;
+ break;
+
+ default:
+ sBodyHtml = bodyHtml;
+ }
+
+ var h = WYMeditor.Helper;
+
+ //construct the dialog
+ var dialogHtml = this._options.dialogHtml;
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.BASE_PATH, this._options.basePath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIRECTION, this._options.direction);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.CSS_PATH, this._options.skinPath + WYMeditor.SKINS_DEFAULT_CSS);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.WYM_PATH, this._options.wymPath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.JQUERY_PATH, this._options.jQueryPath);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_TITLE, this.encloseString( dialogType ));
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.DIALOG_BODY, sBodyHtml);
+ dialogHtml = h.replaceAll(dialogHtml, WYMeditor.INDEX, this._index);
+
+ dialogHtml = this.replaceStrings(dialogHtml);
+
+ var doc = wDialog.document;
+ doc.write(dialogHtml);
+ doc.close();
+ }
+};
+
+/* @name toggleHtml
+ * @description Shows/Hides the HTML
+ */
+WYMeditor.editor.prototype.toggleHtml = function() {
+ jQuery(this._box).find(this._options.htmlSelector).toggle();
+};
+
+WYMeditor.editor.prototype.uniqueStamp = function() {
+ var now = new Date();
+ return("wym-" + now.getTime());
+};
+
+WYMeditor.editor.prototype.paste = function(sData) {
+
+ var sTmp;
+ var container = this.selected();
+
+ //split the data, using double newlines as the separator
+ var aP = sData.split(this._newLine + this._newLine);
+ var rExp = new RegExp(this._newLine, "g");
+
+ //add a P for each item
+ if(container && container.tagName.toLowerCase() != WYMeditor.BODY) {
+ for(x = aP.length - 1; x >= 0; x--) {
+ sTmp = aP[x];
+ //simple newlines are replaced by a break
+ sTmp = sTmp.replace(rExp, "<br />");
+ jQuery(container).after("<p>" + sTmp + "</p>");
+ }
+ } else {
+ for(x = 0; x < aP.length; x++) {
+ sTmp = aP[x];
+ //simple newlines are replaced by a break
+ sTmp = sTmp.replace(rExp, "<br />");
+ jQuery(this._doc.body).append("<p>" + sTmp + "</p>");
+ }
+
+ }
+};
+
+WYMeditor.editor.prototype.insert = function(html) {
+ // Do we have a selection?
+ if (this._iframe.contentWindow.getSelection().focusNode != null) {
+ // Overwrite selection with provided html
+ this._exec( WYMeditor.INSERT_HTML, html);
+ } else {
+ // Fall back to the internal paste function if there's no selection
+ this.paste(html)
+ }
+};
+
+WYMeditor.editor.prototype.wrap = function(left, right) {
+ // Do we have a selection?
+ if (this._iframe.contentWindow.getSelection().focusNode != null) {
+ // Wrap selection with provided html
+ this._exec( WYMeditor.INSERT_HTML, left + this._iframe.contentWindow.getSelection().toString() + right);
+ }
+};
+
+WYMeditor.editor.prototype.unwrap = function() {
+ // Do we have a selection?
+ if (this._iframe.contentWindow.getSelection().focusNode != null) {
+ // Unwrap selection
+ this._exec( WYMeditor.INSERT_HTML, this._iframe.contentWindow.getSelection().toString() );
+ }
+};
+
+WYMeditor.editor.prototype.addCssRules = function(doc, aCss) {
+ var styles = doc.styleSheets[0];
+ if(styles) {
+ for(var i = 0; i < aCss.length; i++) {
+ var oCss = aCss[i];
+ if(oCss.name && oCss.css) this.addCssRule(styles, oCss);
+ }
+ }
+};
+
+/********** CONFIGURATION **********/
+
+WYMeditor.editor.prototype.computeBasePath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src').replace(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/, '');
+};
+
+WYMeditor.editor.prototype.computeWymPath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery\.wymeditor(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src');
+};
+
+WYMeditor.editor.prototype.computeJqueryPath = function() {
+ return jQuery(jQuery.grep(jQuery('script'), function(s){
+ return (s.src && s.src.match(/jquery(-(.*)){0,1}(\.pack|\.min|\.packed)?\.js(\?.*)?$/ ))
+ })).attr('src');
+};
+
+WYMeditor.editor.prototype.computeCssPath = function() {
+ return jQuery(jQuery.grep(jQuery('link'), function(s){
+ return (s.href && s.href.match(/wymeditor\/skins\/(.*)screen\.css(\?.*)?$/ ))
+ })).attr('href');
+};
+
+WYMeditor.editor.prototype.configureEditorUsingRawCss = function() {
+
+ var CssParser = new WYMeditor.WymCssParser();
+ if(this._options.stylesheet){
+ CssParser.parse(jQuery.ajax({url: this._options.stylesheet,async:false}).responseText);
+ }else{
+ CssParser.parse(this._options.styles, false);
+ }
+
+ if(this._options.classesItems.length == 0) {
+ this._options.classesItems = CssParser.css_settings.classesItems;
+ }
+ if(this._options.editorStyles.length == 0) {
+ this._options.editorStyles = CssParser.css_settings.editorStyles;
+ }
+ if(this._options.dialogStyles.length == 0) {
+ this._options.dialogStyles = CssParser.css_settings.dialogStyles;
+ }
+};
+
+/********** EVENTS **********/
+
+WYMeditor.editor.prototype.listen = function() {
+
+ //don't use jQuery.find() on the iframe body
+ //because of MSIE + jQuery + expando issue (#JQ1143)
+ //jQuery(this._doc.body).find("*").bind("mouseup", this.mouseup);
+
+ jQuery(this._doc.body).bind("mousedown", this.mousedown);
+ var images = this._doc.body.getElementsByTagName("img");
+ for(var i=0; i < images.length; i++) {
+ jQuery(images[i]).bind("mousedown", this.mousedown);
+ }
+};
+
+WYMeditor.editor.prototype.mousedown = function(evt) {
+
+ var wym = WYMeditor.INSTANCES[this.ownerDocument.title];
+ wym._selected_image = (this.tagName.toLowerCase() == WYMeditor.IMG) ? this : null;
+ evt.stopPropagation();
+};
+
+/********** SKINS **********/
+
+/*
+ * Function: WYMeditor.loadCss
+ * Loads a stylesheet in the document.
+ *
+ * Parameters:
+ * href - The CSS path.
+ */
+WYMeditor.loadCss = function(href) {
+
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = href;
+
+ var head = jQuery('head').get(0);
+ head.appendChild(link);
+};
+
+/*
+ * Function: WYMeditor.editor.loadSkin
+ * Loads the skin CSS and initialization script (if needed).
+ */
+WYMeditor.editor.prototype.loadSkin = function() {
+
+ //does the user want to automatically load the CSS (default: yes)?
+ //we also test if it hasn't been already loaded by another instance
+ //see below for a better (second) test
+ if(this._options.loadSkin && !WYMeditor.SKINS[this._options.skin]) {
+
+ //check if it hasn't been already loaded
+ //so we don't load it more than once
+ //(we check the existing <link> elements)
+
+ var found = false;
+ var rExp = new RegExp(this._options.skin
+ + '\/' + WYMeditor.SKINS_DEFAULT_CSS + '$');
+
+ jQuery('link').each( function() {
+ if(this.href.match(rExp)) found = true;
+ });
+
+ //load it, using the skin path
+ if(!found) WYMeditor.loadCss( this._options.skinPath
+ + WYMeditor.SKINS_DEFAULT_CSS );
+ }
+
+ //put the classname (ex. wym_skin_default) on wym_box
+ jQuery(this._box).addClass( "wym_skin_" + this._options.skin );
+
+ //does the user want to use some JS to initialize the skin (default: yes)?
+ //also check if it hasn't already been loaded by another instance
+ if(this._options.initSkin && !WYMeditor.SKINS[this._options.skin]) {
+
+ eval(jQuery.ajax({url:this._options.skinPath
+ + WYMeditor.SKINS_DEFAULT_JS, async:false}).responseText);
+ }
+
+ //init the skin, if needed
+ if(WYMeditor.SKINS[this._options.skin]
+ && WYMeditor.SKINS[this._options.skin].init)
+ WYMeditor.SKINS[this._options.skin].init(this);
+
+};
+
+
+/********** DIALOGS **********/
+
+WYMeditor.INIT_DIALOG = function(index) {
+
+ var wym = window.opener.WYMeditor.INSTANCES[index];
+ var doc = window.document;
+ var selected = wym.selected();
+ var dialogType = jQuery(wym._options.dialogTypeSelector).val();
+ var sStamp = wym.uniqueStamp();
+
+ switch(dialogType) {
+
+ case WYMeditor.DIALOG_LINK:
+ //ensure that we select the link to populate the fields
+ if(selected && selected.tagName && selected.tagName.toLowerCase != WYMeditor.A)
+ selected = jQuery(selected).parentsOrSelf(WYMeditor.A);
+
+ //fix MSIE selection if link image has been clicked
+ if(!selected && wym._selected_image)
+ selected = jQuery(wym._selected_image).parentsOrSelf(WYMeditor.A);
+ break;
+
+ }
+
+ //pre-init functions
+ if(jQuery.isFunction(wym._options.preInitDialog))
+ wym._options.preInitDialog(wym,window);
+
+ //add css rules from options
+ var styles = doc.styleSheets[0];
+ var aCss = eval(wym._options.dialogStyles);
+
+ wym.addCssRules(doc, aCss);
+
+ //auto populate fields if selected container (e.g. A)
+ if(selected) {
+ jQuery(wym._options.hrefSelector).val(jQuery(selected).attr(WYMeditor.HREF));
+ jQuery(wym._options.srcSelector).val(jQuery(selected).attr(WYMeditor.SRC));
+ jQuery(wym._options.titleSelector).val(jQuery(selected).attr(WYMeditor.TITLE));
+ jQuery(wym._options.altSelector).val(jQuery(selected).attr(WYMeditor.ALT));
+ }
+
+ //auto populate image fields if selected image
+ if(wym._selected_image) {
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.srcSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.SRC));
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.titleSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.TITLE));
+ jQuery(wym._options.dialogImageSelector + " " + wym._options.altSelector)
+ .val(jQuery(wym._selected_image).attr(WYMeditor.ALT));
+ }
+
+ jQuery(wym._options.dialogLinkSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var sUrl = jQuery(wym._options.hrefSelector).val();
+ if(sUrl.length > 0) {
+
+ wym._exec(WYMeditor.CREATE_LINK, sStamp);
+
+ jQuery("a[href=" + sStamp + "]", wym._doc.body)
+ .attr(WYMeditor.HREF, sUrl)
+ .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val());
+
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogImageSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var sUrl = jQuery(wym._options.srcSelector).val();
+ if(sUrl.length > 0) {
+
+ wym._exec(WYMeditor.INSERT_IMAGE, sStamp);
+
+ jQuery("img[src$=" + sStamp + "]", wym._doc.body)
+ .attr(WYMeditor.SRC, sUrl)
+ .attr(WYMeditor.TITLE, jQuery(wym._options.titleSelector).val())
+ .attr(WYMeditor.ALT, jQuery(wym._options.altSelector).val());
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogTableSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var iRows = jQuery(wym._options.rowsSelector).val();
+ var iCols = jQuery(wym._options.colsSelector).val();
+
+ if(iRows > 0 && iCols > 0) {
+
+ var table = wym._doc.createElement(WYMeditor.TABLE);
+ var newRow = null;
+ var newCol = null;
+
+ var sCaption = jQuery(wym._options.captionSelector).val();
+
+ //we create the caption
+ var newCaption = table.createCaption();
+ newCaption.innerHTML = sCaption;
+
+ //we create the rows and cells
+ for(x=0; x<iRows; x++) {
+ newRow = table.insertRow(x);
+ for(y=0; y<iCols; y++) {newRow.insertCell(y);}
+ }
+
+ //set the summary attr
+ jQuery(table).attr('summary',
+ jQuery(wym._options.summarySelector).val());
+
+ //append the table after the selected container
+ var node = jQuery(wym.findUp(wym.container(),
+ WYMeditor.MAIN_CONTAINERS)).get(0);
+ if(!node || !node.parentNode) jQuery(wym._doc.body).append(table);
+ else jQuery(node).after(table);
+ }
+ window.close();
+ });
+
+ jQuery(wym._options.dialogPasteSelector + " "
+ + wym._options.submitSelector).click(function() {
+
+ var sText = jQuery(wym._options.textSelector).val();
+ wym.paste(sText);
+ window.close();
+ });
+
+ jQuery(wym._options.dialogPreviewSelector + " "
+ + wym._options.previewSelector)
+ .html(wym.xhtml());
+
+ //cancel button
+ jQuery(wym._options.cancelSelector).mousedown(function() {
+ window.close();
+ });
+
+ //pre-init functions
+ if(jQuery.isFunction(wym._options.postInitDialog))
+ wym._options.postInitDialog(wym,window);
+
+};
+
+/********** XHTML LEXER/PARSER **********/
+
+/*
+* @name xml
+* @description Use these methods to generate XML and XHTML compliant tags and
+* escape tag attributes correctly
+* @author Bermi Ferrer - http://bermi.org
+* @author David Heinemeier Hansson http://loudthinking.com
+*/
+WYMeditor.XmlHelper = function()
+{
+ this._entitiesDiv = document.createElement('div');
+ return this;
+};
+
+
+/*
+* @name tag
+* @description
+* Returns an empty HTML tag of type *name* which by default is XHTML
+* compliant. Setting *open* to true will create an open tag compatible
+* with HTML 4.0 and below. Add HTML attributes by passing an attributes
+* array to *options*. For attributes with no value like (disabled and
+* readonly), give it a value of true in the *options* array.
+*
+* Examples:
+*
+* this.tag('br')
+* # => <br />
+* this.tag ('br', false, true)
+* # => <br>
+* this.tag ('input', jQuery({type:'text',disabled:true }) )
+* # => <input type="text" disabled="disabled" />
+*/
+WYMeditor.XmlHelper.prototype.tag = function(name, options, open)
+{
+ options = options || false;
+ open = open || false;
+ return '<'+name+(options ? this.tagOptions(options) : '')+(open ? '>' : ' />');
+};
+
+/*
+* @name contentTag
+* @description
+* Returns a XML block tag of type *name* surrounding the *content*. Add
+* XML attributes by passing an attributes array to *options*. For attributes
+* with no value like (disabled and readonly), give it a value of true in
+* the *options* array. You can use symbols or strings for the attribute names.
+*
+* this.contentTag ('p', 'Hello world!' )
+* # => <p>Hello world!</p>
+* this.contentTag('div', this.contentTag('p', "Hello world!"), jQuery({class : "strong"}))
+* # => <div class="strong"><p>Hello world!</p></div>
+* this.contentTag("select", options, jQuery({multiple : true}))
+* # => <select multiple="multiple">...options...</select>
+*/
+WYMeditor.XmlHelper.prototype.contentTag = function(name, content, options)
+{
+ options = options || false;
+ return '<'+name+(options ? this.tagOptions(options) : '')+'>'+content+'</'+name+'>';
+};
+
+/*
+* @name cdataSection
+* @description
+* Returns a CDATA section for the given +content+. CDATA sections
+* are used to escape blocks of text containing characters which would
+* otherwise be recognized as markup. CDATA sections begin with the string
+* <tt><![CDATA[</tt> and } with (and may not contain) the string
+* <tt>]]></tt>.
+*/
+WYMeditor.XmlHelper.prototype.cdataSection = function(content)
+{
+ return '<![CDATA['+content+']]>';
+};
+
+
+/*
+* @name escapeOnce
+* @description
+* Returns the escaped +xml+ without affecting existing escaped entities.
+*
+* this.escapeOnce( "1 > 2 & 3")
+* # => "1 > 2 & 3"
+*/
+WYMeditor.XmlHelper.prototype.escapeOnce = function(xml)
+{
+ return this._fixDoubleEscape(this.escapeEntities(xml));
+};
+
+/*
+* @name _fixDoubleEscape
+* @description
+* Fix double-escaped entities, such as &amp;, &#123;, etc.
+*/
+WYMeditor.XmlHelper.prototype._fixDoubleEscape = function(escaped)
+{
+ return escaped.replace(/&([a-z]+|(#\d+));/ig, "&$1;");
+};
+
+/*
+* @name tagOptions
+* @description
+* Takes an array like the one generated by Tag.parseAttributes
+* [["src", "http://www.editam.com/?a=b&c=d&f=g"], ["title", "Editam, <Simplified> CMS"]]
+* or an object like {src:"http://www.editam.com/?a=b&c=d&f=g", title:"Editam, <Simplified> CMS"}
+* and returns a string properly escaped like
+* ' src = "http://www.editam.com/?a=b&c=d&f=g" title = "Editam, <Simplified> CMS"'
+* which is valid for strict XHTML
+*/
+WYMeditor.XmlHelper.prototype.tagOptions = function(options)
+{
+ var xml = this;
+ xml._formated_options = '';
+
+ for (var key in options) {
+ var formated_options = '';
+ var value = options[key];
+ if(typeof value != 'function' && value.length > 0) {
+
+ if(parseInt(key) == key && typeof value == 'object'){
+ key = value.shift();
+ value = value.pop();
+ }
+ if(key != '' && value != ''){
+ xml._formated_options += ' '+key+'="'+xml.escapeOnce(value)+'"';
+ }
+ }
+ }
+ return xml._formated_options;
+};
+
+/*
+* @name escapeEntities
+* @description
+* Escapes XML/HTML entities <, >, & and ". If seccond parameter is set to false it
+* will not escape ". If set to true it will also escape '
+*/
+WYMeditor.XmlHelper.prototype.escapeEntities = function(string, escape_quotes)
+{
+ this._entitiesDiv.innerHTML = string;
+ this._entitiesDiv.textContent = string;
+ var result = this._entitiesDiv.innerHTML;
+ if(typeof escape_quotes == 'undefined'){
+ if(escape_quotes != false) result = result.replace('"', '"');
+ if(escape_quotes == true) result = result.replace('"', ''');
+ }
+ return result;
+};
+
+/*
+* Parses a string conatining tag attributes and values an returns an array formated like
+* [["src", "http://www.editam.com"], ["title", "Editam, Simplified CMS"]]
+*/
+WYMeditor.XmlHelper.prototype.parseAttributes = function(tag_attributes)
+{
+ // Use a compounded regex to match single quoted, double quoted and unquoted attribute pairs
+ var result = [];
+ var matches = tag_attributes.split(/((=\s*")(")("))|((=\s*\')(\')(\'))|((=\s*[^>\s]*))/g);
+ if(matches.toString() != tag_attributes){
+ for (var k in matches) {
+ var v = matches[k];
+ if(typeof v != 'function' && v.length != 0){
+ var re = new RegExp('(\\w+)\\s*'+v);
+ if(match = tag_attributes.match(re) ){
+ var value = v.replace(/^[\s=]+/, "");
+ var delimiter = value.charAt(0);
+ delimiter = delimiter == '"' ? '"' : (delimiter=="'"?"'":'');
+ if(delimiter != ''){
+ value = delimiter == '"' ? value.replace(/^"|"+$/g, '') : value.replace(/^'|'+$/g, '');
+ }
+ tag_attributes = tag_attributes.replace(match[0],'');
+ result.push([match[1] , value]);
+ }
+ }
+ }
+ }
+ return result;
+};
+
+/**
+* XhtmlValidator for validating tag attributes
+*
+* @author Bermi Ferrer - http://bermi.org
+*/
+WYMeditor.XhtmlValidator = {
+ "_attributes":
+ {
+ "core":
+ {
+ "except":[
+ "base",
+ "head",
+ "html",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "class",
+ "id",
+ "style",
+ "title",
+ "accesskey",
+ "tabindex"
+ ]
+ },
+ "language":
+ {
+ "except":[
+ "base",
+ "br",
+ "hr",
+ "iframe",
+ "param",
+ "script"
+ ],
+ "attributes":
+ {
+ "dir":[
+ "ltr",
+ "rtl"
+ ],
+ "0":"lang",
+ "1":"xml:lang"
+ }
+ },
+ "keyboard":
+ {
+ "attributes":
+ {
+ "accesskey":/^(\w){1}$/,
+ "tabindex":/^(\d)+$/
+ }
+ }
+ },
+ "_events":
+ {
+ "window":
+ {
+ "only":[
+ "body"
+ ],
+ "attributes":[
+ "onload",
+ "onunload"
+ ]
+ },
+ "form":
+ {
+ "only":[
+ "form",
+ "input",
+ "textarea",
+ "select",
+ "a",
+ "label",
+ "button"
+ ],
+ "attributes":[
+ "onchange",
+ "onsubmit",
+ "onreset",
+ "onselect",
+ "onblur",
+ "onfocus"
+ ]
+ },
+ "keyboard":
+ {
+ "except":[
+ "base",
+ "bdo",
+ "br",
+ "frame",
+ "frameset",
+ "head",
+ "html",
+ "iframe",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "onkeydown",
+ "onkeypress",
+ "onkeyup"
+ ]
+ },
+ "mouse":
+ {
+ "except":[
+ "base",
+ "bdo",
+ "br",
+ "head",
+ "html",
+ "meta",
+ "param",
+ "script",
+ "style",
+ "title"
+ ],
+ "attributes":[
+ "onclick",
+ "ondblclick",
+ "onmousedown",
+ "onmousemove",
+ "onmouseover",
+ "onmouseout",
+ "onmouseup"
+ ]
+ }
+ },
+ "_tags":
+ {
+ "a":
+ {
+ "attributes":
+ {
+ "0":"charset",
+ "1":"coords",
+ "2":"href",
+ "3":"hreflang",
+ "4":"name",
+ "rel":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
+ "rev":/^(alternate|designates|stylesheet|start|next|prev|contents|index|glossary|copyright|chapter|section|subsection|appendix|help|bookmark| |shortcut|icon)+$/,
+ "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/,
+ "5":"type"
+ }
+ },
+ "0":"abbr",
+ "1":"acronym",
+ "2":"address",
+ "area":
+ {
+ "attributes":
+ {
+ "0":"alt",
+ "1":"coords",
+ "2":"href",
+ "nohref":/^(true|false)$/,
+ "shape":/^(rect|rectangle|circ|circle|poly|polygon)$/
+ },
+ "required":[
+ "alt"
+ ]
+ },
+ "3":"b",
+ "base":
+ {
+ "attributes":[
+ "href"
+ ],
+ "required":[
+ "href"
+ ]
+ },
+ "bdo":
+ {
+ "attributes":
+ {
+ "dir":/^(ltr|rtl)$/
+ },
+ "required":[
+ "dir"
+ ]
+ },
+ "4":"big",
+ "blockquote":
+ {
+ "attributes":[
+ "cite"
+ ]
+ },
+ "5":"body",
+ "6":"br",
+ "button":
+ {
+ "attributes":
+ {
+ "disabled":/^(disabled)$/,
+ "type":/^(button|reset|submit)$/,
+ "0":"value"
+ },
+ "inside":"form"
+ },
+ "7":"caption",
+ "8":"cite",
+ "9":"code",
+ "col":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "span":/^(\d)+$/,
+ "valign":/^(top|middle|bottom|baseline)$/,
+ "2":"width"
+ },
+ "inside":"colgroup"
+ },
+ "colgroup":
+ {
+ "attributes":
+ {
+ "align":/^(right|left|center|justify)$/,
+ "0":"char",
+ "1":"charoff",
+ "span":/^(\d)+$/,
+ "valign":/^(top|middle|bottom|baseline)$/,
+ "2":"width"
+ }
+ },
+ "10":"dd",
+ "del":
+ {
+ "attributes":
+ {
+ "0":"cite",
+ "datetime":/^([0-9]){8}/
+ }
+ },
+ "11":"div",
+ "12":"dfn",
+ "13":"dl",
+ "14":"dt",
+ "15":"em",
+ "fieldset":
+ {
+ "inside":"form"
+ },
+ "form":
+ {
+ "attributes":
+ {
+ "0":"action",
+ "1":"accept",
+ "2":"accept-charset",
+ "3":"enctype",
+ "method":/^(get|post)$/
+ },
+ "required":[
+ "action"
+ ]
+ },
+ "head":
+ {
+ "attributes":[
+ "profile"
+ ]
+ },
+ "16":"h1",
+ "17":"h2",
+ "18":"h3",
+ "19":"h4",
+ "20":"h5",
+ "21":"h6",
+ "22":"hr",
+ "html":
+ {
+ "attributes":[
+ "xmlns"
+ ]
+ },
+ "23":"i",
+ "img":
+ {
+ "attributes":[
+ "alt",
+ "src",
+ "height",
+ "ismap",
+ "longdesc",
+ "usemap",
+ "width"
+ ],
+ "required":[
+ "alt",
+ "src"
+ ]
+ },
+ "input":
+ {
+ "attributes":
+ {
+ "0":"accept",
+ "1":"alt",
+ "checked":/^(checked)$/,
+ "disabled":/^(disabled)$/,
+ "maxlength":/^(\d)+$/,
+ "2":"name",
+ "readonly":/^(readonly)$/,
+ "size":/^(\d)+$/,
+ "3":"src",
+ "type":/^(button|checkbox|file|hidden|image|password|radio|reset|submit|text)$/,
+ "4":"value"
+ },
+ "inside":"form"
+ },
+ "ins":
+ {
+ "attributes":
+ {
+ "0":"cite",
+ "datetime":/^([0-9]){8}/
+ }
+ },
+ "24":"kbd",
+ "label":
+ {
+ "attributes":[
+ "for"
+ ],
+ "inside":"form"
+ },
+ "25":"legend",
+ "26":"li",
+ "link":
+ {
+ "attributes":
+ {
+ "0":"charset",
+ "1":"href",
+ "2":"hreflang",
+ "media":/^(all|braille|print|projection|screen|speech|,|;| )+$/i,
+ //next comment line required by Opera!
+ /*"rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,*/
+ "rel":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
+ "rev":/^(alternate|appendix|bookmark|chapter|contents|copyright|glossary|help|home|index|next|prev|section|start|stylesheet|subsection| |shortcut|icon)+$/i,
+ "3":"type"
+ },
+ "inside":"head"
+ },
+ "map":
+ {
+ "attributes":[
+ "id",
+ "name"
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/278df6aa7139
changeset: 2612:278df6aa7139
user: James Taylor <james(a)jamestaylor.org>
date: Fri Aug 21 14:28:46 2009 -0400
description:
Repack jstore without comments
2 file(s) affected in this change:
static/scripts/jquery.jstore-all.js
static/scripts/packed/jquery.jstore-all.js
diffs (118 lines):
diff -r 52a145b22b69 -r 278df6aa7139 static/scripts/jquery.jstore-all.js
--- a/static/scripts/jquery.jstore-all.js Sun Aug 23 12:31:20 2009 -0400
+++ b/static/scripts/jquery.jstore-all.js Fri Aug 21 14:28:46 2009 -0400
@@ -1,4 +1,4 @@
-/*!
+/*
* jStore - Persistent Client-Side Storage
*
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
@@ -71,7 +71,7 @@
return Class;
};
})();
-/*!
+/*
* jStore Delegate Framework
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
*/
@@ -357,7 +357,7 @@
});
})(jQuery);
-/*!
+/*
* jStore DOM Storage Engine
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
*/
@@ -426,7 +426,7 @@
$.jStore.EngineOrder[ 1 ] = 'local';
})(jQuery);
-/*!
+/*
* jStore Flash Storage Engine
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
* jStore.swf Copyright (c) 2008 Daniel Bulli (http://www.nuff-respec.com)
@@ -551,7 +551,7 @@
function flash_ready(){
$.jStore.delegate.trigger('flash-ready');
}
-/*!
+/*
* jStore Google Gears Storage Engine
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
*/
@@ -621,7 +621,7 @@
$.jStore.EngineOrder[ 3 ] = 'gears';
})(jQuery);
-/*!
+/*
* jStore HTML5 Specification Storage Engine
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
*/
@@ -693,7 +693,7 @@
$.jStore.EngineOrder[ 0 ] = 'html5';
})(jQuery);
-/*!*
+/*
* jStore IE Storage Engine
* Copyright (c) 2009 Eric Garside (http://eric.garside.name)
*/
@@ -745,4 +745,4 @@
// Store the ordering preference
$.jStore.EngineOrder[ 4 ] = 'ie';
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff -r 52a145b22b69 -r 278df6aa7139 static/scripts/packed/jquery.jstore-all.js
--- a/static/scripts/packed/jquery.jstore-all.js Sun Aug 23 12:31:20 2009 -0400
+++ b/static/scripts/packed/jquery.jstore-all.js Fri Aug 21 14:28:46 2009 -0400
@@ -1,41 +1,1 @@
-/*
- * jStore - Persistent Client-Side Storage
- *
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- *
- * Dual licensed under:
- * MIT: http://www.opensource.org/licenses/mit-license.php
- * GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
- */
-(function(){var a=false,b=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(g){var f=this.prototype;a=true;var e=new this();a=false;for(var d in g){e[d]=typeof g[d]=="function"&&typeof f[d]=="function"&&b.test(g[d])?(function(h,i){return function(){var k=this._super;this._super=f[h];var j=i.apply(this,arguments);this._super=k;return j}})(d,g[d]):g[d]}function c(){if(!a&&this.init){this.init.apply(this,arguments)}}c.prototype=e;c.constructor=c;c.extend=arguments.callee;return c}})();
-/*
- * jStore Delegate Framework
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function(a){this.jStoreDelegate=Class.extend({init:function(b){this.parent=b;this.callbacks={}},bind:function(b,c){if(!a.isFunction(c)){return this}if(!this.callbacks[b]){this.callbacks[b]=[]}this.callbacks[b].push(c);return this},trigger:function(){var d=this.parent,c=[].slice.call(arguments),e=c.shift(),b=this.callbacks[e];if(!b){return false}a.each(b,function(){this.apply(d,c)});return this}})})(jQuery);(function(a){a.jStore={};a.extend(a.jStore,{EngineOrder:[],Availability:{},Engines:{},Instances:{},CurrentEngine:null,defaults:{project:null,engine:null,autoload:true,flash:"jStore.Flash.html"},isReady:false,isFlashReady:false,delegate:new jStoreDelegate(a.jStore).bind("jStore-ready",function(b){a.jStore.isReady=true;if(a.jStore.defaults.autoload){b.connect()}}).bind("flash-ready",function(){a.jStore.isFlashReady=true})});a.jStore.ready=function(b){if(a.jStore.isReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("jStore-ready",b)}};a.jStore.fail
=function(b){a.jStore.delegate.bind("jStore-failure",b)};a.jStore.flashReady=function(b){if(a.jStore.isFlashReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("flash-ready",b)}};a.jStore.use=function(d,g,c){g=g||a.jStore.defaults.project||location.hostname.replace(/\./g,"-")||"unknown";var f=a.jStore.Engines[d.toLowerCase()]||null,b=(c?c+".":"")+g+"."+d;if(!f){throw"JSTORE_ENGINE_UNDEFINED"}f=new f(g,b);if(a.jStore.Instances[b]){throw"JSTORE_JRI_CONFLICT"}if(f.isAvailable()){a.jStore.Instances[b]=f;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=f}a.jStore.delegate.trigger("jStore-ready",f)}else{if(!f.autoload){throw"JSTORE_ENGINE_UNAVILABLE"}else{f.included(function(){if(this.isAvailable()){a.jStore.Instances[b]=this;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=this}a.jStore.delegate.trigger("jStore-ready",this)}else{a.jStore.delegate.trigger("jStore-failure",this)}}).include()}}};a.jStore.setCurrentEngine=function(b){if(!a.jStore.Instanc
es.length){return a.jStore.FindEngine()}if(!b&&a.jStore.Instances.length>=1){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[0]);return a.jStore.CurrentEngine=a.jStore.Instances[0]}if(b&&a.jStore.Instances[b]){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[b]);return a.jStore.CurrentEngine=a.jStore.Instances[b]}throw"JSTORE_JRI_NO_MATCH"};a.jStore.FindEngine=function(){a.each(a.jStore.EngineOrder,function(b){if(a.jStore.Availability[this]()){a.jStore.use(this,a.jStore.defaults.project,"default");return false}})};a.jStore.store=function(b,c){if(!a.jStore.CurrentEngine){return false}if(!c){return a.jStore.CurrentEngine.get(b)}return a.jStore.CurrentEngine.set(b,c)};a.jStore.remove=function(b){if(!a.jStore.CurrentEngine){return false}return a.jStore.CurrentEngine.rem(b)};a.fn.store=function(c,d){if(!a.jStore.CurrentEngine){return this}var b=a.jStore.store(c,d);return !d?b:this};a.fn.removeStore=function(b){a.jStore.remove(b);return this};a.jStore.load=f
unction(){if(a.jStore.defaults.engine){return a.jStore.use(a.jStore.defaults.engine,a.jStore.defaults.project,"default")}try{a.jStore.FindEngine()}catch(b){}}})(jQuery);(function(a){this.StorageEngine=Class.extend({init:function(c,b){this.project=c;this.jri=b;this.data={};this.limit=-1;this.includes=[];this.delegate=new jStoreDelegate(this).bind("engine-ready",function(){this.isReady=true}).bind("engine-included",function(){this.hasIncluded=true});this.autoload=false;this.isReady=false;this.hasIncluded=false},include:function(){var b=this,d=this.includes.length,c=0;a.each(this.includes,function(){a.ajax({type:"get",url:this,dataType:"script",cache:true,success:function(){c++;if(c==d){b.delegate.trigger("engine-included")}}})})},isAvailable:function(){return false},ready:function(b){if(this.isReady){b.apply(this)}else{this.delegate.bind("engine-ready",b)}return this},included:function(b){if(this.hasIncluded){b.apply(this)}else{this.delegate.bind("engine-included",b)}return th
is},get:function(b){return this.data[b]||null},set:function(b,c){this.data[b]=c;return c},rem:function(b){var c=this.data[b];this.data[b]=null;return c}})})(jQuery);
-/*
- * jStore DOM Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function(c){var b=c.jStore.Availability.session=function(){return !!window.sessionStorage},a=c.jStore.Availability.local=function(){return !!(window.localStorage||window.globalStorage)};this.jStoreDom=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="DOM";this.limit=5*1024*1024},connect:function(){this.delegate.trigger("engine-ready")},get:function(e){var d=this.db.getItem(e);return d&&d.value?d.value:d},set:function(d,e){this.db.setItem(d,e);return e},rem:function(e){var d=this.get(e);this.db.removeItem(e);return d}});this.jStoreLocal=jStoreDom.extend({connect:function(){this.db=!window.globalStorage?window.localStorage:window.globalStorage[location.hostname];this._super()},isAvailable:a});this.jStoreSession=jStoreDom.extend({connect:function(){this.db=sessionStorage;this._super()},isAvailable:b});c.jStore.Engines.local=jStoreLocal;c.jStore.Engines.session=jStoreSession;c.jStore.EngineOrder[1]="local"})(jQuery);
-/*
- * jStore Flash Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- * jStore.swf Copyright (c) 2008 Daniel Bulli (http://www.nuff-respec.com)
- */
-(function(b){var a=b.jStore.Availability.flash=function(){return !!(b.jStore.hasFlash("8.0.0"))};this.jStoreFlash=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="Flash";var c=this;b.jStore.flashReady(function(){c.flashReady()})},connect:function(){var c="jstore-flash-embed-"+this.project;b(document.body).append('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="'+b.jStore.defaults.flash+'"></iframe>')},flashReady:function(f){var c=b("#jStoreFlashFrame")[0];if(c.Document&&b.isFunction(c.Document.jStoreFlash.f_get_cookie)){this.db=c.Document.jStoreFlash}else{if(c.contentWindow&&c.contentWindow.document){var d=c.contentWindow.document;if(b.isFunction(b("object",b(d))[0].f_get_cookie)){this.db=b("object",b(d))[0]}else{if(b.isFunction(b("embed",b(d))[0].f_get_cookie)){this.db=b("embed",b(d))[0]}}}}if(this.db){this.delegate.trigger("engine-ready")}},isAvailable:a,get:function(d){var c=this.db.f_g
et_cookie(d);return c=="null"?null:c},set:function(c,d){this.db.f_set_cookie(c,d);return d},rem:function(c){var d=this.get(c);this.db.f_delete_cookie(c);return d}});b.jStore.Engines.flash=jStoreFlash;b.jStore.EngineOrder[2]="flash";b.jStore.hasFlash=function(c){var e=b.jStore.flashVersion().match(/\d+/g),f=c.match(/\d+/g);for(var d=0;d<3;d++){e[d]=parseInt(e[d]||0);f[d]=parseInt(f[d]||0);if(e[d]<f[d]){return false}if(e[d]>f[d]){return true}}return true};b.jStore.flashVersion=function(){try{try{var c=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");try{c.AllowScriptAccess="always"}catch(d){return"6,0,0"}}catch(d){}return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}catch(d){try{if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){return(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}}catch(d){}}r
eturn"0,0,0"}})(jQuery);function flash_ready(){$.jStore.delegate.trigger("flash-ready")}
-/*
- * jStore Google Gears Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function(b){var a=b.jStore.Availability.gears=function(){return !!(window.google&&window.google.gears)};this.jStoreGears=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="Google Gears";this.includes.push("http://code.google.com/apis/gears/gears_init.js");this.autoload=true},connect:function(){var c=this.db=google.gears.factory.create("beta.database");c.open("jstore-"+this.project);c.execute("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)");this.updateCache()},updateCache:function(){var c=this.db.execute("SELECT k,v FROM jstore");while(c.isValidRow()){this.data[c.field(0)]=c.field(1);c.next()}c.close();this.delegate.trigger("engine-ready")},isAvailable:a,set:function(d,e){var c=this.db;c.execute("BEGIN");c.execute("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[d,e]);c.execute("COMMIT");return this._super(d,e)},rem:function(d){var c=this.db;c.execute("BEGIN");c.execute("DELETE FROM jstore WHERE k = ?",[d]);c.ex
ecute("COMMIT");return this._super(d)}});b.jStore.Engines.gears=jStoreGears;b.jStore.EngineOrder[3]="gears"})(jQuery);
-/*
- * jStore HTML5 Specification Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function(b){var a=b.jStore.Availability.html5=function(){return !!window.openDatabase};this.jStoreHtml5=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="HTML5";this.limit=1024*200},connect:function(){var c=this.db=openDatabase("jstore-"+this.project,"1.0",this.project,this.limit);if(!c){throw"JSTORE_ENGINE_HTML5_NODB"}c.transaction(function(d){d.executeSql("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)")});this.updateCache()},updateCache:function(){var c=this;this.db.transaction(function(d){d.executeSql("SELECT k,v FROM jstore",[],function(f,e){var h=e.rows,g=0,j;for(;g<h.length;++g){j=h.item(g);c.data[j.k]=j.v}c.delegate.trigger("engine-ready")})})},isAvailable:a,set:function(c,d){this.db.transaction(function(e){e.executeSql("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[c,d])});return this._super(c,d)},rem:function(c){this.db.transaction(function(d){d.executeSql("DELETE FROM jstore WHERE k = ?",[c])});ret
urn this._super(c)}});b.jStore.Engines.html5=jStoreHtml5;b.jStore.EngineOrder[0]="html5"})(jQuery);
-/**
- * jStore IE Storage Engine
- * Copyright (c) 2009 Eric Garside (http://eric.garside.name)
- */
-(function(b){var a=b.jStore.Availability.ie=function(){return !!window.ActiveXObject};this.jStoreIE=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="IE";this.limit=64*1024},connect:function(){this.db=b('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-'+this.project+'"></div>').appendTo(document.body).get(0);this.delegate.trigger("engine-ready")},isAvailable:a,get:function(c){this.db.load(this.project);return this.db.getAttribute(c)},set:function(c,d){this.db.setAttribute(c,d);this.db.save(this.project);return d},rem:function(c){var d=this.get(c);this.db.removeAttribute(c);this.db.save(this.project);return d}});b.jStore.Engines.ie=jStoreIE;b.jStore.EngineOrder[4]="ie"})(jQuery);
\ No newline at end of file
+(function(){var a=false,b=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){};Class.extend=function(g){var f=this.prototype;a=true;var e=new this();a=false;for(var d in g){e[d]=typeof g[d]=="function"&&typeof f[d]=="function"&&b.test(g[d])?(function(h,i){return function(){var k=this._super;this._super=f[h];var j=i.apply(this,arguments);this._super=k;return j}})(d,g[d]):g[d]}function c(){if(!a&&this.init){this.init.apply(this,arguments)}}c.prototype=e;c.constructor=c;c.extend=arguments.callee;return c}})();(function(a){this.jStoreDelegate=Class.extend({init:function(b){this.parent=b;this.callbacks={}},bind:function(b,c){if(!a.isFunction(c)){return this}if(!this.callbacks[b]){this.callbacks[b]=[]}this.callbacks[b].push(c);return this},trigger:function(){var d=this.parent,c=[].slice.call(arguments),e=c.shift(),b=this.callbacks[e];if(!b){return false}a.each(b,function(){this.apply(d,c)});return this}})})(jQuery);(function(a){a.jStore={};a.extend(a.jStore,{Engin
eOrder:[],Availability:{},Engines:{},Instances:{},CurrentEngine:null,defaults:{project:null,engine:null,autoload:true,flash:"jStore.Flash.html"},isReady:false,isFlashReady:false,delegate:new jStoreDelegate(a.jStore).bind("jStore-ready",function(b){a.jStore.isReady=true;if(a.jStore.defaults.autoload){b.connect()}}).bind("flash-ready",function(){a.jStore.isFlashReady=true})});a.jStore.ready=function(b){if(a.jStore.isReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("jStore-ready",b)}};a.jStore.fail=function(b){a.jStore.delegate.bind("jStore-failure",b)};a.jStore.flashReady=function(b){if(a.jStore.isFlashReady){b.apply(a.jStore,[a.jStore.CurrentEngine])}else{a.jStore.delegate.bind("flash-ready",b)}};a.jStore.use=function(d,g,c){g=g||a.jStore.defaults.project||location.hostname.replace(/\./g,"-")||"unknown";var f=a.jStore.Engines[d.toLowerCase()]||null,b=(c?c+".":"")+g+"."+d;if(!f){throw"JSTORE_ENGINE_UNDEFINED"}f=new f(g,b);if(a.jStore.Instances[b]){
throw"JSTORE_JRI_CONFLICT"}if(f.isAvailable()){a.jStore.Instances[b]=f;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=f}a.jStore.delegate.trigger("jStore-ready",f)}else{if(!f.autoload){throw"JSTORE_ENGINE_UNAVILABLE"}else{f.included(function(){if(this.isAvailable()){a.jStore.Instances[b]=this;if(!a.jStore.CurrentEngine){a.jStore.CurrentEngine=this}a.jStore.delegate.trigger("jStore-ready",this)}else{a.jStore.delegate.trigger("jStore-failure",this)}}).include()}}};a.jStore.setCurrentEngine=function(b){if(!a.jStore.Instances.length){return a.jStore.FindEngine()}if(!b&&a.jStore.Instances.length>=1){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[0]);return a.jStore.CurrentEngine=a.jStore.Instances[0]}if(b&&a.jStore.Instances[b]){a.jStore.delegate.trigger("jStore-ready",a.jStore.Instances[b]);return a.jStore.CurrentEngine=a.jStore.Instances[b]}throw"JSTORE_JRI_NO_MATCH"};a.jStore.FindEngine=function(){a.each(a.jStore.EngineOrder,function(b){if(a.jStore.Availabilit
y[this]()){a.jStore.use(this,a.jStore.defaults.project,"default");return false}})};a.jStore.store=function(b,c){if(!a.jStore.CurrentEngine){return false}if(!c){return a.jStore.CurrentEngine.get(b)}return a.jStore.CurrentEngine.set(b,c)};a.jStore.remove=function(b){if(!a.jStore.CurrentEngine){return false}return a.jStore.CurrentEngine.rem(b)};a.fn.store=function(c,d){if(!a.jStore.CurrentEngine){return this}var b=a.jStore.store(c,d);return !d?b:this};a.fn.removeStore=function(b){a.jStore.remove(b);return this};a.jStore.load=function(){if(a.jStore.defaults.engine){return a.jStore.use(a.jStore.defaults.engine,a.jStore.defaults.project,"default")}try{a.jStore.FindEngine()}catch(b){}}})(jQuery);(function(a){this.StorageEngine=Class.extend({init:function(c,b){this.project=c;this.jri=b;this.data={};this.limit=-1;this.includes=[];this.delegate=new jStoreDelegate(this).bind("engine-ready",function(){this.isReady=true}).bind("engine-included",function(){this.hasIncluded=true});this.aut
oload=false;this.isReady=false;this.hasIncluded=false},include:function(){var b=this,d=this.includes.length,c=0;a.each(this.includes,function(){a.ajax({type:"get",url:this,dataType:"script",cache:true,success:function(){c++;if(c==d){b.delegate.trigger("engine-included")}}})})},isAvailable:function(){return false},ready:function(b){if(this.isReady){b.apply(this)}else{this.delegate.bind("engine-ready",b)}return this},included:function(b){if(this.hasIncluded){b.apply(this)}else{this.delegate.bind("engine-included",b)}return this},get:function(b){return this.data[b]||null},set:function(b,c){this.data[b]=c;return c},rem:function(b){var c=this.data[b];this.data[b]=null;return c}})})(jQuery);(function(c){var b=c.jStore.Availability.session=function(){return !!window.sessionStorage},a=c.jStore.Availability.local=function(){return !!(window.localStorage||window.globalStorage)};this.jStoreDom=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="DOM";this.limit=5*1024*1
024},connect:function(){this.delegate.trigger("engine-ready")},get:function(e){var d=this.db.getItem(e);return d&&d.value?d.value:d},set:function(d,e){this.db.setItem(d,e);return e},rem:function(e){var d=this.get(e);this.db.removeItem(e);return d}});this.jStoreLocal=jStoreDom.extend({connect:function(){this.db=!window.globalStorage?window.localStorage:window.globalStorage[location.hostname];this._super()},isAvailable:a});this.jStoreSession=jStoreDom.extend({connect:function(){this.db=sessionStorage;this._super()},isAvailable:b});c.jStore.Engines.local=jStoreLocal;c.jStore.Engines.session=jStoreSession;c.jStore.EngineOrder[1]="local"})(jQuery);(function(b){var a=b.jStore.Availability.flash=function(){return !!(b.jStore.hasFlash("8.0.0"))};this.jStoreFlash=StorageEngine.extend({init:function(e,d){this._super(e,d);this.type="Flash";var c=this;b.jStore.flashReady(function(){c.flashReady()})},connect:function(){var c="jstore-flash-embed-"+this.project;b(document.body).append('<if
rame style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="'+b.jStore.defaults.flash+'"></iframe>')},flashReady:function(f){var c=b("#jStoreFlashFrame")[0];if(c.Document&&b.isFunction(c.Document.jStoreFlash.f_get_cookie)){this.db=c.Document.jStoreFlash}else{if(c.contentWindow&&c.contentWindow.document){var d=c.contentWindow.document;if(b.isFunction(b("object",b(d))[0].f_get_cookie)){this.db=b("object",b(d))[0]}else{if(b.isFunction(b("embed",b(d))[0].f_get_cookie)){this.db=b("embed",b(d))[0]}}}}if(this.db){this.delegate.trigger("engine-ready")}},isAvailable:a,get:function(d){var c=this.db.f_get_cookie(d);return c=="null"?null:c},set:function(c,d){this.db.f_set_cookie(c,d);return d},rem:function(c){var d=this.get(c);this.db.f_delete_cookie(c);return d}});b.jStore.Engines.flash=jStoreFlash;b.jStore.EngineOrder[2]="flash";b.jStore.hasFlash=function(c){var e=b.jStore.flashVersion().match(/\d+/g),f=c.match(/\d+/g);for(var d=0;d<
3;d++){e[d]=parseInt(e[d]||0);f[d]=parseInt(f[d]||0);if(e[d]<f[d]){return false}if(e[d]>f[d]){return true}}return true};b.jStore.flashVersion=function(){try{try{var c=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");try{c.AllowScriptAccess="always"}catch(d){return"6,0,0"}}catch(d){}return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}catch(d){try{if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){return(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}}catch(d){}}return"0,0,0"}})(jQuery);function flash_ready(){$.jStore.delegate.trigger("flash-ready")}(function(b){var a=b.jStore.Availability.gears=function(){return !!(window.google&&window.google.gears)};this.jStoreGears=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="Google Gears";this.includes.push("http://code.google.com/ap
is/gears/gears_init.js");this.autoload=true},connect:function(){var c=this.db=google.gears.factory.create("beta.database");c.open("jstore-"+this.project);c.execute("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)");this.updateCache()},updateCache:function(){var c=this.db.execute("SELECT k,v FROM jstore");while(c.isValidRow()){this.data[c.field(0)]=c.field(1);c.next()}c.close();this.delegate.trigger("engine-ready")},isAvailable:a,set:function(d,e){var c=this.db;c.execute("BEGIN");c.execute("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[d,e]);c.execute("COMMIT");return this._super(d,e)},rem:function(d){var c=this.db;c.execute("BEGIN");c.execute("DELETE FROM jstore WHERE k = ?",[d]);c.execute("COMMIT");return this._super(d)}});b.jStore.Engines.gears=jStoreGears;b.jStore.EngineOrder[3]="gears"})(jQuery);(function(b){var a=b.jStore.Availability.html5=function(){return !!window.openDatabase};this.jStoreHtml5=StorageEngine.extend({init:
function(d,c){this._super(d,c);this.type="HTML5";this.limit=1024*200},connect:function(){var c=this.db=openDatabase("jstore-"+this.project,"1.0",this.project,this.limit);if(!c){throw"JSTORE_ENGINE_HTML5_NODB"}c.transaction(function(d){d.executeSql("CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)")});this.updateCache()},updateCache:function(){var c=this;this.db.transaction(function(d){d.executeSql("SELECT k,v FROM jstore",[],function(f,e){var h=e.rows,g=0,j;for(;g<h.length;++g){j=h.item(g);c.data[j.k]=j.v}c.delegate.trigger("engine-ready")})})},isAvailable:a,set:function(c,d){this.db.transaction(function(e){e.executeSql("INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)",[c,d])});return this._super(c,d)},rem:function(c){this.db.transaction(function(d){d.executeSql("DELETE FROM jstore WHERE k = ?",[c])});return this._super(c)}});b.jStore.Engines.html5=jStoreHtml5;b.jStore.EngineOrder[0]="html5"})(jQuery);(function(b){var a=b.jStore.Avail
ability.ie=function(){return !!window.ActiveXObject};this.jStoreIE=StorageEngine.extend({init:function(d,c){this._super(d,c);this.type="IE";this.limit=64*1024},connect:function(){this.db=b('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-'+this.project+'"></div>').appendTo(document.body).get(0);this.delegate.trigger("engine-ready")},isAvailable:a,get:function(c){this.db.load(this.project);return this.db.getAttribute(c)},set:function(c,d){this.db.setAttribute(c,d);this.db.save(this.project);return d},rem:function(c){var d=this.get(c);this.db.removeAttribute(c);this.db.save(this.project);return d}});b.jStore.Engines.ie=jStoreIE;b.jStore.EngineOrder[4]="ie"})(jQuery);
\ No newline at end of file
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/5f60d79f483f
changeset: 2613:5f60d79f483f
user: James Taylor <james(a)jamestaylor.org>
date: Sun Aug 23 14:01:08 2009 -0400
description:
Remove logging from galaxy.base
2 file(s) affected in this change:
static/scripts/galaxy.base.js
static/scripts/packed/galaxy.base.js
diffs (26 lines):
diff -r 278df6aa7139 -r 5f60d79f483f static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js Fri Aug 21 14:28:46 2009 -0400
+++ b/static/scripts/galaxy.base.js Sun Aug 23 14:01:08 2009 -0400
@@ -77,14 +77,12 @@
};
function attach_popupmenu( button_element, wrapper ) {
- console.log( button_element, wrapper );
var clean = function() {
wrapper.unbind().hide();
$("#popup-helper").unbind( "click.popupmenu" ).hide();
// $(document).unbind( "click.popupmenu" );
};
var click = function( e ) {
- console.log( e );
var o = $(button_element).offset();
$("#popup-helper").bind( "click.popupmenu", clean ).show();
// $(document).bind( "click.popupmenu", clean );
diff -r 278df6aa7139 -r 5f60d79f483f static/scripts/packed/galaxy.base.js
--- a/static/scripts/packed/galaxy.base.js Fri Aug 21 14:28:46 2009 -0400
+++ b/static/scripts/packed/galaxy.base.js Sun Aug 23 14:01:08 2009 -0400
@@ -1,1 +1,1 @@
-$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.show()})});function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ens
ure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></div>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){console.log(b,d);var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){console.log(g);var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)};
\ No newline at end of file
+$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.show()})});function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ens
ure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></div>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)};
\ No newline at end of file
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/b8205e8e077f
changeset: 2614:b8205e8e077f
user: James Taylor <james(a)jamestaylor.org>
date: Sun Aug 23 15:16:43 2009 -0400
description:
Use small messages by default
5 file(s) affected in this change:
static/june_2007_style/base.css.tmpl
static/june_2007_style/blue/base.css
static/welcome.html
templates/message.mako
templates/tool_executed.mako
diffs (406 lines):
diff -r 5f60d79f483f -r b8205e8e077f static/june_2007_style/base.css.tmpl
--- a/static/june_2007_style/base.css.tmpl Sun Aug 23 14:01:08 2009 -0400
+++ b/static/june_2007_style/base.css.tmpl Sun Aug 23 15:16:43 2009 -0400
@@ -187,8 +187,7 @@
## Messages
-.errormessage, .warningmessage, .donemessage, .infomessage, .welcomeBlue, .welcomeRed , .screencastBox, .yellowbox, .redbox, .bluebox, .greenbox
-{
+.errormessagelarge, .warningmessagelarge, .donemessagelarge, .infomessagelarge {
padding: 10px;
padding-left: 52px;
min-height: 32px;
@@ -199,127 +198,35 @@
background-position: 10px 10px;
}
-.warningmessage
-{
+.warningmessagelarge {
background-image: url(warn_message_icon.png);
border-color: $warn_message_border;
background-color: $warn_message_bg;
}
-.donemessage
-{
+.donemessagelarge {
background-image: url(done_message_icon.png);
border-color: $done_message_border;
background-color: $done_message_bg;
}
-.infomessage
-{
+.infomessagelarge {
background-image: url(info_message_icon.png);
border-color: $info_message_border;
background-color: $info_message_bg;
}
-.welcomeBlue
-{
- padding-left: 10px;
- border-color: $info_message_border;
- background-color: $info_message_bg;
- background-image: none;
-}
-
-.welcomeRed
-{
- padding-left: 10px;
- border-color: $error_message_border;
- background-color: $error_message_bg;
- background-image: none;
-}
-
-.screencastBox
-{
+.screencastBox {
padding-left: 10px;
border-color: #AAAA66;
background-color: #FFFFCC;
background-image: none;
}
-.redbox
-{
- border: none;
- padding: 10px;
- border-color: #000000;
- background-color: #FF6666;
- background-image: none;
- border-right-width: 1px;
- border-right-style: dotted;
- border-bottom-width: 1px;
- border-bottom-style: dotted;
- margin-top: 5px;
- min-height: 32px;
-
-}
-
-.yellowbox
-{
- border: none;
- padding: 10px;
- border-color: #000000;
- background-color: #FFCC00;
- background-image: none;
- border-right-width: 1px;
- border-right-style: dotted;
- border-bottom-width: 1px;
- border-bottom-style: dotted;
- margin-top: 5px;
- min-height: 32px;
-}
-
-.bluebox
-{
- border: none;
- padding: 10px;
- border-color: #000000;
- background-color: #6666FF;
- background-image: none;
- border-right-width: 1px;
- border-right-style: dotted;
- border-bottom-width: 1px;
- border-bottom-style: dotted;
- margin-top: 5px;
- color: #FFFFFF;
- min-height: 32px;
-}
-
-.greenbox
-{
- border: none;
- padding: 10px;
- border-color: #000000;
- background-color: #00CC00;
- background-image: none;
- border-right-width: 1px;
- border-right-style: dotted;
- border-bottom-width: 1px;
- border-bottom-style: dotted;
- margin-top: 5px;
- min-height: 32px;
-}
-
-.redbox li, .yellowbox li, .bluebox li, .greenbox li {
- list-style: disc;
- text-transform: none;
- list-style-position: inside;
- list-style-image: none;
- margin: 3px;
-}
-
-
-.errormessagesmall, .warningmessagesmall, .donemessagesmall, .infomessagesmall
-{
+.errormessage, .warningmessage, .donemessage, .infomessage, .errormessagesmall, .warningmessagesmall, .donemessagesmall, .infomessagesmall {
padding: 5px;
padding-left: 25px;
- min-height: 25px;
+ min-height: 15px;
border: 1px solid $error_message_border;
background-color: $error_message_bg;
background-image: url(error_small.png);
@@ -327,83 +234,70 @@
background-position: 5px 5px;
}
-.warningmessagesmall
-{
+.warningmessage, .warningmessagesmall {
background-image: url(warn_small.png);
border-color: $warn_message_border;
background-color: $warn_message_bg;
}
-.donemessagesmall
-{
+.donemessage, .donemessagesmall {
background-image: url(ok_small.png);
border-color: $done_message_border;
background-color: $done_message_bg;
}
-.infomessagesmall
-{
+.infomessage, .infomessagesmall {
background-image: url(info_small.png);
border-color: $info_message_border;
background-color: $info_message_bg;
}
-.errormark, .warningmark, .donemark, .infomark, .ok_bgr, .err_bgr
-{
+.errormark, .warningmark, .donemark, .infomark, .ok_bgr, .err_bgr {
padding-left: 20px;
min-height: 15px;
background: url(error_small.png) no-repeat;
}
-.warningmark
-{
+.warningmark {
background-image: url(warn_small.png);
}
-.donemark
-{
+.donemark {
background-image: url(ok_small.png);
}
-.infomark, .ok_bgr
-{
+.infomark, .ok_bgr {
background-image: url(info_small.png);
}
-table.colored
-{
- border-top: solid $table_border 1px;
- border-bottom: solid $table_border 1px;
+table.colored {
+ border-top: solid $table_border 1px;
+ border-bottom: solid $table_border 1px;
}
-table.colored td, table.colored th
-{
+table.colored td, table.colored th {
text-align: left;
padding: 5px;
}
-table.colored tr.header
-{
- background: $table_header_bg;
- background-image: url(form_title_bg.png);
- background-repeat: repeat-x;
- background-position: top;
- border-bottom: solid $table_border 1px;
- font-weight: bold;
+table.colored tr.header {
+ background: $table_header_bg;
+ background-image: url(form_title_bg.png);
+ background-repeat: repeat-x;
+ background-position: top;
+ border-bottom: solid $table_border 1px;
+ font-weight: bold;
}
-table.colored tr
-{
- background: $table_row_bg;
+table.colored tr {
+ background: $table_row_bg;
}
-table.colored tr.odd_row
-{
- background: $odd_row_bg;
+table.colored tr.odd_row {
+ background: $odd_row_bg;
}
-div.debug
-{
+div.debug {
margin: 10px;
padding: 5px;
background: #FFFF99;
@@ -411,8 +305,7 @@
color: black;
}
-div.odd_row
-{
+div.odd_row {
background: $odd_row_bg;
}
@@ -422,51 +315,44 @@
## Tool panel stuff
-td.panel-body
-{
+td.panel-body {
background: white;
color: $base_text;
background: $menu_bg_over url(menu_bg.png) top repeat-x;
}
-div.toolSectionPad
-{
+div.toolSectionPad {
margin: 0;
padding: 0;
height: 5px;
font-size: 0px;
}
-div.toolSectionDetailsInner
-{
- margin-left: 5px;
- margin-right: 5px;
+div.toolSectionDetailsInner {
+ margin-left: 5px;
+ margin-right: 5px;
}
-div.toolSectionTitle
-{
- padding-bottom: 0px;
- font-weight: bold;
+div.toolSectionTitle {
+ padding-bottom: 0px;
+ font-weight: bold;
}
-div.toolTitle
-{
- padding-top: 5px;
- padding-bottom: 5px;
- margin-left: 16px;
- margin-right: 10px;
- display: list-item;
- list-style: square outside;
+div.toolTitle {
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin-left: 16px;
+ margin-right: 10px;
+ display: list-item;
+ list-style: square outside;
}
-span.toolParameterExpandableCollapsable
-{
- font-weight: bold;
- cursor: pointer;
+span.toolParameterExpandableCollapsable {
+ font-weight: bold;
+ cursor: pointer;
}
-ul.toolParameterExpandableCollapsable
-{
- list-style: none;
+ul.toolParameterExpandableCollapsable {
+ list-style: none;
}
ul.manage-table-actions {
diff -r 5f60d79f483f -r b8205e8e077f static/june_2007_style/blue/base.css
--- a/static/june_2007_style/blue/base.css Sun Aug 23 14:01:08 2009 -0400
+++ b/static/june_2007_style/blue/base.css Sun Aug 23 15:16:43 2009 -0400
@@ -35,22 +35,15 @@
div.form-row-error-message{width:300px;float:left;color:red;font-weight:bold;padding:3px 0 0 1em;}
select,input,textarea{font:inherit;font-size:115%;}
select,textarea,input[type="text"],input[type="file"],input[type="password"]{-webkit-box-sizing:border-box;max-width:300px;}
-.errormessage,.warningmessage,.donemessage,.infomessage,.welcomeBlue,.welcomeRed,.screencastBox,.yellowbox,.redbox,.bluebox,.greenbox{padding:10px;padding-left:52px;min-height:32px;border:1px solid #AA6666;background-color:#FFCCCC;background-image:url(error_message_icon.png);background-repeat:no-repeat;background-position:10px 10px;}
-.warningmessage{background-image:url(warn_message_icon.png);border-color:#AAAA66;background-color:#FFFFCC;}
-.donemessage{background-image:url(done_message_icon.png);border-color:#66AA66;background-color:#CCFFCC;}
-.infomessage{background-image:url(info_message_icon.png);border-color:#6666AA;background-color:#CCCCFF;}
-.welcomeBlue{padding-left:10px;border-color:#6666AA;background-color:#CCCCFF;background-image:none;}
-.welcomeRed{padding-left:10px;border-color:#AA6666;background-color:#FFCCCC;background-image:none;}
+.errormessagelarge,.warningmessagelarge,.donemessagelarge,.infomessagelarge{padding:10px;padding-left:52px;min-height:32px;border:1px solid #AA6666;background-color:#FFCCCC;background-image:url(error_message_icon.png);background-repeat:no-repeat;background-position:10px 10px;}
+.warningmessagelarge{background-image:url(warn_message_icon.png);border-color:#AAAA66;background-color:#FFFFCC;}
+.donemessagelarge{background-image:url(done_message_icon.png);border-color:#66AA66;background-color:#CCFFCC;}
+.infomessagelarge{background-image:url(info_message_icon.png);border-color:#6666AA;background-color:#CCCCFF;}
.screencastBox{padding-left:10px;border-color:#AAAA66;background-color:#FFFFCC;background-image:none;}
-.redbox{border:none;padding:10px;border-color:#000000;background-color:#FF6666;background-image:none;border-right-width:1px;border-right-style:dotted;border-bottom-width:1px;border-bottom-style:dotted;margin-top:5px;min-height:32px;}
-.yellowbox{border:none;padding:10px;border-color:#000000;background-color:#FFCC00;background-image:none;border-right-width:1px;border-right-style:dotted;border-bottom-width:1px;border-bottom-style:dotted;margin-top:5px;min-height:32px;}
-.bluebox{border:none;padding:10px;border-color:#000000;background-color:#6666FF;background-image:none;border-right-width:1px;border-right-style:dotted;border-bottom-width:1px;border-bottom-style:dotted;margin-top:5px;color:#FFFFFF;min-height:32px;}
-.greenbox{border:none;padding:10px;border-color:#000000;background-color:#00CC00;background-image:none;border-right-width:1px;border-right-style:dotted;border-bottom-width:1px;border-bottom-style:dotted;margin-top:5px;min-height:32px;}
-.redbox li,.yellowbox li,.bluebox li,.greenbox li{list-style:disc;text-transform:none;list-style-position:inside;list-style-image:none;margin:3px;}
-.errormessagesmall,.warningmessagesmall,.donemessagesmall,.infomessagesmall{padding:5px;padding-left:25px;min-height:25px;border:1px solid #AA6666;background-color:#FFCCCC;background-image:url(error_small.png);background-repeat:no-repeat;background-position:5px 5px;}
-.warningmessagesmall{background-image:url(warn_small.png);border-color:#AAAA66;background-color:#FFFFCC;}
-.donemessagesmall{background-image:url(ok_small.png);border-color:#66AA66;background-color:#CCFFCC;}
-.infomessagesmall{background-image:url(info_small.png);border-color:#6666AA;background-color:#CCCCFF;}
+.errormessage,.warningmessage,.donemessage,.infomessage,.errormessagesmall,.warningmessagesmall,.donemessagesmall,.infomessagesmall{padding:5px;padding-left:25px;min-height:15px;border:1px solid #AA6666;background-color:#FFCCCC;background-image:url(error_small.png);background-repeat:no-repeat;background-position:5px 5px;}
+.warningmessage,.warningmessagesmall{background-image:url(warn_small.png);border-color:#AAAA66;background-color:#FFFFCC;}
+.donemessage,.donemessagesmall{background-image:url(ok_small.png);border-color:#66AA66;background-color:#CCFFCC;}
+.infomessage,.infomessagesmall{background-image:url(info_small.png);border-color:#6666AA;background-color:#CCCCFF;}
.errormark,.warningmark,.donemark,.infomark,.ok_bgr,.err_bgr{padding-left:20px;min-height:15px;background:url(error_small.png) no-repeat;}
.warningmark{background-image:url(warn_small.png);}
.donemark{background-image:url(ok_small.png);}
diff -r 5f60d79f483f -r b8205e8e077f static/welcome.html
--- a/static/welcome.html Sun Aug 23 14:01:08 2009 -0400
+++ b/static/welcome.html Sun Aug 23 15:16:43 2009 -0400
@@ -37,7 +37,7 @@
<body>
<div class="document">
-<div class="infomessage">
+<div class="infomessagelarge">
<strong>We are hiring!</strong>
<hr>
Thanks to your support and an unprecedented level of usage, we are looking for an experienced software developer to join our team. For more information about this position, please, click <a target="_blank" href="https://www.bx.psu.edu/cgi-bin/trac.cgi/galaxy/wiki/PythonDeveloper">here</a>.
diff -r 5f60d79f483f -r b8205e8e077f templates/message.mako
--- a/templates/message.mako Sun Aug 23 14:01:08 2009 -0400
+++ b/templates/message.mako Sun Aug 23 15:16:43 2009 -0400
@@ -45,7 +45,7 @@
</script>
</%def>
-<div class="${message_type}message">${_(message)}</div>
+<div class="${message_type}messagelarge">${_(message)}</div>
## Render a message
<%def name="render_msg( msg, messagetype='done' )">
diff -r 5f60d79f483f -r b8205e8e077f templates/tool_executed.mako
--- a/templates/tool_executed.mako Sun Aug 23 14:01:08 2009 -0400
+++ b/templates/tool_executed.mako Sun Aug 23 15:16:43 2009 -0400
@@ -37,7 +37,7 @@
<body onLoad="main()">
-<div class="donemessage">
+<div class="donemessagelarge">
<p>The following job has been succesfully added to the queue:</p>
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/168a6ef6a99a
changeset: 2615:168a6ef6a99a
user: James Taylor <james(a)jamestaylor.org>
date: Sun Aug 23 17:13:04 2009 -0400
description:
IE fixes for menu buttons
3 file(s) affected in this change:
static/scripts/galaxy.base.js
templates/history/grid.mako
templates/workflow/list.mako
diffs (36 lines):
diff -r b8205e8e077f -r 168a6ef6a99a static/scripts/galaxy.base.js
--- a/static/scripts/galaxy.base.js Sun Aug 23 15:16:43 2009 -0400
+++ b/static/scripts/galaxy.base.js Sun Aug 23 17:13:04 2009 -0400
@@ -59,7 +59,7 @@
// container_element = container_element.parent();
// }
// ontainer_element).css( "position", "relative" );
- var menu_element = $( "<ul id='" + button_element.attr('id') + "-menu'></div>" );
+ var menu_element = $( "<ul id='" + button_element.attr('id') + "-menu'></ul>" );
$.each( options, function( k, v ) {
if ( v ) {
$( "<li/>" ).html( k ).click( v ).appendTo( menu_element );
diff -r b8205e8e077f -r 168a6ef6a99a templates/history/grid.mako
--- a/templates/history/grid.mako Sun Aug 23 15:16:43 2009 -0400
+++ b/templates/history/grid.mako Sun Aug 23 17:13:04 2009 -0400
@@ -158,7 +158,7 @@
extra = ""
%>
%if href:
- <td><div class="menubutton split"><a class="label" href="${href}">${v}${extra}</a></td>
+ <td><div class="menubutton split" style="float: left;"><a class="label" href="${href}">${v}${extra}</a> </td>
%else:
<td >${v}${extra}</td>
%endif
diff -r b8205e8e077f -r 168a6ef6a99a templates/workflow/list.mako
--- a/templates/workflow/list.mako Sun Aug 23 15:16:43 2009 -0400
+++ b/templates/workflow/list.mako Sun Aug 23 17:13:04 2009 -0400
@@ -37,7 +37,7 @@
%for i, workflow in enumerate( workflows ):
<tr>
<td>
- <div class="menubutton">
+ <div class="menubutton" style="float: left;">
<a id="wf-${i}-popup" class="arrow" style="display: none;"><span>▼</span></a>
${workflow.name}
</div>
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/77b761ff6cd3
changeset: 2607:77b761ff6cd3
user: James Taylor <james(a)jamestaylor.org>
date: Sun Aug 23 09:22:19 2009 -0400
description:
Fix clone history tests
1 file(s) affected in this change:
templates/history/clone.mako
diffs (12 lines):
diff -r e6dda627e6b0 -r 77b761ff6cd3 templates/history/clone.mako
--- a/templates/history/clone.mako Sat Aug 22 23:20:09 2009 -0400
+++ b/templates/history/clone.mako Sun Aug 23 09:22:19 2009 -0400
@@ -8,7 +8,7 @@
<form action="${h.url_for( controller='history', action='clone' )}" method="post" >
<div class="form-row">
%if id_argument is not None:
- <input type="hidden" name="id" value="${trans.security.encode_id( id_argument )}">
+ <input type="hidden" name="id" value="${id_argument}">
%endif
You can clone the history such that the clone will include all items in the original
history, or you can eliminate the original history's deleted items from the clone.
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/e6dda627e6b0
changeset: 2606:e6dda627e6b0
user: James Taylor <james(a)jamestaylor.org>
date: Sat Aug 22 23:20:09 2009 -0400
description:
Merge
2 file(s) affected in this change:
templates/base_panels.mako
templates/history/grid.mako
diffs (2711 lines):
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/model/migrate/versions/0009_request_table.py
--- a/lib/galaxy/model/migrate/versions/0009_request_table.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/model/migrate/versions/0009_request_table.py Sat Aug 22 23:20:09 2009 -0400
@@ -9,6 +9,7 @@
from migrate.changeset import *
import sys, logging
from galaxy.model.custom_types import *
+from sqlalchemy.exceptions import *
log = logging.getLogger( __name__ )
log.setLevel(logging.DEBUG)
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/tools/__init__.py Sat Aug 22 23:20:09 2009 -0400
@@ -1534,10 +1534,19 @@
child_dataset.flush()
child_dataset.set_size()
child_dataset.name = "Secondary Dataset (%s)" % ( designation )
- child_dataset.state = child_dataset.states.OK
child_dataset.init_meta()
child_dataset.set_meta()
child_dataset.set_peek()
+ # Associate new dataset with job
+ job = None
+ for assoc in outdata.creating_job_associations:
+ job = assoc.job
+ break
+ if job:
+ assoc = self.app.model.JobToOutputDatasetAssociation( '__new_child_file_%s|%s__' % ( name, designation ), child_dataset )
+ assoc.job = job
+ assoc.flush()
+ child_dataset.state = outdata.state
child_dataset.flush()
# Add child to return dict
children[name][designation] = child_dataset
@@ -1550,7 +1559,7 @@
def collect_primary_datasets( self, output):
primary_datasets = {}
- #Loop through output file names, looking for generated primary datasets in form of 'primary_associatedWithDatasetID_designation_visibility_extension'
+ #Loop through output file names, looking for generated primary datasets in form of 'primary_associatedWithDatasetID_designation_visibility_extension(_DBKEY)'
for name, outdata in output.items():
for filename in glob.glob(os.path.join(self.app.config.new_file_path,"primary_%i_*" % outdata.id) ):
if not name in primary_datasets:
@@ -1563,19 +1572,32 @@
if visible == "visible": visible = True
else: visible = False
ext = fields.pop(0).lower()
+ dbkey = outdata.dbkey
+ if fields:
+ dbkey = fields[ 0 ]
# Create new primary dataset
- primary_data = self.app.model.HistoryDatasetAssociation( extension=ext, designation=designation, visible=visible, dbkey=outdata.dbkey, create_dataset=True )
+ primary_data = self.app.model.HistoryDatasetAssociation( extension=ext, designation=designation, visible=visible, dbkey=dbkey, create_dataset=True )
self.app.security_agent.copy_dataset_permissions( outdata.dataset, primary_data.dataset )
primary_data.flush()
# Move data from temp location to dataset location
shutil.move( filename, primary_data.file_name )
primary_data.set_size()
- primary_data.name = dataset.name
- primary_data.info = dataset.info
- primary_data.state = primary_data.states.OK
- primary_data.init_meta( copy_from=dataset )
+ primary_data.name = outdata.name
+ primary_data.info = outdata.info
+ primary_data.init_meta( copy_from=outdata )
+ primary_data.dbkey = dbkey
primary_data.set_meta()
primary_data.set_peek()
+ # Associate new dataset with job
+ job = None
+ for assoc in outdata.creating_job_associations:
+ job = assoc.job
+ break
+ if job:
+ assoc = self.app.model.JobToOutputDatasetAssociation( '__new_primary_file_%s|%s__' % ( name, designation ), primary_data )
+ assoc.job = job
+ assoc.flush()
+ primary_data.state = outdata.state
primary_data.flush()
outdata.history.add_dataset( primary_data )
# Add dataset to return dict
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/tools/actions/upload.py
--- a/lib/galaxy/tools/actions/upload.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/tools/actions/upload.py Sat Aug 22 23:20:09 2009 -0400
@@ -20,12 +20,10 @@
for upload_dataset in incoming['files']:
f = upload_dataset['file_data']
if isinstance( f, FieldStorage ):
- # very small files can be StringIOs
- if 'name' in dir( f.file ) and f.file.name != '<fdopen>':
- local_filename = util.mkstemp_ln( f.file.name, 'upload_file_data_' )
- f.file.close()
- else:
- local_filename = datatypes.sniff.stream_to_file( f.file, prefix="strio_upload_file_" )[0]
+ assert not isinstance( f.file, StringIO.StringIO )
+ assert f.file.name != '<fdopen>'
+ local_filename = util.mkstemp_ln( f.file.name, 'upload_file_data_' )
+ f.file.close()
upload_dataset['file_data'] = dict( filename = f.filename,
local_filename = local_filename )
if upload_dataset['url_paste'].strip() != '':
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/tools/parameters/dynamic_options.py
--- a/lib/galaxy/tools/parameters/dynamic_options.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/tools/parameters/dynamic_options.py Sat Aug 22 23:20:09 2009 -0400
@@ -242,6 +242,55 @@
rval.append( add_value )
return rval
+class RemoveValueFilter( Filter ):
+ """
+ Removes a value from an options list.
+
+ Type: remove_value
+
+ Required Attributes:
+ value: value to remove from select list
+ or
+ ref: param to refer to
+ or
+ meta_ref: dataset to refer to
+ key: metadata key to compare to
+ """
+ def __init__( self, d_option, elem ):
+ Filter.__init__( self, d_option, elem )
+ self.value = elem.get( "value", None )
+ self.ref_name = elem.get( "ref", None )
+ self.meta_ref = elem.get( "meta_ref", None )
+ self.metadata_key = elem.get( "key", None )
+ assert self.value is not None or ( ( self.ref_name is not None or self.meta_ref is not None )and self.metadata_key is not None ), ValueError( "Required 'value' or 'ref' and 'key' attributes missing from filter" )
+ self.multiple = string_as_bool( elem.get( "multiple", "False" ) )
+ self.separator = elem.get( "separator", "," )
+ def filter_options( self, options, trans, other_values ):
+ if trans is not None and trans.workflow_building_mode: return options
+ assert self.value is not None or ( self.ref_name is not None and self.ref_name in other_values ) or (self.meta_ref is not None and self.meta_ref in other_values ) or ( trans is not None and trans.workflow_building_mode), Exception( "Required dependency '%s' or '%s' not found in incoming values" % ( self.ref_name, self.meta_ref ) )
+ def compare_value( option_value, filter_value ):
+ if isinstance( filter_value, list ):
+ if self.multiple:
+ option_value = option_value.split( self.separator )
+ for value in filter_value:
+ if value not in filter_value:
+ return False
+ return True
+ return option_value in filter_value
+ if self.multiple:
+ return filter_value in option_value.split( self.separator )
+ return option_value == filter_value
+ value = self.value
+ if value is None:
+ if self.ref_name is not None:
+ value = other_values.get( self.ref_name )
+ else:
+ data_ref = other_values.get( self.meta_ref )
+ if not isinstance( data_ref, self.dynamic_option.tool_param.tool.app.model.HistoryDatasetAssociation ):
+ return options #cannot modify options
+ value = data_ref.metadata.get( self.metadata_key, None )
+ return [ ( disp_name, optval, selected ) for disp_name, optval, selected in options if not compare_value( optval, value ) ]
+
class SortByColumnFilter( Filter ):
"""
Sorts an options list by a column
@@ -274,6 +323,7 @@
unique_value = UniqueValueFilter,
multiple_splitter = MultipleSplitterFilter,
add_value = AdditionalValueFilter,
+ remove_value = RemoveValueFilter,
sort_by = SortByColumnFilter )
class DynamicOptions( object ):
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/admin.py
--- a/lib/galaxy/web/controllers/admin.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/admin.py Sat Aug 22 23:20:09 2009 -0400
@@ -764,11 +764,7 @@
# See if we have any field contents
info = info_association.info
if info:
- field_contents = {}
- for index, value in enumerate( info.content ):
- key = 'field_%i' % index
- field_contents[ key ] = value
- widgets = get_form_widgets( trans, template, field_contents )
+ widgets = get_form_widgets( trans, template, info.content )
else:
widgets = get_form_widgets( trans, template )
else:
@@ -965,11 +961,7 @@
# See if we have any field contents
info = info_association.info
if info:
- field_contents = {}
- for index, value in enumerate( info.content ):
- key = 'field_%i' % index
- field_contents[ key ] = value
- widgets = get_form_widgets( trans, template, field_contents )
+ widgets = get_form_widgets( trans, template, info.content )
else:
widgets = get_form_widgets( trans, template )
else:
@@ -1220,11 +1212,7 @@
# See if we have any field contents
info = info_association.info
if info:
- field_contents = {}
- for index, value in enumerate( info.content ):
- key = 'field_%i' % index
- field_contents[ key ] = value
- widgets = get_form_widgets( trans, template, field_contents )
+ widgets = get_form_widgets( trans, template, info.content )
else:
widgets = get_form_widgets( trans, template )
else:
@@ -2027,7 +2015,7 @@
num_states = int( util.restore_text( params.get( 'num_states', 0 ) ))
proceed = True
for i in range( num_states ):
- if not util.restore_text( params.get( 'new_element_name_%i' % i, None ) ):
+ if not util.restore_text( params.get( 'state_name_%i' % i, None ) ):
proceed = False
break
if not proceed:
@@ -2045,8 +2033,8 @@
ss.delete()
ss.flush()
for i in range( num_states ):
- name = util.restore_text( params.get( 'new_element_name_%i' % i, None ))
- desc = util.restore_text( params.get( 'new_element_description_%i' % i, None ))
+ name = util.restore_text( params.get( 'state_name_%i' % i, None ))
+ desc = util.restore_text( params.get( 'state_desc_%i' % i, None ))
ss = trans.app.model.SampleState(name, desc, rt)
ss.flush()
msg = "The new request type named '%s' with %s state(s) has been created" % (rt.name, num_states)
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/forms.py
--- a/lib/galaxy/web/controllers/forms.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/forms.py Sat Aug 22 23:20:09 2009 -0400
@@ -175,7 +175,7 @@
'visible': True,
'required': False,
'type': BaseField.form_field_types()[0],
- 'selectlist': '' }
+ 'selectlist': [] }
self.current_form['fields'].append(empty_field)
def __get_field(self, index, **kwd):
params = util.Params( kwd )
@@ -183,10 +183,10 @@
# To reproduce, create a new form, click the "add field" button, click the
# browser back arrow, then click the "add field" button again.
# You should never attempt to "restore_text()" on a None object...
- name = util.restore_text( params.get( 'field_name_%i' % index, None ) )
- helptext = util.restore_text( params.get( 'field_helptext_%i' % index, None ) )
+ name = util.restore_text( params.get( 'field_name_%i' % index, '' ) )
+ helptext = util.restore_text( params.get( 'field_helptext_%i' % index, '' ) )
required = params.get( 'field_required_%i' % index, False )
- field_type = util.restore_text( params.get( 'field_type_%i' % index, None ) )
+ field_type = util.restore_text( params.get( 'field_type_%i' % index, '' ) )
if field_type == 'SelectField':
selectlist = self.__get_selectbox_options(index, **kwd)
return {'label': name,
@@ -419,22 +419,39 @@
else:
fdc_list = trans.app.model.FormDefinitionCurrent.query().all()
return [ fdc.latest_form for fdc in fdc_list ]
-def get_form_widgets( trans, form, contents={} ):
+
+
+def get_form_widgets( trans, form, contents=[], user=None, **kwd ):
'''
Return the list of widgets that comprise a form definition,
including field contents if any.
'''
+ params = util.Params( kwd )
+ if not user:
+ user = trans.user
widgets = []
for index, field in enumerate( form.fields ):
field_name = 'field_%i' % index
- if field_name in contents:
- value = contents[ field_name ]
- elif field[ 'type' ] == 'CheckboxField':
- # Since we do not have contents, set checkbox value to False
- value = False
+ # determine the value of the field
+ if field_name in kwd:
+ # the user had already filled out this field and the same form is re-rendered
+ # due to some reason like required fields have been left out.
+ if field[ 'type' ] == 'CheckboxField':
+ value = CheckboxField.is_checked( util.restore_text( params.get( field_name, False ) ) )
+ else:
+ value = util.restore_text( params.get( field_name, '' ) )
+ elif contents:
+ # this field has a saved value
+ value = str(contents[ index ])
else:
- # Set other field types to empty string
- value = ''
+ # if none of the above, then leave the field empty
+ if field[ 'type' ] == 'CheckboxField':
+ # Since we do not have contents, set checkbox value to False
+ value = False
+ else:
+ # Set other field types to empty string
+ value = ''
+ # create the field widget
field_widget = eval( field[ 'type' ] )( field_name )
if field[ 'type' ] == 'TextField':
field_widget.set_size( 40 )
@@ -442,6 +459,10 @@
elif field[ 'type' ] == 'TextArea':
field_widget.set_size( 3, 40 )
field_widget.value = value
+ elif field['type'] == 'AddressField':
+ field_widget.user = user
+ field_widget.value = value
+ field_widget.params = params
elif field[ 'type' ] == 'SelectField':
for option in field[ 'selectlist' ]:
if option == value:
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/library.py
--- a/lib/galaxy/web/controllers/library.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/library.py Sat Aug 22 23:20:09 2009 -0400
@@ -137,11 +137,7 @@
# See if we have any field contents
info = library.info_association[0].info
if info:
- field_contents = {}
- for index, value in enumerate( info.content ):
- key = 'field_%i' % index
- field_contents[ key ] = value
- widgets = get_form_widgets( trans, template, field_contents )
+ widgets = get_form_widgets( trans, template, info.content )
else:
widgets = get_form_widgets( trans, template )
else:
@@ -475,11 +471,7 @@
# See if we have any field contents
info = info_association.info
if info:
- field_contents = {}
- for index, value in enumerate( info.content ):
- key = 'field_%i' % index
- field_contents[ key ] = value
- widgets = get_form_widgets( trans, template, field_contents )
+ widgets = get_form_widgets( trans, template, info.content )
else:
widgets = get_form_widgets( trans, template )
else:
@@ -996,11 +988,7 @@
# See if we have any field contents
info = info_association.info
if info:
- field_contents = {}
- for index, value in enumerate( info.content ):
- key = 'field_%i' % index
- field_contents[ key ] = value
- widgets = get_form_widgets( trans, template, field_contents )
+ widgets = get_form_widgets( trans, template, info.content )
else:
widgets = get_form_widgets( trans, template )
else:
@@ -1173,3 +1161,13 @@
edit_info=True,
msg=util.sanitize_text( msg ),
messagetype='done' ) )
+
+
+def get_authorized_libs(trans, user):
+ all_libraries = trans.app.model.Library.filter(trans.app.model.Library.table.c.deleted == False).order_by(trans.app.model.Library.name).all()
+ authorized_libraries = []
+ for library in all_libraries:
+ if trans.app.security_agent.allow_action(user, trans.app.security_agent.permitted_actions.LIBRARY_ADD, library_item=library) \
+ or trans.app.security_agent.allow_action(user, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY, library_item=library):
+ authorized_libraries.append(library)
+ return authorized_libraries
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/requests.py
--- a/lib/galaxy/web/controllers/requests.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/requests.py Sat Aug 22 23:20:09 2009 -0400
@@ -8,6 +8,8 @@
from galaxy.web.form_builder import *
from datetime import datetime, timedelta
from cgi import escape, FieldStorage
+from galaxy.web.controllers.forms import get_form_widgets
+from galaxy.web.controllers.library import get_authorized_libs
log = logging.getLogger( __name__ )
@@ -62,18 +64,12 @@
request_grid = RequestsListGrid()
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def index( self, trans ):
return trans.fill_template( "requests/index.mako" )
-
- def get_authorized_libs(self, trans):
- all_libraries = trans.app.model.Library.filter(trans.app.model.Library.table.c.deleted == False).order_by(trans.app.model.Library.name).all()
- authorized_libraries = []
- for library in all_libraries:
- if trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_ADD, library_item=library) \
- or trans.app.security_agent.allow_action(trans.user, trans.app.security_agent.permitted_actions.LIBRARY_MODIFY, library_item=library):
- authorized_libraries.append(library)
- return authorized_libraries
+
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def list( self, trans, **kwargs ):
'''
List all request made by the current user
@@ -137,7 +133,6 @@
Shows the request details
'''
request = trans.app.model.Request.get(id)
- libraries = self.get_authorized_libs(trans)
# list of widgets to be rendered on the request form
request_details = []
# main details
@@ -146,6 +141,9 @@
helptext=''))
request_details.append(dict(label='Type',
value=request.type.name,
+ helptext=''))
+ request_details.append(dict(label='State',
+ value=request.state,
helptext=''))
request_details.append(dict(label='Date created',
value=request.create_time,
@@ -201,6 +199,7 @@
copy_list.add_option(s[0], i)
return copy_list
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def show_request(self, trans, **kwd):
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
@@ -326,6 +325,7 @@
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def delete_sample(self, trans, **kwd):
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
@@ -348,6 +348,7 @@
edit_mode=self.edit_mode)
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def toggle_request_details(self, trans, **kwd):
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
@@ -384,6 +385,7 @@
select_reqtype.add_option(rt.name, rt.id)
return select_reqtype
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def new(self, trans, **kwd):
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
@@ -413,8 +415,8 @@
if params.get('create_request_button', False) == 'Save':
return trans.response.send_redirect( web.url_for( controller='requests',
action='list',
- msg=msg ,
- messagetype='done') )
+ message=msg ,
+ status='done') )
elif params.get('create_request_samples_button', False) == 'Add samples':
new_kwd = {}
new_kwd['id'] = trans.security.encode_id(request.id)
@@ -457,12 +459,10 @@
helptext='(Optional)'))
# libraries selectbox
- libraries = self.get_authorized_libs(trans)
+ libraries = get_authorized_libs(trans, trans.user)
libui = self.__library_ui(libraries, **kwd)
widgets = widgets + libui
- widgets = self.__create_form(trans, request_type.request_form_id, widgets,
- form_values, **kwd)
- title = 'Add a new request of type: %s' % request_type.name
+ widgets = widgets + get_form_widgets(trans, request_type.request_form, contents=[], **kwd)
return trans.fill_template( '/requests/new_request.mako',
select_request_type=select_request_type,
request_type=request_type,
@@ -502,58 +502,13 @@
return [widget, new_lib]
else:
return [widget]
-
- def __create_form(self, trans, form_id, widgets=[], form_values=None, **kwd):
- # TODO: RC - replace this method by importing as follows:
- # from galaxy.web.controllers.forms import get_form_widgets
- params = util.Params( kwd )
- form = trans.app.model.FormDefinition.get(form_id)
- # form fields
- for index, field in enumerate(form.fields):
- # value of the field
- if field['type'] == 'CheckboxField':
- value = util.restore_text( params.get( 'field_%i' % index, False ) )
- else:
- value = util.restore_text( params.get( 'field_%i' % index, '' ) )
- if not value:
- if form_values:
- value = str(form_values.content[index])
- # create the field
- fw = eval(field['type'])('field_%i' % index)
- if field['type'] == 'TextField':
- fw.set_size(40)
- fw.value = value
- elif field['type'] == 'TextArea':
- fw.set_size(3, 40)
- fw.value = value
- elif field['type'] == 'AddressField':
- fw.user = trans.user
- fw.value = value
- fw.params = params
- elif field['type'] == 'SelectField':
- for option in field['selectlist']:
- if option == value:
- fw.add_option(option, option, selected=True)
- else:
- fw.add_option(option, option)
- elif field['type'] == 'CheckboxField':
- fw.checked = value
- # require/optional
- if field['required'] == 'required':
- req = 'Required'
- else:
- req = 'Optional'
- widgets.append(dict(label=field['label'],
- widget=fw,
- helptext=field['helptext']+' ('+req+')'))
- return widgets
def __validate(self, trans, request):
'''
Validates the request entered by the user
'''
empty_fields = []
- if not request.library:
- empty_fields.append('Data library')
+# if not request.library:
+# empty_fields.append('Data library')
# check rest of the fields of the form
for index, field in enumerate(request.type.request_form.fields):
if field['required'] == 'required' and request.values.content[index] in ['', None]:
@@ -622,6 +577,7 @@
request.flush()
return request
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def edit(self, trans, **kwd):
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
@@ -698,11 +654,10 @@
helptext='(Optional)'))
# libraries selectbox
- libraries = self.get_authorized_libs(trans)
+ libraries = get_authorized_libs(trans, trans.user)
libui = self.__library_ui(libraries, request, **kwd)
widgets = widgets + libui
- widgets = self.__create_form(trans, request.type.request_form_id, widgets,
- request.values, **kwd)
+ widgets = widgets + get_form_widgets(trans, request.type.request_form, request.values.content, **kwd)
return trans.fill_template( '/requests/edit_request.mako',
select_request_type=select_request_type,
request_type=request.type,
@@ -735,6 +690,8 @@
kwd['id'] = trans.security.encode_id(request.id)
return trans.response.send_redirect( web.url_for( controller='requests',
action='list',
+ status='done',
+ message='The request <b>%s</b> has been deleted.' % request.name,
**kwd) )
def __undelete_request(self, trans, id):
try:
@@ -754,6 +711,8 @@
kwd['id'] = trans.security.encode_id(request.id)
return trans.response.send_redirect( web.url_for( controller='requests',
action='list',
+ status='done',
+ message='The request <b>%s</b> has been undeleted.' % request.name,
**kwd) )
def __submit(self, trans, id):
try:
@@ -784,10 +743,14 @@
request.flush()
kwd = {}
kwd['id'] = trans.security.encode_id(request.id)
+ kwd['status'] = 'done'
+ kwd['message'] = 'The request <b>%s</b> has been submitted.' % request.name
return trans.response.send_redirect( web.url_for( controller='requests',
action='list',
+ show_filter=trans.app.model.Request.states.SUBMITTED,
**kwd) )
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def submit_request(self, trans, **kwd):
params = util.Params( kwd )
try:
@@ -817,13 +780,16 @@
# change request's submitted field
request.state = request.states.SUBMITTED
request.flush()
- ## TODO
kwd['id'] = trans.security.encode_id(request.id)
+ kwd['status'] = 'done'
+ kwd['message'] = 'The request <b>%s</b> has been submitted.' % request.name
return trans.response.send_redirect( web.url_for( controller='requests',
action='list',
+ show_filter=trans.app.model.Request.states.SUBMITTED,
**kwd) )
@web.expose
+ @web.require_login( "create/submit sequencing requests" )
def show_events(self, trans, **kwd):
params = util.Params( kwd )
try:
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/requests_admin.py
--- a/lib/galaxy/web/controllers/requests_admin.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/requests_admin.py Sat Aug 22 23:20:09 2009 -0400
@@ -7,19 +7,20 @@
import logging, tempfile, zipfile, tarfile, os, sys
from galaxy.web.form_builder import *
from datetime import datetime, timedelta
+from galaxy.web.controllers.forms import get_form_widgets
+from galaxy.web.controllers.library import get_authorized_libs
log = logging.getLogger( __name__ )
-
-# States for passing messages
-SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
class RequestsListGrid( grids.Grid ):
title = "Sequencing Requests"
model_class = model.Request
default_sort_key = "-create_time"
+ show_filter = model.Request.states.SUBMITTED
columns = [
grids.GridColumn( "Name", key="name",
- link=( lambda item: iff( item.deleted, None, dict( operation="show_request", id=item.id ) ) )),
+ link=( lambda item: iff( item.deleted, None, dict( operation="show_request", id=item.id ) ) ),
+ attach_popup=True ),
grids.GridColumn( "Description", key="desc"),
grids.GridColumn( "Sample(s)", method='number_of_samples',
link=( lambda item: iff( item.deleted, None, dict( operation="show_request", id=item.id ) ) ), ),
@@ -30,15 +31,18 @@
]
operations = [
-# grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
-# grids.GridOperation( "Samples", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
-# grids.GridOperation( "Delete", condition=( lambda item: not item.deleted ) ),
-# grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
+ grids.GridOperation( "Submit", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() and item.samples ) ),
+ grids.GridOperation( "Edit", allow_multiple=False, condition=( lambda item: not item.deleted ) ),
+ grids.GridOperation( "Delete", allow_multiple=False, condition=( lambda item: not item.deleted and item.unsubmitted() ) ),
+ grids.GridOperation( "Undelete", condition=( lambda item: item.deleted ) ),
]
standard_filters = [
+ grids.GridColumnFilter( model.Request.states.UNSUBMITTED,
+ args=dict( state=model.Request.states.UNSUBMITTED, deleted=False ) ),
grids.GridColumnFilter( model.Request.states.SUBMITTED,
args=dict( state=model.Request.states.SUBMITTED, deleted=False ) ),
grids.GridColumnFilter( model.Request.states.COMPLETE, args=dict( state=model.Request.states.COMPLETE, deleted=False ) ),
+ grids.GridColumnFilter( "Deleted", args=dict( deleted=True ) ),
grids.GridColumnFilter( "All", args=dict( deleted=False ) )
]
def get_user(self, trans, request):
@@ -48,9 +52,9 @@
def get_request_type(self, trans, request):
request_type = trans.app.model.RequestType.get(request.request_type_id)
return request_type.name
- def apply_default_filter( self, trans, query ):
- return query.filter(or_(self.model_class.state==self.model_class.states.SUBMITTED,
- self.model_class.state==self.model_class.states.COMPLETE))
+# def apply_default_filter( self, trans, query ):
+# return query.filter(or_(self.model_class.state==self.model_class.states.SUBMITTED,
+# self.model_class.state==self.model_class.states.COMPLETE))
def number_of_samples(self, trans, request):
return str(len(request.samples))
@@ -67,32 +71,277 @@
'''
List all request made by the current user
'''
- status = message = None
+ message = util.restore_text( kwargs.get( 'message', '' ) )
+ status = kwargs.get( 'status', 'done' )
self.request_grid.default_filter = dict(state=trans.app.model.Request.states.SUBMITTED,
deleted=False)
if 'operation' in kwargs:
operation = kwargs['operation'].lower()
if operation == "show_request":
id = trans.security.decode_id(kwargs['id'])
- return self.__show_request(trans, id)
-
+ return self.__show_request(trans, id, status, message)
+ elif operation == "submit":
+ id = trans.security.decode_id(kwargs['id'])
+ return self.__submit(trans, id)
+ elif operation == "edit":
+ id = trans.security.decode_id(kwargs['id'])
+ return self.__edit_request(trans, id)
+ elif operation == "delete":
+ id = trans.security.decode_id(kwargs['id'])
+ return self.__delete_request(trans, id)
+ elif operation == "undelete":
+ id = trans.security.decode_id(kwargs['id'])
+ return self.__undelete_request(trans, id)
if 'show_filter' in kwargs.keys():
if kwargs['show_filter'] == 'All':
- self.request_grid.default_filter = dict(deleted=False)
+ self.request_grid.default_filter = {}
+ elif kwargs['show_filter'] == 'Deleted':
+ self.request_grid.default_filter = dict(deleted=True)
else:
- self.request_grid.default_filter = dict(state=kwargs['show_filter'], deleted=False)
+ self.request_grid.default_filter = dict(state=kwargs['show_filter'], deleted=False)
+ self.request_grid.show_filter = kwargs.get('show_filter', trans.app.model.Request.states.SUBMITTED)
# Render the list view
return self.request_grid( trans, template='/admin/requests/grid.mako', **kwargs )
- def __show_request(self, trans, id):
+ @web.expose
+ @web.require_admin
+ def edit(self, trans, **kwd):
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
+ try:
+ request = trans.app.model.Request.get(int(params.get('request_id', None)))
+ except:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message="Invalid request ID",
+ **kwd) )
+ if params.get('show', False) == 'True':
+ return self.__edit_request(trans, request.id, **kwd)
+ elif params.get('save_changes_request_button', False) == 'Save changes' \
+ or params.get('edit_samples_button', False) == 'Edit samples':
+ request_type = trans.app.model.RequestType.get(int(params.select_request_type))
+ if not util.restore_text(params.get('name', '')):
+ msg = 'Please enter the <b>Name</b> of the request'
+ kwd['messagetype'] = 'error'
+ kwd['msg'] = msg
+ kwd['show'] = 'True'
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='edit',
+ **kwd) )
+ request = self.__save_request(trans, request, **kwd)
+ msg = 'The changes made to the request named %s has been saved' % request.name
+ if params.get('save_changes_request_button', False) == 'Save changes':
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ message=msg ,
+ status='done') )
+ elif params.get('edit_samples_button', False) == 'Edit samples':
+ new_kwd = {}
+ new_kwd['request_id'] = request.id
+ new_kwd['edit_samples_button'] = 'Edit samples'
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='show_request',
+ msg=msg ,
+ messagetype='done',
+ **new_kwd) )
+ elif params.get('refresh', False) == 'true':
+ return self.__edit_request(trans, request.id, **kwd)
+ def __edit_request(self, trans, id, **kwd):
try:
request = trans.app.model.Request.get(id)
except:
- return trans.response.send_redirect( web.url_for( controller='requests',
+ msg = "Invalid request ID"
+ log.warn( msg )
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message=msg) )
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
+ select_request_type = self.__select_request_type(trans, request.type.id)
+ # list of widgets to be rendered on the request form
+ widgets = []
+ if util.restore_text( params.get( 'name', '' ) ):
+ name = util.restore_text( params.get( 'name', '' ) )
+ else:
+ name = request.name
+ widgets.append(dict(label='Name',
+ widget=TextField('name', 40, name),
+ helptext='(Required)'))
+ if util.restore_text( params.get( 'desc', '' ) ):
+ desc = util.restore_text( params.get( 'desc', '' ) )
+ else:
+ desc = request.desc
+ widgets.append(dict(label='Description',
+ widget=TextField('desc', 40, desc),
+ helptext='(Optional)'))
+ # libraries selectbox
+ libui = self.__library_ui(trans, request.user, request, **kwd)
+ widgets = widgets + libui
+ widgets = widgets + get_form_widgets(trans, request.type.request_form, request.values.content, request.user, **kwd)
+ return trans.fill_template( '/admin/requests/edit_request.mako',
+ select_request_type=select_request_type,
+ request_type=request.type,
+ request=request,
+ widgets=widgets,
+ msg=msg,
+ messagetype=messagetype)
+ return self.__show_request_form(trans)
+ def __delete_request(self, trans, id):
+ try:
+ request = trans.app.model.Request.get(id)
+ except:
+ msg = "Invalid request ID"
+ log.warn( msg )
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message=msg,
+ **kwd) )
+ # change request's submitted field
+ if not request.unsubmitted():
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message='This request cannot be deleted as it is already been submitted',
+ **kwd) )
+ request.deleted = True
+ request.flush()
+ kwd = {}
+ kwd['id'] = trans.security.encode_id(request.id)
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ show_filter=trans.app.model.Request.states.UNSUBMITTED,
+ status='done',
+ message='The request <b>%s</b> has been deleted.' % request.name,
+ **kwd) )
+ def __undelete_request(self, trans, id):
+ try:
+ request = trans.app.model.Request.get(id)
+ except:
+ msg = "Invalid request ID"
+ log.warn( msg )
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message=msg,
+ **kwd) )
+ # change request's submitted field
+ request.deleted = False
+ request.flush()
+ kwd = {}
+ kwd['id'] = trans.security.encode_id(request.id)
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ show_filter=trans.app.model.Request.states.UNSUBMITTED,
+ status='done',
+ message='The request <b>%s</b> has been undeleted.' % request.name,
+ **kwd) )
+ def __submit(self, trans, id):
+ try:
+ request = trans.app.model.Request.get(id)
+ except:
+ msg = "Invalid request ID"
+ log.warn( msg )
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message=msg,
+ **kwd) )
+ msg = self.__validate(trans, request)
+ if msg:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='edit',
+ messagetype = 'error',
+ msg=msg,
+ request_id=request.id,
+ show='True') )
+ # get the new state
+ new_state = request.type.states[0]
+ for s in request.samples:
+ event = trans.app.model.SampleEvent(s, new_state, 'Samples submitted to the system')
+ event.flush()
+ # change request's submitted field
+ request.state = request.states.SUBMITTED
+ request.flush()
+ kwd = {}
+ kwd['id'] = trans.security.encode_id(request.id)
+ kwd['status'] = 'done'
+ kwd['message'] = 'The request <b>%s</b> has been submitted.' % request.name
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ show_filter=trans.app.model.Request.states.SUBMITTED,
+ **kwd) )
+ @web.expose
+ @web.require_admin
+ def submit_request(self, trans, **kwd):
+ params = util.Params( kwd )
+ try:
+ id = int(params.get('id', False))
+ request = trans.app.model.Request.get(id)
+ except:
+ msg = "Invalid request ID"
+ log.warn( msg )
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message=msg,
+ **kwd) )
+ msg = self.__validate(trans, request)
+ if msg:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='edit',
+ messagetype='error',
+ msg=msg,
+ request_id=request.id,
+ show='True') )
+ # get the new state
+ new_state = request.type.states[0]
+ for s in request.samples:
+ event = trans.app.model.SampleEvent(s, new_state, 'Samples submitted to the system')
+ event.flush()
+ # change request's submitted field
+ request.state = request.states.SUBMITTED
+ request.flush()
+ kwd['id'] = trans.security.encode_id(request.id)
+ kwd['status'] = 'done'
+ kwd['message'] = 'The request <b>%s</b> has been submitted.' % request.name
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ show_filter=trans.app.model.Request.states.SUBMITTED,
+ **kwd) )
+ def __copy_sample(self):
+ copy_list = SelectField('copy_sample')
+ copy_list.add_option('None', -1, selected=True)
+ for i, s in enumerate(self.current_samples):
+ copy_list.add_option(s[0], i)
+ return copy_list
+ def __update_samples(self, request, **kwd):
+ params = util.Params( kwd )
+ num_samples = len(self.current_samples)
+ self.current_samples = []
+ for s in request.samples:
+ self.current_samples.append([s.name, s.values.content])
+ for index in range(num_samples-len(request.samples)):
+ sample_index = index + len(request.samples)
+ sample_name = util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) )
+ sample_values = []
+ for field_index in range(len(request.type.sample_form.fields)):
+ sample_values.append(util.restore_text( params.get( 'sample_%i_field_%i' % (sample_index, field_index), '' ) ))
+ self.current_samples.append([sample_name, sample_values])
+ def __show_request(self, trans, id, messagetype, msg):
+ try:
+ request = trans.app.model.Request.get(id)
+ except:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='list',
status='error',
message="Invalid request ID",
**kwd) )
self.current_samples = []
+ self.edit_mode = False
for s in request.samples:
self.current_samples.append([s.name, s.values.content])
self.details_state = 'Show request details'
@@ -100,8 +349,163 @@
request=request,
request_details=self.request_details(trans, id),
current_samples = self.current_samples,
- details_state=self.details_state)
+ sample_copy=self.__copy_sample(),
+ details_state=self.details_state,
+ edit_mode=self.edit_mode,
+ msg=msg, messagetype=messagetype)
@web.expose
+ @web.require_admin
+ def show_request(self, trans, **kwd):
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
+ try:
+ request = trans.app.model.Request.get(int(params.get('request_id', None)))
+ except:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ message="Invalid request ID",
+ **kwd) )
+ if params.get('import_samples_button', False) == 'Import samples':
+ try:
+ file_obj = params.get('file_data', '')
+ import csv
+ reader = csv.reader(file_obj.file)
+ for row in reader:
+ self.current_samples.append([row[0], row[1:]])
+ return trans.fill_template( '/admin/requests/show_request.mako',
+ request=request,
+ request_details=self.request_details(trans, request.id),
+ current_samples=self.current_samples,
+ sample_copy=self.__copy_sample(),
+ details_state=self.details_state,
+ edit_mode=self.edit_mode)
+ except:
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ status='error',
+ operation='show_request',
+ id=trans.security.encode_id(request.id),
+ message='Error in importing samples from the given file.',
+ **kwd))
+ elif params.get('add_sample_button', False) == 'Add New':
+ # save the all (saved+unsaved) sample info in 'current_samples'
+ self.__update_samples(request, **kwd)
+ # add an empty or filled sample
+ # if the user has selected a sample no. to copy then copy the contents
+ # of the src sample to the new sample else an empty sample
+ src_sample_index = int(params.get( 'copy_sample', -1 ))
+ if src_sample_index == -1:
+ # empty sample
+ self.current_samples.append(['Sample_%i' % (len(self.current_samples)+1),['' for field in request.type.sample_form.fields]])
+ else:
+ self.current_samples.append([self.current_samples[src_sample_index][0]+'_%i' % (len(self.current_samples)+1),
+ [val for val in self.current_samples[src_sample_index][1]]])
+ return trans.fill_template( '/admin/requests/show_request.mako',
+ request=request,
+ request_details=self.request_details(trans, request.id),
+ current_samples=self.current_samples,
+ sample_copy=self.__copy_sample(),
+ details_state=self.details_state,
+ edit_mode=self.edit_mode)
+ elif params.get('save_samples_button', False) == 'Save':
+ # update current_samples
+ self.__update_samples(request, **kwd)
+ # check for duplicate sample names
+ msg = ''
+ for index in range(len(self.current_samples)-len(request.samples)):
+ sample_index = index + len(request.samples)
+ sample_name = self.current_samples[sample_index][0]
+ if not sample_name.strip():
+ msg = 'Please enter the name of sample number %i' % sample_index
+ break
+ count = 0
+ for i in range(len(self.current_samples)):
+ if sample_name == self.current_samples[i][0]:
+ count = count + 1
+ if count > 1:
+ msg = "This request has <b>%i</b> samples with the name <b>%s</b>.\nSamples belonging to a request must have unique names." % (count, sample_name)
+ break
+ if msg:
+ return trans.fill_template( '/admin/requests/show_request.mako',
+ request=request,
+ request_details=self.request_details(trans, request.id),
+ current_samples = self.current_samples,
+ sample_copy=self.__copy_sample(), details_state=self.details_state,
+ messagetype='error', msg=msg)
+ # save all the new/unsaved samples entered by the user
+ if not self.edit_mode:
+ for index in range(len(self.current_samples)-len(request.samples)):
+ sample_index = index + len(request.samples)
+ sample_name = util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) )
+ sample_values = []
+ for field_index in range(len(request.type.sample_form.fields)):
+ sample_values.append(util.restore_text( params.get( 'sample_%i_field_%i' % (sample_index, field_index), '' ) ))
+ form_values = trans.app.model.FormValues(request.type.sample_form, sample_values)
+ form_values.flush()
+ s = trans.app.model.Sample(sample_name, '', request, form_values)
+ s.flush()
+ else:
+ for index in range(len(self.current_samples)):
+ sample_index = index
+ sample_name = self.current_samples[sample_index][0]
+ new_sample_name = util.restore_text( params.get( 'sample_%i_name' % sample_index, '' ) )
+ sample_values = []
+ for field_index in range(len(request.type.sample_form.fields)):
+ sample_values.append(util.restore_text( params.get( 'sample_%i_field_%i' % (sample_index, field_index), '' ) ))
+ sample = request.has_sample(sample_name)
+ if sample:
+ form_values = trans.app.model.FormValues.get(sample.values.id)
+ form_values.content = sample_values
+ form_values.flush()
+ sample.name = new_sample_name
+ sample.flush()
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ operation='show_request',
+ id=trans.security.encode_id(request.id)) )
+ elif params.get('edit_samples_button', False) == 'Edit samples':
+ self.edit_mode = True
+ return trans.fill_template( '/admin/requests/show_request.mako',
+ request=request,
+ request_details=self.request_details(trans, request.id),
+ current_samples=self.current_samples,
+ sample_copy=self.__copy_sample(),
+ details_state=self.details_state,
+ edit_mode=self.edit_mode)
+ elif params.get('cancel_changes_button', False) == 'Cancel':
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ operation='show_request',
+ id=trans.security.encode_id(request.id)) )
+
+
+ @web.expose
+ @web.require_admin
+ def delete_sample(self, trans, **kwd):
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
+ request = trans.app.model.Request.get(int(params.get('request_id', 0)))
+ sample_index = int(params.get('sample_id', 0))
+ sample_name = self.current_samples[sample_index][0]
+ s = request.has_sample(sample_name)
+ if s:
+ s.delete()
+ s.flush()
+ request.flush()
+ del self.current_samples[sample_index]
+ return trans.fill_template( '/admin/requests/show_request.mako',
+ request=request,
+ request_details=self.request_details(trans, request.id),
+ current_samples = self.current_samples,
+ sample_copy=self.__copy_sample(),
+ details_state=self.details_state,
+ edit_mode=self.edit_mode)
+
+ @web.expose
+ @web.require_admin
def toggle_request_details(self, trans, **kwd):
params = util.Params( kwd )
msg = util.restore_text( params.get( 'msg', '' ) )
@@ -128,24 +532,28 @@
# list of widgets to be rendered on the request form
request_details = []
# main details
+ request_details.append(dict(label='User',
+ value=str(request.user.email),
+ helptext=''))
request_details.append(dict(label='Description',
value=request.desc,
helptext=''))
request_details.append(dict(label='Type',
value=request.type.name,
helptext=''))
+ request_details.append(dict(label='State',
+ value=request.state,
+ helptext=''))
request_details.append(dict(label='Date created',
value=request.create_time,
helptext=''))
- request_details.append(dict(label='Date updated',
- value=request.create_time,
- helptext=''))
- request_details.append(dict(label='User',
- value=str(request.user.email),
- helptext=''))
# library associated
+ if request.library:
+ value=request.library.name
+ else:
+ value = None
request_details.append(dict(label='Data library',
- value=trans.app.model.Library.get(request.library_id).name,
+ value=value,
helptext='Data library where the resultant dataset will be stored'))
# form fields
for index, field in enumerate(request.type.request_form.fields):
@@ -167,15 +575,262 @@
value=request.values.content[index],
helptext=field['helptext']+' ('+req+')'))
return request_details
+
+ def __select_request_type(self, trans, rtid):
+ rt_ids = ['none']
+ for rt in trans.app.model.RequestType.query().all():
+ if not rt.deleted:
+ rt_ids.append(str(rt.id))
+ select_reqtype = SelectField('select_request_type',
+ refresh_on_change=True,
+ refresh_on_change_values=rt_ids[1:])
+ if rtid == 'none':
+ select_reqtype.add_option('Select one', 'none', selected=True)
+ else:
+ select_reqtype.add_option('Select one', 'none')
+ for rt in trans.app.model.RequestType.query().all():
+ if not rt.deleted:
+ if rtid == rt.id:
+ select_reqtype.add_option(rt.name, rt.id, selected=True)
+ else:
+ select_reqtype.add_option(rt.name, rt.id)
+ return select_reqtype
+ @web.expose
+ @web.require_admin
+ def new(self, trans, **kwd):
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
+ if params.get('select_request_type', False) == 'True':
+ return trans.fill_template( '/admin/requests/new_request.mako',
+ select_request_type=self.__select_request_type(trans, 'none'),
+ widgets=[],
+ msg=msg,
+ messagetype=messagetype)
+ elif params.get('create', False) == 'True':
+ if params.get('create_request_button', False) == 'Save' \
+ or params.get('create_request_samples_button', False) == 'Add samples':
+ request_type = trans.app.model.RequestType.get(int(params.select_request_type))
+ if not util.restore_text(params.get('name', '')) \
+ or util.restore_text(params.get('select_user', '')) == unicode('none'):
+ msg = 'Please enter the <b>Name</b> of the request and the <b>user</b> on behalf of whom this request will be submitted before saving this request'
+ kwd['create'] = 'True'
+ kwd['messagetype'] = 'error'
+ kwd['msg'] = msg
+ kwd['create_request_button'] = None
+ kwd['create_request_samples_button'] = None
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='new',
+ **kwd) )
+ request = self.__save_request(trans, None, **kwd)
+ msg = 'The new request named %s has been created' % request.name
+ if params.get('create_request_button', False) == 'Save':
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ show_filter=trans.app.model.Request.states.UNSUBMITTED,
+ message=msg ,
+ status='done') )
+ elif params.get('create_request_samples_button', False) == 'Add samples':
+ new_kwd = {}
+ new_kwd['id'] = trans.security.encode_id(request.id)
+ new_kwd['operation'] = 'show_request'
+ new_kwd['add_sample'] = True
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
+ action='list',
+ message=msg ,
+ status='done',
+ **new_kwd) )
+ else:
+ return self.__show_request_form(trans, **kwd)
+ elif params.get('refresh', False) == 'true':
+ return self.__show_request_form(trans, **kwd)
+ def __show_request_form(self, trans, **kwd):
+ params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
+ try:
+ request_type = trans.app.model.RequestType.get(int(params.select_request_type))
+ except:
+ return trans.fill_template( '/admin/requests/new_request.mako',
+ select_request_type=self.__select_request_type(trans, 'none'),
+ widgets=[],
+ msg=msg,
+ messagetype=messagetype)
+ form_values = None
+ select_request_type = self.__select_request_type(trans, request_type.id)
+ # user
+ user_id = params.get( 'select_user', 'none' )
+ try:
+ user = trans.app.model.User.get(int(user_id))
+ except:
+ user = None
+ # list of widgets to be rendered on the request form
+ widgets = []
+ widgets.append(dict(label='Select user',
+ widget=self.__select_user(trans, user_id),
+ helptext='The request would be submitted on behalf of this user (Required)'))
+ widgets.append(dict(label='Name',
+ widget=TextField('name', 40,
+ util.restore_text( params.get( 'name', '' ) )),
+ helptext='(Required)'))
+ widgets.append(dict(label='Description',
+ widget=TextField('desc', 40,
+ util.restore_text( params.get( 'desc', '' ) )),
+ helptext='(Optional)'))
+ # libraries selectbox
+ libui = self.__library_ui(trans, user, **kwd)
+ widgets = widgets + libui
+ widgets = widgets + get_form_widgets(trans, request_type.request_form, contents=[], user=user, **kwd)
+ return trans.fill_template( '/admin/requests/new_request.mako',
+ select_request_type=select_request_type,
+ request_type=request_type,
+ widgets=widgets,
+ msg=msg,
+ messagetype=messagetype)
+ def __select_user(self, trans, userid):
+ user_ids = ['none']
+ for user in trans.app.model.User.query().all():
+ if not user.deleted:
+ user_ids.append(str(user.id))
+ select_user = SelectField('select_user',
+ refresh_on_change=True,
+ refresh_on_change_values=user_ids[1:])
+ if userid == 'none':
+ select_user.add_option('Select one', 'none', selected=True)
+ else:
+ select_user.add_option('Select one', 'none')
+ for user in trans.app.model.User.query().all():
+ if not user.deleted:
+ if userid == str(user.id):
+ select_user.add_option(user.email, user.id, selected=True)
+ else:
+ select_user.add_option(user.email, user.id)
+ return select_user
+
+ def __library_ui(self, trans, user, request=None, **kwd):
+ params = util.Params( kwd )
+ lib_id = params.get( 'library_id', 'none' )
+ if not user:
+ libraries = trans.app.model.Library.filter(trans.app.model.Library.table.c.deleted == False).order_by(trans.app.model.Library.name).all()
+ else:
+ libraries = get_authorized_libs(trans, user)
+ lib_list = SelectField('library_id', refresh_on_change=True,
+ refresh_on_change_values=['new'])
+ if request and lib_id == 'none':
+ if request.library:
+ lib_id = str(request.library.id)
+ if lib_id == 'none':
+ lib_list.add_option('Select one', 'none', selected=True)
+ else:
+ lib_list.add_option('Select one', 'none')
+ for lib in libraries:
+ if str(lib.id) == lib_id:
+ lib_list.add_option(lib.name, lib.id, selected=True)
+ else:
+ lib_list.add_option(lib.name, lib.id)
+ if lib_id == 'new':
+ lib_list.add_option('Create a new data library', 'new', selected=True)
+ else:
+ lib_list.add_option('Create a new data library', 'new')
+ widget = dict(label='Data library',
+ widget=lib_list,
+ helptext='Data library where the resultant dataset will be stored.')
+ if lib_id == 'new':
+ new_lib = dict(label='Create a new Library',
+ widget=TextField('new_library_name', 40,
+ util.restore_text( params.get( 'new_library_name', '' ) )),
+ helptext='Enter a library name here to request a new library')
+ return [widget, new_lib]
+ else:
+ return [widget]
+ def __validate(self, trans, request):
+ '''
+ Validates the request entered by the user
+ '''
+ empty_fields = []
+# if not request.library:
+# empty_fields.append('Library')
+ # check rest of the fields of the form
+ for index, field in enumerate(request.type.request_form.fields):
+ if field['required'] == 'required' and request.values.content[index] in ['', None]:
+ empty_fields.append(field['label'])
+ if empty_fields:
+ msg = 'Fill the following fields of the request <b>%s</b> before submitting<br/>' % request.name
+ for ef in empty_fields:
+ msg = msg + '<b>' +ef + '</b><br/>'
+ return msg
+ return None
+ def __save_request(self, trans, request=None, **kwd):
+ '''
+ This method saves a new request if request_id is None.
+ '''
+ params = util.Params( kwd )
+ request_type = trans.app.model.RequestType.get(int(params.select_request_type))
+ if request:
+ user = request.user
+ else:
+ user = trans.app.model.User.get(int(params.get('select_user', '')))
+ name = util.restore_text(params.get('name', ''))
+ desc = util.restore_text(params.get('desc', ''))
+ # library
+ try:
+ library = trans.app.model.Library.get(int(params.get('library_id', None)))
+ except:
+ library = None
+ # fields
+ values = []
+ for index, field in enumerate(request_type.request_form.fields):
+ if field['type'] == 'AddressField':
+ value = util.restore_text(params.get('field_%i' % index, ''))
+ if value == 'new':
+ # save this new address in the list of this user's addresses
+ user_address = trans.app.model.UserAddress( user=user )
+ user_address.desc = util.restore_text(params.get('field_%i_short_desc' % index, ''))
+ user_address.name = util.restore_text(params.get('field_%i_name' % index, ''))
+ user_address.institution = util.restore_text(params.get('field_%i_institution' % index, ''))
+ user_address.address = util.restore_text(params.get('field_%i_address1' % index, ''))+' '+util.restore_text(params.get('field_%i_address2' % index, ''))
+ user_address.city = util.restore_text(params.get('field_%i_city' % index, ''))
+ user_address.state = util.restore_text(params.get('field_%i_state' % index, ''))
+ user_address.postal_code = util.restore_text(params.get('field_%i_postal_code' % index, ''))
+ user_address.country = util.restore_text(params.get('field_%i_country' % index, ''))
+ user_address.phone = util.restore_text(params.get('field_%i_phone' % index, ''))
+ user_address.flush()
+ trans.user.refresh()
+ values.append(int(user_address.id))
+ elif value == unicode('none'):
+ values.append('')
+ else:
+ values.append(int(value))
+ else:
+ values.append(util.restore_text(params.get('field_%i' % index, '')))
+ form_values = trans.app.model.FormValues(request_type.request_form, values)
+ form_values.flush()
+ if not request:
+ request = trans.app.model.Request(name, desc, request_type,
+ user, form_values,
+ library=library,
+ state=trans.app.model.Request.states.UNSUBMITTED)
+ request.flush()
+ else:
+ request.name = name
+ request.desc = desc
+ request.type = request_type
+ request.user = user
+ request.values = form_values
+ request.library = library
+ request.flush()
+ return request
@web.expose
@web.require_admin
def bar_codes(self, trans, **kwd):
params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
request_id = params.get( 'request_id', None )
if request_id:
request = trans.app.model.Request.get( int( request_id ))
if not request:
- return trans.response.send_redirect( web.url_for( controller='requests',
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='list',
status='error',
message="Invalid request ID",
@@ -191,7 +846,9 @@
bc))
return trans.fill_template( '/admin/samples/bar_codes.mako',
samples_list=[s for s in request.samples],
- user=request.user, request=request, widgets=widgets)
+ user=request.user, request=request, widgets=widgets,
+ messagetype=messagetype,
+ msg=msg)
@web.expose
@web.require_admin
def save_bar_codes(self, trans, **kwd):
@@ -199,7 +856,7 @@
try:
request = trans.app.model.Request.get(int(params.get('request_id', None)))
except:
- return trans.response.send_redirect( web.url_for( controller='requests',
+ return trans.response.send_redirect( web.url_for( controller='requests_admin',
action='list',
status='error',
message="Invalid request ID",
@@ -255,9 +912,10 @@
sample.bar_code = bar_code
sample.flush()
return trans.response.send_redirect( web.url_for( controller='requests_admin',
- action='list',
- operation='show_request',
- id=trans.security.encode_id(request.id)) )
+ action='bar_codes',
+ request_id=request.id,
+ msg='Bar codes has been saved for this request',
+ messagetype='done'))
def __set_request_state(self, request):
# check if all the samples of the current request are in the final state
complete = True
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/tool_runner.py
--- a/lib/galaxy/web/controllers/tool_runner.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/tool_runner.py Sat Aug 22 23:20:09 2009 -0400
@@ -82,7 +82,7 @@
job = assoc.job
break
if not job:
- raise Exception("Failed to get job information for dataset hid %d" % hid)
+ raise Exception("Failed to get job information for dataset hid %d" % data.hid)
# Get the tool object
tool_id = job.tool_id
try:
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/controllers/user.py
--- a/lib/galaxy/web/controllers/user.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/controllers/user.py Sat Aug 22 23:20:09 2009 -0400
@@ -229,6 +229,8 @@
def manage_addresses(self, trans, **kwd):
if trans.user:
params = util.Params( kwd )
+ msg = util.restore_text( params.get( 'msg', '' ) )
+ messagetype = params.get( 'messagetype', 'done' )
show_filter = util.restore_text( params.get( 'show_filter', 'Active' ) )
if show_filter == 'All':
addresses = [address for address in trans.user.addresses]
@@ -238,7 +240,9 @@
addresses = [address for address in trans.user.addresses if not address.deleted]
return trans.fill_template( 'user/address.mako',
addresses=addresses,
- show_filter=show_filter)
+ show_filter=show_filter,
+ msg=msg,
+ messagetype=messagetype)
else:
# User not logged in, history group must be only public
return trans.show_error_message( "You must be logged in to change your default permitted actions." )
diff -r fbad627b45ac -r e6dda627e6b0 lib/galaxy/web/framework/base.py
--- a/lib/galaxy/web/framework/base.py Sat Aug 22 23:18:47 2009 -0400
+++ b/lib/galaxy/web/framework/base.py Sat Aug 22 23:20:09 2009 -0400
@@ -216,11 +216,18 @@
# tempfiles. Necessary for externalizing the upload tool. It's a little hacky
# but for performance reasons it's way better to use Paste's tempfile than to
# create a new one and copy.
-import cgi
+import cgi, tempfile
class FieldStorage( cgi.FieldStorage ):
def make_file(self, binary=None):
- import tempfile
return tempfile.NamedTemporaryFile()
+ def read_lines(self):
+ # Always make a new file
+ self.file = self.make_file()
+ self.__file = None
+ if self.outerboundary:
+ self.read_lines_to_outerboundary()
+ else:
+ self.read_lines_to_eof()
cgi.FieldStorage = FieldStorage
class Request( webob.Request ):
diff -r fbad627b45ac -r e6dda627e6b0 static/welcome.html
--- a/static/welcome.html Sat Aug 22 23:18:47 2009 -0400
+++ b/static/welcome.html Sat Aug 22 23:20:09 2009 -0400
@@ -71,7 +71,7 @@
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td>
- <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie1_TabSeq/flow.html',width:640,height:500,scroll:'no'})">
+ <a href="javascript:parent.show_in_overlay({url:'http://screencast.g2.bx.psu.edu/galaxy/quickie1_TabSeq/quickie1_TabSeq.flv',width:640,height:500,scroll:'no'})">
<div class="quickie">
<img src="images/qk/quickie1_small.png" border="0">
</div>
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/forms/edit_form.mako
--- a/templates/admin/forms/edit_form.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/admin/forms/edit_form.mako Sat Aug 22 23:20:09 2009 -0400
@@ -70,7 +70,7 @@
</%def>
<div class="toolForm">
- <div class="toolFormTitle">Edit form definition '${form.name}'</div>
+ <div class="toolFormTitle">Edit form definition "${form.name}"</div>
<form id="edit_form" name="edit_form" action="${h.url_for( controller='forms', action='edit', form_id=form.id, num_fields=len(form.fields) )}" method="post" >
%for label, input in form_details:
<div class="form-row">
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/requests/add_states.mako
--- a/templates/admin/requests/add_states.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/admin/requests/add_states.mako Sat Aug 22 23:20:09 2009 -0400
@@ -12,9 +12,9 @@
%for element_count in range( num_states ):
<div class="form-row">
<label>${1+element_count}) State name:</label>
- <input type="text" name="new_element_name_${element_count}" value="" size="40"/>
+ <input type="text" name="state_name_${element_count}" value="" size="40"/>
<label>State help text (optional):</label>
- <input type="text" name="new_element_description_${element_count}" value="" size="40"/>
+ <input type="text" name="state_desc_${element_count}" value="" size="40"/>
</div>
<div style="clear: both"></div>
%endfor
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/requests/edit_request.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/admin/requests/edit_request.mako Sat Aug 22 23:20:09 2009 -0400
@@ -0,0 +1,88 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+<script type="text/javascript">
+$( function() {
+ $( "select[refresh_on_change='true']").change( function() {
+ var refresh = false;
+ var refresh_on_change_values = $( this )[0].attributes.getNamedItem( 'refresh_on_change_values' )
+ if ( refresh_on_change_values ) {
+ refresh_on_change_values = refresh_on_change_values.value.split( ',' );
+ var last_selected_value = $( this )[0].attributes.getNamedItem( 'last_selected_value' );
+ for( i= 0; i < refresh_on_change_values.length; i++ ) {
+ if ( $( this )[0].value == refresh_on_change_values[i] || ( last_selected_value && last_selected_value.value == refresh_on_change_values[i] ) ){
+ refresh = true;
+ break;
+ }
+ }
+ }
+ else {
+ refresh = true;
+ }
+ if ( refresh ){
+ $( "#edit_request" ).submit();
+ }
+ });
+});
+</script>
+
+<br/>
+<br/>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='list', operation='show_request', id=trans.security.encode_id(request.id) )}">
+ <span>Browse this request</span></a>
+ </li>
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='list')}">
+ <span>Browse requests</span></a>
+ </li>
+</ul>
+
+<div class="toolForm">
+ <div class="toolFormTitle">Edit request "${request.name}" from ${request.user.email}</div>
+ %if len(select_request_type.options) == 1:
+ There are no request types created for a new request.
+ %else:
+ <div class="toolFormBody">
+ <form name="edit_request" id="edit_request" action="${h.url_for( controller='requests_admin', action='edit', request_id=request.id)}" method="post" >
+ <div class="form-row">
+ <label>
+ Select Request Type:
+ </label>
+ ${select_request_type.get_html()}
+ </div>
+
+ %if select_request_type.get_selected() != ('Select one', 'none'):
+ %for i, field in enumerate(widgets):
+ <div class="form-row">
+ <label>${field['label']}</label>
+ ${field['widget'].get_html()}
+ %if field['label'] == 'Library' and new_library:
+ ${new_library.get_html()}
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ ${field['helptext']}
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ %endfor
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="refresh" value="true" size="40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="save_changes_request_button" value="Save changes"/>
+ ##<input type="submit" name="edit_samples_button" value="Edit samples"/>
+ </div>
+ %endif
+ </form>
+ </div>
+</div>
+%endif
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/requests/grid.mako
--- a/templates/admin/requests/grid.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/admin/requests/grid.mako Sat Aug 22 23:20:09 2009 -0400
@@ -76,29 +76,28 @@
<div class="grid-header">
<h2>${grid.title}</h2>
- ##%if len(query.all()):
+ %if len(trans.app.model.Request.query().all()):
##<span class="title">Filter:</span>
%for i, filter in enumerate( grid.standard_filters ):
%if i > 0:
<span>|</span>
%endif
- %if 'state' in grid.default_filter:
- %if grid.default_filter['state'] == filter.label:
- <span class="filter"><a href="${h.url_for( controller='requests_admin', action='list', show_filter=filter.label )}"><b>${filter.label}</b></a></span>
- %else:
- <span class="filter"><a href="${h.url_for( controller='requests_admin', action='list', show_filter=filter.label )}">${filter.label}</a></span>
- %endif
+ %if grid.show_filter == filter.label:
+ <span class="filter"><a href="${h.url_for( controller='requests_admin', action='list', show_filter=filter.label )}"><b>${filter.label}</b></a></span>
%else:
- %if filter.label == 'All':
- <span class="filter"><a href="${h.url_for( controller='requests_admin', action='list', show_filter=filter.label )}"><b>${filter.label}</b></a></span>
- %else:
- <span class="filter"><a href="${h.url_for( controller='requests_admin', action='list', show_filter=filter.label )}">${filter.label}</a></span>
- %endif
+ <span class="filter"><a href="${h.url_for( controller='requests_admin', action='list', show_filter=filter.label )}">${filter.label}</a></span>
%endif
%endfor
- ##%endif
+ %endif
</div>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='new', select_request_type=True )}">
+ <img src="${h.url_for('/static/images/silk/add.png')}" />
+ <span>Create a new request</span></a>
+ </li>
+</ul>
%if not len(query.all()):
There are no requests.
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/requests/new_request.mako
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/templates/admin/requests/new_request.mako Sat Aug 22 23:20:09 2009 -0400
@@ -0,0 +1,84 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if msg:
+ ${render_msg( msg, messagetype )}
+%endif
+
+<script type="text/javascript">
+$( function() {
+ $( "select[refresh_on_change='true']").change( function() {
+ var refresh = false;
+ var refresh_on_change_values = $( this )[0].attributes.getNamedItem( 'refresh_on_change_values' )
+ if ( refresh_on_change_values ) {
+ refresh_on_change_values = refresh_on_change_values.value.split( ',' );
+ var last_selected_value = $( this )[0].attributes.getNamedItem( 'last_selected_value' );
+ for( i= 0; i < refresh_on_change_values.length; i++ ) {
+ if ( $( this )[0].value == refresh_on_change_values[i] || ( last_selected_value && last_selected_value.value == refresh_on_change_values[i] ) ){
+ refresh = true;
+ break;
+ }
+ }
+ }
+ else {
+ refresh = true;
+ }
+ if ( refresh ){
+ $( "#new_request" ).submit();
+ }
+ });
+});
+</script>
+
+<br/>
+<br/>
+<ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='list')}">
+ <span>Browse requests</span></a>
+ </li>
+</ul>
+
+<div class="toolForm">
+ <div class="toolFormTitle">Add a new request</div>
+ %if len(select_request_type.options) == 1:
+ There are no request types created for a new request.
+ %else:
+ <div class="toolFormBody">
+ <form name="new_request" id="new_request" action="${h.url_for( controller='requests_admin', action='new', create=True )}" method="post" >
+ <div class="form-row">
+ <label>
+ Select Request Type
+ </label>
+ ${select_request_type.get_html()}
+ </div>
+
+ %if select_request_type.get_selected() != ('Select one', 'none'):
+ %for i, field in enumerate(widgets):
+ <div class="form-row">
+ <label>${field['label']}</label>
+ ${field['widget'].get_html()}
+ %if field['label'] == 'Library' and new_library:
+ ${new_library.get_html()}
+ %endif
+ <div class="toolParamHelp" style="clear: both;">
+ ${field['helptext']}
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ %endfor
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="refresh" value="true" size="40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="create_request_button" value="Save"/>
+ <input type="submit" name="create_request_samples_button" value="Add samples"/>
+ </div>
+ %endif
+ </form>
+ </div>
+</div>
+%endif
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/requests/show_request.mako
--- a/templates/admin/requests/show_request.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/admin/requests/show_request.mako Sat Aug 22 23:20:09 2009 -0400
@@ -12,18 +12,49 @@
</div>
<ul class="manage-table-actions">
- <li>
- <a class="action-button" href="${h.url_for( controller='requests_admin', action='bar_codes', request_id=request.id)}">
- <span>Bar codes</span></a>
- </li>
+ %if request.unsubmitted() and request.samples:
+ <li>
+ <a class="action-button" confirm="More samples cannot be added to this request once it is submitted. Click OK to submit." href="${h.url_for( controller='requests_admin', action='submit_request', id=request.id)}">
+ <span>Submit request</span></a>
+ </li>
+ %endif
+ %if request.submitted() and request.samples:
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='bar_codes', request_id=request.id)}">
+ <span>Bar codes</span></a>
+ </li>
+ %endif
</ul>
+
+<%def name="render_sample_form( index, sample_name, sample_values )">
+ <td>
+ <input type="text" name=sample_${index}_name value="${sample_name}" size="10"/>
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${' (required)' }</i>
+ </div>
+ </td>
+ <td>
+ </td>
+ %for field_index, field in enumerate(request.type.sample_form.fields):
+ <td>
+ <input type="text" name=sample_${index}_field_${field_index} value="${sample_values[field_index]}" size="7"/>
+ <div class="toolParamHelp" style="clear: both;">
+ <i>${'('+field['required']+')' }</i>
+ </div>
+ </td>
+ %endfor
+</%def>
<%def name="render_sample( index, sample )">
<td>
${sample.name}
</td>
<td>
- <a href="${h.url_for( controller='requests_admin', action='show_events', sample_id=sample.id)}">${sample.current_state().name}</a>
+ %if sample.request.unsubmitted():
+ Unsubmitted
+ %else:
+ <a href="${h.url_for( controller='requests_admin', action='show_events', sample_id=sample.id)}">${sample.current_state().name}</a>
+ %endif
</td>
%for field_index, field in enumerate(request.type.sample_form.fields):
<td>
@@ -34,10 +65,10 @@
%endif
</td>
%endfor
+
</%def>
<div class="toolForm">
- ##<div class="toolFormTitle">Request Details: '${request_details[0]['value']}'</div>
<div class="form-row">
<a href="${h.url_for( controller='requests_admin', action='toggle_request_details', request_id=request.id )}">${details_state}</a>
</div>
@@ -57,13 +88,21 @@
</div>
<div style="clear: both"></div>
%endfor
+ <div class="form-row">
+ <ul class="manage-table-actions">
+ <li>
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='edit', show=True, request_id=request.id)}">
+ <span>Edit request details</span></a>
+ </li>
+ </ul>
+ </div>
%endif
</div>
</div>
<div class="toolForm">
##<div class="toolFormTitle">Samples (${len(request.samples)})</div>
- <form id="edit_form" name="edit_form" action="${h.url_for( controller='requests', action='show_request', request_id=request.id )}" method="post" >
+ <form id="edit_form" name="edit_form" action="${h.url_for( controller='requests_admin', action='show_request' )}" enctype="multipart/form-data" method="post" >
<div class="form-row">
%if current_samples:
<table class="grid">
@@ -80,22 +119,88 @@
</div>
</th>
%endfor
+ <th></th>
</tr>
<thead>
<tbody>
+ <%
+ request.refresh()
+ %>
%for sample_index, sample in enumerate(current_samples):
- <tr>
- <td>${sample_index+1}</td>
- ${render_sample( sample_index, request.samples[sample_index] )}
- </tr>
+ %if edit_mode:
+ <tr>
+ <td>${sample_index+1}</td>
+ ${render_sample_form( sample_index, sample[0], sample[1])}
+ </tr>
+ %else:
+ <tr>
+ <td>${sample_index+1}</td>
+ %if sample_index in range(len(request.samples)):
+ ${render_sample( sample_index, request.samples[sample_index] )}
+ %else:
+ ${render_sample_form( sample_index, sample[0], sample[1])}
+ %endif
+ <td>
+ %if request.unsubmitted():
+ <a class="action-button" href="${h.url_for( controller='requests_admin', action='delete_sample', request_id=request.id, sample_id=sample_index)}">
+ <img src="${h.url_for('/static/images/delete_icon.png')}" />
+ <span></span></a>
+ %endif
+ </td>
+ </tr>
+ %endif
%endfor
</tbody>
</table>
%else:
<label>There are no samples.</label>
%endif
-
</div>
- ##</div>
+ %if not edit_mode:
+ <table class="grid">
+ <tbody>
+ <tr>
+ <div class="form-row">
+ <td>
+ %if current_samples and not request.complete():
+ <input type="submit" name="edit_samples_button" value="Edit samples"/>
+ %endif
+ </td>
+ %if request.unsubmitted():
+ <td>
+ <label>Import from csv file</label>
+ <input type="file" name="file_data" />
+ <input type="submit" name="import_samples_button" value="Import samples"/>
+ </td>
+ <td>
+ %if current_samples:
+ <label>Copy from sample</label>
+ ${sample_copy.get_html()}
+ %endif
+ <input type="submit" name="add_sample_button" value="Add New"/>
+ </td>
+ %endif
+ </div>
+ </tr>
+ </tbody>
+ </table>
+ %endif
+ %if request.samples or current_samples:
+ <div class="form-row">
+ <div style="float: left; width: 250px; margin-right: 10px;">
+ <input type="hidden" name="refresh" value="true" size="40"/>
+ </div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ %if edit_mode:
+ <input type="submit" name="save_samples_button" value="Save"/>
+ <input type="submit" name="cancel_changes_button" value="Cancel"/>
+ %elif request.unsubmitted():
+ <input type="submit" name="save_samples_button" value="Save"/>
+ %endif
+ </div>
+ %endif
+ <input type="hidden" name="request_id" value="${request.id}" />
</form>
</div>
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/samples/bar_codes.mako
--- a/templates/admin/samples/bar_codes.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/admin/samples/bar_codes.mako Sat Aug 22 23:20:09 2009 -0400
@@ -40,7 +40,7 @@
</tbody>
</table>
<div class="form-row">
- <input type="submit" name="save_new_sample_type" value="Save"/>
+ <input type="submit" name="save_bar_codes" value="Save"/>
</div>
</form>
</div>
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 templates/admin/samples/events.mako
--- a/templates/admin/samples/events.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/admin/samples/events.mako Sat Aug 22 23:20:09 2009 -0400
@@ -56,7 +56,7 @@
</div>
%endfor
<div class="form-row">
- <input type="submit" name="add_event" value="Save"/>
+ <input type="submit" name="add_event_button" value="Save"/>
</div>
</form>
</div>
diff -r fbad627b45ac -r e6dda627e6b0 templates/base_panels.mako
--- a/templates/base_panels.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/base_panels.mako Sat Aug 22 23:20:09 2009 -0400
@@ -138,7 +138,7 @@
${tab( "libraries", "Data Libraries", h.url_for( controller='library', action='index' ))}
- %if trans.request_types():
+ %if trans.user and trans.request_types():
<td class="tab">
<a>Lab</a>
<div class="submenu">
diff -r fbad627b45ac -r e6dda627e6b0 templates/requests/grid.mako
--- a/templates/requests/grid.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/requests/grid.mako Sat Aug 22 23:20:09 2009 -0400
@@ -76,7 +76,7 @@
<div class="grid-header">
<h2>${grid.title}</h2>
- ##%if len(query.all()):
+ %if len(trans.user.requests):
##<span class="title">Filter:</span>
%for i, filter in enumerate( grid.standard_filters ):
%if i > 0:
@@ -88,17 +88,16 @@
<span class="filter"><a href="${h.url_for( controller='requests', action='list', show_filter=filter.label )}">${filter.label}</a></span>
%endif
%endfor
- ##%endif
+ %endif
</div>
<ul class="manage-table-actions">
<li>
<a class="action-button" href="${h.url_for( controller='requests', action='new', select_request_type=True )}">
<img src="${h.url_for('/static/images/silk/add.png')}" />
- <span>New request</span></a>
+ <span>Create a new request</span></a>
</li>
</ul>
-
%if not len(query.all()):
There are no request(s).
@@ -215,4 +214,4 @@
</tfoot>
</table>
</form>
-%endif
\ No newline at end of file
+%endif
diff -r fbad627b45ac -r e6dda627e6b0 templates/requests/show_request.mako
--- a/templates/requests/show_request.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/requests/show_request.mako Sat Aug 22 23:20:09 2009 -0400
@@ -68,7 +68,6 @@
</%def>
<div class="toolForm">
- ##<div class="toolFormTitle">Request Details: '${request_details[0]['value']}'</div>
<div class="form-row">
<a href="${h.url_for( controller='requests', action='toggle_request_details', request_id=request.id )}">${details_state}</a>
</div>
@@ -217,7 +216,6 @@
%endif
</div>
%endif
- ##</div>
- <input type="hidden" name="request_id" value="${request.id}" />
+ <input type="hidden" name="request_id" value="${request.id}" />
</form>
</div>
diff -r fbad627b45ac -r e6dda627e6b0 templates/user/address.mako
--- a/templates/user/address.mako Sat Aug 22 23:18:47 2009 -0400
+++ b/templates/user/address.mako Sat Aug 22 23:20:09 2009 -0400
@@ -33,51 +33,48 @@
</div>
-
-<div class="toolForm">
- ##<div class="toolFormTitle">Addresses</div>
- <div class="toolFormBody">
- <% trans.user.refresh() %>
- %if not trans.user.addresses:
- <label>There are no addresses</label>
- %else:
- <table class="grid">
- <tbody>
- %for index, address in enumerate(addresses):
- <tr class="libraryRow libraryOrFolderRow" id="libraryRow">
-
- <td>
- <div class="form-row">
- <label>${address.desc}</label>
- ${address.display()}
- </div>
- <div class="form-row">
- <ul class="manage-table-actions">
- <li>
- %if not address.deleted:
- <a class="action-button" href="${h.url_for( controller='user', action='edit_address', address_id=address.id,
- short_desc=address.desc,
- name=address.name, institution=address.institution,
- address1=address.address, city=address.city,
- state=address.state, postal_code=address.postal_code,
- country=address.country, phone=address.phone)}">
- <span>Edit</span></a>
- <a class="action-button" href="${h.url_for( controller='user', action='delete_address', address_id=address.id)}">
- <span>Delete address</span></a>
- %else:
- <a class="action-button" href="${h.url_for( controller='user', action='undelete_address', address_id=address.id)}">
- <span>Undelete address</span></a>
- %endif
-
- </li>
- </ul>
- </div>
- </td>
- </tr>
- %endfor
- </tbody>
- </table>
- %endif
+%if not addresses:
+ <label>There are no addresses</label>
+%else:
+ <div class="toolForm">
+ <div class="toolFormBody">
+ <% trans.user.refresh() %>
+ <table class="grid">
+ <tbody>
+ %for index, address in enumerate(addresses):
+ <tr class="libraryRow libraryOrFolderRow" id="libraryRow">
+ <td>
+ <div class="form-row">
+ <label>${address.desc}</label>
+ ${address.display()}
+ </div>
+ <div class="form-row">
+ <ul class="manage-table-actions">
+ <li>
+ %if not address.deleted:
+ <a class="action-button" href="${h.url_for( controller='user', action='edit_address', address_id=address.id,
+ short_desc=address.desc,
+ name=address.name, institution=address.institution,
+ address1=address.address, city=address.city,
+ state=address.state, postal_code=address.postal_code,
+ country=address.country, phone=address.phone)}">
+ <span>Edit</span></a>
+ <a class="action-button" href="${h.url_for( controller='user', action='delete_address', address_id=address.id)}">
+ <span>Delete</span></a>
+ %else:
+ <a class="action-button" href="${h.url_for( controller='user', action='undelete_address', address_id=address.id)}">
+ <span>Undelete</span></a>
+ %endif
+
+ </li>
+ </ul>
+ </div>
+ </td>
+ </tr>
+ %endfor
+ </tbody>
+ </table>
+ %endif
+ </div>
</div>
-</div>
%endif
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 test/base/twilltestcase.py
--- a/test/base/twilltestcase.py Sat Aug 22 23:18:47 2009 -0400
+++ b/test/base/twilltestcase.py Sat Aug 22 23:20:09 2009 -0400
@@ -972,7 +972,7 @@
self.home()
# Form stuff
- def create_form( self, name='Form One', description='This is Form One', num_fields=1 ):
+ def create_form( self, name='Form One', desc='This is Form One', num_fields=1 ):
"""
Create a new form definition. Testing framework is still limited to only testing
one instance for each repeat. This has to do with the 'flat' nature of defining
@@ -988,7 +988,7 @@
self.visit_url( "%s/forms/new" % self.url )
self.check_page_for_string( 'Create a new form definition' )
tc.fv( "1", "name", name ) # form field 1 is the field named name...
- tc.fv( "1", "description", description ) # form field 1 is the field named name...
+ tc.fv( "1", "description", desc ) # form field 1 is the field named name...
tc.submit( "create_form_button" )
for index in range( num_fields ):
field_name = 'field_name_%i' % index
@@ -1001,7 +1001,140 @@
check_str = "The form '%s' has been updated with the changes." % name
self.check_page_for_string( check_str )
self.home()
-
+ def edit_form( self, form_id, form_name, new_form_name="Form One's Name (Renamed)", new_form_desc="This is Form One's description (Re-described)"):
+ """
+ Edit form details; name & description
+ """
+ self.home()
+ self.visit_url( "%s/forms/edit?form_id=%i&show_form=True" % (self.url, form_id) )
+ self.check_page_for_string( 'Edit form definition "%s"' % form_name )
+ tc.fv( "1", "name", new_form_name )
+ tc.fv( "1", "description", new_form_desc )
+ tc.submit( "save_changes_button" )
+ self.check_page_for_string( "The form '%s' has been updated with the changes." % new_form_name )
+ self.home()
+ def form_add_field( self, form_id, form_name, field_index, fields):
+ """
+ Add a new fields to the form definition
+ """
+ self.home()
+ self.visit_url( "%s/forms/edit?form_id=%i&show_form=True" % (self.url, form_id) )
+ self.check_page_for_string( 'Edit form definition "%s"' % form_name)
+ for i, field in enumerate(fields):
+ index = i+field_index
+ tc.submit( "add_field_button" )
+ tc.fv( "1", "field_name_%i" % index, field['name'] )
+ tc.fv( "1", "field_helptext_%i" % index, field['desc'] )
+ tc.fv( "1", "field_type_%i" % index, field['type'] )
+ tc.fv( "1", "field_required_%i" % index, field['required'] )
+# if field['type'] == 'SelectField':
+# for option_index, option in enumerate(field['selectlist']):
+# self.visit_url( "%s/forms/edit?select_box_options=add&form_id=%i&field_index=%i" % \
+# (self.url, form_id, index))
+# #data = self.last_page()
+# #file( "rc.html", 'wb' ).write(data)
+# tc.fv( "1", "field_%i_option_%i" % (index, option_index), option )
+ tc.submit( "save_changes_button" )
+ check_str = "The form '%s' has been updated with the changes." % form_name
+ self.check_page_for_string( check_str )
+ self.home()
+ def form_remove_field( self, form_id, form_name, field_name):
+ """
+ Remove a field from the form definition
+ """
+ self.home()
+ self.visit_url( "%s/forms/edit?form_id=%i&show_form=True" % (self.url, form_id) )
+ self.check_page_for_string( 'Edit form definition "%s"' % form_name)
+ tc.submit( "remove_button" )
+ tc.submit( "save_changes_button" )
+ check_str = "The form '%s' has been updated with the changes." % form_name
+ self.check_page_for_string( check_str )
+ self.home()
+ # Requests stuff
+ def create_request_type( self, name, desc, request_form_id, sample_form_id, states ):
+ self.home()
+ self.visit_url( "%s/admin/request_type?create=True" % self.url )
+ self.check_page_for_string( 'Create a new request type' )
+ tc.fv( "1", "name", name )
+ tc.fv( "1", "description", desc )
+ tc.fv( "1", "request_form_id", request_form_id )
+ tc.fv( "1", "sample_form_id", sample_form_id )
+ tc.fv( "1", "num_states", str( len( states ) ) )
+ tc.submit( "define_states_button" )
+ self.check_page_for_string( "Create %i states for the '%s' request type" % ( len(states), name ))
+ for index, state in enumerate(states):
+ tc.fv("1", "state_name_%i" % index, state[0])
+ tc.fv("1", "state_desc_%i" % index, state[1])
+ tc.submit( "save_request_type" )
+ self.check_page_for_string( "Request type <b>%s</b> has been created" % name )
+ def create_request( self, request_type_id, name, desc, library_id, fields ):
+ self.home()
+ self.visit_url( "%s/requests/new?create=True&select_request_type=%i" % (self.url, request_type_id) )
+ self.check_page_for_string( 'Add a new request' )
+ tc.fv( "1", "name", name )
+ tc.fv( "1", "desc", desc )
+ tc.fv( "1", "library_id", str(library_id) )
+ for index, field_value in enumerate(fields):
+ tc.fv( "1", "field_%i" % index, field_value )
+ tc.submit( "create_request_button" )
+ def create_request_admin( self, request_type_id, user_id, name, desc, library_id, fields ):
+ self.home()
+ self.visit_url( "%s/requests_admin/new?create=True&select_request_type=%i" % (self.url, request_type_id) )
+ self.check_page_for_string( 'Add a new request' )
+ tc.fv( "1", "select_user", str(user_id) )
+ tc.fv( "1", "name", name )
+ tc.fv( "1", "desc", desc )
+ tc.fv( "1", "library_id", str(library_id) )
+ for index, field_value in enumerate(fields):
+ tc.fv( "1", "field_%i" % index, field_value )
+ tc.submit( "create_request_button" )
+ def edit_request( self, request_id, name, new_name, new_desc, new_library_id, new_fields):
+ self.home()
+ self.visit_url( "%s/requests/edit?request_id=%i&show=True" % (self.url, request_id) )
+ self.check_page_for_string( 'Edit request "%s"' % name )
+ tc.fv( "1", "name", new_name )
+ tc.fv( "1", "desc", new_desc )
+ tc.fv( "1", "library_id", str(new_library_id) )
+ for index, field_value in enumerate(new_fields):
+ tc.fv( "1", "field_%i" % index, field_value )
+ tc.submit( "save_changes_request_button" )
+ def add_samples( self, request_id, request_name, samples ):
+ self.home()
+ self.visit_url( "%s/requests/list?sort=-create_time&operation=show_request&id=%s" % ( self.url, self.security.encode_id( request_id ) ))
+ self.check_page_for_string( 'Sequencing Request "%s"' % request_name )
+ for sample_index, sample in enumerate(samples):
+ tc.submit( "add_sample_button" )
+ sample_name, fields = sample
+ tc.fv( "1", "sample_%i_name" % sample_index, sample_name )
+ for field_index, field_value in enumerate(fields):
+ tc.fv( "1", "sample_%i_field_%i" % ( sample_index, field_index ), field_value )
+ tc.submit( "save_samples_button" )
+ def submit_request( self, request_id, request_name ):
+ self.home()
+ self.visit_url( "%s/requests/submit_request?id=%i" % ( self.url, request_id ))
+ self.check_page_for_string( 'The request <b>%s</b> has been submitted.' % request_name )
+ def add_bar_codes( self, request_id, request_name, bar_codes ):
+ self.home()
+ self.visit_url( "%s/requests_admin/bar_codes?request_id=%i" % (self.url, request_id) )
+ self.check_page_for_string( 'Bar codes for Samples of Request "%s"' % request_name )
+ for index, bar_code in enumerate(bar_codes):
+ tc.fv( "1", "sample_%i_bar_code" % index, bar_code )
+ tc.submit( "save_bar_codes" )
+ def change_sample_state( self, sample_name, sample_id, new_state_id, comment='' ):
+ self.home()
+ self.visit_url( "%s/requests_admin/show_events?sample_id=%i" % (self.url, sample_id) )
+ self.check_page_for_string( 'Events for Sample "%s"' % sample_name )
+ tc.fv( "1", "select_state", str(new_state_id) )
+ tc.fv( "1", "comment", comment )
+ tc.submit( "add_event_button" )
+ # Address stuff
+ def create_address( self, address ):
+ self.home()
+ self.visit_url( "%s/user/new_address" % self.url )
+ self.check_page_for_string( 'New address' )
+ for name, value in address.iteritems():
+ tc.fv( "1", name, value )
+ tc.submit( "Save_button" )
# Library stuff
def create_library( self, name='Library One', description='This is Library One' ):
"""Create a new library"""
diff -r fbad627b45ac -r e6dda627e6b0 test/functional/test_forms_and_requests.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/functional/test_forms_and_requests.py Sat Aug 22 23:20:09 2009 -0400
@@ -0,0 +1,274 @@
+import galaxy.model
+from galaxy.model.orm import *
+from base.twilltestcase import *
+
+not_logged_in_as_admin_security_msg = 'You must be logged in as an administrator to access this feature.'
+logged_in_as_admin_security_msg = 'You must be an administrator to access this feature.'
+not_logged_in_security_msg = 'You must be logged in to create/submit sequencing requests'
+form_one_name = "Request Form"
+form_two_name = "Sample Form"
+request_type_name = 'Test Requestype'
+sample_states = [ ( 'New', 'Sample entered into the system' ),
+ ( 'Received', 'Sample tube received' ),
+ ( 'Done', 'Sequence run complete' ) ]
+address1 = dict( short_desc="Office",
+ name="James Bond",
+ institution="MI6" ,
+ address1="MI6 Headquaters",
+ address2="",
+ city="London",
+ state="London",
+ postal_code="007",
+ country="United Kingdom",
+ phone="007-007-0007" )
+
+
+def get_latest_form(form_name):
+ fdc_list = galaxy.model.FormDefinitionCurrent.filter( galaxy.model.FormDefinitionCurrent.table.c.deleted==False )\
+ .order_by( galaxy.model.FormDefinitionCurrent.table.c.create_time.desc() )
+ for fdc in fdc_list:
+ if form_name == fdc.latest_form.name:
+ return fdc.latest_form
+ return None
+
+
+class TestFormsAndRequests( TwillTestCase ):
+ def test_000_create_form( self ):
+ """Testing creating a new form and editing it"""
+ self.logout()
+ self.login( email='test(a)bx.psu.edu' )
+ # create a form
+ global form_one_name
+ name = form_one_name
+ desc = "This is Form One's description"
+ self.create_form( name=name, desc=desc )
+ self.home()
+ self.visit_page( 'forms/manage' )
+ self.check_page_for_string( name )
+ self.check_page_for_string( desc )
+ # Get the form_definition object for later tests
+ form_one = galaxy.model.FormDefinition.filter( and_( galaxy.model.FormDefinition.table.c.name==name,
+ galaxy.model.FormDefinition.table.c.desc==desc ) ).all()[-1]
+ assert form_one is not None, 'Problem retrieving form named "%s" from the database' % name
+ # edit form & add few more fields
+ new_name = "Request Form (Renamed)"
+ new_desc = "This is Form One's Re-described"
+ self.edit_form( form_one.id, form_one.name, new_form_name=new_name, new_form_desc=new_desc )
+ self.home()
+ self.visit_page( 'forms/manage' )
+ self.check_page_for_string( new_name )
+ self.check_page_for_string( new_desc )
+ form_one_name = new_name
+ def test_005_add_form_fields( self ):
+ """Testing adding fields to a form definition"""
+ fields = [dict(name='Test field name one',
+ desc='Test field description one',
+ type='TextField',
+ required='required'),
+ dict(name='Test field name two',
+ desc='Test field description two',
+ type='AddressField',
+ required='optional')]
+ form_one = get_latest_form(form_one_name)
+ self.form_add_field(form_one.id, form_one.name, field_index=len(form_one.fields), fields=fields)
+ form_one_latest = get_latest_form(form_one_name)
+ assert len(form_one_latest.fields) == len(form_one.fields)+len(fields)
+#This following test has been commented out as it is causing:
+#TwillException: multiple matches to "remove_button"
+# def test_010_remove_form_fields( self ):
+# """Testing removing fields from a form definition"""
+# form_one = get_latest_form(form_one_name)
+# self.form_remove_field( form_one.id, form_one.name, 'Test field name one' )
+# form_one_latest = get_latest_form(form_one_name)
+# assert len(form_one_latest.fields) == len(form_one.fields)-1
+ def test_015_create_sample_form( self ):
+ """Testing creating another form (for samples)"""
+ global form_two_name
+ name = form_two_name
+ desc = "This is Form One's description"
+ self.create_form( name=name, desc=desc )
+ self.home()
+ self.visit_page( 'forms/manage' )
+ self.check_page_for_string( name )
+ self.check_page_for_string( desc )
+ def test_020_create_request_type( self ):
+ """Testing creating a new requestype"""
+ request_form = get_latest_form(form_one_name)
+ sample_form = get_latest_form(form_two_name)
+ self.create_request_type(request_type_name, "test request type",
+ str(request_form.id), str(sample_form.id), sample_states )
+ global request_type
+ request_type = galaxy.model.RequestType.filter( and_( galaxy.model.RequestType.table.c.name==request_type_name ) ).all()[-1]
+ assert request_type is not None, 'Problem retrieving request type named "%s" from the database' % request_type_name
+ def test_025_create_address_and_library( self ):
+ """Testing address & library creation"""
+ # first create a regular user
+ self.logout()
+ self.login( email='test1(a)bx.psu.edu' )
+ self.logout()
+ self.login( email='test(a)bx.psu.edu' )
+ # first create a library for the request so that it can be submitted later
+ lib_name = 'TestLib001'
+ self.create_library( lib_name, '' )
+ self.visit_page( 'admin/browse_libraries' )
+ self.check_page_for_string( lib_name )
+ # Get the library object for later tests
+ global library_one
+ library_one = galaxy.model.Library.filter( and_( galaxy.model.Library.table.c.name==lib_name,
+ galaxy.model.Library.table.c.deleted==False ) ).first()
+ assert library_one is not None, 'Problem retrieving library named "%s" from the database' % lib_name
+ global admin_user
+ admin_user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test(a)bx.psu.edu' ).first()
+ assert admin_user is not None, 'Problem retrieving user with email "test(a)bx.psu.edu" from the database'
+ # Get the admin user's private role for later use
+ global admin_user_private_role
+ admin_user_private_role = None
+ for role in admin_user.all_roles():
+ if role.name == admin_user.email and role.description == 'Private Role for %s' % admin_user.email:
+ admin_user_private_role = role
+ break
+ if not admin_user_private_role:
+ raise AssertionError( "Private role not found for user '%s'" % admin_user.email )
+ global regular_user1
+ regular_user1 = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test1(a)bx.psu.edu' ).first()
+ assert regular_user1 is not None, 'Problem retrieving user with email "test1(a)bx.psu.edu" from the database'
+ # Get the regular user's private role for later use
+ global regular_user1_private_role
+ regular_user1_private_role = None
+ for role in regular_user1.all_roles():
+ if role.name == regular_user1.email and role.description == 'Private Role for %s' % regular_user1.email:
+ regular_user1_private_role = role
+ break
+ if not regular_user1_private_role:
+ raise AssertionError( "Private role not found for user '%s'" % regular_user1.email )
+ # Set permissions on the library, sort for later testing
+ permissions_in = [ k for k, v in galaxy.model.Library.permitted_actions.items() ]
+ permissions_out = []
+ # Role one members are: admin_user, regular_user1. Each of these users will be permitted to
+ # LIBRARY_ADD, LIBRARY_MODIFY, LIBRARY_MANAGE for library items.
+ self.set_library_permissions( str( library_one.id ), library_one.name, str( regular_user1_private_role.id ), permissions_in, permissions_out )
+ # create address
+ #self.create_address( user_address1 )
+ #self.check_page_for_string( 'Address <b>%s</b> has been added' % user_address1[ 'short_desc' ] )
+ ## TODO: FIX HACK
+ ## the user address creation should be done as a test.
+ global user_address
+ user_address = galaxy.model.UserAddress()
+ user_address.user = galaxy.model.User.filter( galaxy.model.User.table.c.email=='test1(a)bx.psu.edu' ).first()
+ user_address.desc = address1[ 'short_desc' ]
+ user_address.name = address1[ 'name' ]
+ user_address.institution = address1[ 'institution' ]
+ user_address.address = address1[ 'address1' ]+' '+address1[ 'address2' ]
+ user_address.city = address1[ 'city' ]
+ user_address.state = address1[ 'state' ]
+ user_address.postal_code = address1[ 'postal_code' ]
+ user_address.country = address1[ 'country' ]
+ user_address.phone = address1[ 'phone' ]
+ user_address.flush()
+ user_address.user.refresh()
+ def test_030_create_request( self ):
+ """Testing creating, editing and submitting a request as a regular user"""
+ # login as a regular user
+ self.logout()
+ self.login( email='test1(a)bx.psu.edu' )
+ # set field values
+ fields = ['field one value', 'field two value', str(user_address.id)]
+ # create the request
+ request_name, request_desc = 'Request One', 'Request One Description'
+ self.create_request(request_type.id, request_name, request_desc, library_one.id, fields)
+ self.check_page_for_string( request_name )
+ self.check_page_for_string( request_desc )
+ global request_one
+ request_one = galaxy.model.Request.filter( and_( galaxy.model.Request.table.c.name==request_name,
+ galaxy.model.Request.table.c.deleted==False ) ).first()
+ # check if the request's state is now set to 'unsubmitted'
+ assert request_one.state is not request_one.states.UNSUBMITTED, "The state of the request '%s' should be set to '%s'" % ( request_one.name, request_one.states.UNSUBMITTED )
+ # sample fields
+ samples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
+ ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
+ # add samples to this request
+ self.add_samples( request_one.id, request_one.name, samples )
+ for sample_name, fields in samples:
+ self.check_page_for_string( sample_name )
+ self.check_page_for_string( 'Unsubmitted' )
+ for field_value in fields:
+ self.check_page_for_string( field_value )
+ # edit this request
+ fields = ['field one value (editted)', 'field two value (editted)', str(user_address.id)]
+ self.edit_request(request_one.id, request_one.name, request_one.name+' (Renamed)', request_one.desc+' (Re-described)', library_one.id, fields)
+ request_one.refresh()
+ self.check_page_for_string( request_name+' (Renamed)' )
+ self.check_page_for_string( request_desc+' (Re-described)' )
+ # submit the request
+ self.submit_request( request_one.id, request_one.name )
+ request_one.refresh()
+ # check if the request's state is now set to 'submitted'
+ assert request_one.state is not request_one.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" % ( request_one.name, request_one.states.SUBMITTED )
+ def test_035_request_lifecycle( self ):
+ """Testing request lifecycle as it goes through all the states"""
+ # goto admin manage requests page
+ self.logout()
+ self.login( email='test(a)bx.psu.edu' )
+ self.home()
+ self.visit_page( 'requests_admin/list' )
+ self.check_page_for_string( request_one.name )
+ self.visit_url( "%s/requests_admin/list?sort=-create_time&operation=show_request&id=%s" \
+ % ( self.url, self.security.encode_id( request_one.id ) ))
+ self.check_page_for_string( 'Sequencing Request "%s"' % request_one.name )
+ # set bar codes for the samples
+ bar_codes = [ '1234567890', '0987654321' ]
+ self.add_bar_codes( request_one.id, request_one.name, bar_codes )
+ self.check_page_for_string( 'Bar codes has been saved for this request' )
+ # change the states of all the samples of this request
+ for sample in request_one.samples:
+ self.change_sample_state( sample.name, sample.id, request_type.states[1].id )
+ self.check_page_for_string( request_type.states[1].name )
+ self.check_page_for_string( request_type.states[1].desc )
+ self.change_sample_state( sample.name, sample.id, request_type.states[2].id )
+ self.check_page_for_string( request_type.states[2].name )
+ self.check_page_for_string( request_type.states[2].desc )
+ self.home()
+ request_one.refresh()
+ # check if the request's state is now set to 'complete'
+ assert request_one.state is not request_one.states.COMPLETE, "The state of the request '%s' should be set to '%s'" % ( request_one.name, request_one.states.COMPLETE )
+# def test_40_admin_create_request_on_behalf_of_regular_user( self ):
+# """Testing creating and submitting a request as an admin on behalf of a regular user"""
+# self.logout()
+# self.login( email='test(a)bx.psu.edu' )
+## permissions_in = [ k for k, v in galaxy.model.Library.permitted_actions.items() ]
+## permissions_out = []
+## self.set_library_permissions( str( library_one.id ), library_one.name, str( admin_user_private_role.id ), permissions_in, permissions_out )
+# # set field values
+# fields = ['field one value', 'field two value', str(user_address.id)]
+# # create the request
+# request_name, request_desc = 'Request Two', 'Request Two Description'
+# self.create_request_admin(request_type.id, regular_user1.id, request_name, request_desc, library_one.id, fields)
+# self.check_page_for_string( request_name )
+# self.check_page_for_string( request_desc )
+# global request_two
+# request_one = galaxy.model.Request.filter( and_( galaxy.model.Request.table.c.name==request_name,
+# galaxy.model.Request.table.c.deleted==False ) ).first()
+# # check if the request's state is now set to 'unsubmitted'
+# assert request_two.state is not request_two.states.UNSUBMITTED, "The state of the request '%s' should be set to '%s'" % ( request_two.name, request_two.states.UNSUBMITTED )
+# # sample fields
+# samples = [ ( 'Sample One', [ 'S1 Field 0 Value' ] ),
+# ( 'Sample Two', [ 'S2 Field 0 Value' ] ) ]
+# # add samples to this request
+# self.add_samples( request_two.id, request_two.name, samples )
+# for sample_name, fields in samples:
+# self.check_page_for_string( sample_name )
+# self.check_page_for_string( 'Unsubmitted' )
+# for field_value in fields:
+# self.check_page_for_string( field_value )
+# # submit the request
+# self.submit_request( request_two.id, request_two.name )
+# request_two.refresh()
+# # check if the request's state is now set to 'submitted'
+# assert request_two.state is not request_two.states.SUBMITTED, "The state of the request '%s' should be set to '%s'" % ( request_two.name, request_two.states.SUBMITTED )
+
+
+
+
+
+
+
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 test/functional/test_security_and_libraries.py
--- a/test/functional/test_security_and_libraries.py Sat Aug 22 23:18:47 2009 -0400
+++ b/test/functional/test_security_and_libraries.py Sat Aug 22 23:20:09 2009 -0400
@@ -196,7 +196,7 @@
dhps.sort()
# Compare DefaultHistoryPermissions and actions_in - should be the same
if dhps != actions_in:
- raise AssertionError( 'DefaultHistoryPermissions "%s" for history id %d differ from actions "%s" passed for changing' \
+ raise AssertionError( 'DefaultHistoryPermissions "%s" for history id %d differ from actions "%s" passed for changing' \
% ( str( dhps ), latest_history.id, str( actions_in ) ) )
# Make sure DatasetPermissionss are correct
if len( latest_dataset.actions ) != len( latest_history.default_permissions ):
@@ -209,7 +209,7 @@
dps.sort()
# Compare DatasetPermissionss and DefaultHistoryPermissions - should be the same
if dps != dhps:
- raise AssertionError( 'DatasetPermissionss "%s" for dataset id %d differ from DefaultHistoryPermissions "%s"' \
+ raise AssertionError( 'DatasetPermissionss "%s" for dataset id %d differ from DefaultHistoryPermissions "%s"' \
% ( str( dps ), latest_dataset.id, str( dhps ) ) )
self.logout()
def test_020_create_new_user_account_as_admin( self ):
diff -r fbad627b45ac -r e6dda627e6b0 tools/extract/liftOver_wrapper_code.py
--- a/tools/extract/liftOver_wrapper_code.py Sat Aug 22 23:18:47 2009 -0400
+++ b/tools/extract/liftOver_wrapper_code.py Sat Aug 22 23:20:09 2009 -0400
@@ -3,7 +3,6 @@
to_dbkey = param_dict['to_dbkey'].split('.')[0].split('To')[1]
to_dbkey = to_dbkey[0].lower()+to_dbkey[1:]
out_data['out_file1'].set_dbkey(to_dbkey)
- out_data['out_file2'].set_dbkey(to_dbkey)
out_data['out_file1'].name = out_data['out_file1'].name + " [ MAPPED COORDINATES ]"
out_data['out_file2'].name = out_data['out_file2'].name + " [ UNMAPPED COORDINATES ]"
diff -r fbad627b45ac -r e6dda627e6b0 tools/metag_tools/split_paired_reads.py
--- a/tools/metag_tools/split_paired_reads.py Sat Aug 22 23:18:47 2009 -0400
+++ b/tools/metag_tools/split_paired_reads.py Sat Aug 22 23:20:09 2009 -0400
@@ -1,7 +1,7 @@
#! /usr/bin/python
"""
-Split Solexa paired end reads
+Split fixed length paired end reads
"""
import os, sys
@@ -12,9 +12,13 @@
outfile_end1 = open(sys.argv[2], 'w')
outfile_end2 = open(sys.argv[3], 'w')
- for i, line in enumerate(file(infile)):
+ i = 0
+
+ for line in file( infile ):
line = line.rstrip()
- if not line or line.startswith('#'): continue
+
+ if not line:
+ continue
end1 = ''
end2 = ''
@@ -42,5 +46,9 @@
outfile_end1.write('%s\n' %(end1))
outfile_end2.write('%s\n' %(end2))
+ i += 1
+
+ if i % 4 != 0 :
+ sys.stderr.write("WARNING: Number of lines in the input file was not divisible by 4.\nCheck consistency of the input fastq file.\n")
outfile_end1.close()
outfile_end2.close()
\ No newline at end of file
diff -r fbad627b45ac -r e6dda627e6b0 tools/solid_tools/maq_cs_wrapper.py
--- a/tools/solid_tools/maq_cs_wrapper.py Sat Aug 22 23:18:47 2009 -0400
+++ b/tools/solid_tools/maq_cs_wrapper.py Sat Aug 22 23:20:09 2009 -0400
@@ -48,9 +48,9 @@
cmd1 = "solid2fastq_modified.pl 'yes' %s %s %s %s %s %s %s 2>&1" %(tmpf.name,tmpr.name,tmps.name,f3_read_fname,f3_qual_fname,r3_read_fname,r3_qual_fname)
try:
os.system(cmd1)
- os.system('zcat -f %s >> %s' %(tmpf.name,tmpffastq.name))
- os.system('zcat -f %s >> %s' %(tmpr.name,tmprfastq.name))
- os.system('zcat -f %s >> %s' %(tmps.name,tmpsfastq.name))
+ os.system('gunzip -c %s >> %s' %(tmpf.name,tmpffastq.name))
+ os.system('gunzip -c %s >> %s' %(tmpr.name,tmprfastq.name))
+ os.system('gunzip -c %s >> %s' %(tmps.name,tmpsfastq.name))
except Exception, eq:
stop_err("Error converting data to fastq format." + str(eq))
@@ -135,7 +135,7 @@
cmd1 = "solid2fastq_modified.pl 'no' %s %s %s %s %s %s %s 2>&1" %(tmpf.name,None,None,f3_read_fname,f3_qual_fname,None,None)
try:
os.system(cmd1)
- os.system('zcat -f %s >> %s' %(tmpf.name,tmpfastq.name))
+ os.system('gunzip -c %s >> %s' %(tmpf.name,tmpfastq.name))
tmpf.close()
except:
stop_err("Error converting data to fastq format.")
1
0
details: http://www.bx.psu.edu/hg/galaxy/rev/19b86ccccf6f
changeset: 2608:19b86ccccf6f
user: James Taylor <james(a)jamestaylor.org>
date: Sun Aug 23 12:26:46 2009 -0400
description:
[mq]: pages
5 file(s) affected in this change:
lib/galaxy/model/__init__.py
lib/galaxy/model/mapping.py
lib/galaxy/model/migrate/versions/0014_pages.py
lib/galaxy/web/controllers/user.py
templates/user/index.mako
diffs (220 lines):
diff -r 77b761ff6cd3 -r 19b86ccccf6f lib/galaxy/model/__init__.py
--- a/lib/galaxy/model/__init__.py Sun Aug 23 09:22:19 2009 -0400
+++ b/lib/galaxy/model/__init__.py Sun Aug 23 12:26:46 2009 -0400
@@ -33,6 +33,7 @@
self.external = False
self.deleted = False
self.purged = False
+ self.username = None
# Relationships
self.histories = []
@@ -1118,7 +1119,20 @@
self.country+'<br/>'+ \
'Phone: '+self.phone
+class Page( object ):
+ def __init__( self ):
+ self.id = None
+ self.user = None
+ self.title = None
+ self.slug = None
+ self.latest_revision_id = None
+ self.revisions = []
+class PageRevision( object ):
+ def __init__( self ):
+ self.user = None
+ self.title = None
+ self.content = None
diff -r 77b761ff6cd3 -r 19b86ccccf6f lib/galaxy/model/mapping.py
--- a/lib/galaxy/model/mapping.py Sun Aug 23 09:22:19 2009 -0400
+++ b/lib/galaxy/model/mapping.py Sun Aug 23 12:26:46 2009 -0400
@@ -42,6 +42,7 @@
Column( "create_time", DateTime, default=now ),
Column( "update_time", DateTime, default=now, onupdate=now ),
Column( "email", TrimmedString( 255 ), nullable=False ),
+ Column( "username", TrimmedString( 255 ), index=True, unique=True ),
Column( "password", TrimmedString( 40 ), nullable=False ),
Column( "external", Boolean, default=False ),
Column( "deleted", Boolean, index=True, default=False ),
@@ -523,6 +524,26 @@
Column( "sample_state_id", Integer, ForeignKey( "sample_state.id" ), index=True ),
Column( "comment", TEXT ) )
+Page.table = Table( "page", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
+ Column( "latest_revision_id", Integer,
+ ForeignKey( "page_revision.id", use_alter=True, name='page_latest_revision_id_fk' ), index=True ),
+ Column( "title", TEXT ),
+ Column( "slug", TEXT, unique=True, index=True ),
+ )
+
+PageRevision.table = Table( "page_revision", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "page_id", Integer, ForeignKey( "page.id" ), index=True, nullable=False ),
+ Column( "title", TEXT ),
+ Column( "content", TEXT )
+ )
+
# With the tables defined we can define the mappers and setup the
# relationships between the model objects.
@@ -905,6 +926,18 @@
assign_mapper( context, MetadataFile, MetadataFile.table,
properties=dict( history_dataset=relation( HistoryDatasetAssociation ), library_dataset=relation( LibraryDatasetDatasetAssociation ) ) )
+assign_mapper( context, PageRevision, PageRevision.table )
+
+assign_mapper( context, Page, Page.table,
+ properties=dict( user=relation( User ),
+ revisions=relation( PageRevision, backref='page',
+ cascade="all, delete-orphan",
+ primaryjoin=( Page.table.c.id == PageRevision.table.c.page_id ) ),
+ latest_revision=relation( PageRevision, post_update=True,
+ primaryjoin=( Page.table.c.latest_revision_id == PageRevision.table.c.id ),
+ lazy=False )
+ ) )
+
def db_next_hid( self ):
"""
Override __next_hid to generate from the database in a concurrency
diff -r 77b761ff6cd3 -r 19b86ccccf6f lib/galaxy/model/migrate/versions/0014_pages.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/galaxy/model/migrate/versions/0014_pages.py Sun Aug 23 12:26:46 2009 -0400
@@ -0,0 +1,56 @@
+from sqlalchemy import *
+from migrate import *
+from migrate.changeset import *
+
+import datetime
+now = datetime.datetime.utcnow
+
+import logging
+log = logging.getLogger( __name__ )
+
+metadata = MetaData( migrate_engine )
+
+Page_table = Table( "page", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True, nullable=False ),
+ Column( "latest_revision_id", Integer,
+ ForeignKey( "page_revision.id", use_alter=True, name='page_latest_revision_id_fk' ), index=True ),
+ Column( "title", TEXT ),
+ Column( "slug", TEXT, unique=True, index=True ),
+ )
+
+PageRevision_table = Table( "page_revision", metadata,
+ Column( "id", Integer, primary_key=True ),
+ Column( "create_time", DateTime, default=now ),
+ Column( "update_time", DateTime, default=now, onupdate=now ),
+ Column( "page_id", Integer, ForeignKey( "page.id" ), index=True, nullable=False ),
+ Column( "title", TEXT ),
+ Column( "content", TEXT )
+ )
+
+def upgrade():
+ metadata.reflect()
+ try:
+ Page_table.create()
+ except:
+ log.debug( "Could not create page table" )
+ try:
+ PageRevision_table.create()
+ except:
+ log.debug( "Could not create page_revision table" )
+
+ # Add 1 column to the user table
+ User_table = Table( "galaxy_user", metadata, autoload=True )
+ col = Column( 'username', String(255), index=True, unique=True, default=False )
+ print type( col ), col
+ col.create( User_table )
+ assert col is User_table.c.username
+
+def downgrade():
+ metadata.reflect()
+ Page_table.drop()
+ PageRevision_table.drop()
+ User_table = Table( "galaxy_user", metadata, autoload=True )
+ User_table.c.username.drop()
\ No newline at end of file
diff -r 77b761ff6cd3 -r 19b86ccccf6f lib/galaxy/web/controllers/user.py
--- a/lib/galaxy/web/controllers/user.py Sun Aug 23 09:22:19 2009 -0400
+++ b/lib/galaxy/web/controllers/user.py Sun Aug 23 12:26:46 2009 -0400
@@ -4,7 +4,7 @@
from galaxy.web.base.controller import *
from galaxy.model.orm import *
from galaxy import util
-import logging, os, string
+import logging, os, string, re
from random import choice
log = logging.getLogger( __name__ )
@@ -19,6 +19,8 @@
"""
require_login_nocreation_template = require_login_template % ""
require_login_creation_template = require_login_template % " If you don't already have an account, <a href='%s'>you may create one</a>."
+
+VALID_USERNAME_RE = re.compile( "^[a-zA-Z0-9\-\_]+$" )
class User( BaseController ):
edit_address_id = None
@@ -78,6 +80,37 @@
.add_text( "email", "Email", value=email, error=email_err )
.add_text( "conf_email", "Confirm Email", value='', error=conf_email_err )
.add_password( "password", "Password", value='', error=pass_err ) )
+
+ @web.expose
+ def change_username(self, trans, username='', **kwd):
+ username_err = ''
+ user = trans.get_user()
+ if not user:
+ trans.response.send_redirect( web.url_for( action='login' ) )
+ if trans.request.method == "POST":
+ if len( username ) < 4:
+ username_err = "Username must be at least 4 characters in length"
+ elif len( username ) > 255:
+ username_err = "USername must be at most 255 characters in length"
+ elif not( VALID_USERNAME_RE.match( username ) ):
+ username_err = "Username must contain only letters, numbers, '-', and '_'"
+ elif trans.app.model.User.filter_by( username=username ).first():
+ username_err = "This username is not available"
+ else:
+ user.username = username
+ user.flush()
+ trans.log_event( "User change username" )
+ return trans.show_ok_message( "Username been set to: " + user.username )
+ else:
+ username = user.username or ''
+ return trans.show_form(
+ web.FormBuilder( web.url_for(), "Change username", submit_text="Submit" )
+ .add_text( "username", "Username", value=username, error=username_err,
+ help="""Your username is an optional identifier that
+ will be used to generate adresses for information
+ you share publicly. Usernames must be at least
+ four characters in length and contain only letters,
+ numbers, and the '-' and '_' characters""" ) )
@web.expose
def login( self, trans, email='', password='' ):
diff -r 77b761ff6cd3 -r 19b86ccccf6f templates/user/index.mako
--- a/templates/user/index.mako Sun Aug 23 09:22:19 2009 -0400
+++ b/templates/user/index.mako Sun Aug 23 12:26:46 2009 -0400
@@ -9,6 +9,7 @@
<ul>
<li><a href="${h.url_for( action='change_password' )}">${_('Change your password')}</a></li>
<li><a href="${h.url_for( action='change_email' )}">${_('Update your email address')}</a></li>
+ <li><a href="${h.url_for( action='change_username' )}">${_('Change your public username')}</a></li>
<li><a href="${h.url_for( action='set_default_permissions' )}">${_('Change default permissions')}</a> for new histories</li>
<li><a href="${h.url_for( action='manage_addresses' )}">${_('Manage your addresses')}</a></li>
<li><a href="${h.url_for( action='logout' )}">${_('Logout')}</a></li>
1
0