@Override
  protected void doExportStagedModel(PortletDataContext portletDataContext, KBArticle kbArticle)
      throws Exception {

    if (kbArticle.getParentResourcePrimKey() != KBFolderConstants.DEFAULT_PARENT_FOLDER_ID) {

      long kbArticleClassNameId = PortalUtil.getClassNameId(KBArticleConstants.getClassName());

      if (kbArticle.getParentResourceClassNameId() == kbArticleClassNameId) {

        KBArticle parentKBArticle =
            KBArticleLocalServiceUtil.getLatestKBArticle(
                kbArticle.getParentResourcePrimKey(), WorkflowConstants.STATUS_APPROVED);

        StagedModelDataHandlerUtil.exportReferenceStagedModel(
            portletDataContext,
            kbArticle,
            parentKBArticle,
            PortletDataContext.REFERENCE_TYPE_PARENT);
      } else {
        KBFolder parentKBFolder =
            KBFolderLocalServiceUtil.getKBFolder(kbArticle.getParentResourcePrimKey());

        StagedModelDataHandlerUtil.exportReferenceStagedModel(
            portletDataContext,
            kbArticle,
            parentKBFolder,
            PortletDataContext.REFERENCE_TYPE_PARENT);
      }
    }

    Element kbArticleElement = portletDataContext.getExportDataElement(kbArticle);

    exportKBArticleAttachments(portletDataContext, kbArticleElement, kbArticle);

    portletDataContext.addClassedModel(
        kbArticleElement, ExportImportPathUtil.getModelPath(kbArticle), kbArticle);
  }
  protected void updatePermissionFields(long resourcePrimKey, long parentResourcePrimKey)
      throws PortalException, SystemException {

    // See KBArticlePermission#contains

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

    if (kbArticle.getParentResourcePrimKey() == parentResourcePrimKey) {
      return;
    }

    long rootResourcePrimKey = getRootResourcePrimKey(resourcePrimKey, parentResourcePrimKey);

    if (kbArticle.getRootResourcePrimKey() == rootResourcePrimKey) {
      return;
    }

    // Sync database

    List<KBArticle> kbArticles1 =
        getKBArticleAndAllDescendants(resourcePrimKey, WorkflowConstants.STATUS_ANY, null);

    for (KBArticle kbArticle1 : kbArticles1) {
      List<KBArticle> kbArticles2 =
          getKBArticleVersions(
              kbArticle1.getResourcePrimKey(),
              WorkflowConstants.STATUS_ANY,
              QueryUtil.ALL_POS,
              QueryUtil.ALL_POS,
              null);

      for (KBArticle kbArticle2 : kbArticles2) {
        kbArticle2.setRootResourcePrimKey(rootResourcePrimKey);

        kbArticlePersistence.update(kbArticle2, false);
      }
    }

    // Sync indexed permission fields

    SearchEngineUtil.updatePermissionFields(
        KBArticle.class.getName(), String.valueOf(resourcePrimKey));
  }
  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;
  }
  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();
  }
  @Override
  protected void doImportStagedModel(PortletDataContext portletDataContext, KBArticle kbArticle)
      throws Exception {

    long userId = portletDataContext.getUserId(kbArticle.getUserUuid());

    if (kbArticle.getParentResourcePrimKey() != KBFolderConstants.DEFAULT_PARENT_FOLDER_ID) {

      if (kbArticle.getClassNameId() == kbArticle.getParentResourceClassNameId()) {

        StagedModelDataHandlerUtil.importReferenceStagedModels(
            portletDataContext, kbArticle, KBArticle.class);
      } else {
        StagedModelDataHandlerUtil.importReferenceStagedModels(
            portletDataContext, kbArticle, KBFolder.class);
      }
    }

    Map<Long, Long> kbArticleResourcePrimKeys =
        (Map<Long, Long>) portletDataContext.getNewPrimaryKeysMap(KBArticle.class);

    long parentResourcePrimKey =
        MapUtil.getLong(
            kbArticleResourcePrimKeys,
            kbArticle.getParentResourcePrimKey(),
            KBFolderConstants.DEFAULT_PARENT_FOLDER_ID);

    long resourcePrimaryKey =
        MapUtil.getLong(kbArticleResourcePrimKeys, kbArticle.getResourcePrimKey(), 0);

    if (parentResourcePrimKey == KBFolderConstants.DEFAULT_PARENT_FOLDER_ID) {

      Map<Long, Long> kbFolderResourcePrimKeys =
          (Map<Long, Long>) portletDataContext.getNewPrimaryKeysMap(KBFolder.class);

      parentResourcePrimKey =
          MapUtil.getLong(
              kbFolderResourcePrimKeys,
              kbArticle.getParentResourcePrimKey(),
              KBFolderConstants.DEFAULT_PARENT_FOLDER_ID);
    }

    String[] sections = AdminUtil.unescapeSections(kbArticle.getSections());

    ServiceContext serviceContext = portletDataContext.createServiceContext(kbArticle);

    KBArticle importedKBArticle = null;

    if (portletDataContext.isDataStrategyMirror()) {
      KBArticle existingKBArticle =
          KBArticleUtil.fetchByR_V(resourcePrimaryKey, kbArticle.getVersion());

      if (existingKBArticle == null) {
        existingKBArticle =
            fetchStagedModelByUuidAndGroupId(
                kbArticle.getUuid(), portletDataContext.getScopeGroupId());
      }

      if (existingKBArticle == null) {
        serviceContext.setUuid(kbArticle.getUuid());

        existingKBArticle =
            KBArticleLocalServiceUtil.fetchLatestKBArticle(
                resourcePrimaryKey, WorkflowConstants.STATUS_ANY);

        if (existingKBArticle == null) {
          importedKBArticle =
              KBArticleLocalServiceUtil.addKBArticle(
                  userId,
                  kbArticle.getParentResourceClassNameId(),
                  parentResourcePrimKey,
                  kbArticle.getTitle(),
                  kbArticle.getUrlTitle(),
                  kbArticle.getContent(),
                  kbArticle.getDescription(),
                  kbArticle.getSourceURL(),
                  sections,
                  null,
                  serviceContext);

          KBArticleLocalServiceUtil.updatePriority(
              importedKBArticle.getResourcePrimKey(), kbArticle.getPriority());
        } else {
          KBArticleLocalServiceUtil.updateKBArticle(
              userId,
              existingKBArticle.getResourcePrimKey(),
              kbArticle.getTitle(),
              kbArticle.getContent(),
              kbArticle.getDescription(),
              kbArticle.getSourceURL(),
              sections,
              null,
              null,
              serviceContext);

          KBArticleLocalServiceUtil.moveKBArticle(
              userId,
              existingKBArticle.getResourcePrimKey(),
              existingKBArticle.getParentResourceClassNameId(),
              parentResourcePrimKey,
              kbArticle.getPriority());

          importedKBArticle =
              KBArticleLocalServiceUtil.getLatestKBArticle(
                  existingKBArticle.getResourcePrimKey(), WorkflowConstants.STATUS_APPROVED);
        }
      } else {
        importedKBArticle = existingKBArticle;
      }
    } else {
      importedKBArticle =
          KBArticleLocalServiceUtil.addKBArticle(
              userId,
              kbArticle.getParentResourceClassNameId(),
              parentResourcePrimKey,
              kbArticle.getTitle(),
              kbArticle.getUrlTitle(),
              kbArticle.getContent(),
              kbArticle.getDescription(),
              kbArticle.getSourceURL(),
              sections,
              null,
              serviceContext);

      KBArticleLocalServiceUtil.updatePriority(
          importedKBArticle.getResourcePrimKey(), kbArticle.getPriority());
    }

    importKBArticleAttachments(portletDataContext, kbArticle, importedKBArticle);

    portletDataContext.importClassedModel(kbArticle, importedKBArticle);

    if (!kbArticle.isMain()) {
      kbArticleResourcePrimKeys.put(
          kbArticle.getResourcePrimKey(), importedKBArticle.getResourcePrimKey());
    }
  }