/** * This script will add view permissions to all journal articles such that any * role that has permission to view a portlet that has been manually configured * to display the journal article will also have permission to view the journal * article itself. * * The script contains a configurable dryRun boolean variable on line 77. This * is set to true by default, meaning that the script will not make any * modifications to the articles' permissions, but will merely report what it * would have done if dryRun were set to false. This variable can be set to * false by modifying line 77 to read as follows: * * boolean dryRun = false; * * It is highly recommended that you back up your database prior to running the * script with dryRun set to false. The recommendation is to do the following: * 1. Back up your database. * 2. Rerun the script with dryRun set to true. * 3. Examine the script output. If you agree with the changes that it mentions, * set dryRun to false and rerun the script. */ import com.liferay.asset.kernel.model.AssetEntry; import com.liferay.asset.publisher.constants.AssetPublisherPortletKeys; import com.liferay.asset.publisher.util.AssetPublisherHelper; import com.liferay.journal.constants.JournalContentPortletKeys; import com.liferay.journal.model.JournalArticle; import com.liferay.journal.service.JournalArticleLocalServiceUtil; import com.liferay.journal.util.comparator.ArticleIDComparator; import com.liferay.petra.function.UnsafeConsumer; import com.liferay.petra.string.StringBundler; import com.liferay.petra.string.StringPool; import com.liferay.portal.kernel.dao.orm.ActionableDynamicQuery; import com.liferay.portal.kernel.dao.orm.Disjunction; import com.liferay.portal.kernel.dao.orm.DynamicQuery; import com.liferay.portal.kernel.dao.orm.Property; import com.liferay.portal.kernel.dao.orm.PropertyFactoryUtil; import com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Group; import com.liferay.portal.kernel.model.GroupConstants; import com.liferay.portal.kernel.model.Layout; import com.liferay.portal.kernel.model.PortletConstants; import com.liferay.portal.kernel.model.PortletPreferences; import com.liferay.portal.kernel.model.ResourceAction; import com.liferay.portal.kernel.model.ResourceConstants; import com.liferay.portal.kernel.model.ResourcePermission; import com.liferay.portal.kernel.model.Role; import com.liferay.portal.kernel.model.role.RoleConstants; import com.liferay.portal.kernel.module.util.SystemBundleUtil; import com.liferay.portal.kernel.portlet.PortletIdCodec; import com.liferay.portal.kernel.security.permission.ActionKeys; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.service.CompanyLocalServiceUtil; import com.liferay.portal.kernel.service.LayoutLocalServiceUtil; import com.liferay.portal.kernel.service.PortletPreferenceValueLocalServiceUtil; import com.liferay.portal.kernel.service.PortletPreferencesLocalServiceUtil; import com.liferay.portal.kernel.service.ResourceActionLocalServiceUtil; import com.liferay.portal.kernel.service.ResourcePermissionLocalServiceUtil; import com.liferay.portal.kernel.service.RoleLocalServiceUtil; import com.liferay.portal.kernel.service.permission.PortletPermissionUtil; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.SetUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.comparator.RoleNameComparator; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; boolean dryRun = true; public class ModifyJournalArticlePermissions { public ModifyJournalArticlePermissions(boolean dryRun) { _dryRun = dryRun; } public void doModifyJournalArticlePermissions() { try { if (_log.isInfoEnabled()) { _log.info( "Starting script to modify journal article permissions " + "to be consistent with the portlets that display them"); } CompanyLocalServiceUtil.forEachCompanyId( new UnsafeConsumer() { public void accept(Long companyId) throws Exception { ActionableDynamicQuery actionableDynamicQuery = getActionableDynamicQuery(companyId); actionableDynamicQuery.performActions(); } }); if (_log.isInfoEnabled()) { _log.info( "Finished script to modify journal article permissions " + "to be consistent with the portlets that display them"); } } catch (Exception e) { _log.error( "Error while running script to modify journal article " + "permissions to be consistent with the portlets that " + "display them", e); } } public TreeMap> getArticleRolesMap() { return _articleRolesMap; } protected void addViewResourcePermission( JournalArticle article, Role role, String portletId) { if (_log.isInfoEnabled()) { _log.info( StringBundler.concat( "The ", role.getName(), " role has permissions to view ", "portlet ", portletId, " but does not have permissions to ", "view its displayed journal article ", article.getId(), ". Adding view permissions for the ", role.getName(), " role on journal article ", article.getId(), "...")); } try { ResourcePermissionLocalServiceUtil.setResourcePermissions( article.getCompanyId(), JournalArticle.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL, String.valueOf(article.getResourcePrimKey()), role.getRoleId(), ArrayUtil.append( getExistingResourceActionsArray(article, role), ActionKeys.VIEW)); if (_log.isInfoEnabled()) { _log.info( StringBundler.concat( "Successfully added view permissions for the ", role.getName(), " role on journal article ", article.getId())); } addToArticleRolesMap(article, role); } catch (Exception e) { _log.error( StringBundler.concat( "Unable to add view permissions for the ", role.getName(), " role on journal article ", article.getId()), e); } } protected ActionableDynamicQuery getActionableDynamicQuery(long companyId) { ActionableDynamicQuery actionableDynamicQuery = PortletPreferencesLocalServiceUtil.getActionableDynamicQuery(); actionableDynamicQuery.setAddCriteriaMethod( new ActionableDynamicQuery.AddCriteriaMethod() { @Override public void addCriteria(DynamicQuery dynamicQuery) { Property companyIdProperty = PropertyFactoryUtil.forName( "companyId"); dynamicQuery.add(companyIdProperty.eq(companyId)); Property portletIdProperty = PropertyFactoryUtil.forName( "portletId"); Disjunction disjunction = RestrictionsFactoryUtil.disjunction(); disjunction.add( portletIdProperty.like( JournalContentPortletKeys.JOURNAL_CONTENT + StringPool.PERCENT)); disjunction.add( portletIdProperty.like( AssetPublisherPortletKeys.ASSET_PUBLISHER + StringPool.PERCENT)); dynamicQuery.add(disjunction); } }); actionableDynamicQuery.setPerformActionMethod( new ActionableDynamicQuery.PerformActionMethod() { @Override public void performAction( PortletPreferences portletPreferences) { modifyJournalArticlePermissions(portletPreferences); } }); return actionableDynamicQuery; } protected List getAssetPublisherPortletJournalArticles( PortletPreferences portletPreferences, Layout layout) { javax.portlet.PortletPreferences jxPortletPreferences = PortletPreferenceValueLocalServiceUtil.getPreferences( portletPreferences); long[] groupIds = SystemBundleUtil.callService( AssetPublisherHelper.class, {assetPublisherHelper -> assetPublisherHelper.getGroupIds( jxPortletPreferences, layout.getGroupId(), layout)}); List assetEntries = SystemBundleUtil.callService( AssetPublisherHelper.class, {assetPublisherHelper -> assetPublisherHelper.getAssetEntries( null, jxPortletPreferences, PermissionThreadLocal.getPermissionChecker(), groupIds, false, false)}); long classNameId = PortalUtil.getClassNameId(JournalArticle.class); List articles = new ArrayList<>(); for (AssetEntry assetEntry : assetEntries) { if (assetEntry.getClassNameId() != classNameId) { continue; } JournalArticle article = JournalArticleLocalServiceUtil.fetchLatestArticle( assetEntry.getClassPK()); if (article == null) { if (_log.isWarnEnabled()) { _log.warn( StringBundler.concat( "Unable to get journal article with resource ", "primary key ", assetEntry.getClassPK(), " referenced by asset entry ", assetEntry.getEntryId())); } continue; } articles.add(article); } return articles; } protected String[] getExistingResourceActionsArray( JournalArticle article, Role role) { ResourcePermission resourcePermission = ResourcePermissionLocalServiceUtil.fetchResourcePermission( article.getCompanyId(), JournalArticle.class.getName(), ResourceConstants.SCOPE_INDIVIDUAL, String.valueOf(article.getResourcePrimKey()), role.getRoleId()); if (resourcePermission == null) { return new String[0]; } List actionIds = new ArrayList<>(); List resourceActions = ResourceActionLocalServiceUtil.getResourceActions( JournalArticle.class.getName()); for (ResourceAction resourceAction : resourceActions) { if (resourcePermission.hasAction(resourceAction)) { actionIds.add(resourceAction.getActionId()); } } return actionIds.toArray(new String[0]); } protected Set getJournalArticleViewPermissionRoles( JournalArticle article) { return getResourcePermissionRoles( article.getCompanyId(), JournalArticle.class.getName(), article.getGroupId(), String.valueOf(article.getResourcePrimKey()), ActionKeys.VIEW); } protected List getJournalArticles( PortletPreferences portletPreferences, Layout layout) { String portletId = portletPreferences.getPortletId(); if (portletId.startsWith(JournalContentPortletKeys.JOURNAL_CONTENT)) { return getJournalContentPortletJournalArticles(portletPreferences); } else if (portletId.startsWith( AssetPublisherPortletKeys.ASSET_PUBLISHER)) { return getAssetPublisherPortletJournalArticles( portletPreferences, layout); } _log.error("Unexpected portlet id: " + portletId); return Collections.emptyList(); } protected List getJournalContentPortletJournalArticles( PortletPreferences portletPreferences) { javax.portlet.PortletPreferences jxPortletPreferences = PortletPreferenceValueLocalServiceUtil.getPreferences( portletPreferences); long groupId = GetterUtil.getLong( jxPortletPreferences.getValue("groupId", null)); if (groupId <= 0) { if (_log.isWarnEnabled()) { _log.warn( StringBundler.concat( "Invalid group id ", groupId, " for portlet ", "preferences ", portletPreferences.getPortletPreferencesId())); } return Collections.emptyList(); } String articleId = jxPortletPreferences.getValue("articleId", null); if (Validator.isBlank(articleId)) { if (_log.isWarnEnabled()) { _log.warn( "No article id for portlet preferences " + portletPreferences.getPortletPreferencesId()); } return Collections.emptyList(); } JournalArticle article = JournalArticleLocalServiceUtil.fetchArticle( groupId, articleId); if (article != null) { return Collections.singletonList(article); } if (_log.isWarnEnabled()) { _log.warn( StringBundler.concat( "Unable to find article with group id ", groupId, " and ", "article id \"", articleId, "\" referenced by portlet ", "preferences ", portletPreferences.getPortletPreferencesId())); } return Collections.emptyList(); } protected Set getLayoutViewPermissionRoles(Layout layout) { Set viewPermissionRoles = getResourcePermissionRoles( layout.getCompanyId(), Layout.class.getName(), layout.getGroupId(), String.valueOf(layout.getPlid()), ActionKeys.VIEW); if (!layout.isPrivateLayout()) { return viewPermissionRoles; } Set privateLayoutAccessRoles = new HashSet<>(); privateLayoutAccessRoles.add( RoleLocalServiceUtil.getRole( layout.getCompanyId(), RoleConstants.SITE_MEMBER)); privateLayoutAccessRoles.addAll( getResourcePermissionRoles( layout.getCompanyId(), Group.class.getName(), layout.getGroupId(), String.valueOf(layout.getGroupId()), ActionKeys.MANAGE_LAYOUTS)); privateLayoutAccessRoles.addAll( getResourcePermissionRoles( layout.getCompanyId(), Group.class.getName(), layout.getGroupId(), String.valueOf(layout.getGroupId()), ActionKeys.UPDATE)); return SetUtil.intersect(viewPermissionRoles, privateLayoutAccessRoles); } protected Set getPortletPreferencesViewPermissionRoles( PortletPreferences portletPreferences, Layout layout) { String name = PortletIdCodec.decodePortletName( portletPreferences.getPortletId()); String primKey = PortletPermissionUtil.getPrimaryKey( layout.getPlid(), portletPreferences.getPortletId()) Set viewPermissionRoles = getResourcePermissionRoles( portletPreferences.getCompanyId(), name, layout.getGroupId(), primKey, ActionKeys.VIEW); int count = ResourcePermissionLocalServiceUtil.getResourcePermissionsCount( portletPreferences.getCompanyId(), name, ResourceConstants.SCOPE_INDIVIDUAL, primKey); if (count == 0) { viewPermissionRoles.add( RoleLocalServiceUtil.getRole( portletPreferences.getCompanyId(), RoleConstants.OWNER)); viewPermissionRoles.add( RoleLocalServiceUtil.getDefaultGroupRole(layout.getGroupId())); viewPermissionRoles.add( RoleLocalServiceUtil.getRole( portletPreferences.getCompanyId(), RoleConstants.GUEST)); } return viewPermissionRoles; } protected Set getResourcePermissionRoles( long companyId, String name, long groupId, String primKey, String actionId) { Set resourcePermissionRoles = new HashSet<>(); resourcePermissionRoles.addAll( RoleLocalServiceUtil.getResourceRoles( companyId, name, ResourceConstants.SCOPE_INDIVIDUAL, primKey, actionId)); resourcePermissionRoles.addAll( RoleLocalServiceUtil.getResourceRoles( companyId, name, ResourceConstants.SCOPE_GROUP_TEMPLATE, String.valueOf(GroupConstants.DEFAULT_PARENT_GROUP_ID), actionId)); resourcePermissionRoles.addAll( RoleLocalServiceUtil.getResourceRoles( companyId, name, ResourceConstants.SCOPE_GROUP, String.valueOf(groupId), ActionKeys.VIEW)); resourcePermissionRoles.addAll( RoleLocalServiceUtil.getResourceRoles( companyId, name, ResourceConstants.SCOPE_COMPANY, String.valueOf(companyId), ActionKeys.VIEW)); return resourcePermissionRoles } protected void modifyJournalArticlePermissions( PortletPreferences portletPreferences) { Layout layout = LayoutLocalServiceUtil.fetchLayout( portletPreferences.getPlid()); if (layout == null) { if (_log.isWarnEnabled()) { _log.warn( StringBundler.concat( "Unable to find layout ", portletPreferences.getPlid(), " associated with portlet preferences ", portletPreferences.getPortletPreferencesId())); } return; } List articles = getJournalArticles( portletPreferences, layout); for (JournalArticle article : articles) { modifyJournalArticlePermissions( article, portletPreferences, layout); } } protected void modifyJournalArticlePermissions( JournalArticle article, PortletPreferences portletPreferences, Layout layout) { Set portletViewPermissionRoles = SetUtil.intersect( getLayoutViewPermissionRoles(layout), getPortletPreferencesViewPermissionRoles( portletPreferences, layout)); Set articleViewPermissionRoles = getJournalArticleViewPermissionRoles(article); for (Role role : portletViewPermissionRoles) { if (articleViewPermissionRoles.contains(role)) { continue; } if (_dryRun) { if (_log.isInfoEnabled()) { _log.info( StringBundler.concat( "The ", role.getName(), " role has permissions to ", "view portlet ", portletPreferences.getPortletId(), " but does not have permissions to view its ", "displayed journal article ", article.getId(), ". ", "Because dryRun is set to true, no action will be ", "taken. Rerun the script with dryRun set to false ", "to add view permissions for the ", role.getName(), " role on journal article ", article.getId(), ".")); } addToArticleRolesMap(article, role); } else { addViewResourcePermission( article, role, portletPreferences.getPortletId()); } } } protected void addToArticleRolesMap(JournalArticle article, Role role) { TreeSet roles = _articleRolesMap.get(article); if (roles == null) { roles = new TreeSet<>(new RoleNameComparator(true)); _articleRolesMap.put(article, roles); } roles.add(role); } private static Log _log = LogFactoryUtil.getLog( ModifyJournalArticlePermissions.class); private final boolean _dryRun; private final TreeMap> _articleRolesMap = new TreeMap<>(new ArticleIDComparator(true)); } ModifyJournalArticlePermissions modifyJournalArticlePermissions= new ModifyJournalArticlePermissions(dryRun); modifyJournalArticlePermissions.doModifyJournalArticlePermissions(); out.println("Script finished running."); if (dryRun) { out.println("No action was taken because dryRun was set to true."); out.println( "Rerun the script with dryRun set to false to add View permissions " + "to the following articles for the following roles:"); } else { out.println( "View permissions have been added to the following articles for the " + "following roles:"); } for (Map.Entry> entry : modifyJournalArticlePermissions.getArticleRolesMap()) { JournalArticle article = entry.getKey(); TreeSet roles = entry.getValue(); StringBundler sb = new StringBundler(5 + roles.size() * 2); sb.append("Article "); sb.append(article.getId()); sb.append(" with title \""); sb.append(article.getTitle()); sb.append("\" for the following roles: "); for (Role role : roles) { sb.append(role.getName()); sb.append(StringPool.COMMA_AND_SPACE); } sb.setIndex(sb.index() - 1) out.println(sb.toString()); }