Merge branch 'release' of https://github.com/appsmithorg/appsmith into release
This commit is contained in:
commit
fa72df3c36
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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}';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
1
app/client/src/assets/icons/help/discord.svg
Normal file
1
app/client/src/assets/icons/help/discord.svg
Normal 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 |
|
|
@ -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 |
27
app/client/src/assets/icons/help/github-icon.svg
Normal file
27
app/client/src/assets/icons/help/github-icon.svg
Normal 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 |
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -61,4 +61,9 @@ export type AppsmithUIConfigs = {
|
|||
|
||||
featureFlag?: FeatureFlagConfig;
|
||||
logLevel: LogLevelDesc;
|
||||
appVersion: {
|
||||
id: string;
|
||||
releaseDate: string;
|
||||
};
|
||||
intercomAppID: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 || "";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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())))));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user