portico: Add page to list open organizations.

Fixes #22020
This commit is contained in:
Aman Agrawal 2022-08-30 05:47:43 +00:00 committed by Tim Abbott
parent b859cc4a3d
commit 379c6acbea
7 changed files with 269 additions and 5 deletions

View File

@ -9,6 +9,7 @@ from corporate.views.event_status import event_status, event_status_page
from corporate.views.portico import ( from corporate.views.portico import (
app_download_link_redirect, app_download_link_redirect,
apps_view, apps_view,
communities_view,
hello_view, hello_view,
landing_view, landing_view,
plans_view, plans_view,
@ -125,6 +126,7 @@ landing_page_urls = [
landing_view, landing_view,
{"template_name": "corporate/case-studies/recurse-center-case-study.html"}, {"template_name": "corporate/case-studies/recurse-center-case-study.html"},
), ),
path("communities/", communities_view),
] ]
i18n_urlpatterns += landing_page_urls i18n_urlpatterns += landing_page_urls

View File

@ -10,6 +10,8 @@ from django.template.response import TemplateResponse
from zerver.context_processors import get_realm_from_request, latest_info_context from zerver.context_processors import get_realm_from_request, latest_info_context
from zerver.decorator import add_google_analytics from zerver.decorator import add_google_analytics
from zerver.lib.github import InvalidPlatform, get_latest_github_release_download_link_for_platform from zerver.lib.github import InvalidPlatform, get_latest_github_release_download_link_for_platform
from zerver.lib.realm_description import get_realm_text_description
from zerver.lib.realm_icon import get_realm_icon_url
from zerver.lib.subdomains import is_subdomain_root_or_alias from zerver.lib.subdomains import is_subdomain_root_or_alias
from zerver.models import Realm from zerver.models import Realm
@ -103,3 +105,47 @@ def landing_view(request: HttpRequest, template_name: str) -> HttpResponse:
@add_google_analytics @add_google_analytics
def hello_view(request: HttpRequest) -> HttpResponse: def hello_view(request: HttpRequest) -> HttpResponse:
return TemplateResponse(request, "corporate/hello.html", latest_info_context()) return TemplateResponse(request, "corporate/hello.html", latest_info_context())
@add_google_analytics
def communities_view(request: HttpRequest) -> HttpResponse:
eligible_realms = []
unique_org_type_ids = set()
want_to_be_advertised_realms = Realm.objects.filter(
want_advertise_in_communities_directory=True
).order_by("name")
for realm in want_to_be_advertised_realms:
if realm.allow_web_public_streams_access():
eligible_realms.append(
{
"id": realm.id,
"name": realm.name,
"realm_url": realm.uri,
"logo_url": get_realm_icon_url(realm),
"description": get_realm_text_description(realm),
"org_type_key": [
org_type
for org_type in Realm.ORG_TYPES
if Realm.ORG_TYPES[org_type]["id"] == realm.org_type
][0],
}
)
unique_org_type_ids.add(realm.org_type)
# Remove org_types for which there are no open organizations.
org_types = dict()
for org_type in Realm.ORG_TYPES:
if Realm.ORG_TYPES[org_type]["id"] in unique_org_type_ids:
org_types[org_type] = Realm.ORG_TYPES[org_type]
# Remove `Unspecified` ORG_TYPE
org_types.pop("unspecified", None)
return TemplateResponse(
request,
"corporate/communities.html",
context={
"eligible_realms": eligible_realms,
"org_types": org_types,
},
)

View File

@ -0,0 +1,23 @@
import $ from "jquery";
function sync_open_organizations_page_with_current_hash() {
const hash = window.location.hash;
if (!hash || hash === "#all" || hash === "#undefined") {
$(".eligible_realm").show();
$(".realm-category").removeClass("selected");
$(`[data-category="all"]`).addClass("selected");
} else {
$(".eligible_realm").hide();
$(`.eligible_realm[data-org-type="${CSS.escape(hash.slice(1))}"]`).show();
$(".realm-category").removeClass("selected");
$(`[data-category="${CSS.escape(hash.slice(1))}"]`).addClass("selected");
}
}
// init
$(() => {
sync_open_organizations_page_with_current_hash();
$(window).on("hashchange", () => {
sync_open_organizations_page_with_current_hash();
});
});

View File

@ -342,7 +342,8 @@ $category-text: hsl(219, 23%, 33%);
font-weight: 400; font-weight: 400;
} }
.integration-category { .integration-category,
.realm-category {
font-size: 0.85em; font-size: 0.85em;
margin-top: 5px; margin-top: 5px;
font-weight: 400; font-weight: 400;
@ -390,7 +391,8 @@ $category-text: hsl(219, 23%, 33%);
font-size: 1em; font-size: 1em;
} }
.integration-category { .integration-category,
.realm-category {
font-size: 0.8em; font-size: 0.8em;
} }
} }
@ -416,7 +418,8 @@ $category-text: hsl(219, 23%, 33%);
font-size: 0.9em; font-size: 0.9em;
} }
.integration-category { .integration-category,
.realm-category {
font-size: 0.77em; font-size: 0.77em;
} }
} }
@ -465,7 +468,8 @@ $category-text: hsl(219, 23%, 33%);
margin: 0 21px 20px; margin: 0 21px 20px;
order: 1; order: 1;
.integration-category { .integration-category,
.realm-category {
display: none; display: none;
} }
@ -501,7 +505,8 @@ $category-text: hsl(219, 23%, 33%);
.categories { .categories {
order: 2; order: 2;
.integration-category { .integration-category,
.realm-category {
font-size: 0.9em; font-size: 0.9em;
font-weight: 400; font-weight: 400;
padding: 6px 3px; padding: 6px 3px;
@ -585,3 +590,89 @@ $category-text: hsl(219, 23%, 33%);
} }
} }
} }
.portico-landing.integrations.communities {
.main {
visibility: visible;
}
.portico-page-heading {
color: hsl(0, 0%, 0%);
font-size: 44px;
}
.portico-page-subheading {
font-size: 16px;
font-weight: 400;
color: hsl(0, 0%, 20%);
max-width: 340px;
margin: auto;
}
.integration-categories-sidebar h3 {
color: hsl(0, 0%, 0%);
font-size: 20px;
font-weight: 600;
}
.catalog {
margin-top: 50px;
}
.eligible_realms {
display: flex;
flex-direction: column;
gap: 30px;
.eligible_realm {
display: flex;
gap: 20px;
padding: 5px 10px;
border: 1px solid transparent;
border-radius: 4px;
&:hover {
border: 1px solid hsl(167, 34%, 56%);
}
.eligible_realm_logo {
display: flex;
width: 60px;
height: 60px;
}
.eligible_realm_details {
display: flex;
flex-direction: column;
justify-content: center;
.eligible_realm_name {
font-weight: 400;
font-size: 21px;
color: hsl(220.6, 20%, 33.3%);
line-height: 23px;
margin: 0;
}
.eligible_realm_description {
font-weight: 400;
font-size: 15px;
color: hsl(220, 2.7%, 56.5%);
line-height: 19px;
margin: 0;
width: min(600px, 50vw);
/* For restricting text to only two lines.
See https://caniuse.com/?search=display%3A%20-webkit-box for support. */
overflow: hidden;
display: -webkit-box; /* stylelint-disable-line value-no-vendor-prefix */
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
}
}
.eligible_realm_end_notice {
text-align: center;
}
}
}

View File

@ -0,0 +1,89 @@
{% extends "zerver/portico.html" %}
{% set entrypoint = "communities" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
{% endblock %}
{% block hello_page_container %} hello-main{% endblock %}
{% block portico_content %}
{% include 'zerver/landing_nav.html' %}
{% include 'zerver/gradients.html' %}
<div class="portico-landing integrations communities">
<div class="main">
<div class="padded-content">
<div class="inner-content">
<div class="integration-main-text">
<header>
<h1 class="portico-page-heading">
{% trans %}Open communities directory{% endtrans %}
</h1>
<p class="portico-page-subheading">
These Zulip communities are open to the public, and have
<a href="/help/communities-directory">asked to be listed</a>.
</p>
</header>
</div>
<div class="integration-categories-dropdown">
<div class="dropdown-toggle">
<h3 class="dropdown-category-label">{% trans %}Filter by category{% endtrans %}</h3>
<i class="fa fa-angle-down" aria-hidden="true"></i>
</div>
<div class="dropdown-list">
<a href="#all">
<h4 class="realm-category selected" data-category="all">All</h4>
</a>
{% for org_type in org_types.keys() %}
<a href="#{{ org_type }}">
<h4 class="realm-category" data-category="{{ org_type }}">
{{ org_types[org_type]["name"] }}
</h4>
</a>
{% endfor %}
</div>
</div>
<div class="catalog">
<div class="integration-categories-sidebar">
<h3>{% trans %}Categories{% endtrans %}</h3>
<a href="#all">
<h4 data-category="all" class="realm-category selected">{% trans %}All{% endtrans %}</h4>
</a>
{% for org_type in org_types.keys() %}
<a href="#{{ org_type }}">
<h4 data-category="{{ org_type }}" class="realm-category">
{{ org_types[org_type]["name"] }}
</h4>
</a>
{% endfor %}
</div>
<div class="eligible_realms">
{% for eligible_realm in eligible_realms %}
<a class="eligible_realm" data-org-type="{{ eligible_realm.org_type_key }}" href="{{ eligible_realm.realm_url }}">
<img class="eligible_realm_logo" src="{{ eligible_realm.logo_url }}" alt="{{ eligible_realm.name }} logo"/>
<div class="eligible_realm_details">
<h3 class="eligible_realm_name">{{ eligible_realm.name }}</h3>
<h4 class="eligible_realm_description">
{{ eligible_realm.description }}
</h4>
</div>
</a>
{% endfor %}
<hr />
<div class="eligible_realm_end_notice">
<p>Learn how Zulip can be a <a href="/for/communities">home for your community</a>.</p>
</div>
</div>
</div>
</div> <!-- .inner-content -->
</div> <!-- .padded-content -->
</div> <!-- .main -->
</div> <!-- .portico-landing -->
{% endblock %}

View File

@ -57,6 +57,12 @@
"./static/styles/portico/landing_page.css", "./static/styles/portico/landing_page.css",
"./static/styles/portico/integrations.css" "./static/styles/portico/integrations.css"
], ],
"communities": [
"./static/js/bundles/portico",
"./static/js/portico/communities",
"./static/styles/portico/landing_page.css",
"./static/styles/portico/integrations.css"
],
"signup": ["./static/js/bundles/portico", "jquery-validation", "./static/js/portico/signup"], "signup": ["./static/js/bundles/portico", "jquery-validation", "./static/js/portico/signup"],
"register": ["./static/js/bundles/portico", "jquery-validation", "./static/js/portico/signup"], "register": ["./static/js/bundles/portico", "jquery-validation", "./static/js/portico/signup"],
"confirm-preregistrationuser": [ "confirm-preregistrationuser": [

View File

@ -199,6 +199,13 @@ class DocPageTest(ZulipTestCase):
result = self.client_get("/for/companies/", follow=True) result = self.client_get("/for/companies/", follow=True)
self.assert_in_success_response(["Communication efficiency represents"], result) self.assert_in_success_response(["Communication efficiency represents"], result)
def test_open_organizations_endpoint(self) -> None:
realm = get_realm("zulip")
realm.want_advertise_in_communities_directory = True
realm.save()
self._test("/communities/", "Open communities directory")
def test_portico_pages_open_graph_metadata(self) -> None: def test_portico_pages_open_graph_metadata(self) -> None:
# Why Zulip # Why Zulip
url = "/why-zulip/" url = "/why-zulip/"