Merge branch 'master' of github.com:doubaniux/boofilsic
This commit is contained in:
commit
d5e7519ba2
45 changed files with 3196 additions and 257 deletions
|
@ -158,6 +158,7 @@ if not DEBUG:
|
|||
'format': '{levelname} {asctime} {name}:{lineno} {message}',
|
||||
'style': '{',
|
||||
},
|
||||
<<<<<<< HEAD
|
||||
},
|
||||
'handlers': {
|
||||
'file': {
|
||||
|
@ -167,6 +168,17 @@ if not DEBUG:
|
|||
'formatter': 'simple'
|
||||
},
|
||||
},
|
||||
=======
|
||||
},
|
||||
'handlers': {
|
||||
'file': {
|
||||
'level': 'INFO',
|
||||
'class': 'logging.FileHandler',
|
||||
'filename': os.path.join(BASE_DIR, 'log'),
|
||||
'formatter': 'simple'
|
||||
},
|
||||
},
|
||||
>>>>>>> 5000175c587bb9ad7dd309f6cd2b895c3fea1ba1
|
||||
'root': {
|
||||
'handlers': ['file'],
|
||||
'level': 'INFO',
|
||||
|
@ -228,11 +240,21 @@ LOGIN_URL = '/users/login/'
|
|||
ADMIN_URL = 'lpLub76xn0crtyet9byernHd'
|
||||
|
||||
# Luminati proxy settings
|
||||
<<<<<<< HEAD
|
||||
LUMINATI_USERNAME = 'lum-customer-hl_ov7b8j5-zone-static'
|
||||
LUMINATI_PASSWORD = 'd65r7vtb8yn98'
|
||||
|
||||
# Spotify credentials
|
||||
SPOTIFY_CREDENTIAL = "Nz67nbCVBNh095hPbv9y8tfh0987fvh098f3NDg2NjljYjg3Nzc0MjIwODQ0ZWE="
|
||||
=======
|
||||
LUMINATI_USERNAME = 'lum-customer-hl_7bed6f85-zone-static'
|
||||
LUMINATI_PASSWORD = 'dwy4lz5ck438'
|
||||
|
||||
# Spotify credentials
|
||||
# SPOTIFY_CLIENT_ID = "***REMOVED***"
|
||||
# SPOTIFY_CLIENT_SECRET = "***REMOVED***"
|
||||
SPOTIFY_CREDENTIAL = "***REMOVED***"
|
||||
>>>>>>> 5000175c587bb9ad7dd309f6cd2b895c3fea1ba1
|
||||
|
||||
# IMDb API service https://imdb-api.com/
|
||||
IMDB_API_KEY = "***REMOVED***"
|
||||
|
|
|
@ -26,6 +26,7 @@ urlpatterns = [
|
|||
path('books/', include('books.urls')),
|
||||
path('movies/', include('movies.urls')),
|
||||
path('music/', include('music.urls')),
|
||||
path('games/', include('games.urls')),
|
||||
path('announcement/', include('management.urls')),
|
||||
path('', include('common.urls')),
|
||||
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
{% if book.other_info %}
|
||||
{% for k, v in book.other_info.items %}
|
||||
<div>
|
||||
{{k}}:{{v}}
|
||||
{{ k }}:{{ v | urlize }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<h5>
|
||||
{% trans '根据豆瓣内容填写下方表单' %}
|
||||
</h5>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://search.douban.com/book/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts" src="https://search.douban.com/book/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<div class="dividing-line"></div>
|
||||
<div id="parser">
|
||||
<label style="font-size: small;">{% trans '解析器:' %}</label>
|
||||
|
|
20
common/config.py
Normal file
20
common/config.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# how many items are showed in one search result page
|
||||
ITEMS_PER_PAGE = 20
|
||||
|
||||
# how many pages links in the pagination
|
||||
PAGE_LINK_NUMBER = 7
|
||||
|
||||
# max tags on list page
|
||||
TAG_NUMBER_ON_LIST = 5
|
||||
|
||||
# how many books have in each set at the home page
|
||||
BOOKS_PER_SET = 5
|
||||
|
||||
# how many movies have in each set at the home page
|
||||
MOVIES_PER_SET = 5
|
||||
|
||||
# how many music items have in each set at the home page
|
||||
MUSIC_PER_SET = 5
|
||||
|
||||
# how many games have in each set at the home page
|
||||
GAMES_PER_SET = 5
|
|
@ -22,6 +22,7 @@ class SourceSiteEnum(models.TextChoices):
|
|||
DOUBAN = "douban", _("豆瓣")
|
||||
SPOTIFY = "spotify", _("Spotify")
|
||||
IMDB = "imdb", _("IMDb")
|
||||
STEAM = "steam", _("STEAM")
|
||||
|
||||
|
||||
class Entity(models.Model):
|
||||
|
|
|
@ -22,6 +22,8 @@ from books.models import Book
|
|||
from books.forms import BookForm
|
||||
from music.models import Album, Song
|
||||
from music.forms import AlbumForm, SongForm
|
||||
from games.models import Game
|
||||
from games.forms import GameForm
|
||||
|
||||
|
||||
RE_NUMBERS = re.compile(r"\d+\d*")
|
||||
|
@ -1018,3 +1020,131 @@ class ImdbMovieScraper(AbstractScraper):
|
|||
@classmethod
|
||||
def get_api_url(cls, url):
|
||||
return f"https://imdb-api.com/zh/API/Title/{IMDB_API_KEY}/{cls.regex.findall(url)[0]}/FullActor,"
|
||||
|
||||
|
||||
class DoubanGameScraper(AbstractScraper):
|
||||
site_name = SourceSiteEnum.DOUBAN.value
|
||||
host = 'www.douban.com/game/'
|
||||
data_class = Game
|
||||
form_class = GameForm
|
||||
|
||||
regex = re.compile(r"https://www\.douban\.com/game/\d+/{0,1}")
|
||||
|
||||
def scrape(self, url):
|
||||
headers = DEFAULT_REQUEST_HEADERS.copy()
|
||||
headers['Host'] = 'www.douban.com'
|
||||
content = self.download_page(url, headers)
|
||||
|
||||
try:
|
||||
raw_title = content.xpath(
|
||||
"//div[@id='content']/h1/text()")[0].strip()
|
||||
except IndexError:
|
||||
raise ValueError("given url contains no movie info")
|
||||
|
||||
title = raw_title
|
||||
|
||||
other_title_elem = content.xpath(
|
||||
"//dl[@class='game-attr']//dt[text()='别名:']/following-sibling::dd[1]/text()")
|
||||
other_title = other_title_elem[0].strip().split(' / ') if other_title_elem else None
|
||||
|
||||
developer_elem = content.xpath(
|
||||
"//dl[@class='game-attr']//dt[text()='开发商:']/following-sibling::dd[1]/text()")
|
||||
developer = developer_elem[0].strip().split(' / ') if developer_elem else None
|
||||
|
||||
publisher_elem = content.xpath(
|
||||
"//dl[@class='game-attr']//dt[text()='发行商:']/following-sibling::dd[1]/text()")
|
||||
publisher = publisher_elem[0].strip().split(' / ') if publisher_elem else None
|
||||
|
||||
platform_elem = content.xpath(
|
||||
"//dl[@class='game-attr']//dt[text()='平台:']/following-sibling::dd[1]/a/text()")
|
||||
platform = platform_elem if platform_elem else None
|
||||
|
||||
genre_elem = content.xpath(
|
||||
"//dl[@class='game-attr']//dt[text()='类型:']/following-sibling::dd[1]/a/text()")
|
||||
genre = None
|
||||
if genre_elem:
|
||||
genre = [g for g in genre_elem if g != '游戏']
|
||||
|
||||
date_elem = content.xpath(
|
||||
"//dl[@class='game-attr']//dt[text()='发行日期:']/following-sibling::dd[1]/text()")
|
||||
release_date = dateparser.parse(date_elem[0].strip(), settings={
|
||||
"RELATIVE_BASE": datetime.datetime(1900, 1, 1)}) if date_elem else None
|
||||
|
||||
brief_elem = content.xpath("//div[@class='mod item-desc']/p/text()")
|
||||
brief = '\n'.join(brief_elem) if brief_elem else None
|
||||
|
||||
img_url_elem = content.xpath(
|
||||
"//div[@class='item-subject-info']/div[@class='pic']//img/@src")
|
||||
img_url = img_url_elem[0].strip() if img_url_elem else None
|
||||
raw_img, ext = self.download_image(img_url)
|
||||
|
||||
data = {
|
||||
'title': title,
|
||||
'other_title': other_title,
|
||||
'developer': developer,
|
||||
'publisher': publisher,
|
||||
'release_date': release_date,
|
||||
'genre': genre,
|
||||
'platform': platform,
|
||||
'brief': brief,
|
||||
'other_info': None,
|
||||
'source_site': self.site_name,
|
||||
'source_url': self.get_effective_url(url),
|
||||
}
|
||||
|
||||
self.raw_data, self.raw_img, self.img_ext = data, raw_img, ext
|
||||
return data, raw_img
|
||||
|
||||
|
||||
class SteamGameScraper(AbstractScraper):
|
||||
site_name = SourceSiteEnum.STEAM.value
|
||||
host = 'store.steampowered.com'
|
||||
data_class = Game
|
||||
form_class = GameForm
|
||||
|
||||
regex = re.compile(r"https://store\.steampowered\.com/app/\d+/{0,1}")
|
||||
|
||||
def scrape(self, url):
|
||||
headers = DEFAULT_REQUEST_HEADERS.copy()
|
||||
headers['Host'] = self.host
|
||||
content = self.download_page(url, headers)
|
||||
|
||||
title = content.xpath("//div[@class='apphub_AppName']/text()")[0]
|
||||
developer = content.xpath("//div[@id='developers_list']/a/text()")
|
||||
publisher = content.xpath("//div[@class='glance_ctn']//div[@class='dev_row'][2]//a/text()")
|
||||
release_date = dateparser.parse(
|
||||
content.xpath(
|
||||
"//div[@class='release_date']/div[@class='date']/text()")[0],
|
||||
settings={
|
||||
"RELATIVE_BASE": datetime.datetime(1900, 1, 1)
|
||||
}
|
||||
)
|
||||
|
||||
genre = content.xpath(
|
||||
"//div[@class='details_block']/b[2]/following-sibling::a/text()")
|
||||
|
||||
platform = ['PC']
|
||||
|
||||
brief = content.xpath(
|
||||
"//div[@class='game_description_snippet']/text()")[0].strip()
|
||||
|
||||
img_url = content.xpath("//img[@class='game_header_image_full']/@src")[
|
||||
0].replace("header.jpg", "library_600x900.jpg")
|
||||
raw_img, ext = self.download_image(img_url)
|
||||
|
||||
data = {
|
||||
'title': title,
|
||||
'other_title': None,
|
||||
'developer': developer,
|
||||
'publisher': publisher,
|
||||
'release_date': release_date,
|
||||
'genre': genre,
|
||||
'platform': platform,
|
||||
'brief': brief,
|
||||
'other_info': None,
|
||||
'source_site': self.site_name,
|
||||
'source_url': self.get_effective_url(url),
|
||||
}
|
||||
|
||||
self.raw_data, self.raw_img, self.img_ext = data, raw_img, ext
|
||||
return data, raw_img
|
||||
|
|
|
@ -1246,6 +1246,14 @@ select::placeholder {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.source-label.source-label__steam {
|
||||
background: linear-gradient(30deg, #1387b8, #111d2e);
|
||||
color: white;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.main-section-wrapper {
|
||||
padding: 32px 48px 32px 36px;
|
||||
background-color: #f7f7f7;
|
||||
|
|
2
common/static/css/boofilsic.min.css
vendored
2
common/static/css/boofilsic.min.css
vendored
File diff suppressed because one or more lines are too long
|
@ -7,6 +7,8 @@ $spotify-color-primary: #1ed760
|
|||
$spotify-color-secondary: black
|
||||
$imdb-color-primary: #F5C518
|
||||
$imdb-color-secondary: #121212
|
||||
$steam-color-primary: #1387b8
|
||||
$steam-color-secondary: #111d2e
|
||||
|
||||
.source-label
|
||||
display: inline
|
||||
|
@ -46,3 +48,9 @@ $imdb-color-secondary: #121212
|
|||
color: $imdb-color-secondary
|
||||
border: none
|
||||
font-weight: bold
|
||||
&.source-label__steam
|
||||
background: linear-gradient(30deg, $steam-color-primary, $steam-color-secondary)
|
||||
color: white
|
||||
border: none
|
||||
font-weight: 600
|
||||
padding-top: 2px
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
{% endif %}
|
||||
|
||||
</a>
|
||||
{% if not request.GET.c or request.GET.c != 'movie' and request.GET.c != 'book'%}
|
||||
{% if not request.GET.c or not request.GET.c in categories %}
|
||||
<span class="entity-list__entity-category">[{{item.verbose_category_name}}]</span>
|
||||
{% endif %}
|
||||
<a href="{{ book.source_url }}">
|
||||
|
@ -161,7 +161,7 @@
|
|||
{% endif %}
|
||||
</a>
|
||||
|
||||
{% if not request.GET.c or request.GET.c != 'movie' and request.GET.c != 'book'%}
|
||||
{% if not request.GET.c or not request.GET.c in categories %}
|
||||
<span class="entity-list__entity-category">[{{item.verbose_category_name}}]</span>
|
||||
{% endif %}
|
||||
<a href="{{ movie.source_url }}">
|
||||
|
@ -221,6 +221,86 @@
|
|||
</li>
|
||||
{% endwith %}
|
||||
|
||||
{% elif item.category_name|lower == 'game' %}
|
||||
|
||||
{% with game=item %}
|
||||
<li class="entity-list__entity">
|
||||
<div class="entity-list__entity-img-wrapper">
|
||||
<a href="{% url 'games:retrieve' game.id %}">
|
||||
<img src="{{ game.cover|thumb:'normal' }}" alt="" class="entity-list__entity-img">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-list__entity-text">
|
||||
<div class="entity-list__entity-title">
|
||||
<a href="{% url 'games:retrieve' game.id %}" class="entity-list__entity-link">
|
||||
{% if request.GET.q %}
|
||||
{{ game.title | highlight:request.GET.q }}
|
||||
{% else %}
|
||||
{{ game.title }}
|
||||
{% endif %}
|
||||
</a>
|
||||
|
||||
{% if not request.GET.c or not request.GET.c in categories %}
|
||||
<span class="entity-list__entity-category">[{{item.verbose_category_name}}]</span>
|
||||
{% endif %}
|
||||
<a href="{{ game.source_url }}">
|
||||
<span class="source-label source-label__{{ game.source_site }}">{{ game.get_source_site_display }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if game.rating %}
|
||||
<div class="rating-star entity-list__rating-star" data-rating-score="{{ game.rating | floatformat:"0" }}"></div>
|
||||
<span class="entity-list__rating-score rating-score">{{ game.rating }}</span>
|
||||
{% else %}
|
||||
<div class="entity-list__rating entity-list__rating--empty"> {% trans '暂无评分' %}</div>
|
||||
{% endif %}
|
||||
|
||||
<span class="entity-list__entity-info entity-list__entity-info--full-length">
|
||||
|
||||
{% if game.other_title %}{% trans '别名' %}
|
||||
{% for other_title in game.other_title %}
|
||||
{{ other_title }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
{% if game.developer %}{% trans '开发商' %}
|
||||
{% for developer in game.developer %}
|
||||
{{ developer }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
{% if game.genre %}{% trans '类型' %}
|
||||
{% for genre in game.genre %}
|
||||
{{ genre }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
{% if game.platform %}{% trans '平台' %}
|
||||
{% for platform in game.platform %}
|
||||
{{ platform }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
</span>
|
||||
<p class="entity-list__entity-brief">
|
||||
{{ game.brief }}
|
||||
</p>
|
||||
<div class="tag-collection">
|
||||
{% for tag_dict in game.tag_list %}
|
||||
{% for k, v in tag_dict.items %}
|
||||
{% if k == 'content' %}
|
||||
<span class="tag-collection__tag">
|
||||
<a href="{% url 'common:search' %}?tag={{ v }}">{{ v }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
{% endwith %}
|
||||
|
||||
{% elif item.category_name|lower == 'album' or item.category_name|lower == 'song' %}
|
||||
|
||||
{% with music=item %}
|
||||
|
@ -260,7 +340,7 @@
|
|||
{% endif %}
|
||||
|
||||
|
||||
{% if not request.GET.c or request.GET.c != 'music' and request.GET.c != 'book' and request.GET.c != 'music' %}
|
||||
{% if not request.GET.c or not request.GET.c in categories %}
|
||||
<span class="entity-list__entity-category">[{{item.verbose_category_name}}]</span>
|
||||
{% endif %}
|
||||
<a href="{{ music.source_url }}">
|
||||
|
@ -371,7 +451,7 @@
|
|||
<div class="add-entity-entries__label">
|
||||
{% trans '没有想要的结果?' %}
|
||||
</div>
|
||||
{% if request.GET.c %}
|
||||
{% if request.GET.c and request.GET.c in categories %}
|
||||
|
||||
{% if request.GET.c|lower == 'book' %}
|
||||
|
||||
|
@ -387,17 +467,6 @@
|
|||
|
||||
{% elif request.GET.c|lower == 'music' %}
|
||||
|
||||
<a href="{% url 'movies:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加音乐' %}</button>
|
||||
</a>
|
||||
|
||||
{% else %}
|
||||
<a href="{% url 'books:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加书' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'movies:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加电影/剧集' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:create_album' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加专辑' %}</button>
|
||||
</a>
|
||||
|
@ -405,6 +474,12 @@
|
|||
<button class="add-entity-entries__button">{% trans '添加单曲' %}</button>
|
||||
</a>
|
||||
|
||||
{% elif request.GET.c|lower == 'game' %}
|
||||
|
||||
<a href="{% url 'games:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加游戏' %}</button>
|
||||
</a>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
@ -420,10 +495,13 @@
|
|||
<a href="{% url 'music:create_song' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加单曲' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'games:create' %}">
|
||||
<button class="add-entity-entries__button">{% trans '添加游戏' %}</button>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="add-entity-entries__entry">
|
||||
{% if request.GET.c %}
|
||||
{% if request.GET.c and request.GET.c in categories %}
|
||||
|
||||
{% if request.GET.c|lower == 'book' %}
|
||||
|
||||
|
@ -443,6 +521,15 @@
|
|||
<button class="add-entity-entries__button">{% trans '从表瓣剽取数据' %}</button>
|
||||
</a>
|
||||
|
||||
{% elif request.GET.c|lower == 'game' %}
|
||||
|
||||
<div class="add-entity-entries__label">
|
||||
{% trans '或者(≖ ◡ ≖)✧' %}
|
||||
</div>
|
||||
<a href="{% url 'games:scrape' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '从表瓣剽取数据' %}</button>
|
||||
</a>
|
||||
|
||||
{% elif request.GET.c|lower == 'music' %}
|
||||
|
||||
<div class="add-entity-entries__label">
|
||||
|
@ -451,22 +538,7 @@
|
|||
<a href="{% url 'music:scrape_album' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '从表瓣剽取数据' %}</button>
|
||||
</a>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="add-entity-entries__label">
|
||||
{% trans '或从表瓣剽取' %}
|
||||
</div>
|
||||
<a href="{% url 'books:scrape' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '书' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'movies:scrape' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '电影/剧集' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'music:scrape_album' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '专辑' %}</button>
|
||||
</a>
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
@ -483,6 +555,9 @@
|
|||
<a href="{% url 'music:scrape_album' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '专辑' %}</button>
|
||||
</a>
|
||||
<a href="{% url 'games:scrape' %}{% if request.GET.q %}?q={{ request.GET.q }}{% endif %}">
|
||||
<button class="add-entity-entries__button">{% trans '游戏' %}</button>
|
||||
</a>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<option value="book" {% if request.GET.c and request.GET.c == 'book' %}selected{% endif %}>{% trans '书籍' %}</option>
|
||||
<option value="movie" {% if request.GET.c and request.GET.c == 'movie' %}selected{% endif %}>{% trans '电影' %}</option>
|
||||
<option value="music" {% if request.GET.c and request.GET.c == 'music' %}selected{% endif %}>{% trans '音乐' %}</option>
|
||||
<option value="game" {% if request.GET.c and request.GET.c == 'game' %}selected{% endif %}>{% trans '游戏' %}</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="navbar__dropdown-btn">• • •</button>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
|
||||
app_name = 'common'
|
||||
urlpatterns = [
|
||||
path('', home),
|
||||
|
|
175
common/views.py
175
common/views.py
|
@ -12,137 +12,22 @@ from django.db.models import Q, Count
|
|||
from django.http import HttpResponseBadRequest
|
||||
from books.models import Book
|
||||
from movies.models import Movie
|
||||
from games.models import Game
|
||||
from music.models import Album, Song, AlbumMark, SongMark
|
||||
from users.models import Report, User, Preference
|
||||
from mastodon.decorators import mastodon_request_included
|
||||
from users.views import home as user_home
|
||||
from common.models import MarkStatusEnum
|
||||
from common.utils import PageLinksGenerator
|
||||
from common.scraper import scraper_registry
|
||||
from common.config import *
|
||||
from management.models import Announcement
|
||||
|
||||
|
||||
# how many books have in each set at the home page
|
||||
BOOKS_PER_SET = 5
|
||||
|
||||
# how many movies have in each set at the home page
|
||||
MOVIES_PER_SET = 5
|
||||
|
||||
MUSIC_PER_SET = 5
|
||||
|
||||
# how many items are showed in one search result page
|
||||
ITEMS_PER_PAGE = 20
|
||||
|
||||
# how many pages links in the pagination
|
||||
PAGE_LINK_NUMBER = 7
|
||||
|
||||
# max tags on list page
|
||||
TAG_NUMBER_ON_LIST = 5
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@login_required
|
||||
def home(request):
|
||||
"""
|
||||
Should be merged to users:home in the future
|
||||
"""
|
||||
if request.method == 'GET':
|
||||
|
||||
# really shitty code here
|
||||
|
||||
unread_announcements = Announcement.objects.filter(
|
||||
pk__gt=request.user.read_announcement_index).order_by('-pk')
|
||||
try:
|
||||
request.user.read_announcement_index = Announcement.objects.latest('pk').pk
|
||||
request.user.save(update_fields=['read_announcement_index'])
|
||||
except ObjectDoesNotExist as e:
|
||||
# when there is no annoucenment
|
||||
pass
|
||||
|
||||
do_book_marks = request.user.user_bookmarks.filter(
|
||||
status=MarkStatusEnum.DO).order_by("-edited_time")
|
||||
do_books_more = True if do_book_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
wish_book_marks = request.user.user_bookmarks.filter(
|
||||
status=MarkStatusEnum.WISH).order_by("-edited_time")
|
||||
wish_books_more = True if wish_book_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
collect_book_marks = request.user.user_bookmarks.filter(
|
||||
status=MarkStatusEnum.COLLECT).order_by("-edited_time")
|
||||
collect_books_more = True if collect_book_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
|
||||
do_movie_marks = request.user.user_moviemarks.filter(
|
||||
status=MarkStatusEnum.DO).order_by("-edited_time")
|
||||
do_movies_more = True if do_movie_marks.count() > MOVIES_PER_SET else False
|
||||
|
||||
wish_movie_marks = request.user.user_moviemarks.filter(
|
||||
status=MarkStatusEnum.WISH).order_by("-edited_time")
|
||||
wish_movies_more = True if wish_movie_marks.count() > MOVIES_PER_SET else False
|
||||
|
||||
collect_movie_marks = request.user.user_moviemarks.filter(
|
||||
status=MarkStatusEnum.COLLECT).order_by("-edited_time")
|
||||
collect_movies_more = True if collect_movie_marks.count() > MOVIES_PER_SET else False
|
||||
|
||||
do_music_marks = list(request.user.user_songmarks.filter(status=MarkStatusEnum.DO)[:MUSIC_PER_SET]) \
|
||||
+ list(request.user.user_albummarks.filter(status=MarkStatusEnum.DO)[:MUSIC_PER_SET])
|
||||
do_music_more = True if len(do_music_marks) > MUSIC_PER_SET else False
|
||||
do_music_marks = sorted(do_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
|
||||
wish_music_marks = list(request.user.user_songmarks.filter(status=MarkStatusEnum.WISH)[:MUSIC_PER_SET]) \
|
||||
+ list(request.user.user_albummarks.filter(status=MarkStatusEnum.WISH)[:MUSIC_PER_SET])
|
||||
wish_music_more = True if len(wish_music_marks) > MUSIC_PER_SET else False
|
||||
wish_music_marks = sorted(wish_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
|
||||
collect_music_marks = list(request.user.user_songmarks.filter(status=MarkStatusEnum.COLLECT)[:MUSIC_PER_SET]) \
|
||||
+ list(request.user.user_albummarks.filter(status=MarkStatusEnum.COLLECT)[:MUSIC_PER_SET])
|
||||
collect_music_more = True if len(collect_music_marks) > MUSIC_PER_SET else False
|
||||
collect_music_marks = sorted(collect_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
|
||||
for mark in do_music_marks + wish_music_marks + collect_music_marks:
|
||||
# for template convenience
|
||||
if mark.__class__ == AlbumMark:
|
||||
mark.type = "album"
|
||||
else:
|
||||
mark.type = "song"
|
||||
|
||||
reports = Report.objects.order_by('-submitted_time').filter(is_read=False)
|
||||
# reports = Report.objects.latest('submitted_time').filter(is_read=False)
|
||||
|
||||
try:
|
||||
layout = request.user.preference.get_serialized_home_layout()
|
||||
except ObjectDoesNotExist:
|
||||
Preference.objects.create(user=request.user)
|
||||
layout = request.user.preference.get_serialized_home_layout()
|
||||
|
||||
return render(
|
||||
request,
|
||||
'common/home.html',
|
||||
{
|
||||
'do_book_marks': do_book_marks[:BOOKS_PER_SET],
|
||||
'wish_book_marks': wish_book_marks[:BOOKS_PER_SET],
|
||||
'collect_book_marks': collect_book_marks[:BOOKS_PER_SET],
|
||||
'do_books_more': do_books_more,
|
||||
'wish_books_more': wish_books_more,
|
||||
'collect_books_more': collect_books_more,
|
||||
'do_movie_marks': do_movie_marks[:MOVIES_PER_SET],
|
||||
'wish_movie_marks': wish_movie_marks[:MOVIES_PER_SET],
|
||||
'collect_movie_marks': collect_movie_marks[:MOVIES_PER_SET],
|
||||
'do_movies_more': do_movies_more,
|
||||
'wish_movies_more': wish_movies_more,
|
||||
'collect_movies_more': collect_movies_more,
|
||||
'do_music_marks': do_music_marks,
|
||||
'wish_music_marks': wish_music_marks,
|
||||
'collect_music_marks': collect_music_marks,
|
||||
'do_music_more': do_music_more,
|
||||
'wish_music_more': wish_music_more,
|
||||
'collect_music_more': collect_music_more,
|
||||
'reports': reports,
|
||||
'unread_announcements': unread_announcements,
|
||||
'layout': layout,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
return user_home(request, request.user.id)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -266,6 +151,51 @@ def search(request):
|
|||
ordered_queryset = list(queryset)
|
||||
return ordered_queryset
|
||||
|
||||
def game_param_handler(**kwargs):
|
||||
# keywords
|
||||
keywords = kwargs.get('keywords')
|
||||
# tag
|
||||
tag = kwargs.get('tag')
|
||||
|
||||
query_args = []
|
||||
q = Q()
|
||||
|
||||
for keyword in keywords:
|
||||
q = q | Q(title__icontains=keyword)
|
||||
q = q | Q(other_title__icontains=keyword)
|
||||
q = q | Q(developer__icontains=keyword)
|
||||
q = q | Q(publisher__icontains=keyword)
|
||||
if tag:
|
||||
q = q & Q(game_tags__content__iexact=tag)
|
||||
|
||||
query_args.append(q)
|
||||
queryset = Game.objects.filter(*query_args).distinct()
|
||||
|
||||
def calculate_similarity(game):
|
||||
if keywords:
|
||||
# search by name
|
||||
developer_dump = ' '.join(game.developer)
|
||||
publisher_dump = ' '.join(game.publisher)
|
||||
similarity, n = 0, 0
|
||||
for keyword in keywords:
|
||||
similarity += 1/2 * SequenceMatcher(None, keyword, game.title).quick_ratio()
|
||||
+ 1/4 * SequenceMatcher(None, keyword, game.other_title).quick_ratio()
|
||||
+ 1/16 * SequenceMatcher(None, keyword, developer_dump).quick_ratio()
|
||||
+ 1/16 * SequenceMatcher(None, keyword, publisher_dump).quick_ratio()
|
||||
n += 1
|
||||
game.similarity = similarity / n
|
||||
elif tag:
|
||||
# search by single tag
|
||||
game.similarity = 0 if game.rating_number is None else game.rating_number
|
||||
else:
|
||||
game.similarity = 0
|
||||
return game.similarity
|
||||
if len(queryset) > 0:
|
||||
ordered_queryset = sorted(queryset, key=calculate_similarity, reverse=True)
|
||||
else:
|
||||
ordered_queryset = list(queryset)
|
||||
return ordered_queryset
|
||||
|
||||
def music_param_handler(**kwargs):
|
||||
# keywords
|
||||
keywords = kwargs.get('keywords')
|
||||
|
@ -329,8 +259,9 @@ def search(request):
|
|||
book_queryset = book_param_handler(**kwargs)
|
||||
movie_queryset = movie_param_handler(**kwargs)
|
||||
music_queryset = music_param_handler(**kwargs)
|
||||
game_queryset = game_param_handler(**kwargs)
|
||||
ordered_queryset = sorted(
|
||||
book_queryset + movie_queryset + music_queryset,
|
||||
book_queryset + movie_queryset + music_queryset + game_queryset,
|
||||
key=operator.attrgetter('similarity'),
|
||||
reverse=True
|
||||
)
|
||||
|
@ -340,10 +271,13 @@ def search(request):
|
|||
'book': book_param_handler,
|
||||
'movie': movie_param_handler,
|
||||
'music': music_param_handler,
|
||||
'game': game_param_handler,
|
||||
'all': all_param_handler,
|
||||
'': all_param_handler
|
||||
}
|
||||
|
||||
categories = [k for k in param_handler.keys() if not k in ['all', '']]
|
||||
|
||||
try:
|
||||
queryset = param_handler[category](
|
||||
keywords=keywords,
|
||||
|
@ -367,6 +301,7 @@ def search(request):
|
|||
"common/search_result.html",
|
||||
{
|
||||
"items": items,
|
||||
"categories": categories,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
0
games/__init__.py
Normal file
0
games/__init__.py
Normal file
3
games/admin.py
Normal file
3
games/admin.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
5
games/apps.py
Normal file
5
games/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class GamesConfig(AppConfig):
|
||||
name = 'games'
|
98
games/forms.py
Normal file
98
games/forms.py
Normal file
|
@ -0,0 +1,98 @@
|
|||
from django import forms
|
||||
from django.contrib.postgres.forms import SimpleArrayField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from .models import Game, GameMark, GameReview
|
||||
from common.models import MarkStatusEnum
|
||||
from common.forms import *
|
||||
|
||||
|
||||
def GameMarkStatusTranslator(status):
|
||||
trans_dict = {
|
||||
MarkStatusEnum.DO.value: _("在玩"),
|
||||
MarkStatusEnum.WISH.value: _("想玩"),
|
||||
MarkStatusEnum.COLLECT.value: _("玩过")
|
||||
}
|
||||
return trans_dict[status]
|
||||
|
||||
|
||||
class GameForm(forms.ModelForm):
|
||||
# id = forms.IntegerField(required=False, widget=forms.HiddenInput())
|
||||
|
||||
other_info = JSONField(required=False, label=_("其他信息"))
|
||||
|
||||
class Meta:
|
||||
model = Game
|
||||
fields = [
|
||||
'title',
|
||||
'source_site',
|
||||
'source_url',
|
||||
'other_title',
|
||||
'developer',
|
||||
'publisher',
|
||||
'release_date',
|
||||
'genre',
|
||||
'platform',
|
||||
'cover',
|
||||
'brief',
|
||||
'other_info'
|
||||
]
|
||||
|
||||
widgets = {
|
||||
'other_title': forms.TextInput(attrs={'placeholder': _("多个别名使用英文逗号分隔")}),
|
||||
'developer': forms.TextInput(attrs={'placeholder': _("多个开发商使用英文逗号分隔")}),
|
||||
'publisher': forms.TextInput(attrs={'placeholder': _("多个发行商使用英文逗号分隔")}),
|
||||
'genre': forms.TextInput(attrs={'placeholder': _("多个类型使用英文逗号分隔")}),
|
||||
'platform': forms.TextInput(attrs={'placeholder': _("多个平台使用英文逗号分隔")}),
|
||||
'cover': PreviewImageInput(),
|
||||
}
|
||||
|
||||
|
||||
class GameMarkForm(MarkForm):
|
||||
|
||||
STATUS_CHOICES = [(v, GameMarkStatusTranslator(v))
|
||||
for v in MarkStatusEnum.values]
|
||||
|
||||
status = forms.ChoiceField(
|
||||
label=_(""),
|
||||
widget=forms.RadioSelect(),
|
||||
choices=STATUS_CHOICES
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = GameMark
|
||||
fields = [
|
||||
'id',
|
||||
'game',
|
||||
'status',
|
||||
'rating',
|
||||
'text',
|
||||
'is_private',
|
||||
]
|
||||
labels = {
|
||||
'rating': _("评分"),
|
||||
}
|
||||
widgets = {
|
||||
'game': forms.TextInput(attrs={"hidden": ""}),
|
||||
}
|
||||
|
||||
|
||||
class GameReviewForm(ReviewForm):
|
||||
|
||||
class Meta:
|
||||
model = GameReview
|
||||
fields = [
|
||||
'id',
|
||||
'game',
|
||||
'title',
|
||||
'content',
|
||||
'is_private'
|
||||
]
|
||||
labels = {
|
||||
'book': "",
|
||||
'title': _("标题"),
|
||||
'content': _("正文"),
|
||||
'share_to_mastodon': _("分享到长毛象")
|
||||
}
|
||||
widgets = {
|
||||
'game': forms.TextInput(attrs={"hidden": ""}),
|
||||
}
|
128
games/models.py
Normal file
128
games/models.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
import uuid
|
||||
import django.contrib.postgres.fields as postgres
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db import models
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.shortcuts import reverse
|
||||
from common.models import Entity, Mark, Review, Tag
|
||||
from common.utils import ChoicesDictGenerator
|
||||
from boofilsic.settings import GAME_MEDIA_PATH_ROOT, DEFAULT_GAME_IMAGE
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
def game_cover_path(instance, filename):
|
||||
ext = filename.split('.')[-1]
|
||||
filename = "%s.%s" % (uuid.uuid4(), ext)
|
||||
root = ''
|
||||
if GAME_MEDIA_PATH_ROOT.endswith('/'):
|
||||
root = GAME_MEDIA_PATH_ROOT
|
||||
else:
|
||||
root = GAME_MEDIA_PATH_ROOT + '/'
|
||||
return root + timezone.now().strftime('%Y/%m/%d') + f'{filename}'
|
||||
|
||||
|
||||
class Game(Entity):
|
||||
"""
|
||||
"""
|
||||
|
||||
title = models.CharField(_("名称"), max_length=500)
|
||||
|
||||
other_title = postgres.ArrayField(
|
||||
models.CharField(blank=True,default='', max_length=500),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("别名")
|
||||
)
|
||||
|
||||
developer = postgres.ArrayField(
|
||||
models.CharField(blank=True, default='', max_length=500),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("开发商")
|
||||
)
|
||||
|
||||
publisher = postgres.ArrayField(
|
||||
models.CharField(blank=True, default='', max_length=500),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("发行商")
|
||||
)
|
||||
|
||||
release_date = models.DateField(
|
||||
_('发行日期'),
|
||||
auto_now=False,
|
||||
auto_now_add=False,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
genre = postgres.ArrayField(
|
||||
models.CharField(blank=True, default='', max_length=50),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("类型")
|
||||
)
|
||||
|
||||
platform = postgres.ArrayField(
|
||||
models.CharField(blank=True, default='', max_length=50),
|
||||
null=True,
|
||||
blank=True,
|
||||
default=list,
|
||||
verbose_name=_("平台")
|
||||
)
|
||||
|
||||
cover = models.ImageField(_("封面"), upload_to=game_cover_path, default=DEFAULT_GAME_IMAGE, blank=True)
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("games:retrieve", args=[self.id])
|
||||
|
||||
def get_tags_manager(self):
|
||||
return self.game_tags
|
||||
|
||||
@property
|
||||
def verbose_category_name(self):
|
||||
return _("游戏")
|
||||
|
||||
|
||||
class GameMark(Mark):
|
||||
game = models.ForeignKey(
|
||||
Game, on_delete=models.CASCADE, related_name='game_marks', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['owner', 'game'], name='unique_game_mark')
|
||||
]
|
||||
|
||||
|
||||
class GameReview(Review):
|
||||
game = models.ForeignKey(
|
||||
Game, on_delete=models.CASCADE, related_name='game_reviews', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['owner', 'game'], name='unique_game_review')
|
||||
]
|
||||
|
||||
|
||||
class GameTag(Tag):
|
||||
game = models.ForeignKey(
|
||||
Game, on_delete=models.CASCADE, related_name='game_tags', null=True)
|
||||
mark = models.ForeignKey(
|
||||
GameMark, on_delete=models.CASCADE, related_name='gamemark_tags', null=True)
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(
|
||||
fields=['content', 'mark'], name="unique_gamemark_tag")
|
||||
]
|
95
games/templates/games/create_update.html
Normal file
95
games/templates/games/create_update.html
Normal file
|
@ -0,0 +1,95 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content" class="container">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<a href="{% url 'games:scrape' %}"
|
||||
class="single-section-wrapper__link single-section-wrapper__link--secondary">{% trans '>>> 试试一键剽取~ <<<' %}</a>
|
||||
<form class="entity-form" action="{{ submit_url }}" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{% for field in form %}
|
||||
|
||||
{% if field.name == 'release_date' %}
|
||||
{{ field.label_tag }}
|
||||
|
||||
<input type="date" name="{{ field.name }}" id="{{ field.id_for_label }}" value="{{ form.instance.release_date | date:"
|
||||
Y-m-d" }}">
|
||||
|
||||
{% else %}
|
||||
{% if field.name != 'id' %}
|
||||
{{ field.label_tag }}
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
<input class="button" type="submit" value="{% trans '提交' %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
// mark required
|
||||
$("#content *[required]").each(function () {
|
||||
$(this).prev().prepend("*");
|
||||
});
|
||||
|
||||
// when source site is this site, hide url input box and populate it with fake url
|
||||
// the backend would update this field
|
||||
if ($("select[name='source_site']").val() == "{{ this_site_enum_value }}") {
|
||||
$("input[name='source_url']").hide();
|
||||
$("label[for='id_source_url']").hide();
|
||||
$("input[name='source_url']").val("https://www.temp.com/" + Date.now() + Math.random());
|
||||
}
|
||||
$("select[name='source_site']").change(function () {
|
||||
let value = $(this).val();
|
||||
if (value == "{{ this_site_enum_value }}") {
|
||||
$("input[name='source_url']").hide();
|
||||
$("label[for='id_source_url']").hide();
|
||||
$("input[name='source_url']").val("https://www.temp.com/" + Date.now() + Math.random());
|
||||
} else {
|
||||
$("input[name='source_url']").show();
|
||||
$("label[for='id_source_url']").show();
|
||||
$("input[name='source_url']").val("");
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
130
games/templates/games/create_update_review.html
Normal file
130
games/templates/games/create_update_review.html
Normal file
|
@ -0,0 +1,130 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/create_update_review.js' %}"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper">
|
||||
<div class="entity-card entity-card--horizontal">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'games:retrieve' game.id %}">
|
||||
<img src="{{ game.cover|thumb:'normal' }}" alt="" class="item-image float-left">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
|
||||
|
||||
<h5 class="entity-card__title"><a href="{% url 'games:retrieve' game.id %}">
|
||||
{{ game.title }}
|
||||
</a>
|
||||
<a href="{{ game.source_url }}"><span class="source-label source-label__{{ game.source_site }}">{{ game.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>
|
||||
{% if game.genre %}{% trans '类型:' %}
|
||||
{% for genre in game.genre %}
|
||||
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.developer %}{% trans '开发商:' %}
|
||||
{% for developer in game.developer %}
|
||||
<span>{{ developer }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if game.release_date %}
|
||||
{% trans '发行日期:' %}{{ game.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if game.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ game.rating | floatformat:"0" }}"> </span>
|
||||
<span class="entity-card__rating-score rating-score"> {{ game.rating }} </span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
<form action="{{ submit_url }}" method="post" class="review-form">
|
||||
{% csrf_token %}
|
||||
{{ form.game }}
|
||||
<div>
|
||||
{{ form.title.label }}
|
||||
</div>
|
||||
{{ form.title }}
|
||||
<div class="clearfix">
|
||||
<span class="float-left">
|
||||
{{ form.content.label }}
|
||||
</span>
|
||||
<span class="float-right">
|
||||
<span class="review-form__preview-button">{% trans '预览' %}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="rawContent">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
<div class="review-form__fyi">{% trans '不知道什么是Markdown?可以参考' %}<a target="_blank" href="https://www.markdownguide.org/">{% trans '这里' %}</a></div>
|
||||
<div class="review-form__option">
|
||||
<div class="review-form__visibility-radio">
|
||||
|
||||
{{ form.is_private.label }}{{ form.is_private }}
|
||||
</div>
|
||||
<div class="review-form__share-checkbox">
|
||||
{{ form.share_to_mastodon }}{{ form.share_to_mastodon.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix">
|
||||
<input class="button float-right" type="submit" value="{% trans '提交' %}">
|
||||
</div>
|
||||
{{ form.media }}
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
105
games/templates/games/delete.html
Normal file
105
games/templates/games/delete.html
Normal file
|
@ -0,0 +1,105 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 删除电影/剧集' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<h5>{% trans '确认删除这个游戏吗?相关评论和标记将一并删除。' %}</h5>
|
||||
|
||||
<div class="entity-card entity-card--horizontal">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'games:retrieve' game.id %}">
|
||||
<img src="{{ game.cover|thumb:'normal' }}" alt="" class="item-image float-left">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper entity-card__info-wrapper--horizontal">
|
||||
<a href="{% url 'games:retrieve' game.id %}">
|
||||
<h5 class="entity-card__title">
|
||||
{{ game.title }}
|
||||
<a href="{{ game.source_url }}"><span class="source-label source-label__{{ game.source_site }}">{{ game.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
</a>
|
||||
{% if game.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ game.rating | floatformat:"0" }}">
|
||||
</span>
|
||||
<span class="entity-card__rating-score">{{ game.rating }}</span>
|
||||
{% else %}
|
||||
<span>{% trans '评分:暂无评分' %}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if game.last_editor %}
|
||||
<div>
|
||||
{% trans '最近编辑者:' %}
|
||||
<a href="{% url 'users:home' game.last_editor.id %}">
|
||||
<span>{{ game.last_editor | default:"" }}</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div>{% trans '上次编辑时间:' %}{{ game.edited_time }}</div>
|
||||
|
||||
{% if game.game_marks.all %}
|
||||
<div><strong>{% trans '这个条目有' %} <a href="javascript:void();">{{ game.game_marks.count }}</a> 个标记</strong></div>
|
||||
{% endif %}
|
||||
{% if game.game_reviews.all %}
|
||||
<div><strong>{% trans '这个条目有' %} <a href="javascript:void();">{{ game.game_reviews.count }}</a> 个评论</strong></div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
<div class="clearfix">
|
||||
<form action="{% url 'games:delete' game.id %}" method="post" class="float-right">
|
||||
{% csrf_token %}
|
||||
<input class="button" type="submit" value="{% trans '确认' %}">
|
||||
</form>
|
||||
<button onclick="history.back()" class="button button-clear float-right">{% trans '返回' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
107
games/templates/games/delete_review.html
Normal file
107
games/templates/games/delete_review.html
Normal file
|
@ -0,0 +1,107 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 删除评论' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="single-section-wrapper" id="main">
|
||||
<h5>{% trans '确认删除这篇评论吗?' %}</h5>
|
||||
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
|
||||
<div class="review-head">
|
||||
|
||||
<h5 class="review-head__title">
|
||||
{{ review.title }}
|
||||
</h5>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
|
||||
<div class="review-head__body">
|
||||
<div class="review-head__info">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}"
|
||||
class="review-head__owner-link">{{ review.owner.username }}</a>
|
||||
|
||||
{% if mark %}
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="review-head__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<span class="review-head__time">{{ review.edited_time }}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div id="rawContent" class="delete-preview">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
{{ form.media }}
|
||||
|
||||
<div class="dividing-line"></div>
|
||||
|
||||
<div class="clearfix">
|
||||
<form action="{% url 'games:delete_review' review.id %}" method="post" class="float-right">
|
||||
{% csrf_token %}
|
||||
<input class="button" type="submit" value="{% trans '确认' %}">
|
||||
</form>
|
||||
<button onclick="history.back()"
|
||||
class="button button-clear float-right">{% trans '返回' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
$(".markdownx textarea").hide();
|
||||
$(".markdownx .markdownx-preview").show();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
420
games/templates/games/detail.html
Normal file
420
games/templates/games/detail.html
Normal file
|
@ -0,0 +1,420 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load strip_scheme %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="NiceDB游戏 - {{ game.title }}">
|
||||
<meta property="og:type" content="game">
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{{ game.cover.url }}">
|
||||
<meta property="og:site_name" content="NiceDB">
|
||||
<meta property="og:description"content="{{ game.brief }}">
|
||||
|
||||
<title>{% trans 'NiceDB - 游戏详情' %} | {{ game.title }}</title>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/detail.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-detail">
|
||||
|
||||
<a href="{{ game.cover.url }}" class="entity-detail__img-origin" target="_blank" title="{% trans '查看原图' %}">
|
||||
<img src="{{ game.cover|thumb:'normal' }}" class="entity-detail__img" alt="{{ game.title }}">
|
||||
</a>
|
||||
|
||||
<div class="entity-detail__info">
|
||||
<h5 class="entity-detail__title">
|
||||
{{ game.title }}
|
||||
<a href="{{ game.source_url }}"><span class="source-label source-label__{{ game.source_site }}">{{ game.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div class="entity-detail__fields">
|
||||
<div class="entity-detail__rating">
|
||||
{% if game.rating %}
|
||||
<span class="entity-detail__rating-star rating-star" data-rating-score="{{ game.rating | floatformat:"0" }}"></span>
|
||||
<span class="entity-detail__rating-score"> {{ game.rating }} </span>
|
||||
{% else %}
|
||||
<span> {% trans '评分:暂无评分' %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if game.other_title %}{% trans '别名:' %}
|
||||
{% for other_title in game.other_title %}
|
||||
<span {% if forloop.counter > 5 %}style="display: none;" {% endif %}>
|
||||
<span class="other_title">{{ other_title }}</span>
|
||||
{% if not forloop.last %} / {% endif %}
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if game.other_title|length > 5 %}
|
||||
<a href="javascript:void(0);" id="otherTitleMore">{% trans '更多' %}</a>
|
||||
<script>
|
||||
$("#otherTitleMore").click(function (e) {
|
||||
$("span.other_title:not(:visible)").each(function (e) {
|
||||
$(this).parent().removeAttr('style');
|
||||
});
|
||||
$(this).remove();
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.genre %}{% trans '类型:' %}
|
||||
{% for genre in game.genre %}
|
||||
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.developer %}{% trans '开发商:' %}
|
||||
{% for developer in game.developer %}
|
||||
<span>{{ developer }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.publisher %}{% trans '发行商:' %}
|
||||
{% for publisher in game.publisher %}
|
||||
<span>{{ publisher }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="entity-detail__fields">
|
||||
<div>{% if game.release_date %}
|
||||
{% trans '发行日期:' %}{{ game.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{% if game.platform %}{% trans '平台:' %}
|
||||
{% for platform in game.platform %}
|
||||
<span>{{ platform }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if game.other_info %}
|
||||
{% for k, v in game.other_info.items %}
|
||||
<div>
|
||||
{{ k }}:{{ v | urlize }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if game.last_editor %}
|
||||
<div>{% trans '最近编辑者:' %}<a href="{% url 'users:home' game.last_editor.id %}">{{ game.last_editor | default:"" }}</a></div>
|
||||
{% endif %}
|
||||
|
||||
<div>
|
||||
<a href="{% url 'games:update' game.id %}">{% trans '编辑这个游戏' %}</a>
|
||||
|
||||
{% if user.is_staff %}
|
||||
/<a href="{% url 'games:delete' game.id %}"> {% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tag-collection">
|
||||
|
||||
{% for tag_dict in game_tag_list %}
|
||||
{% for k, v in tag_dict.items %}
|
||||
{% if k == 'content' %}
|
||||
<span class="tag-collection__tag">
|
||||
<a href="{% url 'common:search' %}?tag={{ v }}">{{ v }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dividing-line"></div>
|
||||
{% if game.brief %}
|
||||
<div class="entity-desc" id="description">
|
||||
<h5 class="entity-desc__title">{% trans '简介' %}</h5>
|
||||
|
||||
<p class="entity-desc__content">{{ game.brief | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div class="entity-marks">
|
||||
|
||||
<h5 class="entity-marks__title">{% trans '这个游戏的标记' %}</h5>
|
||||
{% if mark_list_more %}
|
||||
<a href="{% url 'games:retrieve_mark_list' game.id %}" class="entity-marks__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
{% if mark_list %}
|
||||
<ul class="entity-marks__mark-list">
|
||||
{% for others_mark in mark_list %}
|
||||
<li class="entity-marks__mark">
|
||||
<a href="{% url 'users:home' others_mark.owner.id %}" class="entity-marks__owner-link">{{ others_mark.owner.username }}</a>
|
||||
<span>{{ others_mark.get_status_display }}</span>
|
||||
{% if others_mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star" data-rating-score="{{ others_mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% if others_mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{{ others_mark.edited_time }}</span>
|
||||
{% if others_mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ others_mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div>{% trans '暂无标记' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="entity-reviews">
|
||||
<h5 class="entity-reviews__title">{% trans '这个游戏的评论' %}</h5>
|
||||
|
||||
{% if review_list_more %}
|
||||
<a href="{% url 'games:retrieve_review_list' game.id %}" class="entity-reviews__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
{% if review_list %}
|
||||
<ul class="entity-reviews__review-list">
|
||||
{% for others_review in review_list %}
|
||||
<li class="entity-reviews__review">
|
||||
<a href="{% url 'users:home' others_review.owner.id %}" class="entity-reviews__owner-link">{{ others_review.owner.username }}</a>
|
||||
{% if others_review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-reviews__review-time">{{ others_review.edited_time }}</span>
|
||||
<span class="entity-reviews__review-title"> <a href="{% url 'games:retrieve_review' others_review.id %}">{{ others_review.title }}</a></span>
|
||||
<span>{{ others_review.get_plain_content | truncate:100 }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div>{% trans '暂无评论' %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
|
||||
{% if mark %}
|
||||
<div class="mark-panel">
|
||||
|
||||
<span class="mark-panel__status">{% trans '我' %}{{ mark.get_status_display }}</span>
|
||||
{% if mark.status == status_enum.DO.value or mark.status == status_enum.COLLECT.value%}
|
||||
{% if mark.rating %}
|
||||
<span class="mark-panel__rating-star rating-star" data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="mark-panel__actions">
|
||||
<a href="" class="edit">{% trans '修改' %}</a>
|
||||
<form action="{% url 'games:delete_mark' mark.id %}" method="post">
|
||||
{% csrf_token %}
|
||||
<a href="" class="delete">{% trans '删除' %}</a>
|
||||
</form>
|
||||
</span>
|
||||
<div class="mark-panel__clear"></div>
|
||||
|
||||
<div class="mark-panel__time">{{ mark.edited_time }}</div>
|
||||
|
||||
{% if mark.text %}
|
||||
<p class="mark-panel__text">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
<div class="tag-collection">
|
||||
|
||||
{% for tag in mark_tags %}
|
||||
<span class="tag-collection__tag">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="action-panel" id="addMarkPanel">
|
||||
|
||||
<div class="action-panel__label">{% trans '标记这个游戏' %}</div>
|
||||
<div class="action-panel__button-group">
|
||||
<button class="action-panel__button" data-status="{{ status_enum.WISH.value }}" id="wishButton">{% trans '想玩' %}</button>
|
||||
<button class="action-panel__button" data-status="{{ status_enum.DO.value }}">{% trans '在玩' %}</button>
|
||||
<button class="action-panel__button" data-status="{{ status_enum.COLLECT.value }}">{% trans '玩过' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="aside-section-wrapper">
|
||||
{% if review %}
|
||||
<div class="review-panel">
|
||||
|
||||
<span class="review-panel__label">{% trans '我的评论' %}</span>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
|
||||
<span class="review-panel__actions">
|
||||
<a href="{% url 'games:update_review' review.id %}">{% trans '编辑' %}</a>
|
||||
<a href="{% url 'games:delete_review' review.id %}">{% trans '删除' %}</a>
|
||||
</span>
|
||||
|
||||
<div class="review-panel__time">{{ review.edited_time }}</div>
|
||||
|
||||
<a href="{% url 'games:retrieve_review' review.id %}" class="review-panel__review-title">
|
||||
{{ review.title }}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
<div class="action-panel">
|
||||
<div class="action-panel__label">{% trans '我的评论' %}</div>
|
||||
<div class="action-panel__button-group action-panel__button-group--center">
|
||||
|
||||
<a href="{% url 'games:create_review' game.id %}">
|
||||
<button class="action-panel__button">{% trans '去写评论' %}</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
<div id="modals">
|
||||
<div class="mark-modal modal">
|
||||
<div class="mark-modal__head">
|
||||
|
||||
{% if not mark %}
|
||||
|
||||
<style>
|
||||
.mark-modal__title::after {
|
||||
content: "{% trans '这个游戏' %}";
|
||||
}
|
||||
</style>
|
||||
|
||||
<span class="mark-modal__title"></span>
|
||||
{% else %}
|
||||
<span class="mark-modal__title">{% trans '我的标记' %}</span>
|
||||
{% endif %}
|
||||
|
||||
<span class="mark-modal__close-button modal-close">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mark-modal__body">
|
||||
<form action="{% url 'games:create_update_mark' %}" method="post">
|
||||
{{ mark_form.media }}
|
||||
{% csrf_token %}
|
||||
{{ mark_form.id }}
|
||||
{{ mark_form.game }}
|
||||
{% if mark.rating %}
|
||||
{% endif %}
|
||||
|
||||
<div class="mark-modal__rating-star rating-star-edit"></div>
|
||||
{{ mark_form.rating }}
|
||||
<div id="statusSelection" class="mark-modal__status-radio" {% if not mark %}hidden{% endif %}>
|
||||
{{ mark_form.status }}
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__clear"></div>
|
||||
|
||||
{{ mark_form.text }}
|
||||
|
||||
<div class="mark-modal__tag">
|
||||
<label>{{ mark_form.tags.label }}</label>
|
||||
{{ mark_form.tags }}
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>{{ mark_form.is_private.label }}:</span>
|
||||
{{ mark_form.is_private }}
|
||||
</div>
|
||||
<div class="mark-modal__share-checkbox">
|
||||
{{ mark_form.share_to_mastodon }}{{ mark_form.share_to_mastodon.label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mark-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="{% trans '提交' %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="confirm-modal modal">
|
||||
<div class="confirm-modal__head">
|
||||
<span class="confirm-modal__title">{% trans '确定要删除你的标记吗?' %}</span>
|
||||
<span class="confirm-modal__close-button modal-close">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="confirm-modal__body">
|
||||
<div class="confirm-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="{% trans '确认' %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-mask"></div>
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
165
games/templates/games/mark_list.html
Normal file
165
games/templates/games/mark_list.html
Normal file
|
@ -0,0 +1,165 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ game.title }}{% trans '的标记' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-marks">
|
||||
<h5 class="entity-marks__title entity-marks__title--stand-alone">
|
||||
<a href="{% url 'games:retrieve' game.id %}">{{ game.title }}</a>{% trans ' 的标记' %}
|
||||
</h5>
|
||||
<ul class="entity-marks__mark-list">
|
||||
|
||||
{% for mark in marks %}
|
||||
|
||||
<li class="entity-marks__mark entity-marks__mark--wider">
|
||||
<a href="{% url 'users:home' mark.owner.id %}"
|
||||
class="entity-marks__owner-link">{{ mark.owner.username }}</a>
|
||||
<span>{{ mark.get_status_display }}</span>
|
||||
{% if mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{{ mark.edited_time }}</span>
|
||||
{% if mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% empty %}
|
||||
<div>
|
||||
{% trans '无结果' %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if marks.pagination.has_prev %}
|
||||
<a href="?page=1"
|
||||
class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ marks.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in marks.pagination.page_range %}
|
||||
|
||||
{% if page == marks.pagination.current_page %}
|
||||
<a href="?page={{ page }}"
|
||||
class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}"
|
||||
class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if marks.pagination.has_next %}
|
||||
<a href="?page={{ marks.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ marks.pagination.last_page }}"
|
||||
class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'games:retrieve' game.id %}"><img src="{{ game.cover|thumb:'normal' }}" alt="" class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'games:retrieve' game.id %}">
|
||||
{{ game.title }}
|
||||
</a>
|
||||
<a href="{{ game.source_url }}"><span class="source-label source-label__{{ game.source_site }}">{{ game.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>
|
||||
{% if game.genre %}{% trans '类型:' %}
|
||||
{% for genre in game.genre %}
|
||||
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.developer %}{% trans '开发商:' %}
|
||||
{% for developer in game.developer %}
|
||||
<span>{{ developer }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if game.release_date %}
|
||||
{% trans '发行日期:' %}{{ game.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if game.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star" data-rating-score="{{ game.rating | floatformat:'0'}}"> </span>
|
||||
<span class="entity-card__rating-score rating-score"> {{ game.rating }} </span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
152
games/templates/games/review_detail.html
Normal file
152
games/templates/games/review_detail.html
Normal file
|
@ -0,0 +1,152 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta property="og:title" content="NiceDB影评 - {{ review.title }}">
|
||||
<meta property="og:type" content="article">
|
||||
<meta property="og:article:author" content="{{ review.owner.username }}">
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}">
|
||||
<meta property="og:image" content="{{ request.scheme }}://{{ request.get_host }}{% static 'img/logo_square.svg' %}">
|
||||
<title>{% trans 'NiceDB - 评论详情' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="review-head">
|
||||
<h5 class="review-head__title">
|
||||
{{ review.title }}
|
||||
</h5>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<div class="review-head__body">
|
||||
<div class="review-head__info">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}" class="review-head__owner-link">{{ review.owner.username }}</a>
|
||||
|
||||
{% if mark %}
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="review-head__rating-star rating-star" data-rating-score="{{ mark.rating | floatformat:"0" }}"></span>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
<span class="review-head__time">{{ review.edited_time }}</span>
|
||||
|
||||
</div>
|
||||
<div class="review-head__actions">
|
||||
{% if request.user == review.owner %}
|
||||
<a class="review-head__action-link" href="{% url 'games:update_review' review.id %}">{% trans '编辑' %}</a>
|
||||
<a class="review-head__action-link" href="{% url 'games:delete_review' review.id %}">{% trans '删除' %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="dividing-line"></div> -->
|
||||
<div id="rawContent">
|
||||
{{ form.content }}
|
||||
</div>
|
||||
{{ form.media }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'games:retrieve' game.id %}"><img src="{{ game.cover|thumb:'normal' }}" alt=""
|
||||
class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title">
|
||||
<a href="{% url 'games:retrieve' game.id %}">
|
||||
{{ game.title }}
|
||||
</a>
|
||||
<a href="{{ game.source_url }}">
|
||||
<span class="source-label source-label__{{ game.source_site }}">
|
||||
{{ game.get_source_site_display }}
|
||||
</span>
|
||||
</a>
|
||||
</h5>
|
||||
|
||||
<div>
|
||||
{% if game.genre %}{% trans '类型:' %}
|
||||
{% for genre in game.genre %}
|
||||
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.developer %}{% trans '开发商:' %}
|
||||
{% for developer in game.developer %}
|
||||
<span>{{ developer }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if game.release_date %}
|
||||
{% trans '发行日期:' %}{{ game.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if game.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ game.rating | floatformat:'0'}}"> </span>
|
||||
<span class="entity-card__rating-score rating-score"> {{ game.rating }} </span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
$(".markdownx textarea").hide();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
153
games/templates/games/review_list.html
Normal file
153
games/templates/games/review_list.html
Normal file
|
@ -0,0 +1,153 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load highlight %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ game.title }}{% trans '的评论' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid">
|
||||
<div class="grid__main" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-reviews">
|
||||
<h5 class="entity-reviews__title entity-reviews__title--stand-alone">
|
||||
<a href="{% url 'games:retrieve' game.id %}">{{ game.title }}</a>{% trans ' 的评论' %}
|
||||
</h5>
|
||||
<ul class="entity-reviews__review-list">
|
||||
|
||||
{% for review in reviews %}
|
||||
|
||||
<li class="entity-reviews__review entity-reviews__review--wider">
|
||||
|
||||
<a href="{% url 'users:home' review.owner.id %}" class="entity-reviews__owner-link">{{ review.owner.username }}</a>
|
||||
{% if review.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z"/></svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-reviews__review-time">{{ review.edited_time }}</span>
|
||||
|
||||
|
||||
<span href="{% url 'games:retrieve_review' review.id %}" class="entity-reviews__review-title"><a href="{% url 'games:retrieve_review' review.id %}">{{ review.title }}</a></span>
|
||||
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>{% trans '无结果' %}</div>
|
||||
{% endfor %}
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if reviews.pagination.has_prev %}
|
||||
<a href="?page=1" class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ reviews.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in reviews.pagination.page_range %}
|
||||
|
||||
{% if page == reviews.pagination.current_page %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if reviews.pagination.has_next %}
|
||||
<a href="?page={{ reviews.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ reviews.pagination.last_page }}" class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside" id="aside">
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="entity-card">
|
||||
<div class="entity-card__img-wrapper">
|
||||
<a href="{% url 'games:retrieve' game.id %}"><img src="{{ game.cover|thumb:'normal' }}" alt=""
|
||||
class="entity-card__img"></a>
|
||||
</div>
|
||||
<div class="entity-card__info-wrapper">
|
||||
<h5 class="entity-card__title"><a href="{% url 'games:retrieve' game.id %}">
|
||||
{{ game.title }}
|
||||
</a>
|
||||
<a href="{{ game.source_url }}"><span class="source-label source-label__{{ game.source_site }}">{{ game.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
|
||||
<div>
|
||||
{% if game.genre %}{% trans '类型:' %}
|
||||
{% for genre in game.genre %}
|
||||
<span>{{ genre }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if game.developer %}{% trans '开发商:' %}
|
||||
{% for developer in game.developer %}
|
||||
<span>{{ developer }}</span>{% if not forloop.last %} / {% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>{% if game.release_date %}
|
||||
{% trans '发行日期:' %}{{ game.release_date }}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if game.rating %}
|
||||
{% trans '评分:' %}<span class="entity-card__rating-star rating-star"
|
||||
data-rating-score="{{ game.rating | floatformat:'0'}}"> </span>
|
||||
<span class="entity-card__rating-score rating-score"> {{ game.rating }} </span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
{% comment %}
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% endcomment %}
|
||||
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
112
games/templates/games/scrape.html
Normal file
112
games/templates/games/scrape.html
Normal file
|
@ -0,0 +1,112 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - 从豆瓣获取数据' %}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'js/scrape.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
#scrape {
|
||||
overflow: auto;
|
||||
}
|
||||
#scrape iframe {
|
||||
width: 100%;
|
||||
}
|
||||
#scrape textarea {
|
||||
height: 200px;
|
||||
resize: vertical;
|
||||
}
|
||||
#scrape iframe {
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content">
|
||||
<div class="grid grid--reverse-order">
|
||||
<div class="grid__main grid__main--reverse-order" id="main">
|
||||
<div class="main-section-wrapper">
|
||||
<div id="scrape">
|
||||
<h5>
|
||||
{% trans '根据豆瓣内容填写下方表单' %}
|
||||
</h5>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts" src="https://www.douban.com/game/explore/?genres=&platforms=&sort=rating&q={% if q %}{{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<div class="dividing-line"></div>
|
||||
<div id="scrapeForm">
|
||||
<form action="{% url 'games:create' %}" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form.media }}
|
||||
|
||||
{% for field in form %}
|
||||
|
||||
{% if field.name == 'release_date' %}
|
||||
{{ field.label_tag }}
|
||||
|
||||
<input type="date" name="{{ field.name }}" id="{{ field.id_for_label }}" value="{{ form.instance.release_date | date:"
|
||||
Y-m-d" }}">
|
||||
|
||||
{% else %}
|
||||
{% if field.name != 'id' %}
|
||||
{{ field.label_tag }}
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</form>
|
||||
<a href="#" class="button add-button submit">{% trans '剽取!' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside grid__aside--reverse-order" id="aside">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--singular">
|
||||
<h5>
|
||||
{% trans '复制详情页链接' %}
|
||||
</h5>
|
||||
<form action="{% url 'games:click_to_scrape' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="text" name="url" required placeholder="https://game.douban.com/subject/1000000/">
|
||||
<input type="submit" class="button add-button" value="{% trans '一键剽取!' %}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
// mark required
|
||||
$("#content *[required]").each(function () {
|
||||
$(this).prev().prepend("*");
|
||||
});
|
||||
$('form').submit(function () {
|
||||
$(this).find("input[type='submit']").prop('disabled', true);
|
||||
$(this).find("button[type='submit']").prop('disabled', true);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
3
games/tests.py
Normal file
3
games/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
23
games/urls.py
Normal file
23
games/urls.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
|
||||
app_name = 'games'
|
||||
urlpatterns = [
|
||||
path('create/', create, name='create'),
|
||||
path('<int:id>/', retrieve, name='retrieve'),
|
||||
path('update/<int:id>/', update, name='update'),
|
||||
path('delete/<int:id>/', delete, name='delete'),
|
||||
path('mark/', create_update_mark, name='create_update_mark'),
|
||||
path('<int:game_id>/mark/list/',
|
||||
retrieve_mark_list, name='retrieve_mark_list'),
|
||||
path('mark/delete/<int:id>/', delete_mark, name='delete_mark'),
|
||||
path('<int:game_id>/review/create/', create_review, name='create_review'),
|
||||
path('review/update/<int:id>/', update_review, name='update_review'),
|
||||
path('review/delete/<int:id>/', delete_review, name='delete_review'),
|
||||
path('review/<int:id>/', retrieve_review, name='retrieve_review'),
|
||||
path('<int:game_id>/review/list/',
|
||||
retrieve_review_list, name='retrieve_review_list'),
|
||||
path('scrape/', scrape, name='scrape'),
|
||||
path('click_to_scrape/', click_to_scrape, name='click_to_scrape'),
|
||||
]
|
591
games/views.py
Normal file
591
games/views.py
Normal file
|
@ -0,0 +1,591 @@
|
|||
import logging
|
||||
from django.shortcuts import render, get_object_or_404, redirect, reverse
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.http import HttpResponseBadRequest, HttpResponseServerError
|
||||
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import Count
|
||||
from django.utils import timezone
|
||||
from django.core.paginator import Paginator
|
||||
from mastodon import mastodon_request_included
|
||||
from mastodon.api import check_visibility, post_toot, TootVisibilityEnum
|
||||
from mastodon.utils import rating_to_emoji
|
||||
from common.utils import PageLinksGenerator
|
||||
from common.views import PAGE_LINK_NUMBER, jump_or_scrape
|
||||
from common.models import SourceSiteEnum
|
||||
from .models import *
|
||||
from .forms import *
|
||||
from boofilsic.settings import MASTODON_TAGS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
mastodon_logger = logging.getLogger("django.mastodon")
|
||||
|
||||
|
||||
# how many marks showed on the detail page
|
||||
MARK_NUMBER = 5
|
||||
# how many marks at the mark page
|
||||
MARK_PER_PAGE = 20
|
||||
# how many reviews showed on the detail page
|
||||
REVIEW_NUMBER = 5
|
||||
# how many reviews at the mark page
|
||||
REVIEW_PER_PAGE = 20
|
||||
# max tags on detail page
|
||||
TAG_NUMBER = 10
|
||||
|
||||
|
||||
# public data
|
||||
###########################
|
||||
@login_required
|
||||
def create(request):
|
||||
if request.method == 'GET':
|
||||
form = GameForm()
|
||||
return render(
|
||||
request,
|
||||
'games/create_update.html',
|
||||
{
|
||||
'form': form,
|
||||
'title': _('添加游戏'),
|
||||
'submit_url': reverse("games:create"),
|
||||
# provided for frontend js
|
||||
'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
|
||||
}
|
||||
)
|
||||
elif request.method == 'POST':
|
||||
if request.user.is_authenticated:
|
||||
# only local user can alter public data
|
||||
form = GameForm(request.POST, request.FILES)
|
||||
if form.is_valid():
|
||||
form.instance.last_editor = request.user
|
||||
try:
|
||||
with transaction.atomic():
|
||||
form.save()
|
||||
if form.instance.source_site == SourceSiteEnum.IN_SITE.value:
|
||||
real_url = form.instance.get_absolute_url()
|
||||
form.instance.source_url = real_url
|
||||
form.instance.save()
|
||||
except IntegrityError as e:
|
||||
logger.error(e.__str__())
|
||||
return HttpResponseServerError("integrity error")
|
||||
return redirect(reverse("games:retrieve", args=[form.instance.id]))
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
'games/create_update.html',
|
||||
{
|
||||
'form': form,
|
||||
'title': _('添加游戏'),
|
||||
'submit_url': reverse("games:create"),
|
||||
# provided for frontend js
|
||||
'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return redirect(reverse("users:login"))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@login_required
|
||||
def update(request, id):
|
||||
if request.method == 'GET':
|
||||
game = get_object_or_404(Game, pk=id)
|
||||
form = GameForm(instance=game)
|
||||
page_title = _('修改游戏')
|
||||
return render(
|
||||
request,
|
||||
'games/create_update.html',
|
||||
{
|
||||
'form': form,
|
||||
'title': page_title,
|
||||
'submit_url': reverse("games:update", args=[game.id]),
|
||||
# provided for frontend js
|
||||
'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
|
||||
}
|
||||
)
|
||||
elif request.method == 'POST':
|
||||
game = get_object_or_404(Game, pk=id)
|
||||
form = GameForm(request.POST, request.FILES, instance=game)
|
||||
page_title = _("修改游戏")
|
||||
if form.is_valid():
|
||||
form.instance.last_editor = request.user
|
||||
form.instance.edited_time = timezone.now()
|
||||
try:
|
||||
with transaction.atomic():
|
||||
form.save()
|
||||
if form.instance.source_site == SourceSiteEnum.IN_SITE.value:
|
||||
real_url = form.instance.get_absolute_url()
|
||||
form.instance.source_url = real_url
|
||||
form.instance.save()
|
||||
except IntegrityError as e:
|
||||
logger.error(e.__str__())
|
||||
return HttpResponseServerError("integrity error")
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
'games/create_update.html',
|
||||
{
|
||||
'form': form,
|
||||
'title': page_title,
|
||||
'submit_url': reverse("games:update", args=[game.id]),
|
||||
# provided for frontend js
|
||||
'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
|
||||
}
|
||||
)
|
||||
return redirect(reverse("games:retrieve", args=[form.instance.id]))
|
||||
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
# @login_required
|
||||
def retrieve(request, id):
|
||||
if request.method == 'GET':
|
||||
game = get_object_or_404(Game, pk=id)
|
||||
mark = None
|
||||
mark_tags = None
|
||||
review = None
|
||||
|
||||
# retreive tags
|
||||
game_tag_list = game.game_tags.values('content').annotate(
|
||||
tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER]
|
||||
|
||||
# retrieve user mark and initialize mark form
|
||||
try:
|
||||
if request.user.is_authenticated:
|
||||
mark = GameMark.objects.get(owner=request.user, game=game)
|
||||
except ObjectDoesNotExist:
|
||||
mark = None
|
||||
if mark:
|
||||
mark_tags = mark.gamemark_tags.all()
|
||||
mark.get_status_display = GameMarkStatusTranslator(mark.status)
|
||||
mark_form = GameMarkForm(instance=mark, initial={
|
||||
'tags': mark_tags
|
||||
})
|
||||
else:
|
||||
mark_form = GameMarkForm(initial={
|
||||
'game': game,
|
||||
'tags': mark_tags
|
||||
})
|
||||
|
||||
# retrieve user review
|
||||
try:
|
||||
if request.user.is_authenticated:
|
||||
review = GameReview.objects.get(
|
||||
owner=request.user, game=game)
|
||||
except ObjectDoesNotExist:
|
||||
review = None
|
||||
|
||||
# retrieve other related reviews and marks
|
||||
if request.user.is_anonymous:
|
||||
# hide all marks and reviews for anonymous user
|
||||
mark_list = None
|
||||
review_list = None
|
||||
mark_list_more = None
|
||||
review_list_more = None
|
||||
else:
|
||||
mark_list = GameMark.get_available(
|
||||
game, request.user, request.session['oauth_token'])
|
||||
review_list = GameReview.get_available(
|
||||
game, request.user, request.session['oauth_token'])
|
||||
mark_list_more = True if len(mark_list) > MARK_NUMBER else False
|
||||
mark_list = mark_list[:MARK_NUMBER]
|
||||
for m in mark_list:
|
||||
m.get_status_display = GameMarkStatusTranslator(m.status)
|
||||
review_list_more = True if len(
|
||||
review_list) > REVIEW_NUMBER else False
|
||||
review_list = review_list[:REVIEW_NUMBER]
|
||||
|
||||
# def strip_html_tags(text):
|
||||
# import re
|
||||
# regex = re.compile('<.*?>')
|
||||
# return re.sub(regex, '', text)
|
||||
|
||||
# for r in review_list:
|
||||
# r.content = strip_html_tags(r.content)
|
||||
|
||||
return render(
|
||||
request,
|
||||
'games/detail.html',
|
||||
{
|
||||
'game': game,
|
||||
'mark': mark,
|
||||
'review': review,
|
||||
'status_enum': MarkStatusEnum,
|
||||
'mark_form': mark_form,
|
||||
'mark_list': mark_list,
|
||||
'mark_list_more': mark_list_more,
|
||||
'review_list': review_list,
|
||||
'review_list_more': review_list_more,
|
||||
'game_tag_list': game_tag_list,
|
||||
'mark_tags': mark_tags,
|
||||
}
|
||||
)
|
||||
else:
|
||||
logger.warning('non-GET method at /games/<id>')
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@permission_required("games.delete_game")
|
||||
@login_required
|
||||
def delete(request, id):
|
||||
if request.method == 'GET':
|
||||
game = get_object_or_404(Game, pk=id)
|
||||
return render(
|
||||
request,
|
||||
'games/delete.html',
|
||||
{
|
||||
'game': game,
|
||||
}
|
||||
)
|
||||
elif request.method == 'POST':
|
||||
if request.user.is_staff:
|
||||
# only staff has right to delete
|
||||
game = get_object_or_404(Game, pk=id)
|
||||
game.delete()
|
||||
return redirect(reverse("common:home"))
|
||||
else:
|
||||
raise PermissionDenied()
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
# user owned entites
|
||||
###########################
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def create_update_mark(request):
|
||||
# check list:
|
||||
# clean rating if is wish
|
||||
# transaction on updating game rating
|
||||
# owner check(guarantee)
|
||||
if request.method == 'POST':
|
||||
pk = request.POST.get('id')
|
||||
old_rating = None
|
||||
old_tags = None
|
||||
if pk:
|
||||
mark = get_object_or_404(GameMark, pk=pk)
|
||||
if request.user != mark.owner:
|
||||
return HttpResponseBadRequest()
|
||||
old_rating = mark.rating
|
||||
old_tags = mark.gamemark_tags.all()
|
||||
# update
|
||||
form = GameMarkForm(request.POST, instance=mark)
|
||||
else:
|
||||
# create
|
||||
form = GameMarkForm(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
if form.instance.status == MarkStatusEnum.WISH.value:
|
||||
form.instance.rating = None
|
||||
form.cleaned_data['rating'] = None
|
||||
form.instance.owner = request.user
|
||||
form.instance.edited_time = timezone.now()
|
||||
game = form.instance.game
|
||||
|
||||
try:
|
||||
with transaction.atomic():
|
||||
# update game rating
|
||||
game.update_rating(old_rating, form.instance.rating)
|
||||
form.save()
|
||||
# update tags
|
||||
if old_tags:
|
||||
for tag in old_tags:
|
||||
tag.delete()
|
||||
if form.cleaned_data['tags']:
|
||||
for tag in form.cleaned_data['tags']:
|
||||
GameTag.objects.create(
|
||||
content=tag,
|
||||
game=game,
|
||||
mark=form.instance
|
||||
)
|
||||
except IntegrityError as e:
|
||||
logger.error(e.__str__())
|
||||
return HttpResponseServerError("integrity error")
|
||||
|
||||
if form.cleaned_data['share_to_mastodon']:
|
||||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("games:retrieve",
|
||||
args=[game.id])
|
||||
words = GameMarkStatusTranslator(form.cleaned_data['status']) +\
|
||||
f"《{game.title}》" + \
|
||||
rating_to_emoji(form.cleaned_data['rating'])
|
||||
|
||||
# tags = MASTODON_TAGS % {'category': '书', 'type': '标记'}
|
||||
tags = ''
|
||||
content = words + '\n' + url + '\n' + \
|
||||
form.cleaned_data['text'] + '\n' + tags
|
||||
response = post_toot(request.user.mastodon_site, content, visibility,
|
||||
request.session['oauth_token'])
|
||||
if response.status_code != 200:
|
||||
mastodon_logger.error(
|
||||
f"CODE:{response.status_code} {response.text}")
|
||||
return HttpResponseServerError("publishing mastodon status failed")
|
||||
else:
|
||||
return HttpResponseBadRequest("invalid form data")
|
||||
|
||||
return redirect(reverse("games:retrieve", args=[form.instance.game.id]))
|
||||
else:
|
||||
return HttpResponseBadRequest("invalid method")
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def retrieve_mark_list(request, game_id):
|
||||
if request.method == 'GET':
|
||||
game = get_object_or_404(Game, pk=game_id)
|
||||
queryset = GameMark.get_available(
|
||||
game, request.user, request.session['oauth_token'])
|
||||
paginator = Paginator(queryset, MARK_PER_PAGE)
|
||||
page_number = request.GET.get('page', default=1)
|
||||
marks = paginator.get_page(page_number)
|
||||
marks.pagination = PageLinksGenerator(
|
||||
PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
for m in marks:
|
||||
m.get_status_display = GameMarkStatusTranslator(m.status)
|
||||
return render(
|
||||
request,
|
||||
'games/mark_list.html',
|
||||
{
|
||||
'marks': marks,
|
||||
'game': game,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@login_required
|
||||
def delete_mark(request, id):
|
||||
if request.method == 'POST':
|
||||
mark = get_object_or_404(GameMark, pk=id)
|
||||
if request.user != mark.owner:
|
||||
return HttpResponseBadRequest()
|
||||
game_id = mark.game.id
|
||||
try:
|
||||
with transaction.atomic():
|
||||
# update game rating
|
||||
mark.game.update_rating(mark.rating, None)
|
||||
mark.delete()
|
||||
except IntegrityError as e:
|
||||
return HttpResponseServerError()
|
||||
return redirect(reverse("games:retrieve", args=[game_id]))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def create_review(request, game_id):
|
||||
if request.method == 'GET':
|
||||
form = GameReviewForm(initial={'game': game_id})
|
||||
game = get_object_or_404(Game, pk=game_id)
|
||||
return render(
|
||||
request,
|
||||
'games/create_update_review.html',
|
||||
{
|
||||
'form': form,
|
||||
'title': _("添加评论"),
|
||||
'game': game,
|
||||
'submit_url': reverse("games:create_review", args=[game_id]),
|
||||
}
|
||||
)
|
||||
elif request.method == 'POST':
|
||||
form = GameReviewForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.instance.owner = request.user
|
||||
form.save()
|
||||
if form.cleaned_data['share_to_mastodon']:
|
||||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("games:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论"
|
||||
# tags = MASTODON_TAGS % {'category': '书', 'type': '评论'}
|
||||
tags = ''
|
||||
content = words + '\n' + url + \
|
||||
'\n' + form.cleaned_data['title'] + '\n' + tags
|
||||
response = post_toot(request.user.mastodon_site, content, visibility,
|
||||
request.session['oauth_token'])
|
||||
if response.status_code != 200:
|
||||
mastodon_logger.error(
|
||||
f"CODE:{response.status_code} {response.text}")
|
||||
return HttpResponseServerError("publishing mastodon status failed")
|
||||
return redirect(reverse("games:retrieve_review", args=[form.instance.id]))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def update_review(request, id):
|
||||
if request.method == 'GET':
|
||||
review = get_object_or_404(GameReview, pk=id)
|
||||
if request.user != review.owner:
|
||||
return HttpResponseBadRequest()
|
||||
form = GameReviewForm(instance=review)
|
||||
game = review.game
|
||||
return render(
|
||||
request,
|
||||
'games/create_update_review.html',
|
||||
{
|
||||
'form': form,
|
||||
'title': _("编辑评论"),
|
||||
'game': game,
|
||||
'submit_url': reverse("games:update_review", args=[review.id]),
|
||||
}
|
||||
)
|
||||
elif request.method == 'POST':
|
||||
review = get_object_or_404(GameReview, pk=id)
|
||||
if request.user != review.owner:
|
||||
return HttpResponseBadRequest()
|
||||
form = GameReviewForm(request.POST, instance=review)
|
||||
if form.is_valid():
|
||||
form.instance.edited_time = timezone.now()
|
||||
form.save()
|
||||
if form.cleaned_data['share_to_mastodon']:
|
||||
if form.cleaned_data['is_private']:
|
||||
visibility = TootVisibilityEnum.PRIVATE
|
||||
else:
|
||||
visibility = TootVisibilityEnum.UNLISTED
|
||||
url = "https://" + request.get_host() + reverse("games:retrieve_review",
|
||||
args=[form.instance.id])
|
||||
words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论"
|
||||
# tags = MASTODON_TAGS % {'category': '书', 'type': '评论'}
|
||||
tags = ''
|
||||
content = words + '\n' + url + \
|
||||
'\n' + form.cleaned_data['title'] + '\n' + tags
|
||||
response = post_toot(request.user.mastodon_site, content, visibility,
|
||||
request.session['oauth_token'])
|
||||
if response.status_code != 200:
|
||||
mastodon_logger.error(
|
||||
f"CODE:{response.status_code} {response.text}")
|
||||
return HttpResponseServerError("publishing mastodon status failed")
|
||||
return redirect(reverse("games:retrieve_review", args=[form.instance.id]))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@login_required
|
||||
def delete_review(request, id):
|
||||
if request.method == 'GET':
|
||||
review = get_object_or_404(GameReview, pk=id)
|
||||
if request.user != review.owner:
|
||||
return HttpResponseBadRequest()
|
||||
review_form = GameReviewForm(instance=review)
|
||||
return render(
|
||||
request,
|
||||
'games/delete_review.html',
|
||||
{
|
||||
'form': review_form,
|
||||
'review': review,
|
||||
}
|
||||
)
|
||||
elif request.method == 'POST':
|
||||
review = get_object_or_404(GameReview, pk=id)
|
||||
if request.user != review.owner:
|
||||
return HttpResponseBadRequest()
|
||||
game_id = review.game.id
|
||||
review.delete()
|
||||
return redirect(reverse("games:retrieve", args=[game_id]))
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def retrieve_review(request, id):
|
||||
if request.method == 'GET':
|
||||
review = get_object_or_404(GameReview, pk=id)
|
||||
if not check_visibility(review, request.session['oauth_token'], request.user):
|
||||
msg = _("你没有访问这个页面的权限😥")
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': msg,
|
||||
}
|
||||
)
|
||||
review_form = GameReviewForm(instance=review)
|
||||
game = review.game
|
||||
try:
|
||||
mark = GameMark.objects.get(owner=review.owner, game=game)
|
||||
mark.get_status_display = GameMarkStatusTranslator(mark.status)
|
||||
except ObjectDoesNotExist:
|
||||
mark = None
|
||||
return render(
|
||||
request,
|
||||
'games/review_detail.html',
|
||||
{
|
||||
'form': review_form,
|
||||
'review': review,
|
||||
'game': game,
|
||||
'mark': mark,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def retrieve_review_list(request, game_id):
|
||||
if request.method == 'GET':
|
||||
game = get_object_or_404(Game, pk=game_id)
|
||||
queryset = GameReview.get_available(
|
||||
game, request.user, request.session['oauth_token'])
|
||||
paginator = Paginator(queryset, REVIEW_PER_PAGE)
|
||||
page_number = request.GET.get('page', default=1)
|
||||
reviews = paginator.get_page(page_number)
|
||||
reviews.pagination = PageLinksGenerator(
|
||||
PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
return render(
|
||||
request,
|
||||
'games/review_list.html',
|
||||
{
|
||||
'reviews': reviews,
|
||||
'game': game,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@login_required
|
||||
def scrape(request):
|
||||
if request.method == 'GET':
|
||||
keywords = request.GET.get('q')
|
||||
form = GameForm()
|
||||
return render(
|
||||
request,
|
||||
'games/scrape.html',
|
||||
{
|
||||
'q': keywords,
|
||||
'form': form,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@login_required
|
||||
def click_to_scrape(request):
|
||||
if request.method == "POST":
|
||||
url = request.POST.get("url")
|
||||
if url:
|
||||
return jump_or_scrape(request, url)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
|
@ -49,7 +49,7 @@
|
|||
{% if movie.year %}({{ movie.year }}){% endif %}
|
||||
{% endif %}
|
||||
</a>
|
||||
<a href="{{ movie.source_url }}"><a href="{{ movie.source_url }}"><span class="source-label source-label__{{ movie.source_site }}">{{ movie.get_source_site_display }}</span></a></a>
|
||||
<a href="{{ movie.source_url }}"><span class="source-label source-label__{{ movie.source_site }}">{{ movie.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
<div>{% if movie.director %}{% trans '导演:' %}
|
||||
{% for director in movie.director %}
|
||||
|
|
|
@ -190,7 +190,7 @@
|
|||
{% if movie.other_info %}
|
||||
{% for k, v in movie.other_info.items %}
|
||||
<div>
|
||||
{{k}}:{{v}}
|
||||
{{ k }}:{{ v | urlize }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<h5>
|
||||
{% trans '根据豆瓣内容填写下方表单' %}
|
||||
</h5>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://search.douban.com/movie/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts" src="https://search.douban.com/movie/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<div class="dividing-line"></div>
|
||||
<div id="scrapeForm">
|
||||
<form action="{% url 'movies:create' %}" method="POST" enctype="multipart/form-data">
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
{% if album.other_info %}
|
||||
{% for k, v in album.other_info.items %}
|
||||
<div>
|
||||
{{k}}:{{v}}
|
||||
{{ k }}:{{ v | urlize }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_album' album.id %}">
|
||||
{{ album.title }}
|
||||
</a>
|
||||
<a href="{{ album.source_url }}"><a href="{{ album.source_url }}"><span class="source-label source-label__{{ album.source_site }}">{{ album.get_source_site_display }}</span></a></a>
|
||||
<a href="{{ album.source_url }}"><span class="source-label source-label__{{ album.source_site }}">{{ album.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
<div>{% if album.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in album.artist %}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
<h5 class="entity-card__title"><a href="{% url 'music:retrieve_song' song.id %}">
|
||||
{{ song.title }}
|
||||
</a>
|
||||
<a href="{{ song.source_url }}"><a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a></a>
|
||||
<a href="{{ song.source_url }}"><span class="source-label source-label__{{ song.source_site }}">{{ song.get_source_site_display }}</span></a>
|
||||
</h5>
|
||||
<div>{% if song.artist %}{% trans '艺术家:' %}
|
||||
{% for artist in song.artist %}
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<h5>
|
||||
{% trans '根据豆瓣内容填写下方表单' %}
|
||||
</h5>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts allow-popups allow-forms" src="https://search.douban.com/music/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<iframe id='test' sandbox="allow-same-origin allow-scripts" src="https://search.douban.com/music/subject_search{% if q %}?search_text={{ q }}{% endif %}" frameborder="0"></iframe>
|
||||
<div class="dividing-line"></div>
|
||||
<div id="scrapeForm">
|
||||
<form action="{% url 'music:create_album' %}" method="POST" enctype="multipart/form-data">
|
||||
|
@ -54,14 +54,17 @@
|
|||
|
||||
{% for field in form %}
|
||||
|
||||
{% if field.id_for_label == 'id_is_series' %}
|
||||
<label for="{{ field.id_for_label }}" style="display: inline-block; position: relative; left: -4px;">{{ field.label }}</label>
|
||||
{{ field }}
|
||||
{% if field.name == 'release_date' %}
|
||||
{{ field.label_tag }}
|
||||
|
||||
<input type="date" name="{{ field.name }}" id="{{ field.id_for_label }}" value="{{ form.instance.release_date | date:"
|
||||
Y-m-d" }}">
|
||||
|
||||
{% else %}
|
||||
{% if field.id_for_label != 'id_id' %}
|
||||
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% if field.name != 'id' %}
|
||||
{{ field.label_tag }}
|
||||
{% endif %}
|
||||
{{ field }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
|
|
@ -107,7 +107,7 @@
|
|||
{% if song.other_info %}
|
||||
{% for k, v in song.other_info.items %}
|
||||
<div>
|
||||
{{k}}:{{v}}
|
||||
{{ k }}:{{ v | urlize }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
|
@ -95,7 +95,7 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
<p class="entity-list__entity-brief">
|
||||
{{ mark.book.brief | truncate:170 }}
|
||||
{{ mark.book.brief }}
|
||||
</p>
|
||||
<div class="tag-collection">
|
||||
{% for tag_dict in mark.book.tag_list %}
|
||||
|
|
272
users/templates/users/game_list.html
Normal file
272
users/templates/users/game_list.html
Normal file
|
@ -0,0 +1,272 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans 'NiceDB - ' %}{{ user.username }}{{ list_title }}</title>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||
<script src="{% static 'lib/js/rating-star.js' %}"></script>
|
||||
<script src="{% static 'js/rating-star-readonly.js' %}"></script>
|
||||
<script src="{% static 'js/mastodon.js' %}"></script>
|
||||
<script src="{% static 'js/home.js' %}"></script>
|
||||
<link rel="stylesheet" href="{% static 'lib/css/rating-star.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/boofilsic.min.css' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page-wrapper">
|
||||
<div id="content-wrapper">
|
||||
{% include "partial/_navbar.html" %}
|
||||
|
||||
<section id="content" class="container">
|
||||
<div class="grid grid--reverse-order">
|
||||
<div class="grid__main grid__main--reverse-order">
|
||||
<div class="main-section-wrapper">
|
||||
<div class="entity-list">
|
||||
|
||||
<div class="set">
|
||||
<h5 class="entity-list__title">
|
||||
{{ user.username }}{{ list_title }}
|
||||
</h5>
|
||||
</div>
|
||||
<ul class="entity-list__entities">
|
||||
|
||||
{% for mark in marks %}
|
||||
|
||||
<li class="entity-list__entity">
|
||||
<div class="entity-list__entity-img-wrapper">
|
||||
<a href="{% url 'games:retrieve' mark.game.id %}">
|
||||
<img src="{{ mark.game.cover|thumb:'normal' }}" alt="" class="entity-list__entity-img">
|
||||
</a>
|
||||
</div>
|
||||
<div class="entity-list__entity-text">
|
||||
<div class="entity-list__entity-title">
|
||||
<a href="{% url 'games:retrieve' mark.game.id %}" class="entity-list__entity-link">
|
||||
{{ mark.game.title }}
|
||||
</a>
|
||||
<a href="{{ mark.game.source_url }}">
|
||||
<span class="source-label source-label__{{ mark.game.source_site }}">{{ mark.game.get_source_site_display }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<span class="entity-list__entity-info entity-list__entity-info--full-length">
|
||||
{% if mark.game.other_title %}{% trans '别名' %}
|
||||
{% for other_title in mark.game.other_title %}
|
||||
{{ other_title }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
{% if mark.game.developer %}{% trans '开发商' %}
|
||||
{% for developer in mark.game.developer %}
|
||||
{{ developer }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
{% if mark.game.genre %}{% trans '类型' %}
|
||||
{% for genre in mark.game.genre %}
|
||||
{{ genre }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
|
||||
{% if mark.game.platform %}{% trans '平台' %}
|
||||
{% for platform in mark.game.platform %}
|
||||
{{ platform }}{% if not forloop.last %} {% endif %}
|
||||
{% endfor %}/
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<p class="entity-list__entity-brief">
|
||||
{{ mark.game.brief }}
|
||||
</p>
|
||||
<div class="tag-collection">
|
||||
{% for tag_dict in mark.game.tag_list %}
|
||||
{% for k, v in tag_dict.items %}
|
||||
{% if k == 'content' %}
|
||||
<span class="tag-collection__tag">
|
||||
<a href="{% url 'common:search' %}?tag={{ v }}">{{ v }}</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<div class="dividing-line dividing-line--dashed"></div>
|
||||
<div class="entity-marks" style="margin-bottom: 0;">
|
||||
<ul class="entity-marks__mark-list">
|
||||
<li class="entity-marks__mark">
|
||||
|
||||
{% if mark.rating %}
|
||||
<span class="entity-marks__rating-star rating-star"
|
||||
data-rating-score="{{ mark.rating | floatformat:"0" }}" style="left: -4px;"></span>
|
||||
{% endif %}
|
||||
{% if mark.is_private %}
|
||||
<span class="icon-lock"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M17,8.48h-.73V6.27a6.27,6.27,0,1,0-12.53,0V8.48H3a.67.67,0,0,0-.67.67V19.33A.67.67,0,0,0,3,20H17a.67.67,0,0,0,.67-.67V9.15A.67.67,0,0,0,17,8.48ZM6.42,6.27h0a3.57,3.57,0,0,1,7.14,0h0V8.48H6.42Z" />
|
||||
</svg></span>
|
||||
{% endif %}
|
||||
<span class="entity-marks__mark-time">{% trans '于' %} {{ mark.edited_time }} {% trans '标记' %}</span>
|
||||
{% if mark.text %}
|
||||
<p class="entity-marks__mark-content">{{ mark.text }}</p>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>{% trans '无结果' %}</div>
|
||||
{% endfor %}
|
||||
<!-- user mark -->
|
||||
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<div class="pagination">
|
||||
|
||||
{% if marks.pagination.has_prev %}
|
||||
<a href="?page=1" class="pagination__nav-link pagination__nav-link">«</a>
|
||||
<a href="?page={{ marks.previous_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--right-margin pagination__nav-link">‹</a>
|
||||
{% endif %}
|
||||
|
||||
{% for page in marks.pagination.page_range %}
|
||||
|
||||
{% if page == marks.pagination.current_page %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link pagination__page-link--current">{{ page }}</a>
|
||||
{% else %}
|
||||
<a href="?page={{ page }}" class="pagination__page-link">{{ page }}</a>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% if marks.pagination.has_next %}
|
||||
<a href="?page={{ marks.next_page_number }}"
|
||||
class="pagination__nav-link pagination__nav-link--left-margin">›</a>
|
||||
<a href="?page={{ marks.pagination.last_page }}" class="pagination__nav-link">»</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid__aside grid__aside--reverse-order grid__aside--tablet-column">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--no-margin">
|
||||
<div class="user-profile" id="userInfoCard">
|
||||
<div class="user-profile__header">
|
||||
<!-- <img src="" class="user-profile__avatar mast-avatar" alt="{{ user.username }}"> -->
|
||||
<img src="" class="user-profile__avatar mast-avatar">
|
||||
<a href="{% url 'users:home' user.id %}">
|
||||
<h5 class="user-profile__username mast-displayname"></h5>
|
||||
</a>
|
||||
</div>
|
||||
<p class="user-profile__bio mast-brief"></p>
|
||||
<!-- <a href="#" class="follow">{% trans '关注TA' %}</a> -->
|
||||
|
||||
{% if request.user != user %}
|
||||
<a href="{% url 'users:report' %}?user_id={{ user.id }}"
|
||||
class="user-profile__report-link">{% trans '举报用户' %}</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relation-dropdown">
|
||||
<div class="relation-dropdown__button">
|
||||
<span class="icon-arrow">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
|
||||
<path d="M8.12,3.29,5,6.42,1.86,3.29H.45L5,7.84,9.55,3.29Z" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="relation-dropdown__body">
|
||||
<div class="aside-section-wrapper aside-section-wrapper--transparent aside-section-wrapper--collapse">
|
||||
|
||||
<div class="user-relation" id="followings">
|
||||
<h5 class="user-relation__label">
|
||||
{% trans '关注的人' %}
|
||||
</h5>
|
||||
<a href="{% url 'users:following' user.id %}"
|
||||
class="user-relation__more-link mast-following-more">{% trans '更多' %}</a>
|
||||
<ul class="user-relation__related-user-list mast-following">
|
||||
<li class="user-relation__related-user">
|
||||
<a>
|
||||
<img src="" alt="" class="user-relation__related-user-avatar">
|
||||
<div class="user-relation__related-user-name mast-displayname">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="user-relation" id="followers">
|
||||
<h5 class="user-relation__label">
|
||||
{% trans '被他们关注' %}
|
||||
</h5>
|
||||
<a href="{% url 'users:followers' user.id %}"
|
||||
class="user-relation__more-link mast-followers-more">{% trans '更多' %}</a>
|
||||
<ul class="user-relation__related-user-list mast-followers">
|
||||
<li class="user-relation__related-user">
|
||||
<a>
|
||||
<img src="" alt="" class="user-relation__related-user-avatar">
|
||||
<div class="user-relation__related-user-name mast-displayname">
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
|
||||
|
||||
<div id="oauth2Token" hidden="true">{% oauth_token %}</div>
|
||||
<div id="mastodonURI" hidden="true">{% mastodon request.user.mastodon_site %}</div>
|
||||
<!--current user mastodon id-->
|
||||
{% if user == request.user %}
|
||||
<div id="userMastodonID" hidden="true">{{ user.mastodon_id }}</div>
|
||||
{% else %}
|
||||
<div id="userMastodonID" hidden="true">{{ user.target_site_id }}</div>
|
||||
{% endif %}
|
||||
<div id="userPageURL" hidden="true">{% url 'users:home' 0 %}</div>
|
||||
<div id="spinner" hidden>
|
||||
<div class="spinner">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
|
@ -40,7 +40,7 @@
|
|||
<h5 class="entity-sort__label">
|
||||
{% trans '想读的书' %}
|
||||
</h5>
|
||||
{% if wish_books_more %}
|
||||
{% if wish_book_more %}
|
||||
<a href="{% url 'users:book_list' user.id 'wish' %}"
|
||||
class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
@ -65,7 +65,7 @@
|
|||
<h5 class="entity-sort__label">
|
||||
{% trans '在读的书' %}
|
||||
</h5>
|
||||
{% if do_books_more %}
|
||||
{% if do_book_more %}
|
||||
<a href="{% url 'users:book_list' user.id 'do' %}"
|
||||
class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
@ -91,7 +91,7 @@
|
|||
<h5 class="entity-sort__label">
|
||||
{% trans '读过的书' %}
|
||||
</h5>
|
||||
{% if collect_books_more %}
|
||||
{% if collect_book_more %}
|
||||
<a href="{% url 'users:book_list' user.id 'collect' %}"
|
||||
class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
@ -117,7 +117,7 @@
|
|||
<h5 class="entity-sort__label">
|
||||
{% trans '想看的电影/剧集' %}
|
||||
</h5>
|
||||
{% if wish_movies_more %}
|
||||
{% if wish_movie_more %}
|
||||
<a href="{% url 'users:movie_list' user.id 'wish' %}"
|
||||
class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
@ -143,7 +143,7 @@
|
|||
<h5 class="entity-sort__label">
|
||||
{% trans '在看的电影/剧集' %}
|
||||
</h5>
|
||||
{% if do_movies_more %}
|
||||
{% if do_movie_more %}
|
||||
<a href="{% url 'users:movie_list' user.id 'do' %}"
|
||||
class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
@ -169,7 +169,7 @@
|
|||
<h5 class="entity-sort__label">
|
||||
{% trans '看过的电影/剧集' %}
|
||||
</h5>
|
||||
{% if collect_movies_more %}
|
||||
{% if collect_movie_more %}
|
||||
<a href="{% url 'users:movie_list' user.id 'collect' %}"
|
||||
class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
@ -299,6 +299,81 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="entity-sort" id="gameWish">
|
||||
<h5 class="entity-sort__label">
|
||||
{% trans '想玩的游戏' %}
|
||||
</h5>
|
||||
{% if wish_game_more %}
|
||||
<a href="{% url 'users:game_list' user.id 'wish' %}" class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
||||
<ul class="entity-sort__entity-list">
|
||||
{% for wish_game_mark in wish_game_marks %}
|
||||
<li class="entity-sort__entity">
|
||||
|
||||
<a href="{% url 'games:retrieve' wish_game_mark.game.id %}">
|
||||
<img src="{{ wish_game_mark.game.cover|thumb:'normal' }}" alt="{{wish_game_mark.game.title}}"
|
||||
class="entity-sort__entity-img">
|
||||
<div class="entity-sort__entity-name" title="{{wish_game_mark.game.title}}">
|
||||
{{ wish_game_mark.game.title }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>暂无记录</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="entity-sort" id="gameDo">
|
||||
<h5 class="entity-sort__label">
|
||||
{% trans '在玩的游戏' %}
|
||||
</h5>
|
||||
{% if do_game_more %}
|
||||
<a href="{% url 'users:game_list' user.id 'do' %}" class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
||||
<ul class="entity-sort__entity-list">
|
||||
{% for do_game_mark in do_game_marks %}
|
||||
<li class="entity-sort__entity">
|
||||
|
||||
<a href="{% url 'games:retrieve' do_game_mark.game.id %}">
|
||||
<img src="{{ do_game_mark.game.cover|thumb:'normal' }}" alt="{{do_game_mark.game.title}}"
|
||||
class="entity-sort__entity-img">
|
||||
<div class="entity-sort__entity-name" title="{{do_game_mark.game.title}}">
|
||||
{{ do_game_mark.game.title }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>暂无记录</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="entity-sort" id="gameCollect">
|
||||
<h5 class="entity-sort__label">
|
||||
{% trans '玩过的游戏' %}
|
||||
</h5>
|
||||
{% if collect_game_more %}
|
||||
<a href="{% url 'users:game_list' user.id 'collect' %}" class="entity-sort__more-link">{% trans '更多' %}</a>
|
||||
{% endif %}
|
||||
|
||||
<ul class="entity-sort__entity-list">
|
||||
{% for collect_game_mark in collect_game_marks %}
|
||||
<li class="entity-sort__entity">
|
||||
|
||||
<a href="{% url 'games:retrieve' collect_game_mark.game.id %}">
|
||||
<img src="{{ collect_game_mark.game.cover|thumb:'normal' }}" alt="{{collect_game_mark.game.title}}"
|
||||
class="entity-sort__entity-img">
|
||||
<span class="entity-sort__entity-name" title="{{collect_game_mark.game.title}}">{{collect_game_mark.game.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% empty %}
|
||||
<div>暂无记录</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% if user == request.user %}
|
|
@ -98,7 +98,7 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
<p class="entity-list__entity-brief">
|
||||
{{ mark.movie.brief | truncate:170 }}
|
||||
{{ mark.movie.brief }}
|
||||
</p>
|
||||
<div class="tag-collection">
|
||||
{% for tag_dict in mark.movie.tag_list %}
|
||||
|
|
|
@ -15,6 +15,7 @@ urlpatterns = [
|
|||
path('<int:id>/book/<str:status>/', book_list, name='book_list'),
|
||||
path('<int:id>/movie/<str:status>/', movie_list, name='movie_list'),
|
||||
path('<int:id>/music/<str:status>/', music_list, name='music_list'),
|
||||
path('<int:id>/game/<str:status>/', game_list, name='game_list'),
|
||||
path('<str:id>/', home, name='home'),
|
||||
path('<str:id>/followers/', followers, name='followers'),
|
||||
path('<str:id>/following/', following, name='following'),
|
||||
|
|
254
users/views.py
254
users/views.py
|
@ -12,15 +12,18 @@ from .forms import ReportForm
|
|||
from mastodon.auth import *
|
||||
from mastodon.api import *
|
||||
from mastodon import mastodon_request_included
|
||||
from common.views import BOOKS_PER_SET, ITEMS_PER_PAGE, PAGE_LINK_NUMBER, TAG_NUMBER_ON_LIST, MOVIES_PER_SET, MUSIC_PER_SET
|
||||
from common.config import *
|
||||
from common.models import MarkStatusEnum
|
||||
from common.utils import PageLinksGenerator
|
||||
from management.models import Announcement
|
||||
from books.models import *
|
||||
from movies.models import *
|
||||
from music.models import *
|
||||
from games.models import *
|
||||
from books.forms import BookMarkStatusTranslator
|
||||
from movies.forms import MovieMarkStatusTranslator
|
||||
from music.forms import MusicMarkStatusTranslator
|
||||
from games.forms import GameMarkStatusTranslator
|
||||
from mastodon.models import MastodonApplication
|
||||
|
||||
|
||||
|
@ -172,10 +175,37 @@ def home(request, id):
|
|||
'secondary_msg': sec_msg,
|
||||
}
|
||||
)
|
||||
|
||||
# access one's own home page
|
||||
if user == request.user:
|
||||
return redirect("common:home")
|
||||
reports = Report.objects.order_by(
|
||||
'-submitted_time').filter(is_read=False)
|
||||
unread_announcements = Announcement.objects.filter(
|
||||
pk__gt=request.user.read_announcement_index).order_by('-pk')
|
||||
try:
|
||||
request.user.read_announcement_index = Announcement.objects.latest(
|
||||
'pk').pk
|
||||
request.user.save(update_fields=['read_announcement_index'])
|
||||
except ObjectDoesNotExist as e:
|
||||
# when there is no annoucenment
|
||||
pass
|
||||
book_marks = request.user.user_bookmarks.all()
|
||||
movie_marks = request.user.user_moviemarks.all()
|
||||
album_marks = request.user.user_albummarks.all()
|
||||
song_marks = request.user.user_songmarks.all()
|
||||
game_marks = request.user.user_gamemarks.all()
|
||||
|
||||
# visit other's home page
|
||||
else:
|
||||
# mastodon request
|
||||
# no these value on other's home page
|
||||
reports = None
|
||||
unread_announcements = None
|
||||
|
||||
# cross site info for visiting other's home page
|
||||
user.target_site_id = get_cross_site_id(
|
||||
user, request.user.mastodon_site, request.session['oauth_token'])
|
||||
|
||||
# make queries
|
||||
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
|
||||
if relation['blocked_by']:
|
||||
msg = _("你没有访问TA主页的权限😥")
|
||||
|
@ -187,89 +217,86 @@ def home(request, id):
|
|||
}
|
||||
)
|
||||
book_marks = BookMark.get_available_by_user(user, relation['following'])
|
||||
do_book_marks = book_marks.filter(status=MarkStatusEnum.DO)
|
||||
do_books_more = True if do_book_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
wish_book_marks = book_marks.filter(status=MarkStatusEnum.WISH)
|
||||
wish_books_more = True if wish_book_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
collect_book_marks = book_marks.filter(status=MarkStatusEnum.COLLECT)
|
||||
collect_books_more = True if collect_book_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
movie_marks = MovieMark.get_available_by_user(user, relation['following'])
|
||||
do_movie_marks = movie_marks.filter(status=MarkStatusEnum.DO)
|
||||
do_movies_more = True if do_movie_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
wish_movie_marks = movie_marks.filter(status=MarkStatusEnum.WISH)
|
||||
wish_movies_more = True if wish_movie_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
collect_movie_marks = movie_marks.filter(status=MarkStatusEnum.COLLECT)
|
||||
collect_movies_more = True if collect_movie_marks.count() > BOOKS_PER_SET else False
|
||||
|
||||
song_marks = SongMark.get_available_by_user(user, relation['following'])
|
||||
album_marks = AlbumMark.get_available_by_user(user, relation['following'])
|
||||
game_marks = GameMark.get_available_by_user(user, relation['following'])
|
||||
|
||||
do_music_marks = list(song_marks.filter(status=MarkStatusEnum.DO)[:MUSIC_PER_SET]) \
|
||||
+ list(album_marks.filter(status=MarkStatusEnum.DO)[:MUSIC_PER_SET])
|
||||
do_music_more = True if len(do_music_marks) > MUSIC_PER_SET else False
|
||||
do_music_marks = sorted(do_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
# book marks
|
||||
filtered_book_marks = filter_marks(book_marks, BOOKS_PER_SET, 'book')
|
||||
|
||||
wish_music_marks = list(song_marks.filter(status=MarkStatusEnum.WISH)[:MUSIC_PER_SET]) \
|
||||
+ list(album_marks.filter(status=MarkStatusEnum.WISH)[:MUSIC_PER_SET])
|
||||
wish_music_more = True if len(wish_music_marks) > MUSIC_PER_SET else False
|
||||
wish_music_marks = sorted(wish_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
# movie marks
|
||||
filtered_movie_marks = filter_marks(movie_marks, MOVIES_PER_SET, 'movie')
|
||||
|
||||
collect_music_marks = list(song_marks.filter(status=MarkStatusEnum.COLLECT)[:MUSIC_PER_SET]) \
|
||||
+ list(album_marks.filter(status=MarkStatusEnum.COLLECT)[:MUSIC_PER_SET])
|
||||
collect_music_more = True if len(collect_music_marks) > MUSIC_PER_SET else False
|
||||
collect_music_marks = sorted(collect_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
# game marks
|
||||
filtered_game_marks = filter_marks(game_marks, GAMES_PER_SET, 'game')
|
||||
|
||||
for mark in do_music_marks + wish_music_marks + collect_music_marks:
|
||||
# for template convenience
|
||||
if mark.__class__ == AlbumMark:
|
||||
mark.type = "album"
|
||||
else:
|
||||
mark.type = "song"
|
||||
# music marks
|
||||
do_music_marks = list(song_marks.filter(status=MarkStatusEnum.DO)[:MUSIC_PER_SET]) \
|
||||
+ list(album_marks.filter(status=MarkStatusEnum.DO)[:MUSIC_PER_SET])
|
||||
do_music_more = True if len(do_music_marks) > MUSIC_PER_SET else False
|
||||
do_music_marks = sorted(do_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
|
||||
user.target_site_id = get_cross_site_id(
|
||||
user, request.user.mastodon_site, request.session['oauth_token'])
|
||||
wish_music_marks = list(song_marks.filter(status=MarkStatusEnum.WISH)[:MUSIC_PER_SET]) \
|
||||
+ list(album_marks.filter(status=MarkStatusEnum.WISH)[:MUSIC_PER_SET])
|
||||
wish_music_more = True if len(wish_music_marks) > MUSIC_PER_SET else False
|
||||
wish_music_marks = sorted(wish_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
|
||||
try:
|
||||
layout = user.preference.get_serialized_home_layout()
|
||||
except ObjectDoesNotExist:
|
||||
Preference.objects.create(user=user)
|
||||
layout = user.preference.get_serialized_home_layout()
|
||||
collect_music_marks = list(song_marks.filter(status=MarkStatusEnum.COLLECT)[:MUSIC_PER_SET]) \
|
||||
+ list(album_marks.filter(status=MarkStatusEnum.COLLECT)[:MUSIC_PER_SET])
|
||||
collect_music_more = True if len(collect_music_marks) > MUSIC_PER_SET else False
|
||||
collect_music_marks = sorted(collect_music_marks, key=lambda e: e.edited_time, reverse=True)[:MUSIC_PER_SET]
|
||||
|
||||
return render(
|
||||
request,
|
||||
'common/home.html',
|
||||
{
|
||||
'user': user,
|
||||
'do_book_marks': do_book_marks[:BOOKS_PER_SET],
|
||||
'wish_book_marks': wish_book_marks[:BOOKS_PER_SET],
|
||||
'collect_book_marks': collect_book_marks[:BOOKS_PER_SET],
|
||||
'do_books_more': do_books_more,
|
||||
'wish_books_more': wish_books_more,
|
||||
'collect_books_more': collect_books_more,
|
||||
'do_movie_marks': do_movie_marks[:MOVIES_PER_SET],
|
||||
'wish_movie_marks': wish_movie_marks[:MOVIES_PER_SET],
|
||||
'collect_movie_marks': collect_movie_marks[:MOVIES_PER_SET],
|
||||
'do_movies_more': do_movies_more,
|
||||
'wish_movies_more': wish_movies_more,
|
||||
'collect_movies_more': collect_movies_more,
|
||||
'do_music_marks': do_music_marks,
|
||||
'wish_music_marks': wish_music_marks,
|
||||
'collect_music_marks': collect_music_marks,
|
||||
'do_music_more': do_music_more,
|
||||
'wish_music_more': wish_music_more,
|
||||
'collect_music_more': collect_music_more,
|
||||
'layout': layout,
|
||||
}
|
||||
)
|
||||
for mark in do_music_marks + wish_music_marks + collect_music_marks:
|
||||
# for template convenience
|
||||
if mark.__class__ == AlbumMark:
|
||||
mark.type = "album"
|
||||
else:
|
||||
mark.type = "song"
|
||||
|
||||
try:
|
||||
layout = user.preference.get_serialized_home_layout()
|
||||
except ObjectDoesNotExist:
|
||||
Preference.objects.create(user=user)
|
||||
layout = user.preference.get_serialized_home_layout()
|
||||
|
||||
return render(
|
||||
request,
|
||||
'users/home.html',
|
||||
{
|
||||
'user': user,
|
||||
**filtered_book_marks,
|
||||
**filtered_movie_marks,
|
||||
**filtered_game_marks,
|
||||
'do_music_marks': do_music_marks,
|
||||
'wish_music_marks': wish_music_marks,
|
||||
'collect_music_marks': collect_music_marks,
|
||||
'do_music_more': do_music_more,
|
||||
'wish_music_more': wish_music_more,
|
||||
'collect_music_more': collect_music_more,
|
||||
'layout': layout,
|
||||
'reports': reports,
|
||||
'unread_announcements': unread_announcements,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
def filter_marks(queryset, maximum, type_name):
|
||||
result = {}
|
||||
for k in MarkStatusEnum.names:
|
||||
result[f"{k}_{type_name}_marks"] = queryset.filter(
|
||||
status=MarkStatusEnum[k.upper()]
|
||||
).order_by("-edited_time")
|
||||
if result[f"{k}_{type_name}_marks"].count() > maximum:
|
||||
result[f"{k}_{type_name}_more"] = True
|
||||
result[f"{k}_{type_name}_marks"] = result[f"{k}_{type_name}_marks"][:maximum]
|
||||
else:
|
||||
result[f"{k}_{type_name}_more"] = False
|
||||
return result
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def followers(request, id):
|
||||
|
@ -429,7 +456,7 @@ def book_list(request, id, status):
|
|||
mark.book.tag_list = mark.book.get_tags_manager().values('content').annotate(
|
||||
tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER_ON_LIST]
|
||||
marks.pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
list_title = str(BookMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("标记的书"))
|
||||
list_title = str(BookMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的书"))
|
||||
return render(
|
||||
request,
|
||||
'users/book_list.html',
|
||||
|
@ -471,7 +498,7 @@ def movie_list(request, id, status):
|
|||
'msg': msg,
|
||||
'secondary_msg': sec_msg,
|
||||
}
|
||||
)
|
||||
)
|
||||
if not user == request.user:
|
||||
# mastodon request
|
||||
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
|
||||
|
@ -484,10 +511,11 @@ def movie_list(request, id, status):
|
|||
'msg': msg,
|
||||
}
|
||||
)
|
||||
queryset = MovieMark.get_available_by_user(user, relation['following']).filter(
|
||||
status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
|
||||
user.target_site_id = get_cross_site_id(
|
||||
user, request.user.mastodon_site, request.session['oauth_token'])
|
||||
|
||||
queryset = MovieMark.get_available_by_user(user, relation['following']).filter(
|
||||
status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
|
||||
else:
|
||||
queryset = MovieMark.objects.filter(
|
||||
owner=user, status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
|
||||
|
@ -498,7 +526,7 @@ def movie_list(request, id, status):
|
|||
mark.movie.tag_list = mark.movie.get_tags_manager().values('content').annotate(
|
||||
tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER_ON_LIST]
|
||||
marks.pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
list_title = str(MovieMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("标记的电影和剧集"))
|
||||
list_title = str(MovieMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的电影和剧集"))
|
||||
return render(
|
||||
request,
|
||||
'users/movie_list.html',
|
||||
|
@ -512,6 +540,76 @@ def movie_list(request, id, status):
|
|||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def game_list(request, id, status):
|
||||
if request.method == 'GET':
|
||||
if not status.upper() in MarkStatusEnum.names:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
if isinstance(id, str):
|
||||
try:
|
||||
username = id.split('@')[0]
|
||||
site = id.split('@')[1]
|
||||
except IndexError as e:
|
||||
return HttpResponseBadRequest("Invalid user id")
|
||||
query_kwargs = {'username': username, 'mastodon_site': site}
|
||||
elif isinstance(id, int):
|
||||
query_kwargs = {'pk': id}
|
||||
try:
|
||||
user = User.objects.get(**query_kwargs)
|
||||
except ObjectDoesNotExist:
|
||||
msg = _("😖哎呀这位老师还没有注册书影音呢,快去长毛象喊TA来吧!")
|
||||
sec_msg = _("目前只开放本站用户注册")
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': msg,
|
||||
'secondary_msg': sec_msg,
|
||||
}
|
||||
)
|
||||
if not user == request.user:
|
||||
# mastodon request
|
||||
relation = get_relationship(request.user, user, request.session['oauth_token'])[0]
|
||||
if relation['blocked_by']:
|
||||
msg = _("你没有访问TA主页的权限😥")
|
||||
return render(
|
||||
request,
|
||||
'common/error.html',
|
||||
{
|
||||
'msg': msg,
|
||||
}
|
||||
)
|
||||
user.target_site_id = get_cross_site_id(
|
||||
user, request.user.mastodon_site, request.session['oauth_token'])
|
||||
|
||||
queryset = GameMark.get_available_by_user(user, relation['following']).filter(
|
||||
status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
|
||||
else:
|
||||
queryset = GameMark.objects.filter(
|
||||
owner=user, status=MarkStatusEnum[status.upper()]).order_by("-edited_time")
|
||||
paginator = Paginator(queryset, ITEMS_PER_PAGE)
|
||||
page_number = request.GET.get('page', default=1)
|
||||
marks = paginator.get_page(page_number)
|
||||
for mark in marks:
|
||||
mark.game.tag_list = mark.game.get_tags_manager().values('content').annotate(
|
||||
tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER_ON_LIST]
|
||||
marks.pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
list_title = str(GameMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的游戏"))
|
||||
return render(
|
||||
request,
|
||||
'users/game_list.html',
|
||||
{
|
||||
'marks': marks,
|
||||
'user': user,
|
||||
'list_title' : list_title,
|
||||
}
|
||||
)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
||||
|
||||
@mastodon_request_included
|
||||
@login_required
|
||||
def music_list(request, id, status):
|
||||
|
@ -578,7 +676,7 @@ def music_list(request, id, status):
|
|||
tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER_ON_LIST]
|
||||
|
||||
marks.pagination = PageLinksGenerator(PAGE_LINK_NUMBER, page_number, paginator.num_pages)
|
||||
list_title = str(MovieMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("标记的音乐"))
|
||||
list_title = str(MusicMarkStatusTranslator(MarkStatusEnum[status.upper()])) + str(_("的音乐"))
|
||||
return render(
|
||||
request,
|
||||
'users/music_list.html',
|
||||
|
|
Loading…
Add table
Reference in a new issue