mirror of
https://gitee.com/dromara/MaxKey.git
synced 2025-12-06 17:08:29 +08:00
Merge branch 'master' of https://github.com/MaxKeyTop/MaxKey.git
This commit is contained in:
commit
cc76bb25f4
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||||
|
*
|
||||||
|
* 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 org.maxkey.authn.online;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import org.ehcache.UserManagedCache;
|
||||||
|
import org.ehcache.config.builders.ExpiryPolicyBuilder;
|
||||||
|
import org.ehcache.config.builders.UserManagedCacheBuilder;
|
||||||
|
|
||||||
|
|
||||||
|
public class InMemoryOnlineTicketServices implements OnlineTicketServices{
|
||||||
|
|
||||||
|
protected final static UserManagedCache<String, OnlineTicket> onlineTicketStore =
|
||||||
|
UserManagedCacheBuilder.newUserManagedCacheBuilder(String.class, OnlineTicket.class)
|
||||||
|
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(30)))
|
||||||
|
.build(true);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(String ticketId, OnlineTicket ticket) {
|
||||||
|
onlineTicketStore.put(ticketId, ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OnlineTicket remove(String ticketId) {
|
||||||
|
OnlineTicket ticket=onlineTicketStore.get(ticketId);
|
||||||
|
onlineTicketStore.remove(ticketId);
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OnlineTicket get(String ticketId) {
|
||||||
|
OnlineTicket ticket=onlineTicketStore.get(ticketId);
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package org.maxkey.authn.online;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import org.maxkey.domain.apps.Apps;
|
||||||
|
|
||||||
|
public class OnlineTicket implements Serializable{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 9008067569150338296L;
|
||||||
|
|
||||||
|
public String id;
|
||||||
|
|
||||||
|
private Apps authorizeApps;
|
||||||
|
|
||||||
|
|
||||||
|
public OnlineTicket(String id) {
|
||||||
|
super();
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("OnlineTicket [id=");
|
||||||
|
builder.append(id);
|
||||||
|
builder.append("]");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||||
|
*
|
||||||
|
* 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 org.maxkey.authn.online;
|
||||||
|
|
||||||
|
|
||||||
|
public interface OnlineTicketServices {
|
||||||
|
|
||||||
|
public void store(String ticketId, OnlineTicket ticket);
|
||||||
|
|
||||||
|
public OnlineTicket remove(String ticket);
|
||||||
|
|
||||||
|
public OnlineTicket get(String ticketId);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
|
||||||
|
*
|
||||||
|
* 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 org.maxkey.authn.online;
|
||||||
|
|
||||||
|
import org.maxkey.persistence.redis.RedisConnection;
|
||||||
|
import org.maxkey.persistence.redis.RedisConnectionFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public class RedisOnlineTicketServices implements OnlineTicketServices {
|
||||||
|
|
||||||
|
|
||||||
|
protected int serviceTicketValiditySeconds = 60 * 30; //default 30 minutes.
|
||||||
|
|
||||||
|
RedisConnectionFactory connectionFactory;
|
||||||
|
|
||||||
|
public static String PREFIX="REDIS_ONLINE_TICKET_";
|
||||||
|
/**
|
||||||
|
* @param connectionFactory
|
||||||
|
*/
|
||||||
|
public RedisOnlineTicketServices(RedisConnectionFactory connectionFactory) {
|
||||||
|
super();
|
||||||
|
this.connectionFactory = connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public RedisOnlineTicketServices() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnectionFactory(RedisConnectionFactory connectionFactory) {
|
||||||
|
this.connectionFactory = connectionFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void store(String ticketId, OnlineTicket ticket) {
|
||||||
|
RedisConnection conn=connectionFactory.getConnection();
|
||||||
|
conn.setexObject(PREFIX+ticketId, serviceTicketValiditySeconds, ticket);
|
||||||
|
conn.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OnlineTicket remove(String ticketId) {
|
||||||
|
RedisConnection conn=connectionFactory.getConnection();
|
||||||
|
OnlineTicket ticket = conn.getObject(PREFIX+ticketId);
|
||||||
|
conn.delete(PREFIX+ticketId);
|
||||||
|
conn.close();
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OnlineTicket get(String ticketId) {
|
||||||
|
RedisConnection conn=connectionFactory.getConnection();
|
||||||
|
OnlineTicket ticket = conn.getObject(PREFIX+ticketId);
|
||||||
|
conn.close();
|
||||||
|
return ticket;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -109,7 +109,10 @@ public class Apps extends JpaBaseDomain implements Serializable {
|
|||||||
private String principal;
|
private String principal;
|
||||||
@Column
|
@Column
|
||||||
private String credentials;
|
private String credentials;
|
||||||
|
@Column
|
||||||
|
private String logoutUrl;
|
||||||
|
@Column
|
||||||
|
private int logoutType;
|
||||||
/*
|
/*
|
||||||
* extendAttr
|
* extendAttr
|
||||||
*/
|
*/
|
||||||
@ -516,6 +519,23 @@ public class Apps extends JpaBaseDomain implements Serializable {
|
|||||||
public void setInducer(String inducer) {
|
public void setInducer(String inducer) {
|
||||||
this.inducer = inducer;
|
this.inducer = inducer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getLogoutUrl() {
|
||||||
|
return logoutUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogoutUrl(String logoutUrl) {
|
||||||
|
this.logoutUrl = logoutUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLogoutType() {
|
||||||
|
return logoutType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogoutType(int logoutType) {
|
||||||
|
this.logoutType = logoutType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@ -552,6 +572,10 @@ public class Apps extends JpaBaseDomain implements Serializable {
|
|||||||
builder.append(principal);
|
builder.append(principal);
|
||||||
builder.append(", credentials=");
|
builder.append(", credentials=");
|
||||||
builder.append(credentials);
|
builder.append(credentials);
|
||||||
|
builder.append(", logoutUrl=");
|
||||||
|
builder.append(logoutUrl);
|
||||||
|
builder.append(", logoutType=");
|
||||||
|
builder.append(logoutType);
|
||||||
builder.append(", isExtendAttr=");
|
builder.append(", isExtendAttr=");
|
||||||
builder.append(isExtendAttr);
|
builder.append(isExtendAttr);
|
||||||
builder.append(", extendAttr=");
|
builder.append(", extendAttr=");
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.maxkey.authz.singlelogout;
|
||||||
|
|
||||||
|
public class CasSingleLogout extends SingleLogout{
|
||||||
|
|
||||||
|
public String logoutRequestMessage=
|
||||||
|
"<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"%s\" Version=\"2.0\" "
|
||||||
|
+ "IssueInstant=\"%s\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">%s"
|
||||||
|
+ "</saml:NameID><samlp:SessionIndex>%s</samlp:SessionIndex></samlp:LogoutRequest>";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendRequest() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.maxkey.authz.singlelogout;
|
||||||
|
|
||||||
|
public class DefaultSingleLogout extends SingleLogout{
|
||||||
|
|
||||||
|
public String logoutRequestMessage=
|
||||||
|
"<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"%s\" Version=\"2.0\" "
|
||||||
|
+ "IssueInstant=\"%s\"><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">%s"
|
||||||
|
+ "</saml:NameID><samlp:SessionIndex>%s</samlp:SessionIndex></samlp:LogoutRequest>";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendRequest() {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package org.maxkey.authz.singlelogout;
|
||||||
|
|
||||||
|
public class LogoutType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For no SLO.
|
||||||
|
*/
|
||||||
|
public static int NONE = 0;
|
||||||
|
/**
|
||||||
|
* For back channel SLO.
|
||||||
|
*/
|
||||||
|
public static int BACK_CHANNEL = 1;
|
||||||
|
/**
|
||||||
|
* For front channel SLO.
|
||||||
|
*/
|
||||||
|
public static int FRONT_CHANNEL = 2;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package org.maxkey.authz.singlelogout;
|
||||||
|
|
||||||
|
public abstract class SingleLogout {
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void sendRequest() ;
|
||||||
|
}
|
||||||
@ -275,6 +275,11 @@ apps.visible.all=\u6240\u6709\u7528\u6237
|
|||||||
apps.visible.internet=\u5185\u90e8\u7528\u6237
|
apps.visible.internet=\u5185\u90e8\u7528\u6237
|
||||||
apps.visible.intranet=\u5916\u90e8\u7528\u6237
|
apps.visible.intranet=\u5916\u90e8\u7528\u6237
|
||||||
apps.loginUrl=\u767b\u5f55\u5730\u5740
|
apps.loginUrl=\u767b\u5f55\u5730\u5740
|
||||||
|
apps.logoutUrl=\u6ce8\u9500\u5730\u5740
|
||||||
|
apps.logoutType=\u6ce8\u9500\u65b9\u5f0f
|
||||||
|
apps.logoutType.none=\u65e0
|
||||||
|
apps.logoutType.back_channel=\u540e\u53f0
|
||||||
|
apps.logoutType.front_channel=\u524d\u53f0
|
||||||
apps.credential=\u51ed\u8bc1\u7c7b\u578b
|
apps.credential=\u51ed\u8bc1\u7c7b\u578b
|
||||||
apps.credential.user-defined=\u7528\u6237\u81ea\u5b9a\u4e49
|
apps.credential.user-defined=\u7528\u6237\u81ea\u5b9a\u4e49
|
||||||
apps.credential.shared=\u5e94\u7528\u5171\u4eab
|
apps.credential.shared=\u5e94\u7528\u5171\u4eab
|
||||||
|
|||||||
@ -275,6 +275,11 @@ apps.visible.all=All
|
|||||||
apps.visible.internet=Internet
|
apps.visible.internet=Internet
|
||||||
apps.visible.intranet=Intranet
|
apps.visible.intranet=Intranet
|
||||||
apps.loginUrl=loginUrl
|
apps.loginUrl=loginUrl
|
||||||
|
apps.logoutUrl=logoutUrl
|
||||||
|
apps.logoutType=logoutType
|
||||||
|
apps.logoutType.none=NONE
|
||||||
|
apps.logoutType.back_channel=BACK_CHANNEL
|
||||||
|
apps.logoutType.front_channel=FRONT_CHANNEL
|
||||||
apps.credential=credential
|
apps.credential=credential
|
||||||
apps.credential.user-defined=user-defined
|
apps.credential.user-defined=user-defined
|
||||||
apps.credential.shared=shared
|
apps.credential.shared=shared
|
||||||
|
|||||||
@ -275,6 +275,11 @@ apps.visible.all=\u6240\u6709\u7528\u6237
|
|||||||
apps.visible.internet=\u5185\u90e8\u7528\u6237
|
apps.visible.internet=\u5185\u90e8\u7528\u6237
|
||||||
apps.visible.intranet=\u5916\u90e8\u7528\u6237
|
apps.visible.intranet=\u5916\u90e8\u7528\u6237
|
||||||
apps.loginUrl=\u767b\u5f55\u5730\u5740
|
apps.loginUrl=\u767b\u5f55\u5730\u5740
|
||||||
|
apps.logoutUrl=\u6ce8\u9500\u5730\u5740
|
||||||
|
apps.logoutType=\u6ce8\u9500\u65b9\u5f0f
|
||||||
|
apps.logoutType.none=\u65e0
|
||||||
|
apps.logoutType.back_channel=\u540e\u53f0
|
||||||
|
apps.logoutType.front_channel=\u524d\u53f0
|
||||||
apps.credential=\u51ed\u8bc1\u7c7b\u578b
|
apps.credential=\u51ed\u8bc1\u7c7b\u578b
|
||||||
apps.credential.user-defined=\u7528\u6237\u81ea\u5b9a\u4e49
|
apps.credential.user-defined=\u7528\u6237\u81ea\u5b9a\u4e49
|
||||||
apps.credential.shared=\u5e94\u7528\u5171\u4eab
|
apps.credential.shared=\u5e94\u7528\u5171\u4eab
|
||||||
|
|||||||
@ -29,6 +29,20 @@
|
|||||||
<input type="text" class="form-control" id="loginUrl" name="loginUrl" size="100" title="" value="" required="" />
|
<input type="text" class="form-control" id="loginUrl" name="loginUrl" size="100" title="" value="" required="" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><@locale code="apps.logoutUrl"/>:</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="logoutUrl" class="form-control" name="logoutUrl" title="" value=""/>
|
||||||
|
</td>
|
||||||
|
<th><@locale code="apps.logoutType"/></th>
|
||||||
|
<td>
|
||||||
|
<select id="logoutType" name="logoutType" class="form-control" >
|
||||||
|
<option value="0" selected ><@locale code="apps.logoutType.none"/></option>
|
||||||
|
<option value="1" ><@locale code="apps.logoutType.back_channel"/></option>
|
||||||
|
<option value="2" ><@locale code="apps.logoutType.front_channel"/></option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width:15%;"><@locale code="apps.protocol"/>:</th>
|
<th style="width:15%;"><@locale code="apps.protocol"/>:</th>
|
||||||
<td style="width:35%;">
|
<td style="width:35%;">
|
||||||
|
|||||||
@ -29,6 +29,20 @@
|
|||||||
<input type="text" class="form-control" id="loginUrl" name="loginUrl" title="" value="${model.loginUrl!}" required="" />
|
<input type="text" class="form-control" id="loginUrl" name="loginUrl" title="" value="${model.loginUrl!}" required="" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><@locale code="apps.logoutUrl"/>:</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" id="logoutUrl" class="form-control" name="logoutUrl" title="" value="${model.logoutUrl!}"/>
|
||||||
|
</td>
|
||||||
|
<th><@locale code="apps.logoutType"/></th>
|
||||||
|
<td>
|
||||||
|
<select id="logoutType" name="logoutType" class="form-control" >
|
||||||
|
<option value="0" <#if 0==model.logoutType!>selected</#if> ><@locale code="apps.logoutType.none"/></option>
|
||||||
|
<option value="1" <#if 1==model.logoutType!>selected</#if> ><@locale code="apps.logoutType.back_channel"/></option>
|
||||||
|
<option value="2" <#if 2==model.logoutType!>selected</#if> ><@locale code="apps.logoutType.front_channel"/></option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th><@locale code="apps.protocol"/>:</th>
|
<th><@locale code="apps.protocol"/>:</th>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
<#include "../../layout/header.ftl"/>
|
<#include "../../layout/header.ftl"/>
|
||||||
<#include "../../layout/common.cssjs.ftl"/>
|
<#include "../../layout/common.cssjs.ftl"/>
|
||||||
<#include "../appCommonHead.ftl"/>
|
<#include "../appCommonHead.ftl"/>
|
||||||
|
<#setting number_format="#">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form id="actionForm_app" method="post" type="label" autoclose="true" closeWindow="true"
|
<form id="actionForm_app" method="post" type="label" autoclose="true" closeWindow="true"
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<#include "../../layout/header.ftl"/>
|
<#include "../../layout/header.ftl"/>
|
||||||
<#include "../../layout/common.cssjs.ftl"/>
|
<#include "../../layout/common.cssjs.ftl"/>
|
||||||
|
<#setting number_format="#">
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
.table th, .table td {
|
.table th, .table td {
|
||||||
padding: .2rem;
|
padding: .2rem;
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
<#include "../../layout/header.ftl"/>
|
<#include "../../layout/header.ftl"/>
|
||||||
<#include "../../layout/common.cssjs.ftl"/>
|
<#include "../../layout/common.cssjs.ftl"/>
|
||||||
<#include "../appCommonHead.ftl"/>
|
<#include "../appCommonHead.ftl"/>
|
||||||
|
<#setting number_format="#">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
<!--
|
<!--
|
||||||
$(function(){
|
$(function(){
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
<#include "../../layout/header.ftl"/>
|
<#include "../../layout/header.ftl"/>
|
||||||
<#include "../../layout/common.cssjs.ftl"/>
|
<#include "../../layout/common.cssjs.ftl"/>
|
||||||
<#include "../appCommonHead.ftl"/>
|
<#include "../appCommonHead.ftl"/>
|
||||||
|
<#setting number_format="#">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
<!--
|
<!--
|
||||||
$(function(){
|
$(function(){
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
<#include "../../layout/header.ftl"/>
|
<#include "../../layout/header.ftl"/>
|
||||||
<#include "../../layout/common.cssjs.ftl"/>
|
<#include "../../layout/common.cssjs.ftl"/>
|
||||||
<#include "../appCommonHead.ftl"/>
|
<#include "../appCommonHead.ftl"/>
|
||||||
|
<#setting number_format="#">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
<!--
|
<!--
|
||||||
$(function(){
|
$(function(){
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
<#include "../../layout/header.ftl"/>
|
<#include "../../layout/header.ftl"/>
|
||||||
<#include "../../layout/common.cssjs.ftl"/>
|
<#include "../../layout/common.cssjs.ftl"/>
|
||||||
<#include "../appCommonHead.ftl"/>
|
<#include "../appCommonHead.ftl"/>
|
||||||
|
<#setting number_format="#">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form id="actionForm_app" method="post" type="label" autoclose="true" closeWindow="true"
|
<form id="actionForm_app" method="post" type="label" autoclose="true" closeWindow="true"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user