@Override
  public void exportStagedModel(PortletDataContext portletDataContext, T stagedModel)
      throws PortletDataException {

    super.exportStagedModel(portletDataContext, stagedModel);

    boolean updateLastPublishDate =
        MapUtil.getBoolean(
            portletDataContext.getParameterMap(), PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);

    if (ExportImportThreadLocal.isStagingInProcess()
        && updateLastPublishDate
        && (stagedModel instanceof StagedGroupedModel)) {

      ExportImportProcessCallbackRegistryUtil.registerCallback(
          new UpdateStagedModelLastPublishDateCallable(
              stagedModel, portletDataContext.getDateRange()));
    }
  }
  public void exportPortletData(
      PortletDataContext portletDataContext,
      Portlet portlet,
      Layout layout,
      javax.portlet.PortletPreferences jxPortletPreferences,
      Element parentElement)
      throws Exception {

    if (portlet == null) {
      return;
    }

    PortletDataHandler portletDataHandler = portlet.getPortletDataHandlerInstance();

    if ((portletDataHandler == null) || portletDataHandler.isDataPortletInstanceLevel()) {

      return;
    }

    Group group = layout.getGroup();

    if (group.isStagingGroup()) {
      group = group.getLiveGroup();
    }

    String portletId = portlet.getPortletId();

    if (ExportImportThreadLocal.isStagingInProcess() && !group.isStagedPortlet(portletId)) {

      if (_log.isDebugEnabled()) {
        _log.debug(
            "Not exporting data for " + portletId + " because it is configured not to be staged");
      }

      return;
    }

    if (_log.isDebugEnabled()) {
      _log.debug("Exporting data for " + portletId);
    }

    StringBundler sb = new StringBundler(4);

    sb.append(ExportImportPathUtil.getPortletPath(portletDataContext, portletId));
    sb.append(StringPool.SLASH);

    if (portlet.isPreferencesUniquePerLayout()) {
      sb.append(layout.getPlid());
    } else {
      sb.append(portletDataContext.getScopeGroupId());
    }

    sb.append("/portlet-data.xml");

    String path = sb.toString();

    if (portletDataContext.hasPrimaryKey(String.class, path)) {
      return;
    }

    Date originalStartDate = portletDataContext.getStartDate();

    Date portletLastPublishDate =
        ExportImportDateUtil.getLastPublishDate(portletDataContext, jxPortletPreferences);

    portletDataContext.setStartDate(portletLastPublishDate);

    long groupId = portletDataContext.getGroupId();

    portletDataContext.setGroupId(portletDataContext.getScopeGroupId());

    portletDataContext.clearScopedPrimaryKeys();

    String data = null;

    try {
      data = portletDataHandler.exportData(portletDataContext, portletId, jxPortletPreferences);
    } catch (PortletDataException pde) {
      throw pde;
    } catch (Exception e) {
      throw new SystemException(e);
    } finally {
      portletDataContext.setGroupId(groupId);
      portletDataContext.setStartDate(originalStartDate);
    }

    if (Validator.isNull(data)) {
      if (_log.isDebugEnabled()) {
        _log.debug("Not exporting data for " + portletId + " because null data was returned");
      }

      return;
    }

    Element portletDataElement = parentElement.addElement("portlet-data");

    portletDataElement.addAttribute("path", path);

    portletDataContext.addZipEntry(path, data);

    boolean updateLastPublishDate =
        MapUtil.getBoolean(
            portletDataContext.getParameterMap(), PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);

    if (ExportImportThreadLocal.isStagingInProcess() && updateLastPublishDate) {

      DateRange adjustedDateRange =
          new DateRange(portletLastPublishDate, portletDataContext.getEndDate());

      ExportImportProcessCallbackRegistryUtil.registerCallback(
          new UpdatePortletLastPublishDateCallable(
              adjustedDateRange,
              portletDataContext.getEndDate(),
              portletDataContext.getGroupId(),
              layout.getPlid(),
              portletId));
    }
  }
  protected File doExport(PortletDataContext portletDataContext, long[] layoutIds)
      throws Exception {

    Map<String, String[]> parameterMap = portletDataContext.getParameterMap();

    boolean ignoreLastPublishDate =
        MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.IGNORE_LAST_PUBLISH_DATE);
    boolean layoutSetPrototypeSettings =
        MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.LAYOUT_SET_PROTOTYPE_SETTINGS);
    boolean layoutSetSettings =
        MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.LAYOUT_SET_SETTINGS);
    boolean logo = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.LOGO);
    boolean permissions = MapUtil.getBoolean(parameterMap, PortletDataHandlerKeys.PERMISSIONS);

    if (_log.isDebugEnabled()) {
      _log.debug("Export permissions " + permissions);
    }

    LayoutSet layoutSet =
        _layoutSetLocalService.getLayoutSet(
            portletDataContext.getGroupId(), portletDataContext.isPrivateLayout());

    long companyId = layoutSet.getCompanyId();
    long defaultUserId = _userLocalService.getDefaultUserId(companyId);

    ServiceContext serviceContext = ServiceContextThreadLocal.popServiceContext();

    if (serviceContext == null) {
      serviceContext = new ServiceContext();
    }

    serviceContext.setCompanyId(companyId);
    serviceContext.setSignedIn(false);
    serviceContext.setUserId(defaultUserId);

    serviceContext.setAttribute("exporting", Boolean.TRUE);

    long layoutSetBranchId = MapUtil.getLong(parameterMap, "layoutSetBranchId");

    serviceContext.setAttribute("layoutSetBranchId", layoutSetBranchId);

    ServiceContextThreadLocal.pushServiceContext(serviceContext);

    if (ignoreLastPublishDate) {
      portletDataContext.setEndDate(null);
      portletDataContext.setStartDate(null);
    }

    StopWatch stopWatch = new StopWatch();

    stopWatch.start();

    Document document = SAXReaderUtil.createDocument();

    Element rootElement = document.addElement("root");

    portletDataContext.setExportDataRootElement(rootElement);

    Element headerElement = rootElement.addElement("header");

    headerElement.addAttribute(
        "available-locales",
        StringUtil.merge(LanguageUtil.getAvailableLocales(portletDataContext.getScopeGroupId())));
    headerElement.addAttribute("build-number", String.valueOf(ReleaseInfo.getBuildNumber()));
    headerElement.addAttribute("export-date", Time.getRFC822());

    if (portletDataContext.hasDateRange()) {
      headerElement.addAttribute("start-date", String.valueOf(portletDataContext.getStartDate()));
      headerElement.addAttribute("end-date", String.valueOf(portletDataContext.getEndDate()));
    }

    headerElement.addAttribute("company-id", String.valueOf(portletDataContext.getCompanyId()));
    headerElement.addAttribute(
        "company-group-id", String.valueOf(portletDataContext.getCompanyGroupId()));
    headerElement.addAttribute("group-id", String.valueOf(portletDataContext.getGroupId()));
    headerElement.addAttribute(
        "user-personal-site-group-id",
        String.valueOf(portletDataContext.getUserPersonalSiteGroupId()));
    headerElement.addAttribute(
        "private-layout", String.valueOf(portletDataContext.isPrivateLayout()));

    Group group = layoutSet.getGroup();

    String type = "layout-set";

    if (group.isLayoutPrototype()) {
      type = "layout-prototype";

      LayoutPrototype layoutPrototype =
          _layoutPrototypeLocalService.getLayoutPrototype(group.getClassPK());

      headerElement.addAttribute("type-uuid", layoutPrototype.getUuid());

      layoutIds =
          ExportImportHelperUtil.getAllLayoutIds(
              portletDataContext.getGroupId(), portletDataContext.isPrivateLayout());
    } else if (group.isLayoutSetPrototype()) {
      type = "layout-set-prototype";

      LayoutSetPrototype layoutSetPrototype =
          _layoutSetPrototypeLocalService.getLayoutSetPrototype(group.getClassPK());

      headerElement.addAttribute("type-uuid", layoutSetPrototype.getUuid());
    }

    headerElement.addAttribute("type", type);

    LayoutSetBranch layoutSetBranch =
        _layoutSetBranchLocalService.fetchLayoutSetBranch(layoutSetBranchId);

    if (logo) {
      Image image = null;

      if (layoutSetBranch != null) {
        image = _imageLocalService.getImage(layoutSetBranch.getLogoId());
      } else {
        image = _imageLocalService.getImage(layoutSet.getLogoId());
      }

      if ((image != null) && (image.getTextObj() != null)) {
        String logoPath = ExportImportPathUtil.getRootPath(portletDataContext);

        logoPath += "/logo";

        headerElement.addAttribute("logo-path", logoPath);

        portletDataContext.addZipEntry(logoPath, image.getTextObj());
      }
    }

    String layoutSetPrototypeUuid = layoutSet.getLayoutSetPrototypeUuid();

    if (layoutSetPrototypeSettings && Validator.isNotNull(layoutSetPrototypeUuid)) {

      LayoutSetPrototype layoutSetPrototype =
          _layoutSetPrototypeLocalService.getLayoutSetPrototypeByUuidAndCompanyId(
              layoutSetPrototypeUuid, companyId);

      headerElement.addAttribute("layout-set-prototype-uuid", layoutSetPrototypeUuid);
      headerElement.addAttribute(
          "layout-set-prototype-name", layoutSetPrototype.getName(LocaleUtil.getDefault()));
    }

    Element missingReferencesElement = rootElement.addElement("missing-references");

    portletDataContext.setMissingReferencesElement(missingReferencesElement);

    if (layoutSetBranch != null) {
      _themeExporter.exportTheme(portletDataContext, layoutSetBranch);
    } else {
      _themeExporter.exportTheme(portletDataContext, layoutSet);
    }

    if (layoutSetSettings) {
      Element settingsElement = headerElement.addElement("settings");

      if (layoutSetBranch != null) {
        settingsElement.addCDATA(layoutSetBranch.getSettings());
      } else {
        settingsElement.addCDATA(layoutSet.getSettings());
      }
    }

    Map<String, Object[]> portletIds = new LinkedHashMap<>();

    List<Layout> layouts =
        _layoutLocalService.getLayouts(
            portletDataContext.getGroupId(), portletDataContext.isPrivateLayout());

    if (group.isStagingGroup()) {
      group = group.getLiveGroup();
    }

    // Collect data portlets

    for (Portlet portlet : ExportImportHelperUtil.getDataSiteLevelPortlets(companyId)) {

      String portletId = portlet.getRootPortletId();

      if (ExportImportThreadLocal.isStagingInProcess() && !group.isStagedPortlet(portletId)) {

        continue;
      }

      // Calculate the amount of exported data

      if (BackgroundTaskThreadLocal.hasBackgroundTask()) {
        PortletDataHandler portletDataHandler = portlet.getPortletDataHandlerInstance();

        portletDataHandler.prepareManifestSummary(portletDataContext);
      }

      // Add portlet ID to exportable portlets list

      portletIds.put(
          PortletPermissionUtil.getPrimaryKey(0, portletId),
          new Object[] {
            portletId,
            LayoutConstants.DEFAULT_PLID,
            portletDataContext.getGroupId(),
            StringPool.BLANK,
            StringPool.BLANK
          });

      if (!portlet.isScopeable()) {
        continue;
      }

      // Scoped data

      for (Layout layout : layouts) {
        if (!ArrayUtil.contains(layoutIds, layout.getLayoutId())
            || !layout.isTypePortlet()
            || !layout.hasScopeGroup()) {

          continue;
        }

        Group scopeGroup = layout.getScopeGroup();

        portletIds.put(
            PortletPermissionUtil.getPrimaryKey(layout.getPlid(), portlet.getPortletId()),
            new Object[] {
              portlet.getPortletId(),
              layout.getPlid(),
              scopeGroup.getGroupId(),
              StringPool.BLANK,
              layout.getUuid()
            });
      }
    }

    // Collect layout portlets

    for (Layout layout : layouts) {
      getLayoutPortlets(portletDataContext, layoutIds, portletIds, layout);
    }

    if (BackgroundTaskThreadLocal.hasBackgroundTask()) {
      ManifestSummary manifestSummary = portletDataContext.getManifestSummary();

      PortletDataHandlerStatusMessageSenderUtil.sendStatusMessage(
          "layout", ArrayUtil.toStringArray(portletIds.keySet()), manifestSummary);

      manifestSummary.resetCounters();
    }

    // Export actual data

    portletDataContext.addDeletionSystemEventStagedModelTypes(new StagedModelType(Layout.class));

    // Force to always have a layout group element

    portletDataContext.getExportDataGroupElement(Layout.class);

    for (Layout layout : layouts) {
      exportLayout(portletDataContext, layoutIds, layout);
    }

    Element portletsElement = rootElement.addElement("portlets");

    Element servicesElement = rootElement.addElement("services");

    long previousScopeGroupId = portletDataContext.getScopeGroupId();

    for (Map.Entry<String, Object[]> portletIdsEntry : portletIds.entrySet()) {

      Object[] portletObjects = portletIdsEntry.getValue();

      String portletId = null;
      long plid = LayoutConstants.DEFAULT_PLID;
      long scopeGroupId = 0;
      String scopeType = StringPool.BLANK;
      String scopeLayoutUuid = null;

      if (portletObjects.length == 4) {
        portletId = (String) portletIdsEntry.getValue()[0];
        plid = (Long) portletIdsEntry.getValue()[1];
        scopeGroupId = (Long) portletIdsEntry.getValue()[2];
        scopeLayoutUuid = (String) portletIdsEntry.getValue()[3];
      } else {
        portletId = (String) portletIdsEntry.getValue()[0];
        plid = (Long) portletIdsEntry.getValue()[1];
        scopeGroupId = (Long) portletIdsEntry.getValue()[2];
        scopeType = (String) portletIdsEntry.getValue()[3];
        scopeLayoutUuid = (String) portletIdsEntry.getValue()[4];
      }

      Layout layout = _layoutLocalService.fetchLayout(plid);

      if (layout == null) {
        layout = new LayoutImpl();

        layout.setCompanyId(companyId);
        layout.setGroupId(portletDataContext.getGroupId());
      }

      portletDataContext.setPlid(plid);
      portletDataContext.setOldPlid(plid);
      portletDataContext.setPortletId(portletId);
      portletDataContext.setScopeGroupId(scopeGroupId);
      portletDataContext.setScopeType(scopeType);
      portletDataContext.setScopeLayoutUuid(scopeLayoutUuid);

      Map<String, Boolean> exportPortletControlsMap =
          ExportImportHelperUtil.getExportPortletControlsMap(
              companyId, portletId, parameterMap, type);

      try {
        _exportImportLifecycleManager.fireExportImportLifecycleEvent(
            EVENT_PORTLET_EXPORT_STARTED,
            getProcessFlag(),
            PortletDataContextFactoryUtil.clonePortletDataContext(portletDataContext));

        _portletExportController.exportPortlet(
            portletDataContext,
            layout,
            portletsElement,
            permissions,
            exportPortletControlsMap.get(PortletDataHandlerKeys.PORTLET_ARCHIVED_SETUPS),
            exportPortletControlsMap.get(PortletDataHandlerKeys.PORTLET_DATA),
            exportPortletControlsMap.get(PortletDataHandlerKeys.PORTLET_SETUP),
            exportPortletControlsMap.get(PortletDataHandlerKeys.PORTLET_USER_PREFERENCES));
        _portletExportController.exportService(
            portletDataContext,
            servicesElement,
            exportPortletControlsMap.get(PortletDataHandlerKeys.PORTLET_SETUP));

        _exportImportLifecycleManager.fireExportImportLifecycleEvent(
            EVENT_PORTLET_EXPORT_SUCCEEDED,
            getProcessFlag(),
            PortletDataContextFactoryUtil.clonePortletDataContext(portletDataContext));
      } catch (Throwable t) {
        _exportImportLifecycleManager.fireExportImportLifecycleEvent(
            EVENT_PORTLET_EXPORT_FAILED,
            getProcessFlag(),
            PortletDataContextFactoryUtil.clonePortletDataContext(portletDataContext),
            t);

        throw t;
      }
    }

    portletDataContext.setScopeGroupId(previousScopeGroupId);

    _portletExportController.exportAssetLinks(portletDataContext);
    _portletExportController.exportExpandoTables(portletDataContext);
    _portletExportController.exportLocks(portletDataContext);

    _deletionSystemEventExporter.exportDeletionSystemEvents(portletDataContext);

    if (permissions) {
      _permissionExporter.exportPortletDataPermissions(portletDataContext);
    }

    ExportImportHelperUtil.writeManifestSummary(document, portletDataContext.getManifestSummary());

    if (_log.isInfoEnabled()) {
      _log.info("Exporting layouts takes " + stopWatch.getTime() + " ms");
    }

    boolean updateLastPublishDate =
        MapUtil.getBoolean(
            portletDataContext.getParameterMap(), PortletDataHandlerKeys.UPDATE_LAST_PUBLISH_DATE);

    if (ExportImportThreadLocal.isStagingInProcess() && updateLastPublishDate) {

      ExportImportProcessCallbackRegistryUtil.registerCallback(
          new UpdateLayoutSetLastPublishDateCallable(
              portletDataContext.getDateRange(),
              portletDataContext.getGroupId(),
              portletDataContext.isPrivateLayout()));
    }

    portletDataContext.addZipEntry("/manifest.xml", document.formattedString());

    ZipWriter zipWriter = portletDataContext.getZipWriter();

    return zipWriter.getFile();
  }