0
votes

We implemented a corejsphook module to customize user_action.jsp in Liferay 7.0.6, which worked fine.

After upgrading to Liferay 7.3.6 it stopped working. I have made sure the module that we are replacing the jsp into is correct by searching for it in the gogo shell. I also looked at the source code to confirm the package that contains the .jsp is right. And I updated the version number to the Liferay 7.3 bundle version (5.0.53). The entry in my bnd.bnd is: Fragment-Host: com.liferay.users.admin.web;bundle-version="5.0.53"

When I deploy the module, it does not restart the fragment host bundle as it should (and did in Liferay 7.0). Restarting that bundle manually doesn't work either - the modified .jsp isn't used.

As a side note, we have other corejsphooks that work fine. This is the only one that doesn't work, so I must be missing something here.

Update: I am including the code as requested. Here are the four files in the module (not counting the default files like build.gradle and Language_en.properties that are created when the module is created) in the module.

bnd.bnd

Bundle-Version: 7.3.6.1
Fragment-Host: com.liferay.users.admin.web;bundle-version="5.0.53"
-sources: true
-jsp: *.jsp,*.jspf

user_action.jsp

<%--
/**
 * 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.
 */
--%>

<%@ include file="/init.jsp" %>
 
<%@ page import="com.liferay.petra.encryptor.Encryptor" %>
<%@ page import="com.liferay.portal.kernel.util.HtmlUtil" %>

<%

UserSearch searchContainer = (UserSearch)request.getAttribute("liferay-ui:search:searchContainer");

String redirect = searchContainer.getIteratorURL().toString();

ResultRow row = (ResultRow)request.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);

User user2 = (User)row.getObject();

long userId = user2.getUserId();

String userLandingPage = null;

User currentUser = user2;
List<Organization> userOrganizations = currentUser.getOrganizations();


if (Validator.isNotNull(userOrganizations) && !userOrganizations.isEmpty())
{
    // If user is member of more than one organization then it will take
    // first organization from list
    Organization organization = userOrganizations.get(0);
    if (Validator.isNotNull(organization))
    {
        Group organizationGroup = organization.getGroup();
        if (organizationGroup.getPrivateLayoutsPageCount() > 0)
        {
            
            String privateGroupURL = PropsUtil.get(PropsKeys.LAYOUT_FRIENDLY_URL_PRIVATE_GROUP_SERVLET_MAPPING);
            String groupFriendlyURL = organizationGroup.getFriendlyURL();
            userLandingPage = privateGroupURL + groupFriendlyURL;

            String encDoAsUserId = Encryptor.encrypt(
                company.getKeyObj(), String.valueOf(userId));
            
            userLandingPage = userLandingPage + "?doAsUserId=" + HtmlUtil.escapeURL(encDoAsUserId);
            System.out.println("userLandingPage is: " + userLandingPage);
        } 
        else
        {
            System.out.println(organizationGroup.getName() + " organization site doesn't have any private page. So default landing page will be used");
        }
    }
}

// System.out.println("Landing page is: " + userLandingPage);

 
%>

<liferay-ui:icon-menu
    direction="left-side"
    icon="<%= StringPool.BLANK %>"
    markupView="lexicon"
    message="<%= StringPool.BLANK %>"
    showWhenSingleIcon="<%= true %>"
>

    <%
    boolean hasUpdatePermission = UserPermissionUtil.contains(permissionChecker, userId, ActionKeys.UPDATE);
    %>

    <c:if test="<%= hasUpdatePermission %>">
        <portlet:renderURL var="editUserURL">
            <portlet:param name="mvcRenderCommandName" value="/users_admin/edit_user" />
            <portlet:param name="redirect" value="<%= redirect %>" />
            <portlet:param name="p_u_i_d" value="<%= String.valueOf(userId) %>" />
        </portlet:renderURL>

        <liferay-ui:icon
            message="edit"
            url="<%= editUserURL %>"
        />
    </c:if>

    <c:if test="<%= UserPermissionUtil.contains(permissionChecker, userId, ActionKeys.PERMISSIONS) %>">
        <liferay-security:permissionsURL
            modelResource="<%= User.class.getName() %>"
            modelResourceDescription="<%= user2.getFullName() %>"
            resourcePrimKey="<%= String.valueOf(userId) %>"
            var="permissionsUserURL"
            windowState="<%= LiferayWindowState.POP_UP.toString() %>"
        />

        <liferay-ui:icon
            message="permissions"
            method="get"
            url="<%= permissionsUserURL %>"
            useDialog="<%= true %>"
        />
    </c:if>

    <c:if test="<%= (PropsValues.LAYOUT_USER_PRIVATE_LAYOUTS_ENABLED || PropsValues.LAYOUT_USER_PUBLIC_LAYOUTS_ENABLED) && hasUpdatePermission %>">

        <%
        PortletURL managePagesURL = PortletProviderUtil.getPortletURL(request, user2.getGroup(), Layout.class.getName(), PortletProvider.Action.EDIT);

        managePagesURL.setParameter("redirect", redirect);
        %>

        <liferay-ui:icon
            message="manage-pages"
            url="<%= managePagesURL.toString() %>"
        />
    </c:if>

    <c:if test="<%= !PropsValues.PORTAL_JAAS_ENABLE && PropsValues.PORTAL_IMPERSONATION_ENABLE && (userId != user.getUserId()) && !themeDisplay.isImpersonated() && UserPermissionUtil.contains(permissionChecker, userId, ActionKeys.IMPERSONATE) %>">
        <liferay-security:doAsURL
            doAsUserId="<%= userId %>"
            var="impersonateUserURL"
        />

        <liferay-ui:icon
            message="impersonate-user"
            target="_blank"
            url="<%= Validator.isNotNull(userLandingPage) ? userLandingPage : impersonateUserURL %>"
        />
    </c:if>

    <c:if test="<%= UserPermissionUtil.contains(permissionChecker, userId, ActionKeys.DELETE) %>">
        <c:if test="<%= !user2.isActive() %>">
            <portlet:actionURL name="/users_admin/edit_user" var="restoreUserURL">
                <portlet:param name="<%= Constants.CMD %>" value="<%= Constants.RESTORE %>" />
                <portlet:param name="redirect" value="<%= redirect %>" />
                <portlet:param name="deleteUserIds" value="<%= String.valueOf(userId) %>" />
            </portlet:actionURL>

            <liferay-ui:icon
                message="activate"
                url="<%= restoreUserURL %>"
            />
        </c:if>

        <portlet:actionURL name="/users_admin/edit_user" var="deleteUserURL">
            <portlet:param name="<%= Constants.CMD %>" value="<%= user2.isActive() ? Constants.DEACTIVATE : Constants.DELETE %>" />
            <portlet:param name="redirect" value="<%= redirect %>" />
            <portlet:param name="deleteUserIds" value="<%= String.valueOf(userId) %>" />
        </portlet:actionURL>

        <c:if test="<%= userId != user.getUserId() %>">
            <c:choose>
                <c:when test="<%= user2.isActive() %>">
                    <liferay-ui:icon-deactivate
                        url="<%= deleteUserURL %>"
                    />
                </c:when>
                <c:when test="<%= !user2.isActive() && PropsValues.USERS_DELETE %>">
                    <liferay-ui:icon-delete
                        url="<%= deleteUserURL %>"
                    />
                </c:when>
            </c:choose>
        </c:if>
    </c:if>

</liferay-ui:icon-menu>

corejsphook.UserActionCustomJspBag.java

/**
 * Copyright 2000-present Liferay, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package corejsphook;

import com.liferay.portal.deploy.hot.CustomJspBag;
import com.liferay.portal.kernel.url.URLContainer;

import java.net.URL;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;

/**
 * @author Scott McIntosh, ICF
 */
@Component(
    immediate = true,
    property = {
        "context.id=UserActionCustomJspBag",
        "context.name=User Action Custom JSP Bag",
        "service.ranking:Integer=111"
    }
)
public class UserActionCustomJspBag implements CustomJspBag 
{
    @Override
    public String getCustomJspDir() {
        return "META-INF/resources/";
    }

    @Override
    public List<String> getCustomJsps() {
        System.out.println("In getCustomJsps().  _customJsps is null?");
        System.out.println(_customJsps == null);
        return _customJsps;
    }

    @Override
    public URLContainer getURLContainer() {
        System.out.println("In getURLContainer()");
        return _urlContainer;
    }

    @Override
    public boolean isCustomJspGlobal() {
        System.out.println("In isCustomJspGlobal()");
        return true;
    }

    @Activate
    protected void activate(BundleContext bundleContext) {
        System.out.println("In activate()");
        bundle = bundleContext.getBundle();

        _customJsps = new ArrayList<>();

        Enumeration<URL> entries = bundle.findEntries(
            getCustomJspDir(), "*.jsp", true);

        while (entries.hasMoreElements()) {
            URL url = entries.nextElement();
            System.out.println("Processing jsp: " + url);
            _customJsps.add(url.getPath());
        }
    }

    private List<String> _customJsps;

    private final URLContainer _urlContainer = new URLContainer() {

        @Override
        public Set<String> getResources(String path) {
            System.out.println("In getResources()");
            Set<String> paths = new HashSet<>();

            for (String entry : _customJsps) {
                if (entry.startsWith(path)) {
                    paths.add(entry);
                }
            }

            return paths;
        }

        @Override
        public URL getResource(String name) {
            System.out.println("In getResource()");
            return bundle.getEntry(name);
        }

    };

    private Bundle bundle;

}

gov.nysdot.rideshare.NYRideshareEnUsResourceBundle.java

ackage gov.nysdot.rideshare;

import com.liferay.portal.kernel.language.UTF8Control;

import java.util.Enumeration;
import java.util.ResourceBundle;
import org.osgi.service.component.annotations.Component;

@Component(
        property = { "language.id=en_US" }, 
        service = ResourceBundle.class
    )
    public class NYRideshareEnUsResourceBundle extends ResourceBundle {

        @Override
        protected Object handleGetObject(String key) {
            return _resourceBundle.getObject(key);
        }

        @Override
        public Enumeration<String> getKeys() {
            return _resourceBundle.getKeys();
        }

        private final ResourceBundle _resourceBundle = ResourceBundle.getBundle(
            "content.Language_en_US", UTF8Control.INSTANCE);

    }

Update: I have started from scratch, but I'm getting the same result. I created a new module using:

$ blade create -t fragment gov.nysdot.user-action-hook --host-bundle-symbolic-name com.liferay.users.admin.web --host-bundle-version 5.0.53

I then copied user_action.jsp from source (/modules/apps/users-admin/users-admin-web/src/main/resources/META-INF/resources/user_action.jsp).

To test, I simply duplicated the separator line at line 156 () so that I'd have a visual clue. If the override worked I'd see two separator lines.

I built with 'grade build' and deployed to my server. It did not restart the com.liferay.users.admin.web bundle, and no changed were present.

Now, there are practically no changes from what the module creation script created.

1
please show code, preferably a minimal reproducible exampleOlaf Kock
@OlafKock I have updated the question with the module's code. Thanks!Scott McIntosh

1 Answers

0
votes

What strikes me in your code/description:

  • The jsp is a module's jsp, not a core jsp. Thus I wouldn't expect a CustomJspBag, but rather follow the module_jsp_override sample
  • A fragment bundle will never be ACTIVE, only RESOLVED (as in OSGi bundle lifecycles). Thus, it can deliver files that override a host bundle's files, but none of its own code. You're providing separate services, that certainly wouldn't be available and started - they should live in other bundles. Consider a fragment bundle as passive.

Thus, without turning your files into a project to try, I'd assume that this is the problem at hand.

Convert the bundle to a module-jsp-override project (no java code, pay attention to bnd.bnd). Make sure that the JSP is still what it needs to be: You can't just copy the 7.0 JSP to 7.3 and assume that those changes still accurately implement what you need. Rather, you'll need a 3-way-merge between the stock 7.0 version, your modifications, and the stock 7.3 jsp.