Merge branch 'release' of https://github.com/appsmithorg/appsmith into release

This commit is contained in:
Automated Github Action 2020-08-14 10:06:03 +00:00
commit fa72df3c36
22 changed files with 466 additions and 217 deletions

View File

@ -33,6 +33,9 @@ server {
sub_filter __APPSMITH_TNC_PP__ '${APPSMITH_TNC_PP}';
sub_filter __APPSMITH_SENTRY_RELEASE__ '${APPSMITH_SENTRY_RELEASE}';
sub_filter __APPSMITH_SENTRY_ENVIRONMENT__ '${APPSMITH_SENTRY_ENVIRONMENT}';
sub_filter __APPSMITH_VERSION_ID__ '${APPSMITH_VERSION_ID}';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '${APPSMITH_VERSION_RELEASE_DATE}';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '${APPSMITH_INTERCOM_APP_ID}';
}
location /f {
@ -97,6 +100,9 @@ server {
sub_filter __APPSMITH_TNC_PP__ '${APPSMITH_TNC_PP}';
sub_filter __APPSMITH_SENTRY_RELEASE__ '${APPSMITH_SENTRY_RELEASE}';
sub_filter __APPSMITH_SENTRY_ENVIRONMENT__ '${APPSMITH_SENTRY_ENVIRONMENT}';
sub_filter __APPSMITH_VERSION_ID__ '${APPSMITH_VERSION_ID}';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '${APPSMITH_VERSION_RELEASE_DATE}';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '${APPSMITH_INTERCOM_APP_ID}';
}
location /f {

View File

@ -33,6 +33,9 @@ server {
sub_filter __APPSMITH_TNC_PP__ '${APPSMITH_TNC_PP}';
sub_filter __APPSMITH_SENTRY_RELEASE__ '${APPSMITH_SENTRY_RELEASE}';
sub_filter __APPSMITH_SENTRY_ENVIRONMENT__ '${APPSMITH_SENTRY_ENVIRONMENT}';
sub_filter __APPSMITH_VERSION_ID__ '${APPSMITH_VERSION_ID}';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '${APPSMITH_VERSION_RELEASE_DATE}';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '${APPSMITH_INTERCOM_APP_ID}';
}
location /f {
@ -98,6 +101,9 @@ server {
sub_filter __APPSMITH_TNC_PP__ '${APPSMITH_TNC_PP}';
sub_filter __APPSMITH_SENTRY_RELEASE__ '${APPSMITH_SENTRY_RELEASE}';
sub_filter __APPSMITH_SENTRY_ENVIRONMENT__ '${APPSMITH_SENTRY_ENVIRONMENT}';
sub_filter __APPSMITH_VERSION_ID__ '${APPSMITH_VERSION_ID}';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '${APPSMITH_VERSION_RELEASE_DATE}';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '${APPSMITH_INTERCOM_APP_ID}';
}

View File

@ -76,9 +76,20 @@
logLevel: CONFIG_LOG_LEVEL_INDEX > -1 ? LOG_LEVELS[CONFIG_LOG_LEVEL_INDEX] : LOG_LEVELS[1],
google: parseConfig("__APPSMITH_GOOGLE_MAPS_API_KEY__"),
cloudHosting: parseConfig("__APPSMITH_CLOUD_HOSTING__").length > 0,
enableTNCPP: parseConfig("__APPSMITH_TNC_PP__").length > 0
enableTNCPP: parseConfig("__APPSMITH_TNC_PP__").length > 0,
appVersion: {
id: parseConfig("__APPSMITH_VERSION_ID__"),
releaseDate: parseConfig("__APPSMITH_VERSION_RELEASE_DATE__")
},
intercomAppID: parseConfig("__APPSMITH_INTERCOM_APP_ID__"),
};
</script>
<script>
if(window.APPSMITH_FEATURE_CONFIGS.cloudHosting && window.APPSMITH_FEATURE_CONFIGS.intercomAppID) {
const WIDGET_URL = `https://widget.intercom.io/widget/${window.APPSMITH_FEATURE_CONFIGS.intercomAppID}`;
(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',w.intercomSettings);}else{var d=document;var i=function(){i.c(arguments);};i.q=[];i.c=function(args){i.q.push(args);};w.Intercom=i;var l=function(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src=WIDGET_URL;var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);};if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})();
}
</script>
<script rel="prefetch" type="text/javascript" src="/shims/realms-shim.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.4.0/tinymce.min.js" referrerpolicy="origin"></script>
</body>

View File

@ -0,0 +1 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 245 240"><style>.st0{fill:#FFFFFF;}</style><path class="st0" d="M104.4 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1.1-6.1-4.5-11.1-10.2-11.1zM140.9 103.9c-5.7 0-10.2 5-10.2 11.1s4.6 11.1 10.2 11.1c5.7 0 10.2-5 10.2-11.1s-4.5-11.1-10.2-11.1z"/><path class="st0" d="M189.5 20h-134C44.2 20 35 29.2 35 40.6v135.2c0 11.4 9.2 20.6 20.5 20.6h113.4l-5.3-18.5 12.8 11.9 12.1 11.2 21.5 19V40.6c0-11.4-9.2-20.6-20.5-20.6zm-38.6 130.6s-3.6-4.3-6.6-8.1c13.1-3.7 18.1-11.9 18.1-11.9-4.1 2.7-8 4.6-11.5 5.9-5 2.1-9.8 3.5-14.5 4.3-9.6 1.8-18.4 1.3-25.9-.1-5.7-1.1-10.6-2.7-14.7-4.3-2.3-.9-4.8-2-7.3-3.4-.3-.2-.6-.3-.9-.5-.2-.1-.3-.2-.4-.3-1.8-1-2.8-1.7-2.8-1.7s4.8 8 17.5 11.8c-3 3.8-6.7 8.3-6.7 8.3-22.1-.7-30.5-15.2-30.5-15.2 0-32.2 14.4-58.3 14.4-58.3 14.4-10.8 28.1-10.5 28.1-10.5l1 1.2c-18 5.2-26.3 13.1-26.3 13.1s2.2-1.2 5.9-2.9c10.7-4.7 19.2-6 22.7-6.3.6-.1 1.1-.2 1.7-.2 6.1-.8 13-1 20.2-.2 9.5 1.1 19.7 3.9 30.1 9.6 0 0-7.9-7.5-24.9-12.7l1.4-1.6s13.7-.3 28.1 10.5c0 0 14.4 26.1 14.4 58.3 0 0-8.5 14.5-30.6 15.2z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,9 +1,7 @@
<svg width="13" height="16" viewBox="0 0 13 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.7">
<path d="M8 1H2.4C2.0287 1 1.6726 1.1475 1.41005 1.41005C1.1475 1.6726 1 2.0287 1 2.4V13.6C1 13.9713 1.1475 14.3274 1.41005 14.5899C1.6726 14.8525 2.0287 15 2.4 15H10.8C11.1713 15 11.5274 14.8525 11.7899 14.5899C12.0525 14.3274 12.2 13.9713 12.2 13.6V5.2L8 1Z" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 1V5.2H12.2" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.40005 8.70117H3.80005" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9.40005 11.5H3.80005" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.20005 5.89941H4.50005H3.80005" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 841 B

After

Width:  |  Height:  |  Size: 818 B

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="438.549px" height="438.549px" viewBox="0 0 438.549 438.549"
xml:space="preserve">
<path d="M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365
c-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63
c0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996
c2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136
c-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559
c-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559
c-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997
c-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851
c5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136
c6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41
c-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126
c-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817
c-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994
c5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849
c7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24
c15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979
c-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146
c9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995
c44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906
C438.536,184.851,428.728,148.168,409.132,114.573z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { SyntheticEvent } from "react";
import algoliasearch from "algoliasearch/lite";
import {
InstantSearch,
@ -8,25 +8,29 @@ import {
Configure,
PoweredBy,
} from "react-instantsearch-dom";
import "instantsearch.css/themes/algolia.css";
import PropTypes from "prop-types";
import { Icon } from "@blueprintjs/core";
import { useDispatch, useSelector } from "react-redux";
import {
setHelpModalVisibility,
setHelpDefaultRefinement,
} from "actions/helpActions";
import { connect } from "react-redux";
import styled from "styled-components";
import { HelpIcons } from "icons/HelpIcons";
import { HelpBaseURL } from "constants/HelpConstants";
import { getDefaultRefinement } from "selectors/helpSelectors";
import { getAppsmithConfigs } from "configs";
const { algolia } = getAppsmithConfigs();
import { AppState } from "reducers";
import {
setHelpDefaultRefinement,
setHelpModalVisibility,
} from "actions/helpActions";
import { Icon } from "@blueprintjs/core";
import moment from "moment";
const { algolia, appVersion, cloudHosting } = getAppsmithConfigs();
const searchClient = algoliasearch(algolia.apiId, algolia.apiKey);
const OenLinkIcon = HelpIcons.OPEN_LINK;
const DocumentIcon = HelpIcons.DOCUMENT;
const GithubIcon = HelpIcons.GITHUB;
const ChatIcon = HelpIcons.CHAT;
const DiscordIcon = HelpIcons.DISCORD;
const StyledOpenLinkIcon = styled(OenLinkIcon)`
position: absolute;
@ -47,43 +51,78 @@ const StyledDocumentIcon = styled(DocumentIcon)`
margin-top: 1px;
position: absolute;
`;
function Hit(props: any) {
const StyledGithubIcon = styled(GithubIcon)`
margin-left: 14px;
margin-right: 10.8px;
margin-top: 1px;
position: absolute;
`;
const StyledChatIcon = styled(ChatIcon)`
&&& {
margin-left: 14px;
margin-right: 10.8px;
margin-top: 1px;
position: absolute;
}
`;
const StyledDiscordIcon = styled(DiscordIcon)`
&&& {
margin-left: 12px;
margin-right: 10.8px;
margin-top: 1px;
position: absolute;
}
`;
const Hit = (props: { hit: { path: string } }) => {
return (
<div
className="t--docHit"
onClick={() => {
window.open(
(props.hit.path as string).replace("master", HelpBaseURL),
"_blank",
);
window.open(props.hit.path.replace("master", HelpBaseURL), "_blank");
}}
>
<div className="hit-name t--docHitTitle">
<StyledDocumentIcon
width={11.2}
height={14}
color="#181F24"
></StyledDocumentIcon>
<StyledDocumentIcon width={11.2} height={14} color="#181F24" />
<Highlight attribute="title" hit={props.hit} />
<StyledOpenLinkIcon
className="t--docOpenLink open-link"
color={"#181F24"}
></StyledOpenLinkIcon>
/>
</div>
</div>
);
}
Hit.propTypes = {
hit: PropTypes.object.isRequired,
};
const Header = styled.div`
position: absolute;
width: 100%;
border-top-right-radius: 3px;
border-top-left-radius: 3px;
`;
const DefaultHelpMenuItem = (props: {
item: { label: string; link?: string; id?: string; icon: React.ReactNode };
onSelect: Function;
}) => {
return (
<li className="ais-Hits-item">
<div
className="t--docHit"
id={props.item.id}
onClick={() => {
if (props.item.link) window.open(props.item.link, "_blank");
props.onSelect();
}}
>
<div className="hit-name t--docHitTitle">
{props.item.icon}
<span className="ais-Highlight">{props.item.label}</span>
<StyledOpenLinkIcon
className="t--docOpenLink open-link"
color={"#181F24"}
/>
</div>
</div>
</li>
);
};
const SearchContainer = styled.div`
height: 100%;
@ -93,7 +132,7 @@ const SearchContainer = styled.div`
position: relative;
height: 30px;
margin: 14px;
margin-top: 0;
margin-top: 10px;
}
.ais-SearchBox-form {
@ -107,8 +146,6 @@ const SearchContainer = styled.div`
}
.ais-Hits {
margin-top: 86px;
height: calc(100% - 86px);
overflow: auto;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
@ -205,10 +242,19 @@ const SearchContainer = styled.div`
}
`;
const Header = styled.div`
padding: 10px 0;
position: absolute;
width: 100%;
border-top-right-radius: 3px;
border-top-left-radius: 3px;
height: 50px;
`;
const StyledPoweredBy = styled(PoweredBy)`
position: absolute;
right: 21px;
bottom: 23px;
top: 30px;
z-index: 1;
.ais-PoweredBy-text {
@ -216,73 +262,155 @@ const StyledPoweredBy = styled(PoweredBy)`
}
`;
export default function DocumentationSearch(props: { hitsPerPage: number }) {
const dispatch = useDispatch();
const defaultRefinement = useSelector(getDefaultRefinement);
if (!algolia.enabled) return null;
return (
<SearchContainer className="ais-InstantSearch t--docSearchModal">
<Icon
className="t--docsMinimize"
style={{
position: "absolute",
top: 4,
right: 6,
padding: 8,
cursor: "pointer",
zIndex: 1,
}}
icon="minus"
color="white"
iconSize={14}
onClick={() => {
dispatch(setHelpModalVisibility(false));
dispatch(setHelpDefaultRefinement(""));
}}
></Icon>
<div
style={{
height: "100%",
overflow: "auto",
}}
>
const HelpContainer = styled.div`
height: 100%;
position: relative;
display: flex;
flex-direction: column;
`;
const HelpFooter = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding: 5px 10px;
height: 30px;
color: rgba(255, 255, 255, 0.7);
`;
const HelpBody = styled.div`
padding-top: 60px;
flex: 5;
`;
type Props = { hitsPerPage: number; defaultRefinement: string; dispatch: any };
type State = { showResults: boolean };
type HelpItem = {
label: string;
link?: string;
id?: string;
icon: React.ReactNode;
};
const HELP_MENU_ITEMS: HelpItem[] = [
{
icon: <StyledDocumentIcon width={11.2} height={14} color="#181F24" />,
label: "Documentation",
link: "https://docs.appsmith.com/",
},
{
icon: <StyledGithubIcon width={11.2} height={14} color="#fff" />,
label: "Report a bug",
link: "https://github.com/appsmithorg/appsmith/issues/new/choose",
},
{
icon: <StyledChatIcon width={11.2} height={14} color="#fff" />,
label: "Chat with us",
link: "https://github.com/appsmithorg/appsmith/discussions",
},
{
icon: <StyledDiscordIcon width={16} height={16} />,
label: "Join our Discord",
link: "https://discord.gg/rBTTVJp",
},
];
if (cloudHosting) {
HELP_MENU_ITEMS[2] = {
icon: <StyledChatIcon width={11.2} height={14} color="#fff" />,
label: "Chat with us",
id: "intercom-trigger",
};
}
class DocumentationSearch extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
showResults: props.defaultRefinement.length > 0,
};
}
onSearchValueChange = (event: SyntheticEvent<HTMLInputElement, Event>) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
const value = event.target.value;
if (value === "" && this.state.showResults) {
this.setState({
showResults: false,
});
} else if (value !== "" && !this.state.showResults) {
this.setState({
showResults: true,
});
}
};
handleClose = () => {
this.props.dispatch(setHelpModalVisibility(false));
this.props.dispatch(setHelpDefaultRefinement(""));
};
render() {
if (!algolia.enabled) return null;
return (
<SearchContainer className="ais-InstantSearch t--docSearchModal">
<Icon
className="t--docsMinimize"
style={{
position: "absolute",
top: 6,
right: 10,
cursor: "pointer",
zIndex: 1,
}}
icon="minus"
color="white"
iconSize={14}
onClick={this.handleClose}
/>
<InstantSearch
indexName={algolia.indexName}
searchClient={searchClient}
>
<Configure hitsPerPage={props.hitsPerPage} />
<Header>
<h3
style={{
padding: "0 69px",
marginTop: "14px",
marginBottom: "14px",
lineHeight: "14px",
}}
>
<span
style={{
textAlign: "center",
color: "white",
position: "relative",
fontWeight: 500,
fontSize: "14px",
lineHeight: "14px",
letterSpacing: "0.2px",
margin: "0 auto",
width: "121px",
}}
>
Documentation
</span>
</h3>
<StyledPoweredBy></StyledPoweredBy>
<SearchBox defaultRefinement={defaultRefinement}></SearchBox>
</Header>
<Hits hitComponent={Hit as any} />
<Configure hitsPerPage={this.props.hitsPerPage} />
<HelpContainer>
<Header>
<StyledPoweredBy />
<SearchBox
onChange={this.onSearchValueChange}
defaultRefinement={this.props.defaultRefinement}
/>
</Header>
<HelpBody>
{this.state.showResults ? (
<Hits hitComponent={Hit as any} />
) : (
<ul className="ais-Hits-list">
{HELP_MENU_ITEMS.map(item => (
<DefaultHelpMenuItem
key={item.label}
item={item}
onSelect={this.handleClose}
/>
))}
</ul>
)}
</HelpBody>
{appVersion.id && (
<HelpFooter>
<span>Appsmith {appVersion.id}</span>
<span>Released {moment(appVersion.releaseDate).fromNow()}</span>
</HelpFooter>
)}
</HelpContainer>
</InstantSearch>
</div>
</SearchContainer>
);
</SearchContainer>
);
}
}
const mapStateToProps = (state: AppState) => ({
defaultRefinement: getDefaultRefinement(state),
});
export default connect(mapStateToProps)(DocumentationSearch);

View File

@ -1,12 +1,6 @@
import React, { useContext } from "react";
import React, { SyntheticEvent } from "react";
import DocumentationSearch from "components/designSystems/appsmith/help/DocumentationSearch";
import { useSelector } from "store";
import { useDispatch } from "react-redux";
import {
getHelpModalOpen,
getHelpModalDimensions,
} from "selectors/helpSelectors";
import { getHelpModalOpen } from "selectors/helpSelectors";
import {
setHelpDefaultRefinement,
setHelpModalVisibility,
@ -14,17 +8,20 @@ import {
import styled from "styled-components";
import { theme } from "constants/DefaultTheme";
import ModalComponent from "components/designSystems/blueprint/ModalComponent";
import { LayersContext } from "constants/Layers";
import { HelpIcons } from "icons/HelpIcons";
import { getAppsmithConfigs } from "configs";
import { LayersContext } from "constants/Layers";
import { connect } from "react-redux";
import { AppState } from "reducers";
const { algolia } = getAppsmithConfigs();
const HelpButton = styled.div<{
const HelpButton = styled.button<{
highlight: boolean;
layer: number;
}>`
&&&&& {
position: absolute;
bottom: 46px;
bottom: 27px;
right: 27px;
z-index: ${props => props.layer};
background: ${props =>
@ -47,48 +44,66 @@ const HelpButton = styled.div<{
}
`;
const MODAL_WIDTH = 240;
const MODAL_HEIGHT = 210;
const MODAL_BOTTOM_DISTANCE = 45;
const MODAL_RIGHT_DISTANCE = 30;
const HelpIcon = HelpIcons.HELP_ICON;
export function HelpModal() {
const isHelpModalOpen = useSelector(getHelpModalOpen);
const helpDimensions = useSelector(getHelpModalDimensions);
const helpModalOpen = useSelector(getHelpModalOpen);
const dispatch = useDispatch();
const layers = useContext(LayersContext);
type Props = {
isHelpModalOpen: boolean;
dispatch: any;
};
return (
<>
<ModalComponent
canOutsideClickClose
canEscapeKeyClose
scrollContents
width={helpDimensions.width}
height={helpDimensions.height}
top={window.innerHeight - 105 - helpDimensions.height}
left={window.innerWidth - 31 - helpDimensions.width}
data-cy={"help-modal"}
hasBackDrop={false}
onClose={() => {
dispatch(setHelpModalVisibility(false));
dispatch(setHelpDefaultRefinement(""));
}}
isOpen={isHelpModalOpen}
zIndex={layers.help}
>
<DocumentationSearch hitsPerPage={5} />
</ModalComponent>
{algolia.enabled && (
<HelpButton
className="t--helpGlobalButton"
highlight={!helpModalOpen}
layer={layers.help}
onClick={() => {
dispatch(setHelpModalVisibility(!helpModalOpen));
class HelpModal extends React.Component<Props> {
static contextType = LayersContext;
render() {
const { dispatch, isHelpModalOpen } = this.props;
const layers = this.context;
return (
<>
<ModalComponent
canOutsideClickClose
canEscapeKeyClose
scrollContents
height={MODAL_HEIGHT}
width={MODAL_WIDTH}
top={window.innerHeight - MODAL_BOTTOM_DISTANCE - MODAL_HEIGHT}
left={window.innerWidth - MODAL_RIGHT_DISTANCE - MODAL_WIDTH}
data-cy={"help-modal"}
hasBackDrop={false}
onClose={(event: SyntheticEvent<HTMLElement>) => {
dispatch(setHelpModalVisibility(false));
dispatch(setHelpDefaultRefinement(""));
event.stopPropagation();
event.preventDefault();
}}
isOpen={isHelpModalOpen}
zIndex={layers.help}
>
<HelpIcon />
</HelpButton>
)}
</>
);
<DocumentationSearch hitsPerPage={4} />
</ModalComponent>
{algolia.enabled && (
<HelpButton
className="t--helpGlobalButton"
highlight={!isHelpModalOpen}
layer={layers.help}
onClick={() => {
dispatch(setHelpModalVisibility(!isHelpModalOpen));
}}
>
<HelpIcon />
</HelpButton>
)}
</>
);
}
}
const mapStateToProps = (state: AppState) => ({
isHelpModalOpen: getHelpModalOpen(state),
});
export default connect(mapStateToProps)(HelpModal);

View File

@ -24,10 +24,16 @@ type INJECTED_CONFIGS = {
indexName: string;
};
logLevel: "debug" | "error";
appVersion: {
id: string;
releaseDate: string;
};
intercomAppID: string;
};
declare global {
interface Window {
APPSMITH_FEATURE_CONFIGS: INJECTED_CONFIGS;
Intercom: any;
}
}
@ -79,6 +85,11 @@ const getConfigsFromEnvVars = (): INJECTED_CONFIGS => {
cloudHosting: process.env.REACT_APP_CLOUD_HOSTING
? process.env.REACT_APP_CLOUD_HOSTING.length > 0
: false,
appVersion: {
id: process.env.REACT_APP_VERSION_ID || "",
releaseDate: process.env.REACT_APP_VERSION_RELEASE_DATE || "",
},
intercomAppID: process.env.REACT_APP_INTERCOM_APP_ID || "",
};
};
@ -192,5 +203,8 @@ export const getAppsmithConfigs = (): AppsmithUIConfigs => {
),
logLevel: ENV_CONFIG.logLevel || APPSMITH_FEATURE_CONFIGS.logLevel,
enableTNCPP: ENV_CONFIG.enableTNCPP || APPSMITH_FEATURE_CONFIGS.enableTNCPP,
appVersion: ENV_CONFIG.appVersion || APPSMITH_FEATURE_CONFIGS.appVersion,
intercomAppID:
ENV_CONFIG.intercomAppID || APPSMITH_FEATURE_CONFIGS.intercomAppID,
};
};

View File

@ -61,4 +61,9 @@ export type AppsmithUIConfigs = {
featureFlag?: FeatureFlagConfig;
logLevel: LogLevelDesc;
appVersion: {
id: string;
releaseDate: string;
};
intercomAppID: string;
};

View File

@ -3,6 +3,9 @@ import { IconProps, IconWrapper } from "constants/IconConstants";
import { ReactComponent as OpenLinkIcon } from "assets/icons/help/openlink.svg";
import { ReactComponent as DocumentIcon } from "assets/icons/help/document.svg";
import { ReactComponent as HelpIcon } from "assets/icons/help/help.svg";
import { ReactComponent as GithubIcon } from "assets/icons/help/github-icon.svg";
import { ReactComponent as DiscordIcon } from "assets/icons/help/discord.svg";
import { Icon } from "@blueprintjs/core";
/* eslint-disable react/display-name */
@ -24,6 +27,21 @@ export const HelpIcons: {
<HelpIcon />
</IconWrapper>
),
GITHUB: (props: IconProps) => (
<IconWrapper {...props}>
<GithubIcon />
</IconWrapper>
),
CHAT: (props: IconProps) => (
<IconWrapper {...props}>
<Icon icon={"chat"} />
</IconWrapper>
),
DISCORD: (props: IconProps) => (
<IconWrapper {...props}>
<DiscordIcon />
</IconWrapper>
),
};
export type HelpIconName = keyof typeof HelpIcons;

View File

@ -12,7 +12,7 @@ import AppInviteUsersForm from "pages/organization/AppInviteUsersForm";
import Button from "components/editorComponents/Button";
import StyledHeader from "components/designSystems/appsmith/StyledHeader";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { HelpModal } from "components/designSystems/appsmith/help/HelpModal";
import HelpModal from "components/designSystems/appsmith/help/HelpModal";
import { FormDialogComponent } from "components/editorComponents/form/FormDialogComponent";
import { Colors } from "constants/Colors";
import AppsmithLogo from "assets/images/appsmith_logo_white.png";
@ -257,7 +257,6 @@ export const EditorHeader = (props: EditorHeaderProps) => {
/>
</DeploySection>
</HeaderSection>
{}
<HelpModal />
</HeaderWrapper>
);

View File

@ -36,6 +36,11 @@ import {
} from "constants/Explorer";
import history from "utils/history";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import { getAppsmithConfigs } from "configs";
import { getCurrentUser } from "selectors/usersSelectors";
import { User } from "constants/userConstants";
const { cloudHosting, intercomAppID } = getAppsmithConfigs();
type EditorProps = {
currentApplicationId?: string;
@ -45,6 +50,7 @@ type EditorProps = {
isEditorLoading: boolean;
isEditorInitialized: boolean;
errorPublishing: boolean;
user?: User;
};
type Props = EditorProps & RouteComponentProps<BuilderRouteParams>;
@ -84,6 +90,7 @@ class Editor extends Component<Props> {
};
componentDidMount() {
const { user } = this.props;
editorInitializer().then(() => {
this.setState({ registered: true });
});
@ -91,8 +98,19 @@ class Editor extends Component<Props> {
if (applicationId && pageId) {
this.props.initEditor(applicationId, pageId);
}
if (cloudHosting) {
window.Intercom("boot", {
// eslint-disable-next-line @typescript-eslint/camelcase
app_id: intercomAppID,
// eslint-disable-next-line @typescript-eslint/camelcase
custom_launcher_selector: "#intercom-trigger",
name: user?.username,
email: user?.email,
});
}
}
componentDidUpdate(previously: Props) {
if (cloudHosting) window.Intercom("update");
if (
previously.isPublishing &&
!(this.props.isPublishing || this.props.errorPublishing)
@ -184,6 +202,7 @@ const mapStateToProps = (state: AppState) => ({
isPublishing: getIsPublishingApplication(state),
isEditorLoading: getIsEditorLoading(state),
isEditorInitialized: getIsEditorInitialized(state),
user: getCurrentUser(state),
});
const mapDispatchToProps = (dispatch: any) => {

View File

@ -4,8 +4,6 @@ import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
const initialState: HelpReduxState = {
url: "",
modalOpen: false,
height: 243,
width: 269,
defaultRefinement: "",
};
@ -27,8 +25,6 @@ const helpReducer = createReducer(initialState, {
export interface HelpReduxState {
url: string;
modalOpen: boolean;
height: number;
width: number;
defaultRefinement: string;
}

View File

@ -1,18 +1,8 @@
import { AppState } from "reducers";
export const getHelpUrl = (state: AppState): string => state.ui.help.url;
export const getHelpModalOpen = (state: AppState): boolean =>
state.ui.help.modalOpen;
export const getHelpModalDimensions = (
state: AppState,
): { height: number; width: number } => {
return {
height: state.ui.help.height,
width: state.ui.help.width,
};
};
export const getDefaultRefinement = (state: AppState): string => {
return state.ui.help.defaultRefinement || "";
};

View File

@ -19,6 +19,8 @@ public interface OrganizationService extends CrudService<Organization, String> {
Mono<String> getNextUniqueSlug(String initialSlug);
Mono<Organization> createPersonal(Organization organization, User user);
Mono<Organization> create(Organization organization, User user);
Mono<Organization> findById(String id, AclPermission permission);

View File

@ -46,9 +46,7 @@ import static java.util.stream.Collectors.toMap;
@Service
public class OrganizationServiceImpl extends BaseService<OrganizationRepository, Organization, String> implements OrganizationService {
private final OrganizationRepository repository;
private final SettingService settingService;
private final GroupService groupService;
private final PluginRepository pluginRepository;
private final SessionUserService sessionUserService;
private final UserOrganizationService userOrganizationService;
@ -63,16 +61,13 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
OrganizationRepository repository,
SettingService settingService,
AnalyticsService analyticsService,
GroupService groupService,
PluginRepository pluginRepository,
SessionUserService sessionUserService,
UserOrganizationService userOrganizationService,
UserRepository userRepository,
RoleGraph roleGraph) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.repository = repository;
this.settingService = settingService;
this.groupService = groupService;
this.pluginRepository = pluginRepository;
this.sessionUserService = sessionUserService;
this.userOrganizationService = userOrganizationService;
@ -104,6 +99,20 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
.map(max -> initialSlug + (max == 0 ? "" : (max + 1)));
}
/**
* Creates the given organization as a personal organization for the given user. That is, the organization's name
* is changed to "[username]'s Personal Organization" and then created. The current value of the organization name
* is discarded.
* @param organization Organization object to be created.
* @param user User to whom this organization will belong to, as a personal organization.
* @return Publishes the saved organization.
*/
@Override
public Mono<Organization> createPersonal(final Organization organization, User user) {
organization.setName(user.computeFirstName() + "'s Personal Organization");
return create(organization, user);
}
/**
* This function does the following:
* 1. Creates the organization for the user
@ -112,10 +121,11 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
* 4. Adds the user to the newly created organization
* 5. Assigns the default groups to the user creating the organization
*
* @param organization
* @param user
* @return
* @param organization Organization object to be created.
* @param user User to whom this organization will belong to.
* @return Publishes the saved organization.
*/
@Override
public Mono<Organization> create(Organization organization, User user) {
if (organization == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORGANIZATION));
@ -123,9 +133,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
// Does the user have permissions to create an organization?
boolean isManageOrgPolicyPresent = user.getPolicies().stream()
.filter(policy -> policy.getPermission().equals(USER_MANAGE_ORGANIZATIONS.getValue()))
.findFirst()
.isPresent();
.anyMatch(policy -> policy.getPermission().equals(USER_MANAGE_ORGANIZATIONS.getValue()));
if (!isManageOrgPolicyPresent) {
return Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS));
@ -254,7 +262,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
Mono<Organization> organizationMono = repository.findById(orgId, ORGANIZATION_INVITE_USERS);
Mono<String> usernameMono = sessionUserService
.getCurrentUser()
.map(user -> user.getUsername());
.map(User::getUsername);
return organizationMono
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.ORGANIZATION, orgId)))
@ -280,7 +288,7 @@ public class OrganizationServiceImpl extends BaseService<OrganizationRepository,
Map<String, String> appsmithRolesMap = appsmithRoles
.stream()
.collect(toMap(role -> role.getName(), AppsmithRole::getDescription));
.collect(toMap(AppsmithRole::getName, AppsmithRole::getDescription));
return Mono.just(appsmithRolesMap);
});

View File

@ -66,6 +66,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
private final OrganizationRepository organizationRepository;
private final UserOrganizationService userOrganizationService;
private final RoleGraph roleGraph;
private final ConfigService configService;
private static final String WELCOME_USER_EMAIL_TEMPLATE = "email/welcomeUserTemplate.html";
private static final String FORGOT_PASSWORD_EMAIL_TEMPLATE = "email/forgotPasswordTemplate.html";
@ -92,7 +93,8 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
PolicyUtils policyUtils,
OrganizationRepository organizationRepository,
UserOrganizationService userOrganizationService,
RoleGraph roleGraph) {
RoleGraph roleGraph,
ConfigService configService) {
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
this.organizationService = organizationService;
this.analyticsService = analyticsService;
@ -105,6 +107,7 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
this.organizationRepository = organizationRepository;
this.userOrganizationService = userOrganizationService;
this.roleGraph = roleGraph;
this.configService = configService;
}
@Override
@ -429,30 +432,32 @@ public class UserServiceImpl extends BaseService<UserRepository, User, String> i
if (user.getPassword() == null || user.getPassword().isBlank()) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_CREDENTIALS));
}
user.setPassword(this.passwordEncoder.encode(user.getPassword()));
user.setPassword(passwordEncoder.encode(user.getPassword()));
}
Organization personalOrg = new Organization();
if (user.getName() == null) {
if (!StringUtils.hasText(user.getName())) {
user.setName(user.getEmail());
}
String personalOrganizationName = user.computeFirstName() + "'s Personal Organization";
personalOrg.setName(personalOrganizationName);
// Set the permissions for the user
user.getPolicies().addAll(crudUserPolicy(user));
// Save the new user
Mono<User> savedUserMono = Mono.just(user)
return Mono.just(user)
.flatMap(this::validateObject)
.flatMap(repository::save);
.flatMap(repository::save)
.zipWith(configService.getTemplateOrganizationId().defaultIfEmpty(""))
.flatMap(tuple -> {
final String templateOrganizationId = tuple.getT2();
return savedUserMono
.flatMap(savedUser -> {
// Creating the personal workspace and assigning the default groups to the new user
log.debug("Going to create organization: {} for user: {}", personalOrg, savedUser.getEmail());
return organizationService.create(personalOrg, savedUser);
if (!StringUtils.hasText(templateOrganizationId)) {
// Since template organization is not configured, we create an empty personal organization.
final User savedUser = tuple.getT1();
log.debug("Creating blank personal organization for user '{}'.", savedUser.getEmail());
return organizationService.createPersonal(new Organization(), savedUser);
}
return Mono.empty();
})
.then(repository.findByEmail(user.getUsername()))
.flatMap(analyticsService::trackNewUser);

View File

@ -117,9 +117,8 @@ public class ExamplesOrganizationCloner {
if (!CollectionUtils.isEmpty(organization.getUserRoles())) {
organization.getUserRoles().clear();
}
organization.setName(user.computeFirstName() + "'s Examples");
organization.setSlug(null);
return organizationService.create(organization, user);
return organizationService.createPersonal(organization, user);
})
.flatMap(newOrganization -> {
User userUpdate = new User();

View File

@ -63,6 +63,7 @@ public class ExampleApplicationsAreMarked {
.flatMap(tuple -> {
final Organization organization = tuple.getT1();
assert organization.getId() != null;
Mockito.when(configService.getTemplateOrganizationId()).thenReturn(Mono.just(organization.getId()));
final Application app1 = new Application();

View File

@ -26,7 +26,6 @@ import com.appsmith.server.services.LayoutActionService;
import com.appsmith.server.services.OrganizationService;
import com.appsmith.server.services.PageService;
import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.services.UserService;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.bson.types.ObjectId;
@ -68,9 +67,6 @@ import static org.assertj.core.api.Assertions.assertThat;
@DirtiesContext
public class ExamplesOrganizationClonerTests {
@Autowired
UserService userService;
@Autowired
private ExamplesOrganizationCloner examplesOrganizationCloner;
@ -161,7 +157,7 @@ public class ExamplesOrganizationClonerTests {
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.applications).isEmpty();
@ -203,7 +199,7 @@ public class ExamplesOrganizationClonerTests {
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.applications).hasSize(1);
@ -255,7 +251,7 @@ public class ExamplesOrganizationClonerTests {
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.applications).hasSize(2);
@ -310,7 +306,7 @@ public class ExamplesOrganizationClonerTests {
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.applications).isEmpty();
@ -361,7 +357,7 @@ public class ExamplesOrganizationClonerTests {
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.datasources).hasSize(2);
@ -437,7 +433,7 @@ public class ExamplesOrganizationClonerTests {
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.applications).hasSize(2);
@ -561,9 +557,7 @@ public class ExamplesOrganizationClonerTests {
newPageAction.setPageId(page.getId());
return applicationPageService.addPageToApplication(app, page, false)
.then(actionCollectionService.createAction(newPageAction))
.flatMap(savedAction -> {
return layoutActionService.updateAction(savedAction.getId(), savedAction);
})
.flatMap(savedAction -> layoutActionService.updateAction(savedAction.getId(), savedAction))
.then(pageService.findById(page.getId(), READ_PAGES));
})
.map(tuple2 -> {
@ -578,16 +572,14 @@ public class ExamplesOrganizationClonerTests {
})
.then(examplesOrganizationCloner.cloneOrganizationForUser(organization.getId(), tuple.getT2()));
})
.doOnError(error -> {
log.error("Error preparing data for test", error);
})
.doOnError(error -> log.error("Error preparing data for test", error))
.flatMap(this::loadOrganizationData);
StepVerifier.create(resultMono)
.assertNext(data -> {
assertThat(data.organization).isNotNull();
assertThat(data.organization.getId()).isNotNull();
assertThat(data.organization.getName()).isEqualTo("api_user's Examples");
assertThat(data.organization.getName()).isEqualTo("api_user's Personal Organization");
assertThat(data.organization.getPolicies()).isNotEmpty();
assertThat(data.applications).hasSize(2);
@ -596,10 +588,13 @@ public class ExamplesOrganizationClonerTests {
"second application"
);
final Application firstApplication = data.applications.stream().filter(app -> app.getName().equals("first application")).findFirst().get();
final Application firstApplication = data.applications.stream().filter(app -> app.getName().equals("first application")).findFirst().orElse(null);
assert firstApplication != null;
final Page newPage = mongoTemplate.findOne(Query.query(Criteria.where("applicationId").is(firstApplication.getId()).and("name").is("A New Page")), Page.class);
assert newPage != null;
final String actionId = newPage.getLayouts().get(0).getLayoutOnLoadActions().get(0).iterator().next().getId();
final Action newPageAction = mongoTemplate.findOne(Query.query(Criteria.where("id").is(actionId)), Action.class);
assert newPageAction != null;
assertThat(newPageAction.getOrganizationId()).isEqualTo(data.organization.getId());
assertThat(data.datasources).hasSize(2);
@ -628,7 +623,7 @@ public class ExamplesOrganizationClonerTests {
return applicationService
.findByOrganizationId(organization.getId(), READ_APPLICATIONS)
.flatMap(application -> pageService.findByApplicationId(application.getId(), READ_PAGES))
.flatMap(page -> actionService.get(new LinkedMultiValueMap<String, String>(
.flatMap(page -> actionService.get(new LinkedMultiValueMap<>(
Map.of(FieldName.PAGE_ID, Collections.singletonList(page.getId())))));
}
}

View File

@ -44,6 +44,9 @@ $NGINX_SSL_CMNT server_name $custom_domain ;
sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '\''${APPSMITH_CLIENT_LOG_LEVEL}'\'';
sub_filter __APPSMITH_GOOGLE_MAPS_API_KEY__ '\''${APPSMITH_GOOGLE_MAPS_API_KEY}'\'';
sub_filter __APPSMITH_TNC_PP__ '\''${APPSMITH_TNC_PP}'\'';
sub_filter __APPSMITH_VERSION_ID__ '\''${APPSMITH_VERSION_ID}'\'';
sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '\''${APPSMITH_VERSION_RELEASE_DATE}'\'';
sub_filter __APPSMITH_INTERCOM_APP_ID__ '\''${APPSMITH_INTERCOM_APP_ID}'\'';
}
location /f {
@ -96,6 +99,9 @@ $NGINX_SSL_CMNT sub_filter __APPSMITH_ALGOLIA_API_KEY__ '\''${APPSMITH_AL
$NGINX_SSL_CMNT sub_filter __APPSMITH_CLIENT_LOG_LEVEL__ '\''${APPSMITH_CLIENT_LOG_LEVEL}'\'';
$NGINX_SSL_CMNT sub_filter __APPSMITH_GOOGLE_MAPS_API_KEY__ '\''${APPSMITH_GOOGLE_MAPS_API_KEY}'\'';
$NGINX_SSL_CMNT sub_filter __APPSMITH_TNC_PP__ '\''${APPSMITH_TNC_PP}'\'';
$NGINX_SSL_CMNT sub_filter __APPSMITH_VERSION_ID__ '\''${APPSMITH_VERSION_ID}'\'';
$NGINX_SSL_CMNT sub_filter __APPSMITH_VERSION_RELEASE_DATE__ '\''${APPSMITH_VERSION_RELEASE_DATE}'\'';
$NGINX_SSL_CMNT sub_filter __APPSMITH_INTERCOM_APP_ID__ '\''${APPSMITH_INTERCOM_APP_ID}'\'';
$NGINX_SSL_CMNT }
$NGINX_SSL_CMNT
$NGINX_SSL_CMNT location /f {