protected List<Layout> getCandidateLayouts(long plid, boolean privateLayout, KBArticle kbArticle)
      throws Exception {

    List<Layout> candidateLayouts = new ArrayList<>();

    Group group = GroupLocalServiceUtil.getGroup(kbArticle.getGroupId());

    if (group.isLayout()) {
      Layout layout = LayoutLocalServiceUtil.getLayout(group.getClassPK());

      candidateLayouts.add(layout);

      group = layout.getGroup();
    }

    List<Layout> layouts =
        LayoutLocalServiceUtil.getLayouts(
            group.getGroupId(), privateLayout, LayoutConstants.TYPE_PORTLET);

    candidateLayouts.addAll(layouts);

    Layout layout = LayoutLocalServiceUtil.getLayout(plid);

    if ((layout.getGroupId() == kbArticle.getGroupId()) && layout.isTypePortlet()) {

      candidateLayouts.remove(layout);
      candidateLayouts.add(0, layout);
    }

    return candidateLayouts;
  }
  public void updateKBArticleAsset(
      long userId, KBArticle kbArticle, long[] assetCategoryIds, String[] assetTagNames)
      throws PortalException, SystemException {
    // TBD
    long classTypeId = 0;

    assetEntryLocalService.updateEntry(
        userId,
        kbArticle.getGroupId(),
        KBArticle.class.getName(),
        kbArticle.getClassPK(),
        kbArticle.getUuid(),
        classTypeId,
        assetCategoryIds,
        assetTagNames,
        false,
        null,
        null,
        null,
        null,
        ContentTypes.TEXT_HTML,
        kbArticle.getTitle(),
        kbArticle.getDescription(),
        null,
        null,
        null,
        0,
        0,
        null,
        false);
  }
  public void updateKBArticleResources(
      KBArticle kbArticle, String[] groupPermissions, String[] guestPermissions)
      throws PortalException, SystemException {

    resourceLocalService.updateResources(
        kbArticle.getCompanyId(),
        kbArticle.getGroupId(),
        KBArticle.class.getName(),
        kbArticle.getResourcePrimKey(),
        groupPermissions,
        guestPermissions);
  }
  public static void updateAttachments(KBArticle kbArticle) {
    try {
      long folderId = kbArticle.getClassPK();

      String oldDirName = "knowledgebase/articles/" + folderId;
      String newDirName = "knowledgebase/kbarticles/" + folderId;

      DLLocalServiceUtil.addDirectory(
          kbArticle.getCompanyId(), CompanyConstants.SYSTEM, newDirName);

      String[] fileNames =
          DLLocalServiceUtil.getFileNames(
              kbArticle.getCompanyId(), CompanyConstants.SYSTEM, oldDirName);

      ServiceContext serviceContext = new ServiceContext();

      serviceContext.setCompanyId(kbArticle.getCompanyId());
      serviceContext.setScopeGroupId(kbArticle.getGroupId());

      for (String fileName : fileNames) {
        String shortFileName = FileUtil.getShortFileName(fileName);
        byte[] bytes =
            DLLocalServiceUtil.getFile(kbArticle.getCompanyId(), CompanyConstants.SYSTEM, fileName);

        DLLocalServiceUtil.addFile(
            kbArticle.getCompanyId(),
            CompanyConstants.SYSTEM_STRING,
            GroupConstants.DEFAULT_PARENT_GROUP_ID,
            CompanyConstants.SYSTEM,
            newDirName + StringPool.SLASH + shortFileName,
            0,
            StringPool.BLANK,
            serviceContext.getModifiedDate(null),
            serviceContext,
            bytes);
      }

      DLLocalServiceUtil.deleteDirectory(
          kbArticle.getCompanyId(),
          CompanyConstants.SYSTEM_STRING,
          CompanyConstants.SYSTEM,
          oldDirName);

      if (_log.isInfoEnabled()) {
        _log.info("Added attachments for " + folderId);
      }
    } catch (Exception e) {
      _log.error(e.getMessage());
    }
  }
  public void moveKBArticle(
      long userId, long resourcePrimKey, long parentResourcePrimKey, double priority)
      throws PortalException, SystemException {

    // KB article

    validate(priority);

    updatePermissionFields(resourcePrimKey, parentResourcePrimKey);

    List<KBArticle> kbArticles =
        getKBArticleVersions(
            resourcePrimKey,
            WorkflowConstants.STATUS_ANY,
            QueryUtil.ALL_POS,
            QueryUtil.ALL_POS,
            new KBArticleVersionComparator());

    for (KBArticle kbArticle : kbArticles) {
      kbArticle.setParentResourcePrimKey(parentResourcePrimKey);
      kbArticle.setPriority(priority);

      kbArticlePersistence.update(kbArticle, false);
    }

    // Social

    KBArticle kbArticle = getLatestKBArticle(resourcePrimKey, WorkflowConstants.STATUS_ANY);

    if (kbArticle.isApproved() || !kbArticle.isFirstVersion()) {
      socialActivityLocalService.addActivity(
          userId,
          kbArticle.getGroupId(),
          KBArticle.class.getName(),
          resourcePrimKey,
          AdminActivityKeys.MOVE_KB_ARTICLE,
          StringPool.BLANK,
          0);
    }
  }
  public KBArticle updateStatus(
      long userId, long resourcePrimKey, int status, ServiceContext serviceContext)
      throws PortalException, SystemException {

    // KB article

    User user = userPersistence.findByPrimaryKey(userId);
    boolean main = false;
    Date now = new Date();

    if (status == WorkflowConstants.STATUS_APPROVED) {
      main = true;
    }

    KBArticle kbArticle = getLatestKBArticle(resourcePrimKey, WorkflowConstants.STATUS_ANY);

    kbArticle.setModifiedDate(serviceContext.getModifiedDate(now));
    kbArticle.setMain(main);
    kbArticle.setStatus(status);
    kbArticle.setStatusByUserId(user.getUserId());
    kbArticle.setStatusByUserName(user.getFullName());
    kbArticle.setStatusDate(serviceContext.getModifiedDate(now));

    kbArticlePersistence.update(kbArticle, false);

    if (status != WorkflowConstants.STATUS_APPROVED) {
      return kbArticle;
    }

    if (!kbArticle.isFirstVersion()) {
      KBArticle oldKBArticle =
          kbArticlePersistence.findByR_V(resourcePrimKey, kbArticle.getVersion() - 1);

      oldKBArticle.setMain(false);

      kbArticlePersistence.update(oldKBArticle, false);
    }

    // Asset

    AssetEntry assetEntry =
        assetEntryLocalService.getEntry(KBArticle.class.getName(), kbArticle.getKbArticleId());

    updateKBArticleAsset(userId, kbArticle, assetEntry.getCategoryIds(), assetEntry.getTagNames());

    assetEntryLocalService.deleteEntry(KBArticle.class.getName(), kbArticle.getKbArticleId());

    assetEntryLocalService.updateVisible(
        KBArticle.class.getName(), kbArticle.getResourcePrimKey(), true);

    // Social

    if (!kbArticle.isFirstVersion()) {
      socialActivityLocalService.addActivity(
          userId,
          kbArticle.getGroupId(),
          KBArticle.class.getName(),
          resourcePrimKey,
          AdminActivityKeys.UPDATE_KB_ARTICLE,
          StringPool.BLANK,
          0);
    } else {
      socialActivityLocalService.addActivity(
          userId,
          kbArticle.getGroupId(),
          KBArticle.class.getName(),
          resourcePrimKey,
          AdminActivityKeys.ADD_KB_ARTICLE,
          StringPool.BLANK,
          0);
    }

    // Indexer

    Indexer indexer = IndexerRegistryUtil.getIndexer(KBArticle.class);

    indexer.reindex(kbArticle);

    // Attachments

    if (!kbArticle.isFirstVersion()) {
      deleteKBArticleAttachments(kbArticle, resourcePrimKey);
    }

    String dirName = KBArticleConstants.DIR_NAME_PREFIX + kbArticle.getKbArticleId();

    addKBArticleAttachments(kbArticle, dirName, serviceContext);

    deleteKBArticleAttachments(kbArticle, kbArticle.getKbArticleId());

    // Subscriptions

    notifySubscribers(kbArticle, serviceContext);

    return kbArticle;
  }
  public KBArticle updateKBArticle(
      long userId,
      long resourcePrimKey,
      String title,
      String content,
      String description,
      String[] sections,
      String dirName,
      ServiceContext serviceContext)
      throws PortalException, SystemException {

    // KB article

    User user = userPersistence.findByPrimaryKey(userId);
    int version = KBArticleConstants.DEFAULT_VERSION;
    int status = WorkflowConstants.STATUS_DRAFT;

    validate(title, content);

    KBArticle oldKBArticle = getLatestKBArticle(resourcePrimKey, WorkflowConstants.STATUS_ANY);

    long oldResourcePrimKey = oldKBArticle.getResourcePrimKey();
    long oldGroupId = oldKBArticle.getGroupId();
    Date oldCreateDate = oldKBArticle.getCreateDate();
    long oldRootResourcePrimKey = oldKBArticle.getRootResourcePrimKey();
    long oldParentResourcePrimKey = oldKBArticle.getParentResourcePrimKey();
    int oldVersion = oldKBArticle.getVersion();
    double oldPriority = oldKBArticle.getPriority();
    int oldViewCount = oldKBArticle.getViewCount();
    int oldStatus = oldKBArticle.getStatus();

    KBArticle kbArticle = null;

    if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
      long kbArticleId = counterLocalService.increment();

      kbArticle = kbArticlePersistence.create(kbArticleId);
      version = oldVersion + 1;
    } else {
      kbArticle = oldKBArticle;
      version = oldVersion;
    }

    if (oldStatus == WorkflowConstants.STATUS_PENDING) {
      status = WorkflowConstants.STATUS_PENDING;
    }

    kbArticle.setResourcePrimKey(oldResourcePrimKey);
    kbArticle.setGroupId(oldGroupId);
    kbArticle.setCompanyId(user.getCompanyId());
    kbArticle.setUserId(user.getUserId());
    kbArticle.setUserName(user.getFullName());
    kbArticle.setCreateDate(oldCreateDate);
    kbArticle.setModifiedDate(serviceContext.getModifiedDate(null));
    kbArticle.setRootResourcePrimKey(oldRootResourcePrimKey);
    kbArticle.setParentResourcePrimKey(oldParentResourcePrimKey);
    kbArticle.setVersion(version);
    kbArticle.setTitle(title);
    kbArticle.setContent(content);
    kbArticle.setDescription(description);
    kbArticle.setPriority(oldPriority);
    kbArticle.setSections(StringUtil.merge(AdminUtil.escapeSections(sections)));
    kbArticle.setViewCount(oldViewCount);
    kbArticle.setLatest(true);
    kbArticle.setMain(false);
    kbArticle.setStatus(status);

    kbArticlePersistence.update(kbArticle, false);

    if (oldVersion < version) {
      oldKBArticle.setLatest(false);

      kbArticlePersistence.update(oldKBArticle, false);
    }

    // Resources

    if ((serviceContext.getGroupPermissions() != null)
        || (serviceContext.getGuestPermissions() != null)) {

      updateKBArticleResources(
          kbArticle, serviceContext.getGroupPermissions(), serviceContext.getGuestPermissions());
    }

    // Asset

    updateKBArticleAsset(
        userId, kbArticle, serviceContext.getAssetCategoryIds(), serviceContext.getAssetTagNames());

    // Attachments

    updateKBArticleAttachments(kbArticle, oldVersion, dirName, serviceContext);

    // Workflow

    WorkflowHandlerRegistryUtil.startWorkflowInstance(
        user.getCompanyId(),
        kbArticle.getGroupId(),
        userId,
        KBArticle.class.getName(),
        resourcePrimKey,
        kbArticle,
        serviceContext);

    return kbArticle;
  }
  @Override
  public void deleteKBArticle(KBArticle kbArticle) throws PortalException, SystemException {

    // Child kb articles

    List<KBArticle> siblingKBArticles =
        getSiblingKBArticles(
            kbArticle.getGroupId(),
            kbArticle.getResourcePrimKey(),
            WorkflowConstants.STATUS_ANY,
            QueryUtil.ALL_POS,
            QueryUtil.ALL_POS,
            new KBArticlePriorityComparator());

    for (KBArticle siblingKBArticle : siblingKBArticles) {
      deleteKBArticle(siblingKBArticle);
    }

    // Resources

    resourceLocalService.deleteResource(
        kbArticle.getCompanyId(),
        KBArticle.class.getName(),
        ResourceConstants.SCOPE_INDIVIDUAL,
        kbArticle.getResourcePrimKey());

    // KB articles

    kbArticlePersistence.removeByResourcePrimKey(kbArticle.getResourcePrimKey());

    // KB comments

    kbCommentLocalService.deleteKBComments(
        KBArticle.class.getName(), kbArticle.getResourcePrimKey());

    // Asset

    deleteAssets(kbArticle);

    // Ratings

    ratingsStatsLocalService.deleteStats(KBArticle.class.getName(), kbArticle.getResourcePrimKey());

    // Social

    socialActivityLocalService.deleteActivities(
        KBArticle.class.getName(), kbArticle.getResourcePrimKey());

    // Indexer

    Indexer indexer = IndexerRegistryUtil.getIndexer(KBArticle.class);

    indexer.delete(kbArticle);

    // Attachments

    deleteKBArticleAttachments(kbArticle);

    // Subscriptions

    deleteSubscriptions(kbArticle);

    // Workflow

    workflowInstanceLinkLocalService.deleteWorkflowInstanceLinks(
        kbArticle.getCompanyId(), kbArticle.getGroupId(),
        KBArticle.class.getName(), kbArticle.getResourcePrimKey());
  }
  protected void notifySubscribers(KBArticle kbArticle, ServiceContext serviceContext)
      throws PortalException, SystemException {

    if (Validator.isNull(serviceContext.getLayoutFullURL())) {
      return;
    }

    PortletPreferences preferences = ServiceContextUtil.getPortletPreferences(serviceContext);

    if (preferences == null) {
      long ownerId = kbArticle.getGroupId();
      int ownerType = PortletKeys.PREFS_OWNER_TYPE_GROUP;
      long plid = PortletKeys.PREFS_PLID_SHARED;
      String portletId = PortletKeys.KNOWLEDGE_BASE_ADMIN;
      String defaultPreferences = null;

      preferences =
          portletPreferencesLocalService.getPreferences(
              kbArticle.getCompanyId(), ownerId, ownerType, plid, portletId, defaultPreferences);
    }

    if (serviceContext.isCommandAdd() && !AdminUtil.getEmailKBArticleAddedEnabled(preferences)) {

      return;
    }

    if (serviceContext.isCommandUpdate()
        && !AdminUtil.getEmailKBArticleUpdatedEnabled(preferences)) {

      return;
    }

    String fromName = AdminUtil.getEmailFromName(preferences);
    String fromAddress = AdminUtil.getEmailFromAddress(preferences);

    String kbArticleContent =
        StringUtil.replace(
            kbArticle.getContent(),
            new String[] {"href=\"/", "src=\"/"},
            new String[] {
              "href=\"" + serviceContext.getPortalURL() + "/",
              "src=\"" + serviceContext.getPortalURL() + "/"
            });

    Map<String, String> kbArticleDiffs = getEmailKBArticleDiffs(kbArticle);

    for (String key : kbArticleDiffs.keySet()) {
      String value =
          StringUtil.replace(
              kbArticleDiffs.get(key),
              new String[] {"href=\"/", "src=\"/"},
              new String[] {
                "href=\"" + serviceContext.getPortalURL() + "/",
                "src=\"" + serviceContext.getPortalURL() + "/"
              });

      kbArticleDiffs.put(key, value);
    }

    String subject = null;
    String body = null;

    if (serviceContext.isCommandAdd()) {
      subject = AdminUtil.getEmailKBArticleAddedSubject(preferences);
      body = AdminUtil.getEmailKBArticleUpdatedBody(preferences);
    } else {
      subject = AdminUtil.getEmailKBArticleUpdatedSubject(preferences);
      body = AdminUtil.getEmailKBArticleUpdatedBody(preferences);
    }

    SubscriptionSender subscriptionSender = new AdminSubscriptionSender(kbArticle, serviceContext);

    subscriptionSender.setBody(body);
    subscriptionSender.setCompanyId(kbArticle.getCompanyId());
    subscriptionSender.setContextAttributes(
        "[$ARTICLE_CONTENT$]",
        kbArticleContent,
        "[$ARTICLE_CONTENT_DIFF$]",
        kbArticleDiffs.get("content"),
        "[$ARTICLE_TITLE$]",
        kbArticle.getTitle(),
        "[$ARTICLE_TITLE_DIFF$]",
        kbArticleDiffs.get("title"));
    subscriptionSender.setContextUserPrefix("ARTICLE");
    subscriptionSender.setFrom(fromAddress, fromName);
    subscriptionSender.setHtmlFormat(true);
    subscriptionSender.setMailId("kb_article", kbArticle.getKbArticleId());
    subscriptionSender.setPortletId(serviceContext.getPortletId());
    subscriptionSender.setReplyToAddress(fromAddress);
    subscriptionSender.setScopeGroupId(kbArticle.getGroupId());
    subscriptionSender.setSubject(subject);
    subscriptionSender.setUserId(kbArticle.getUserId());

    subscriptionSender.addPersistedSubscribers(KBArticle.class.getName(), kbArticle.getGroupId());
    subscriptionSender.addPersistedSubscribers(
        KBArticle.class.getName(), kbArticle.getResourcePrimKey());

    while (!kbArticle.isRoot()) {
      kbArticle =
          getLatestKBArticle(
              kbArticle.getParentResourcePrimKey(), WorkflowConstants.STATUS_APPROVED);

      subscriptionSender.addPersistedSubscribers(
          KBArticle.class.getName(), kbArticle.getResourcePrimKey());
    }

    subscriptionSender.flushNotificationsAsync();
  }
 public static long[] getGroupIds(Group companyGroup, KBArticle kbArticle) {
   return new long[] {kbArticle.getGroupId(), companyGroup.getGroupId()};
 }
  protected void notifySubscribers(KBComment kbComment, ServiceContext serviceContext)
      throws PortalException {

    PortletPreferences preferences =
        portletPreferencesLocalService.getPreferences(
            kbComment.getCompanyId(),
            kbComment.getGroupId(),
            PortletKeys.PREFS_OWNER_TYPE_GROUP,
            PortletKeys.PREFS_PLID_SHARED,
            PortletKeys.KNOWLEDGE_BASE_ADMIN,
            null);

    if (!AdminUtil.isSuggestionStatusChangeNotificationEnabled(
        kbComment.getStatus(), preferences)) {

      return;
    }

    String fromName = AdminUtil.getEmailFromName(preferences, serviceContext.getCompanyId());
    String fromAddress = AdminUtil.getEmailFromAddress(preferences, kbComment.getCompanyId());

    String subject =
        AdminUtil.getEmailKBArticleSuggestionNotificationSubject(
            kbComment.getStatus(), preferences);
    String body =
        AdminUtil.getEmailKBArticleSuggestionNotificationBody(kbComment.getStatus(), preferences);

    KBArticle kbArticle =
        kbArticleLocalService.getLatestKBArticle(
            kbComment.getClassPK(), WorkflowConstants.STATUS_APPROVED);

    String kbArticleContent =
        StringUtil.replace(
            kbArticle.getContent(),
            new String[] {"href=\"/", "src=\"/"},
            new String[] {
              "href=\"" + serviceContext.getPortalURL() + "/",
              "src=\"" + serviceContext.getPortalURL() + "/"
            });

    SubscriptionSender subscriptionSender = new AdminSubscriptionSender(kbArticle, serviceContext);

    subscriptionSender.setBody(body);
    subscriptionSender.setCompanyId(kbArticle.getCompanyId());
    subscriptionSender.setContextAttribute("[$ARTICLE_CONTENT$]", kbArticleContent, false);
    subscriptionSender.setContextAttribute("[$ARTICLE_TITLE$]", kbArticle.getTitle(), false);
    subscriptionSender.setContextAttribute("[$COMMENT_CONTENT$]", kbComment.getContent(), false);
    subscriptionSender.setContextUserPrefix("ARTICLE");
    subscriptionSender.setFrom(fromAddress, fromName);
    subscriptionSender.setHtmlFormat(true);
    subscriptionSender.setMailId("kb_article", kbArticle.getKbArticleId());
    subscriptionSender.setPortletId(serviceContext.getPortletId());
    subscriptionSender.setReplyToAddress(fromAddress);
    subscriptionSender.setScopeGroupId(kbArticle.getGroupId());
    subscriptionSender.setSubject(subject);
    subscriptionSender.setUserId(kbArticle.getUserId());

    User user = userLocalService.getUser(kbComment.getUserId());

    subscriptionSender.addRuntimeSubscribers(user.getEmailAddress(), user.getFullName());

    subscriptionSender.flushNotificationsAsync();
  }