Merge ~ines-almeida/launchpad:social-accounts-display-view into launchpad:master
- Git
- lp:~ines-almeida/launchpad
- social-accounts-display-view
- Merge into 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) |
Related bugs: |
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
Description of the change
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
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/canonical/launchpad/icing/css/components/_index.scss b/lib/canonical/launchpad/icing/css/components/_index.scss |
2 | index 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'; |
11 | diff --git a/lib/canonical/launchpad/icing/css/components/social_accounts.scss b/lib/canonical/launchpad/icing/css/components/social_accounts.scss |
12 | new file mode 100644 |
13 | index 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 | +} |
29 | diff --git a/lib/canonical/launchpad/images/social-irc.png b/lib/canonical/launchpad/images/social-irc.png |
30 | new file mode 100644 |
31 | index 0000000..2676869 |
32 | Binary files /dev/null and b/lib/canonical/launchpad/images/social-irc.png differ |
33 | diff --git a/lib/canonical/launchpad/images/social-jabber.png b/lib/canonical/launchpad/images/social-jabber.png |
34 | new file mode 100644 |
35 | index 0000000..9303f48 |
36 | Binary files /dev/null and b/lib/canonical/launchpad/images/social-jabber.png differ |
37 | diff --git a/lib/canonical/launchpad/images/social-matrix.png b/lib/canonical/launchpad/images/social-matrix.png |
38 | new file mode 100644 |
39 | index 0000000..1004d71 |
40 | Binary files /dev/null and b/lib/canonical/launchpad/images/social-matrix.png differ |
41 | diff --git a/lib/canonical/launchpad/images/src/social-irc.svg b/lib/canonical/launchpad/images/src/social-irc.svg |
42 | new file mode 100644 |
43 | index 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> |
65 | diff --git a/lib/canonical/launchpad/images/src/social-jabber.svg b/lib/canonical/launchpad/images/src/social-jabber.svg |
66 | new file mode 100644 |
67 | index 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> |
88 | diff --git a/lib/canonical/launchpad/images/src/social-matrix.svg b/lib/canonical/launchpad/images/src/social-matrix.svg |
89 | new file mode 100644 |
90 | index 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> |
115 | diff --git a/lib/lp/app/browser/configure.zcml b/lib/lp/app/browser/configure.zcml |
116 | index 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" |
133 | diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py |
134 | index 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 |
183 | diff --git a/lib/lp/app/tests/test_tales.py b/lib/lp/app/tests/test_tales.py |
184 | index 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 | |
260 | diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py |
261 | index 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 | |
311 | diff --git a/lib/lp/registry/interfaces/socialaccount.py b/lib/lp/registry/interfaces/socialaccount.py |
312 | index 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 |
347 | diff --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 |
348 | index 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. |
375 | diff --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 |
376 | index 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 |
475 | diff --git a/lib/lp/registry/stories/person/xx-person-home.rst b/lib/lp/registry/stories/person/xx-person-home.rst |
476 | index 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: <email address hidden> |
498 | + >>> for item in find_tags_by_class( |
499 | + ... anon_browser.contents, "jabber_account" |
500 | + ... ): |
501 | + ... print(extract_text(item.find("span"))) |
502 | + <email address hidden> |
503 | |
504 | |
505 | OpenPGP keys |
506 | diff --git a/lib/lp/registry/templates/person-portlet-contact-details.pt b/lib/lp/registry/templates/person-portlet-contact-details.pt |
507 | index 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> |
Display social accounts page: /pasteboard. co/OK17RhhgO6Fd .png /pasteboard. co/MfMHZs4f970z .png
Before: https:/
After: https:/