Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/templates/sitewide/user-profile-page.ezt b/templates/sitewide/user-profile-page.ezt
new file mode 100644
index 0000000..7517d15
--- /dev/null
+++ b/templates/sitewide/user-profile-page.ezt
@@ -0,0 +1,474 @@
+[define category_css]css/ph_list.css[end]
+[include "../framework/header.ezt" "showusertabs" "t1"]
+[include "../framework/js-placeholders.ezt"]
+<div id="colcontrol">
+
+<h2>
+ [if-any viewing_self][else]
+ [if-any user_stars_enabled]
+ [if-any logged_in_user]
+ [if-any read_only][else]
+ [if-any user_stars_enabled]
+ [if-any logged_in_user]
+ [if-any read_only][else]
+ <a id="user_star"
+ style="color:[if-any is_user_starred]cornflowerblue[else]gray[end]"
+ title="[if-any is_user_starred]Un-s[else]S[end]tar this user">
+ [if-any is_user_starred]★[else]☆[end]
+ </a>
+ [end]
+ [end]
+ [end]
+ [end]
+ [end]
+ [end]
+ [end]
+
+ [viewed_user_display_name]
+</h2>
+
+<p>
+ <b>Last visit:</b>
+ [last_visit_str]
+</p>
+
+[if-any last_bounce_str]
+ <p>
+ <b>Email to this user bounced:</b>
+ [last_bounce_str]
+ [define offer_clear_bouncing]No[end]
+ [if-any viewing_self][define offer_clear_bouncing]Yes[end][end]
+ [if-any perms._EditOtherUsers][define offer_clear_bouncing]Yes[end][end]
+ [is offer_clear_bouncing "Yes"]
+ <a href="[viewed_user.profile_url]clearBouncing" style="margin-left:2em">Clear</a>
+ [end]
+ </p>
+[end]
+
+[if-any vacation_message]
+ <p>
+ <b>Vacation message:</b>
+ [vacation_message]
+ </p>
+[end]
+
+[if-any linked_parent]
+ <p>
+ <b>Linked parent account:</b>
+ [include "../framework/user-link.ezt" linked_parent]
+ [if-any offer_unlink perms._EditOtherUsers]
+ <input type="button" class="unlink_account secondary"
+ data-parent="[linked_parent.email]"
+ data-child="[viewed_user.email]"
+ value="Unlink">
+ [end]
+ </p>
+[end]
+
+[if-any linked_children]
+ <p>
+ <b>Linked child accounts:</b>
+ [for linked_children]
+ [include "../framework/user-link.ezt" linked_children]
+ [if-any offer_unlink perms._EditOtherUsers]
+ <input type="button" class="unlink_account secondary"
+ data-parent="[viewed_user.email]"
+ data-child="[linked_children.email]"
+ value="Unlink">
+ [end]
+ [end]
+ </p>
+[end]
+
+[if-any incoming_invite_users]
+ <b>Accept linked sub-account:</b>
+ [for incoming_invite_users]
+ <div>
+ [include "../framework/user-link.ezt" incoming_invite_users]
+ [if-any can_edit_invites][# TODO(jrobbins): allow site admin to accept invites for other users.]
+ <input type="button" class="incoming_invite" data-email="[incoming_invite_users.email]" value="Accept">
+ [# TODO(jrobbins): Button to decline invite.]
+ [end]
+ </div>
+ [end]
+[else][if-any outgoing_invite_users]
+ <b>Waiting for acceptance by parent-account:</b>
+ [for outgoing_invite_users]
+ <div>
+ [include "../framework/user-link.ezt" outgoing_invite_users]
+ </div>
+ [end]
+[else][if-any possible_parent_accounts]
+ <b>Link this account to:</b>
+ <select id="parent_to_invite">
+ <option value="" selected="selected">----</option>
+ [for possible_parent_accounts]
+ <option value="[possible_parent_accounts]">[possible_parent_accounts]</option>
+ [end]
+ </select>
+ [if-any can_edit_invites][# TODO(jrobbins): allow site admin to create invites for other users.]
+ <button id="create_linked_account_invite" disabled="disabled">Link</button>
+ [end]
+[end][end][end]
+
+
+[if-any user_stars_enabled]
+<p>
+<b>Starred developers:</b>
+[if-any starred_users]
+[for starred_users]
+ [include "../framework/user-link.ezt" starred_users][if-index starred_users last][else], [end]
+[end]
+[else]<i>None</i>[end]
+</p>
+[end]
+<br>
+
+<div class="list">
+ <table style="width: 100%;" cellspacing="0" cellpadding="0">
+ <tbody><tr>
+ <th style="text-align: left;">Projects
+ </th>
+ </tr></tbody>
+ </table>
+</div>
+
+<table cellspacing="0" cellpadding="2" border="0" class="results striped" id="projecttable" width="100%">
+ <tbody>
+ <tr id="headingrow">
+ [if-any logged_in_user]
+ <th style="white-space:nowrap; width:3%;"></th>
+ [end]
+ <th style="white-space:nowrap; width:15%;">Role</th>
+ <th style="white-space:nowrap; width:25%;">Project</th>
+ <th style="white-space:nowrap; width:57%;">Summary</th>
+ </tr>
+ [if-any owner_of_projects committer_of_projects contributor_to_projects]
+ [if-any owner_of_projects]
+ [for owner_of_projects]
+ <tr data-url="[owner_of_projects.relative_home_url]" data-project-name="[owner_of_projects.project_name]">
+ [if-any logged_in_user]
+ <td class="rowwidgets">
+ <a class="star"
+ style="color:[if-any owner_of_projects.starred]cornflowerblue[else]gray[end]"
+ title="[if-any owner_of_projects.starred]Un-s[else]S[end]tar this project"
+ data-project-name="[owner_of_projects.project_name]">
+ [if-any owner_of_projects.starred]★[else]☆[end]
+ </a>
+ </td>
+ [end]
+ <td>Owner</td>
+ <td class="id" name="owner">
+ <a href="[owner_of_projects.relative_home_url]/">[owner_of_projects.project_name]</a>
+ [is owner_of_projects.state_name "HIDDEN"]<span style="color:red"> - hidden</span>[end]
+ </td>
+ <td>[owner_of_projects.summary]</td>
+ </tr>
+ [end]
+ [end]
+ [if-any committer_of_projects]
+ [for committer_of_projects]
+ <tr data-url="[committer_of_projects.relative_home_url]" data-project-name="[committer_of_projects.project_name]">
+ [if-any logged_in_user]
+ <td class="rowwidgets">
+ <a class="star"
+ style="color:[if-any committer_of_projects.starred]cornflowerblue[else]gray[end]"
+ title="[if-any committer_of_projects.starred]Un-s[else]S[end]tar this project"
+ data-project-name="[committer_of_projects.project_name]">
+ [if-any committer_of_projects.starred]★[else]☆[end]
+ </a>
+ </td>
+ [end]
+ <td>Committer</td>
+ <td class="id" name="committer">
+ <a href="[committer_of_projects.relative_home_url]/">[committer_of_projects.project_name]
+ </a>
+ </td>
+ <td>
+ [committer_of_projects.summary]
+ </td>
+ </tr>
+ [end]
+ [end]
+
+ [if-any contributor_to_projects]
+ [for contributor_to_projects]
+ <tr data-url="[contributor_to_projects.relative_home_url]" data-project-name="[contributor_to_projects.project_name]">
+ [if-any logged_in_user]
+ <td class="rowwidgets">
+ <a class="star"
+ style="color:[if-any contributor_to_projects.starred]cornflowerblue[else]gray[end]"
+ title="[if-any contributor_to_projects.starred]Un-s[else]S[end]tar this project"
+ data-project-name="[contributor_to_projects.project_name]">
+ [if-any contributor_to_projects.starred]★[else]☆[end]
+ </a>
+ </td>
+ [end]
+ <td>Contributor</td>
+ <td class="id" name="contributor">
+ <a href="[contributor_to_projects.relative_home_url]/">[contributor_to_projects.project_name]
+ </a>
+ [is contributor_to_projects.state_name "HIDDEN"]<span style="color:red"> - hidden</span>[end]</td>
+ <td>
+ [contributor_to_projects.summary]
+ </td>
+ </tr>
+ [end]
+ [end]
+
+ [else]
+ <tr>
+ <td colspan="4"><i>No projects.</i></td>
+ <tr>
+ [end]
+ </tbody>
+</table>
+
+
+[if-any starred_projects]
+<br>
+<div class="list">
+ <table style="width: 100%;" cellspacing="0" cellpadding="0">
+ <tbody><tr>
+ <th style="text-align: left;">
+ Starred by [if-any viewing_self]me[else]
+ [viewed_user_display_name]
+ [end]
+ </th>
+ </tr></tbody>
+ </table>
+</div>
+<table cellspacing="0" cellpadding="2" border="0" class="results striped" id="starredtable" width="100%">
+ <tbody>
+ <tr id="headingrow">
+ [if-any logged_in_user]
+ <th style="white-space:nowrap; width:3%;"></th>
+ [end]
+ <th style="white-space:nowrap; width:25%;">Name</th>
+ <th style="white-space:nowrap; width:57%;">Summary</th>
+ </tr>
+
+ [for starred_projects]
+ <tr data-url="[starred_projects.relative_home_url]" data-project-name="[starred_projects.project_name]">
+ [if-any logged_in_user]
+ <td class="rowwidgets">
+ <a class="star"
+ style="color:[if-any starred_projects.starred]cornflowerblue[else]gray[end]"
+ title="[if-any starred_projects.starred]Un-s[else]S[end]tar this project"
+ data-project-name="[starred_projects.project_name]">
+ [if-any starred_projects.starred]★[else]☆[end]
+ </a>
+ </td>
+ [end]
+ <td class="id" name="starred_project">
+ <a href="[starred_projects.relative_home_url]/">[starred_projects.project_name]</a>
+ [is starred_projects.state_name "HIDDEN"]<span style="color:red"> - hidden</span>[end]
+ </td>
+ <td>
+ [starred_projects.summary]
+ </td>
+ </tr>
+ [end]
+
+</table>
+[end]
+
+[if-any owner_of_archived_projects]
+<br>
+<div class="list">
+ <table style="width: 100%;" cellspacing="0" cellpadding="0">
+ <tbody><tr>
+ <th style="text-align: left;">Archived projects
+ </th>
+ </tr></tbody>
+ </table>
+</div>
+<table cellspacing="0" cellpadding="2" border="0" class="results striped" id="archivedtable" width="100%">
+ <tbody>
+ <tr id="headingrow">
+ <th style="white-space:nowrap; width:25%;">Name</th>
+ <th style="white-space:nowrap; width:60%;">Summary</th>
+ </tr>
+ [for owner_of_archived_projects]
+ <tr data-url="[owner_of_archived_projects.relative_home_url]/adminAdvanced">
+ <td class="id" name="deleted_project">[owner_of_archived_projects.project_name] -
+ <a href="[owner_of_archived_projects.relative_home_url]/adminAdvanced">Unarchive or delete</a>
+ </td>
+ <td>
+ [owner_of_archived_projects.summary]
+ </td>
+ </tr>
+ [end]
+</table>
+[end]
+
+[if-any user_groups]
+<br>
+<div class="list">
+ <table style="width: 100%;" cellspacing="0" cellpadding="0">
+ <tbody><tr>
+ <th style="text-align: left;">User groups
+ </th>
+ </tr></tbody>
+ </table>
+</div>
+<table cellspacing="0" cellpadding="2" border="0" class="results striped" id="usergrouptable" width="100%">
+ <tbody>
+ <tr id="headingrow">
+ <th style="white-space:nowrap; width:25%;">Name</th>
+ </tr>
+ [for user_groups]
+ <tr data-url="[user_groups.profile_url]">
+ <td class="id">
+ <a href="[user_groups.profile_url]">[user_groups.email]</a>
+ </td>
+ </tr>
+ [end]
+ </tbody>
+</table>
+[end]
+
+[if-any can_ban]
+ <form action="ban.do" method="POST">
+ <input type="hidden" name="token" value="[ban_token]">
+ <h4>Banned for abuse</h4>
+ <div style="margin:0 0 2em 2em">
+ <input type="checkbox" name="banned" id="banned" value="1"
+ [if-any settings_user_is_banned]checked="checked"[end] >
+ <label for="banned">This user is banned because:</label>
+ <input type="text" size="50" name="banned_reason" id="banned_reason" value="[settings_user_pb.banned]">
+ </div>
+
+ <div style="margin:0 0 2em 2em">
+ <input id="submit_btn" type="submit" name="btn"
+ value="Update banned status">
+ </div>
+
+ </form>
+
+ [if-any viewed_user_is_spammer]
+ <form action="banSpammer.do" method="POST">
+ <input type="hidden" name="token" value="[ban_spammer_token]">
+ <input type="hidden" size="50" name="banned_reason" id="banned_reason" value="">
+ <input type="submit" name="undoBanSpammerButton" id="undo_ban_spammer_btn" value="Un-ban this user as a spammer">
+ </form>
+ [end]
+
+
+ [if-any viewed_user_may_be_spammer]
+ <form action="banSpammer.do" method="POST">
+ <input type="hidden" name="token" value="[ban_spammer_token]">
+ <input type="hidden" name="banned" value="True">
+ <input type="hidden" size="50" name="banned_reason" id="banned_reason" value="Spam">
+ <input type="submit" name="banSpammerButton" id="ban_spammer_btn" value="Ban this user as a spammer">
+ </form>
+ [end]
+
+[end]
+
+[if-any perms._EditOtherUsers]
+<h3 style="clear:both">Edit user</h3>
+ <form action="edit.do" method="POST">
+ <input type="hidden" name="token" value="[form_token]">
+ <h4>Site administration</h4>
+ <div style="margin:0 0 2em 2em">
+ <input type="checkbox" name="site_admin" id="site_admin" value="1" [if-any viewed_user_pb.is_site_admin_bool]checked="checked"[end] >
+ <label for="site_admin">This user is a site administrator (a super user)</label>
+ </div>
+
+ [include "unified-settings.ezt"]
+
+ <div style="margin:0 0 2em 2em">
+ <input id="submit_btn" type="submit" name="btn"
+ value="Save changes">
+ </div>
+
+ </form>
+[end]
+
+[if-any can_delete_user]
+<h3 style="clear:both">Delete user account</h3>
+ <p>Deleting a user account deletes the user and most user owned items from the site.
+ The user's email will be removed from any issues that the user participated in.
+ Hotlists owned by the user will either be transferred to another editor or get deleted.
+ Any Project Rules that the user is involved in will get deleted.
+ </p>
+ <div style="margin:0 0 2em 2em">
+ <input id="delete_btn" type="submit" name="btn" value="Delete user account">
+ <div id="delete_error" class="fielderror"></div>
+ </div>
+[end]
+
+</div>
+</div>
+
+<script type="text/javascript" nonce="[nonce]">
+runOnLoad(function() {
+ if ($("user_star")) {
+ [# The user viewing this page wants to star the user *on* this page]
+ $("user_star").addEventListener("click", function () {
+ _TKR_toggleStar($("user_star"), null, null, "[viewed_user_id]", null, null);
+ });
+ }
+
+ var stars = document.getElementsByClassName("star");
+ for (var i = 0; i < stars.length; ++i) {
+ var star = stars[[]i];
+ star.addEventListener("click", function (event) {
+ var projectName = event.target.getAttribute("data-project-name");
+ _TKR_toggleStar(event.target, projectName);
+ });
+ }
+
+ function _handleProjectClick(event) {
+ var target = event.target;
+ if (target.tagName == "A")
+ return;
+
+ if (target.classList.contains("rowwidgets") || target.parentNode.classList.contains("rowwidgets"))
+ return;
+ if (target.tagName != "TR") target = target.parentNode;
+ _go(target.attributes[[]"data-url"].value,
+ (event.metaKey || event.ctrlKey || event.button == 1));
+ };
+ $("projecttable").addEventListener("click", _handleProjectClick);
+ if ($("starredtable")) {
+ $("starredtable").addEventListener("click", _handleProjectClick);
+ }
+ if ($("archivedtable")) {
+ $("archivedtable").addEventListener("click", _handleProjectClick);
+ }
+
+ if ($("banned_reason")) {
+ $("banned_reason").addEventListener("keyup", function() {
+ $("banned").checked = $("banned_reason").value != "";
+ });
+ }
+
+ if ($("ban_spammer_btn")) {
+ $("ban_spammer_btn").addEventListener("click", function(evt) {
+ var ok = window.confirm("This will remove all issues and comments " +
+ "created by this user. Continue?");
+ if (!ok) {
+ evt.preventDefault();
+ }
+ });
+ }
+
+ if ($("delete_btn")) {
+ $("delete_btn").addEventListener("click", async function(event) {
+ const expungeCall = window.prpcClient.call(
+ 'monorail.Users', 'ExpungeUser', {email: "[viewed_user_display_name]"});
+ expungeCall.then((resp) => {
+ location.replace(location.origin);
+ }).catch((reason) => {
+ $("delete_error").textContent = reason;
+ });
+ });
+ }
+});
+</script>
+<script type="module" defer src="[version_base]/static/js/sitewide/linked-accounts.js" nonce="[nonce]"></script>
+
+
+[include "../framework/footer.ezt"]