Merge branch 'release' of https://github.com/appsmithorg/appsmith into release
This commit is contained in:
commit
bc40d337f5
|
|
@ -13,6 +13,7 @@ import {
|
||||||
getAllUsers,
|
getAllUsers,
|
||||||
getCurrentOrg,
|
getCurrentOrg,
|
||||||
} from "selectors/organizationSelectors";
|
} from "selectors/organizationSelectors";
|
||||||
|
import Spinner from "components/editorComponents/Spinner";
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import { InviteUsersToOrgFormValues, inviteUsersToOrg } from "./helpers";
|
import { InviteUsersToOrgFormValues, inviteUsersToOrg } from "./helpers";
|
||||||
import { INVITE_USERS_TO_ORG_FORM } from "constants/forms";
|
import { INVITE_USERS_TO_ORG_FORM } from "constants/forms";
|
||||||
|
|
@ -110,6 +111,12 @@ const StyledButton = styled(Button)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Loading = styled(Spinner)`
|
||||||
|
padding-top: 10px;
|
||||||
|
margin: auto;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
const validateFormValues = (values: { users: string; role: string }) => {
|
const validateFormValues = (values: { users: string; role: string }) => {
|
||||||
if (values.users && values.users.length > 0) {
|
if (values.users && values.users.length > 0) {
|
||||||
const _users = values.users.split(",").filter(Boolean);
|
const _users = values.users.split(",").filter(Boolean);
|
||||||
|
|
@ -158,6 +165,7 @@ const OrgInviteUsersForm = (props: any) => {
|
||||||
fetchCurrentOrg,
|
fetchCurrentOrg,
|
||||||
currentOrg,
|
currentOrg,
|
||||||
isApplicationInvite,
|
isApplicationInvite,
|
||||||
|
isLoading,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const currentPath = useLocation().pathname;
|
const currentPath = useLocation().pathname;
|
||||||
|
|
@ -237,6 +245,9 @@ const OrgInviteUsersForm = (props: any) => {
|
||||||
type="submit"
|
type="submit"
|
||||||
/>
|
/>
|
||||||
</StyledInviteFieldGroup>
|
</StyledInviteFieldGroup>
|
||||||
|
{isLoading ? (
|
||||||
|
<Loading size={30} />
|
||||||
|
) : (
|
||||||
<UserList style={{ justifyContent: "space-between" }}>
|
<UserList style={{ justifyContent: "space-between" }}>
|
||||||
{allUsers.map((user: { username: string; roleName: string }) => {
|
{allUsers.map((user: { username: string; roleName: string }) => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -247,6 +258,7 @@ const OrgInviteUsersForm = (props: any) => {
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</UserList>
|
</UserList>
|
||||||
|
)}
|
||||||
{!pathRegex.test(currentPath) && canManage && (
|
{!pathRegex.test(currentPath) && canManage && (
|
||||||
<Button
|
<Button
|
||||||
className="manageUsers"
|
className="manageUsers"
|
||||||
|
|
@ -269,6 +281,7 @@ export default connect(
|
||||||
roles: getRolesForField(state),
|
roles: getRolesForField(state),
|
||||||
allUsers: getAllUsers(state),
|
allUsers: getAllUsers(state),
|
||||||
currentOrg: getCurrentOrg(state),
|
currentOrg: getCurrentOrg(state),
|
||||||
|
isLoading: state.ui.orgs.loadingStates.isFetchAllUsers,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
(dispatch: any) => ({
|
(dispatch: any) => ({
|
||||||
|
|
|
||||||
|
|
@ -84,4 +84,10 @@ public class PageController extends BaseController<PageService, Page, String> {
|
||||||
return service.delete(id)
|
return service.delete(id)
|
||||||
.map(deletedResource -> new ResponseDTO<>(HttpStatus.OK.value(), deletedResource, null));
|
.map(deletedResource -> new ResponseDTO<>(HttpStatus.OK.value(), deletedResource, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/clone/{pageId}")
|
||||||
|
public Mono<ResponseDTO<Page>> clonePage(@PathVariable String pageId) {
|
||||||
|
return applicationPageService.clonePage(pageId)
|
||||||
|
.map(page -> new ResponseDTO<>(HttpStatus.CREATED.value(), page, null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,6 @@ public interface ApplicationPageService {
|
||||||
Mono<Application> cloneApplication(Application application);
|
Mono<Application> cloneApplication(Application application);
|
||||||
|
|
||||||
Mono<Application> deleteApplication(String id);
|
Mono<Application> deleteApplication(String id);
|
||||||
|
|
||||||
|
Mono<Page> clonePage(String pageId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,23 @@ import com.appsmith.server.acl.AclPermission;
|
||||||
import com.appsmith.server.acl.PolicyGenerator;
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.constants.AnalyticsEvents;
|
import com.appsmith.server.constants.AnalyticsEvents;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.domains.Action;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.ApplicationPage;
|
import com.appsmith.server.domains.ApplicationPage;
|
||||||
import com.appsmith.server.domains.Layout;
|
import com.appsmith.server.domains.Layout;
|
||||||
import com.appsmith.server.domains.Organization;
|
import com.appsmith.server.domains.Organization;
|
||||||
import com.appsmith.server.domains.Page;
|
import com.appsmith.server.domains.Page;
|
||||||
import com.appsmith.server.domains.User;
|
import com.appsmith.server.domains.User;
|
||||||
|
import com.appsmith.server.dtos.ApplicationPagesDTO;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.repositories.ApplicationRepository;
|
import com.appsmith.server.repositories.ApplicationRepository;
|
||||||
import com.mongodb.client.result.UpdateResult;
|
import com.mongodb.client.result.UpdateResult;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -26,6 +30,7 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.appsmith.server.acl.AclPermission.MANAGE_ACTIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_APPLICATIONS;
|
||||||
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
import static com.appsmith.server.acl.AclPermission.MANAGE_PAGES;
|
||||||
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS;
|
||||||
|
|
@ -40,26 +45,32 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final SessionUserService sessionUserService;
|
private final SessionUserService sessionUserService;
|
||||||
private final OrganizationService organizationService;
|
private final OrganizationService organizationService;
|
||||||
|
private final LayoutActionService layoutActionService;
|
||||||
|
|
||||||
private final AnalyticsService analyticsService;
|
private final AnalyticsService analyticsService;
|
||||||
private final PolicyGenerator policyGenerator;
|
private final PolicyGenerator policyGenerator;
|
||||||
|
|
||||||
private final ApplicationRepository applicationRepository;
|
private final ApplicationRepository applicationRepository;
|
||||||
|
private final ActionService actionService;
|
||||||
|
|
||||||
public ApplicationPageServiceImpl(ApplicationService applicationService,
|
public ApplicationPageServiceImpl(ApplicationService applicationService,
|
||||||
PageService pageService,
|
PageService pageService,
|
||||||
SessionUserService sessionUserService,
|
SessionUserService sessionUserService,
|
||||||
OrganizationService organizationService,
|
OrganizationService organizationService,
|
||||||
|
LayoutActionService layoutActionService,
|
||||||
AnalyticsService analyticsService,
|
AnalyticsService analyticsService,
|
||||||
PolicyGenerator policyGenerator,
|
PolicyGenerator policyGenerator,
|
||||||
ApplicationRepository applicationRepository) {
|
ApplicationRepository applicationRepository,
|
||||||
|
ActionService actionService) {
|
||||||
this.applicationService = applicationService;
|
this.applicationService = applicationService;
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.sessionUserService = sessionUserService;
|
this.sessionUserService = sessionUserService;
|
||||||
this.organizationService = organizationService;
|
this.organizationService = organizationService;
|
||||||
|
this.layoutActionService = layoutActionService;
|
||||||
this.analyticsService = analyticsService;
|
this.analyticsService = analyticsService;
|
||||||
this.policyGenerator = policyGenerator;
|
this.policyGenerator = policyGenerator;
|
||||||
this.applicationRepository = applicationRepository;
|
this.applicationRepository = applicationRepository;
|
||||||
|
this.actionService = actionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Mono<Page> createPage(Page page) {
|
public Mono<Page> createPage(Page page) {
|
||||||
|
|
@ -324,4 +335,85 @@ public class ApplicationPageServiceImpl implements ApplicationPageService {
|
||||||
.flatMap(deletedObj -> analyticsService.sendEvent(AnalyticsEvents.DELETE + "_" + deletedObj.getClass().getSimpleName().toUpperCase(), (Application) deletedObj));
|
.flatMap(deletedObj -> analyticsService.sendEvent(AnalyticsEvents.DELETE + "_" + deletedObj.getClass().getSimpleName().toUpperCase(), (Application) deletedObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Page> clonePage(String pageId) {
|
||||||
|
|
||||||
|
// Find the source page and then prune the page layout fields to only contain the required fields that should be
|
||||||
|
// copied.
|
||||||
|
Mono<Page> sourcePageMono = pageService.findById(pageId, MANAGE_PAGES)
|
||||||
|
.flatMap(page -> Flux.fromIterable(page.getLayouts())
|
||||||
|
.map(layout -> layout.getDsl())
|
||||||
|
.map(dsl -> {
|
||||||
|
Layout newLayout = new Layout();
|
||||||
|
String id = new ObjectId().toString();
|
||||||
|
newLayout.setId(id);
|
||||||
|
newLayout.setDsl(dsl);
|
||||||
|
return newLayout;
|
||||||
|
})
|
||||||
|
.collectList()
|
||||||
|
.map(layouts -> {
|
||||||
|
page.setLayouts(layouts);
|
||||||
|
return page;
|
||||||
|
}));
|
||||||
|
|
||||||
|
Flux<Action> sourceActionFlux = actionService.findByPageId(pageId, MANAGE_ACTIONS);
|
||||||
|
|
||||||
|
return sourcePageMono
|
||||||
|
.flatMap(page -> {
|
||||||
|
Mono<ApplicationPagesDTO> pageNamesMono = pageService
|
||||||
|
.findNamesByApplicationId(page.getApplicationId());
|
||||||
|
return pageNamesMono
|
||||||
|
// Set a unique name for the cloned page and then create the page.
|
||||||
|
.flatMap(pageNames -> {
|
||||||
|
Set<String> names = pageNames.getPages()
|
||||||
|
.stream()
|
||||||
|
.map(pageNameIdDTO -> pageNameIdDTO.getName()).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
String newPageName = page.getName() + " Copy";
|
||||||
|
int i = 0;
|
||||||
|
String name = newPageName;
|
||||||
|
while(names.contains(name)) {
|
||||||
|
i++;
|
||||||
|
name = newPageName + i;
|
||||||
|
}
|
||||||
|
newPageName = name;
|
||||||
|
// Now we have a unique name. Proceed with creating the copy of the page
|
||||||
|
page.setId(null);
|
||||||
|
page.setName(newPageName);
|
||||||
|
return pageService.createDefault(page);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.flatMap(page -> {
|
||||||
|
String newPageId = page.getId();
|
||||||
|
return sourceActionFlux
|
||||||
|
.flatMap(action -> {
|
||||||
|
action.setId(null);
|
||||||
|
action.setPageId(newPageId);
|
||||||
|
return actionService.create(action);
|
||||||
|
})
|
||||||
|
.collectList()
|
||||||
|
.thenReturn(page);
|
||||||
|
})
|
||||||
|
// Calculate the onload actions for this page now that the page and actions have been created
|
||||||
|
.flatMap(savedPage -> {
|
||||||
|
List<Layout> layouts = savedPage.getLayouts();
|
||||||
|
|
||||||
|
return Flux.fromIterable(layouts)
|
||||||
|
.flatMap(layout -> layoutActionService.updateLayout(savedPage.getId(), layout.getId(), layout))
|
||||||
|
.collectList()
|
||||||
|
.thenReturn(savedPage);
|
||||||
|
})
|
||||||
|
.flatMap(page -> {
|
||||||
|
Mono<Application> applicationMono = applicationService.findById(page.getApplicationId(), MANAGE_APPLICATIONS);
|
||||||
|
return applicationMono
|
||||||
|
.flatMap(application -> {
|
||||||
|
ApplicationPage applicationPage = new ApplicationPage();
|
||||||
|
applicationPage.setId(page.getId());
|
||||||
|
application.getPages().add(applicationPage);
|
||||||
|
return applicationService.save(application)
|
||||||
|
.thenReturn(page);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ public class LayoutActionServiceImpl implements LayoutActionService {
|
||||||
private final ActionService actionService;
|
private final ActionService actionService;
|
||||||
private final PageService pageService;
|
private final PageService pageService;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final ApplicationPageService applicationPageService;
|
|
||||||
private final AnalyticsService analyticsService;
|
private final AnalyticsService analyticsService;
|
||||||
/*
|
/*
|
||||||
* This pattern finds all the String which have been extracted from the mustache dynamic bindings.
|
* This pattern finds all the String which have been extracted from the mustache dynamic bindings.
|
||||||
|
|
@ -67,12 +66,10 @@ public class LayoutActionServiceImpl implements LayoutActionService {
|
||||||
public LayoutActionServiceImpl(ActionService actionService,
|
public LayoutActionServiceImpl(ActionService actionService,
|
||||||
PageService pageService,
|
PageService pageService,
|
||||||
ObjectMapper objectMapper,
|
ObjectMapper objectMapper,
|
||||||
ApplicationPageService applicationPageService,
|
|
||||||
AnalyticsService analyticsService) {
|
AnalyticsService analyticsService) {
|
||||||
this.actionService = actionService;
|
this.actionService = actionService;
|
||||||
this.pageService = pageService;
|
this.pageService = pageService;
|
||||||
this.objectMapper = objectMapper;
|
this.objectMapper = objectMapper;
|
||||||
this.applicationPageService = applicationPageService;
|
|
||||||
this.analyticsService = analyticsService;
|
this.analyticsService = analyticsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package com.appsmith.server.services;
|
package com.appsmith.server.services;
|
||||||
|
|
||||||
import com.appsmith.external.models.Policy;
|
import com.appsmith.external.models.Policy;
|
||||||
import com.appsmith.server.configurations.WithMockAppsmithUser;
|
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.Page;
|
import com.appsmith.server.domains.Page;
|
||||||
|
|
@ -17,7 +16,6 @@ import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.security.test.context.support.WithMockUser;
|
|
||||||
import org.springframework.security.test.context.support.WithUserDetails;
|
import org.springframework.security.test.context.support.WithUserDetails;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
|
@ -174,6 +172,44 @@ public class PageServiceTest {
|
||||||
.verifyComplete();
|
.verifyComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUserDetails(value = "api_user")
|
||||||
|
public void clonePage() throws ParseException {
|
||||||
|
Policy managePagePolicy = Policy.builder().permission(MANAGE_PAGES.getValue())
|
||||||
|
.users(Set.of("api_user"))
|
||||||
|
.build();
|
||||||
|
Policy readPagePolicy = Policy.builder().permission(READ_PAGES.getValue())
|
||||||
|
.users(Set.of("api_user"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Page testPage = new Page();
|
||||||
|
testPage.setName("PageServiceTest CloneTest Source");
|
||||||
|
setupTestApplication();
|
||||||
|
testPage.setApplicationId(application.getId());
|
||||||
|
|
||||||
|
Mono<Page> pageMono = applicationPageService.createPage(testPage)
|
||||||
|
.flatMap(page -> applicationPageService.clonePage(page.getId()));
|
||||||
|
|
||||||
|
Object parsedJson = new JSONParser(JSONParser.MODE_PERMISSIVE).parse(FieldName.DEFAULT_PAGE_LAYOUT);
|
||||||
|
StepVerifier
|
||||||
|
.create(pageMono)
|
||||||
|
.assertNext(page -> {
|
||||||
|
assertThat(page).isNotNull();
|
||||||
|
assertThat(page.getId()).isNotNull();
|
||||||
|
assertThat("PageServiceTest CloneTest Source Copy".equals(page.getName()));
|
||||||
|
|
||||||
|
assertThat(page.getPolicies()).isNotEmpty();
|
||||||
|
assertThat(page.getPolicies()).containsOnly(managePagePolicy, readPagePolicy);
|
||||||
|
|
||||||
|
assertThat(page.getLayouts()).isNotEmpty();
|
||||||
|
assertThat(page.getLayouts().get(0).getDsl()).isEqualTo(parsedJson);
|
||||||
|
assertThat(page.getLayouts().get(0).getWidgetNames()).isNotEmpty();
|
||||||
|
assertThat(page.getLayouts().get(0).getPublishedDsl()).isNullOrEmpty();
|
||||||
|
})
|
||||||
|
.verifyComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void purgeAllPages() {
|
public void purgeAllPages() {
|
||||||
pageService.deleteAll();
|
pageService.deleteAll();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user