diff --git a/.github/workflows/jekyll_gh_pages.yml b/.github/workflows/jekyll_gh_pages.yml index 2a9708a..6bd9014 100644 --- a/.github/workflows/jekyll_gh_pages.yml +++ b/.github/workflows/jekyll_gh_pages.yml @@ -4,7 +4,7 @@ name: Deploy SharePoint Integration Documentation on: # Runs on pushes targeting the default branch push: - branches: ["1.x"] + branches: ['1.x'] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -18,7 +18,7 @@ permissions: # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: - group: "pages" + group: 'pages' cancel-in-progress: false jobs: @@ -27,12 +27,19 @@ jobs: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: - name: Checkout uses: actions/checkout@v3 + - name: Setup Pages uses: actions/configure-pages@v3 + + - name: Move LaserficheSharePointIntegrationAppManifest.json + run: cp ./UserDocuments/LaserficheSharePointIntegrationAppManifest.json ./jekyll_files/docs/assets/LaserficheSharePointIntegrationAppManifest.json + - name: Build with Jekyll uses: actions/jekyll-build-pages@v1 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 962fc8b..f4c1255 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,10 +34,10 @@ jobs: message: Workflow run ${{github.server_url}}/${{github.repository}}/actions/runs/${{ github.run_id}} - name: replace config/package-solution.json version - run: sed -i 's/VERSION_TO_UPDATE/${{env.fullVersion}}/g' config/package-solution.json + run: sed -i 's/"1.0.0.0"/"${{env.fullVersion}}"/g' config/package-solution.json - name: replace package.json version - run: sed -i 's/VERSION_TO_UPDATE/${{env.fullVersion}}/g' package.json + run: sed -i 's/"1.0.0.0"/"${{env.fullVersion}}"/g' package.json - name: Install dependencies run: npm ci diff --git a/config/package-solution.json b/config/package-solution.json index 4bb6d13..c386fc2 100644 --- a/config/package-solution.json +++ b/config/package-solution.json @@ -3,7 +3,7 @@ "solution": { "name": "Laserfiche Sharepoint Online Integration", "id": "9d9d4fa7-bbf4-489a-acd5-0e1709d40e8b", - "version": "VERSION_TO_UPDATE", + "version": "1.0.0.0", "includeClientSideAssets": true, "title": "Laserfiche SharePoint Online Integration", "iconPath": "assets/laserfiche-logo-96.png", @@ -20,7 +20,7 @@ "title": "Save to Laserfiche", "description": "Allows user to save SharePoint documents to their Laserfiche repository, with admin-configured metadata mappings.", "id": "9c2b7b96-fb6e-4c42-9676-9d434992110f", - "version": "VERSION_TO_UPDATE", + "version": "1.0.0.0", "assets": { "elementManifests": ["elements.xml"] } diff --git a/jekyll_files/docs/admin-documentation/register-app-in-laserfiche.md b/jekyll_files/docs/admin-documentation/register-app-in-laserfiche.md index b6415eb..3e07368 100644 --- a/jekyll_files/docs/admin-documentation/register-app-in-laserfiche.md +++ b/jekyll_files/docs/admin-documentation/register-app-in-laserfiche.md @@ -15,7 +15,7 @@ parent: Laserfiche SharePoint Integration Administration Guide 1. Open the [Developer Console](https://developer.laserfiche.com/developer-console.html). -1. Attempt to Create a New App from Manifest, and upload the manifest provided [here](../assets/manifest.json). +1. Attempt to Create a New App from Manifest, and upload the manifest provided [here](../assets/LaserficheSharePointIntegrationAppManifest.json). 1. If the attempt fails because an app with that client ID already exists, find the app with that client id by opening [this url](https://app.laserfiche.com/devconsole/apps/8ee987ea-a0b1-4ca2-85c4-a79b335cd214/config) in a new tab. diff --git a/jekyll_files/docs/assets/manifest.json b/jekyll_files/docs/assets/manifest.json deleted file mode 100644 index 4d1c5d2..0000000 --- a/jekyll_files/docs/assets/manifest.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "clientId": "8ee987ea-a0b1-4ca2-85c4-a79b335cd214", - "name": "Laserfiche SharePoint Integration", - "description": "View your Laserfiche repository and send SharePoint documents to Laserfiche from directly within SharePoint.", - "type": "SPA", - "expiresInMinutes": 60, - "scopes": [ - "repository.Write", - "repository.Read" - ], - "privacyPolicyUrl": "https://www.laserfiche.com/legal/privacy/", - "documentationUrl": "https://laserfiche.github.io/laserfiche-sharepoint-integration/", - "termsOfServiceUrl": "https://www.laserfiche.com/legal/terms-of-use/" -} diff --git a/package-lock.json b/package-lock.json index 959131e..f58d790 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "laserfiche-sharepoint-integration", - "version": "VERSION_TO_UPDATE", + "version": "1.0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "laserfiche-sharepoint-integration", - "version": "VERSION_TO_UPDATE", + "version": "1.0.0.0", "license": "MIT", "dependencies": { "@laserfiche/lf-repository-api-client": "^1.0.12", diff --git a/package.json b/package.json index bcf1a64..0800eb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "laserfiche-sharepoint-integration", - "version": "VERSION_TO_UPDATE", + "version": "1.0.0.0", "private": true, "main": "lib/index.js", "license": "MIT", diff --git a/src/extensions/savetoLaserfiche/SaveToLaserficheDialog.tsx b/src/extensions/savetoLaserfiche/SaveToLaserficheDialog.tsx index aec1fc3..b4bb9f7 100644 --- a/src/extensions/savetoLaserfiche/SaveToLaserficheDialog.tsx +++ b/src/extensions/savetoLaserfiche/SaveToLaserficheDialog.tsx @@ -22,10 +22,7 @@ import { SPComponentLoader } from '@microsoft/sp-loader'; import * as ReactDOM from 'react-dom'; import { BaseDialog } from '@microsoft/sp-dialog'; import { getRegion } from '../../Utils/Funcs'; -import { - APIServerException, - Entry, -} from '@laserfiche/lf-repository-api-client'; +import { Entry } from '@laserfiche/lf-repository-api-client'; import { RepositoryClientExInternal } from '../../repository-client/repository-client'; import { IRepositoryApiClientExInternal } from '../../repository-client/repository-client-types'; import { PathUtils } from '@laserfiche/lf-js-utils'; @@ -76,7 +73,8 @@ export default class SaveToLaserficheCustomDialog extends BaseDialog { } } -const ENTRY_WITH_SAME_NAME_EXISTS_IN_FOLDER_IF_CONTINUE_LF_WILL_RENAME = 'An entry with the same name already exists in the specified folder. If you continue, Laserfiche will automatically rename the new document.'; +const ENTRY_WITH_SAME_NAME_EXISTS_IN_FOLDER_IF_CONTINUE_LF_WILL_RENAME = + 'An entry with the same name already exists in the specified folder. If you continue, Laserfiche will automatically rename the new document.'; function SaveToLaserficheDialog(props: { isSuccessfulLoggedIn: (success: boolean) => void; closeClick: (success?: SavedToLaserficheDocumentData) => Promise; @@ -112,9 +110,9 @@ function SaveToLaserficheDialog(props: { React.useEffect(() => { const initializeComponentAsync: () => Promise = async () => { + await SPComponentLoader.loadScript(ZONE_JS_URL); + await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); try { - await SPComponentLoader.loadScript(ZONE_JS_URL); - await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); if (loginComponent.current?.authorization_credentials) { const validRepoClient = await tryGetValidRepositoryClientAsync(); const saveToLF = new SaveDocumentToLaserfiche( @@ -129,13 +127,14 @@ function SaveToLaserficheDialog(props: { repoId, entryId: Number.parseInt(props.spFileMetadata.entryId, 10), }); - const entryWithPathExists = validRepoClient.entriesClient.getEntryByPath({ - repoId, - fullPath: PathUtils.combinePaths( - entryInfo.fullPath, - props.spFileMetadata.fileName - ), - }); + const entryWithPathExists = + await validRepoClient.entriesClient.getEntryByPath({ + repoId, + fullPath: PathUtils.combinePaths( + entryInfo.fullPath, + PathUtils.removeFileExtension(props.spFileMetadata.fileName) + ), + }); if (entryWithPathExists) { setShowSaveTo(false); const confirmSave = await getConfirmation( @@ -151,11 +150,10 @@ function SaveToLaserficheDialog(props: { } } } catch (err) { - const docDoesNotAlreadyExists = (err as APIServerException).statusCode === 404; + const docDoesNotAlreadyExists = err.status === 404; if (docDoesNotAlreadyExists) { await continueSavingDocumentAsync(saveToLF); - } - else { + } else { throw err; } } diff --git a/src/extensions/savetoLaserfiche/SavetoLaserficheCommandSet.tsx b/src/extensions/savetoLaserfiche/SavetoLaserficheCommandSet.tsx index 2daa1a5..07f0ecd 100644 --- a/src/extensions/savetoLaserfiche/SavetoLaserficheCommandSet.tsx +++ b/src/extensions/savetoLaserfiche/SavetoLaserficheCommandSet.tsx @@ -10,7 +10,7 @@ import { import { PathUtils } from '@laserfiche/lf-js-utils'; import { CreateConfigurations } from '../../Utils/CreateConfigurations'; import { getSPListURL } from '../../Utils/Funcs'; -import { SP_LOCAL_STORAGE_KEY } from '../../webparts/constants'; +import { LASERFICHE_SIGNIN_PAGE_NAME, SP_LOCAL_STORAGE_KEY } from '../../webparts/constants'; /** * If your command set uses the ClientSideComponentProperties JSON input, @@ -23,10 +23,6 @@ export interface ISendToLfCommandSetProperties { sampleTextTwo: string; } -enum SpWebPartNames { - 'LaserficheSignIn' = 'LaserficheSignIn', -} - const LOG_SOURCE = 'SendToLfCommandSet'; export default class SendToLfCommandSet extends BaseListViewCommandSet { @@ -112,7 +108,7 @@ export default class SendToLfCommandSet extends BaseListViewCommandSet Promise = async () => { + await SPComponentLoader.loadScript(ZONE_JS_URL); + await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); + SPComponentLoader.loadCss(LF_INDIGO_PINK_CSS_URL); + SPComponentLoader.loadCss(LF_MS_OFFICE_LITE_CSS_URL); try { - await SPComponentLoader.loadScript(ZONE_JS_URL); - await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); - SPComponentLoader.loadCss(LF_INDIGO_PINK_CSS_URL); - SPComponentLoader.loadCss(LF_MS_OFFICE_LITE_CSS_URL); const loginCompleted: () => Promise = async () => { await getAndInitializeRepositoryClientAndServicesAsync(); setLoggedIn(true); @@ -121,10 +123,50 @@ export default function LaserficheRepositoryAccessWebPart( void initializeComponentAsync(); }, []); - function clickLogin(): void { + async function pageConfigurationCheck(): Promise { + try { + const res = await fetch( + `${getSPListURL(props.context, 'Site Pages')}/items`, + { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + } + ); + const sitePages = await res.json(); + for (let o = 0; o < sitePages.value.length; o++) { + const pageName = sitePages.value[o].Title; + if (pageName === LASERFICHE_SIGNIN_PAGE_NAME) { + return true; + } + } + } catch (error) { + console.warn(`Unable to determine if a SharePoint Page with name ${LASERFICHE_SIGNIN_PAGE_NAME} exists.`) + return false; + } + return false; + } + + async function clickLogin(): Promise { const url = props.context.pageContext.web.absoluteUrl + '/SitePages/LaserficheSignIn.aspx?autologin'; + const hasSignIn = await pageConfigurationCheck(); + if (!hasSignIn) { + const mes = ( + { + setMessageErrorModal(undefined); + }} + /> + ); + setMessageErrorModal(mes); + return; + } const loginWindow = window.open(url, 'loginWindow', 'popup'); loginWindow.resizeTo(800, 600); window.addEventListener('message', (event) => { @@ -176,15 +218,15 @@ export default function LaserficheRepositoryAccessWebPart( {messageErrorModal !== undefined && ( -
- {messageErrorModal} -
- )} +
+ {messageErrorModal} +
+ )} Promise = async () => { if (folderName) { - if (/^[\\\\]*$/.test(folderName)) { + const fileContainsBackslash = folderName.includes('\\'); + if (fileContainsBackslash) { setCreateFolderNameValidationMessage(folderBackslashNameValidation); } else { setCreateFolderNameValidationMessage(undefined); diff --git a/src/webparts/constants.ts b/src/webparts/constants.ts index dc84dc0..44f0a1b 100644 --- a/src/webparts/constants.ts +++ b/src/webparts/constants.ts @@ -5,6 +5,7 @@ export const MANAGE_MAPPING = 'ManageMapping'; export const MANAGE_CONFIGURATIONS = 'ManageConfigurations'; export const LOGIN_WINDOW_SUCCESS = 'loginWindowSuccess'; +export const LASERFICHE_SIGNIN_PAGE_NAME = 'LaserficheSignIn'; export const SP_LOCAL_STORAGE_KEY = 'spdocdata'; export const SPDEVMODE_LOCAL_STORAGE_KEY = 'spDevMode'; diff --git a/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.module.scss b/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.module.scss index cdd592d..7fc3241 100644 --- a/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.module.scss +++ b/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.module.scss @@ -1,5 +1,10 @@ @import '~@fluentui/react/dist/sass/References.scss'; +@font-face { + font-family: 'Open Sans'; + src: local('Open Sans'), local('OpenSans'), url(https://lfxstatic.com/libs/Open_Sans/OpenSans.woff) format('woff'), url(https://lfxstatic.com/libs/Open_Sans/OpenSans-Regular.ttf) format('truetype'); +} + .lfMaterialIconButton { cursor: pointer; display: flex; @@ -154,7 +159,6 @@ lf-repository-browser { .profileTitle { color: #333333; - font-style: 'Open Sans'; font-size: 16px; font-weight: 600; } diff --git a/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.tsx b/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.tsx index 5185b21..8870235 100644 --- a/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.tsx +++ b/src/webparts/laserficheAdminConfiguration/components/LaserficheAdminConfiguration.tsx @@ -11,6 +11,7 @@ import EditManageConfiguration from './EditManageConfiguration/EditManageConfigu import AddNewManageConfiguration from './AddNewManageConfiguration/AddNewManageConfiguration'; import { clientId, + LASERFICHE_SIGNIN_PAGE_NAME, LF_INDIGO_PINK_CSS_URL, LF_MS_OFFICE_LITE_CSS_URL, LF_UI_COMPONENTS_URL, @@ -25,7 +26,7 @@ import { import { RepositoryClientExInternal } from '../../../repository-client/repository-client'; import { IRepositoryApiClientExInternal } from '../../../repository-client/repository-client-types'; import { SPComponentLoader } from '@microsoft/sp-loader'; -import { getRegion } from '../../../Utils/Funcs'; +import { getRegion, getSPListURL } from '../../../Utils/Funcs'; import styles from './LaserficheAdminConfiguration.module.scss'; import { SPPermission } from '@microsoft/sp-page-context'; import { MessageDialog } from '../../../extensions/savetoLaserfiche/CommonDialogs'; @@ -33,6 +34,7 @@ import { MessageDialog } from '../../../extensions/savetoLaserfiche/CommonDialog const YOU_DO_NOT_HAVE_RIGHTS_FOR_ADMIN_CONFIG_PLEASE_CONTACT_ADMIN = 'You do not have the necessary rights to view or edit the Laserfiche SharePoint Integration configuration. Please contact your administrator for help.'; +const needLaserficheSignInPage = `Missing "${LASERFICHE_SIGNIN_PAGE_NAME}" SharePoint page. Please refer to the Adding App to SharePoint Site topic in the administration guide for configuration steps.`; export default function LaserficheAdminConfiguration( props: ILaserficheAdminConfigurationProps ): JSX.Element { @@ -80,11 +82,11 @@ export default function LaserficheAdminConfiguration( React.useEffect(() => { const initializeComponentAsync: () => Promise = async () => { + SPComponentLoader.loadCss(LF_INDIGO_PINK_CSS_URL); + SPComponentLoader.loadCss(LF_MS_OFFICE_LITE_CSS_URL); + await SPComponentLoader.loadScript(ZONE_JS_URL); + await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); try { - SPComponentLoader.loadCss(LF_INDIGO_PINK_CSS_URL); - SPComponentLoader.loadCss(LF_MS_OFFICE_LITE_CSS_URL); - await SPComponentLoader.loadScript(ZONE_JS_URL); - await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); const loginCompleted: () => Promise = async () => { await getAndInitializeRepositoryClientAndServicesAsync(); setLoggedIn(true); @@ -113,10 +115,50 @@ export default function LaserficheAdminConfiguration( void initializeComponentAsync(); }, []); - function clickLogin(): void { + async function pageConfigurationCheck(): Promise { + try { + const res = await fetch( + `${getSPListURL(props.context, 'Site Pages')}/items`, + { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + } + ); + const sitePages = await res.json(); + for (let o = 0; o < sitePages.value.length; o++) { + const pageName = sitePages.value[o].Title; + if (pageName === LASERFICHE_SIGNIN_PAGE_NAME) { + return true; + } + } + } catch (error) { + console.warn(`Unable to determine if a SharePoint Page with name ${LASERFICHE_SIGNIN_PAGE_NAME} exists.`) + return false; + } + return false; + } + + async function clickLogin(): Promise { const url = props.context.pageContext.web.absoluteUrl + '/SitePages/LaserficheSignIn.aspx?autologin'; + const hasSignIn = await pageConfigurationCheck(); + if (!hasSignIn) { + const mes = ( + { + setMessageErrorModal(undefined); + }} + /> + ); + setMessageErrorModal(mes); + return; + } const loginWindow = window.open(url, 'loginWindow', 'popup'); loginWindow.resizeTo(800, 600); window.addEventListener('message', (event) => { diff --git a/src/webparts/sendToLaserficheLoginComponent/components/SendToLaserficheLoginComponent.tsx b/src/webparts/sendToLaserficheLoginComponent/components/SendToLaserficheLoginComponent.tsx index 3b1ad79..c77502c 100644 --- a/src/webparts/sendToLaserficheLoginComponent/components/SendToLaserficheLoginComponent.tsx +++ b/src/webparts/sendToLaserficheLoginComponent/components/SendToLaserficheLoginComponent.tsx @@ -8,6 +8,7 @@ import { } from '@laserfiche/types-lf-ui-components'; import { clientId, + LASERFICHE_SIGNIN_PAGE_NAME, LF_INDIGO_PINK_CSS_URL, LF_MS_OFFICE_LITE_CSS_URL, LF_UI_COMPONENTS_URL, @@ -19,7 +20,7 @@ import { NgElement, WithProperties } from '@angular/elements'; import { ISendToLaserficheLoginComponentProps } from './ISendToLaserficheLoginComponentProps'; import { ISPDocumentData } from '../../../Utils/Types'; import SaveToLaserficheCustomDialog from '../../../extensions/savetoLaserfiche/SaveToLaserficheDialog'; -import { getEntryWebAccessUrl, getRegion } from '../../../Utils/Funcs'; +import { getEntryWebAccessUrl, getRegion, getSPListURL } from '../../../Utils/Funcs'; import styles from './SendToLaserficheLoginComponent.module.scss'; import { MessageDialog } from '../../../extensions/savetoLaserfiche/CommonDialogs'; declare global { @@ -39,6 +40,7 @@ const YOU_MUST_BE_CLOUD_USER_TO_USE_WEB_PART = 'You must be a currently licensed Laserfiche Cloud user to use this web part.'; const FOR_MORE_INFO_VISIT = 'For more information visit'; +const needLaserficheSignInPage = `Missing ${LASERFICHE_SIGNIN_PAGE_NAME} SharePoint page. Please refer to the Adding App to SharePoint Site topic in the administration guide for configuration steps.`; export default function SendToLaserficheLoginComponent( props: ISendToLaserficheLoginComponentProps ): JSX.Element { @@ -136,16 +138,16 @@ export default function SendToLaserficheLoginComponent( }; const setUpLoginComponentAsync: () => Promise = async () => { - try { - SPComponentLoader.loadCss(LF_INDIGO_PINK_CSS_URL); - SPComponentLoader.loadCss(LF_MS_OFFICE_LITE_CSS_URL); - loginComponent.current.addEventListener( - 'logoutCompleted', - logoutCompletedInPopup - ); - await SPComponentLoader.loadScript(ZONE_JS_URL); - await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); + SPComponentLoader.loadCss(LF_INDIGO_PINK_CSS_URL); + SPComponentLoader.loadCss(LF_MS_OFFICE_LITE_CSS_URL); + loginComponent.current.addEventListener( + 'logoutCompleted', + logoutCompletedInPopup + ); + await SPComponentLoader.loadScript(ZONE_JS_URL); + await SPComponentLoader.loadScript(LF_UI_COMPONENTS_URL); + try { if (window.location.href.includes('autologin')) { document.body.style.display = 'none'; await handleLoginOrLogoutInPopupAsync(); @@ -305,10 +307,50 @@ export default function SendToLaserficheLoginComponent( Navigation.navigate(path, true); } - function clickLogin(): void { + async function pageConfigurationCheck(): Promise { + try { + const res = await fetch( + `${getSPListURL(props.context, 'Site Pages')}/items`, + { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + } + ); + const sitePages = await res.json(); + for (let o = 0; o < sitePages.value.length; o++) { + const pageName = sitePages.value[o].Title; + if (pageName === LASERFICHE_SIGNIN_PAGE_NAME) { + return true; + } + } + } catch (error) { + console.warn(`Unable to determine if a SharePoint Page with name ${LASERFICHE_SIGNIN_PAGE_NAME} exists.`) + return false; + } + return false; + } + + async function clickLogin(): Promise { const url = props.context.pageContext.web.absoluteUrl + '/SitePages/LaserficheSignIn.aspx?autologin'; + const hasSignIn = await pageConfigurationCheck(); + if (!hasSignIn) { + const mes = ( + { + setMessageErrorModal(undefined); + }} + /> + ); + setMessageErrorModal(mes); + return; + } const loginWindow = window.open(url, 'loginWindow', 'popup'); loginWindow.resizeTo(800, 600);