mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Pages can now be deleted. Pages have a 'published' attribute that determines whether they are publicly accessible. There is now a list of all published pages from all users
This commit is contained in:
@@ -545,6 +545,8 @@ Page.table = Table( "page", metadata,
|
||||
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 ),
|
||||
Column( "published", Boolean, index=True, default=False ),
|
||||
Column( "deleted", Boolean, index=True, default=False ),
|
||||
)
|
||||
|
||||
PageRevision.table = Table( "page_revision", metadata,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
Migration script to add columns for tracking whether pages are deleted and
|
||||
publicly accessible.
|
||||
"""
|
||||
|
||||
from sqlalchemy import *
|
||||
from migrate import *
|
||||
from migrate.changeset import *
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
metadata = MetaData( migrate_engine )
|
||||
|
||||
def upgrade():
|
||||
|
||||
print __doc__
|
||||
metadata.reflect()
|
||||
|
||||
Page_table = Table( "page", metadata, autoload=True )
|
||||
|
||||
c = Column( "published", Boolean, index=True, default=False )
|
||||
c.create( Page_table )
|
||||
assert c is Page_table.c.published
|
||||
|
||||
c = Column( "deleted", Boolean, index=True, default=False )
|
||||
c.create( Page_table )
|
||||
assert c is Page_table.c.deleted
|
||||
|
||||
def downgrade():
|
||||
metadata.reflect()
|
||||
|
||||
Page_table = Table( "page", metadata, autoload=True )
|
||||
Page_table.c.published.drop()
|
||||
Page_table.c.deleted.drop()
|
||||
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Remove unique constraint from page slugs to allow creating a page with
|
||||
the same slug as a deleted page.
|
||||
"""
|
||||
|
||||
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 )
|
||||
|
||||
def upgrade():
|
||||
print __doc__
|
||||
metadata.reflect()
|
||||
|
||||
Page_table = Table( "page", metadata, autoload=True )
|
||||
|
||||
i = Index( "ix_page_slug", Page_table.c.slug )
|
||||
i.drop()
|
||||
|
||||
i = Index( "ix_page_slug", Page_table.c.slug, unique=False )
|
||||
i.create()
|
||||
|
||||
|
||||
def downgrade():
|
||||
metadata.reflect()
|
||||
#Page_table = Table( "page", metadata, autoload=True )
|
||||
#Page_table.c.slug.alter( unique=True )
|
||||
@@ -6,16 +6,27 @@ import re
|
||||
|
||||
VALID_SLUG_RE = re.compile( "^[a-z0-9\-]+$" )
|
||||
|
||||
def format_bool( b ):
|
||||
if b:
|
||||
return "yes"
|
||||
else:
|
||||
return ""
|
||||
|
||||
class PublicURLColumn( 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=item.user.username, slug=item.slug )
|
||||
else:
|
||||
return None
|
||||
|
||||
class OwnerColumn( grids.GridColumn ):
|
||||
def get_value( self, trans, grid, item ):
|
||||
return item.user.username
|
||||
|
||||
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=item.user.username, slug=item.slug )
|
||||
else:
|
||||
return None
|
||||
# Grid definition
|
||||
use_panels = True
|
||||
title = "Your pages"
|
||||
@@ -23,7 +34,8 @@ class PageListGrid( grids.Grid ):
|
||||
default_sort_key = "-create_time"
|
||||
columns = [
|
||||
grids.GridColumn( "Title", key="title", attach_popup=True ),
|
||||
URLColumn( "Public URL" ),
|
||||
PublicURLColumn( "Public URL" ),
|
||||
grids.GridColumn( "Published", key="published", format=format_bool ),
|
||||
grids.GridColumn( "Created", key="create_time", format=time_ago ),
|
||||
grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
|
||||
]
|
||||
@@ -32,25 +44,67 @@ class PageListGrid( grids.Grid ):
|
||||
]
|
||||
operations = [
|
||||
grids.GridOperation( "View", allow_multiple=False, url_args=dict( action='display') ),
|
||||
grids.GridOperation( "Edit", allow_multiple=False, url_args=dict( action='edit') )
|
||||
grids.GridOperation( "Edit name/id", allow_multiple=False, url_args=dict( action='edit') ),
|
||||
grids.GridOperation( "Edit content", allow_multiple=False, url_args=dict( action='edit_content') ),
|
||||
grids.GridOperation( "Delete" ),
|
||||
grids.GridOperation( "Publish", condition=( lambda item: not item.published ) ),
|
||||
grids.GridOperation( "Unpublish", condition=( lambda item: item.published ) ),
|
||||
]
|
||||
def apply_default_filter( self, trans, query, **kwargs ):
|
||||
return query.filter_by( user=trans.user )
|
||||
return query.filter_by( user=trans.user, deleted=False )
|
||||
|
||||
class PageAllPublishedGrid( grids.Grid ):
|
||||
# Grid definition
|
||||
use_panels = True
|
||||
title = "Published pages from all users"
|
||||
model_class = model.Page
|
||||
default_sort_key = "-create_time"
|
||||
columns = [
|
||||
grids.GridColumn( "Title", key="title" ),
|
||||
PublicURLColumn( "Public URL" ),
|
||||
OwnerColumn( "Published by" ),
|
||||
grids.GridColumn( "Created", key="create_time", format=time_ago ),
|
||||
grids.GridColumn( "Last Updated", key="update_time", format=time_ago ),
|
||||
]
|
||||
def apply_default_filter( self, trans, query, **kwargs ):
|
||||
return query.filter_by( deleted=False, published=True )
|
||||
|
||||
class PageController( BaseController ):
|
||||
|
||||
list = PageListGrid()
|
||||
_page_list = PageListGrid()
|
||||
_all_published_list = PageAllPublishedGrid()
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
@web.require_login()
|
||||
def index( self, trans, *args, **kwargs ):
|
||||
# Handle operation
|
||||
if 'operation' in kwargs and 'id' in kwargs:
|
||||
session = trans.sa_session
|
||||
operation = kwargs['operation']
|
||||
ids = util.listify( kwargs['id'] )
|
||||
for id in ids:
|
||||
item = session.query( model.Page ).get( trans.security.decode_id( id ) )
|
||||
if operation == "Delete":
|
||||
item.deleted = True
|
||||
elif operation == "Publish":
|
||||
item.published = True
|
||||
elif operation == "Unpublish":
|
||||
item.published = False
|
||||
session.flush()
|
||||
# Build grid
|
||||
grid = self.list( trans, *args, **kwargs )
|
||||
grid = self._page_list( trans, *args, **kwargs )
|
||||
# Render grid wrapped in panels
|
||||
return trans.fill_template( "page/index.mako", grid=grid )
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
@web.require_login()
|
||||
def list_published( self, trans, *args, **kwargs ):
|
||||
grid = self._all_published_list( trans, *args, **kwargs )
|
||||
# Render grid wrapped in panels
|
||||
return trans.fill_template( "page/index.mako", grid=grid )
|
||||
|
||||
|
||||
@web.expose
|
||||
@web.require_login( "create pages" )
|
||||
def create( self, trans, page_title="", page_slug="" ):
|
||||
"""
|
||||
@@ -65,7 +119,7 @@ class PageController( BaseController ):
|
||||
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 trans.sa_session.query( model.Page ).filter_by( user=user, slug=page_slug ).first():
|
||||
elif trans.sa_session.query( model.Page ).filter_by( user=user, slug=page_slug, deleted=False ).first():
|
||||
page_slug_err = "Page id must be unique"
|
||||
else:
|
||||
# Create the new stored workflow
|
||||
@@ -98,9 +152,50 @@ class PageController( BaseController ):
|
||||
template="page/create.mako" )
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
@web.require_login( "create pages" )
|
||||
def edit( self, trans, id, page_title="", page_slug="" ):
|
||||
"""
|
||||
Create a new page
|
||||
"""
|
||||
encoded_id = id
|
||||
id = trans.security.decode_id( id )
|
||||
session = trans.sa_session
|
||||
page = session.query( model.Page ).get( id )
|
||||
user = trans.user
|
||||
assert page.user == 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 page_slug == page.slug or trans.sa_session.query( model.Page ).filter_by( user=user, slug=page_slug, deleted=False ).first():
|
||||
page_slug_err = "Page id must be unique"
|
||||
else:
|
||||
page.title = page_title
|
||||
page.slug = page_slug
|
||||
session.flush()
|
||||
# Display the management page
|
||||
return trans.response.send_redirect( web.url_for( action='index' ) )
|
||||
else:
|
||||
page_title = page.title
|
||||
page_slug = page.slug
|
||||
return trans.show_form(
|
||||
web.FormBuilder( web.url_for( id=encoded_id ), "Edit page attributes", 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_login( "edit pages" )
|
||||
def edit( self, trans, id ):
|
||||
def edit_content( self, trans, id ):
|
||||
"""
|
||||
Render the main page editor interface.
|
||||
"""
|
||||
@@ -110,7 +205,7 @@ class PageController( BaseController ):
|
||||
return trans.fill_template( "page/editor.mako", page=page )
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
@web.require_login()
|
||||
def save( self, trans, id, content ):
|
||||
id = trans.security.decode_id( id )
|
||||
page = trans.sa_session.query( model.Page ).get( id )
|
||||
@@ -126,10 +221,12 @@ class PageController( BaseController ):
|
||||
trans.sa_session.flush()
|
||||
|
||||
@web.expose
|
||||
@web.require_admin
|
||||
@web.require_login()
|
||||
def display( self, trans, id ):
|
||||
id = trans.security.decode_id( id )
|
||||
page = trans.sa_session.query( model.Page ).get( id )
|
||||
if page.user is not trans.user:
|
||||
error( "Page is not owned by current user" )
|
||||
return trans.fill_template( "page/display.mako", page=page )
|
||||
|
||||
@web.expose
|
||||
@@ -138,7 +235,7 @@ class PageController( BaseController ):
|
||||
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()
|
||||
page = trans.sa_session.query( model.Page ).filter_by( user=user, slug=slug, deleted=False, published=True ).first()
|
||||
if page is None:
|
||||
raise web.httpexceptions.HTTPNotFound()
|
||||
return trans.fill_template( "page/display.mako", page=page )
|
||||
|
||||
@@ -279,7 +279,7 @@ class GridOperation( object ):
|
||||
temp['id'] = item.id
|
||||
return temp
|
||||
else:
|
||||
return dict( operation=operation.label, id=item.id )
|
||||
return dict( operation=self.label, id=item.id )
|
||||
|
||||
def allowed( self, item ):
|
||||
if self.condition:
|
||||
|
||||
@@ -243,6 +243,9 @@
|
||||
<li><hr style="color: inherit; background-color: gray"/></li>
|
||||
<li><a target="galaxy_main" href="${h.url_for( controller='history', action='list' )}">Histories</a></li>
|
||||
<li><a target="galaxy_main" href="${h.url_for( controller='dataset', action='list' )}">Datasets</a></li>
|
||||
%if app.config.get_bool( 'enable_pages', False ):
|
||||
<li><a href="${h.url_for( controller='page' )}">Pages</a></li>
|
||||
%endif
|
||||
%endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user