diff --git a/lib/galaxy/managers/histories.py b/lib/galaxy/managers/histories.py
index c7e143bbbc3..007f2bfe2b6 100644
--- a/lib/galaxy/managers/histories.py
+++ b/lib/galaxy/managers/histories.py
@@ -440,6 +440,7 @@ class HistorySerializer(sharable.SharableModelSerializer, deletable.PurgableSeri
"detailed",
[
"contents_url",
+ "email_hash",
"empty",
"size",
"user_id",
diff --git a/lib/galaxy/managers/sharable.py b/lib/galaxy/managers/sharable.py
index 53f90351dfa..aebef5bb039 100644
--- a/lib/galaxy/managers/sharable.py
+++ b/lib/galaxy/managers/sharable.py
@@ -45,6 +45,7 @@ from galaxy.schema.schema import (
)
from galaxy.structured_app import MinimalManagerApp
from galaxy.util import ready_name_for_url
+from galaxy.util.hash_util import md5_hash_str
if TYPE_CHECKING:
from sqlalchemy.orm import Query
@@ -383,7 +384,17 @@ class SharableModelSerializer(
def __init__(self, app, **kwargs):
super().__init__(app, **kwargs)
self.add_view(
- "sharing", ["id", "title", "importable", "published", "username", "username_and_slug", "users_shared_with"]
+ "sharing",
+ [
+ "id",
+ "title",
+ "email_hash",
+ "importable",
+ "published",
+ "username",
+ "username_and_slug",
+ "users_shared_with",
+ ],
)
def add_serializers(self):
@@ -398,11 +409,17 @@ class SharableModelSerializer(
"username": self.serialize_username,
"username_and_slug": self.serialize_username_and_slug,
"users_shared_with": self.serialize_users_shared_with,
+ "email_hash": self.serialize_email_hash,
}
)
# these use the default serializer but must still be white-listed
self.serializable_keyset.update(["importable", "published", "slug"])
+ def serialize_email_hash(self, item, key, **context):
+ if not (item.user and item.user.email):
+ return None
+ return md5_hash_str(item.user.email)
+
def serialize_title(self, item, key, **context):
if hasattr(item, "title"):
return item.title
diff --git a/lib/galaxy/managers/workflows.py b/lib/galaxy/managers/workflows.py
index 795ea44fa84..79451a454e6 100644
--- a/lib/galaxy/managers/workflows.py
+++ b/lib/galaxy/managers/workflows.py
@@ -69,6 +69,7 @@ from galaxy.tools.parameters.basic import (
RuntimeValue,
workflow_building_modes,
)
+from galaxy.util.hash_util import md5_hash_str
from galaxy.util.json import (
safe_dumps,
safe_loads,
@@ -1480,6 +1481,7 @@ class WorkflowContentsManager(UsesAnnotations):
item["name"] = workflow.name
item["url"] = url_for("workflow", id=item["id"])
item["owner"] = stored.user.username
+ item["email_hash"] = md5_hash_str(stored.user.email)
item["slug"] = stored.slug
inputs = {}
for step in workflow.input_steps:
diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py
index 2e3dcd5116a..aa41c8b782d 100644
--- a/lib/galaxy/model/__init__.py
+++ b/lib/galaxy/model/__init__.py
@@ -146,7 +146,10 @@ from galaxy.util.form_builder import (
WorkflowField,
WorkflowMappingField,
)
-from galaxy.util.hash_util import new_insecure_hash
+from galaxy.util.hash_util import (
+ md5_hash_str,
+ new_insecure_hash,
+)
from galaxy.util.json import safe_loads
from galaxy.util.sanitize_html import sanitize_html
@@ -8836,7 +8839,7 @@ class CloudAuthz(Base, _HasTable):
)
-class Page(Base, Dictifiable, RepresentById):
+class Page(Base, HasTags, Dictifiable, RepresentById):
__tablename__ = "page"
__table_args__ = (Index("ix_page_slug", "slug", mysql_length=200),)
@@ -8892,6 +8895,7 @@ class Page(Base, Dictifiable, RepresentById):
"importable",
"deleted",
"username",
+ "email_hash",
]
def to_dict(self, view="element"):
@@ -8907,6 +8911,11 @@ class Page(Base, Dictifiable, RepresentById):
def username(self):
return self.user.username
+ # email needed for hash generation
+ @property
+ def email_hash(self):
+ return md5_hash_str(self.user.email)
+
class PageRevision(Base, Dictifiable, RepresentById):
__tablename__ = "page_revision"
@@ -8942,7 +8951,7 @@ class PageUserShareAssociation(Base, UserShareAssociation):
page = relationship("Page", back_populates="users_shared_with")
-class Visualization(Base, RepresentById):
+class Visualization(Base, HasTags, RepresentById):
__tablename__ = "visualization"
__table_args__ = (
Index("ix_visualization_dbkey", "dbkey", mysql_length=200),
diff --git a/lib/galaxy/schema/schema.py b/lib/galaxy/schema/schema.py
index cce48d50316..cb011c284b3 100644
--- a/lib/galaxy/schema/schema.py
+++ b/lib/galaxy/schema/schema.py
@@ -3014,11 +3014,6 @@ class SharingStatus(Model):
title="Importable",
description="Whether this resource can be published using a link.",
)
- username: Optional[str] = Field(
- None,
- title="Username",
- description="The owner's username.",
- )
published: bool = Field(
...,
title="Published",
@@ -3029,6 +3024,16 @@ class SharingStatus(Model):
title="Users shared with",
description="The list of encoded ids for users the resource has been shared.",
)
+ email_hash: Optional[str] = Field(
+ None,
+ title="Encoded Email",
+ description="Encoded owner email.",
+ )
+ username: Optional[str] = Field(
+ None,
+ title="Username",
+ description="The owner's username.",
+ )
username_and_slug: Optional[str] = Field(
None,
title="Username and slug",
@@ -3199,6 +3204,11 @@ class PageSummary(PageSummaryBase):
title="Username",
description="The name of the user owning this Page.",
)
+ email_hash: str = Field(
+ ..., # Required
+ title="Encoded email",
+ description="The encoded email of the user",
+ )
published: bool = Field(
..., # Required
title="Published",
diff --git a/lib/galaxy/util/hash_util.py b/lib/galaxy/util/hash_util.py
index 53fe69a8aa0..6c5f6a5424e 100644
--- a/lib/galaxy/util/hash_util.py
+++ b/lib/galaxy/util/hash_util.py
@@ -90,6 +90,15 @@ def md5_hash_file(path: Union[str, os.PathLike]) -> Optional[str]:
return None
+def md5_hash_str(s):
+ """
+ Return hex encoded md5 hash of string s
+ """
+ m = hashlib.md5()
+ m.update(smart_str(s))
+ return m.hexdigest()
+
+
def new_secure_hash_v2(text_type: Union[bytes, str]) -> str:
"""More modern version of new_secure_hash.
diff --git a/lib/galaxy/web/framework/helpers/__init__.py b/lib/galaxy/web/framework/helpers/__init__.py
index 9c75b88e976..d52b0a56cd5 100644
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -14,11 +14,7 @@ from babel import default_locale
from babel.dates import format_timedelta
from routes import url_for
-from galaxy.util import (
- hash_util,
- smart_str,
- unicodify,
-)
+from galaxy.util import unicodify
from galaxy.util.json import safe_dumps as dumps # noqa: F401
from .tags import (
javascript_link,
@@ -102,16 +98,6 @@ def dist_js(*args):
return js_helper("static/dist/", *args)
-# Hashes
-def md5(s):
- """
- Return hex encoded md5 hash of string s
- """
- m = hash_util.md5()
- m.update(smart_str(s))
- return m.hexdigest()
-
-
# Unicode help
def to_unicode(a_string):
"""
diff --git a/lib/galaxy/webapps/galaxy/api/visualizations.py b/lib/galaxy/webapps/galaxy/api/visualizations.py
index 3d854f7fb20..1208b138a57 100644
--- a/lib/galaxy/webapps/galaxy/api/visualizations.py
+++ b/lib/galaxy/webapps/galaxy/api/visualizations.py
@@ -28,6 +28,7 @@ from galaxy.schema.schema import (
ShareWithStatus,
SharingStatus,
)
+from galaxy.util.hash_util import md5_hash_str
from galaxy.web import expose_api
from galaxy.webapps.base.controller import UsesVisualizationMixin
from galaxy.webapps.base.webapp import GalaxyWebTransaction
@@ -191,6 +192,8 @@ class VisualizationsController(BaseGalaxyAPIController, UsesVisualizationMixin,
slug=visualization.slug,
)
dictionary["username"] = visualization.user.username
+ dictionary["email_hash"] = md5_hash_str(visualization.user.email)
+ dictionary["tags"] = visualization.make_tag_string_list()
dictionary["annotation"] = self.get_item_annotation_str(trans.sa_session, trans.user, visualization)
# need to encode ids in revisions as well
encoded_revisions = []
diff --git a/lib/galaxy/webapps/galaxy/controllers/dataset.py b/lib/galaxy/webapps/galaxy/controllers/dataset.py
index 3aad8714803..765305a80ea 100644
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -555,23 +555,6 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE
"""Does nothing because datasets do not have an importable/accessible attribute. This method could potentially set another attribute."""
return
- @web.expose
- @web.require_login("rate items")
- @web.json
- def rate_async(self, trans, id, rating):
- """Rate a dataset asynchronously and return updated community data."""
-
- decoded_id = self.decode_id(id)
- dataset = self.hda_manager.get_accessible(decoded_id, trans.user)
- dataset = self.hda_manager.error_if_uploading(dataset)
- if not dataset:
- return trans.show_error_message("The specified dataset does not exist.")
-
- # Rate dataset.
- self.rate_item(trans.sa_session, trans.get_user(), dataset, rating)
-
- return self.get_ave_item_rating_data(trans.sa_session, dataset)
-
@web.expose
def display_by_username_and_slug(self, trans, username, slug, filename=None, preview=True):
"""Display dataset by username and slug; because datasets do not yet have slugs, the slug is the dataset's id."""
@@ -600,24 +583,11 @@ class DatasetInterface(BaseUIController, UsesAnnotations, UsesItemRatings, UsesE
trans.response.set_content_type(dataset.get_mime())
return open(dataset.file_name, "rb")
else:
- # Get rating data.
- user_item_rating = 0
- if trans.get_user():
- user_item_rating = self.get_user_item_rating(trans.sa_session, trans.get_user(), dataset)
- if user_item_rating:
- user_item_rating = user_item_rating.rating
- else:
- user_item_rating = 0
- ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, dataset)
-
return trans.fill_template_mako(
"/dataset/display.mako",
item=dataset,
item_data=dataset_data,
truncated=truncated,
- user_item_rating=user_item_rating,
- ave_item_rating=ave_item_rating,
- num_ratings=num_ratings,
first_chunk=first_chunk,
)
diff --git a/lib/galaxy/webapps/galaxy/controllers/history.py b/lib/galaxy/webapps/galaxy/controllers/history.py
index 11962483e9d..1b2de3d6c55 100644
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -571,16 +571,6 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt
# Security check raises error if user cannot access history.
self.history_manager.error_unless_accessible(history, trans.user, current_history=trans.history)
- # Get rating data.
- user_item_rating = 0
- if trans.get_user():
- user_item_rating = self.get_user_item_rating(trans.sa_session, trans.get_user(), history)
- if user_item_rating:
- user_item_rating = user_item_rating.rating
- else:
- user_item_rating = 0
- ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, history)
-
# Encode history id.
history_id = trans.security.encode_id(history.id)
@@ -590,9 +580,6 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt
controller="published",
action="history",
id=history_id,
- user_item_rating=user_item_rating,
- ave_item_rating=ave_item_rating,
- num_ratings=num_ratings,
)
)
@@ -741,19 +728,6 @@ class HistoryController(BaseUIController, SharableMixin, UsesAnnotations, UsesIt
return trans.show_ok_message("Your jobs have been resumed.", refresh_frames=refresh_frames)
# TODO: used in index.mako
- @web.expose
- @web.require_login("rate items")
- @web.json
- def rate_async(self, trans, id, rating):
- """Rate a history asynchronously and return updated community data."""
- history = self.history_manager.get_accessible(self.decode_id(id), trans.user, current_history=trans.history)
- if not history:
- return trans.show_error_message("The specified history does not exist.")
- # Rate history.
- self.rate_item(trans.sa_session, trans.get_user(), history, rating)
- return self.get_ave_item_rating_data(trans.sa_session, history)
- # TODO: used in display_base.mako
-
@web.expose
@web.json
@web.require_login("get history name and link")
diff --git a/lib/galaxy/webapps/galaxy/controllers/page.py b/lib/galaxy/webapps/galaxy/controllers/page.py
index 30970043dba..180d782dea9 100644
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -549,25 +549,12 @@ class PageController(BaseUIController, SharableMixin, UsesStoredWorkflowMixin, U
# Encode page identifier.
page_id = trans.security.encode_id(page.id)
- # Get rating data.
- user_item_rating = 0
- if trans.get_user():
- user_item_rating = self.get_user_item_rating(trans.sa_session, trans.get_user(), page)
- if user_item_rating:
- user_item_rating = user_item_rating.rating
- else:
- user_item_rating = 0
- ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, page)
-
# Redirect to client.
return trans.response.send_redirect(
web.url_for(
controller="published",
action="page",
id=page_id,
- user_item_rating=user_item_rating,
- ave_item_rating=ave_item_rating,
- num_ratings=num_ratings,
)
)
@@ -587,21 +574,6 @@ class PageController(BaseUIController, SharableMixin, UsesStoredWorkflowMixin, U
trans.sa_session.flush()
return
- @web.expose
- @web.require_login("rate items")
- @web.json
- def rate_async(self, trans, id, rating):
- """Rate a page asynchronously and return updated community data."""
-
- page = self.get_page(trans, id, check_ownership=False, check_accessible=True)
- if not page:
- return trans.show_error_message("The specified page does not exist.")
-
- # Rate page.
- self.rate_item(trans.sa_session, trans.get_user(), page, rating)
-
- return self.get_ave_item_rating_data(trans.sa_session, page)
-
@web.expose
def get_embed_html_async(self, trans, id):
"""Returns HTML for embedding a workflow in a page."""
diff --git a/lib/galaxy/webapps/galaxy/controllers/visualization.py b/lib/galaxy/webapps/galaxy/controllers/visualization.py
index fa6534a7939..e56b249d8a6 100644
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -435,21 +435,6 @@ class VisualizationController(
return
- @web.expose
- @web.require_login("rate items")
- @web.json
- def rate_async(self, trans, id, rating):
- """Rate a visualization asynchronously and return updated community data."""
-
- visualization = self.get_visualization(trans, id, check_ownership=False, check_accessible=True)
- if not visualization:
- return trans.show_error_message("The specified visualization does not exist.")
-
- # Rate visualization.
- self.rate_item(trans.sa_session, trans.get_user(), visualization, rating)
-
- return self.get_ave_item_rating_data(trans.sa_session, visualization)
-
@web.expose
@web.require_login("share Galaxy visualizations")
def imp(self, trans, id):
@@ -511,25 +496,12 @@ class VisualizationController(
# Encode page identifier.
visualization_id = trans.security.encode_id(visualization.id)
- # Get rating data.
- user_item_rating = 0
- if trans.get_user():
- user_item_rating = self.get_user_item_rating(trans.sa_session, trans.get_user(), visualization)
- if user_item_rating:
- user_item_rating = user_item_rating.rating
- else:
- user_item_rating = 0
- ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, visualization)
-
# Redirect to client.
return trans.response.send_redirect(
web.url_for(
controller="published",
action="visualization",
id=visualization_id,
- user_item_rating=user_item_rating,
- ave_item_rating=ave_item_rating,
- num_ratings=num_ratings,
)
)
diff --git a/lib/galaxy/webapps/galaxy/controllers/workflow.py b/lib/galaxy/webapps/galaxy/controllers/workflow.py
index a3cf20c3e39..63a8d666985 100644
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -268,16 +268,6 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi
for step in stored_workflow.latest_workflow.steps:
step.annotation = self.get_item_annotation_str(trans.sa_session, stored_workflow.user, step)
- # Get rating data.
- user_item_rating = 0
- if trans.get_user():
- user_item_rating = self.get_user_item_rating(trans.sa_session, trans.get_user(), stored_workflow)
- if user_item_rating:
- user_item_rating = user_item_rating.rating
- else:
- user_item_rating = 0
- ave_item_rating, num_ratings = self.get_ave_item_rating_data(trans.sa_session, stored_workflow)
-
# Encode page identifier.
workflow_id = trans.security.encode_id(stored_workflow.id)
@@ -287,9 +277,6 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi
controller="published",
action="workflow",
id=workflow_id,
- user_item_rating=user_item_rating,
- ave_item_rating=ave_item_rating,
- num_ratings=num_ratings,
)
)
@@ -346,21 +333,6 @@ class WorkflowController(BaseUIController, SharableMixin, UsesStoredWorkflowMixi
trans.sa_session.flush()
return new_annotation
- @web.expose
- @web.require_login("rate items")
- @web.json
- def rate_async(self, trans, id, rating):
- """Rate a workflow asynchronously and return updated community data."""
-
- stored = self.get_stored_workflow(trans, id, check_ownership=False, check_accessible=True)
- if not stored:
- return trans.show_error_message("The specified workflow does not exist.")
-
- # Rate workflow.
- self.rate_item(trans.sa_session, trans.get_user(), stored, rating)
-
- return self.get_ave_item_rating_data(trans.sa_session, stored)
-
@web.expose
def get_embed_html_async(self, trans, id):
"""Returns HTML for embedding a workflow in a page."""
diff --git a/templates/display_base.mako b/templates/display_base.mako
index 597d49abcf6..e0a6d5e2403 100644
--- a/templates/display_base.mako
+++ b/templates/display_base.mako
@@ -138,80 +138,7 @@
All published ${item_plural.lower()}
Published ${item_plural.lower()} by ${item.user.username | h}
- ## Rating.
-
Rating
- <%
- label = "ratings"
- if num_ratings == 1:
- label = "rating"
- %>
-
- Community
-
- (${num_ratings} ${label},
- ${"%.1f" % ave_item_rating} average)
-
-
-
- 0 and ave_item_rating <= 1.5:
- checked="checked"
- %endif
- />
- 1.5 and ave_item_rating <= 2.5:
- checked="checked"
- %endif
- />
- 2.5 and ave_item_rating <= 3.5:
- checked="checked"
- %endif
- />
- 3.5 and ave_item_rating <= 4.5:
- checked="checked"
- %endif
- />
- 4.5:
- checked="checked"
- %endif
- />
-
-
- %if trans.get_user():
-
- Yours
(thanks!)
-
-
-
-
-
-
-
-
- %endif
+
## Tags.
Tags