Merge ~ines-almeida/launchpad:social-accounts-display-view into launchpad:master

Proposed by Ines Almeida
Status: Merged
Approved by: Ines Almeida
Approved revision: c7a4eb49fb56761e62171856e43e0b6bc40b2eb8
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~ines-almeida/launchpad:social-accounts-display-view
Merge into: launchpad:master
Prerequisite: ~ines-almeida/launchpad:social-accounts-edit-view
Diff against target: 596 lines (+317/-47)
14 files modified
lib/canonical/launchpad/icing/css/components/_index.scss (+1/-0)
lib/canonical/launchpad/icing/css/components/social_accounts.scss (+12/-0)
lib/canonical/launchpad/images/src/social-irc.svg (+18/-0)
lib/canonical/launchpad/images/src/social-jabber.svg (+17/-0)
lib/canonical/launchpad/images/src/social-matrix.svg (+21/-0)
lib/lp/app/browser/configure.zcml (+7/-0)
lib/lp/app/browser/tales.py (+35/-0)
lib/lp/app/tests/test_tales.py (+59/-0)
lib/lp/registry/browser/person.py (+26/-0)
lib/lp/registry/interfaces/socialaccount.py (+11/-0)
lib/lp/registry/stories/person/xx-person-edit-jabber-ids.rst (+8/-4)
lib/lp/registry/stories/person/xx-person-edit-matrix-accounts.rst (+44/-4)
lib/lp/registry/stories/person/xx-person-home.rst (+10/-8)
lib/lp/registry/templates/person-portlet-contact-details.pt (+48/-31)
Reviewer Review Type Date Requested Status
Guruprasad Approve
Simone Pelosi Approve
Review via email: mp+458730@code.launchpad.net

Commit message

Update display social accounts view

To post a comment you must log in.
Revision history for this message
Ines Almeida (ines-almeida) wrote :
Revision history for this message
Guruprasad (lgp171188) wrote :

Left a small comment. Don't forget to add appropriate tests.

Revision history for this message
Ines Almeida (ines-almeida) :
Revision history for this message
Ines Almeida (ines-almeida) wrote :

Rebased with latest changes from API, added new tests for the display formatter and updated existing rst tests

Revision history for this message
Simone Pelosi (pelpsi) wrote :

LGTM

review: Approve
Revision history for this message
Guruprasad (lgp171188) wrote :

LGTM 👍

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/canonical/launchpad/icing/css/components/_index.scss b/lib/canonical/launchpad/icing/css/components/_index.scss
2index 3facac8..59e25ed 100644
3--- a/lib/canonical/launchpad/icing/css/components/_index.scss
4+++ b/lib/canonical/launchpad/icing/css/components/_index.scss
5@@ -6,4 +6,5 @@
6 'bug_listing',
7 'portlets',
8 'sharing',
9+ 'social_accounts',
10 'yui_picker';
11diff --git a/lib/canonical/launchpad/icing/css/components/social_accounts.scss b/lib/canonical/launchpad/icing/css/components/social_accounts.scss
12new file mode 100644
13index 0000000..8284e29
14--- /dev/null
15+++ b/lib/canonical/launchpad/icing/css/components/social_accounts.scss
16@@ -0,0 +1,12 @@
17+.social_accounts {
18+ &__icon {
19+ margin-right: 0.5em;
20+ height: 1.4em;
21+ width: 1.4em;
22+ margin-bottom: -0.3em;
23+ }
24+
25+ & a.action-icon {
26+ padding-bottom: 0;
27+ }
28+}
29diff --git a/lib/canonical/launchpad/images/social-irc.png b/lib/canonical/launchpad/images/social-irc.png
30new file mode 100644
31index 0000000..2676869
32Binary files /dev/null and b/lib/canonical/launchpad/images/social-irc.png differ
33diff --git a/lib/canonical/launchpad/images/social-jabber.png b/lib/canonical/launchpad/images/social-jabber.png
34new file mode 100644
35index 0000000..9303f48
36Binary files /dev/null and b/lib/canonical/launchpad/images/social-jabber.png differ
37diff --git a/lib/canonical/launchpad/images/social-matrix.png b/lib/canonical/launchpad/images/social-matrix.png
38new file mode 100644
39index 0000000..1004d71
40Binary files /dev/null and b/lib/canonical/launchpad/images/social-matrix.png differ
41diff --git a/lib/canonical/launchpad/images/src/social-irc.svg b/lib/canonical/launchpad/images/src/social-irc.svg
42new file mode 100644
43index 0000000..099302d
44--- /dev/null
45+++ b/lib/canonical/launchpad/images/src/social-irc.svg
46@@ -0,0 +1,18 @@
47+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
48+<svg
49+ version="1.1"
50+ viewBox="0 0 31.933693 32.009163"
51+ id="svg334"
52+ width="31.933693"
53+ height="32.009163"
54+ xml:space="preserve"
55+ xmlns="http://www.w3.org/2000/svg"
56+ xmlns:svg="http://www.w3.org/2000/svg"><defs
57+ id="defs338" /><title
58+ id="title324">IRC logo</title><path
59+ style="fill:none;stroke:#000000;stroke-width:1.074;stroke-dasharray:none"
60+ d="m 27.310407,29.54155 c 0,0 -0.35044,1.07703 -1.82255,0.359306 -0.92858,-0.45246 -6.07272,-3.001609 -9.748575,-4.824457 H 8.8533222 c -4.429365,0 -8.02035103,-3.590692 -8.02035103,-8.020356 V 9.6253625 c 0,-4.4293681 3.59098603,-8.0203563 8.02035103,-8.0203563 H 22.993417 c 4.42936,0 8.02036,3.5909882 8.02036,8.0203563 v 7.4306805 c 0,3.258297 -1.94321,6.063248 -4.73397,7.318305 z"
61+ id="path620" /><path
62+ d="m 18.894617,16.559403 -0.87765,3.777002 h -2.613422 l 0.877072,-3.777002 h -2.0494 l -0.853864,3.777002 H 10.810342 L 11.64191,16.559403 H 8.9808932 V 14.492537 H 12.112526 L 12.609062,12.47689 H 9.7886582 v -2.085846 h 3.2693308 l 0.877665,-3.7956822 h 2.612803 l -0.876758,3.7956822 h 2.095488 l 0.87736,-3.7956822 h 2.56762 l -0.87706,3.7956822 h 2.6827 v 2.085846 h -3.15452 l -0.49624,2.015647 h 2.8891 v 2.066866 z m -2.11898,-2.066866 0.49652,-2.015647 h -2.071979 l -0.496831,2.015647 z"
63+ style="opacity:0.9;fill:#000000;stroke-width:0.301292"
64+ id="path622" /></svg>
65diff --git a/lib/canonical/launchpad/images/src/social-jabber.svg b/lib/canonical/launchpad/images/src/social-jabber.svg
66new file mode 100644
67index 0000000..93f7090
68--- /dev/null
69+++ b/lib/canonical/launchpad/images/src/social-jabber.svg
70@@ -0,0 +1,17 @@
71+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
72+<svg
73+ version="1.1"
74+ viewBox="0 0 31.933697 32.009163"
75+ id="svg334"
76+ width="31.933697"
77+ height="32.009163"
78+ xml:space="preserve"
79+ xmlns="http://www.w3.org/2000/svg"
80+ xmlns:svg="http://www.w3.org/2000/svg"><defs
81+ id="defs338" /><title
82+ id="title324">Jabber logo</title><g
83+ style="fill:#000000"
84+ id="g495"
85+ transform="matrix(1.3240641,0,0,1.3240641,-0.32920441,0.12153848)"><path
86+ d="m 9.597,11.737 c 0,-0.35 -0.065,-0.732 -0.268,-1.025 -0.408,-0.588 -1.283,-0.775 -1.892,-0.405 -0.308,0.188 -0.48,0.515 -0.576,0.851 -0.191,0.668 -0.104,1.43 0.03,2.1 0.043,0.214 0.088,0.428 0.148,0.639 0.021,0.076 0.031,0.186 0.08,0.25 0.087,0.11 0.297,0.141 0.426,0.12 0.387,-0.065 0.291,-0.703 0.278,-0.974 -0.03,-0.634 -0.218,-1.25 -0.036,-1.881 0.076,-0.268 0.225,-0.568 0.494,-0.684 0.244,-0.105 0.49,0.023 0.586,0.261 0.156,0.385 0.117,0.83 0.215,1.23 0.033,0.137 0.07,0.272 0.131,0.399 0.018,0.037 0.043,0.113 0.094,0.108 0.126,-0.011 0.304,-0.22 0.398,-0.298 0.304,-0.25 0.616,-0.52 0.965,-0.705 0.165,-0.088 0.435,-0.23 0.603,-0.08 a 0.612,0.612 0 0 1 0.108,0.13 c 0.198,0.31 0.002,0.55 -0.127,0.845 -0.166,0.38 -0.336,0.758 -0.577,1.098 -0.207,0.293 -0.49,0.549 -0.655,0.869 -0.107,0.205 -0.167,0.43 -0.123,0.663 0.036,0.188 0.181,0.301 0.373,0.257 0.143,-0.033 0.24,-0.156 0.322,-0.269 0.146,-0.202 0.281,-0.412 0.426,-0.615 0.28,-0.393 0.61,-0.76 0.846,-1.183 a 3.41,3.41 0 0 0 0.42,-1.664 c 0,-0.474 -0.171,-1.198 -0.723,-1.298 a 0.974,0.974 0 0 0 -0.326,0.01 1.432,1.432 0 0 0 -0.374,0.12 2.715,2.715 0 0 0 -0.818,0.637 c -0.146,0.16 -0.276,0.363 -0.449,0.495 M 9.078,0.016 C 8.643,0.074 8.2,0.068 7.763,0.136 6.925,0.265 6.123,0.525 5.338,0.839 5.052,0.953 4.77,1.08 4.493,1.215 4.39,1.265 4.233,1.305 4.15,1.385 4.107,1.426 4.111,1.524 4.106,1.58 4.092,1.736 4.072,1.893 4.056,2.05 3.998,2.655 3.956,3.279 4.043,3.884 4.071,4.079 4.133,4.434 4.373,4.471 4.742,4.529 5.029,4.074 5.21,3.823 5.634,3.237 6.115,2.691 6.81,2.429 7.627,2.121 8.563,2.048 9.428,1.989 c 2.426,-0.167 5.078,0.277 6.865,2.064 0.254,0.254 0.495,0.524 0.7,0.82 0.8,1.159 1.223,2.477 1.427,3.86 0.096,0.65 0.161,1.308 0.013,1.955 -0.257,1.122 -0.932,2.1 -1.706,2.931 -0.53,0.57 -1.128,1.084 -1.749,1.552 -0.347,0.261 -0.736,0.483 -1.062,0.768 -0.375,0.329 -0.688,0.74 -0.925,1.179 -0.639,1.181 -0.81,2.602 -0.622,3.92 0.038,0.27 0.073,0.542 0.134,0.809 0.018,0.08 0.022,0.217 0.073,0.282 0.097,0.122 0.36,0.189 0.508,0.196 0.154,0.007 0.256,-0.11 0.294,-0.249 0.064,-0.236 0.026,-0.498 -0.012,-0.736 -0.076,-0.487 -0.147,-0.977 -0.125,-1.471 a 3.71,3.71 0 0 1 1.026,-2.425 c 0.643,-0.673 1.512,-1.061 2.243,-1.625 1.474,-1.136 2.794,-2.668 3.301,-4.492 A 5.194,5.194 0 0 0 19.97,9.312 C 19.865,8.463 19.555,7.615 19.262,6.815 18.37,4.378 16.84,2.06 14.411,0.945 13.447,0.502 12.438,0.3 11.395,0.155 10.905,0.087 10.415,0.045 9.923,0.023 9.649,0.011 9.351,-0.019 9.078,0.017 M 5.277,15.796 c -0.473,0.068 -0.61,0.447 -0.523,0.876 0.112,0.548 0.543,0.965 0.97,1.295 a 6.03,6.03 0 0 0 3.884,1.238 c 0.538,-0.023 1.124,-0.112 1.617,-0.34 0.265,-0.122 0.542,-0.563 0.181,-0.751 A 0.59,0.59 0 0 0 11.237,18.063 C 11.08,18.037 10.904,18.104 10.755,18.147 10.492,18.222 10.229,18.3 9.958,18.343 9.15,18.473 8.275,18.288 7.606,17.809 7.064,17.422 6.626,16.911 6.213,16.394 5.96,16.078 5.731,15.731 5.277,15.796 m -0.615,2.678 c -0.12,0.016 -0.259,0.011 -0.362,0.087 -0.215,0.158 0.022,0.476 0.135,0.62 0.328,0.417 0.76,0.763 1.192,1.068 a 7.832,7.832 0 0 0 4.03,1.442 c 0.421,0.03 0.85,0 1.267,-0.07 0.152,-0.026 0.342,-0.037 0.482,-0.103 0.399,-0.186 0.284,-0.939 -0.072,-1.106 -0.155,-0.073 -0.404,0.023 -0.567,0.046 -0.385,0.054 -0.771,0.06 -1.158,0.05 C 8.594,20.483 7.513,20.17 6.629,19.677 A 5.589,5.589 0 0 1 5.663,18.984 C 5.482,18.824 5.295,18.564 5.06,18.482 4.95,18.445 4.776,18.459 4.662,18.474 M 4.903,20.73 A 0.638,0.638 0 0 0 4.49,20.966 c -0.078,0.088 -0.152,0.167 -0.197,0.278 -0.246,0.609 0.41,1.183 0.864,1.47 0.504,0.32 1.055,0.558 1.616,0.758 1.266,0.45 2.752,0.739 4.066,0.336 0.391,-0.12 0.778,-0.338 1.062,-0.634 0.16,-0.167 0.27,-0.419 -0.024,-0.526 -0.174,-0.063 -0.385,0.098 -0.543,0.162 a 4.57,4.57 0 0 1 -1.158,0.312 C 9.649,23.186 9.175,23.07 8.668,22.943 A 11.982,11.982 0 0 1 7.377,22.57 4.457,4.457 0 0 1 6.351,22.057 C 6.257,21.991 6.145,21.932 6.069,21.846 5.819,21.564 5.63,21.234 5.362,20.966 5.246,20.85 5.081,20.71 4.903,20.73"
87+ id="path486" /></g></svg>
88diff --git a/lib/canonical/launchpad/images/src/social-matrix.svg b/lib/canonical/launchpad/images/src/social-matrix.svg
89new file mode 100644
90index 0000000..747f705
91--- /dev/null
92+++ b/lib/canonical/launchpad/images/src/social-matrix.svg
93@@ -0,0 +1,21 @@
94+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
95+<svg
96+ version="1.1"
97+ viewBox="0 0 31.933697 32.009163"
98+ id="svg334"
99+ width="31.933697"
100+ height="32.009163"
101+ xml:space="preserve"
102+ xmlns="http://www.w3.org/2000/svg"
103+ xmlns:svg="http://www.w3.org/2000/svg"><defs
104+ id="defs338" /><title
105+ id="title324">Matrix (protocol) logo</title><g
106+ transform="translate(2.0261671,0.05164569)"
107+ fill="#040404"
108+ id="g460"><path
109+ d="M 27.1,31.2 V 0.7 h -2.19 v -0.732 h 3.04 v 32 h -3.04 v -0.732 z"
110+ id="path454" /><path
111+ d="m 8.23,10.4 v 1.54 h 0.044 c 0.385,-0.564 0.893,-1.03 1.49,-1.37 0.58,-0.323 1.25,-0.485 1.99,-0.485 0.72,0 1.38,0.14 1.97,0.42 0.595,0.279 1.05,0.771 1.36,1.48 0.338,-0.5 0.796,-0.941 1.38,-1.32 0.58,-0.383 1.27,-0.574 2.06,-0.574 0.602,0 1.16,0.074 1.67,0.22 0.514,0.148 0.954,0.383 1.32,0.707 0.366,0.323 0.653,0.746 0.859,1.27 0.205,0.522 0.308,1.15 0.308,1.89 v 7.63 h -3.13 v -6.46 c 0,-0.383 -0.015,-0.743 -0.044,-1.08 -0.0209,-0.307 -0.103,-0.607 -0.242,-0.882 -0.133,-0.251 -0.336,-0.458 -0.584,-0.596 -0.257,-0.146 -0.606,-0.22 -1.05,-0.22 -0.44,0 -0.796,0.085 -1.07,0.253 -0.272,0.17 -0.485,0.39 -0.639,0.662 -0.159,0.287 -0.264,0.602 -0.308,0.927 -0.052,0.347 -0.078,0.697 -0.078,1.05 v 6.35 h -3.13 v -6.4 c 0,-0.338 -0.007,-0.673 -0.021,-1 -0.0114,-0.314 -0.0749,-0.623 -0.188,-0.916 -0.108,-0.277 -0.3,-0.512 -0.55,-0.673 -0.258,-0.168 -0.636,-0.253 -1.14,-0.253 -0.198,0.0083 -0.394,0.042 -0.584,0.1 -0.258,0.0745 -0.498,0.202 -0.705,0.374 -0.228,0.184 -0.422,0.449 -0.584,0.794 -0.161,0.346 -0.242,0.798 -0.242,1.36 v 6.62 h -3.13 v -11.4 z"
112+ id="path456" /><path
113+ d="m 0.936,0.732 v 30.5 h 2.19 v 0.732 h -3.04 v -32 h 3.03 v 0.732 z"
114+ id="path458" /></g></svg>
115diff --git a/lib/lp/app/browser/configure.zcml b/lib/lp/app/browser/configure.zcml
116index 9391c39..70e44d9 100644
117--- a/lib/lp/app/browser/configure.zcml
118+++ b/lib/lp/app/browser/configure.zcml
119@@ -669,6 +669,13 @@
120 />
121
122 <adapter
123+ for="lp.registry.interfaces.socialaccount.ISocialAccount"
124+ provides="zope.traversing.interfaces.IPathAdapter"
125+ factory="lp.app.browser.tales.SocialAccountFormatterAPI"
126+ name="fmt"
127+ />
128+
129+ <adapter
130 for="datetime.timedelta"
131 provides="zope.traversing.interfaces.IPathAdapter"
132 factory="lp.app.browser.tales.DurationFormatterAPI"
133diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py
134index eb5cad2..6c962ea 100644
135--- a/lib/lp/app/browser/tales.py
136+++ b/lib/lp/app/browser/tales.py
137@@ -55,6 +55,7 @@ from lp.registry.interfaces.distributionsourcepackage import (
138 from lp.registry.interfaces.person import IPerson
139 from lp.registry.interfaces.product import IProduct
140 from lp.registry.interfaces.projectgroup import IProjectGroup
141+from lp.registry.interfaces.socialaccount import SOCIAL_PLATFORM_TYPES_MAP
142 from lp.services.compat import tzname
143 from lp.services.utils import round_half_up
144 from lp.services.webapp.authorization import check_permission
145@@ -3049,3 +3050,37 @@ class IRCNicknameFormatterAPI(ObjectFormatterAPI):
146 self._context.nickname,
147 self._context.network,
148 ).escapedtext
149+
150+
151+@implementer(ITraversable)
152+class SocialAccountFormatterAPI(ObjectFormatterAPI):
153+ """Adapter from social account objects to a formatted string."""
154+
155+ traversable_names = {
156+ "formatted_display": "formatted_display",
157+ }
158+
159+ def getPlatformClass(self):
160+ return SOCIAL_PLATFORM_TYPES_MAP.get(self._context.platform)
161+
162+ def icon(self, platform):
163+ return (
164+ f'<img class="social_accounts__icon" alt="{platform.title}" '
165+ f'title="{platform.title}" src="/@@/{platform.icon}" />'
166+ )
167+
168+ def formatted_display(self, view_name=None):
169+ platform = self.getPlatformClass()
170+ icon = self.icon(platform)
171+
172+ parsed_identity = {}
173+ for key, value in self._context.identity.items():
174+ parsed_identity[key] = quote(value)
175+
176+ text_display = platform.display_format.format(**parsed_identity)
177+
178+ if platform.url:
179+ url = platform.url.format(**parsed_identity)
180+ text_display = f'<a href={url} target="_blank">{text_display}</a>'
181+
182+ return structured(f"{icon} {text_display}").escapedtext
183diff --git a/lib/lp/app/tests/test_tales.py b/lib/lp/app/tests/test_tales.py
184index 3fd6f32..b219d7e 100644
185--- a/lib/lp/app/tests/test_tales.py
186+++ b/lib/lp/app/tests/test_tales.py
187@@ -17,6 +17,10 @@ from lp.app.browser.tales import (
188 )
189 from lp.registry.interfaces.irc import IIrcIDSet
190 from lp.registry.interfaces.person import PersonVisibility
191+from lp.registry.interfaces.socialaccount import (
192+ ISocialAccountSet,
193+ SocialPlatformType,
194+)
195 from lp.services.webapp.authorization import (
196 check_permission,
197 clear_cache,
198@@ -385,6 +389,61 @@ class TestIRCNicknameFormatterAPI(TestCaseWithFactory):
199 )
200
201
202+class TestSocialAccountFormatterAPI(TestCaseWithFactory):
203+ """Tests for SocialAccountFormatterAPI"""
204+
205+ layer = DatabaseFunctionalLayer
206+
207+ def test_formatted_display(self):
208+ """Social account is displayed as expected."""
209+ person = self.factory.makePerson()
210+ social_account_set = getUtility(ISocialAccountSet)
211+ identity = {
212+ "username": "fred",
213+ "homeserver": "ubuntu.com",
214+ }
215+ social_account = social_account_set.new(
216+ person, SocialPlatformType.MATRIX, identity
217+ )
218+ expected_html = (
219+ '<img class="social_accounts__icon" alt="Matrix" title="Matrix" '
220+ 'src="/@@/social-matrix" /> <a href=https://matrix.to//#/@fred'
221+ ':ubuntu.com target="_blank"><strong>@fred:ubuntu.com</strong></a>'
222+ )
223+
224+ self.assertEqual(
225+ expected_html,
226+ test_tales(
227+ "account/fmt:formatted_display", account=social_account
228+ ),
229+ )
230+
231+ def test_formatted_display_escaping(self):
232+ """Social account is displayed as expected even when there is markup"""
233+ person = self.factory.makePerson()
234+ social_account_set = getUtility(ISocialAccountSet)
235+ # Include some bogus markup to check escaping works.
236+ identity = {
237+ "username": "<b>fred</b>",
238+ "homeserver": "ubuntu.com",
239+ }
240+ social_account = social_account_set.new(
241+ person, SocialPlatformType.MATRIX, identity
242+ )
243+ expected_html = (
244+ '<img class="social_accounts__icon" alt="Matrix" title="Matrix" '
245+ 'src="/@@/social-matrix" /> <a href=https://matrix.to//#/@%3Cb%3E'
246+ 'fred%3C/b%3E:ubuntu.com target="_blank">'
247+ "<strong>@%3Cb%3Efred%3C/b%3E:ubuntu.com</strong></a>"
248+ )
249+ self.assertEqual(
250+ expected_html,
251+ test_tales(
252+ "account/fmt:formatted_display", account=social_account
253+ ),
254+ )
255+
256+
257 class ObjectImageDisplayAPITestCase(TestCaseWithFactory):
258 """Tests for ObjectImageDisplayAPI"""
259
260diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py
261index be1ba98..de0003d 100644
262--- a/lib/lp/registry/browser/person.py
263+++ b/lib/lp/registry/browser/person.py
264@@ -157,6 +157,7 @@ from lp.registry.interfaces.socialaccount import (
265 ISocialAccountSet,
266 MatrixPlatform,
267 SocialAccountIdentityError,
268+ SocialPlatformType,
269 )
270 from lp.registry.interfaces.ssh import ISSHKeySet, SSHKeyAdditionError
271 from lp.registry.interfaces.teammembership import (
272@@ -1681,6 +1682,25 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
273 )
274
275 @property
276+ def should_show_matrix_accounts_section(self):
277+ """Should the matrix accounts section be shown?
278+
279+ It's shown when the person has social accounts for the Matrix platform
280+ registered or has rights to register new ones.
281+ """
282+ return bool(self.matrix_accounts) or (
283+ check_permission("launchpad.Edit", self.context)
284+ )
285+
286+ @property
287+ def should_show_socialaccounts_section(self):
288+ return (
289+ self.should_show_ircnicknames_section
290+ or self.should_show_jabberids_section
291+ or self.should_show_matrix_accounts_section
292+ )
293+
294+ @property
295 def should_show_sshkeys_section(self):
296 """Should the 'SSH keys' section be shown?
297
298@@ -1708,6 +1728,12 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
299 return self.context.gpg_keys
300
301 @cachedproperty
302+ def matrix_accounts(self):
303+ return self.context.getSocialAccountsByPlatform(
304+ SocialPlatformType.MATRIX
305+ )
306+
307+ @cachedproperty
308 def is_probationary_or_invalid_user(self):
309 """True when the user is not active or does not have karma.
310
311diff --git a/lib/lp/registry/interfaces/socialaccount.py b/lib/lp/registry/interfaces/socialaccount.py
312index 1dcb7c8..94e8d78 100644
313--- a/lib/lp/registry/interfaces/socialaccount.py
314+++ b/lib/lp/registry/interfaces/socialaccount.py
315@@ -106,6 +106,9 @@ class SocialPlatform:
316 title = ""
317 identity_fields = []
318 platform_type = None
319+ icon = ""
320+ display_format = ""
321+ url = None
322
323 @classmethod
324 def validate_identity(cls, identity):
325@@ -117,6 +120,9 @@ class MatrixPlatform(SocialPlatform):
326 title = "Matrix"
327 identity_fields = ["username", "homeserver"]
328 platform_type = SocialPlatformType.MATRIX
329+ icon = "social-matrix"
330+ display_format = "<strong>@{username}:{homeserver}</strong>"
331+ url = "https://matrix.to//#/@{username}:{homeserver}"
332
333 @classmethod
334 def validate_identity(cls, identity):
335@@ -144,6 +150,11 @@ class MatrixPlatform(SocialPlatform):
336 )
337
338
339+SOCIAL_PLATFORM_TYPES_MAP = {
340+ SocialPlatformType.MATRIX: MatrixPlatform,
341+}
342+
343+
344 @error_status(http.client.BAD_REQUEST)
345 class SocialAccountIdentityError(Exception):
346 """Raised when Social Account's identity is
347diff --git a/lib/lp/registry/stories/person/xx-person-edit-jabber-ids.rst b/lib/lp/registry/stories/person/xx-person-edit-jabber-ids.rst
348index 508da14..9566651 100644
349--- a/lib/lp/registry/stories/person/xx-person-edit-jabber-ids.rst
350+++ b/lib/lp/registry/stories/person/xx-person-edit-jabber-ids.rst
351@@ -47,9 +47,9 @@ it will be associated with their account.
352 >>> show_errors(user_browser)
353
354 >>> def show_jabberids(browser):
355- ... tags = find_tag_by_id(browser.contents, "jabber-ids")
356- ... for dd in tags.find_all("dd"):
357- ... print(extract_text(dd))
358+ ... tags = find_tags_by_class(browser.contents, "jabber_accounts")
359+ ... for item in tags:
360+ ... print(extract_text(item.find("span")))
361 ...
362
363 >>> show_jabberids(user_browser)
364@@ -64,5 +64,9 @@ checkbox besides the ID:
365 >>> user_browser.getControl("Remove", index=0).click()
366 >>> user_browser.getControl("Save Changes").click()
367
368- >>> show_jabberids(user_browser)
369+ >>> tags = find_tag_by_id(user_browser.contents, "empty-jabber")
370+ >>> for item in tags.find_all("span"):
371+ ... print(extract_text(item))
372+ ...
373+
374 No Jabber IDs registered.
375diff --git a/lib/lp/registry/stories/person/xx-person-edit-matrix-accounts.rst b/lib/lp/registry/stories/person/xx-person-edit-matrix-accounts.rst
376index df4a893..976b13f 100644
377--- a/lib/lp/registry/stories/person/xx-person-edit-matrix-accounts.rst
378+++ b/lib/lp/registry/stories/person/xx-person-edit-matrix-accounts.rst
379@@ -12,13 +12,30 @@ To register a Matrix account with their account, the user visits their
380 profile page and uses the 'Edit Matrix accounts' link.
381
382 >>> def go_to_edit_matrix_accounts_page(browser):
383- ... browser.open("http://launchpad.test/~no-priv/+editmatrixaccounts")
384+ ... browser.open("http://launchpad.test/~no-priv")
385+ ... browser.getLink("Edit Matrix accounts").click()
386 ...
387
388 >>> go_to_edit_matrix_accounts_page(user_browser)
389 >>> print(user_browser.title)
390 No Privileges Person's Matrix...
391
392+An anonymous user, isn't able to see the "Edit Matrix accounts" button, nor go
393+to the page directly:
394+
395+ >>> anon_browser.open("http://launchpad.test/~no-priv")
396+ >>> browser.getLink("Edit Matrix accounts")
397+ Traceback (most recent call last):
398+ ...
399+ AttributeError: 'NoneType' object ...
400+
401+ >>> anon_browser.open(
402+ ... "http://launchpad.test/~no-priv/+editmatrixaccounts"
403+ ... )
404+ Traceback (most recent call last):
405+ ...
406+ zope.security.interfaces.Unauthorized: ...
407+
408 The user enters the username and homeserver combination in the text inputs
409 and clicks on the 'Save Changes' button.
410
411@@ -27,19 +44,30 @@ and clicks on the 'Save Changes' button.
412 ... browser.getControl(name="new_username").value = username
413 ... browser.getControl("Save Changes").click()
414 ...
415-
416- >>> add_matrix_account(user_browser, "mark", "ubuntu.com")
417 >>> def show_notifications(browser, type):
418 ... for notification in find_tags_by_class(browser.contents, type):
419 ... print(extract_text(notification))
420 ...
421
422+ >>> def show_matrix_accounts(browser):
423+ ... for item in find_tags_by_class(
424+ ... browser.contents, "matrix_account"
425+ ... ):
426+ ... print(extract_text(item.find("span")))
427+ ...
428+
429+ >>> add_matrix_account(user_browser, "mark", "ubuntu.com")
430 >>> show_notifications(user_browser, "informational")
431 Matrix accounts saved successfully.
432
433+ >>> show_matrix_accounts(user_browser)
434+ @mark:ubuntu.com
435+
436 In this case, the user tried registering a Matrix account with invalid
437 usernames or homeservers, an error is displayed and the user can enter
438 another one:
439+
440+ >>> go_to_edit_matrix_accounts_page(user_browser)
441 >>> go_to_edit_matrix_accounts_page(user_browser)
442 >>> add_matrix_account(user_browser, "<b>mark</b>", "ubuntu.com")
443 >>> show_notifications(user_browser, "error")
444@@ -83,6 +111,9 @@ input fields:
445 >>> show_notifications(user_browser, "informational")
446 Matrix accounts saved successfully.
447
448+ >>> show_matrix_accounts(user_browser)
449+ @fred:test.com
450+
451 Edited fields will also show an error if not valid:
452
453 >>> go_to_edit_matrix_accounts_page(user_browser)
454@@ -99,10 +130,19 @@ Removing an account
455 -------------------
456
457 To remove an existing Matrix account, the user simply checks the 'Remove'
458-checkbox besides the ID:
459+checkbox besides the ID, and if there are no accounts left, UI informs the
460+user:
461+
462 >>> go_to_edit_matrix_accounts_page(user_browser)
463 >>> user_browser.getControl("Remove", index=0).click()
464 >>> user_browser.getControl("Save Changes").click()
465
466 >>> show_notifications(user_browser, "informational")
467 Matrix accounts saved successfully.
468+
469+ >>> matrix_section = find_tag_by_id(user_browser.contents, "empty-matrix")
470+ >>> print(extract_text(matrix_section.find_all("span")[0]))
471+ No matrix accounts registered.
472+
473+ >>> print(matrix_section.find("a").attrs["href"])
474+ http://.../~no-priv/+editmatrixaccounts
475diff --git a/lib/lp/registry/stories/person/xx-person-home.rst b/lib/lp/registry/stories/person/xx-person-home.rst
476index 2778686..03cd8d6 100644
477--- a/lib/lp/registry/stories/person/xx-person-home.rst
478+++ b/lib/lp/registry/stories/person/xx-person-home.rst
479@@ -104,16 +104,18 @@ Jabber IDs
480 A person's jabber IDs are only show to authenticated users.
481
482 >>> user_browser.open("http://launchpad.test/~mark")
483- >>> print(
484- ... extract_text(find_tag_by_id(user_browser.contents, "jabber-ids"))
485- ... )
486- Jabber: markshuttleworth@jabber.org
487+ >>> for item in find_tags_by_class(
488+ ... user_browser.contents, "jabber_account"
489+ ... ):
490+ ... print(extract_text(item.find("span")))
491+ markshuttleworth@jabber.org
492
493 >>> anon_browser.open("http://launchpad.test/~mark")
494- >>> print(
495- ... extract_text(find_tag_by_id(anon_browser.contents, "jabber-ids"))
496- ... )
497- Jabber: &lt;email address hidden&gt;
498+ >>> for item in find_tags_by_class(
499+ ... anon_browser.contents, "jabber_account"
500+ ... ):
501+ ... print(extract_text(item.find("span")))
502+ &lt;email address hidden&gt;
503
504
505 OpenPGP keys
506diff --git a/lib/lp/registry/templates/person-portlet-contact-details.pt b/lib/lp/registry/templates/person-portlet-contact-details.pt
507index e9b37b2..66d5c65 100644
508--- a/lib/lp/registry/templates/person-portlet-contact-details.pt
509+++ b/lib/lp/registry/templates/person-portlet-contact-details.pt
510@@ -170,38 +170,55 @@
511 class="sprite maybe action-icon">Karma help</a>
512 </dd>
513 </dl>
514- </div>
515-
516- <div class="yui-u two-column-list">
517- <dt>Social Accounts:</dt>
518- <dl tal:condition="view/should_show_ircnicknames_section">
519- <dt>IRC:
520- <a tal:replace="structure overview_menu/editircnicknames/fmt:icon" />
521- </dt>
522- <dd tal:repeat="ircnick context/ircnicknames">
523- <span tal:replace="structure ircnick/fmt:formatted_displayname"/>
524- </dd>
525- <dd tal:condition="not: context/ircnicknames">
526- No IRC nicknames registered.
527- </dd>
528- </dl>
529-
530- <dl tal:condition="view/should_show_jabberids_section" id="jabber-ids">
531- <dt>Jabber:
532- <a tal:replace="structure overview_menu/editjabberids/fmt:icon" />
533- </dt>
534- <dd>
535- <tal:block repeat="jabberid context/jabberids">
536- <span tal:replace="jabberid/jabberid/fmt:obfuscate-email"
537- /><span tal:condition="not: repeat/jabberid/end">,</span>
538- </tal:block>
539- <div tal:condition="context/jabberids/is_empty">
540- No Jabber IDs registered.
541- </div>
542- </dd>
543- </dl>
544- </div>
545+</div>
546
547+ <div class="yui-u social_accounts" tal:condition="view/should_show_socialaccounts_section">
548+ <dl id="social-accounts">
549+ <dt>Social accounts:</dt>
550+
551+ <dd class="social_accounts__item irc_account" tal:repeat="ircnick context/ircnicknames">
552+ <img class="social_accounts__icon" alt="IRC" title="IRC" src="/@@/social-irc"/>
553+ <span><span tal:replace="structure ircnick/fmt:formatted_displayname" /></span>
554+ <a tal:replace="structure overview_menu/editircnicknames/fmt:icon"/>
555+ </dd>
556+
557+ <dd class="social_accounts__item jabber_account" tal:repeat="jabberid context/jabberids">
558+ <img class="social_accounts__icon" alt="Jabber" title="Jabber" src="/@@/social-jabber" />
559+ <span><span tal:replace="jabberid/jabberid/fmt:obfuscate-email" /></span>
560+ <a tal:replace="structure overview_menu/editjabberids/fmt:icon"/>
561+ </dd>
562+
563+ <dd class="social_accounts__item matrix_account" tal:repeat="social_account view/matrix_accounts">
564+ <span><span tal:replace="structure social_account/fmt:formatted_display" /></span>
565+ <a tal:replace="structure overview_menu/editmatrixaccounts/fmt:icon" />
566+ </dd>
567+
568+ <tal:irc condition="view/should_show_ircnicknames_section">
569+ <dd class="social_accounts__item" tal:condition="not: context/ircnicknames" id="empty-irc">
570+ <img class="social_accounts__icon" alt="IRC" title="IRC" src="/@@/social-irc"/>
571+ <span>No IRC nicknames registered.</span>
572+ <a tal:replace="structure overview_menu/editircnicknames/fmt:icon"/>
573+ </dd>
574+ </tal:irc>
575+
576+ <tal:jabber condition="view/should_show_jabberids_section">
577+ <dd class="social_accounts__item" tal:condition="context/jabberids/is_empty" id="empty-jabber">
578+ <img class="social_accounts__icon" alt="Jabber" title="Jabber" src="/@@/social-jabber" />
579+ <span>No Jabber IDs registered.</span>
580+ <a tal:replace="structure overview_menu/editjabberids/fmt:icon" />
581+ </dd>
582+ </tal:jabber>
583+
584+ <tal:matrix condition="view/should_show_matrix_accounts_section">
585+ <dd class="social_accounts__item" tal:condition="not: view/matrix_accounts" id="empty-matrix">
586+ <img class="social_accounts__icon" alt="Matrix" title="Matrix" src="/@@/social-matrix" />
587+ <span>No matrix accounts registered.</span>
588+ <a tal:replace="structure overview_menu/editmatrixaccounts/fmt:icon" />
589+ </dd>
590+ </tal:matrix>
591+
592+ </dl>
593+ </div>
594 </div>
595
596 </tal:root>

Subscribers

People subscribed via source and target branches

to status/vote changes: