Skip to content

Commit

Permalink
Merge pull request #613 from GridProtectionAlliance/EE_Lock_Screen
Browse files Browse the repository at this point in the history
Ee lock screen
  • Loading branch information
gcsantos-gpa authored Feb 24, 2025
2 parents b71c130 + f02380a commit d3a7778
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ public class ApplicationNodeController : ModelController<ApplicationNode> { }
[RoutePrefix("api/OpenXDA/MeterDataQualitySummary")]
public class MeterDataQualitySummaryController : ModelController<MeterDataQualitySummary> { }

[RoutePrefix("api/OpenXDA/remoteXDAInstance")]
[RoutePrefix("api/OpenXDA/remoteXDAInstance"), HttpEditionFilter(Edition.Enterprise)]
public class RemoteXDAInstanceController : ModelController<RemoteXDAInstance>
{
#region [Properties]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
using GSF.Data.Model;
using GSF.Security.Model;
using GSF.Web.Model;
using openXDA.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
Expand All @@ -37,7 +38,7 @@
namespace SystemCenter.Controllers
{

[RoutePrefix("api/SystemCenter/AccessLog")]
[RoutePrefix("api/SystemCenter/AccessLog"), HttpEditionFilter(Edition.Enterprise)]
public class SystemCenterAccessLogController : ApiController {

private string Connection { get; } = "systemSettings";
Expand Down
2 changes: 2 additions & 0 deletions Source/Applications/SystemCenter/SystemCenter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@
<None Include="wwwroot\Images\XDA.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="wwwroot\Images\GiantLogo.png" />
<Content Include="wwwroot\Images\NodeTiles\OpenXDA.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand Down Expand Up @@ -551,6 +552,7 @@
<Content Include="wwwroot\Scripts\TSX\SystemCenter\CommonComponents\AdditionalFieldsProperties.tsx" />
<Content Include="wwwroot\Scripts\TSX\SystemCenter\ExternalDB\ByExternalTable.tsx" />
<Content Include="wwwroot\Scripts\TSX\SystemCenter\CommonComponents\GenericByPage.tsx" />
<Content Include="wwwroot\Scripts\TSX\SystemCenter\CommonComponents\EditionLockPage.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\CommonComponents\EditionTooltip.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\CommonComponents\ExternalDBUpdate.tsx" />
<TypeScriptCompile Include="wwwroot\Scripts\TSX\SystemCenter\ExternalDB\ExternalDBTable.tsx" />
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//******************************************************************************************************
// EditionLockPage.tsx - Gbtc
//
// Copyright © 2025, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 01/14/2025 - Gabriel Santos
// Generated original version of source code.
//
//******************************************************************************************************

import * as React from 'react';
import { useAppSelector, useAppDispatch } from '../hooks';
import { ConfigSlice } from '../Store/Store';
import { ServerErrorIcon, LoadingScreen } from '@gpa-gemstone/react-interactive';

interface IProps {
EditionRequirement?: 'Enterprise' | 'Base'
}

const EditionLockPage: React.FunctionComponent<IProps> = (props) => {
let dispatch = useAppDispatch();
const configStatus = useAppSelector(ConfigSlice.XDAConfigStatus);
const config = useAppSelector(ConfigSlice.XDAConfig);

React.useEffect(() => {
if (configStatus == 'unintiated' || configStatus == 'changed')
dispatch(ConfigSlice.FetchXDAConfig());
}, [configStatus]);

if (config.EditionStatus[props.EditionRequirement ?? 'Enterprise'] ?? false) return <>{props.children}</>;

// Note: Using loading screen this way is intentional, we don't want the error screen to begin rendering before we check the edition
return (
<div className={"container-fluid d-flex h-100 flex-column"}>
{ configStatus === 'loading' ? <LoadingScreen Show={true} /> :
<div className="col" style={{ height: "100%", width: "100%" }}>
<div className="row" style={{ width: "100%", height: "45%", minHeight: "200px", paddingBottom: "50px" }}>
<img src={`${homePath}Images/GiantLogo.png`} className="contain"
style={{ height: "100%", marginLeft: "auto", marginRight: "auto" }} />
</div>
<div className="row" style={{ justifyContent: "center", width: "100%", paddingBottom: "50px" }}>
<ServerErrorIcon Show={true} Label={`${props.EditionRequirement ?? 'Enterprise'} Edition is required to use this feature.`} Size={75} />
</div>
<div className="row" style={{ justifyContent: "center", width: "100%", fontSize: '2em' }}>
<p style={{ width: "50%", whiteSpace: "preserve-spaces", textAlign: "center" }}>
Click <a href="mailto:[email protected]">here</a> if you believe you are receiving this message in error or would like to inquire about openXDA {props.EditionRequirement ?? 'Enterprise'} Edition.
</p>
</div>
</div>
}
</div>
)
}

export default EditionLockPage;

Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ const EditionTooltip: React.FunctionComponent<IProps> = (props) => {

const [inSpecifiedEdition, setInSpecifiedEdition] = React.useState<boolean>(false);

const message: string = React.useMemo(() => {
switch (configStatus) {
case 'error': return "Unable to retrieve edition status.";
case 'idle': return `${props.FeatureName} is only available in ${props.EditionRequirement ?? 'Enterprise'} Edition.`;
default: return "Validating License..."
}
}, [configStatus]);

React.useEffect(() => {
const result = config.EditionStatus[props.EditionRequirement ?? 'Enterprise'] ?? false;
setInSpecifiedEdition(result);
Expand All @@ -55,14 +63,6 @@ const EditionTooltip: React.FunctionComponent<IProps> = (props) => {

if (inSpecifiedEdition) return null;

const message: string = React.useMemo(() => {
switch (configStatus) {
case 'error': return "Unable to retrieve edition status.";
case 'idle': return `${props.FeatureName} is only available in ${props.EditionRequirement ?? 'Enterprise'} Edition.`;
default: return "Retrieving edition status..."
}
}, [configStatus]);

return (
<ToolTip Show={props.Show} Position={'bottom'} Target={props.Target}>
{message}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { GenericController, Modal } from '@gpa-gemstone/react-interactive';
import { CrossMark } from '@gpa-gemstone/gpa-symbols';
import { RemoteXDAInstanceForm, BlankRemoteXDAInstance } from './RemoteXDAInstanceForm';
import GenericByPage from '../CommonComponents/GenericByPage';
import EditionLockPage from '../CommonComponents/EditionLockPage';
import { SystemCenter } from '../global';

declare var homePath: string;
Expand All @@ -51,58 +52,60 @@ const RemoteXDAInstanceMain: Application.Types.iByComponent = (props) => {
navigate(`${homePath}index.cshtml?name=RemoteXDAInstance&ID=${item.row.ID}`);
}

return <>
<GenericByPage<OpenXDA.Types.RemoteXDAInstance>
ControllerPath={controllerPath}
RefreshData={refreshCount}
DefaultSortKey='Name'
PagingID='RemoteXDAInstanceMain'
OnClick={(item) => { handleSelect(item); }}
Columns={fieldCols}
DefaultSearchAscending={true}
DefaultSearchKey='Name'
>
<li className="nav-item" hidden={props.Roles.indexOf('Administrator') < 0} style={{ width: '15%', paddingRight: 10 }}>
<fieldset className="border" style={{ padding: '10px', height: '100%' }}>
<legend className="w-auto" style={{ fontSize: 'large' }}>Actions:</legend>
<form>
<button className="btn btn-primary" onClick={(event) => {
if (props.Roles.indexOf('Administrator') > -1) {
event.preventDefault();
setShowNew(true);
}
}}>Add Remote Connection</button>
</form>
</fieldset>
</li>
<Modal
Show={showNew}
Title={'New Remote openXDA Instance Connection'}
ShowCancel={true}
CallBack={(conf) => {
if (conf)
RemoteXDAInstanceController.DBAction("POST", formInstance).done(() => {
refreshData(x => x + 1);
});
setShowNew(false);
}}
DisableConfirm={newInstErrors.length > 0}
ShowX={true}
ConfirmShowToolTip={newInstErrors.length > 0}
ConfirmToolTipContent={newInstErrors.map((t, i) =>
<p key={i}> {CrossMark} {t} </p>
)}>
<RemoteXDAInstanceForm
BaseInstance={BlankRemoteXDAInstance}
SetInstance={setFormInstance}
SetErrors={setNewInstErrors}
RenderPortalId={'userModal'}
/>
</Modal>
{ /* Portal endpoint for inner modal for new remote instance connection */ }
<div id='userModal' />
</GenericByPage>
</>
return (
<EditionLockPage>
<GenericByPage<OpenXDA.Types.RemoteXDAInstance>
ControllerPath={controllerPath}
RefreshData={refreshCount}
DefaultSortKey='Name'
PagingID='RemoteXDAInstanceMain'
OnClick={(item) => { handleSelect(item); }}
Columns={fieldCols}
DefaultSearchAscending={true}
DefaultSearchKey='Name'
>
<li className="nav-item" hidden={props.Roles.indexOf('Administrator') < 0} style={{ width: '15%', paddingRight: 10 }}>
<fieldset className="border" style={{ padding: '10px', height: '100%' }}>
<legend className="w-auto" style={{ fontSize: 'large' }}>Actions:</legend>
<form>
<button className="btn btn-primary" onClick={(event) => {
if (props.Roles.indexOf('Administrator') > -1) {
event.preventDefault();
setShowNew(true);
}
}}>Add Remote Connection</button>
</form>
</fieldset>
</li>
<Modal
Show={showNew}
Title={'New Remote openXDA Instance Connection'}
ShowCancel={true}
CallBack={(conf) => {
if (conf)
RemoteXDAInstanceController.DBAction("POST", formInstance).done(() => {
refreshData(x => x + 1);
});
setShowNew(false);
}}
DisableConfirm={newInstErrors.length > 0}
ShowX={true}
ConfirmShowToolTip={newInstErrors.length > 0}
ConfirmToolTipContent={newInstErrors.map((t, i) =>
<p key={i}> {CrossMark} {t} </p>
)}>
<RemoteXDAInstanceForm
BaseInstance={BlankRemoteXDAInstance}
SetInstance={setFormInstance}
SetErrors={setNewInstErrors}
RenderPortalId={'userModal'}
/>
</Modal>
{ /* Portal endpoint for inner modal for new remote instance connection */}
<div id='userModal' />
</GenericByPage>
</EditionLockPage>
);
}

export default RemoteXDAInstanceMain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import * as _ from 'lodash';
import moment from 'moment';
import { useAppDispatch, useAppSelector } from '../hooks';
import { ApplicationNodeSlice } from '../Store/Store';
import EditionLockPage from '../CommonComponents/EditionLockPage';

interface Aggregate {
Date: string,
Expand Down Expand Up @@ -171,7 +172,7 @@ const UserStatistics: Application.Types.iByComponent = (props) => {
}

return (
<div className="container-fluid d-flex h-100 flex-column">
<EditionLockPage>
<LoadingScreen Show={tableStatus === 'loading' || plotStatus === 'loading' || applicationNodeStatus === 'loading'} />
<div className="row">
<div className="col">
Expand Down Expand Up @@ -269,9 +270,8 @@ const UserStatistics: Application.Types.iByComponent = (props) => {
</Column>
</Table>
</div>
</div>

</div>
</div>
</EditionLockPage>
)

}
Expand Down

0 comments on commit d3a7778

Please sign in to comment.