Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ public class ApiConstants {
public static final String DEVICE_IDS = "deviceids";
public static final String DEVICE_NAME = "devicename";
public static final String DIRECT_DOWNLOAD = "directdownload";
public static final String DISABLE_DEFAULT_VIEW = "disabledefaultview";
public static final String DISK = "disk";
public static final String DISK_OFFERING_ID = "diskofferingid";
public static final String NEW_DISK_OFFERING_ID = "newdiskofferingid";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public void execute() {
Map<String, Object> vpnCustomerGatewayParameters = (Map<String, Object>) capabilities.get(ApiConstants.VPN_CUSTOMER_GATEWAY_PARAMETERS);
response.setVpnCustomerGatewayParameters(vpnCustomerGatewayParameters);
}
response.setDisableDefaultView((Boolean)capabilities.get(ApiConstants.DISABLE_DEFAULT_VIEW));
response.setObjectName("capability");
response.setResponseName(getCommandName());
this.setResponseObject(response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ public class CapabilitiesResponse extends BaseResponse {
@Param(description = "Excluded and obsolete VPN customer gateway cryptographic parameters")
private Map<String, Object> vpnCustomerGatewayParameters;

@SerializedName(ApiConstants.DISABLE_DEFAULT_VIEW)
@Param(description = "Whether the Default view should be disabled for account.", since = "4.20.0.10-scclouds")
private Boolean disableDefaultView;

public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
this.securityGroupsEnabled = securityGroupsEnabled;
}
Expand Down Expand Up @@ -298,4 +302,8 @@ public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) {
public void setVpnCustomerGatewayParameters(Map<String, Object> vpnCustomerGatewayParameters) {
this.vpnCustomerGatewayParameters = vpnCustomerGatewayParameters;
}

public void setDisableDefaultView(Boolean disableDefaultView) {
this.disableDefaultView = disableDefaultView;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4904,6 +4904,7 @@ public Map<String, Object> listCapabilities(final ListCapabilitiesCmd cmd) {
if (exposeCloudStackVersionInApiListCapabilities.value()) {
capabilities.put("cloudStackVersion", getVersion());
}
final Boolean disableDefaultView = AccountManager.DisableDefaultView.valueIn(caller.getAccountId());

capabilities.put("securityGroupsEnabled", securityGroupsEnabled);
capabilities.put("userPublicTemplateEnabled", userPublicTemplateEnabled);
Expand All @@ -4930,6 +4931,7 @@ public Map<String, Object> listCapabilities(final ListCapabilitiesCmd cmd) {
capabilities.put(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME, StatsCollector.vmDiskStatsMaxRetentionTime.value());
capabilities.put(ApiConstants.INSTANCE_LEASE_ENABLED, VMLeaseManager.InstanceLeaseEnabled.value());
capabilities.put(ApiConstants.DYNAMIC_SCALING_ENABLED, UserVmManager.EnableDynamicallyScaleVm.value());
capabilities.put(ApiConstants.DISABLE_DEFAULT_VIEW, disableDefaultView);
if (apiLimitEnabled) {
capabilities.put("apiLimitInterval", apiLimitInterval);
capabilities.put("apiLimitMax", apiLimitMax);
Expand Down
2 changes: 2 additions & 0 deletions server/src/main/java/com/cloud/user/AccountManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ void buildACLViewSearchCriteria(SearchCriteria<? extends ControlledViewEntity> s
ConfigKey<Boolean> UseSecretKeyInResponse = new ConfigKey<Boolean>("Advanced", Boolean.class, "use.secret.key.in.response", "false",
"This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", true);

ConfigKey<Boolean> DisableDefaultView = new ConfigKey<>("Advanced", Boolean.class, "disable.default.view", "false", "This defines if the account will have access to the default view, or only to the projects it is associated to. In the GUI, if the account does not have access to any project, the Default View will be rendered.", true, ConfigKey.Scope.Account);
Comment thread
erikbocks marked this conversation as resolved.

boolean moveUser(long id, Long domainId, Account newAccount) throws ResourceAllocationException;

UserTwoFactorAuthenticator getUserTwoFactorAuthenticator(final Long domainId, final Long userAccountId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4205,7 +4205,7 @@ public String getConfigComponentName() {
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {UseSecretKeyInResponse, enableUserTwoFactorAuthentication,
userTwoFactorAuthenticationDefaultProvider, mandateUserTwoFactorAuthentication, userTwoFactorAuthenticationIssuer, apiKeyAccess,
userAllowMultipleAccounts, listOfRoleTypesAllowedForOperationsOfSameRoleType, allowOperationsOnUsersInSameAccount};
userAllowMultipleAccounts, listOfRoleTypesAllowedForOperationsOfSameRoleType, allowOperationsOnUsersInSameAccount, DisableDefaultView};
}

public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticationProviders() {
Expand Down
33 changes: 29 additions & 4 deletions ui/src/components/header/ProjectMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
:defaultOption="defaultOption"
defaultIcon="project-outlined"
:pageSize="100"
@change-option="changeProject" />
:selectFirstOption="shouldHideDefaultView"
@change-option="changeProject"/>
</span>
</template>

<script>
import { getAPI } from '@/api'
import InfiniteScrollSelect from '@/components/widgets/InfiniteScrollSelect'
import eventBus from '@/config/eventBus'

Expand All @@ -43,19 +45,29 @@ export default {
data () {
return {
selectedProjectId: null,
loading: false,
availableProjects: [],
timestamp: new Date().getTime()
}
},
created () {
this.selectedProjectId = this.$store.getters?.project?.id || this.defaultOption.id
this.listUserAvailableProjects()
this.selectedProjectId = this.$store.getters?.project?.id || this.defaultOption?.id
this.$store.dispatch('ToggleTheme', this.selectedProjectId ? 'dark' : 'light')
},
computed: {
shouldHideDefaultView () {
return this.$store.getters.features.disabledefaultview && this.availableProjects.length > 0
},
isDisabled () {
return !('listProjects' in this.$store.getters.apis)
},
defaultOption () {
const isDefaultViewDisabled = this.shouldHideDefaultView

if (isDefaultViewDisabled) {
return null
}

return { id: 0, name: this.$t('label.default.view') }
},
projectsApiParams () {
Expand All @@ -73,8 +85,9 @@ export default {
this.selectedProjectId = newId
}
)
eventBus.on('projects-updated', (args) => {
eventBus.on('projects-updated', () => {
this.timestamp = new Date().getTime()
this.listUserAvailableProjects()
})
},
beforeUnmount () {
Expand All @@ -91,6 +104,18 @@ export default {
if (this.$route.name !== 'dashboard') {
this.$router.push({ name: 'dashboard' })
}
},
listUserAvailableProjects () {
getAPI('listProjects', { details: 'min', listall: true })
.then((response) => {
this.availableProjects = response.listprojectsresponse?.project || []
if (this.shouldHideDefaultView && !this.$store.getters.project?.id) {
this.selectedProjectId = null
}
})
Comment on lines +108 to +115
.catch((error) => {
this.$notifyError(error)
})
}
}
}
Expand Down
24 changes: 21 additions & 3 deletions ui/src/components/widgets/InfiniteScrollSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,27 @@ export default {
watch: {
apiParams () {
this.onSearch()
},
defaultOption (newOption, oldOption) {
if (oldOption) {
const oldValue = oldOption[this.optionValueKey]
this.options = this.options.filter(option => option[this.optionValueKey] !== oldValue)
}

if (newOption) {
this.canSelectFirstOption = true
if (!this.options.some(option => option[this.optionValueKey] === newOption[this.optionValueKey])) {
this.options.unshift(newOption)
}
} else {
this.canSelectFirstOption = false
this.$nextTick(() => {
this.autoSelectFirstOptionIfNeeded()
})
}
}
},
emits: ['change-option-value', 'change-option'],
emits: ['update:value', 'change-option-value', 'change-option'],
methods: {
async fetchItems () {
if (this.successiveFetches === 0 && this.loading) return
Expand Down Expand Up @@ -332,8 +350,7 @@ export default {
if (firstOption) {
const firstValue = firstOption[this.optionValueKey]
this.hasAutoSelectedFirst = true
this.$emit('change-option-value', firstValue)
this.$emit('change-option', firstOption)
this.onChange(firstValue)
}
Comment on lines 350 to 354
},
onSearchTimed (value) {
Expand Down Expand Up @@ -362,6 +379,7 @@ export default {
},
onChange (value) {
this.resetPreselectedOptionValue()
this.$emit('update:value', value)
this.$emit('change-option-value', value)
if (Array.isArray(value)) {
return
Expand Down
Loading