blob: 90277534416a3410f3b29a620d50f74b494c4d41 [file] [log] [blame]
/**
* @license
* Copyright 2017 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '@polymer/iron-input/iron-input';
import '../../plugins/gr-endpoint-decorator/gr-endpoint-decorator';
import '../../plugins/gr-endpoint-param/gr-endpoint-param';
import '../../shared/gr-button/gr-button';
import '../../shared/gr-download-commands/gr-download-commands';
import '../../shared/gr-select/gr-select';
import '../../shared/gr-textarea/gr-textarea';
import '../gr-repo-plugin-config/gr-repo-plugin-config';
import {
ConfigInfo,
RepoName,
InheritedBooleanInfo,
SchemesInfoMap,
ConfigInput,
MaxObjectSizeLimitInfo,
PluginParameterToConfigParameterInfoMap,
DownloadSchemeInfo,
} from '../../../types/common';
import {
InheritedBooleanInfoConfiguredValue,
RepoState,
SubmitType,
} from '../../../constants/constants';
import {assertIsDefined, hasOwnProperty} from '../../../utils/common-util';
import {firePageError, fireTitleChange} from '../../../utils/event-util';
import {getAppContext} from '../../../services/app-context';
import {WebLinkInfo} from '../../../types/diff';
import {ErrorCallback} from '../../../api/rest';
import {fontStyles} from '../../../styles/gr-font-styles';
import {grFormStyles} from '../../../styles/gr-form-styles';
import {subpageStyles} from '../../../styles/gr-subpage-styles';
import {sharedStyles} from '../../../styles/shared-styles';
import {BindValueChangeEvent} from '../../../types/events';
import {deepClone} from '../../../utils/deep-util';
import {LitElement, PropertyValues, css, html, nothing} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {when} from 'lit/directives/when.js';
import {subscribe} from '../../lit/subscription-controller';
import {createSearchUrl} from '../../../models/views/search';
import {userModelToken} from '../../../models/user/user-model';
import {resolve} from '../../../models/dependency';
const STATES = {
active: {value: RepoState.ACTIVE, label: 'Active'},
readOnly: {value: RepoState.READ_ONLY, label: 'Read Only'},
hidden: {value: RepoState.HIDDEN, label: 'Hidden'},
};
const SUBMIT_TYPES = {
// Exclude INHERIT, which is handled specially.
mergeIfNecessary: {
value: 'MERGE_IF_NECESSARY',
label: 'Merge if necessary',
},
fastForwardOnly: {
value: 'FAST_FORWARD_ONLY',
label: 'Fast forward only',
},
rebaseAlways: {
value: 'REBASE_ALWAYS',
label: 'Rebase Always',
},
rebaseIfNecessary: {
value: 'REBASE_IF_NECESSARY',
label: 'Rebase if necessary',
},
mergeAlways: {
value: 'MERGE_ALWAYS',
label: 'Merge always',
},
cherryPick: {
value: 'CHERRY_PICK',
label: 'Cherry pick',
},
};
declare global {
interface HTMLElementTagNameMap {
'gr-repo': GrRepo;
}
}
@customElement('gr-repo')
export class GrRepo extends LitElement {
private schemes: string[] = [];
@property({type: String})
repo?: RepoName;
// private but used in test
@state() loading = true;
// private but used in test
@state() repoConfig?: ConfigInfo;
// private but used in test
@state() readOnly = true;
@state() private states = Object.values(STATES);
@state() private originalConfig?: ConfigInfo;
@state() private selectedScheme?: string;
// private but used in test
@state() schemesObj?: SchemesInfoMap;
@state() private weblinks: WebLinkInfo[] = [];
@state() private pluginConfigChanged = false;
private readonly getUserModel = resolve(this, userModelToken);
private readonly restApiService = getAppContext().restApiService;
constructor() {
super();
subscribe(
this,
() => this.getUserModel().preferences$,
prefs => {
if (prefs?.download_scheme) {
// Note (issue 5180): normalize the download scheme with lower-case.
this.selectedScheme = prefs.download_scheme.toLowerCase();
}
}
);
}
override connectedCallback() {
super.connectedCallback();
fireTitleChange(`${this.repo}`);
}
static override get styles() {
return [
fontStyles,
grFormStyles,
subpageStyles,
sharedStyles,
css`
.info {
margin-bottom: var(--spacing-xl);
}
h2.edited:after {
color: var(--deemphasized-text-color);
content: ' *';
}
#options .repositorySettings {
display: none;
}
#options .repositorySettings.showConfig {
display: block;
}
`,
];
}
override render() {
const configChanged = this.hasConfigChanged();
return html`
<div class="main gr-form-styles read-only">
<div class="info">
<h1 id="Title" class="heading-1">${this.repo}</h1>
<hr />
<div>
<a href=${this.weblinks?.[0]?.url}
><gr-button link ?disabled=${!this.weblinks?.[0]?.url}
>Browse</gr-button
></a
><a href=${this.computeChangesUrl(this.repo)}
><gr-button link>View Changes</gr-button></a
>
</div>
</div>
${when(
this.loading || !this.repoConfig,
() => html`<div id="loading">Loading...</div>`,
() => html`<div id="loadedContent">
${this.renderDownloadCommands()}
<h2
id="configurations"
class="heading-2 ${configChanged ? 'edited' : ''}"
>
Configurations
</h2>
<div id="form">
<fieldset>
${this.renderDescription()} ${this.renderRepoOptions()}
${this.renderPluginConfig()}
<gr-button
?disabled=${this.readOnly || !configChanged}
@click=${this.handleSaveRepoConfig}
>Save changes</gr-button
>
</fieldset>
<gr-endpoint-decorator name="repo-config">
<gr-endpoint-param
name="repoName"
.value=${this.repo}
></gr-endpoint-param>
<gr-endpoint-param
name="readOnly"
.value=${this.readOnly}
></gr-endpoint-param>
</gr-endpoint-decorator>
</div>
</div>`
)}
</div>
`;
}
private renderDownloadCommands() {
if (!this.schemes.length) return nothing;
return html`
<div id="downloadContent">
<h2 id="download" class="heading-2">Download</h2>
<fieldset>
<gr-download-commands
id="downloadCommands"
.commands=${this.computeCommands()}
.schemes=${this.schemes}
.selectedScheme=${this.selectedScheme}
.description=${this.computeDescription()}
@selected-scheme-changed=${(e: BindValueChangeEvent) => {
if (this.loading) return;
this.selectedScheme = e.detail.value;
}}
></gr-download-commands>
</fieldset>
</div>
`;
}
private renderDescription() {
assertIsDefined(this.repoConfig, 'repoConfig');
return html`
<h3 id="Description" class="heading-3">Description</h3>
<fieldset>
<gr-textarea
id="descriptionInput"
class="description"
autocomplete="on"
placeholder="&lt;Insert repo description here&gt;"
rows="4"
monospace
?disabled=${this.readOnly}
.text=${this.repoConfig.description ?? ''}
@text-changed=${this.handleDescriptionTextChanged}
></gr-textarea>
</fieldset>
`;
}
private renderRepoOptions() {
return html`
<h3 id="Options" class="heading-3">Repository Options</h3>
<fieldset id="options">
${this.renderState()} ${this.renderSubmitType()}
${this.renderContentMerges()} ${this.renderNewChange()}
${this.renderChangeId()} ${this.renderEnableSignedPush()}
${this.renderRequireSignedPush()} ${this.renderRejectImplicitMerges()}
${this.renderUnRegisteredCc()} ${this.renderPrivateByDefault()}
${this.renderWorkInProgressByDefault()} ${this.renderMaxGitObjectSize()}
${this.renderMatchAuthoredDateWithCommitterDate()}
${this.renderRejectEmptyCommit()}
</fieldset>
<h3 id="Options" class="heading-3">Contributor Agreements</h3>
<fieldset id="agreements">
${this.renderContributorAgreement()} ${this.renderUseSignedOffBy()}
</fieldset>
`;
}
private renderState() {
return html`
<section>
<span class="title">State</span>
<span class="value">
<gr-select
id="stateSelect"
.bindValue=${this.repoConfig?.state}
@bind-value-changed=${this.handleStateSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.states.map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderSubmitType() {
return html`
<section>
<span class="title">Submit type</span>
<span class="value">
<gr-select
id="submitTypeSelect"
.bindValue=${this.repoConfig?.submit_type}
@bind-value-changed=${this.handleSubmitTypeSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatSubmitTypeSelect(this.repoConfig).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderContentMerges() {
return html`
<section>
<span class="title">Allow content merges</span>
<span class="value">
<gr-select
id="contentMergeSelect"
.bindValue=${this.repoConfig?.use_content_merge?.configured_value}
@bind-value-changed=${this.handleContentMergeSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.use_content_merge
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderNewChange() {
return html`
<section>
<span class="title">
Create a new change for every commit not in the target branch
</span>
<span class="value">
<gr-select
id="newChangeSelect"
.bindValue=${this.repoConfig
?.create_new_change_for_all_not_in_target?.configured_value}
@bind-value-changed=${this.handleNewChangeSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.create_new_change_for_all_not_in_target
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderChangeId() {
return html`
<section>
<span class="title">Require Change-Id in commit message</span>
<span class="value">
<gr-select
id="requireChangeIdSelect"
.bindValue=${this.repoConfig?.require_change_id?.configured_value}
@bind-value-changed=${this
.handleRequireChangeIdSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.require_change_id
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderEnableSignedPush() {
return html`
<section
id="enableSignedPushSettings"
class="repositorySettings ${this.repoConfig?.enable_signed_push
? 'showConfig'
: ''}"
>
<span class="title">Enable signed push</span>
<span class="value">
<gr-select
id="enableSignedPush"
.bindValue=${this.repoConfig?.enable_signed_push?.configured_value}
@bind-value-changed=${this.handleEnableSignedPushBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.enable_signed_push
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderRequireSignedPush() {
return html`
<section
id="requireSignedPushSettings"
class="repositorySettings ${this.repoConfig?.require_signed_push
? 'showConfig'
: ''}"
>
<span class="title">Require signed push</span>
<span class="value">
<gr-select
id="requireSignedPush"
.bindValue=${this.repoConfig?.require_signed_push?.configured_value}
@bind-value-changed=${this.handleRequireSignedPushBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.require_signed_push
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderRejectImplicitMerges() {
return html`
<section>
<span class="title">
Reject implicit merges when changes are pushed for review</span
>
<span class="value">
<gr-select
id="rejectImplicitMergesSelect"
.bindValue=${this.repoConfig?.reject_implicit_merges
?.configured_value}
@bind-value-changed=${this
.handleRejectImplicitMergeSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.reject_implicit_merges
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderUnRegisteredCc() {
return html`
<section>
<span class="title">
Enable adding unregistered users as reviewers and CCs on changes</span
>
<span class="value">
<gr-select
id="unRegisteredCcSelect"
.bindValue=${this.repoConfig?.enable_reviewer_by_email
?.configured_value}
@bind-value-changed=${this
.handleUnRegisteredCcSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.enable_reviewer_by_email
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderPrivateByDefault() {
return html`
<section>
<span class="title"> Set all new changes private by default</span>
<span class="value">
<gr-select
id="setAllnewChangesPrivateByDefaultSelect"
.bindValue=${this.repoConfig?.private_by_default?.configured_value}
@bind-value-changed=${this
.handleSetAllNewChangesPrivateByDefaultSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.private_by_default
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderWorkInProgressByDefault() {
return html`
<section>
<span class="title">
Set new changes to "work in progress" by default</span
>
<span class="value">
<gr-select
id="setAllNewChangesWorkInProgressByDefaultSelect"
.bindValue=${this.repoConfig?.work_in_progress_by_default
?.configured_value}
@bind-value-changed=${this
.handleSetAllNewChangesWorkInProgressByDefaultSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.work_in_progress_by_default
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderMaxGitObjectSize() {
return html`
<section>
<span class="title">Maximum Git object size limit</span>
<span class="value">
<iron-input
id="maxGitObjSizeIronInput"
.bindValue=${this.repoConfig?.max_object_size_limit
?.configured_value}
@bind-value-changed=${this.handleMaxGitObjSizeBindValueChanged}
>
<input
id="maxGitObjSizeInput"
type="text"
?disabled=${this.readOnly}
/>
</iron-input>
${this.repoConfig?.max_object_size_limit?.value
? `effective: ${this.repoConfig.max_object_size_limit.value} bytes`
: ''}
</span>
</section>
`;
}
private renderMatchAuthoredDateWithCommitterDate() {
return html`
<section>
<span class="title"
>Match authored date with committer date upon submit</span
>
<span class="value">
<gr-select
id="matchAuthoredDateWithCommitterDateSelect"
.bindValue=${this.repoConfig?.match_author_to_committer_date
?.configured_value}
@bind-value-changed=${this
.handleMatchAuthoredDateWithCommitterDateSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.match_author_to_committer_date
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderRejectEmptyCommit() {
return html`
<section>
<span class="title">Reject empty commit upon submit</span>
<span class="value">
<gr-select
id="rejectEmptyCommitSelect"
.bindValue=${this.repoConfig?.reject_empty_commit?.configured_value}
@bind-value-changed=${this
.handleRejectEmptyCommitSelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.reject_empty_commit
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderContributorAgreement() {
return html`
<section>
<span class="title">
Require a valid contributor agreement to upload</span
>
<span class="value">
<gr-select
id="contributorAgreementSelect"
.bindValue=${this.repoConfig?.use_contributor_agreements
?.configured_value}
@bind-value-changed=${this
.handleUseContributorAgreementsBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.use_contributor_agreements
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderUseSignedOffBy() {
return html`
<section>
<span class="title">Require Signed-off-by in commit message</span>
<span class="value">
<gr-select
id="useSignedOffBySelect"
.bindValue=${this.repoConfig?.use_signed_off_by?.configured_value}
@bind-value-changed=${this
.handleUseSignedOffBySelectBindValueChanged}
>
<select ?disabled=${this.readOnly}>
${this.formatBooleanSelect(
this.repoConfig?.use_signed_off_by
).map(
item => html`
<option value=${item.value}>${item.label}</option>
`
)}
</select>
</gr-select>
</span>
</section>
`;
}
private renderPluginConfig() {
const pluginData = this.computePluginData();
if (!pluginData.length) return nothing;
return html` <div
class="pluginConfig"
@plugin-config-changed=${this.handlePluginConfigChanged}
>
<h3 class="heading-3">Plugins</h3>
${pluginData.map(
item => html`
<gr-repo-plugin-config
.pluginData=${item}
?disabled=${this.readOnly}
></gr-repo-plugin-config>
`
)}
</div>`;
}
override willUpdate(changedProperties: PropertyValues) {
if (changedProperties.has('repo')) {
this.loadRepo();
}
if (changedProperties.has('schemesObj')) {
this.computeSchemesAndDefault();
}
}
// private but used in test
computePluginData() {
if (!this.repoConfig || !this.repoConfig.plugin_config) return [];
const pluginConfig = this.repoConfig.plugin_config;
return Object.keys(pluginConfig).map(name => {
return {name, config: pluginConfig[name]};
});
}
// private but used in test
async loadRepo() {
if (!this.repo) return Promise.resolve();
this.repoConfig = undefined;
this.originalConfig = undefined;
this.loading = true;
this.weblinks = [];
this.schemesObj = undefined;
this.readOnly = true;
const promises = [];
const errFn: ErrorCallback = response => {
firePageError(response);
};
promises.push(
this.restApiService.getLoggedIn().then(loggedIn => {
if (loggedIn) {
const repo = this.repo;
if (!repo) throw new Error('undefined repo');
this.restApiService.getRepo(repo).then(repo => {
if (!repo?.web_links) return;
this.weblinks = repo.web_links;
});
this.restApiService.getRepoAccess(repo).then(access => {
if (!access || this.repo !== repo) {
return;
}
// If the user is not an owner, is_owner is not a property.
this.readOnly = !access[repo]?.is_owner;
});
}
})
);
const repoConfigHelper = async () => {
const config = await this.restApiService.getProjectConfig(
this.repo as RepoName,
errFn
);
if (!config) return;
if (config.default_submit_type) {
// The gr-select is bound to submit_type, which needs to be the
// *configured* submit type. When default_submit_type is
// present, the server reports the *effective* submit type in
// submit_type, so we need to overwrite it before storing the
// config in this.
config.submit_type = config.default_submit_type.configured_value;
}
if (!config.state) {
config.state = STATES.active.value;
}
// To properly check if the config has changed we need it to be a string
// as it's converted to a string in the input.
if (config.description === undefined) {
config.description = '';
}
// To properly check if the config has changed we need it to be a string
// as it's converted to a string in the input.
if (config.max_object_size_limit.configured_value === undefined) {
config.max_object_size_limit.configured_value = '';
}
this.repoConfig = config;
this.originalConfig = deepClone(config) as ConfigInfo;
this.loading = false;
};
promises.push(repoConfigHelper());
const configHelper = async () => {
const config = await this.restApiService.getConfig();
if (!config) return;
this.schemesObj = config.download.schemes;
};
promises.push(configHelper());
await Promise.all(promises);
}
// private but used in test
formatBooleanSelect(item?: InheritedBooleanInfo) {
if (!item) return [];
let inheritLabel = 'Inherit';
if (!(item.inherited_value === undefined)) {
inheritLabel = `Inherit (${item.inherited_value})`;
}
return [
{
label: inheritLabel,
value: 'INHERIT',
},
{
label: 'True',
value: 'TRUE',
},
{
label: 'False',
value: 'FALSE',
},
];
}
private formatSubmitTypeSelect(repoConfig?: ConfigInfo) {
if (!repoConfig) return [];
const allValues = Object.values(SUBMIT_TYPES);
const type = repoConfig.default_submit_type;
if (!type) {
// Server is too old to report default_submit_type, so assume INHERIT
// is not a valid value.
return allValues;
}
let inheritLabel = 'Inherit';
if (type.inherited_value) {
inheritLabel = `Inherit (${type.inherited_value})`;
for (const val of allValues) {
if (val.value === type.inherited_value) {
inheritLabel = `Inherit (${val.label})`;
break;
}
}
}
return [
{
label: inheritLabel,
value: 'INHERIT',
},
...allValues,
];
}
// private but used in test
formatRepoConfigForSave(repoConfig?: ConfigInfo): ConfigInput {
if (!repoConfig) return {};
const configInputObj: ConfigInput = {};
for (const configKey of Object.keys(repoConfig)) {
const key = configKey as keyof ConfigInfo;
if (key === 'default_submit_type') {
// default_submit_type is not in the input type, and the
// configured value was already copied to submit_type by
// _loadProject. Omit this property when saving.
continue;
}
if (key === 'plugin_config') {
configInputObj.plugin_config_values = repoConfig.plugin_config;
} else if (typeof repoConfig[key] === 'object') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const repoConfigObj: any = repoConfig[key];
if (repoConfigObj.configured_value !== undefined) {
configInputObj[key as keyof ConfigInput] =
repoConfigObj.configured_value;
}
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
configInputObj[key as keyof ConfigInput] = repoConfig[key] as any;
}
}
return configInputObj;
}
// private but used in test
async handleSaveRepoConfig() {
if (!this.repoConfig || !this.repo)
return Promise.reject(new Error('undefined repoConfig or repo'));
await this.restApiService.saveRepoConfig(
this.repo,
this.formatRepoConfigForSave(this.repoConfig)
);
this.originalConfig = deepClone(this.repoConfig) as ConfigInfo;
this.pluginConfigChanged = false;
return;
}
private isEdited(
original?: InheritedBooleanInfo | MaxObjectSizeLimitInfo,
repo?: InheritedBooleanInfo | MaxObjectSizeLimitInfo
) {
return original?.configured_value !== repo?.configured_value;
}
private hasConfigChanged() {
const {repoConfig, originalConfig} = this;
if (!repoConfig || !originalConfig) return false;
if (originalConfig.description !== repoConfig.description) {
return true;
}
if (originalConfig.state !== repoConfig.state) {
return true;
}
if (originalConfig.submit_type !== repoConfig.submit_type) {
return true;
}
if (
this.isEdited(
originalConfig.use_content_merge,
repoConfig.use_content_merge
)
) {
return true;
}
if (
this.isEdited(
originalConfig.create_new_change_for_all_not_in_target,
repoConfig.create_new_change_for_all_not_in_target
)
) {
return true;
}
if (
this.isEdited(
originalConfig.require_change_id,
repoConfig.require_change_id
)
) {
return true;
}
if (
this.isEdited(
originalConfig.enable_signed_push,
repoConfig.enable_signed_push
)
) {
return true;
}
if (
this.isEdited(
originalConfig.require_signed_push,
repoConfig.require_signed_push
)
) {
return true;
}
if (
this.isEdited(
originalConfig.reject_implicit_merges,
repoConfig.reject_implicit_merges
)
) {
return true;
}
if (
this.isEdited(
originalConfig.enable_reviewer_by_email,
repoConfig.enable_reviewer_by_email
)
) {
return true;
}
if (
this.isEdited(
originalConfig.private_by_default,
repoConfig.private_by_default
)
) {
return true;
}
if (
this.isEdited(
originalConfig.work_in_progress_by_default,
repoConfig.work_in_progress_by_default
)
) {
return true;
}
if (
this.isEdited(
originalConfig.max_object_size_limit,
repoConfig.max_object_size_limit
)
) {
return true;
}
if (
this.isEdited(
originalConfig.match_author_to_committer_date,
repoConfig.match_author_to_committer_date
)
) {
return true;
}
if (
this.isEdited(
originalConfig.reject_empty_commit,
repoConfig.reject_empty_commit
)
) {
return true;
}
if (
this.isEdited(
originalConfig.use_contributor_agreements,
repoConfig.use_contributor_agreements
)
) {
return true;
}
if (
this.isEdited(
originalConfig.use_signed_off_by,
repoConfig.use_signed_off_by
)
) {
return true;
}
return this.pluginConfigChanged;
}
private computeSchemesAndDefault() {
this.schemes = !this.schemesObj ? [] : Object.keys(this.schemesObj).sort();
if (this.schemes.length > 0) {
if (!this.selectedScheme || !this.schemes.includes(this.selectedScheme)) {
this.selectedScheme = this.schemes.sort()[0];
}
}
}
private getSchemeInfo(): DownloadSchemeInfo | undefined {
if (!this.schemesObj || !this.repo || !this.selectedScheme) {
return undefined;
}
if (!hasOwnProperty(this.schemesObj, this.selectedScheme)) return undefined;
return this.schemesObj[this.selectedScheme];
}
private computeDescription() {
const schemeInfo = this.getSchemeInfo();
return schemeInfo?.description;
}
private computeCommands() {
const schemeInfo = this.getSchemeInfo();
if (!this.repo || !schemeInfo) return undefined;
const commandObj = schemeInfo.clone_commands ?? {};
const commands = [];
for (const [title, command] of Object.entries(commandObj)) {
commands.push({
title,
command: command
.replace(/\${project}/gi, encodeURI(this.repo))
.replace(
/\${project-base-name}/gi,
encodeURI(this.repo.substring(this.repo.lastIndexOf('/') + 1))
),
});
}
return commands;
}
private computeChangesUrl(name?: RepoName) {
if (!name) return '';
return createSearchUrl({repo: name});
}
// private but used in test
handlePluginConfigChanged({
detail: {name, config},
}: {
detail: {
name: string;
config: PluginParameterToConfigParameterInfoMap;
};
}) {
if (this.repoConfig?.plugin_config) {
this.repoConfig.plugin_config[name] = config;
this.pluginConfigChanged = true;
this.requestUpdate();
}
}
private handleDescriptionTextChanged(e: BindValueChangeEvent) {
if (!this.repoConfig || this.loading) return;
if (this.repoConfig.description === e.detail.value) return;
this.repoConfig = {
...this.repoConfig,
description: e.detail.value,
};
this.requestUpdate();
}
private handleStateSelectBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig || this.loading) return;
if (this.repoConfig.state === e.detail.value) return;
this.repoConfig = {
...this.repoConfig,
state: e.detail.value as RepoState,
};
this.requestUpdate();
}
private handleSubmitTypeSelectBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig || this.loading) return;
if (this.repoConfig.submit_type === e.detail.value) return;
this.repoConfig = {
...this.repoConfig,
submit_type: e.detail.value as SubmitType,
};
this.requestUpdate();
}
private handleContentMergeSelectBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.use_content_merge || this.loading) return;
this.repoConfig.use_content_merge.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleNewChangeSelectBindValueChanged(e: BindValueChangeEvent) {
if (
!this.repoConfig?.create_new_change_for_all_not_in_target ||
this.loading
)
return;
this.repoConfig.create_new_change_for_all_not_in_target.configured_value = e
.detail.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleRequireChangeIdSelectBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.require_change_id || this.loading) return;
this.repoConfig.require_change_id.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleEnableSignedPushBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.enable_signed_push || this.loading) return;
this.repoConfig.enable_signed_push.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleRequireSignedPushBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.require_signed_push || this.loading) return;
this.repoConfig.require_signed_push.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleRejectImplicitMergeSelectBindValueChanged(
e: BindValueChangeEvent
) {
if (!this.repoConfig?.reject_implicit_merges || this.loading) return;
this.repoConfig.reject_implicit_merges.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleUnRegisteredCcSelectBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.enable_reviewer_by_email || this.loading) return;
this.repoConfig.enable_reviewer_by_email.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleSetAllNewChangesPrivateByDefaultSelectBindValueChanged(
e: BindValueChangeEvent
) {
if (!this.repoConfig?.private_by_default || this.loading) return;
this.repoConfig.private_by_default.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleSetAllNewChangesWorkInProgressByDefaultSelectBindValueChanged(
e: BindValueChangeEvent
) {
if (!this.repoConfig?.work_in_progress_by_default || this.loading) return;
this.repoConfig.work_in_progress_by_default.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleMaxGitObjSizeBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.max_object_size_limit || this.loading) return;
this.repoConfig.max_object_size_limit.value = e.detail.value;
this.repoConfig.max_object_size_limit.configured_value = e.detail.value;
this.requestUpdate();
}
private handleMatchAuthoredDateWithCommitterDateSelectBindValueChanged(
e: BindValueChangeEvent
) {
if (!this.repoConfig?.match_author_to_committer_date || this.loading)
return;
this.repoConfig.match_author_to_committer_date.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleRejectEmptyCommitSelectBindValueChanged(
e: BindValueChangeEvent
) {
if (!this.repoConfig?.reject_empty_commit || this.loading) return;
this.repoConfig.reject_empty_commit.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleUseContributorAgreementsBindValueChanged(
e: BindValueChangeEvent
) {
if (!this.repoConfig?.use_contributor_agreements || this.loading) return;
this.repoConfig.use_contributor_agreements.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
private handleUseSignedOffBySelectBindValueChanged(e: BindValueChangeEvent) {
if (!this.repoConfig?.use_signed_off_by || this.loading) return;
this.repoConfig.use_signed_off_by.configured_value = e.detail
.value as InheritedBooleanInfoConfiguredValue;
this.requestUpdate();
}
}