• Liferay7 BPM门户开发之42: Liferay核心JSP定制扩展


    Liferay最大的好处是不仅接口强大,利于扩展,就连JSP定制扩展都提供了3种方式。

    修改核心jsp代码,有3种修改方式:
    1、暴力修改
    直接修改(位于portal-web/docroot/html),编译部署,会带来风险,而且不能同步更新。


    2、全量扩展修改
    热部署jsp文件(替代原有jsp),这是v7.0下的OSGi方式,实现方式非常优雅。


    3、CustomJspBag Hook方式
    实现CustomJspBag接口,做jsp片段式的修改,同样是增量热部署,也是v7.0下的OSGi方式(需要增加依赖org.osgi.service.component.annotations.Activate和org.osgi.service.component.annotations.Component;),实现方式可以说更加优雅。

    第一种方式不讲了。

    2、全量扩展修改

    需要新建fragment module。只需要注意2点:
    1、在module工程的OSGi定义(bnd.bnd文件)中指定Fragment-Host声明
    如下:
    Bundle-Version: 1.0.0
    Fragment-Host: com.liferay.login.web;bundle-version="1.0.0"
    -sources: true

    2、把你修改后的JSP文件放在module工程的resources目录,比如
    你的module工程srcmain esourcesMETA-INF esourceslogin.jsp

    login.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" %>
    <p style="color: red">changed</p>
    <c:choose>
        <c:when test="<%= themeDisplay.isSignedIn() %>">
    
            <%
            String signedInAs = HtmlUtil.escape(user.getFullName());
    
            if (themeDisplay.isShowMyAccountIcon() && (themeDisplay.getURLMyAccount() != null)) {
                String myAccountURL = String.valueOf(themeDisplay.getURLMyAccount());
    
                signedInAs = "<a class="signed-in" href="" + HtmlUtil.escape(myAccountURL) + "">" + signedInAs + "</a>";
            }
            %>
    
            <liferay-ui:message arguments="<%= signedInAs %>" key="you-are-signed-in-as-x" translateArguments="<%= false %>" />
        </c:when>
        <c:otherwise>
    
            <%
            String redirect = ParamUtil.getString(request, "redirect");
    
            String login = LoginUtil.getLogin(request, "login", company);
            String password = StringPool.BLANK;
            boolean rememberMe = ParamUtil.getBoolean(request, "rememberMe");
    
            if (Validator.isNull(authType)) {
                authType = company.getAuthType();
            }
            %>
    
            <portlet:actionURL name="/login/login" secure="<%= PropsValues.COMPANY_SECURITY_AUTH_REQUIRES_HTTPS || request.isSecure() %>" var="loginURL" />
    
            <aui:form action="<%= loginURL %>" autocomplete='<%= PropsValues.COMPANY_SECURITY_LOGIN_FORM_AUTOCOMPLETE ? "on" : "off" %>' cssClass="sign-in-form" method="post" name="fm" onSubmit="event.preventDefault();">
                <aui:input name="saveLastPath" type="hidden" value="<%= false %>" />
                <aui:input name="redirect" type="hidden" value="<%= redirect %>" />
                <aui:input name="doActionAfterLogin" type="hidden" value="<%= portletName.equals(PortletKeys.FAST_LOGIN) ? true : false %>" />
    
                <c:choose>
                    <c:when test='<%= SessionMessages.contains(request, "userAdded") %>'>
    
                        <%
                        String userEmailAddress = (String)SessionMessages.get(request, "userAdded");
                        String userPassword = (String)SessionMessages.get(request, "userAddedPassword");
                        %>
    
                        <div class="alert alert-success">
                            <c:choose>
                                <c:when test="<%= company.isStrangersVerify() || Validator.isNull(userPassword) %>">
                                    <liferay-ui:message key="thank-you-for-creating-an-account" />
    
                                    <c:if test="<%= company.isStrangersVerify() %>">
                                        <liferay-ui:message arguments="<%= userEmailAddress %>" key="your-email-verification-code-has-been-sent-to-x" translateArguments="<%= false %>" />
                                    </c:if>
                                </c:when>
                                <c:otherwise>
                                    <liferay-ui:message arguments="<%= userPassword %>" key="thank-you-for-creating-an-account.-your-password-is-x" translateArguments="<%= false %>" />
                                </c:otherwise>
                            </c:choose>
    
                            <c:if test="<%= PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys.ADMIN_EMAIL_USER_ADDED_ENABLED) %>">
                                <liferay-ui:message arguments="<%= userEmailAddress %>" key="your-password-has-been-sent-to-x" translateArguments="<%= false %>" />
                            </c:if>
                        </div>
                    </c:when>
                    <c:when test='<%= SessionMessages.contains(request, "userPending") %>'>
    
                        <%
                        String userEmailAddress = (String)SessionMessages.get(request, "userPending");
                        %>
    
                        <div class="alert alert-success">
                            <liferay-ui:message arguments="<%= userEmailAddress %>" key="thank-you-for-creating-an-account.-you-will-be-notified-via-email-at-x-when-your-account-has-been-approved" translateArguments="<%= false %>" />
                        </div>
                    </c:when>
                </c:choose>
    
                <liferay-ui:error exception="<%= AuthException.class %>" message="authentication-failed" />
                <liferay-ui:error exception="<%= CompanyMaxUsersException.class %>" message="unable-to-log-in-because-the-maximum-number-of-users-has-been-reached" />
                <liferay-ui:error exception="<%= CookieNotSupportedException.class %>" message="authentication-failed-please-enable-browser-cookies" />
                <liferay-ui:error exception="<%= NoSuchUserException.class %>" message="authentication-failed" />
                <liferay-ui:error exception="<%= PasswordExpiredException.class %>" message="your-password-has-expired" />
                <liferay-ui:error exception="<%= UserEmailAddressException.MustNotBeNull.class %>" message="please-enter-an-email-address" />
                <liferay-ui:error exception="<%= UserLockoutException.LDAPLockout.class %>" message="this-account-is-locked" />
    
                <liferay-ui:error exception="<%= UserLockoutException.PasswordPolicyLockout.class %>">
    
                    <%
                    UserLockoutException.PasswordPolicyLockout ule = (UserLockoutException.PasswordPolicyLockout)errorException;
                    %>
    
                    <liferay-ui:message arguments="<%= ule.user.getUnlockDate() %>" key="this-account-is-locked-until-x" translateArguments="<%= false %>" />
                </liferay-ui:error>
    
                <liferay-ui:error exception="<%= UserPasswordException.class %>" message="authentication-failed" />
                <liferay-ui:error exception="<%= UserScreenNameException.MustNotBeNull.class %>" message="the-screen-name-cannot-be-blank" />
    
                <aui:fieldset>
    
                    <%
                    String loginLabel = null;
    
                    if (authType.equals(CompanyConstants.AUTH_TYPE_EA)) {
                        loginLabel = "email-address";
                    }
                    else if (authType.equals(CompanyConstants.AUTH_TYPE_SN)) {
                        loginLabel = "screen-name";
                    }
                    else if (authType.equals(CompanyConstants.AUTH_TYPE_ID)) {
                        loginLabel = "id";
                    }
                    %>
    
                    <aui:input autoFocus="<%= windowState.equals(LiferayWindowState.EXCLUSIVE) || windowState.equals(WindowState.MAXIMIZED) %>" cssClass="clearable" label="<%= loginLabel %>" name="login" showRequiredLabel="<%= false %>" type="text" value="<%= login %>">
                        <aui:validator name="required" />
                    </aui:input>
    
                    <aui:input name="password" showRequiredLabel="<%= false %>" type="password" value="<%= password %>">
                        <aui:validator name="required" />
                    </aui:input>
    
                    <span id="<portlet:namespace />passwordCapsLockSpan" style="display: none;"><liferay-ui:message key="caps-lock-is-on" /></span>
    
                    <c:if test="<%= company.isAutoLogin() && !PropsValues.SESSION_DISABLED %>">
                        <aui:input checked="<%= rememberMe %>" name="rememberMe" type="checkbox" />
                    </c:if>
                </aui:fieldset>
    
                <aui:button-row>
                    <aui:button type="submit" value="sign-in" />
                </aui:button-row>
            </aui:form>
    
            <liferay-util:include page="/navigation.jsp" servletContext="<%= application %>" />
    
            <aui:script sandbox="<%= true %>">
                var form = AUI.$(document.<portlet:namespace />fm);
    
                form.on(
                    'submit',
                    function(event) {
                        var redirect = form.fm('redirect');
    
                        if (redirect) {
                            var redirectVal = redirect.val();
    
                            redirect.val(redirectVal + window.location.hash);
                        }
    
                        submitForm(form);
                    }
                );
    
                form.fm('password').on(
                    'keypress',
                    function(event) {
                        Liferay.Util.showCapsLock(event, '<portlet:namespace />passwordCapsLockSpan');
                    }
                );
            </aui:script>
        </c:otherwise>
    </c:choose>
    View Code

    3、CustomJspBag Hook方式


    这是一种覆盖原有XXXX-ext.jsp的方式,XXXX-ext.jsp是空文件,下面的例子用来覆盖 bottom.jsp ,插入了新定义 bottom-ext.jsp 片段进来
    文件位于 src/main/resources/META-INF/jsps/html/common/themes/bottom-ext.jsp
    bottom-ext.jsp 只有一行

    <h2>HERE I AM!!!!!</h2>

    然后实现CustomJspBag接口,实现类YourCustomJspBag ,有2点需要特别注意

    service.ranking是优先级的意思,数值越大越优先,在例子中是100,当有另外的CustomJspBag实现类,比如定义为200,那么定义为200的类的优先级更高。

    关键方法是activate,作用是为所有的需要自定义的核心JSPs添加URL(目录路径)到List列表,List列表的用途有些过滤器的含义,例子中是添加"META-INF/jsps/"下的所有文件。

    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;
    
    
    @Component(
        immediate = true,
        property = {
            "context.id=YourCustomJspBag",
            "context.name=Custom JSP Bag",
            "service.ranking:Integer=100"
        }
    )
    public class YourCustomJspBag implements CustomJspBag {
    
    
        @Override
        public String getCustomJspDir() {
            return "META-INF/jsps/";
        }
    
        /**
         * 返回自定义JSP URL paths列表.
         */
        @Override
        public List<String> getCustomJsps() {
            return _customJsps;
        }
    
        @Override
        public URLContainer getURLContainer() {
            return _urlContainer;
        }
    
        @Override
        public boolean isCustomJspGlobal() {
            return true;
        }
    
        //osgi激活时触发的动作
        @Activate
        protected void activate(BundleContext bundleContext) {
            _bundle = bundleContext.getBundle();
    
            _customJsps = new ArrayList<>();
    
            Enumeration<URL> entries = _bundle.findEntries(
                getCustomJspDir(), "*.jsp", true);
    
            while (entries.hasMoreElements()) {
                URL url = entries.nextElement();
                //*.jsp全部添加进JSP URL paths
                _customJsps.add(url.getPath());
            }
        }
    
        private Bundle _bundle;
        private List<String> _customJsps;
    
        private final URLContainer _urlContainer = new URLContainer() {
    
            @Override
            public URL getResource(String name) {
                return _bundle.getEntry(name);
            }
    
            @Override
            public Set<String> getResources(String path) {
                Set<String> paths = new HashSet<>();
    
                for (String entry : _customJsps) {
                    if (entry.startsWith(path)) {
                        paths.add(entry);
                    }
                }
    
                return paths;
            }
    
        };
    
    }

    gradle的配置文件build.gradle

    dependencies {
    compile 'com.liferay.portal:com.liferay.portal.kernel:2.0.0'
    compile 'com.liferay.portal:com.liferay.portal.impl:2.0.0'
    compile 'org.osgi:org.osgi.core:6.0.0'
    compile 'org.osgi:org.osgi.service.component.annotations:1.3.0'
    }
    
    version = '1.0.0'

    部署后的界面:


    CustomJspBag后面的秘密

    在portal-web/docroot/html/common/themes/bottom.jsp 文件,在其最下方,有以下代码:

    <liferay-util:include page="/html/common/themes/bottom-ext.jsp" />

    原来必须要依靠原来的JSP包括了一个空的bottom-ext.jsp文件,这是前提
    实际上它只是覆盖了bottom-ext.jsp,而不是它的宿主bottom.jsp

    即所有类似XXXX-ext.jsp这样的文件,都是可以做定制的。

    那么看到这里就很清晰第三种方式(CustomJspBag Hook)和第二种方式(全量扩展修改)之间的区别了,即 片段覆盖全量覆盖的区别,这需要您根据需求来做选择。
    Liferay给开发者这两种选择,目的很清晰,即通过CustomJspBag Hook来降低风险,做合理的分离设计。

  • 相关阅读:
    JS4
    JS3
    JS2
    JS1
    Dos命令
    面向对象的复习
    9.14Css
    9.13列表的用法
    9.12Css
    9.11Css
  • 原文地址:https://www.cnblogs.com/starcrm/p/6116933.html
Copyright © 2020-2023  润新知