/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.upgrade; import com.liferay.exportimport.kernel.staging.StagingUtil; import com.liferay.portal.kernel.dao.jdbc.AutoBatchPreparedStatementUtil; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.LayoutTypePortletConstants; import com.liferay.portal.kernel.model.PortletConstants; import com.liferay.portal.kernel.model.PortletInstance; import com.liferay.portal.kernel.model.ResourceConstants; import com.liferay.portal.kernel.service.permission.PortletPermissionUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.LoggingTimer; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.UnicodeProperties; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; /** * @author Brian Wing Shun Chan */ public abstract class BaseUpgradePortletId extends UpgradeProcess { @Override protected void doUpgrade() throws Exception { upgradeInstanceablePortletIds(); upgradeUninstanceablePortletIds(); } protected String getNewTypeSettings( String typeSettings, String oldRootPortletId, String newRootPortletId) { UnicodeProperties typeSettingsProperties = new UnicodeProperties(true); typeSettingsProperties.fastLoad(typeSettings); String oldStagingPortletId = StagingUtil.getStagedPortletId( oldRootPortletId); if (!typeSettingsProperties.containsKey(oldStagingPortletId)) { return typeSettings; } String newStagingPortletId = StagingUtil.getStagedPortletId( newRootPortletId); String value = typeSettingsProperties.remove(oldStagingPortletId); typeSettingsProperties.setProperty(newStagingPortletId, value); return typeSettingsProperties.toString(); } protected String getNewTypeSettings( String typeSettings, String oldRootPortletId, String newRootPortletId, boolean exactMatch) { UnicodeProperties typeSettingsProperties = new UnicodeProperties(true); typeSettingsProperties.fastLoad(typeSettings); List<String> columnIds = _getLayoutColumnIds(typeSettingsProperties); columnIds.addAll(_getNestedPortletColumnIds(typeSettingsProperties)); for (String columnId : columnIds) { if (!typeSettingsProperties.containsKey(columnId)) { continue; } String[] portletIds = StringUtil.split( typeSettingsProperties.getProperty(columnId)); for (int j = 0; j < portletIds.length; j++) { String portletId = portletIds[j]; if (exactMatch) { if (portletId.equals(oldRootPortletId)) { portletIds[j] = newRootPortletId; } continue; } String rootPortletId = PortletConstants.getRootPortletId( portletId); if (!rootPortletId.equals(oldRootPortletId)) { continue; } long userId = PortletConstants.getUserId(portletId); String instanceId = PortletConstants.getInstanceId(portletId); portletIds[j] = PortletConstants.assemblePortletId( newRootPortletId, userId, instanceId); } typeSettingsProperties.setProperty( columnId, StringUtil.merge(portletIds).concat(StringPool.COMMA)); } return typeSettingsProperties.toString(); } /** * @deprecated As of 7.0.0 */ @Deprecated protected String getNewTypeSettings( String typeSettings, String oldRootPortletId, String newRootPortletId, List<String> columnIds, boolean exactMatch) { UnicodeProperties typeSettingsProperties = new UnicodeProperties(true); typeSettingsProperties.fastLoad(typeSettings); return getNewTypeSettings( typeSettings, oldRootPortletId, newRootPortletId, exactMatch); } protected String[][] getRenamePortletIdsArray() { return new String[0][0]; } protected String getTypeSettingsCriteria(String portletId) { StringBundler sb = new StringBundler(23); sb.append("typeSettings like '%="); sb.append(portletId); sb.append(",%' OR typeSettings like '%="); sb.append(portletId); sb.append("\n%' OR typeSettings like '%="); sb.append(portletId); sb.append("%' OR typeSettings like '%,"); sb.append(portletId); sb.append(",%' OR typeSettings like '%,"); sb.append(portletId); sb.append("\n%' OR typeSettings like '%,"); sb.append(portletId); sb.append("%' OR typeSettings like '%="); sb.append(portletId); sb.append("_INSTANCE_%' OR typeSettings like '%,"); sb.append(portletId); sb.append("_INSTANCE_%' OR typeSettings like '%="); sb.append(portletId); sb.append("_USER_%' OR typeSettings like '%,"); sb.append(portletId); sb.append("_USER_%' OR typeSettings like '%"); sb.append(StagingUtil.getStagedPortletId(portletId)); sb.append("=%'"); return sb.toString(); } protected String[] getUninstanceablePortletIds() { return new String[0]; } protected void updateGroup(long groupId, String typeSettings) throws Exception { String sql = "update Group_ set typeSettings = ? where groupId = " + groupId; try (PreparedStatement ps = connection.prepareStatement(sql)) { ps.setString(1, typeSettings); ps.executeUpdate(); } catch (SQLException sqle) { if (_log.isWarnEnabled()) { _log.warn(sqle, sqle); } } } protected void updateGroup(String oldRootPortletId, String newRootPortletId) throws Exception { String sql = "select groupId, typeSettings from Group_ where " + getTypeSettingsCriteria(oldRootPortletId); try (PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { long groupId = rs.getLong("groupId"); String typeSettings = rs.getString("typeSettings"); String newTypeSettings = getNewTypeSettings( typeSettings, oldRootPortletId, newRootPortletId); updateGroup(groupId, newTypeSettings); } } } protected void updateInstanceablePortletPreferences( String oldRootPortletId, String newRootPortletId) throws Exception { StringBundler sb = new StringBundler(8); sb.append("select portletPreferencesId, portletId from "); sb.append("PortletPreferences where portletId = '"); sb.append(oldRootPortletId); sb.append("' OR portletId like '"); sb.append(oldRootPortletId); sb.append("_INSTANCE_%' OR portletId like '"); sb.append(oldRootPortletId); sb.append("_USER_%_INSTANCE_%'"); try (PreparedStatement ps1 = connection.prepareStatement(sb.toString()); PreparedStatement ps2 = AutoBatchPreparedStatementUtil.concurrentAutoBatch( connection, "update PortletPreferences set portletId = ? where " + "portletPreferencesId = ?"); ResultSet rs = ps1.executeQuery()) { while (rs.next()) { long portletPreferencesId = rs.getLong("portletPreferencesId"); String portletId = rs.getString("portletId"); String newPortletId = StringUtil.replaceFirst( portletId, oldRootPortletId, newRootPortletId); ps2.setString(1, newPortletId); ps2.setLong(2, portletPreferencesId); ps2.addBatch(); } ps2.executeBatch(); } } protected void updateLayout(long plid, String typeSettings) throws Exception { try (PreparedStatement ps = connection.prepareStatement( "update Layout set typeSettings = ? where plid = " + plid)) { ps.setString(1, typeSettings); ps.executeUpdate(); } catch (SQLException sqle) { if (_log.isWarnEnabled()) { _log.warn(sqle, sqle); } } } protected void updateLayout( long plid, String oldPortletId, String newPortletId) throws Exception { try (PreparedStatement ps = connection.prepareStatement( "select typeSettings from Layout where plid = " + plid); ResultSet rs = ps.executeQuery()) { while (rs.next()) { String typeSettings = rs.getString("typeSettings"); String newTypeSettings = StringUtil.replace( typeSettings, oldPortletId, newPortletId); updateLayout(plid, newTypeSettings); } } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } } protected void updateLayoutRevision( long layoutRevisionId, String typeSettings) throws Exception { String sql = "update LayoutRevision set typeSettings = ? where " + "layoutRevisionId = " + layoutRevisionId; try (PreparedStatement ps = connection.prepareStatement(sql)) { ps.setString(1, typeSettings); ps.executeUpdate(); } catch (SQLException sqle) { if (_log.isWarnEnabled()) { _log.warn(sqle, sqle); } } } protected void updateLayoutRevisions( String oldRootPortletId, String newRootPortletId, boolean exactMatch) throws Exception { String sql = "select layoutRevisionId, typeSettings from LayoutRevision where " + getTypeSettingsCriteria(oldRootPortletId); try (PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { long layoutRevisionId = rs.getLong("layoutRevisionId"); String typeSettings = rs.getString("typeSettings"); String newTypeSettings = getNewTypeSettings( typeSettings, oldRootPortletId, newRootPortletId, exactMatch); updateLayoutRevision(layoutRevisionId, newTypeSettings); } } } protected void updateLayouts( String oldRootPortletId, String newRootPortletId, boolean exactMatch) throws Exception { String sql = "select plid, typeSettings from Layout where " + getTypeSettingsCriteria(oldRootPortletId); try (PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { long plid = rs.getLong("plid"); String typeSettings = rs.getString("typeSettings"); String newTypeSettings = getNewTypeSettings( typeSettings, oldRootPortletId, newRootPortletId, exactMatch); updateLayout(plid, newTypeSettings); } } } protected void updatePortlet( String oldRootPortletId, String newRootPortletId) throws Exception { try { updatePortletId(oldRootPortletId, newRootPortletId); updateResourceAction(oldRootPortletId, newRootPortletId); updateResourcePermission(oldRootPortletId, newRootPortletId, true); updateUserNotificationDelivery(oldRootPortletId, newRootPortletId); updateUserNotificationEvent(oldRootPortletId, newRootPortletId); updateInstanceablePortletPreferences( oldRootPortletId, newRootPortletId); } catch (Exception e) { if (_log.isWarnEnabled()) { _log.warn(e, e); } } } protected void updatePortletId( String oldRootPortletId, String newRootPortletId) throws Exception { runSQL( "update Portlet set portletId = '" + newRootPortletId + "' where portletId = '" + oldRootPortletId + "'"); } protected void updateResourceAction(String oldName, String newName) throws Exception { List<String> actionIds = new ArrayList<>(); try (PreparedStatement ps = connection.prepareStatement( "select actionId from ResourceAction where name = '" + oldName + "'"); ResultSet rs = ps.executeQuery()) { while (rs.next()) { actionIds.add(rs.getString("actionId")); } } if (actionIds.isEmpty()) { runSQL( "update ResourceAction set name = '" + newName + "' where name = '" + oldName + "'"); } else { StringBundler sb = new StringBundler(5 + 3 * actionIds.size()); sb.append("update ResourceAction set name = '"); sb.append(newName); sb.append("' where name = '"); sb.append(oldName); sb.append("' and actionId not in ("); for (int i = 0; i < actionIds.size(); i++) { sb.append("'"); sb.append(actionIds.get(i)); if (i == (actionIds.size() - 1)) { sb.append("')"); } else { sb.append("', "); } } runSQL(sb.toString()); runSQL("delete from ResourceAction where name = '" + oldName + "'"); } } protected void updateResourcePermission( String oldRootPortletId, String newRootPortletId, boolean updateName) throws Exception { StringBundler sb = new StringBundler(5); sb.append("select distinct primKey from ResourcePermission where "); sb.append("name = '"); sb.append(oldRootPortletId); sb.append("' and scope = "); sb.append(ResourceConstants.SCOPE_INDIVIDUAL); try (PreparedStatement ps1 = connection.prepareStatement(sb.toString()); PreparedStatement ps2 = AutoBatchPreparedStatementUtil.concurrentAutoBatch( connection, "update ResourcePermission set primKey = ? where primKey " + "= ?"); ResultSet rs = ps1.executeQuery()) { while (rs.next()) { String oldPrimKey = rs.getString("primKey"); int pos = oldPrimKey.indexOf(PortletConstants.LAYOUT_SEPARATOR); if (pos != -1) { long plid = GetterUtil.getLong( oldPrimKey.substring(0, pos)); String portletId = oldPrimKey.substring( pos + PortletConstants.LAYOUT_SEPARATOR.length()); String instanceId = PortletConstants.getInstanceId( portletId); long userId = PortletConstants.getUserId(portletId); String newPortletId = PortletConstants.assemblePortletId( newRootPortletId, userId, instanceId); String newPrimKey = PortletPermissionUtil.getPrimaryKey( plid, newPortletId); ps2.setString(1, newPrimKey); ps2.setString(2, oldPrimKey); ps2.addBatch(); } } ps2.executeBatch(); } catch (SQLException sqle) { if (_log.isWarnEnabled()) { _log.warn(sqle, sqle); } } if (updateName) { runSQL( "update ResourcePermission set primKey = '" + newRootPortletId + "' where primKey = '" + oldRootPortletId + "' and name = " + "'" + oldRootPortletId + "'"); runSQL( "update ResourcePermission set name = '" + newRootPortletId + "' where name = '" + oldRootPortletId + "'"); } } protected void updateUserNotificationDelivery( String oldPortletId, String newPortletId) throws Exception { runSQL( "update UserNotificationDelivery set portletId = '" + newPortletId + "' where portletId = '" + oldPortletId + "'"); } protected void updateUserNotificationEvent( String oldPortletId, String newPortletId) throws Exception { runSQL( "update UserNotificationEvent set type_ = '" + newPortletId + "' where type_ = '" + oldPortletId + "'"); } protected void upgradeInstanceablePortletIds() throws Exception { // Rename instanceable portlet IDs. We expect the root form of the // portlet ID because we will rename all instances of the portlet ID. try (LoggingTimer loggingTimer = new LoggingTimer()) { String[][] renamePortletIdsArray = getRenamePortletIdsArray(); for (String[] renamePortletIds : renamePortletIdsArray) { String oldRootPortletId = renamePortletIds[0]; String newRootPortletId = renamePortletIds[1]; updateGroup(oldRootPortletId, newRootPortletId); updatePortlet(oldRootPortletId, newRootPortletId); updateLayoutRevisions( oldRootPortletId, newRootPortletId, false); updateLayouts(oldRootPortletId, newRootPortletId, false); } } } protected void upgradeUninstanceablePortletIds() throws Exception { // Rename uninstanceable portlet IDs to instanceable portlet IDs try (LoggingTimer loggingTimer = new LoggingTimer()) { String[] uninstanceablePortletIds = getUninstanceablePortletIds(); for (String portletId : uninstanceablePortletIds) { PortletInstance portletInstance = PortletInstance.fromPortletInstanceKey(portletId); if (portletInstance.hasInstanceId()) { if (_log.isWarnEnabled()) { _log.warn( "Portlet " + portletId + " is already instanceable"); } continue; } PortletInstance newPortletInstance = new PortletInstance( portletId); String newPortletInstanceKey = newPortletInstance.getPortletInstanceKey(); updateGroup(portletId, newPortletInstanceKey); updateInstanceablePortletPreferences( portletId, newPortletInstanceKey); updateResourcePermission( portletId, newPortletInstanceKey, false); updateLayoutRevisions(portletId, newPortletInstanceKey, true); updateLayouts(portletId, newPortletInstanceKey, true); } } } private List<String> _getLayoutColumnIds( UnicodeProperties typeSettingsProperties) { List<String> columnIds = new ArrayList<>(); Set<String> keys = typeSettingsProperties.keySet(); for (String key : keys) { if (StringUtil.startsWith( key, LayoutTypePortletConstants.COLUMN_PREFIX)) { columnIds.add(key); } } return columnIds; } private List<String> _getNestedPortletColumnIds( UnicodeProperties typeSettingsProperties) { if (!typeSettingsProperties.containsKey("nested-column-ids")) { return Collections.emptyList(); } String[] nestedPortletColumnIds = StringUtil.split( typeSettingsProperties.getProperty("nested-column-ids")); return ListUtil.fromArray(nestedPortletColumnIds); } private static final Log _log = LogFactoryUtil.getLog( BaseUpgradePortletId.class); }