1 new changeset in galaxy-central: http://bitbucket.org/galaxy/galaxy-central/changeset/c996393163e1/ changeset: c996393163e1 user: greg date: 2011-06-23 21:32:07 summary: Add the ability to select Galaxy tool shed repositories to receive email alerts when they are updated. Also remove a broken link on the Galaxy tool shed Help menu. affected #: 12 files (10.3 KB) --- a/lib/galaxy/webapps/community/config.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/config.py Thu Jun 23 15:32:07 2011 -0400 @@ -54,6 +54,9 @@ self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-user-join@bx.psu.edu") self.error_email_to = kwargs.get( 'error_email_to', None ) self.smtp_server = kwargs.get( 'smtp_server', None ) + self.smtp_username = kwargs.get( 'smtp_username', None ) + self.smtp_password = kwargs.get( 'smtp_password', None ) + self.email_alerts_from = kwargs.get( 'email_alerts_from', None ) self.log_actions = string_as_bool( kwargs.get( 'log_actions', 'False' ) ) self.brand = kwargs.get( 'brand', None ) self.wiki_url = kwargs.get( 'wiki_url', 'http://bitbucket.org/galaxy/galaxy-central/wiki/Home' ) --- a/lib/galaxy/webapps/community/controllers/common.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/controllers/common.py Thu Jun 23 15:32:07 2011 -0400 @@ -1,4 +1,7 @@ -import os, tarfile, tempfile, shutil +import os, tarfile, tempfile, shutil, string, socket +from time import strftime +from datetime import * +from galaxy.util.json import from_json_string, to_json_string from galaxy.web.base.controller import * from galaxy.webapps.community import model from galaxy.model.orm import * @@ -9,6 +12,25 @@ import logging log = logging.getLogger( __name__ ) +email_alert_template = """ +GALAXY TOOL SHED REPOSITORY UPDATE ALERT +----------------------------------------------------------------------------- +You received this alert because you registered to receive email whenever +changes were made to the repository named "${repository_name}". +----------------------------------------------------------------------------- + +Date of change: ${display_date} +Changed by: ${username} + +Revision: ${revision} +Change description: +${description} + +----------------------------------------------------------------------------- +This change alert was sent from the Galaxy tool shed hosted on the server +"${host}" +""" + # States for passing messages SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error" @@ -95,6 +117,42 @@ os.chdir( cloned_repo_dir ) os.system( cmd ) os.chdir( current_working_dir ) + smtp_server = trans.app.config.smtp_server + if smtp_server and repository.email_alerts: + # Send email alert to users that want them. + if trans.app.config.email_alerts_from is not None: + email_alerts_from = trans.app.config.email_alerts_from + elif trans.request.host.split( ':' )[0] == 'localhost': + email_alerts_from = 'galaxy-no-reply@' + socket.getfqdn() + else: + email_alerts_from = 'galaxy-no-reply@' + trans.request.host.split( ':' )[0] + tip_changeset = repo.changelog.tip() + ctx = repo.changectx( tip_changeset ) + t, tz = ctx.date() + date = datetime( *time.gmtime( float( t ) - tz )[:6] ) + display_date = date.strftime( "%Y-%m-%d" ) + try: + username = ctx.user().split()[0] + except: + username = ctx.user() + # Build the email message + body = string.Template( email_alert_template ) \ + .safe_substitute( host=trans.request.host, + repository_name=repository.name, + revision='%s:%s' %( str( ctx.rev() ), ctx ), + display_date=display_date, + description=ctx.description(), + username=username ) + frm = email_alerts_from + subject = "Galaxy tool shed repository update alert" + email_alerts = from_json_string( repository.email_alerts ) + for email in email_alerts: + to = email.strip() + # Send it + try: + util.send_mail( frm, to, subject, body, trans.app.config ) + except Exception, e: + log.exception( "An error occurred sending a tool shed repository update alert by email." ) def hg_remove( file_path, current_working_dir, cloned_repo_dir ): # Remove a file path from a cloned repository. Since mercurial doesn't track # directories (only files), directories are automatically removed when they --- a/lib/galaxy/webapps/community/controllers/repository.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/controllers/repository.py Thu Jun 23 15:32:07 2011 -0400 @@ -5,9 +5,11 @@ from galaxy import util from galaxy.datatypes.checkers import * from galaxy.web.base.controller import * +from galaxy.web.form_builder import CheckboxField from galaxy.webapps.community import model from galaxy.webapps.community.model import directory_hash_id from galaxy.web.framework.helpers import time_ago, iff, grids +from galaxy.util.json import from_json_string, to_json_string from galaxy.model.orm import * from common import * from mercurial import hg, ui, patch @@ -113,6 +115,11 @@ return query return query.filter( and_( model.Repository.table.c.user_id == model.User.table.c.id, model.User.table.c.email == column_filter ) ) + class EmailAlertsColumn( grids.TextColumn ): + def get_value( self, trans, grid, repository ): + if trans.user.email in from_json_string( repository.email_alerts ): + return 'yes' + return '' # Grid definition title = "Repositories" model_class = model.Repository @@ -139,6 +146,8 @@ key="username" ), grids.CommunityRatingColumn( "Average Rating", key="rating" ), + EmailAlertsColumn( "Alert", + attach_popup=False ), # Columns that are valid for filtering but are not visible. EmailColumn( "Email", model_class=model.User, @@ -154,7 +163,10 @@ key="free-text-search", visible=False, filterable="standard" ) ) - operations = [] + operations = [ grids.GridOperation( "Receive email alerts", + allow_multiple=True, + condition=( lambda item: not item.deleted ), + async_compatible=False ) ] standard_filters = [] default_filter = {} num_rows_per_page = 50 @@ -248,6 +260,10 @@ category_id = kwd.get( 'id', None ) category = get_category( trans, category_id ) kwd[ 'f-Category.name' ] = category.name + elif operation == "receive email alerts": + return trans.response.send_redirect( web.url_for( controller='repository', + action='set_email_alerts', + **kwd ) ) # Render the list view return self.repository_list_grid( trans, **kwd ) @web.expose @@ -485,6 +501,30 @@ tip = get_repository_tip( repo ) avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model ) display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) ) + alerts = params.get( 'alerts', '' ) + alerts_checked = CheckboxField.is_checked( alerts ) + if repository.email_alerts: + email_alerts = from_json_string( repository.email_alerts ) + else: + email_alerts = [] + user = trans.user + if params.get( 'receive_email_alerts_button', False ): + flush_needed = False + if alerts_checked: + if user.email not in email_alerts: + email_alerts.append( user.email ) + repository.email_alerts = to_json_string( email_alerts ) + flush_needed = True + else: + if user.email in email_alerts: + email_alerts.remove( user.email ) + repository.email_alerts = to_json_string( email_alerts ) + flush_needed = True + if flush_needed: + trans.sa_session.add( repository ) + trans.sa_session.flush() + checked = alerts_checked or user.email in email_alerts + alerts_check_box = CheckboxField( 'alerts', checked=checked ) return trans.fill_template( '/webapps/community/repository/view_repository.mako', repo=repo, repository=repository, @@ -492,6 +532,7 @@ avg_rating=avg_rating, display_reviews=display_reviews, num_ratings=num_ratings, + alerts_check_box=alerts_check_box, message=message, status=status ) @web.expose @@ -507,11 +548,18 @@ description = util.restore_text( params.get( 'description', repository.description ) ) avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, repository, webapp_model=trans.model ) display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) ) + alerts = params.get( 'alerts', '' ) + alerts_checked = CheckboxField.is_checked( alerts ) + if repository.email_alerts: + email_alerts = from_json_string( repository.email_alerts ) + else: + email_alerts = [] allow_push = params.get( 'allow_push', '' ) error = False + user = trans.user if params.get( 'edit_repository_button', False ): flush_needed = False - if trans.user != repository.user: + if user != repository.user: message = "You are not the owner of this repository, so you cannot manage it." status = error return trans.response.send_redirect( web.url_for( controller='repository', @@ -520,7 +568,7 @@ message=message, status=status ) ) if repo_name != repository.name: - message = self.__validate_repository_name( repo_name, trans.user ) + message = self.__validate_repository_name( repo_name, user ) if message: error = True else: @@ -545,10 +593,27 @@ usernames.append( user.username ) usernames = ','.join( usernames ) self.__set_allow_push( repository, usernames, remove_auth=remove_auth ) + elif params.get( 'receive_email_alerts_button', False ): + flush_needed = False + if alerts_checked: + if user.email not in email_alerts: + email_alerts.append( user.email ) + repository.email_alerts = to_json_string( email_alerts ) + flush_needed = True + else: + if user.email in email_alerts: + email_alerts.remove( user.email ) + repository.email_alerts = to_json_string( email_alerts ) + flush_needed = True + if flush_needed: + trans.sa_session.add( repository ) + trans.sa_session.flush() if error: status = 'error' current_allow_push_list = self.__get_allow_push( repository ).split( ',' ) allow_push_select_field = self.__build_allow_push_select_field( trans, current_allow_push_list ) + checked = alerts_checked or user.email in email_alerts + alerts_check_box = CheckboxField( 'alerts', checked=checked ) return trans.fill_template( '/webapps/community/repository/manage_repository.mako', repo_name=repo_name, description=description, @@ -560,6 +625,7 @@ avg_rating=avg_rating, display_reviews=display_reviews, num_ratings=num_ratings, + alerts_check_box=alerts_check_box, message=message, status=status ) @web.expose @@ -666,6 +732,42 @@ message=message, status=status ) @web.expose + def set_email_alerts( self, trans, **kwd ): + # Set email alerts for selected repositories + params = util.Params( kwd ) + user = trans.user + repository_ids = util.listify( kwd.get( 'id', '' ) ) + total_alerts_added = 0 + total_alerts_removed = 0 + for repository_id in repository_ids: + flush_needed = False + repository = get_repository( trans, repository_id ) + if repository.email_alerts: + email_alerts = from_json_string( repository.email_alerts ) + else: + email_alerts = [] + if user.email in email_alerts: + email_alerts.remove( user.email ) + repository.email_alerts = to_json_string( email_alerts ) + trans.sa_session.add( repository ) + flush_needed = True + total_alerts_removed += 1 + else: + email_alerts.append( user.email ) + repository.email_alerts = to_json_string( email_alerts ) + trans.sa_session.add( repository ) + flush_needed = True + total_alerts_added += 1 + if flush_needed: + trans.sa_session.flush() + message = 'Total alerts added: %d, total alerts removed: %d' % ( total_alerts_added, total_alerts_removed ) + kwd[ 'message' ] = message + kwd[ 'status' ] = 'done' + del( kwd[ 'operation' ] ) + return trans.response.send_redirect( web.url_for( controller='repository', + action='browse_repositories', + **kwd ) ) + @web.expose def download( self, trans, repository_id, file_type, **kwd ): # Download an archive of the repository files compressed as zip, gz or bz2. params = util.Params( kwd ) --- a/lib/galaxy/webapps/community/model/__init__.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/model/__init__.py Thu Jun 23 15:32:07 2011 -0400 @@ -19,8 +19,6 @@ self.deleted = False self.purged = False self.username = None - # Relationships - self.tools = [] def set_password_cleartext( self, cleartext ): """Set 'self.password' to the digest of 'cleartext'.""" self.password = new_secure_hash( text_type=cleartext ) @@ -90,11 +88,12 @@ MARKED_FOR_REMOVAL = 'r', MARKED_FOR_ADDITION = 'a', NOT_TRACKED = '?' ) - def __init__( self, name=None, description=None, user_id=None, private=False ): + def __init__( self, name=None, description=None, user_id=None, private=False, email_alerts=None ): self.name = name or "Unnamed repository" self.description = description self.user_id = user_id self.private = private + self.email_alerts = email_alerts @property def repo_path( self ): # Repository locations on disk are defined in the hgweb.config file --- a/lib/galaxy/webapps/community/model/mapping.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/model/mapping.py Thu Jun 23 15:32:07 2011 -0400 @@ -106,7 +106,8 @@ Column( "description" , TEXT ), Column( "user_id", Integer, ForeignKey( "galaxy_user.id" ), index=True ), Column( "private", Boolean, default=False ), - Column( "deleted", Boolean, index=True, default=False ) ) + Column( "deleted", Boolean, index=True, default=False ), + Column( "email_alerts", JSONType, nullable=True ) ) RepositoryRatingAssociation.table = Table( "repository_rating_association", metadata, Column( "id", Integer, primary_key=True ), --- a/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/model/migrate/versions/0001_initial_tables.py Thu Jun 23 15:32:07 2011 -0400 @@ -11,8 +11,14 @@ # Need our custom types, but don't import anything else from model from galaxy.model.custom_types import * -import logging +import sys, logging log = logging.getLogger( __name__ ) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) metadata = MetaData( migrate_engine ) --- a/lib/galaxy/webapps/community/model/migrate/versions/0002_add_tool_suite_column.py Thu Jun 23 11:03:45 2011 -0400 +++ b/lib/galaxy/webapps/community/model/migrate/versions/0002_add_tool_suite_column.py Thu Jun 23 15:32:07 2011 -0400 @@ -7,8 +7,14 @@ from migrate import * from migrate.changeset import * -import logging +import sys, logging log = logging.getLogger( __name__ ) +log.setLevel(logging.DEBUG) +handler = logging.StreamHandler( sys.stdout ) +format = "%(name)s %(levelname)s %(asctime)s %(message)s" +formatter = logging.Formatter( format ) +handler.setFormatter( formatter ) +log.addHandler( handler ) metadata = MetaData( migrate_engine ) db_session = scoped_session( sessionmaker( bind=migrate_engine, autoflush=False, autocommit=True ) ) @@ -16,7 +22,6 @@ def upgrade(): print __doc__ metadata.reflect() - # Create and initialize imported column in job table. Tool_table = Table( "tool", metadata, autoload=True ) c = Column( "suite", Boolean, default=False, index=True ) @@ -24,22 +29,19 @@ # Create c.create( Tool_table ) assert c is Tool_table.c.suite - # Initialize. if migrate_engine.name == 'mysql' or migrate_engine.name == 'sqlite': default_false = "0" elif migrate_engine.name == 'postgres': default_false = "false" db_session.execute( "UPDATE tool SET suite=%s" % default_false ) - except Exception, e: print "Adding suite column to the tool table failed: %s" % str( e ) log.debug( "Adding suite column to the tool table failed: %s" % str( e ) ) def downgrade(): metadata.reflect() - - # Drop imported column from job table. + # Drop suite column from tool table. Tool_table = Table( "tool", metadata, autoload=True ) try: Tool_table.c.suite.drop() --- a/templates/display_common.mako Thu Jun 23 11:03:45 2011 -0400 +++ b/templates/display_common.mako Thu Jun 23 15:32:07 2011 -0400 @@ -2,6 +2,8 @@ ## Utilities for sharing items and displaying shared items. ## HACK: these should probably go in the web helper object. ## +## TODO: FIXME Cannot import model here, because grids are +## used across webapps, and each webapp has it's own model. <%! from galaxy import model %> @@ -88,7 +90,7 @@ elif a_class == model.UserOpenID: class_plural = "OpenIDs" else: - class_plural = a_class.__name__ + "s" + class_plural = "items" return class_plural %></%def> --- a/templates/webapps/community/base_panels.mako Thu Jun 23 11:03:45 2011 -0400 +++ b/templates/webapps/community/base_panels.mako Thu Jun 23 15:32:07 2011 -0400 @@ -33,7 +33,6 @@ <a>Help</a><div class="submenu"><ul> - <li><a target="galaxy_main" href="${h.url_for( controller='tool', action='help' )}">How to upload, download and install tools</a></li><li><a href="${app.config.get( "bugs_email", "mailto:galaxy-bugs@bx.psu.edu" )}">Email comments, bug reports, or suggestions</a></li><li><a target="_blank" href="${app.config.get( "wiki_url", "http://bitbucket.org/galaxy/galaxy-central/wiki" )}">Galaxy Wiki</a></li><li><a target="_blank" href="${app.config.get( "screencasts_url", "http://galaxycast.org" )}">Video tutorials (screencasts)</a></li> --- a/templates/webapps/community/repository/manage_repository.mako Thu Jun 23 11:03:45 2011 -0400 +++ b/templates/webapps/community/repository/manage_repository.mako Thu Jun 23 15:32:07 2011 -0400 @@ -137,6 +137,26 @@ </form></div></div> +%if trans.app.config.smtp_server: + <p/> + <div class="toolForm"> + <div class="toolFormTitle">${repository.name}</div> + <div class="toolFormBody"> + <form name="receive_email_alerts" id="receive_email_alerts" action="${h.url_for( controller='repository', action='manage_repository', id=trans.security.encode_id( repository.id ) )}" method="post" > + <div class="form-row"> + <label>Receive email alerts:</label> + ${alerts_check_box.get_html()} + <div class="toolParamHelp" style="clear: both;"> + Check the box and click <b>Save</b> to receive email alerts when updates to this repository occur. + </div> + </div> + <div class="form-row"> + <input type="submit" name="receive_email_alerts_button" value="Save"/> + </div> + </form> + </div> + </div> +%endif <p/><div class="toolForm"><div class="toolFormTitle">User access</div> --- a/templates/webapps/community/repository/view_repository.mako Thu Jun 23 11:03:45 2011 -0400 +++ b/templates/webapps/community/repository/view_repository.mako Thu Jun 23 15:32:07 2011 -0400 @@ -134,8 +134,28 @@ </div></div></div> -<p/> +%if trans.user and trans.app.config.smtp_server: + <p/> + <div class="toolForm"> + <div class="toolFormTitle">${repository.name}</div> + <div class="toolFormBody"> + <form name="receive_email_alerts" id="receive_email_alerts" action="${h.url_for( controller='repository', action='view_repository', id=trans.security.encode_id( repository.id ) )}" method="post" > + <div class="form-row"> + <label>Receive email alerts:</label> + ${alerts_check_box.get_html()} + <div class="toolParamHelp" style="clear: both;"> + Check the box and click <b>Save</b> to receive email alerts when updates to this repository occur. + </div> + </div> + <div class="form-row"> + <input type="submit" name="receive_email_alerts_button" value="Save"/> + </div> + </form> + </div> + </div> +%endif %if repository.ratings: + <p/><div class="toolForm"><div class="toolFormTitle">Rating</div><div class="toolFormBody"> Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.