@Before
  public void createTestDocs() throws Exception {

    principal = session.getPrincipal();

    // Create and register 2 synchronization roots for Administrator
    syncRoot1 = session.createDocument(session.createDocumentModel("/", "syncRoot1", "Folder"));
    syncRoot2 = session.createDocument(session.createDocumentModel("/", "syncRoot2", "Folder"));
    Principal administrator = session.getPrincipal();
    nuxeoDriveManager.registerSynchronizationRoot(administrator, syncRoot1, session);
    nuxeoDriveManager.registerSynchronizationRoot(administrator, syncRoot2, session);

    // Folder
    folder = session.createDocumentModel(syncRoot1.getPathAsString(), "aFolder", "Folder");
    folder.setPropertyValue("dc:title", "Jack's folder");
    folder = session.createDocument(folder);

    // File
    file = session.createDocumentModel(folder.getPathAsString(), "aFile", "File");
    Blob blob = new StringBlob("Content of Joe's file.");
    blob.setFilename("Joe.odt");
    file.setPropertyValue("file:content", (Serializable) blob);
    file = session.createDocument(file);

    // Note
    note = session.createDocumentModel(folder.getPathAsString(), "aNote", "Note");
    note.setPropertyValue("note:note", "Content of Bob's note.");
    note = session.createDocument(note);

    // Custom doc type with the "file" schema
    custom = session.createDocumentModel(folder.getPathAsString(), "aCustomDoc", "Custom");
    blob = new StringBlob("Content of Bonnie's file.");
    blob.setFilename("Bonnie's file.odt");
    custom.setPropertyValue("file:content", (Serializable) blob);
    custom = session.createDocument(custom);

    // FolderishFile: doc type with the "file" schema and the "Folderish"
    // facet
    folderishFile =
        session.createDocumentModel(folder.getPathAsString(), "aFolderishFile", "FolderishFile");
    folderishFile.setPropertyValue("dc:title", "Sarah's folderish file");
    folderishFile = session.createDocument(folderishFile);

    // Doc not adaptable as a FileSystemItem (not Folderish nor a
    // BlobHolder)
    notAFileSystemItem =
        session.createDocumentModel(
            folder.getPathAsString(), "notAFileSystemItem", "NotSynchronizable");
    notAFileSystemItem = session.createDocument(notAFileSystemItem);

    // Sub folder
    subFolder = session.createDocumentModel(folder.getPathAsString(), "aSubFolder", "Folder");
    subFolder.setPropertyValue("dc:title", "Tony's sub folder");
    subFolder = session.createDocument(subFolder);

    session.save();
  }
  @Override
  public void startModeration(
      CoreSession session, DocumentModel doc, String commentID, ArrayList<String> moderators) {
    TaskService taskService = Framework.getService(TaskService.class);
    if (moderators == null || moderators.isEmpty()) {
      throw new NuxeoException("No moderators defined");
    }
    Map<String, String> vars = new HashMap<String, String>();
    vars.put(CommentsConstants.COMMENT_ID, commentID);
    vars.put(Task.TaskVariableName.needi18n.name(), "true");
    vars.put(Task.TaskVariableName.taskType.name(), CommentConstants.COMMENT_TASK_TYPE);

    vars.put(CreateTask.OperationTaskVariableName.createdFromCreateTaskOperation.name(), "false");
    vars.put(
        CreateTask.OperationTaskVariableName.acceptOperationChain.name(),
        CommentsConstants.ACCEPT_CHAIN_NAME);
    vars.put(
        CreateTask.OperationTaskVariableName.rejectOperationChain.name(),
        CommentsConstants.REJECT_CHAIN_NAME);

    taskService.createTask(
        session,
        (NuxeoPrincipal) session.getPrincipal(),
        doc,
        CommentsConstants.MODERATION_DIRECTIVE_NAME,
        moderators,
        false,
        null,
        null,
        null,
        vars,
        null);
  }
Example #3
0
 protected void getAllTasks() {
   error = null;
   errorMessage = null;
   userTasks = new ArrayList<>();
   CoreSession coreSession = getCoreSession();
   boolean filterTrashDocs = getFilterDocumentsInTrash();
   NuxeoPrincipal pal = (NuxeoPrincipal) coreSession.getPrincipal();
   TaskService taskService = Framework.getService(TaskService.class);
   List<Task> tasks = taskService.getAllCurrentTaskInstances(coreSession, getSortInfos());
   if (tasks != null) {
     for (Task task : tasks) {
       List<String> targetDocumentsIds = task.getTargetDocumentsIds();
       boolean hasTargetDocuments = targetDocumentsIds != null && !targetDocumentsIds.isEmpty();
       if (task.hasEnded() || task.isCancelled() || !hasTargetDocuments) {
         continue;
       }
       DocumentModel doc = taskService.getTargetDocumentModel(task, coreSession);
       if (doc != null) {
         if (filterTrashDocs
             && LifeCycleConstants.DELETED_STATE.equals(doc.getCurrentLifeCycleState())) {
           continue;
         } else {
           userTasks.add(new DashBoardItemImpl(task, doc, getLocale()));
         }
       } else {
         log.warn(
             String.format(
                 "User '%s' has a task of type '%s' on a missing or deleted document",
                 pal.getName(), task.getName()));
       }
     }
   }
 }
  protected void notifyEvent(
      CoreSession session,
      String eventId,
      Map<String, Serializable> properties,
      String comment,
      String category,
      DocumentModel dm) {

    // Default category
    if (category == null) {
      category = DocumentEventCategories.EVENT_DOCUMENT_CATEGORY;
    }

    if (properties == null) {
      properties = new HashMap<String, Serializable>();
    }

    properties.put(CoreEventConstants.REPOSITORY_NAME, session.getRepositoryName());
    properties.put(CoreEventConstants.SESSION_ID, session.getSessionId());
    properties.put(CoreEventConstants.DOC_LIFE_CYCLE, dm.getCurrentLifeCycleState());

    DocumentEventContext ctx = new DocumentEventContext(session, session.getPrincipal(), dm);

    ctx.setProperties(properties);
    ctx.setComment(comment);
    ctx.setCategory(category);

    EventProducer evtProducer = Framework.getService(EventProducer.class);
    Event event = ctx.newEvent(eventId);
    evtProducer.fireEvent(event);
  }
  protected void notifyEvent(
      String eventId,
      DocumentModel source,
      Map<String, Serializable> options,
      String comment,
      CoreSession session) {

    EventProducer evtProducer = null;

    try {
      evtProducer = Framework.getService(EventProducer.class);
    } catch (Exception e) {
      log.error("Unable to get EventProducer to send event notification", e);
    }

    DocumentEventContext docCtx = new DocumentEventContext(session, session.getPrincipal(), source);
    options.put("category", RelationEvents.CATEGORY);
    options.put("comment", comment);

    try {
      evtProducer.fireEvent(docCtx.newEvent(eventId));
    } catch (ClientException e) {
      log.error("Error while trying to send notification message", e);
    }
  }
  @Test
  public void testGetParentDocuments() {

    setPermissionToAnonymous(EVERYTHING);

    DocumentModel root = session.getRootDocument();

    String name = "Workspaces#1";
    DocumentModel workspaces = new DocumentModelImpl(root.getPathAsString(), name, "Workspace");
    session.createDocument(workspaces);
    String name2 = "repositoryWorkspace2#";
    DocumentModel repositoryWorkspace =
        new DocumentModelImpl(workspaces.getPathAsString(), name2, "Workspace");
    repositoryWorkspace = session.createDocument(repositoryWorkspace);

    String name3 = "ws#3";
    DocumentModel ws1 =
        new DocumentModelImpl(repositoryWorkspace.getPathAsString(), name3, "Workspace");
    ws1 = session.createDocument(ws1);
    String name4 = "ws#4";
    DocumentModel ws2 = new DocumentModelImpl(ws1.getPathAsString(), name4, "Workspace");
    session.createDocument(ws2);

    if (session.isNegativeAclAllowed()) { // always false for Mem
      ACP acp = new ACPImpl();
      ACE denyRead = new ACE("test", READ, false);
      ACL acl = new ACLImpl();
      acl.setACEs(new ACE[] {denyRead});
      acp.addACL(acl);
      // TODO this produces a stack trace
      repositoryWorkspace.setACP(acp, true);
      ws1.setACP(acp, true);
    }

    session.save();

    List<DocumentModel> ws2ParentsUnderAdministrator = session.getParentDocuments(ws2.getRef());
    assertTrue(
        "list parents for"
            + ws2.getName()
            + "under "
            + session.getPrincipal().getName()
            + " is not empty:",
        !ws2ParentsUnderAdministrator.isEmpty());

    CoreSession testSession = openSessionAs("test");
    List<DocumentModel> ws2ParentsUnderTest = testSession.getParentDocuments(ws2.getRef());
    assertTrue(
        "list parents for"
            + ws2.getName()
            + "under "
            + testSession.getPrincipal().getName()
            + " is empty:",
        ws2ParentsUnderTest.isEmpty());
    closeSession(testSession);
  }
  @Test
  public void testCreateSingleTaskAndRunOperationChain() throws Exception {
    OperationContext ctx = new OperationContext(coreSession);
    ctx.setInput(document);

    List<Task> tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    assertNotNull(tasks);
    assertEquals(0, tasks.size());

    automationService.run(ctx, "createSingleTaskAndRunOperationChain");

    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    assertEquals(1, tasks.size());

    Task task = tasks.get(0);

    // accept task
    taskService.acceptTask(
        coreSession, (NuxeoPrincipal) coreSession.getPrincipal(), task, "ok i'm in");
    coreSession.save();
    // test task again
    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    // ended tasks are filtered
    assertEquals(0, tasks.size());

    // check document metadata, refetching doc from core
    document = coreSession.getDocument(document.getRef());
    assertEquals("This document has been accepted", document.getPropertyValue("dc:description"));

    // run another time, and this time reject
    automationService.run(ctx, "createSingleTaskAndRunOperationChain");
    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    assertEquals(1, tasks.size());
    taskService.rejectTask(
        coreSession,
        (NuxeoPrincipal) coreSession.getPrincipal(),
        tasks.get(0),
        "i don't agree with what you're saying");
    document = coreSession.getDocument(document.getRef());
    assertEquals(
        "This document has been rejected !!!", document.getPropertyValue("dc:description"));
  }
  @Override
  public List<Task> getCurrentTaskInstances(CoreSession coreSession) throws ClientException {

    // Get tasks for current user
    // We need to build the task actors list: prefixed and unprefixed names
    // of the principal and all its groups
    NuxeoPrincipal principal = (NuxeoPrincipal) coreSession.getPrincipal();
    List<String> actors = TaskActorsHelper.getTaskActors(principal);

    return getCurrentTaskInstances(actors, coreSession);
  }
 @Override
 public void rejectComment(CoreSession session, DocumentModel doc, String commentId) {
   TaskService taskService = Framework.getService(TaskService.class);
   Task moderationTask = getModerationTask(taskService, session, doc, commentId);
   if (moderationTask == null) {
     session.followTransition(new IdRef(commentId), CommentsConstants.REJECT_STATE);
   } else {
     taskService.rejectTask(
         session, (NuxeoPrincipal) session.getPrincipal(), moderationTask, null);
   }
 }
 private Backend getBackend(CoreSession session) {
   if (session == null) {
     return null;
   }
   Principal principal = session.getPrincipal();
   if (principal == null || StringUtils.isEmpty(principal.getName())) {
     return null;
   }
   String principalName = principal.getName();
   WISession wiSession = SessionCacheHolder.getInstance().getCache().get(principalName);
   wiSession.setAttribute(WISession.CORESESSION_KEY, session);
   return new PluggableBackendFactory().getBackend(wiSession);
 }
  @Override
  public void approveComent(CoreSession session, DocumentModel doc, String commentId) {
    TaskService taskService = Framework.getService(TaskService.class);
    Task moderationTask = getModerationTask(taskService, session, doc, commentId);
    if (moderationTask == null) {
      session.followTransition(
          new IdRef(commentId), CommentsConstants.TRANSITION_TO_PUBLISHED_STATE);
    } else {
      taskService.acceptTask(
          session, (NuxeoPrincipal) session.getPrincipal(), moderationTask, null);
    }

    Map<String, Serializable> eventInfo = new HashMap<String, Serializable>();
    eventInfo.put("emailDetails", "test");
    notifyEvent(session, CommentsConstants.COMMENT_PUBLISHED, null, null, null, doc);
  }
 @Test
 public void testBean() {
   ExpressionContext context = new ExpressionContext();
   DocumentModel source = doCreateDocument();
   EventContext eventContext = new DocumentEventContext(session, session.getPrincipal(), source);
   Map<String, Serializable> properties = new HashMap<String, Serializable>();
   properties.put("test", "test");
   eventContext.setProperties(properties);
   evaluatorUnderTest.bindValue(context, "context", eventContext);
   DocumentModel value =
       evaluatorUnderTest.evaluateExpression(
           context, "${context.arguments[0]}", DocumentModel.class);
   assertNotNull(value);
   assertEquals(source, value);
   String test =
       evaluatorUnderTest.evaluateExpression(context, "${context.properties.test}", String.class);
   assertNotNull(value);
   assertEquals("test", test);
 }
 public Task getModerationTask(
     TaskService taskService, CoreSession session, DocumentModel doc, String commentId) {
   List<Task> tasks =
       DocumentTaskProvider.getTasks(
           "GET_COMMENT_MODERATION_TASKS",
           session,
           false,
           null,
           doc.getId(),
           session.getPrincipal().getName(),
           commentId);
   if (tasks != null && !tasks.isEmpty()) {
     if (tasks.size() > 1) {
       log.error("There are several moderation workflows running, " + "taking only first found");
     }
     Task task = tasks.get(0);
     return task;
   }
   return null;
 }
  @Test
  public void testCreateSingleTaskChain() throws Exception {
    OperationContext ctx = new OperationContext(coreSession);
    ctx.setInput(document);

    List<Task> tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    assertNotNull(tasks);
    assertEquals(0, tasks.size());

    automationService.run(ctx, "createSingleTaskChain");

    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    assertEquals(1, tasks.size());

    Task task = tasks.get(0);
    assertEquals("single test task", task.getName());

    List<String> pooledActorIds = task.getActors();
    assertEquals(3, pooledActorIds.size());
    assertTrue(pooledActorIds.contains(SecurityConstants.MEMBERS));
    assertTrue(pooledActorIds.contains("myuser"));
    assertTrue(pooledActorIds.contains(SecurityConstants.ADMINISTRATOR));

    List<TaskComment> comments = task.getComments();
    assertEquals(1, comments.size());

    TaskComment comment = comments.get(0);
    assertEquals(SecurityConstants.ADMINISTRATOR, comment.getAuthor());
    assertEquals("test comment", comment.getText());

    Calendar calendar = Calendar.getInstance();
    calendar.set(2006, 6, 6, 15, 10, 15);
    calendar.set(Calendar.MILLISECOND, 0);
    calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
    assertEquals(calendar.getTime(), task.getDueDate());
    // task status
    assertTrue(task.isOpened());
    assertFalse(task.isCancelled());
    assertFalse(task.hasEnded());
    assertEquals(6, task.getVariables().size());
    assertEquals(
        document.getRepositoryName(),
        task.getVariable(TaskService.VariableName.documentRepositoryName.name()));
    assertEquals(document.getId(), task.getVariable(TaskService.VariableName.documentId.name()));
    assertEquals("test directive", task.getVariable(TaskService.VariableName.directive.name()));
    assertEquals(
        "true", task.getVariable(OperationTaskVariableName.createdFromCreateTaskOperation.name()));
    assertEquals("true", task.getVariable(TaskService.VariableName.createdFromTaskService.name()));
    assertEquals(SecurityConstants.ADMINISTRATOR, task.getInitiator());

    // accept task
    taskService.acceptTask(
        coreSession, (NuxeoPrincipal) coreSession.getPrincipal(), task, "ok i'm in");
    coreSession.save();
    // test task again
    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    // ended tasks are filtered
    assertEquals(0, tasks.size());

    // check document metadata
    assertNull(document.getPropertyValue("dc:description"));
  }
  @Test
  public void testCreateSeveralTasksChain() throws Exception {
    OperationContext ctx = new OperationContext(coreSession);
    ctx.setInput(document);

    List<Task> tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    assertNotNull(tasks);
    assertEquals(0, tasks.size());

    automationService.run(ctx, "createSeveralTasksChain");

    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    Collections.sort(tasks, new TaskInstanceComparator());
    assertEquals(3, tasks.size());

    Task task1 = tasks.get(0);
    assertEquals("several test tasks", task1.getName());

    List<String> pooledActorIds = task1.getActors();
    assertEquals(1, pooledActorIds.size());
    assertEquals(SecurityConstants.ADMINISTRATOR, pooledActorIds.get(0));

    List<TaskComment> comments = task1.getComments();
    assertEquals(0, comments.size());
    // task status
    assertTrue(task1.isOpened());
    assertFalse(task1.isCancelled());
    assertFalse(task1.hasEnded());
    assertEquals(5, task1.getVariables().size());
    assertEquals(
        document.getRepositoryName(),
        task1.getVariable(TaskService.VariableName.documentRepositoryName.name()));
    assertEquals(document.getId(), task1.getVariable(TaskService.VariableName.documentId.name()));
    assertNull(task1.getVariable(TaskService.VariableName.directive.name()));
    assertEquals(
        "true", task1.getVariable(OperationTaskVariableName.createdFromCreateTaskOperation.name()));
    assertEquals("true", task1.getVariable(TaskService.VariableName.createdFromTaskService.name()));

    assertEquals(SecurityConstants.ADMINISTRATOR, task1.getInitiator());
    // accept task
    taskService.acceptTask(
        coreSession, (NuxeoPrincipal) coreSession.getPrincipal(), task1, "ok i'm in");
    coreSession.save();
    // test task again
    tasks = taskService.getTaskInstances(document, (NuxeoPrincipal) null, coreSession);
    // ended tasks are filtered
    assertEquals(2, tasks.size());

    Collections.sort(tasks, new TaskInstanceComparator());

    // check other tasks
    Task task2 = tasks.get(0);
    assertEquals("several test tasks", task2.getName());

    pooledActorIds = task2.getActors();
    assertEquals(1, pooledActorIds.size());
    assertEquals(SecurityConstants.MEMBERS, pooledActorIds.get(0));

    comments = task2.getComments();
    assertEquals(0, comments.size());
    // task status
    assertTrue(task2.isOpened());
    assertFalse(task2.isCancelled());
    assertFalse(task2.hasEnded());
    assertEquals(5, task2.getVariables().size());
    assertEquals(
        document.getRepositoryName(),
        task2.getVariable(TaskService.VariableName.documentRepositoryName.name()));
    assertEquals(document.getId(), task2.getVariable(TaskService.VariableName.documentId.name()));
    assertNull(task2.getVariable(TaskService.VariableName.directive.name()));
    assertEquals(
        "true", task2.getVariable(OperationTaskVariableName.createdFromCreateTaskOperation.name()));
    assertEquals("true", task2.getVariable(TaskService.VariableName.createdFromTaskService.name()));

    assertEquals(SecurityConstants.ADMINISTRATOR, task2.getInitiator());
    Task task3 = tasks.get(1);
    assertEquals("several test tasks", task3.getName());

    pooledActorIds = task3.getActors();
    assertEquals(1, pooledActorIds.size());
    assertEquals("myuser", pooledActorIds.get(0));

    comments = task3.getComments();
    assertEquals(0, comments.size());
    // task status
    assertTrue(task3.isOpened());
    assertFalse(task3.isCancelled());
    assertFalse(task3.hasEnded());
    assertEquals(5, task3.getVariables().size());
    assertEquals(
        document.getRepositoryName(),
        task3.getVariable(TaskService.VariableName.documentRepositoryName.name()));
    assertEquals(document.getId(), task3.getVariable(TaskService.VariableName.documentId.name()));
    assertNull(task3.getVariable(TaskService.VariableName.directive.name()));
    assertEquals(
        "true", task3.getVariable(OperationTaskVariableName.createdFromCreateTaskOperation.name()));
    assertEquals("true", task3.getVariable(TaskService.VariableName.createdFromTaskService.name()));

    assertEquals(SecurityConstants.ADMINISTRATOR, task3.getInitiator());
    // check document metadata
    assertNull(document.getPropertyValue("dc:description"));
  }
  @SuppressWarnings("unchecked")
  @OperationMethod
  public Paginable<LogEntry> run() throws IOException {

    List<SortInfo> sortInfos = null;
    if (sortInfoAsStringList != null) {
      sortInfos = new ArrayList<SortInfo>();
      for (String sortInfoDesc : sortInfoAsStringList) {
        SortInfo sortInfo;
        if (sortInfoDesc.contains(SORT_PARAMETER_SEPARATOR)) {
          String[] parts = sortInfoDesc.split(SORT_PARAMETER_SEPARATOR);
          sortInfo = new SortInfo(parts[0], Boolean.parseBoolean(parts[1]));
        } else {
          sortInfo = new SortInfo(sortInfoDesc, true);
        }
        sortInfos.add(sortInfo);
      }
    } else {
      // Sort Info Management
      if (!StringUtils.isBlank(sortBy)) {
        sortInfos = new ArrayList<>();
        String[] sorts = sortBy.split(",");
        String[] orders = null;
        if (!StringUtils.isBlank(sortOrder)) {
          orders = sortOrder.split(",");
        }
        for (int i = 0; i < sorts.length; i++) {
          String sort = sorts[i];
          boolean sortAscending =
              (orders != null && orders.length > i && "asc".equals(orders[i].toLowerCase()));
          sortInfos.add(new SortInfo(sort, sortAscending));
        }
      }
    }

    Object[] parameters = null;

    if (strParameters != null && !strParameters.isEmpty()) {
      parameters = strParameters.toArray(new String[strParameters.size()]);
      // expand specific parameters
      for (int idx = 0; idx < parameters.length; idx++) {
        String value = (String) parameters[idx];
        if (value.equals(CURRENT_USERID_PATTERN)) {
          parameters[idx] = session.getPrincipal().getName();
        } else if (value.equals(CURRENT_REPO_PATTERN)) {
          parameters[idx] = session.getRepositoryName();
        }
      }
    }
    if (parameters == null) {
      parameters = new Object[0];
    }

    Map<String, Serializable> props = new HashMap<String, Serializable>();
    props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY, (Serializable) session);

    if (query == null && (providerName == null || providerName.length() == 0)) {
      // provide a defaut provider
      providerName = "AUDIT_BROWSER";
    }

    Long targetPage = null;
    if (page != null) {
      targetPage = page.longValue();
    }
    if (currentPageIndex != null) {
      targetPage = currentPageIndex.longValue();
    }
    Long targetPageSize = null;
    if (pageSize != null) {
      targetPageSize = pageSize.longValue();
    }

    if (query != null) {

      AuditPageProvider app = new AuditPageProvider();
      app.setProperties(props);
      GenericPageProviderDescriptor desc = new GenericPageProviderDescriptor();
      desc.setPattern(query);
      app.setParameters(parameters);
      app.setDefinition(desc);
      app.setSortInfos(sortInfos);
      app.setPageSize(targetPageSize);
      app.setCurrentPage(targetPage);
      return new LogEntryList(app);
    } else {

      DocumentModel searchDoc = null;
      if (namedQueryParams != null && namedQueryParams.size() > 0) {
        String docType =
            ppService.getPageProviderDefinition(providerName).getWhereClause().getDocType();
        searchDoc = session.createDocumentModel(docType);
        DocumentHelper.setProperties(session, searchDoc, namedQueryParams);
      }

      PageProvider<LogEntry> pp =
          (PageProvider<LogEntry>)
              ppService.getPageProvider(
                  providerName,
                  searchDoc,
                  sortInfos,
                  targetPageSize,
                  targetPage,
                  props,
                  parameters);
      // return new PaginablePageProvider<LogEntry>(pp);
      return new LogEntryList(pp);
    }
  }
    @SuppressWarnings("unchecked")
    @OperationMethod
    public PaginableDocumentModelListImpl run() throws Exception {

        PageProviderService pps = Framework.getLocalService(PageProviderService.class);

        List<SortInfo> sortInfos = null;
        if (sortInfoAsStringList != null) {
            sortInfos = new ArrayList<SortInfo>();
            for (String sortInfoDesc : sortInfoAsStringList) {
                SortInfo sortInfo;
                if (sortInfoDesc.contains("|")) {
                    String[] parts = sortInfoDesc.split("|");
                    sortInfo = new SortInfo(parts[0],
                            Boolean.parseBoolean(parts[1]));
                } else {
                    sortInfo = new SortInfo(sortInfoDesc, true);
                }
                sortInfos.add(sortInfo);
            }
        }

        Object[] parameters = null;

        if (strParameters != null && !strParameters.isEmpty()) {
            parameters = strParameters.toArray(new String[strParameters.size()]);
            // expand specific parameters
            for (int idx = 0; idx < parameters.length; idx++) {
                String value = (String) parameters[idx];
                if (value.equals(CURRENT_USERID_PATTERN)) {
                    parameters[idx] = session.getPrincipal().getName();
                } else if (value.equals(CURRENT_REPO_PATTERN)) {
                    parameters[idx] = session.getRepositoryName();
                }
            }
        }

        Map<String, Serializable> props = new HashMap<String, Serializable>();
        props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY,
                (Serializable) session);

        if (query == null
                && (providerName == null || providerName.length() == 0)) {
            // provide a defaut query
            query = "SELECT * from Document";
        }

        Long targetPage = null;
        if (page != null) {
            targetPage = Long.valueOf(page.longValue());
        }
        Long targetPageSize = null;
        if (pageSize != null) {
            targetPageSize = Long.valueOf(pageSize.longValue());
        }
        if (query != null) {
            CoreQueryPageProviderDescriptor desc = new CoreQueryPageProviderDescriptor();
            desc.setPattern(query);
            return new PaginableDocumentModelListImpl(
                    (PageProvider<DocumentModel>) pps.getPageProvider("", desc,
                            sortInfos, targetPageSize, targetPage, props,
                            parameters));
        } else {
            return new PaginableDocumentModelListImpl(
                    (PageProvider<DocumentModel>) pps.getPageProvider(
                            providerName, sortInfos, targetPageSize,
                            targetPage, props, parameters));
        }

    }
  /**
   * Initializes the test hierarchy.
   *
   * <pre>
   * Server side for user1
   * ==============================
   *
   * /user1 (user workspace)
   *   |-- user1Folder1
   *   |     |-- user1File1
   *   |     |-- user1Folder2
   *   |-- user1File2
   *   |-- user1Folder3
   *   |     |-- user1File3
   *   |-- user1Folder4
   *
   * Server side for user2
   * ==============================
   *
   * /user2 (user workspace)
   *   |-- user2Folder1       (registered as a synchronization root with ReadWrite permission for user1)
   *   |     |-- user2File1
   *   |     |-- user2Folder2
   *   |-- user2File2
   *   |-- user2Folder3       (registered as a synchronization root with ReadWrite permission for user1)
   *   |     |-- user2File3
   *
   * </pre>
   */
  @Before
  public void init() throws Exception {

    // Create test users
    createUser("user1", "user1");
    createUser("user2", "user2");

    // Open a core session for each user
    session1 = coreFeature.openCoreSession("user1");
    session2 = coreFeature.openCoreSession("user2");

    // Create user workspace for each user
    userWorkspace1 = userWorkspaceService.getCurrentUserPersonalWorkspace(session1, null);
    userWorkspace2 = userWorkspaceService.getCurrentUserPersonalWorkspace(session2, null);

    userWorkspace1ItemId = USER_SYNC_ROOT_PARENT_ID_PREFIX + userWorkspace1.getId();
    userWorkspace1ItemPath = "/" + TOP_LEVEL_ID + "/" + userWorkspace1ItemId;

    // Populate user workspaces
    // user1
    user1Folder1 =
        createFolder(session1, userWorkspace1.getPathAsString(), "user1Folder1", "Folder");
    user1File1 =
        createFile(
            session1,
            user1Folder1.getPathAsString(),
            "user1File1",
            "File",
            "user1File1.txt",
            CONTENT_PREFIX + "user1File1");
    user1Folder2 = createFolder(session1, user1Folder1.getPathAsString(), "user1Folder2", "Folder");
    user1File2 =
        createFile(
            session1,
            userWorkspace1.getPathAsString(),
            "user1File2",
            "File",
            "user1File2.txt",
            CONTENT_PREFIX + "user1File2");
    user1Folder3 =
        createFolder(session1, userWorkspace1.getPathAsString(), "user1Folder3", "Folder");
    user1File3 =
        createFile(
            session1,
            user1Folder3.getPathAsString(),
            "user1File3",
            "File",
            "user1File3.txt",
            CONTENT_PREFIX + "user1File3");
    user1Folder4 =
        createFolder(session1, userWorkspace1.getPathAsString(), "user1Folder4", "Folder");
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    // user2
    user2Folder1 =
        createFolder(session2, userWorkspace2.getPathAsString(), "user2Folder1", "Folder");
    user2File1 =
        createFile(
            session2,
            user2Folder1.getPathAsString(),
            "user2File1",
            "File",
            "user2File1.txt",
            CONTENT_PREFIX + "user2File1");
    user2Folder2 = createFolder(session2, user2Folder1.getPathAsString(), "user2Folder2", "Folder");
    user2File2 =
        createFile(
            session2,
            userWorkspace2.getPathAsString(),
            "user2File2",
            "File",
            "user2File2.txt",
            CONTENT_PREFIX + "user2File2");
    user2Folder3 =
        createFolder(session2, userWorkspace2.getPathAsString(), "user2Folder3", "Folder");
    user2File3 =
        createFile(
            session2,
            user2Folder3.getPathAsString(),
            "user2File3",
            "File",
            "user2File3.txt",
            CONTENT_PREFIX + "user2File3");
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    setPermission(user2Folder1, "user1", SecurityConstants.READ_WRITE, true);
    setPermission(user2Folder3, "user1", SecurityConstants.READ_WRITE, true);

    // Register shared folders as synchronization roots for user1
    nuxeoDriveManager.registerSynchronizationRoot(
        session1.getPrincipal(), session1.getDocument(user2Folder1.getRef()), session1);
    nuxeoDriveManager.registerSynchronizationRoot(
        session1.getPrincipal(), session1.getDocument(user2Folder3.getRef()), session1);

    // Get an Automation client session for user1
    clientSession1 = automationClient.getSession("user1", "user1");

    mapper = new ObjectMapper();
  }
  @Test
  public void testClientSideUser1() throws Exception {

    // ---------------------------------------------
    // Check active factories
    // ---------------------------------------------
    TopLevelFolderItemFactory topLevelFolderItemFactory =
        fileSystemItemAdapterService.getTopLevelFolderItemFactory();
    assertEquals(
        "org.nuxeo.drive.hierarchy.permission.factory.PermissionTopLevelFactory",
        topLevelFolderItemFactory.getName());

    Set<String> activeFactories = fileSystemItemAdapterService.getActiveFileSystemItemFactories();
    assertEquals(5, activeFactories.size());
    assertTrue(activeFactories.contains("collectionSyncRootFolderItemFactory"));
    assertTrue(activeFactories.contains("defaultFileSystemItemFactory"));
    assertTrue(activeFactories.contains("userSyncRootParentFactory"));
    assertTrue(activeFactories.contains("permissionSyncRootFactory"));
    assertTrue(activeFactories.contains("sharedSyncRootParentFactory"));

    // ---------------------------------------------
    // Check top level folder: "Nuxeo Drive"
    // ---------------------------------------------
    Blob topLevelFolderJSON =
        (Blob) clientSession1.newRequest(NuxeoDriveGetTopLevelFolder.ID).execute();
    assertNotNull(topLevelFolderJSON);

    PermissionTopLevelFolderItem topLevelFolder =
        mapper.readValue(topLevelFolderJSON.getStream(), PermissionTopLevelFolderItem.class);

    assertNotNull(topLevelFolder);
    assertEquals(TOP_LEVEL_ID, topLevelFolder.getId());
    assertNull(topLevelFolder.getParentId());
    assertEquals("/" + TOP_LEVEL_ID, topLevelFolder.getPath());
    assertEquals("Nuxeo Drive", topLevelFolder.getName());
    assertTrue(topLevelFolder.isFolder());
    assertEquals("system", topLevelFolder.getCreator());
    assertEquals("system", topLevelFolder.getLastContributor());
    assertFalse(topLevelFolder.getCanRename());
    assertFalse(topLevelFolder.getCanDelete());
    assertFalse(topLevelFolder.getCanCreateChild());

    /**
     *
     *
     * <pre>
     * ===================================================
     * User workspace registered as a synchronization root
     * ===================================================
     * => Expected client side for user1:
     *
     * Nuxeo Drive
     *   |-- My Docs
     *   |     |-- user1File2
     *   |     |-- user1Folder1
     *   |     |     |-- user1File1
     *   |     |     |-- user1Folder2
     *   |     |-- user1Folder3
     *   |     |      |-- user1File3
     *   |     |-- user1Folder4
     *   |
     *   |-- Other Docs
     *   |     |-- user2Folder1
     *   |     |     |-- user2File1
     *   |     |     |-- user2Folder2
     *   |     |-- user2Folder3
     *   |     |     |-- user2File3
     *
     * </pre>
     */
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    nuxeoDriveManager.registerSynchronizationRoot(
        session1.getPrincipal(), userWorkspace1, session1);

    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();

    // ---------------------------------------------
    // Check top level folder children
    // ---------------------------------------------
    Blob topLevelChildrenJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", topLevelFolder.getId())
                .execute();

    ArrayNode topLevelChildren =
        mapper.readValue(topLevelChildrenJSON.getStream(), ArrayNode.class);
    assertNotNull(topLevelChildren);
    assertEquals(2, topLevelChildren.size());

    // Check "My Docs"
    UserSyncRootParentFolderItem userSyncRootParent =
        mapper.readValue(topLevelChildren.get(0), UserSyncRootParentFolderItem.class);
    assertEquals(userWorkspace1ItemId, userSyncRootParent.getId());
    assertEquals(TOP_LEVEL_ID, userSyncRootParent.getParentId());
    assertEquals(userWorkspace1ItemPath, userSyncRootParent.getPath());
    assertEquals("My Docs", userSyncRootParent.getName());
    assertTrue(userSyncRootParent.isFolder());
    assertEquals("user1", userSyncRootParent.getCreator());
    assertEquals("user1", userSyncRootParent.getLastContributor());
    assertFalse(userSyncRootParent.getCanRename());
    assertFalse(userSyncRootParent.getCanDelete());
    // Can create a child since "My Docs" is the user workspace
    assertTrue(userSyncRootParent.getCanCreateChild());

    // Check "Other Docs"
    SharedSyncRootParentFolderItem sharedSyncRootParent =
        mapper.readValue(topLevelChildren.get(1), SharedSyncRootParentFolderItem.class);
    assertEquals(SHARED_SYNC_ROOT_PARENT_ID, sharedSyncRootParent.getId());
    assertEquals(TOP_LEVEL_ID, sharedSyncRootParent.getParentId());
    assertEquals(
        "/" + TOP_LEVEL_ID + "/" + SHARED_SYNC_ROOT_PARENT_ID, sharedSyncRootParent.getPath());
    assertEquals("Other Docs", sharedSyncRootParent.getName());
    assertTrue(sharedSyncRootParent.isFolder());
    assertEquals("system", sharedSyncRootParent.getCreator());
    assertEquals("system", sharedSyncRootParent.getLastContributor());
    assertFalse(sharedSyncRootParent.getCanRename());
    assertFalse(sharedSyncRootParent.getCanDelete());
    assertFalse(sharedSyncRootParent.getCanCreateChild());

    // --------------------------------------------
    // Check user synchronization roots
    // --------------------------------------------
    Blob userSyncRootsJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", userSyncRootParent.getId())
                .execute();

    ArrayNode userSyncRoots = mapper.readValue(userSyncRootsJSON.getStream(), ArrayNode.class);
    assertNotNull(userSyncRoots);
    assertEquals(4, userSyncRoots.size());

    DocumentBackedFolderItem folderItem;
    DocumentBackedFileItem childFileItem;
    DocumentBackedFileItem fileItem;
    DocumentBackedFolderItem childFolderItem;

    JsonNode[] rootNodes = sortNodeByName(userSyncRoots);

    // user1File2
    fileItem = mapper.readValue(rootNodes[0], DocumentBackedFileItem.class);
    checkFileItem(
        fileItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        user1File2,
        userWorkspace1ItemId,
        userWorkspace1ItemPath,
        "user1File2.txt",
        "user1",
        "user1");

    // user1Folder1
    folderItem = mapper.readValue(rootNodes[1], DocumentBackedFolderItem.class);
    checkFolderItem(
        folderItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        user1Folder1,
        userWorkspace1ItemId,
        userWorkspace1ItemPath,
        "user1Folder1",
        "user1",
        "user1");
    Blob folderItemChildrenJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", folderItem.getId())
                .execute();
    ArrayNode folderItemChildren =
        mapper.readValue(folderItemChildrenJSON.getStream(), ArrayNode.class);
    assertNotNull(folderItemChildren);
    assertEquals(2, folderItemChildren.size());
    {
      JsonNode[] nodes = sortNodeByName(folderItemChildren);

      // user1File1
      childFileItem = mapper.readValue(nodes[0], DocumentBackedFileItem.class);
      checkFileItem(
          childFileItem,
          DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
          user1File1,
          folderItem.getId(),
          folderItem.getPath(),
          "user1File1.txt",
          "user1",
          "user1");

      // user1Folder2
      childFolderItem = mapper.readValue(nodes[1], DocumentBackedFolderItem.class);
      checkFolderItem(
          childFolderItem,
          DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
          user1Folder2,
          folderItem.getId(),
          folderItem.getPath(),
          "user1Folder2",
          "user1",
          "user1");
    }

    // user1Folder3
    folderItem = mapper.readValue(rootNodes[2], DocumentBackedFolderItem.class);
    checkFolderItem(
        folderItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        user1Folder3,
        userWorkspace1ItemId,
        userWorkspace1ItemPath,
        "user1Folder3",
        "user1",
        "user1");
    {
      folderItemChildrenJSON =
          (Blob)
              clientSession1
                  .newRequest(NuxeoDriveGetChildren.ID)
                  .set("id", folderItem.getId())
                  .execute();
      folderItemChildren = mapper.readValue(folderItemChildrenJSON.getStream(), ArrayNode.class);
      assertNotNull(folderItemChildren);
      assertEquals(1, folderItemChildren.size());
      // user1File3
      childFileItem = mapper.readValue(folderItemChildren.get(0), DocumentBackedFileItem.class);
      checkFileItem(
          childFileItem,
          DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
          user1File3,
          folderItem.getId(),
          folderItem.getPath(),
          "user1File3.txt",
          "user1",
          "user1");
    }

    // user1Folder4
    folderItem = mapper.readValue(rootNodes[3], DocumentBackedFolderItem.class);
    checkFolderItem(
        folderItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        user1Folder4,
        userWorkspace1ItemId,
        userWorkspace1ItemPath,
        "user1Folder4",
        "user1",
        "user1");

    // ---------------------------------------------
    // Check shared synchronization roots
    // ---------------------------------------------
    Blob sharedSyncRootsJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", sharedSyncRootParent.getId())
                .execute();

    List<DefaultSyncRootFolderItem> sharedSyncRoots =
        mapper.readValue(
            sharedSyncRootsJSON.getStream(),
            new TypeReference<List<DefaultSyncRootFolderItem>>() {});
    Collections.sort(sharedSyncRoots);

    assertNotNull(sharedSyncRoots);
    assertEquals(2, sharedSyncRoots.size());

    // user2Folder1
    DefaultSyncRootFolderItem sharedSyncRoot = sharedSyncRoots.get(0);
    checkFolderItem(
        sharedSyncRoot,
        SYNC_ROOT_ID_PREFIX,
        session1.getDocument(user2Folder1.getRef()),
        sharedSyncRootParent.getId(),
        sharedSyncRootParent.getPath(),
        "user2Folder1",
        "user2",
        "user1");
    Blob sharedSyncRootChildrenJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", sharedSyncRoot.getId())
                .execute();
    ArrayNode sharedSyncRootChildren =
        mapper.readValue(sharedSyncRootChildrenJSON.getStream(), ArrayNode.class);
    assertNotNull(sharedSyncRootChildren);
    assertEquals(2, sharedSyncRootChildren.size());
    DocumentBackedFolderItem sharedSyncRootChildFolderItem;
    DocumentBackedFileItem sharedSyncRootChildFileItem;
    {
      JsonNode[] nodes = sortNodeByName(sharedSyncRootChildren);
      // user2File1
      sharedSyncRootChildFileItem = mapper.readValue(nodes[0], DocumentBackedFileItem.class);
      checkFileItem(
          sharedSyncRootChildFileItem,
          DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
          session1.getDocument(user2File1.getRef()),
          sharedSyncRoot.getId(),
          sharedSyncRoot.getPath(),
          "user2File1.txt",
          "user2",
          "user2");
      // user2Folder2
      sharedSyncRootChildFolderItem = mapper.readValue(nodes[1], DocumentBackedFolderItem.class);
      checkFolderItem(
          sharedSyncRootChildFolderItem,
          DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
          session1.getDocument(user2Folder2.getRef()),
          sharedSyncRoot.getId(),
          sharedSyncRoot.getPath(),
          "user2Folder2",
          "user2",
          "user2");
    }
    // user2Folder3
    sharedSyncRoot = sharedSyncRoots.get(1);
    checkFolderItem(
        sharedSyncRoot,
        SYNC_ROOT_ID_PREFIX,
        session1.getDocument(user2Folder3.getRef()),
        sharedSyncRootParent.getId(),
        sharedSyncRootParent.getPath(),
        "user2Folder3",
        "user2",
        "user1");
    sharedSyncRootChildrenJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", sharedSyncRoot.getId())
                .execute();
    sharedSyncRootChildren =
        mapper.readValue(sharedSyncRootChildrenJSON.getStream(), ArrayNode.class);
    assertNotNull(sharedSyncRootChildren);
    assertEquals(1, sharedSyncRootChildren.size());
    // user2File3
    sharedSyncRootChildFileItem =
        mapper.readValue(sharedSyncRootChildren.get(0), DocumentBackedFileItem.class);
    checkFileItem(
        sharedSyncRootChildFileItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        session1.getDocument(user2File3.getRef()),
        sharedSyncRoot.getId(),
        sharedSyncRoot.getPath(),
        "user2File3.txt",
        "user2",
        "user2");

    /**
     *
     *
     * <pre>
     * =======================================================
     * User workspace NOT registered as a synchronization root
     * =======================================================
     * => Expected client side for user1:
     *
     * Nuxeo Drive
     *   |-- My Docs
     *   |
     *   |-- Other Docs (unchanged)
     *   |     |-- user2Folder1
     *   |     |     |-- user2File1
     *   |     |     |-- user2Folder2
     *   |     |-- user2Folder3
     *   |     |     |-- user2File3
     *
     * </pre>
     */
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    nuxeoDriveManager.unregisterSynchronizationRoot(
        session1.getPrincipal(), userWorkspace1, session1);

    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    // ---------------------------------------------
    // Check "My Docs"
    // ---------------------------------------------
    Blob userSyncRootParentJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetFileSystemItem.ID)
                .set("id", userWorkspace1ItemId)
                .execute();
    assertNotNull(userSyncRootParentJSON);

    userSyncRootParent =
        mapper.readValue(userSyncRootParentJSON.getStream(), UserSyncRootParentFolderItem.class);
    assertEquals(userWorkspace1ItemId, userSyncRootParent.getId());
    assertEquals(TOP_LEVEL_ID, userSyncRootParent.getParentId());
    assertEquals(userWorkspace1ItemPath, userSyncRootParent.getPath());
    assertEquals("My Docs", userSyncRootParent.getName());
    assertTrue(userSyncRootParent.isFolder());
    assertEquals("user1", userSyncRootParent.getCreator());
    assertEquals("user1", userSyncRootParent.getLastContributor());
    assertFalse(userSyncRootParent.getCanRename());
    assertFalse(userSyncRootParent.getCanDelete());
    // Cannot create a child since "My Docs" is only the parent of the
    // synchronization roots, not the user workspace
    assertFalse(userSyncRootParent.getCanCreateChild());

    // --------------------------------------------
    // Check user synchronization roots
    // --------------------------------------------
    userSyncRootsJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", userSyncRootParent.getId())
                .execute();

    userSyncRoots = mapper.readValue(userSyncRootsJSON.getStream(), ArrayNode.class);
    assertNotNull(userSyncRoots);
    assertEquals(0, userSyncRoots.size());

    /**
     *
     *
     * <pre>
     * =======================================================
     * User workspace NOT registered as a synchronization root
     * but specific folders yes: user1Folder3, user1Folder4
     * =======================================================
     * => Expected client side for user1:
     *
     * Nuxeo Drive
     *   |-- My Docs
     *   |     |-- user1Folder3
     *   |     |      |-- user1File3
     *   |     |-- user1Folder4
     *   |
     *   |-- Other Docs (unchanged)
     *   |     |-- user2Folder1
     *   |     |     |-- user2File1
     *   |     |     |-- user2Folder2
     *   |     |-- user2Folder3
     *   |     |     |-- user2File3
     *
     * </pre>
     */
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    nuxeoDriveManager.registerSynchronizationRoot(session1.getPrincipal(), user1Folder3, session1);
    nuxeoDriveManager.registerSynchronizationRoot(session1.getPrincipal(), user1Folder4, session1);
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();

    // --------------------------------------------
    // Check user synchronization roots
    // --------------------------------------------
    userSyncRootsJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", userSyncRootParent.getId())
                .execute();

    userSyncRoots = mapper.readValue(userSyncRootsJSON.getStream(), ArrayNode.class);
    assertNotNull(userSyncRoots);
    assertEquals(2, userSyncRoots.size());

    // user1Folder3
    folderItem = mapper.readValue(userSyncRoots.get(0), DocumentBackedFolderItem.class);
    checkFolderItem(
        folderItem,
        SYNC_ROOT_ID_PREFIX,
        user1Folder3,
        userWorkspace1ItemId,
        userWorkspace1ItemPath,
        "user1Folder3",
        "user1",
        "user1");
    folderItemChildrenJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", folderItem.getId())
                .execute();
    folderItemChildren = mapper.readValue(folderItemChildrenJSON.getStream(), ArrayNode.class);
    assertNotNull(folderItemChildren);
    assertEquals(1, folderItemChildren.size());
    // user1File3
    childFileItem = mapper.readValue(folderItemChildren.get(0), DocumentBackedFileItem.class);
    checkFileItem(
        childFileItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        user1File3,
        folderItem.getId(),
        folderItem.getPath(),
        "user1File3.txt",
        "user1",
        "user1");
    // user1Folder4
    folderItem = mapper.readValue(userSyncRoots.get(1), DocumentBackedFolderItem.class);
    checkFolderItem(
        folderItem,
        SYNC_ROOT_ID_PREFIX,
        user1Folder4,
        userWorkspace1ItemId,
        userWorkspace1ItemPath,
        "user1Folder4",
        "user1",
        "user1");

    /**
     *
     *
     * <pre>
     * =======================================================
     * Unregister a shared folder: user2Folder1
     * =======================================================
     * => Expected client side for user1:
     *
     * Nuxeo Drive
     *   |-- My Docs (unchanged)
     *   |     |-- user1Folder3
     *   |     |      |-- user1File3
     *   |     |-- user1Folder4
     *   |
     *   |-- Other Docs
     *   |     |-- user2Folder3
     *   |     |     |-- user2File3
     *
     * </pre>
     */
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    nuxeoDriveManager.unregisterSynchronizationRoot(
        session1.getPrincipal(), session1.getDocument(user2Folder1.getRef()), session1);
    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();

    // ---------------------------------------------
    // Check shared synchronization roots
    // ---------------------------------------------
    sharedSyncRootsJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", sharedSyncRootParent.getId())
                .execute();

    sharedSyncRoots =
        mapper.readValue(
            sharedSyncRootsJSON.getStream(),
            new TypeReference<List<DefaultSyncRootFolderItem>>() {});
    assertNotNull(sharedSyncRoots);
    assertEquals(1, sharedSyncRoots.size());

    // user2Folder3
    sharedSyncRoot = sharedSyncRoots.get(0);
    checkFolderItem(
        sharedSyncRoot,
        SYNC_ROOT_ID_PREFIX,
        session1.getDocument(user2Folder3.getRef()),
        sharedSyncRootParent.getId(),
        sharedSyncRootParent.getPath(),
        "user2Folder3",
        "user2",
        "user1");
    sharedSyncRootChildrenJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", sharedSyncRoot.getId())
                .execute();
    sharedSyncRootChildren =
        mapper.readValue(sharedSyncRootChildrenJSON.getStream(), ArrayNode.class);
    assertNotNull(sharedSyncRootChildren);
    assertEquals(1, sharedSyncRootChildren.size());
    // user2File3
    sharedSyncRootChildFileItem =
        mapper.readValue(sharedSyncRootChildren.get(0), DocumentBackedFileItem.class);
    checkFileItem(
        sharedSyncRootChildFileItem,
        DEFAULT_FILE_SYSTEM_ITEM_ID_PREFIX,
        session1.getDocument(user2File3.getRef()),
        sharedSyncRoot.getId(),
        sharedSyncRoot.getPath(),
        "user2File3.txt",
        "user2",
        "user2");

    /**
     *
     *
     * <pre>
     * =======================================================
     * Remove permission on a shared folder: user2Folder3
     * =======================================================
     * => Expected client side for user1:
     *
     * Nuxeo Drive
     *   |-- My Docs (unchanged)
     *   |     |-- user1Folder3
     *   |     |      |-- user1File3
     *   |     |-- user1Folder4
     *   |
     *   |-- Other Docs
     *
     * </pre>
     */
    resetPermissions(user2Folder3.getRef(), "user1");

    TransactionHelper.commitOrRollbackTransaction();
    TransactionHelper.startTransaction();
    // ---------------------------------------------
    // Check shared synchronization roots
    // ---------------------------------------------
    sharedSyncRootsJSON =
        (Blob)
            clientSession1
                .newRequest(NuxeoDriveGetChildren.ID)
                .set("id", sharedSyncRootParent.getId())
                .execute();

    sharedSyncRoots =
        mapper.readValue(
            sharedSyncRootsJSON.getStream(),
            new TypeReference<List<DefaultSyncRootFolderItem>>() {});
    assertNotNull(sharedSyncRoots);
    assertEquals(0, sharedSyncRoots.size());
  }
  @Override
  public void addRelation(
      CoreSession session,
      DocumentModel from,
      Node toResource,
      String predicate,
      boolean inverse,
      boolean includeStatementsInEvents,
      String comment)
      throws ClientException {
    Graph graph = getRelationManager().getGraphByName(RelationConstants.GRAPH_NAME);
    QNameResource fromResource = getNodeFromDocumentModel(from);

    Resource predicateResource = new ResourceImpl(predicate);
    Statement stmt = null;
    List<Statement> statements = null;
    if (inverse) {
      stmt = new StatementImpl(toResource, predicateResource, fromResource);
      statements = graph.getStatements(toResource, predicateResource, fromResource);
      if (statements != null && statements.size() > 0) {
        throw new RelationAlreadyExistsException();
      }
    } else {
      stmt = new StatementImpl(fromResource, predicateResource, toResource);
      statements = graph.getStatements(fromResource, predicateResource, toResource);
      if (statements != null && statements.size() > 0) {
        throw new RelationAlreadyExistsException();
      }
    }

    // Comment ?
    if (!StringUtils.isEmpty(comment)) {
      stmt.addProperty(RelationConstants.COMMENT, new LiteralImpl(comment));
    }
    Literal now = RelationDate.getLiteralDate(new Date());
    if (stmt.getProperties(RelationConstants.CREATION_DATE) == null) {
      stmt.addProperty(RelationConstants.CREATION_DATE, now);
    }
    if (stmt.getProperties(RelationConstants.MODIFICATION_DATE) == null) {
      stmt.addProperty(RelationConstants.MODIFICATION_DATE, now);
    }

    if (session.getPrincipal() != null && stmt.getProperty(RelationConstants.AUTHOR) != null) {
      stmt.addProperty(RelationConstants.AUTHOR, new LiteralImpl(session.getPrincipal().getName()));
    }

    // notifications

    Map<String, Serializable> options = new HashMap<String, Serializable>();
    String currentLifeCycleState = from.getCurrentLifeCycleState();
    options.put(CoreEventConstants.DOC_LIFE_CYCLE, currentLifeCycleState);
    if (includeStatementsInEvents) {
      putStatements(options, stmt);
    }
    options.put(RelationEvents.GRAPH_NAME_EVENT_KEY, RelationConstants.GRAPH_NAME);

    // before notification
    notifyEvent(RelationEvents.BEFORE_RELATION_CREATION, from, options, comment, session);

    // add statement
    graph.add(stmt);

    // XXX AT: try to refetch it from the graph so that resources are
    // transformed into qname resources: useful for indexing
    if (includeStatementsInEvents) {
      putStatements(options, graph.getStatements(stmt));
    }

    // after notification
    notifyEvent(RelationEvents.AFTER_RELATION_CREATION, from, options, comment, session);
  }