Skip to content

Commit

Permalink
Add possibility to lock project template configurations (#665)
Browse files Browse the repository at this point in the history
  • Loading branch information
Remi749 authored Mar 17, 2022
1 parent 77f1e90 commit 3366f13
Show file tree
Hide file tree
Showing 18 changed files with 211 additions and 103 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Every change is marked with issue ID.
- Added functionality for dynamic welcomepages based on project phases #643
- Added 'Vis alle egenskaper' button with panel to ProjectInformation webpart #650
- Added support for {site} token in Planner-tasks #646
- Add possibility to lock'project template configurations #645

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@
@include ms-Grid-col;
@include ms-sm12;
padding: 10px 0 10px 0;

.container {
.item {
width: 50%;
display: inline-block;
vertical-align: top;
vertical-align: top;
padding-right: 5px;
box-sizing: border-box;

.toggle {
display: flex;
align-items: baseline;

.icon {
padding-left: 5px;
color: #a6a6a6;
}
}

.subText {
@include ms-font-s;
margin: 5px 0 15px 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { stringIsNullOrEmpty } from '@pnp/common'
import { Toggle } from 'office-ui-fabric-react/lib/Toggle'
import { Icon } from 'office-ui-fabric-react/lib/Icon'
import * as React from 'react'
import { ProjectExtension } from '../../../models'
import styles from './ExtensionsSection.module.scss'
Expand All @@ -26,12 +27,18 @@ export const ExtensionsSection = (props: IExtensionsSectionProps) => {
<div className={styles.container}>
{props.extensions.map((ext) => (
<div key={ext.key} className={styles.item}>
<Toggle
label={ext.text}
defaultChecked={selectedKeys.indexOf(ext.key) !== -1}
inlineLabel={true}
onChange={(_event, checked) => onChange(ext, checked)}
/>
<div className={styles.toggle}>
<Toggle
label={ext.text}
defaultChecked={selectedKeys.indexOf(ext.key) !== -1}
disabled={props.lockDefault && ext.isDefault}
inlineLabel={true}
onChange={(_event, checked) => onChange(ext, checked)}
/>
{(props.lockDefault && ext.isDefault) && (
<Icon iconName={'Lock'} className={styles.icon} />
)}
</div>
<div className={styles.subText} hidden={stringIsNullOrEmpty(ext.subText)}>
<span>{ext.subText}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface IExtensionsSectionProps {
*/
selectedExtensions?: ProjectExtension[]

/**
* Locks (disables) the default extensions
*/
lockDefault?: boolean

/**
* On extensions changed
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@
@include ms-Grid-col;
@include ms-sm12;
padding: 10px 0 10px 0;

.container {
.item {
width: 50%;
display: inline-block;
vertical-align: top;
vertical-align: top;
padding-right: 5px;
box-sizing: border-box;

.toggle {
display: flex;
align-items: baseline;

.icon {
padding-left: 5px;
color: #a6a6a6;
}
}

.subText {
@include ms-font-s;
margin: 5px 0 15px 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Toggle } from 'office-ui-fabric-react/lib/Toggle'
import { Icon } from 'office-ui-fabric-react/lib/Icon'
import { stringIsNullOrEmpty } from '@pnp/common'

import * as React from 'react'
import { ListContentConfig } from '../../../models'
import { IListContentSectionProps } from './types'
Expand Down Expand Up @@ -34,12 +34,18 @@ export const ListContentSection = (props: IListContentSectionProps) => {
.filter((lcc) => !lcc.hidden)
.map((lcc) => (
<div key={lcc.key} className={styles.item}>
<Toggle
label={lcc.text}
defaultChecked={selectedKeys.indexOf(lcc.key) !== -1}
inlineLabel={true}
onChanged={(checked) => onChanged(lcc, checked)}
/>
<div className={styles.toggle}>
<Toggle
label={lcc.text}
defaultChecked={selectedKeys.indexOf(lcc.key) !== -1}
disabled={props.lockDefault && lcc.isDefault}
inlineLabel={true}
onChanged={(checked) => onChanged(lcc, checked)}
/>
{(props.lockDefault && lcc.isDefault) && (
<Icon iconName={'Lock'} className={styles.icon} />
)}
</div>
<div className={styles.subText} hidden={stringIsNullOrEmpty(lcc.subText)}>
<span>{lcc.subText}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface IListContentSectionProps {
*/
selectedListContentConfig?: ListContentConfig[]

/**
* Locks (disables) the default list content config
*/
lockDefault?: boolean

/**
* On list content config changed
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,15 @@ export class TemplateSelectDialog extends React.Component<
super(props)
this.state = {
selectedTemplate: this._getDefaultTemplate(),
selectedExtensions: props.data.extensions.filter((ext) => ext.isDefault || this._getDefaultTemplate().listExtensionIds?.some((id) => id === ext.id)),
selectedListContentConfig: props.data.listContentConfig.filter((lcc) => lcc.isDefault || this._getDefaultTemplate().listContentConfigIds?.some((id) => id === lcc.id)),
selectedExtensions: props.data.extensions.filter(
(ext) =>
ext.isDefault || this._getDefaultTemplate().listExtensionIds?.some((id) => id === ext.id)
),
selectedListContentConfig: props.data.listContentConfig.filter(
(lcc) =>
lcc.isDefault ||
this._getDefaultTemplate().listContentConfigIds?.some((id) => id === lcc.id)
),
settings: new ProjectSetupSettings().useDefault()
}
}
Expand Down Expand Up @@ -67,27 +74,33 @@ export class TemplateSelectDialog extends React.Component<
{!isEmpty(data.extensions) && (
<PivotItem headerText={strings.ExtensionsTitle} itemIcon='ArrangeBringForward'>
{selectedTemplate.listExtensionIds && (
<MessageBar messageBarType={MessageBarType.info}>
{strings.TemplateListContentConfigText}
</MessageBar>
<div style={{ marginTop: 20 }}>
<MessageBar messageBarType={MessageBarType.info}>
{strings.TemplateListContentConfigText}
</MessageBar>
</div>
)}
<ExtensionsSection
extensions={data.extensions}
selectedExtensions={selectedExtensions}
lockDefault={selectedTemplate.isDefaultExtensionsLocked}
onChange={(s) => this.setState({ selectedExtensions: s })}
/>
</PivotItem>
)}
{!isEmpty(data.listContentConfig) && (
<PivotItem headerText={strings.ListContentTitle} itemIcon='ViewList'>
{selectedTemplate.listContentConfigIds && (
<MessageBar messageBarType={MessageBarType.info}>
{strings.TemplateListContentConfigText}
</MessageBar>
<div style={{ marginTop: 20 }}>
<MessageBar messageBarType={MessageBarType.info}>
{strings.TemplateListContentConfigText}
</MessageBar>
</div>
)}
<ListContentSection
listContentConfig={data.listContentConfig}
selectedListContentConfig={selectedListContentConfig}
lockDefault={selectedTemplate.isDefaultListContentLocked}
onChange={(s) => this.setState({ selectedListContentConfig: s })}
/>
</PivotItem>
Expand All @@ -99,13 +112,17 @@ export class TemplateSelectDialog extends React.Component<

/**
* Sets the selected template to the state, and updates the predfined selected extensions
* @param template
* @param template
*/
private _onTemplateChange(template: ProjectTemplate): void {
this.setState({
selectedTemplate: template,
selectedExtensions: this.props.data.extensions.filter((ext) => ext.isDefault || template.listExtensionIds?.some((id) => id === ext.id)),
selectedListContentConfig: this.props.data.listContentConfig.filter((lcc) => lcc.isDefault || template.listContentConfigIds?.some((id) => id === lcc.id)),
selectedExtensions: this.props.data.extensions.filter(
(ext) => ext.isDefault || template.listExtensionIds?.some((id) => id === ext.id)
),
selectedListContentConfig: this.props.data.listContentConfig.filter(
(lcc) => lcc.isDefault || template.listContentConfigIds?.some((id) => id === lcc.id)
)
})
}

Expand Down Expand Up @@ -141,7 +158,9 @@ export class TemplateSelectDialog extends React.Component<
*/
private _onSubmit() {
const data = { ...this.state }
data.selectedTemplate.listContentConfigIds = this.state.selectedListContentConfig.map((lcc) => lcc.id)
data.selectedTemplate.listContentConfigIds = this.state.selectedListContentConfig.map(
(lcc) => lcc.id
)
this.props.onSubmit(data)
}
}
Expand Down
31 changes: 20 additions & 11 deletions SharePointFramework/ProjectExtensions/src/models/ProjectTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Schema } from 'sp-js-provisioning'
export interface IProjectTemplateSPItem {
Id?: number
IsDefaultTemplate?: boolean
IsDefaultExtensionsLocked?: boolean
IsDefaultListContentLocked?: boolean
IconName?: string
ListContentConfigLookupId?: number[]
File?: { UniqueId: string; Name: string; Title: string; ServerRelativeUrl: string }
Expand All @@ -25,6 +27,8 @@ export class ProjectTemplate implements IDropdownOption {
public text: string
public subText: string
public isDefault: boolean
public isDefaultExtensionsLocked: boolean
public isDefaultListContentLocked: boolean
public iconName: string
public listContentConfigIds: number[]
public projectTemplateId: number
Expand All @@ -42,14 +46,14 @@ export class ProjectTemplate implements IDropdownOption {
this.text = spItem.FieldValuesAsText.Title
this.subText = spItem.FieldValuesAsText.GtDescription
this.isDefault = spItem?.IsDefaultTemplate
this.isDefaultExtensionsLocked = spItem?.IsDefaultExtensionsLocked
this.isDefaultListContentLocked = spItem?.IsDefaultListContentLocked
this.iconName = spItem.IconName
this.listContentConfigIds = spItem.ListContentConfigLookupId?.length > 0
? spItem.ListContentConfigLookupId
: null
this.listContentConfigIds =
spItem.ListContentConfigLookupId?.length > 0 ? spItem.ListContentConfigLookupId : null
this.projectTemplateId = spItem.GtProjectTemplateId
this.listExtensionIds = spItem.GtProjectExtensionsId?.length > 0
? spItem.GtProjectExtensionsId
: null
this.listExtensionIds =
spItem.GtProjectExtensionsId?.length > 0 ? spItem.GtProjectExtensionsId : null
this.projectContentType = spItem.GtProjectContentType
this.projectStatusContentType = spItem.GtProjectStatusContentType
this.projectColumns = spItem.GtProjectColumns
Expand All @@ -60,14 +64,19 @@ export class ProjectTemplate implements IDropdownOption {
public async getSchema(): Promise<Schema> {
const schema = await this.web.getFileByServerRelativeUrl(this.projectTemplateUrl).getJSON()
schema.Parameters = schema.Parameters || {}
schema.Parameters.ProjectContentTypeId = this?.projectContentType ?? schema.Parameters.ProjectContentTypeId
schema.Parameters.ProjectStatusContentTypeId = this?.projectStatusContentType ?? schema.Parameters.ProjectStatusContentTypeId
schema.Parameters.ProvisionSiteFields = this?.projectColumns ?? schema.Parameters.ProvisionSiteFields
schema.Parameters.CustomSiteFields = this?.projectCustomColumns ?? schema.Parameters.CustomSiteFields
schema.Parameters.ProjectContentTypeId =
this?.projectContentType ?? schema.Parameters.ProjectContentTypeId
schema.Parameters.ProjectStatusContentTypeId =
this?.projectStatusContentType ?? schema.Parameters.ProjectStatusContentTypeId
schema.Parameters.ProvisionSiteFields =
this?.projectColumns ?? schema.Parameters.ProvisionSiteFields
schema.Parameters.CustomSiteFields =
this?.projectCustomColumns ?? schema.Parameters.CustomSiteFields
if (!schema.Parameters.TermSetIds) {
schema.Parameters.TermSetIds = {}
}
schema.Parameters.TermSetIds.GtProjectPhase = this?.projectPhaseTermId ?? schema.Parameters.TermSetIds.GtProjectPhase
schema.Parameters.TermSetIds.GtProjectPhase =
this?.projectPhaseTermId ?? schema.Parameters.TermSetIds.GtProjectPhase
return schema
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { IProjectTemplateSPItem } from 'models'

export class ProjectTemplateFile {
public id: number
public serverRelativeUrl: string
public name: string
constructor(item: IProjectTemplateSPItem) {
this.id = item.Id,
this.name = item.File.Name
this.serverRelativeUrl = item.File.ServerRelativeUrl
}
}
export class ProjectTemplateFile {
public id: number
public serverRelativeUrl: string
public name: string

constructor(item: IProjectTemplateSPItem) {
this.id = item.Id
this.name = item.File.Name
this.serverRelativeUrl = item.File.ServerRelativeUrl
}
}
2 changes: 1 addition & 1 deletion SharePointFramework/ProjectExtensions/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ export * from './TemplateItem'
export * from './IListProperties'
export * from './IPlannerTaskSPItem'
export * from './SPFolder'
export * from './ProjectTemplateFile'
export * from './ProjectTemplateFile'
29 changes: 18 additions & 11 deletions SharePointFramework/ProjectExtensions/src/projectSetup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ import {
ProgressDialog,
TemplateSelectDialog
} from '../components'
import { ListContentConfig, ProjectExtension, ProjectTemplate, ProjectTemplateFile } from '../models'
import {
ListContentConfig,
ProjectExtension,
ProjectTemplate,
ProjectTemplateFile
} from '../models'
import { deleteCustomizer } from './deleteCustomizer'
import { ProjectSetupError } from './ProjectSetupError'
import { ProjectSetupSettings } from './ProjectSetupSettings'
Expand Down Expand Up @@ -342,17 +347,19 @@ export default class ProjectSetup extends BaseApplicationCustomizer<IProjectSetu
),
this.properties.extensionsLibrary
? this._portal.getItems(
this.properties.extensionsLibrary,
ProjectExtension,
{
ViewXml:
'<View Scope="RecursiveAll"><Query><Where><Eq><FieldRef Name="FSObjType" /><Value Type="Integer">0</Value></Eq></Where></Query></View>'
},
['File', 'FieldValuesAsText']
)
this.properties.extensionsLibrary,
ProjectExtension,
{
ViewXml:
'<View Scope="RecursiveAll"><Query><Where><Eq><FieldRef Name="FSObjType" /><Value Type="Integer">0</Value></Eq></Where></Query></View>'
},
['File', 'FieldValuesAsText']
)
: Promise.resolve([]),
this.properties.contentConfigList
? this._portal.getItems(this.properties.contentConfigList, ListContentConfig, {}, ['File'])
? this._portal.getItems(this.properties.contentConfigList, ListContentConfig, {}, [
'File'
])
: Promise.resolve([]),
this._portal.getItems(
strings.Lists_ProjectTemplateFiles_Title,
Expand All @@ -364,7 +371,7 @@ export default class ProjectSetup extends BaseApplicationCustomizer<IProjectSetu
)
])
const templates = _templates.map((tmpl) => {
const [tmplFile] = templateFiles.filter(file => file.id === tmpl.projectTemplateId)
const [tmplFile] = templateFiles.filter((file) => file.id === tmpl.projectTemplateId)
tmpl.projectTemplateUrl = tmplFile?.serverRelativeUrl
return tmpl
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IBaseTaskParams } from './IBaseTaskParams'
import { OnProgressCallbackFunction } from '../OnProgressCallbackFunction'

export interface IBaseTask {
params: IBaseTaskParams
taskName: string
Expand Down
Loading

0 comments on commit 3366f13

Please sign in to comment.