Dave Borowitz | 8cdc76b | 2018-03-26 10:04:27 -0400 | [diff] [blame] | 1 | /** |
| 2 | * @license |
Ben Rohlfs | 94fcbbc | 2022-05-27 10:45:03 +0200 | [diff] [blame] | 3 | * Copyright 2017 Google LLC |
| 4 | * SPDX-License-Identifier: Apache-2.0 |
Dave Borowitz | 8cdc76b | 2018-03-26 10:04:27 -0400 | [diff] [blame] | 5 | */ |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 6 | import '../gr-access-section/gr-access-section'; |
Ben Rohlfs | 678e19d | 2023-01-13 14:26:14 +0000 | [diff] [blame^] | 7 | import {singleDecodeURL} from '../../../utils/url-util'; |
Ben Rohlfs | aa53390 | 2022-09-22 09:07:12 +0200 | [diff] [blame] | 8 | import {navigationToken} from '../../core/gr-navigation/gr-navigation'; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 9 | import {toSortedPermissionsArray} from '../../../utils/access-util'; |
Dmitrii Filippov | e3c09ae | 2020-07-10 11:39:50 +0200 | [diff] [blame] | 10 | import { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 11 | RepoName, |
| 12 | ProjectInfo, |
| 13 | CapabilityInfoMap, |
| 14 | LabelNameToLabelTypeInfoMap, |
| 15 | ProjectAccessInput, |
| 16 | GitRef, |
| 17 | UrlEncodedRepoName, |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 18 | RepoAccessGroups, |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 19 | } from '../../../types/common'; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 20 | import {GrButton} from '../../shared/gr-button/gr-button'; |
| 21 | import {GrAccessSection} from '../gr-access-section/gr-access-section'; |
| 22 | import { |
| 23 | AutocompleteQuery, |
| 24 | AutocompleteSuggestion, |
| 25 | } from '../../shared/gr-autocomplete/gr-autocomplete'; |
| 26 | import { |
| 27 | EditableLocalAccessSectionInfo, |
| 28 | PermissionAccessSection, |
| 29 | PropertyTreeNode, |
| 30 | PrimitiveValue, |
| 31 | } from './gr-repo-access-interfaces'; |
Milutin Kristofic | 860fe4d | 2020-11-23 16:13:45 +0100 | [diff] [blame] | 32 | import {firePageError, fireAlert} from '../../../utils/event-util'; |
Chris Poucet | c6e880b | 2021-11-15 19:57:06 +0100 | [diff] [blame] | 33 | import {getAppContext} from '../../../services/app-context'; |
Dhruv Srivastava | d4880e3 | 2021-01-29 13:42:58 +0100 | [diff] [blame] | 34 | import {WebLinkInfo} from '../../../types/diff'; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 35 | import {fontStyles} from '../../../styles/gr-font-styles'; |
| 36 | import {menuPageStyles} from '../../../styles/gr-menu-page-styles'; |
| 37 | import {subpageStyles} from '../../../styles/gr-subpage-styles'; |
| 38 | import {sharedStyles} from '../../../styles/shared-styles'; |
| 39 | import {LitElement, PropertyValues, css, html} from 'lit'; |
Frank Borden | 42c1a45 | 2022-08-11 16:27:20 +0200 | [diff] [blame] | 40 | import {customElement, property, query, state} from 'lit/decorators.js'; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 41 | import {assertIsDefined} from '../../../utils/common-util'; |
| 42 | import {ValueChangedEvent} from '../../../types/events'; |
Frank Borden | 42c1a45 | 2022-08-11 16:27:20 +0200 | [diff] [blame] | 43 | import {ifDefined} from 'lit/directives/if-defined.js'; |
Ben Rohlfs | aa53390 | 2022-09-22 09:07:12 +0200 | [diff] [blame] | 44 | import {resolve} from '../../../models/dependency'; |
| 45 | import {createChangeUrl} from '../../../models/views/change'; |
Kamil Musin | 12755c4 | 2022-11-29 17:11:43 +0100 | [diff] [blame] | 46 | import {throwingErrorCallback} from '../../shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper'; |
Ben Rohlfs | 678e19d | 2023-01-13 14:26:14 +0000 | [diff] [blame^] | 47 | import {createRepoUrl, RepoDetailView} from '../../../models/views/repo'; |
Becky Siegel | 148c7b2 | 2018-01-16 15:01:58 -0800 | [diff] [blame] | 48 | |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 49 | const NOTHING_TO_SAVE = 'No changes to save.'; |
Becky Siegel | 8d7b627 | 2018-03-28 09:38:29 -0700 | [diff] [blame] | 50 | |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 51 | const MAX_AUTOCOMPLETE_RESULTS = 50; |
Becky Siegel | 148c7b2 | 2018-01-16 15:01:58 -0800 | [diff] [blame] | 52 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 53 | declare global { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 54 | interface HTMLElementTagNameMap { |
| 55 | 'gr-repo-access': GrRepoAccess; |
| 56 | } |
| 57 | } |
| 58 | |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 59 | /** |
| 60 | * Fired when save is a no-op |
| 61 | * |
| 62 | * @event show-alert |
| 63 | */ |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 64 | @customElement('gr-repo-access') |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 65 | export class GrRepoAccess extends LitElement { |
| 66 | @query('gr-access-section:last-of-type') accessSection?: GrAccessSection; |
Becky Siegel | 9640eb2 | 2017-12-11 15:58:57 -0800 | [diff] [blame] | 67 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 68 | @property({type: String}) |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 69 | repo?: RepoName; |
Becky Siegel | 9640eb2 | 2017-12-11 15:58:57 -0800 | [diff] [blame] | 70 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 71 | // private but used in test |
| 72 | @state() canUpload?: boolean = false; // restAPI can return undefined |
Becky Siegel | 9640eb2 | 2017-12-11 15:58:57 -0800 | [diff] [blame] | 73 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 74 | // private but used in test |
| 75 | @state() inheritFromFilter?: RepoName; |
Becky Siegel | 9640eb2 | 2017-12-11 15:58:57 -0800 | [diff] [blame] | 76 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 77 | // private but used in test |
| 78 | @state() ownerOf?: GitRef[]; |
Becky Siegel | 6db432f | 2017-08-25 09:17:42 -0700 | [diff] [blame] | 79 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 80 | // private but used in test |
| 81 | @state() capabilities?: CapabilityInfoMap; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 82 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 83 | // private but used in test |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 84 | @state() groups?: RepoAccessGroups; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 85 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 86 | // private but used in test |
| 87 | @state() inheritsFrom?: ProjectInfo; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 88 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 89 | // private but used in test |
| 90 | @state() labels?: LabelNameToLabelTypeInfoMap; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 91 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 92 | // private but used in test |
| 93 | @state() local?: EditableLocalAccessSectionInfo; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 94 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 95 | // private but used in test |
| 96 | @state() editing = false; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 97 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 98 | // private but used in test |
| 99 | @state() modified = false; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 100 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 101 | // private but used in test |
| 102 | @state() sections?: PermissionAccessSection[]; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 103 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 104 | @state() private weblinks?: WebLinkInfo[]; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 105 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 106 | // private but used in test |
| 107 | @state() loading = true; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 108 | |
Paladox none | eb72adf | 2021-11-23 16:14:51 +0000 | [diff] [blame] | 109 | // private but used in the tests |
| 110 | originalInheritsFrom?: ProjectInfo; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 111 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 112 | private readonly query: AutocompleteQuery; |
| 113 | |
Chris Poucet | c6e880b | 2021-11-15 19:57:06 +0100 | [diff] [blame] | 114 | private readonly restApiService = getAppContext().restApiService; |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 115 | |
Ben Rohlfs | aa53390 | 2022-09-22 09:07:12 +0200 | [diff] [blame] | 116 | private readonly getNavigation = resolve(this, navigationToken); |
| 117 | |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 118 | constructor() { |
| 119 | super(); |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 120 | this.query = () => this.getInheritFromSuggestions(); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 121 | this.addEventListener('access-modified', () => |
| 122 | this._handleAccessModified() |
| 123 | ); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 124 | } |
Becky Siegel | 6db432f | 2017-08-25 09:17:42 -0700 | [diff] [blame] | 125 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 126 | static override get styles() { |
| 127 | return [ |
| 128 | fontStyles, |
| 129 | menuPageStyles, |
| 130 | subpageStyles, |
| 131 | sharedStyles, |
| 132 | css` |
| 133 | gr-button, |
| 134 | #inheritsFrom, |
| 135 | #editInheritFromInput, |
| 136 | .editing #inheritFromName, |
| 137 | .weblinks, |
| 138 | .editing .invisible { |
| 139 | display: none; |
| 140 | } |
| 141 | #inheritsFrom.show { |
| 142 | display: flex; |
| 143 | min-height: 2em; |
| 144 | align-items: center; |
| 145 | } |
| 146 | .weblink { |
| 147 | margin-right: var(--spacing-xs); |
| 148 | } |
| 149 | gr-access-section { |
| 150 | margin-top: var(--spacing-l); |
| 151 | } |
| 152 | .weblinks.show, |
| 153 | .referenceContainer { |
| 154 | display: block; |
| 155 | } |
| 156 | .rightsText { |
| 157 | margin-right: var(--spacing-s); |
| 158 | } |
| 159 | |
| 160 | .editing gr-button, |
| 161 | .admin #editBtn { |
| 162 | display: inline-block; |
| 163 | margin: var(--spacing-l) 0; |
| 164 | } |
| 165 | .editing #editInheritFromInput { |
| 166 | display: inline-block; |
| 167 | } |
| 168 | `, |
| 169 | ]; |
| 170 | } |
| 171 | |
| 172 | override render() { |
| 173 | return html` |
| 174 | <div class="main ${this.computeMainClass()}"> |
| 175 | <div id="loading" class=${this.loading ? 'loading' : ''}> |
| 176 | Loading... |
| 177 | </div> |
| 178 | <div id="loadedContent" class=${this.loading ? 'loading' : ''}> |
| 179 | <h3 |
| 180 | id="inheritsFrom" |
| 181 | class="heading-3 ${this.editing || this.inheritsFrom?.id?.length |
| 182 | ? 'show' |
| 183 | : ''}" |
| 184 | > |
| 185 | <span class="rightsText">Rights Inherit From</span> |
| 186 | <a |
| 187 | id="inheritFromName" |
| 188 | href=${this.computeParentHref()} |
| 189 | rel="noopener" |
| 190 | > |
| 191 | ${this.inheritsFrom?.name}</a |
| 192 | > |
| 193 | <gr-autocomplete |
| 194 | id="editInheritFromInput" |
| 195 | .text=${this.inheritFromFilter} |
| 196 | .query=${this.query} |
| 197 | @commit=${(e: ValueChangedEvent) => { |
| 198 | this.handleUpdateInheritFrom(e); |
| 199 | }} |
| 200 | @bind-value-changed=${(e: ValueChangedEvent) => { |
| 201 | this.handleUpdateInheritFrom(e); |
| 202 | }} |
| 203 | @text-changed=${(e: ValueChangedEvent) => { |
| 204 | this.handleEditInheritFromTextChanged(e); |
| 205 | }} |
| 206 | ></gr-autocomplete> |
| 207 | </h3> |
| 208 | <div class="weblinks ${this.weblinks?.length ? 'show' : ''}"> |
| 209 | History: |
| 210 | ${this.weblinks?.map(webLink => this.renderWebLinks(webLink))} |
| 211 | </div> |
| 212 | ${this.sections?.map((section, index) => |
| 213 | this.renderPermissionSections(section, index) |
| 214 | )} |
| 215 | <div class="referenceContainer"> |
| 216 | <gr-button |
| 217 | id="addReferenceBtn" |
| 218 | @click=${() => this.handleCreateSection()} |
| 219 | >Add Reference</gr-button |
| 220 | > |
| 221 | </div> |
| 222 | <div> |
| 223 | <gr-button |
| 224 | id="editBtn" |
| 225 | @click=${() => { |
| 226 | this.handleEdit(); |
| 227 | }} |
| 228 | >${this.editing ? 'Cancel' : 'Edit'}</gr-button |
| 229 | > |
| 230 | <gr-button |
| 231 | id="saveBtn" |
| 232 | class=${this.ownerOf && this.ownerOf.length === 0 |
| 233 | ? 'invisible' |
| 234 | : ''} |
| 235 | primary |
| 236 | ?disabled=${!this.modified} |
| 237 | @click=${this.handleSave} |
| 238 | >Save</gr-button |
| 239 | > |
| 240 | <gr-button |
| 241 | id="saveReviewBtn" |
| 242 | class=${!this.canUpload ? 'invisible' : ''} |
| 243 | primary |
| 244 | ?disabled=${!this.modified} |
| 245 | @click=${this.handleSaveForReview} |
| 246 | >Save for review</gr-button |
| 247 | > |
| 248 | </div> |
| 249 | </div> |
| 250 | </div> |
| 251 | `; |
| 252 | } |
| 253 | |
| 254 | private renderWebLinks(webLink: WebLinkInfo) { |
| 255 | return html` |
| 256 | <a |
| 257 | class="weblink" |
| 258 | href=${webLink.url} |
| 259 | rel="noopener" |
| 260 | target=${ifDefined(webLink.target)} |
| 261 | > |
| 262 | ${webLink.name} |
| 263 | </a> |
| 264 | `; |
| 265 | } |
| 266 | |
| 267 | private renderPermissionSections( |
| 268 | section: PermissionAccessSection, |
| 269 | index: number |
| 270 | ) { |
| 271 | return html` |
| 272 | <gr-access-section |
| 273 | .capabilities=${this.capabilities} |
| 274 | .section=${section} |
| 275 | .labels=${this.labels} |
| 276 | .canUpload=${this.canUpload} |
| 277 | .editing=${this.editing} |
| 278 | .ownerOf=${this.ownerOf} |
| 279 | .groups=${this.groups} |
| 280 | .repo=${this.repo} |
| 281 | @added-section-removed=${() => { |
| 282 | this.handleAddedSectionRemoved(index); |
| 283 | }} |
| 284 | @section-changed=${(e: ValueChangedEvent<PermissionAccessSection>) => { |
| 285 | this.handleAccessSectionChanged(e, index); |
| 286 | }} |
| 287 | ></gr-access-section> |
| 288 | `; |
| 289 | } |
| 290 | |
| 291 | override willUpdate(changedProperties: PropertyValues) { |
| 292 | if (changedProperties.has('repo')) { |
| 293 | this._repoChanged(this.repo); |
| 294 | } |
| 295 | |
| 296 | if (changedProperties.has('editing')) { |
| 297 | this.handleEditingChanged(changedProperties.get('editing') as boolean); |
| 298 | this.requestUpdate(); |
| 299 | } |
| 300 | } |
| 301 | |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 302 | _handleAccessModified() { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 303 | this.modified = true; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 304 | } |
Becky Siegel | 9640eb2 | 2017-12-11 15:58:57 -0800 | [diff] [blame] | 305 | |
Paladox none | eb72adf | 2021-11-23 16:14:51 +0000 | [diff] [blame] | 306 | _repoChanged(repo?: RepoName) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 307 | this.loading = true; |
Becky Siegel | 9640eb2 | 2017-12-11 15:58:57 -0800 | [diff] [blame] | 308 | |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 309 | if (!repo) { |
| 310 | return Promise.resolve(); |
| 311 | } |
Paladox none | 68511792 | 2018-03-17 20:05:21 +0000 | [diff] [blame] | 312 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 313 | return this.reload(repo); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 314 | } |
Paladox none | 70cb10c | 2018-02-17 19:12:09 +0000 | [diff] [blame] | 315 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 316 | private reload(repo: RepoName) { |
Dhruv Srivastava | b0131f9 | 2020-11-24 09:31:54 +0100 | [diff] [blame] | 317 | const errFn = (response?: Response | null) => { |
Ben Rohlfs | a76c82f | 2021-01-22 22:22:32 +0100 | [diff] [blame] | 318 | firePageError(response); |
Dhruv Srivastava | b0131f9 | 2020-11-24 09:31:54 +0100 | [diff] [blame] | 319 | }; |
Paladox none | 70cb10c | 2018-02-17 19:12:09 +0000 | [diff] [blame] | 320 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 321 | this.editing = false; |
Paladox none | 70cb10c | 2018-02-17 19:12:09 +0000 | [diff] [blame] | 322 | |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 323 | // Always reset sections when a repo changes. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 324 | this.sections = []; |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 325 | const sectionsPromises = this.restApiService |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 326 | .getRepoAccessRights(repo, errFn) |
| 327 | .then(res => { |
| 328 | if (!res) { |
| 329 | return Promise.resolve(undefined); |
| 330 | } |
Becky Siegel | 6af6325 | 2018-04-26 14:02:36 -0700 | [diff] [blame] | 331 | |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 332 | // Keep a copy of the original inherit from values separate from |
| 333 | // the ones data bound to gr-autocomplete, so the original value |
| 334 | // can be restored if the user cancels. |
frankborden2@gmail.com | 4c610db | 2021-08-12 17:56:01 +0200 | [diff] [blame] | 335 | if (res.inherits_from) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 336 | this.inheritsFrom = {...res.inherits_from}; |
frankborden2@gmail.com | 4c610db | 2021-08-12 17:56:01 +0200 | [diff] [blame] | 337 | this.originalInheritsFrom = {...res.inherits_from}; |
| 338 | } else { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 339 | this.inheritsFrom = undefined; |
frankborden2@gmail.com | 4c610db | 2021-08-12 17:56:01 +0200 | [diff] [blame] | 340 | this.originalInheritsFrom = undefined; |
| 341 | } |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 342 | // Initialize the filter value so when the user clicks edit, the |
| 343 | // current value appears. If there is no parent repo, it is |
| 344 | // initialized as an empty string. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 345 | this.inheritFromFilter = res.inherits_from |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 346 | ? res.inherits_from.name |
| 347 | : ('' as RepoName); |
| 348 | // 'as EditableLocalAccessSectionInfo' is required because res.local |
| 349 | // type doesn't have index signature |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 350 | this.local = res.local as EditableLocalAccessSectionInfo; |
| 351 | this.groups = res.groups; |
| 352 | this.weblinks = res.config_web_links || []; |
| 353 | this.canUpload = res.can_upload; |
| 354 | this.ownerOf = res.owner_of || []; |
| 355 | return toSortedPermissionsArray(this.local); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 356 | }); |
Becky Siegel | 6af6325 | 2018-04-26 14:02:36 -0700 | [diff] [blame] | 357 | |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 358 | const capabilitiesPromises = this.restApiService |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 359 | .getCapabilities(errFn) |
| 360 | .then(res => { |
| 361 | if (!res) { |
| 362 | return Promise.resolve(undefined); |
| 363 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 364 | |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 365 | return res; |
| 366 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 367 | |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 368 | const labelsPromises = this.restApiService |
| 369 | .getRepo(repo, errFn) |
| 370 | .then(res => { |
| 371 | if (!res) { |
| 372 | return Promise.resolve(undefined); |
| 373 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 374 | |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 375 | return res.labels; |
| 376 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 377 | |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 378 | return Promise.all([ |
| 379 | sectionsPromises, |
| 380 | capabilitiesPromises, |
| 381 | labelsPromises, |
| 382 | ]).then(([sections, capabilities, labels]) => { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 383 | this.capabilities = capabilities; |
| 384 | this.labels = labels; |
| 385 | this.sections = sections; |
| 386 | this.loading = false; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 387 | }); |
| 388 | } |
| 389 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 390 | // private but used in test |
| 391 | handleUpdateInheritFrom(e: ValueChangedEvent) { |
| 392 | this.inheritsFrom = { |
| 393 | ...(this.inheritsFrom ?? {}), |
Dhruv Srivastava | 7176818 | 2021-06-18 15:59:08 +0200 | [diff] [blame] | 394 | id: e.detail.value as UrlEncodedRepoName, |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 395 | name: this.inheritFromFilter, |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 396 | }; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 397 | this._handleAccessModified(); |
| 398 | } |
| 399 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 400 | private getInheritFromSuggestions(): Promise<AutocompleteSuggestion[]> { |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 401 | return this.restApiService |
Kamil Musin | 12755c4 | 2022-11-29 17:11:43 +0100 | [diff] [blame] | 402 | .getRepos( |
| 403 | this.inheritFromFilter, |
| 404 | MAX_AUTOCOMPLETE_RESULTS, |
| 405 | /* offset=*/ undefined, |
| 406 | throwingErrorCallback |
| 407 | ) |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 408 | .then(response => { |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 409 | const repos: AutocompleteSuggestion[] = []; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 410 | if (!response) { |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 411 | return repos; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 412 | } |
| 413 | for (const item of response) { |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 414 | repos.push({ |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 415 | name: item.name, |
| 416 | value: item.id, |
| 417 | }); |
| 418 | } |
Ben Rohlfs | bfc688b | 2022-10-21 12:38:37 +0200 | [diff] [blame] | 419 | return repos; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 420 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 421 | } |
| 422 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 423 | private handleEdit() { |
| 424 | this.editing = !this.editing; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 425 | } |
| 426 | |
Dhruv Srivastava | 0d13395 | 2022-08-23 10:41:45 +0200 | [diff] [blame] | 427 | // private but used in tests |
| 428 | handleAddedSectionRemoved(index: number) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 429 | if (!this.sections) return; |
Dhruv Srivastava | 0d13395 | 2022-08-23 10:41:45 +0200 | [diff] [blame] | 430 | assertIsDefined(this.local, 'local'); |
| 431 | delete this.local[this.sections[index].id]; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 432 | this.sections = this.sections |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 433 | .slice(0, index) |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 434 | .concat(this.sections.slice(index + 1, this.sections.length)); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 435 | } |
| 436 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 437 | private handleEditingChanged(editingOld: boolean) { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 438 | // Ignore when editing gets set initially. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 439 | if (!editingOld || this.editing) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 440 | return; |
| 441 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 442 | // Remove any unsaved but added refs. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 443 | if (this.sections) { |
| 444 | this.sections = this.sections.filter(p => !p.value.added); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 445 | } |
| 446 | // Restore inheritFrom. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 447 | if (this.inheritsFrom) { |
| 448 | this.inheritsFrom = this.originalInheritsFrom |
Dmitrii Filippov | aab9825 | 2021-04-06 19:04:11 +0200 | [diff] [blame] | 449 | ? {...this.originalInheritsFrom} |
frankborden2@gmail.com | 4c610db | 2021-08-12 17:56:01 +0200 | [diff] [blame] | 450 | : undefined; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 451 | this.inheritFromFilter = this.originalInheritsFrom?.name; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 452 | } |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 453 | if (!this.local) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 454 | return; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 455 | } |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 456 | for (const key of Object.keys(this.local)) { |
| 457 | if (this.local[key].added) { |
| 458 | delete this.local[key]; |
Tao Zhou | 704cfe73 | 2020-03-12 08:32:46 +0100 | [diff] [blame] | 459 | } |
Dmitrii Filippov | 3fd2b10 | 2019-11-15 16:16:46 +0100 | [diff] [blame] | 460 | } |
| 461 | } |
| 462 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 463 | private updateRemoveObj( |
| 464 | addRemoveObj: {remove: PropertyTreeNode}, |
| 465 | path: string[] |
| 466 | ) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 467 | let curPos: PropertyTreeNode = addRemoveObj.remove; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 468 | for (const item of path) { |
| 469 | if (!curPos[item]) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 470 | if (item === path[path.length - 1]) { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 471 | if (path[path.length - 2] === 'permissions') { |
| 472 | curPos[item] = {rules: {}}; |
| 473 | } else if (path.length === 1) { |
| 474 | curPos[item] = {permissions: {}}; |
| 475 | } else { |
| 476 | curPos[item] = {}; |
| 477 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 478 | } else { |
| 479 | curPos[item] = {}; |
| 480 | } |
| 481 | } |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 482 | // The last item can be a PrimitiveValue, but we don't use it |
| 483 | // All intermediate items are PropertyTreeNode |
| 484 | // TODO(TS): rewrite this loop and process the last item explicitly |
| 485 | curPos = curPos[item] as PropertyTreeNode; |
| 486 | } |
| 487 | return addRemoveObj; |
| 488 | } |
| 489 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 490 | private updateAddObj( |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 491 | addRemoveObj: {add: PropertyTreeNode}, |
| 492 | path: string[], |
| 493 | value: PropertyTreeNode | PrimitiveValue |
| 494 | ) { |
| 495 | let curPos: PropertyTreeNode = addRemoveObj.add; |
| 496 | for (const item of path) { |
| 497 | if (!curPos[item]) { |
| 498 | if (item === path[path.length - 1]) { |
| 499 | curPos[item] = value; |
| 500 | } else { |
| 501 | curPos[item] = {}; |
| 502 | } |
| 503 | } |
| 504 | // The last item can be a PrimitiveValue, but we don't use it |
| 505 | // All intermediate items are PropertyTreeNode |
| 506 | // TODO(TS): rewrite this loop and process the last item explicitly |
| 507 | curPos = curPos[item] as PropertyTreeNode; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 508 | } |
| 509 | return addRemoveObj; |
| 510 | } |
| 511 | |
| 512 | /** |
| 513 | * Used to recursively remove any objects with a 'deleted' bit. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 514 | * |
| 515 | * private but used in test |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 516 | */ |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 517 | recursivelyRemoveDeleted(obj?: PropertyTreeNode) { |
Ben Rohlfs | 7b71b11 | 2021-02-12 10:36:08 +0100 | [diff] [blame] | 518 | if (!obj) return; |
| 519 | for (const k of Object.keys(obj)) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 520 | const node = obj[k]; |
| 521 | if (typeof node === 'object') { |
| 522 | if (node.deleted) { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 523 | delete obj[k]; |
| 524 | return; |
| 525 | } |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 526 | this.recursivelyRemoveDeleted(node); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 527 | } |
| 528 | } |
| 529 | } |
| 530 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 531 | // private but used in test |
| 532 | recursivelyUpdateAddRemoveObj( |
Ben Rohlfs | 7b71b11 | 2021-02-12 10:36:08 +0100 | [diff] [blame] | 533 | obj: PropertyTreeNode | undefined, |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 534 | addRemoveObj: { |
| 535 | add: PropertyTreeNode; |
| 536 | remove: PropertyTreeNode; |
| 537 | }, |
| 538 | path: string[] = [] |
| 539 | ) { |
Ben Rohlfs | 7b71b11 | 2021-02-12 10:36:08 +0100 | [diff] [blame] | 540 | if (!obj) return; |
| 541 | for (const k of Object.keys(obj)) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 542 | const node = obj[k]; |
| 543 | if (typeof node === 'object') { |
| 544 | const updatedId = node.updatedId; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 545 | const ref = updatedId ? updatedId : k; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 546 | if (node.deleted) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 547 | this.updateRemoveObj(addRemoveObj, path.concat(k)); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 548 | continue; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 549 | } else if (node.modified) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 550 | this.updateRemoveObj(addRemoveObj, path.concat(k)); |
| 551 | this.updateAddObj(addRemoveObj, path.concat(ref), node); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 552 | /* Special case for ref changes because they need to be added and |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 553 | removed in a different way. The new ref needs to include all |
| 554 | changes but also the initial state. To do this, instead of |
| 555 | continuing with the same recursion, just remove anything that is |
| 556 | deleted in the current state. */ |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 557 | if (updatedId && updatedId !== k) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 558 | this.recursivelyRemoveDeleted( |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 559 | addRemoveObj.add[updatedId] as PropertyTreeNode |
| 560 | ); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 561 | } |
| 562 | continue; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 563 | } else if (node.added) { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 564 | this.updateAddObj(addRemoveObj, path.concat(ref), node); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 565 | /** |
| 566 | * As add / delete both can happen in the new section, |
| 567 | * so here to make sure it will remove the deleted ones. |
| 568 | * |
| 569 | * @see Issue 11339 |
| 570 | */ |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 571 | this.recursivelyRemoveDeleted( |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 572 | addRemoveObj.add[k] as PropertyTreeNode |
| 573 | ); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 574 | continue; |
| 575 | } |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 576 | this.recursivelyUpdateAddRemoveObj(node, addRemoveObj, path.concat(k)); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 577 | } |
| 578 | } |
| 579 | } |
| 580 | |
| 581 | /** |
| 582 | * Returns an object formatted for saving or submitting access changes for |
| 583 | * review |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 584 | * |
| 585 | * private but used in test |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 586 | */ |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 587 | computeAddAndRemove() { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 588 | const addRemoveObj: { |
| 589 | add: PropertyTreeNode; |
| 590 | remove: PropertyTreeNode; |
| 591 | parent?: string | null; |
| 592 | } = { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 593 | add: {}, |
| 594 | remove: {}, |
| 595 | }; |
| 596 | |
Dhruv Srivastava | 7e454cd | 2021-02-22 15:06:58 +0100 | [diff] [blame] | 597 | const originalInheritsFromId = this.originalInheritsFrom |
| 598 | ? singleDecodeURL(this.originalInheritsFrom.id) |
frankborden2@gmail.com | 4c610db | 2021-08-12 17:56:01 +0200 | [diff] [blame] | 599 | : undefined; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 600 | const inheritsFromId = this.inheritsFrom |
| 601 | ? singleDecodeURL(this.inheritsFrom.id) |
frankborden2@gmail.com | 4c610db | 2021-08-12 17:56:01 +0200 | [diff] [blame] | 602 | : undefined; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 603 | |
| 604 | const inheritFromChanged = |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 605 | // Inherit from changed |
| 606 | (originalInheritsFromId && originalInheritsFromId !== inheritsFromId) || |
| 607 | // Inherit from added (did not have one initially); |
| 608 | (!originalInheritsFromId && inheritsFromId); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 609 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 610 | if (!this.local) { |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 611 | return addRemoveObj; |
| 612 | } |
| 613 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 614 | this.recursivelyUpdateAddRemoveObj( |
| 615 | this.local as unknown as PropertyTreeNode, |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 616 | addRemoveObj |
| 617 | ); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 618 | |
| 619 | if (inheritFromChanged) { |
| 620 | addRemoveObj.parent = inheritsFromId; |
| 621 | } |
| 622 | return addRemoveObj; |
| 623 | } |
| 624 | |
Ben Rohlfs | 53953e1 | 2022-05-04 11:35:46 +0200 | [diff] [blame] | 625 | private handleCreateSection() { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 626 | if (!this.local) return; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 627 | let newRef = 'refs/for/*'; |
| 628 | // Avoid using an already used key for the placeholder, since it |
| 629 | // immediately gets added to an object. |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 630 | while (this.local[newRef]) { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 631 | newRef = `${newRef}*`; |
| 632 | } |
| 633 | const section = {permissions: {}, added: true}; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 634 | this.sections!.push({id: newRef as GitRef, value: section}); |
| 635 | this.local[newRef] = section; |
| 636 | this.requestUpdate(); |
| 637 | assertIsDefined(this.accessSection, 'accessSection'); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 638 | // Template already instantiated at this point |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 639 | this.accessSection.editReference(); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 640 | } |
| 641 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 642 | private getObjforSave(): ProjectAccessInput | undefined { |
| 643 | const addRemoveObj = this.computeAddAndRemove(); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 644 | // If there are no changes, don't actually save. |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 645 | if ( |
| 646 | !Object.keys(addRemoveObj.add).length && |
| 647 | !Object.keys(addRemoveObj.remove).length && |
| 648 | !addRemoveObj.parent |
| 649 | ) { |
Milutin Kristofic | 860fe4d | 2020-11-23 16:13:45 +0100 | [diff] [blame] | 650 | fireAlert(this, NOTHING_TO_SAVE); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 651 | return; |
| 652 | } |
Chris Poucet | caeea1b | 2021-08-19 22:12:56 +0000 | [diff] [blame] | 653 | const obj: ProjectAccessInput = { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 654 | add: addRemoveObj.add, |
| 655 | remove: addRemoveObj.remove, |
Chris Poucet | caeea1b | 2021-08-19 22:12:56 +0000 | [diff] [blame] | 656 | } as unknown as ProjectAccessInput; |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 657 | if (addRemoveObj.parent) { |
| 658 | obj.parent = addRemoveObj.parent; |
| 659 | } |
| 660 | return obj; |
| 661 | } |
| 662 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 663 | // private but used in test |
| 664 | handleSave(e: Event) { |
| 665 | const obj = this.getObjforSave(); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 666 | if (!obj) { |
| 667 | return; |
| 668 | } |
| 669 | const button = e && (e.target as GrButton); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 670 | if (button) { |
| 671 | button.loading = true; |
| 672 | } |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 673 | const repo = this.repo; |
| 674 | if (!repo) { |
| 675 | return Promise.resolve(); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 676 | } |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 677 | return this.restApiService |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 678 | .setRepoAccessRights(repo, obj) |
| 679 | .then(() => { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 680 | this.reload(repo); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 681 | }) |
| 682 | .finally(() => { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 683 | this.modified = false; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 684 | if (button) { |
| 685 | button.loading = false; |
| 686 | } |
| 687 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 688 | } |
| 689 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 690 | // private but used in test |
| 691 | handleSaveForReview(e: Event) { |
| 692 | const obj = this.getObjforSave(); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 693 | if (!obj) { |
| 694 | return; |
| 695 | } |
| 696 | const button = e && (e.target as GrButton); |
| 697 | if (button) { |
| 698 | button.loading = true; |
| 699 | } |
| 700 | if (!this.repo) { |
| 701 | return; |
| 702 | } |
Ben Rohlfs | 43935a4 | 2020-12-01 19:14:09 +0100 | [diff] [blame] | 703 | return this.restApiService |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 704 | .setRepoAccessRightsForReview(this.repo, obj) |
| 705 | .then(change => { |
Ben Rohlfs | aa53390 | 2022-09-22 09:07:12 +0200 | [diff] [blame] | 706 | this.getNavigation().setUrl(createChangeUrl({change})); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 707 | }) |
| 708 | .finally(() => { |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 709 | this.modified = false; |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 710 | if (button) { |
| 711 | button.loading = false; |
| 712 | } |
| 713 | }); |
| 714 | } |
| 715 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 716 | // private but used in test |
| 717 | computeMainClass() { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 718 | const classList = []; |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 719 | if ((this.ownerOf && this.ownerOf.length > 0) || this.canUpload) { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 720 | classList.push('admin'); |
| 721 | } |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 722 | if (this.editing) { |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 723 | classList.push('editing'); |
| 724 | } |
| 725 | return classList.join(' '); |
| 726 | } |
| 727 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 728 | computeParentHref() { |
| 729 | if (!this.inheritsFrom?.name) return ''; |
Ben Rohlfs | 678e19d | 2023-01-13 14:26:14 +0000 | [diff] [blame^] | 730 | return createRepoUrl({ |
| 731 | repo: this.inheritsFrom.name, |
| 732 | detail: RepoDetailView.ACCESS, |
| 733 | }); |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 734 | } |
Dmitrii Filippov | daf0ec9 | 2020-03-17 11:27:28 +0100 | [diff] [blame] | 735 | |
Paladox none | a6c0589 | 2021-11-23 22:19:27 +0000 | [diff] [blame] | 736 | private handleEditInheritFromTextChanged(e: ValueChangedEvent) { |
| 737 | this.inheritFromFilter = e.detail.value as RepoName; |
| 738 | } |
| 739 | |
| 740 | private handleAccessSectionChanged( |
| 741 | e: ValueChangedEvent<PermissionAccessSection>, |
| 742 | index: number |
| 743 | ) { |
| 744 | this.sections![index] = e.detail.value; |
| 745 | this.requestUpdate(); |
Dmitrii Filippov | 460685c2 | 2020-08-21 11:12:49 +0200 | [diff] [blame] | 746 | } |
| 747 | } |