Browse Source

Allow logged-out users to browse the site

This is messy in a few ways and needs some reworking, but should be fine
for testing and initial enabling public visibility. An invite is still
required for registration, and the registration page isn't even
currently linked anywhere since people should usually get a registration
link as the invite.

May roll this back and/or do follow-up commits if it makes the site
break.
merge-requests/55/head
Deimos 6 years ago
parent
commit
610bcefd25
  1. 12
      tildes/scss/modules/_site-footer.scss
  2. 13
      tildes/scss/themes/_theme_base.scss
  3. 27
      tildes/tildes/auth.py
  4. 20
      tildes/tildes/templates/base.jinja2
  5. 36
      tildes/tildes/templates/error_403.jinja2
  6. 76
      tildes/tildes/templates/home.jinja2
  7. 11
      tildes/tildes/templates/macros/comments.jinja2
  8. 42
      tildes/tildes/templates/macros/user.jinja2
  9. 2
      tildes/tildes/templates/notifications_unread.jinja2
  10. 2
      tildes/tildes/templates/topic.jinja2
  11. 2
      tildes/tildes/templates/topic_listing.jinja2
  12. 2
      tildes/tildes/templates/user.jinja2
  13. 37
      tildes/tildes/views/topic.py
  14. 2
      tildes/tildes/views/user.py

12
tildes/scss/modules/_site-footer.scss

@ -37,3 +37,15 @@
margin-left: 1rem; margin-left: 1rem;
} }
} }
.site-footer-theme-selection {
font-style: normal;
margin-bottom: 1rem;
select {
width: auto;
font-size: 0.6rem;
height: 1.4rem;
padding: 0 0 0 0.2rem;
}
}

13
tildes/scss/themes/_theme_base.scss

@ -61,12 +61,6 @@
} }
} }
a.logged-in-user-username:visited,
a.site-header-context:visited,
a.site-header-logo:visited {
color: unset;
}
@include syntax-highlighting($is-light); @include syntax-highlighting($is-light);
blockquote { blockquote {
@ -299,7 +293,7 @@
@include theme-special-label(map-get($theme, "topic-tag-spoiler"), $is-light); @include theme-special-label(map-get($theme, "topic-tag-spoiler"), $is-light);
} }
.logged-in-user-username {
.logged-in-user-username, .logged-in-user-username:visited {
color: map-get($theme, "foreground-primary"); color: map-get($theme, "foreground-primary");
} }
@ -346,12 +340,11 @@
background-color: map-get($theme, "background-primary"); background-color: map-get($theme, "background-primary");
} }
.site-header-context,
.site-header-username {
.site-header-context, .site-header-context:visited {
color: map-get($theme, "foreground-primary"); color: map-get($theme, "foreground-primary");
} }
.site-header-logo {
.site-header-logo, .site-header-logo:visited {
color: map-get($theme, "foreground-highlight"); color: map-get($theme, "foreground-highlight");
} }

27
tildes/tildes/auth.py

@ -10,7 +10,7 @@ from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.config import Configurator from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import HTTPFound
from pyramid.request import Request from pyramid.request import Request
from pyramid.security import ACLDenied, ACLPermitsResult, Allow, Authenticated, Everyone
from pyramid.security import Allow, Everyone
from tildes.models.user import User from tildes.models.user import User
@ -72,11 +72,7 @@ def includeme(config: Configurator) -> None:
# default permission # default permission
config.set_root_factory(DefaultRootFactory) config.set_root_factory(DefaultRootFactory)
# Set the authorization policy to a custom one that always returns a "denied" result
# if the user isn't logged in. When overall site access is no longer being
# restricted, the AuthorizedOnlyPolicy class can just be replaced with the standard
# ACLAuthorizationPolicy
config.set_authorization_policy(AuthorizedOnlyPolicy())
config.set_authorization_policy(ACLAuthorizationPolicy())
config.set_authentication_policy( config.set_authentication_policy(
SessionAuthenticationPolicy(callback=auth_callback) SessionAuthenticationPolicy(callback=auth_callback)
@ -92,25 +88,6 @@ def includeme(config: Configurator) -> None:
config.add_request_method(has_any_permission, "has_any_permission") config.add_request_method(has_any_permission, "has_any_permission")
class AuthorizedOnlyPolicy(ACLAuthorizationPolicy):
"""ACLAuthorizationPolicy override that always denies logged-out users."""
def permits(
self, context: Any, principals: Sequence[Any], permission: str
) -> ACLPermitsResult:
"""Deny logged-out users, otherwise pass up to normal policy."""
if Authenticated not in principals:
return ACLDenied(
"<authorized only>",
"<no ACLs checked yet>",
permission,
principals,
context,
)
return super().permits(context, principals, permission)
def has_any_permission( def has_any_permission(
request: Request, permissions: Sequence[str], context: Any request: Request, permissions: Sequence[str], context: Any
) -> bool: ) -> bool:

20
tildes/tildes/templates/base.jinja2

@ -90,6 +90,26 @@
</aside> </aside>
<footer id="site-footer" data-js-hide-sidebar-if-open> <footer id="site-footer" data-js-hide-sidebar-if-open>
{# Show the theme-selection dropdown if the user isn't logged in #}
{% if not request.user %}
<div class="site-footer-theme-selection">
<label for="theme">Theme:</label>
<select class="form-select" name="theme" id="theme" data-js-theme-selector>
{% for (theme, description) in (
("white", "White"),
("light", "Solarized Light"),
("dark", "Solarized Dark"),
("black", "Black")) %}
<option value="{{ theme }}"
{{ 'selected' if theme == request.cookies.get("theme", "white") else '' }}
>
{{ description }}
</option>
{% endfor %}
</select>
</div>
{% endif %}
<p>Tildes is a non-profit site that respects its users and prioritizes quality content.</p> <p>Tildes is a non-profit site that respects its users and prioritizes quality content.</p>
<p>It has no advertising, no investors, and is supported by <a href="https://docs.tildes.net/donate">your donations</a>.</p> <p>It has no advertising, no investors, and is supported by <a href="https://docs.tildes.net/donate">your donations</a>.</p>
<ul class="site-footer-links"> <ul class="site-footer-links">

36
tildes/tildes/templates/error_403.jinja2

@ -3,36 +3,12 @@
{% extends 'base_no_sidebar.jinja2' %} {% extends 'base_no_sidebar.jinja2' %}
{% block title %}
{% if not request.user %}
Invite-only alpha
{% else %}
Error 403 (Forbidden)
{% endif %}
{% endblock %}
{% block main_heading %}
{% if request.user %}
Error 403 (Forbidden)
{% endif %}
{% endblock %}
{% block title %}Error 403 (Forbidden){% endblock %}
{% block content %} {% block content %}
{% if not request.user %}
<div class="empty">
<h2 class="empty-title">Tildes is currently in invite-only alpha</h2>
<p class="empty-subtitle">To learn more about Tildes, <a href="https://blog.tildes.net/announcing-tildes">read the announcement post</a>.</p>
<p class="empty-subtitle">There are also some pages available on <a href="https://docs.tildes.net">the docs site</a>.</p>
<div class="empty-action">
<a href="/login" class="btn btn-primary">Log in</a>
<a href="/register" class="btn btn-primary">Register</a>
</div>
</div>
{% else %}
<p>You don't have access to this page.</p>
{% endif %}
<div class="empty">
<h2 class="empty-title">Error 403 (Forbidden)</h2>
<p class="empty-subtitle">You don't have access to this page.</p>
<div class="empty-action"><a href="/" class="btn btn-primary">Back to the home page</a></div>
</div>
{% endblock %} {% endblock %}

76
tildes/tildes/templates/home.jinja2

@ -10,14 +10,18 @@
{% block header_context_link %}{% endblock %} {% block header_context_link %}{% endblock %}
{% block content %} {% block content %}
{% if request.user and request.user.subscriptions %}
{{ super() }}
{% if request.user %}
{% if request.user.subscriptions %}
{{ super() }}
{% else %}
<div class="empty">
<h2 class="empty-title">You aren't subscribed to any groups yet</h2>
<p class="empty-subtitle">This page will show a combined listing of topics from groups that you're subscribed to.</p>
<div class="empty-action"><a href="/groups" class="btn btn-primary">Browse the list of groups</a></div>
</div>
{% endif %}
{% else %} {% else %}
<div class="empty">
<h2 class="empty-title">You aren't subscribed to any groups yet</h2>
<p class="empty-subtitle">This page will show a combined listing of topics from groups that you're subscribed to.</p>
<div class="empty-action"><a href="/groups" class="btn btn-primary">Browse the list of groups</a></div>
</div>
{{ super() }}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
@ -25,33 +29,43 @@
{{ search_form() }} {{ search_form() }}
<h2>Home</h2> <h2>Home</h2>
<p>The home page shows topics from groups that you're subscribed to.</p>
{% if request.user %} {% if request.user %}
{% if request.user.subscriptions %}
<ul class="nav">
<p>The home page shows topics from groups that you're subscribed to.</p>
{% else %}
<p>The home page shows topics from all the groups on Tildes.</p>
<p>Logged-in users can choose which groups they subscribe to and/or filter out posts with particular tags.</p>
{% endif %}
{% if groups %}
<ul class="nav">
{% if request.user %}
<li>Subscriptions</li> <li>Subscriptions</li>
<ul class="nav">
{% for subscription in request.user.subscriptions|sort(attribute='group') %}
<li class="nav-item"><a href="/~{{ subscription.group.path }}">~{{ subscription.group.path }}</a></li>
{% endfor %}
</ul>
{% else %}
<li>Groups</li>
{% endif %}
<ul class="nav">
{% for group in groups|sort %}
<li class="nav-item"><a href="/~{{ group.path }}">~{{ group.path }}</a></li>
{% endfor %}
</ul> </ul>
<a href="/groups" class="btn btn-primary">Browse the list of groups</a>
{% endif %}
</ul>
<a href="/groups" class="btn btn-primary">Browse the list of groups</a>
{% endif %}
{% if not (tag or unfiltered) %}
<hr>
<details>
<summary>Filtered topic tags ({{ request.user.filtered_topic_tags|length }})</summary>
<ul class="topic-tags">
{% for tag in request.user.filtered_topic_tags %}
<li class="label label-topic-tag">{{ tag }}</li>
{% else %}
<li class="label label-topic-tag">No filtered tags</li>
{% endfor %}
</ul>
<a class="btn btn-link" href="/settings/filters">Edit filtered tags</a>
</details>
{% endif %}
{% if request.user and not (tag or unfiltered) %}
<hr>
<details>
<summary>Filtered topic tags ({{ request.user.filtered_topic_tags|length }})</summary>
<ul class="topic-tags">
{% for tag in request.user.filtered_topic_tags %}
<li class="label label-topic-tag">{{ tag }}</li>
{% else %}
<li class="label label-topic-tag">No filtered tags</li>
{% endfor %}
</ul>
<a class="btn btn-link" href="/settings/filters">Edit filtered tags</a>
</details>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

11
tildes/tildes/templates/macros/comments.jinja2

@ -150,6 +150,17 @@
{{ comment.rendered_html|safe }} {{ comment.rendered_html|safe }}
</div> </div>
{# Show votes at the bottom only if the viewer is logged out #}
{% if not request.user and comment.num_votes > 0 %}
<div class="comment-votes">{{ comment.num_votes }}
{% trans num_votes=comment.num_votes %}
vote
{% pluralize %}
votes
{% endtrans %}
</div>
{% endif %}
<menu class="post-buttons"> <menu class="post-buttons">
{% if request.has_permission('vote', comment) %} {% if request.has_permission('vote', comment) %}
{% if comment.user_voted is defined and comment.user_voted %} {% if comment.user_voted is defined and comment.user_voted %}

42
tildes/tildes/templates/macros/user.jinja2

@ -2,29 +2,31 @@
{# SPDX-License-Identifier: AGPL-3.0-or-later #} {# SPDX-License-Identifier: AGPL-3.0-or-later #}
{% macro logged_in_user_info() %} {% macro logged_in_user_info() %}
{% if request.user %}
<div class="logged-in-user-info"> <div class="logged-in-user-info">
<a class="logged-in-user-username" href="/user/{{ request.user }}">{{ request.user }}</a>
{% if request.user %}
<a class="logged-in-user-username" href="/user/{{ request.user }}">{{ request.user }}</a>
{% if request.user.num_unread_messages > 0 %}
<a class="logged-in-user-alert" href="/messages/unread">
{% trans num_messages=request.user.num_unread_messages %}
{{ num_messages }} new message
{% pluralize %}
{{ num_messages }} new messages
{% endtrans %}
</a>
{% endif %}
{% if request.user.num_unread_messages > 0 %}
<a class="logged-in-user-alert" href="/messages/unread">
{% trans num_messages=request.user.num_unread_messages %}
{{ num_messages }} new message
{% pluralize %}
{{ num_messages }} new messages
{% endtrans %}
</a>
{% endif %}
{% if request.user.num_unread_notifications > 0 %}
<a class="logged-in-user-alert" href="/notifications/unread">
{% trans num_notifications=request.user.num_unread_notifications %}
{{ num_notifications }} new comment
{% pluralize %}
{{ num_notifications }} new comments
{% endtrans %}
</a>
{% if request.user.num_unread_notifications > 0 %}
<a class="logged-in-user-alert" href="/notifications/unread">
{% trans num_notifications=request.user.num_unread_notifications %}
{{ num_notifications }} new comment
{% pluralize %}
{{ num_notifications }} new comments
{% endtrans %}
</a>
{% endif %}
{% else %}
<a class="text-small" href="/login">Log in</a>
{% endif %} {% endif %}
</div> </div>
{% endif %}
{% endmacro %} {% endmacro %}

2
tildes/tildes/templates/notifications_unread.jinja2

@ -73,5 +73,5 @@
<p><a href="/notifications">Go to previously read notifications</a></p> <p><a href="/notifications">Go to previously read notifications</a></p>
{% endif %} {% endif %}
{{ comment_label_options_template(comment_label_options) }}
{% if request.user %}{{ comment_label_options_template(comment_label_options) }}{% endif %}
{% endblock %} {% endblock %}

2
tildes/tildes/templates/topic.jinja2

@ -265,7 +265,7 @@
</article> </article>
{{ comment_label_options_template(comment_label_options) }}
{% if request.user %}{{ comment_label_options_template(comment_label_options) }}{% endif %}
{% endblock content %} {% endblock content %}
{% block sidebar %} {% block sidebar %}

2
tildes/tildes/templates/topic_listing.jinja2

@ -74,7 +74,7 @@
</div> </div>
</form> </form>
{% if is_default_view is defined and not is_default_view %}
{% if request.user and is_default_view is defined and not is_default_view %}
<form <form
{% if is_single_group %} {% if is_single_group %}
data-ic-patch-to="{{ request.route_url( data-ic-patch-to="{{ request.route_url(

2
tildes/tildes/templates/user.jinja2

@ -85,7 +85,7 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{{ comment_label_options_template(comment_label_options) }}
{% if request.user %}{{ comment_label_options_template(comment_label_options) }}{% endif %}
{% endblock %} {% endblock %}

37
tildes/tildes/views/topic.py

@ -86,7 +86,7 @@ def post_group_topics(
raise HTTPFound(location=new_topic.permalink) raise HTTPFound(location=new_topic.permalink)
@view_config(route_name="home", renderer="home.jinja2")
@view_config(route_name="home", renderer="home.jinja2") # noqa
@view_config(route_name="group", renderer="topic_listing.jinja2") @view_config(route_name="group", renderer="topic_listing.jinja2")
@use_kwargs(TopicListingSchema()) @use_kwargs(TopicListingSchema())
def get_group_topics( def get_group_topics(
@ -104,7 +104,13 @@ def get_group_topics(
# pylint: disable=too-many-arguments # pylint: disable=too-many-arguments
if request.matched_route.name == "home": if request.matched_route.name == "home":
# on the home page, include topics from the user's subscribed groups # on the home page, include topics from the user's subscribed groups
groups = [sub.group for sub in request.user.subscriptions]
# (or all groups, if logged-out)
if request.user:
groups = [sub.group for sub in request.user.subscriptions]
else:
groups = [
group for group in request.query(Group).all() if group.path != "test"
]
else: else:
# otherwise, just topics from the single group that we're looking at # otherwise, just topics from the single group that we're looking at
groups = [request.context] groups = [request.context]
@ -141,7 +147,7 @@ def get_group_topics(
query = query.after_id36(after) query = query.after_id36(after)
# apply topic tag filters unless they're disabled or viewing a single tag # apply topic tag filters unless they're disabled or viewing a single tag
if not (tag or unfiltered):
if request.user and not (tag or unfiltered):
# pylint: disable=protected-access # pylint: disable=protected-access
query = query.filter( query = query.filter(
~Topic._tags.descendant_of( # type: ignore ~Topic._tags.descendant_of( # type: ignore
@ -160,6 +166,7 @@ def get_group_topics(
return { return {
"group": request.context, "group": request.context,
"groups": groups,
"topics": topics, "topics": topics,
"order": order, "order": order,
"order_options": TopicSortOption, "order_options": TopicSortOption,
@ -323,24 +330,28 @@ def post_comment_on_topic(request: Request, markdown: str) -> HTTPFound:
raise HTTPFound(location=topic.permalink) raise HTTPFound(location=topic.permalink)
def _get_default_settings(request: Request, order: Any) -> DefaultSettings:
def _get_default_settings(request: Request, order: Any) -> DefaultSettings: # noqa
if isinstance(request.context, Group): if isinstance(request.context, Group):
is_home_page = False is_home_page = False
user_settings = (
request.query(UserGroupSettings)
.filter(
UserGroupSettings.user == request.user,
UserGroupSettings.group == request.context,
if request.user:
user_settings = (
request.query(UserGroupSettings)
.filter(
UserGroupSettings.user == request.user,
UserGroupSettings.group == request.context,
)
.one_or_none()
) )
.one_or_none()
)
else:
user_settings = None
else: else:
is_home_page = True is_home_page = True
user_settings = None user_settings = None
if user_settings and user_settings.default_order: if user_settings and user_settings.default_order:
default_order = user_settings.default_order default_order = user_settings.default_order
elif request.user.home_default_order:
elif request.user and request.user.home_default_order:
default_order = request.user.home_default_order default_order = request.user.home_default_order
else: else:
default_order = TopicSortOption.ACTIVITY default_order = TopicSortOption.ACTIVITY
@ -353,7 +364,7 @@ def _get_default_settings(request: Request, order: Any) -> DefaultSettings:
if user_settings and user_settings.default_period: if user_settings and user_settings.default_period:
user_default = user_settings.default_period user_default = user_settings.default_period
default_period = ShortTimePeriod().deserialize(user_default) default_period = ShortTimePeriod().deserialize(user_default)
elif request.user.home_default_period:
elif request.user and request.user.home_default_period:
user_default = request.user.home_default_period user_default = request.user.home_default_period
default_period = ShortTimePeriod().deserialize(user_default) default_period = ShortTimePeriod().deserialize(user_default)
else: else:

2
tildes/tildes/views/user.py

@ -69,7 +69,7 @@ def get_user(
query = query.join_all_relationships() query = query.join_all_relationships()
# include removed posts if the user's looking at their own page or is an admin # include removed posts if the user's looking at their own page or is an admin
if user == request.user or request.user.is_admin:
if request.user and (user == request.user or request.user.is_admin):
query = query.include_removed() query = query.include_removed()
result_sets.append(query.get_page(per_page)) result_sets.append(query.get_page(per_page))

Loading…
Cancel
Save