Hi,
I’m working on a P2 plugin for Confluence Server and I want to create a CustomContentType which I can query using CQL, including permission checks. I have created the custom type in accordance to Bitbucket. Creating entities works just fine and whenever I run a generic CQL query for all content entities in a space (http://cyclobase:1990/confluence/rest/api/content/search?cql=space=DS) my PermissionDelegate.canView(User) is being invoked and returns true. The result however does not contain my content entities.
Here is the relevant code-base:
CustomContent
public class CustomContent {
private static final long serialVersionUID = -2878773629921274872L;
private final CustomContentEntityObject customContentEntityObject;
private final List<Label> labels;
public static boolean isCustomContent(CustomContentEntityObject customContentEntityObject) {
return customContentEntityObject.getPluginModuleKey().equals(CustomContentEntityAdapter.PLUGIN_CONTENT_KEY);
}
CustomContent(@Nonnull final CustomContentEntityObject customContentEntityObject,
@Nullable final List<Label> labels) {
if (!isCustomContent(customContentEntityObject)) {
throw new IllegalArgumentException("Attempted to create a CustomContent from a CustomContentEntityObject which is no CustomContent!");
}
this.customContentEntityObject = customContentEntityObject;
this.labels = labels;
}
public CustomContentEntityObject getEntity() {
return customContentEntityObject;
}
public Long getPageId() {
return customContentEntityObject.getProperties().getLongProperty("pageId", 0L);
}
public String getSpaceKey() {
return customContentEntityObject.getSpaceKey();
}
public Space getSpace() {
return customContentEntityObject.getSpace();
}
public String getTitle() {
return customContentEntityObject.getTitle();
}
public String getBodyAsString() {
return customContentEntityObject.getBodyAsString();
}
public List<Label> getLabels() {
return labels;
}
}
CustomContentBuilderImpl
public class CustomContentBuilderImpl implements CustomContentBuilder {
final PageManager pageManager;
final CustomContentManager customContentManager;
private Long pageId;
private String title;
private String body;
private List<Label> labels;
CustomContentBuilderImpl(final PageManager pageManager,
final CustomContentManager customContentManager) {
this.pageManager = pageManager;
this.customContentManager = customContentManager;
}
public CustomContentBuilder setPageId(Long pageId) {
this.pageId = pageId;
return this;
}
public CustomContentBuilder setTitle(String title) {
this.title = title;
return this;
}
public CustomContentBuilder setBody(String body) {
this.body = body;
return this;
}
public CustomContentBuilder setLabels(List<Label> labels) {
this.labels = labels;
return this;
}
public CustomContent build() {
if (pageId == null || title == null || body == null) {
throw new RuntimeException("pageId, title and body must be set!");
}
AbstractPage abstractPage = pageManager.getAbstractPage(pageId);
if (abstractPage == null) {
throw new IllegalArgumentException("There exists no page with the id " + pageId + "!");
}
final CustomContentEntityObject customEntityObject = customContentManager.newPluginContentEntityObject(CustomContentEntityAdapter.PLUGIN_CONTENT_KEY);
customEntityObject.setPluginModuleKey(CustomContentEntityAdapter.PLUGIN_CONTENT_KEY);
customEntityObject.setTitle(title);
customEntityObject.setCreationDate(new Date());
customEntityObject.setBodyAsString(body);
customEntityObject.setCreator(AuthenticatedUserThreadLocal.get());
customEntityObject.setSpace(abstractPage.getSpace());
customEntityObject.getProperties().setLongProperty("pageId", pageId);
CustomContent customContent = new CustomContent(customEntityObject, labels);
return customContent;
}
}
CustomContentEntityAdapter
@Component
public class CustomContentEntityAdapter implements ContentEntityAdapter {
private static final String BASE_URL = ""; // TODO
public static final String PLUGIN_CONTENT_KEY = "de.cyclonit.confluence.customcontent:custom-content";
@Override
public Option<String> getUrlPath(CustomContentEntityObject customContentEntityObject) {
if (customContentEntityObject.getId() != 0 && CustomContent.isCustomContent(customContentEntityObject)) {
Long pageId = customContentEntityObject.getProperties().getLongProperty("pageId", 0L);
String title = customContentEntityObject.getTitle();
return some(BASE_URL + "/rest/custom/latest/get?pageId=" + pageId + "&title=" + title);
} else {
return none();
}
}
@Override
public Option<String> getDisplayTitle(CustomContentEntityObject customContentEntityObject) {
return none();
}
@Override
public Option<String> getNameForComparison(CustomContentEntityObject customContentEntityObject) {
if (customContentEntityObject.getId() != 0 && CustomContent.isCustomContent(customContentEntityObject)) {
return some(customContentEntityObject.getTitle());
} else {
return none();
}
}
@Override
public Option<String> getAttachmentsUrlPath(CustomContentEntityObject customContentEntityObject) {
return none();
}
@Override
public Option<String> getAttachmentUrlPath(CustomContentEntityObject customContentEntityObject, Attachment attachment) {
return none();
}
@Override
public BodyType getDefaultBodyType(CustomContentEntityObject customContentEntityObject) {
return BodyType.RAW;
}
@Override
public Option<String> getExcerpt(CustomContentEntityObject customContentEntityObject) {
return none();
}
@Override
public boolean isAllowedParent(CustomContentEntityObject customContentEntityObject, CustomContentEntityObject customContentEntityObject1) {
return false;
}
@Override
public boolean isAllowedContainer(ContentEntityObject contentEntityObject, ContentEntityObject contentEntityObject1) {
return false;
}
@Override
public boolean isIndexable(CustomContentEntityObject customContentEntityObject, boolean b) {
return CustomContent.isCustomContent(customContentEntityObject);
}
@Override
public boolean shouldConvertToContent(CustomContentEntityObject customContentEntityObject) {
return false;
}
@Override
public VersionChildOwnerPolicy getVersionChildPolicy(ContentType contentType) {
return null;
}
}
CustomContentManagerImpl
@Component
@Named("customContentManager")
public class CustomContentManagerImpl implements CustomContentManager {
private static final SaveContext CUSTOM_SAVE_CONTEXT = (new DefaultSaveContext.Builder())
.updateLastModifier(false)
.suppressAutowatch(true)
.suppressNotifications(false)
.build();
private final PageManager pageManager;
private final CustomContentManager customContentManager;
private final LabelManager labelManager;
private final TransactionTemplate transactionTemplate;
@Inject
public CustomContentManagerImpl(@ComponentImport final PageManager pageManager,
@ComponentImport final CustomContentManager customContentManager,
@ComponentImport final LabelManager labelManager,
@ComponentImport final TransactionTemplate transactionTemplate) {
this.pageManager = pageManager;
this.customContentManager = customContentManager;
this.labelManager = labelManager;
this.transactionTemplate = transactionTemplate;
}
public CustomContentBuilder builder() {
return new CustomContentBuilderImpl(pageManager, customContentManager);
}
public CustomContent get(Long pageId, String customTitle) {
// TODO: Implement
return null;
}
public void create(final CustomContent customContent) {
ContentEntityObject pageEntity = pageManager.getById(customContent.getPageId());
if (pageEntity == null) {
throw new IllegalArgumentException("There exists no page with the id " + customContent.getPageId() + "!");
}
transactionTemplate.execute(new TransactionCallback() {
@Override
public Void doInTransaction() {
// create the CustomContent itself
CustomContent oldExerpt = get(customContent.getPageId(), customContent.getTitle());
if (oldExerpt != null) {
throw new IllegalArgumentException("attempted to store duplicate CustomContent called " + customContent.getTitle() + " for page " + customContent.getPageId());
}
customContentManager.saveContentEntity(customContent.getEntity(), CUSTOM_SAVE_CONTEXT);
// add labels
if (customContent.getLabels() != null) {
for (Label label : customContent.getLabels()) {
labelManager.addLabel(customContent.getEntity(), label);
}
}
return null;
}
});
}
}
CustomContentType
public class CustomContentType implements ContentType {
private final CustomContentEntityAdapter customContentEntityAdapter;
private final PermissionDelegate customPermissionDelegate;
private final ContentUiSupport contentUiSupport;
@Inject
public CustomContentType(final CustomPermissionDelegate permissionDelegate,
@ComponentImport final WebResourceUrlProvider webResourceUrlProvider) {
this.customContentEntityAdapter = new CustomContentEntityAdapter();
this.customPermissionDelegate = permissionDelegate;
this.contentUiSupport = new CustomUiSupport(webResourceUrlProvider); // TODO: Replace for CustomContent.
}
@Override
public ContentEntityAdapter getContentAdapter() {
return customContentEntityAdapter;
}
@Override
public PermissionDelegate getPermissionDelegate() {
return customPermissionDelegate;
}
@Override
public ContentUiSupport getContentUiSupport() {
return contentUiSupport;
}
}
CustomPermissionDelegateImpl
@Component
@Named("customPermissionDelegate")
public class CustomPermissionDelegateImpl implements CustomPermissionDelegate {
private final PermissionManager permissionManager;
private final PageManager pageManager;
@Autowired
public CustomPermissionDelegateImpl(@ComponentImport final PermissionManager permissionManager,
@ComponentImport final PageManager pageManager) {
this.permissionManager = permissionManager;
this.pageManager = pageManager;
}
public boolean hasCustomPermission(User user, Permission permission, Object obj) {
if (obj instanceof CustomContentEntityObject && CustomContent.isCustomContent((CustomContentEntityObject) obj)) {
CustomContent customContent = new CustomContent((CustomContentEntityObject) obj, null);
return hasCustomPermission(user, permission, customContent);
} else {
return false;
}
}
public boolean hasCustomPermission(User user, Permission permission, CustomContent customContent) {
Long pageId = customContent.getPageId();
AbstractPage abstractPage = pageManager.getAbstractPage(pageId);
return permissionManager.hasPermission(user, permission, abstractPage);
}
@Override
public boolean canView(User user, Object obj) {
return hasCustomPermission(user, Permission.VIEW, obj);
}
@Override
public boolean canView(User user) {
return true; // TODO: Is this right?
}
@Override
public boolean canEdit(User user, Object obj) {
return hasCustomPermission(user, Permission.EDIT, obj);
}
@Override
public boolean canSetPermissions(User user, Object obj) {
return hasCustomPermission(user, Permission.EDIT, obj);
}
@Override
public boolean canRemove(User user, Object obj) {
return hasCustomPermission(user, Permission.REMOVE, obj);
}
@Override
public boolean canExport(User user, Object obj) {
return hasCustomPermission(user, Permission.EXPORT, obj);
}
@Override
public boolean canAdminister(User user, Object obj) {
return hasCustomPermission(user, Permission.ADMINISTER, obj);
}
@Override
public boolean canCreate(User user, Object obj) {
return hasCustomPermission(user, Permission.EDIT, obj);
}
@Override
public boolean canCreateInTarget(User user, Class aClass) {
return true;
}
}
Kind regards,
Christopher