mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #14803 from guerler/add_user_ratings
Add avatar to published item components
This commit is contained in:
@@ -11,8 +11,9 @@
|
||||
<div class="m-3">
|
||||
<div v-if="modelTitle">
|
||||
<h1 class="h-sm">About this {{ modelTitle }}</h1>
|
||||
<h2 class="h-md">{{ details.title || details.name }}</h2>
|
||||
<StatelessTags v-if="details.tags" class="tags mt-2" :value="details.tags" :disabled="true" />
|
||||
<h2 class="h-md">{{ item.title || item.name }}</h2>
|
||||
<img class="float-right" :src="gravatarSource" alt="user avatar" />
|
||||
<StatelessTags v-if="item.tags" class="tags mt-2" :value="item.tags" :disabled="true" />
|
||||
<br />
|
||||
<h2 class="h-sm">Author</h2>
|
||||
<div>{{ owner }}</div>
|
||||
@@ -25,7 +26,7 @@
|
||||
<router-link :to="publishedByUser"> Published {{ plural }} by {{ owner }}. </router-link>
|
||||
</div>
|
||||
</div>
|
||||
<LoadingSpan v-else message="Loading details" />
|
||||
<LoadingSpan v-else message="Loading item details" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -40,21 +41,24 @@ export default {
|
||||
StatelessTags,
|
||||
},
|
||||
props: {
|
||||
details: {
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
gravatarSource() {
|
||||
return `https://secure.gravatar.com/avatar/${this.item.email_hash}?d=identicon`;
|
||||
},
|
||||
modelTitle() {
|
||||
const modelClass = this.details ? this.details.model_class : "Item";
|
||||
const modelClass = this.item ? this.item.model_class : "Item";
|
||||
if (modelClass == "StoredWorkflow") {
|
||||
return "Workflow";
|
||||
}
|
||||
return modelClass;
|
||||
},
|
||||
owner() {
|
||||
return this.details.owner || this.details.username || "Unavailable";
|
||||
return this.item.owner || this.item.username || "Unavailable";
|
||||
},
|
||||
plural() {
|
||||
if (this.modelTitle == "History") {
|
||||
@@ -66,7 +70,7 @@ export default {
|
||||
return this.plural.toLowerCase();
|
||||
},
|
||||
publishedByUser() {
|
||||
return `/${this.pluralPath}/list_published?f-username=${this.details.username}`;
|
||||
return `/${this.pluralPath}/list_published?f-username=${this.item.username}`;
|
||||
},
|
||||
urlAll() {
|
||||
return `/${this.pluralPath}/list_published`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Published :details="history">
|
||||
<Published :item="history">
|
||||
<template v-slot>
|
||||
<HistoryView :id="id" />
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<config-provider v-slot="{ config, loading }">
|
||||
<Published :details="page">
|
||||
<Published :item="page">
|
||||
<template v-slot>
|
||||
<div v-if="!loading">
|
||||
<markdown
|
||||
@@ -57,7 +57,7 @@ export default {
|
||||
},
|
||||
created() {
|
||||
urlData({ url: this.dataUrl }).then((data) => {
|
||||
this.page = { ...data };
|
||||
this.page = data;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Published :details="visualization">
|
||||
<Published :item="visualization">
|
||||
<template v-slot>
|
||||
<CenterFrame :src="getUrl" />
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Published :details="workflow">
|
||||
<Published :item="workflow">
|
||||
<template v-slot>
|
||||
<WorkflowDisplay :args="{ workflow_id: id }" :workflow="workflow" :expanded="true" />
|
||||
</template>
|
||||
|
||||
@@ -440,6 +440,7 @@ class HistorySerializer(sharable.SharableModelSerializer, deletable.PurgableSeri
|
||||
"detailed",
|
||||
[
|
||||
"contents_url",
|
||||
"email_hash",
|
||||
"empty",
|
||||
"size",
|
||||
"user_id",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -138,80 +138,7 @@
|
||||
<p>
|
||||
<a href="${href_to_all_items}">All published ${item_plural.lower()}</a><br>
|
||||
<a href="${href_to_user_items}">Published ${item_plural.lower()} by ${item.user.username | h}</a>
|
||||
## Rating.
|
||||
<h4>Rating</h4>
|
||||
<%
|
||||
label = "ratings"
|
||||
if num_ratings == 1:
|
||||
label = "rating"
|
||||
%>
|
||||
<div style="padding-bottom: 0.75em; float: left">
|
||||
Community<br>
|
||||
<span style="font-size:80%">
|
||||
(<span id="num_ratings">${num_ratings}</span> ${label},
|
||||
<span id="ave_rating">${"%.1f" % ave_item_rating}</span> average)
|
||||
<span>
|
||||
</div>
|
||||
<div style="float: right">
|
||||
<input name="star1" type="radio" class="community_rating_star star" disabled="disabled" value="1"
|
||||
%if ave_item_rating > 0 and ave_item_rating <= 1.5:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star1" type="radio" class="community_rating_star star" disabled="disabled" value="2"
|
||||
%if ave_item_rating > 1.5 and ave_item_rating <= 2.5:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star1" type="radio" class="community_rating_star star" disabled="disabled" value="3"
|
||||
%if ave_item_rating > 2.5 and ave_item_rating <= 3.5:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star1" type="radio" class="community_rating_star star" disabled="disabled" value="4"
|
||||
%if ave_item_rating > 3.5 and ave_item_rating <= 4.5:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star1" type="radio" class="community_rating_star star" disabled="disabled" value="5"
|
||||
%if ave_item_rating > 4.5:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
%if trans.get_user():
|
||||
<div style="float: left">
|
||||
Yours<br><span id="rating_feedback" style="font-size:80%; display: none">(thanks!)</span>
|
||||
</div>
|
||||
<div style="float: right">
|
||||
<input name="star2" type="radio" class="user_rating_star" value="1"
|
||||
%if user_item_rating == 1:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star2" type="radio" class="user_rating_star" value="2"
|
||||
%if user_item_rating == 2:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star2" type="radio" class="user_rating_star" value="3"
|
||||
%if user_item_rating == 3:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star2" type="radio" class="user_rating_star" value="4"
|
||||
%if user_item_rating == 4:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
<input name="star2" type="radio" class="user_rating_star" value="5"
|
||||
%if user_item_rating == 5:
|
||||
checked="checked"
|
||||
%endif
|
||||
/>
|
||||
</div>
|
||||
%endif
|
||||
</p>
|
||||
<div style="clear: both;"></div>
|
||||
## Tags.
|
||||
<h4>Tags</h4>
|
||||
|
||||
Reference in New Issue
Block a user