Browse Source

localization

MaxKey 3 years ago
parent
commit
bd04a24bd8
29 changed files with 615 additions and 37 deletions
  1. 6 0
      maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java
  2. 25 0
      maxkey-core/src/main/java/org/maxkey/entity/Institutions.java
  3. 126 0
      maxkey-core/src/main/java/org/maxkey/entity/Localization.java
  4. 30 2
      maxkey-core/src/main/java/org/maxkey/persistence/db/InstitutionService.java
  5. 156 0
      maxkey-core/src/main/java/org/maxkey/persistence/db/LocalizationService.java
  6. 4 2
      maxkey-core/src/main/java/org/maxkey/web/WebConstants.java
  7. 15 0
      maxkey-core/src/main/java/org/maxkey/web/WebContext.java
  8. 1 4
      maxkey-core/src/main/java/org/maxkey/web/WebInstRequestFilter.java
  9. 5 0
      maxkey-core/src/main/java/org/maxkey/web/WebXssRequestFilter.java
  10. 26 17
      maxkey-core/src/main/java/org/maxkey/web/tag/LocaleTagDirective.java
  11. 1 1
      maxkey-identitys/maxkey-synchronizers-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryOrganizationService.java
  12. 1 0
      maxkey-identitys/maxkey-synchronizers-dingding/src/main/java/org/maxkey/synchronizer/dingding/DingdingOrganizationService.java
  13. 9 1
      maxkey-identitys/maxkey-synchronizers-dingding/src/main/java/org/maxkey/synchronizer/dingding/DingdingUsersService.java
  14. 2 0
      maxkey-identitys/maxkey-synchronizers-workweixin/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinOrganizationService.java
  15. 1 0
      maxkey-identitys/maxkey-synchronizers-workweixin/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinUsersService.java
  16. 1 1
      maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java
  17. 1 1
      maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/layout/header.ftl
  18. 1 1
      maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/layout/nologintop.ftl
  19. 1 1
      maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/layout/top.ftl
  20. 1 0
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtMvcConfig.java
  21. 88 0
      maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/contorller/LocalizationController.java
  22. 7 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message.properties
  23. 8 1
      maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_en.properties
  24. 6 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_zh_CN.properties
  25. 34 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/institutions/updateInstitutions.ftl
  26. 1 1
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/header.ftl
  27. 2 2
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/nologintop.ftl
  28. 2 2
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/top.ftl
  29. 54 0
      maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/localization/updateLocalization.ftl

+ 6 - 0
maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java

@@ -30,6 +30,7 @@ import org.maxkey.crypto.password.PasswordReciprocal;
 import org.maxkey.crypto.password.SM3PasswordEncoder;
 import org.maxkey.crypto.password.StandardPasswordEncoder;
 import org.maxkey.persistence.db.InstitutionService;
+import org.maxkey.persistence.db.LocalizationService;
 import org.maxkey.util.IdGenerator;
 import org.maxkey.util.SnowFlakeId;
 import org.maxkey.web.WebContext;
@@ -69,6 +70,11 @@ public class ApplicationAutoConfiguration  implements InitializingBean {
         return new InstitutionService(jdbcTemplate);
     }
     
+    @Bean(name = "localizationService")
+    public LocalizationService localizationService(JdbcTemplate jdbcTemplate,InstitutionService institutionService) {
+        return new LocalizationService(jdbcTemplate,institutionService);
+    }
+    
     /**
      * Authentication Password Encoder .
      * @return

+ 25 - 0
maxkey-core/src/main/java/org/maxkey/entity/Institutions.java

@@ -46,6 +46,10 @@ public class Institutions extends JpaBaseEntity implements Serializable {
 	@Column
 	private String logo;
 	@Column
+	private String title;
+	@Column
+	private String consoleTitle;
+	@Column
 	private String domain;
 	@Column
     private String division;
@@ -111,6 +115,19 @@ public class Institutions extends JpaBaseEntity implements Serializable {
 	public void setLogo(String logo) {
 		this.logo = logo;
 	}
+	
+	public String getTitle() {
+		return title;
+	}
+	public void setTitle(String title) {
+		this.title = title;
+	}
+	public String getConsoleTitle() {
+		return consoleTitle;
+	}
+	public void setConsoleTitle(String consoleTitle) {
+		this.consoleTitle = consoleTitle;
+	}
 	public String getDomain() {
 		return domain;
 	}
@@ -228,6 +245,14 @@ public class Institutions extends JpaBaseEntity implements Serializable {
 		builder.append(name);
 		builder.append(", fullName=");
 		builder.append(fullName);
+		builder.append(", logo=");
+		builder.append(logo);
+		builder.append(", title=");
+		builder.append(title);
+		builder.append(", consoleTitle=");
+		builder.append(consoleTitle);
+		builder.append(", domain=");
+		builder.append(domain);
 		builder.append(", division=");
 		builder.append(division);
 		builder.append(", country=");

+ 126 - 0
maxkey-core/src/main/java/org/maxkey/entity/Localization.java

@@ -0,0 +1,126 @@
+package org.maxkey.entity;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.apache.mybatis.jpa.persistence.JpaBaseEntity;
+
+@Entity
+@Table(name = "MXK_LOCALIZATION")
+public class Localization  extends JpaBaseEntity implements Serializable {
+
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -142504964446659847L;
+	@Id
+	@Column
+	@GeneratedValue(strategy = GenerationType.AUTO, generator = "snowflakeid")
+	private String id;
+	
+	@Column
+	private String property;
+
+	@Column
+	private String langZh;
+	
+	@Column
+	private String langEn;
+	
+	@Column
+	private String description;
+	
+	@Column
+	private int status;
+	
+	@Column
+	private String instId;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+
+
+	public String getProperty() {
+		return property;
+	}
+
+	public void setProperty(String property) {
+		this.property = property;
+	}
+
+
+
+	public String getLangZh() {
+		return langZh;
+	}
+
+	public void setLangZh(String langZh) {
+		this.langZh = langZh;
+	}
+
+	public String getLangEn() {
+		return langEn;
+	}
+
+	public void setLangEn(String langEn) {
+		this.langEn = langEn;
+	}
+
+	public String getDescription() {
+		return description;
+	}
+
+	public void setDescription(String description) {
+		this.description = description;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public String getInstId() {
+		return instId;
+	}
+
+	public void setInstId(String instId) {
+		this.instId = instId;
+	}
+
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("Localization [id=");
+		builder.append(id);
+		builder.append(", property=");
+		builder.append(property);
+		builder.append(", langZh=");
+		builder.append(langZh);
+		builder.append(", langEn=");
+		builder.append(langEn);
+		builder.append(", description=");
+		builder.append(description);
+		builder.append(", status=");
+		builder.append(status);
+		builder.append(", instId=");
+		builder.append(instId);
+		builder.append("]");
+		return builder.toString();
+	}
+
+}

+ 30 - 2
maxkey-core/src/main/java/org/maxkey/persistence/db/InstitutionService.java

@@ -20,6 +20,7 @@ package org.maxkey.persistence.db;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
 import org.maxkey.constants.ConstantsStatus;
@@ -36,13 +37,20 @@ import com.github.benmanes.caffeine.cache.Caffeine;
 public class InstitutionService {
     private static Logger _logger = LoggerFactory.getLogger(InstitutionService.class);
     
-    private static final String SELECT_STATEMENT = "select * from  mxk_institutions where domain = ? and status = " + ConstantsStatus.ACTIVE;
+    private static final String SELECT_STATEMENT = 
+    						"select * from  mxk_institutions where domain = ? and status = " + ConstantsStatus.ACTIVE;
+
+    private static final String SELECT_STATEMENT_BY_ID = 
+    						"select * from  mxk_institutions where id = ? and status = " + ConstantsStatus.ACTIVE;
 
     protected static final Cache<String, Institutions> institutionsStore = 
             Caffeine.newBuilder()
-                .expireAfterWrite(ConstantsTimeInterval.ONE_HOUR, TimeUnit.MINUTES)
+                .expireAfterWrite(ConstantsTimeInterval.ONE_HOUR, TimeUnit.SECONDS)
                 .build();
     
+    //id domain mapping
+    protected static final  ConcurrentHashMap<String,String> mapper = new ConcurrentHashMap<String,String>();
+    
     protected JdbcTemplate jdbcTemplate;
     
     public InstitutionService(JdbcTemplate jdbcTemplate) {
@@ -61,6 +69,24 @@ public class InstitutionService {
 	        }
 	        
 	        institutionsStore.put(domain, inst);
+	        mapper.put(inst.getId(), domain);
+        }
+        
+        return inst;
+    }
+    
+    public Institutions get(String instId) {
+        _logger.trace(" instId {}" , instId);
+        Institutions inst = institutionsStore.getIfPresent(mapper.get(instId));
+        if(inst == null) {
+	        List<Institutions> institutions = 
+	        		jdbcTemplate.query(SELECT_STATEMENT_BY_ID,new InstitutionsRowMapper(),instId);
+	        
+	        if (institutions != null && institutions.size() > 0) {
+	        	inst = institutions.get(0);
+	        }
+	        institutionsStore.put(inst.getDomain(), inst);
+	        mapper.put(inst.getId(), inst.getDomain());
         }
         
         return inst;
@@ -75,6 +101,8 @@ public class InstitutionService {
         	institution.setFullName(rs.getString("fullname"));
         	institution.setLogo(rs.getString("logo"));
         	institution.setDomain(rs.getString("domain"));
+        	institution.setTitle(rs.getString("title"));
+        	institution.setConsoleTitle(rs.getString("consoletitle"));
             return institution;
         }
     }

+ 156 - 0
maxkey-core/src/main/java/org/maxkey/persistence/db/LocalizationService.java

@@ -0,0 +1,156 @@
+/*
+ * Copyright [2022] [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.persistence.db;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import org.maxkey.constants.ConstantsTimeInterval;
+import org.maxkey.entity.Localization;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+public class LocalizationService {
+	private static Logger _logger = LoggerFactory.getLogger(LocalizationService.class);
+	private static final String INSERT_STATEMENT ="insert into mxk_localization (id, property,langzh,langen,status,description,instid)values(?,?,?,?,?,?,?)";
+	private static final String UPDATE_STATEMENT ="update mxk_localization set langzh = ? , langen =? where id = ?";
+	private static final String DELETE_STATEMENT ="delete from  mxk_localization where id = ?";
+	private static final String SELECT_STATEMENT ="select * from  mxk_localization where ( id = ? ) or (property = ? and instid = ?)";
+	
+	private static final Pattern PATTERN_HTML = Pattern.compile("<[^>]+>", Pattern.CASE_INSENSITIVE);
+	 
+	protected InstitutionService institutionService;
+	
+	JdbcTemplate jdbcTemplate;
+	
+    protected static final Cache<String, String> localizationStore = 
+            Caffeine.newBuilder()
+                .expireAfterWrite(ConstantsTimeInterval.ONE_HOUR, TimeUnit.SECONDS)
+                .build();
+
+	public LocalizationService() {
+
+	}
+	
+	public String getLocale(String code,String htmlTag,Locale locale,String inst) {
+		String message = "";
+		htmlTag = (htmlTag == null ||htmlTag.equalsIgnoreCase("true")) ? "tag" : "rtag";
+		
+		if(code.equals("global.logo")) {
+			message = institutionService.get(inst).getLogo();
+		}else if(code.equals("global.title")) {
+			message = getFromStore(code, htmlTag, locale, inst);
+			if(message == null) {
+				message = institutionService.get(inst).getTitle();
+			}
+		}else if(code.equals("global.consoleTitle")) {
+			message = getFromStore(code, htmlTag, locale, inst);
+			if(message == null) {
+				message = institutionService.get(inst).getConsoleTitle();
+			}
+		}else {
+			message = getFromStore(code, htmlTag, locale, inst);
+		}
+		if(htmlTag.equalsIgnoreCase("rtag")) {
+			message = clearHTMLToString(message);
+		}
+		_logger.trace("{} = {}" , code , message);
+		return message == null ? "" : message;
+	}
+	
+	public String clearHTMLToString(String message) {
+		return PATTERN_HTML.matcher(message).replaceAll("");
+	}
+
+	public String getFromStore(String code,String htmlTag,Locale locale,String inst) {
+		String message = localizationStore.getIfPresent(code+"_"+locale.getLanguage()+"_"+inst);
+		if(message != null) return message;
+		Localization localization = get(code,inst);
+		if(localization != null) {
+			localizationStore.put(code+"_en_"+inst, localization.getLangEn());
+			localizationStore.put(code+"_zh_"+inst, localization.getLangZh());
+			if(locale.getLanguage().equals("en")) {
+				message = localization.getLangEn();
+			}else {
+				message = localization.getLangZh();
+			}
+			if(message != null) return message;
+		}
+		return message;
+	}
+	
+	public void setInstitutionService(InstitutionService institutionService) {
+		this.institutionService = institutionService;
+	}
+
+	public boolean insert(Localization localization) {
+		return jdbcTemplate.update(INSERT_STATEMENT,
+									new Object[] {localization.getId(),localization.getProperty(),
+												  localization.getLangZh(),localization.getLangEn(),
+												  localization.getStatus(),localization.getDescription(),
+												  localization.getInstId()}, 
+									new int[] {Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER,
+											   Types.VARCHAR, Types.VARCHAR,}) > 0;
+	}
+	
+	public boolean update(Localization localization) {
+		jdbcTemplate.update(UPDATE_STATEMENT,localization.getLangZh(),localization.getLangEn(),localization.getId());
+		return true;
+	}
+	
+	public boolean remove(String id) {
+		return jdbcTemplate.update(DELETE_STATEMENT,id) > 0;
+	}
+	
+	public Localization get(String property,String instId) {
+		_logger.debug("load property from database , property {} ,instId {}",property, instId);
+		List<Localization>  localizations = 
+				jdbcTemplate.query(
+							SELECT_STATEMENT,new LocalizationRowMapper(),property,property,instId);
+		return (localizations==null || localizations.size()==0) ? null : localizations.get(0);
+	}
+	
+	public LocalizationService(JdbcTemplate jdbcTemplate,InstitutionService institutionService) {
+		super();
+		this.institutionService = institutionService;
+		this.jdbcTemplate = jdbcTemplate;
+	}
+    
+	   public class LocalizationRowMapper implements RowMapper<Localization> {
+	        @Override
+	        public Localization mapRow(ResultSet rs, int rowNum) throws SQLException {
+	        	Localization localization = new Localization();
+	        	localization.setId(rs.getString("id"));
+	        	localization.setProperty(rs.getString("property"));
+	        	localization.setLangZh(rs.getString("langzh"));
+	        	localization.setLangEn(rs.getString("langen"));
+	        	localization.setStatus(rs.getInt("status"));
+	        	localization.setDescription(rs.getString("description"));
+	        	localization.setInstId(rs.getString("instid"));
+	            return localization;
+	        }
+	    }
+}

+ 4 - 2
maxkey-core/src/main/java/org/maxkey/web/WebConstants.java

@@ -46,9 +46,11 @@ public class WebConstants {
     public static final  String CURRENT_USER_PASSWORD_SET_TYPE 
                                     = "current_user_password_set_type";
 
-    public static final  String CURRENT_MESSAGE = "current_message";
+    public static final  String CURRENT_MESSAGE 	= "current_message";
     
-    public static final  String CURRENT_INST 	= "current_inst";
+    public static final  String CURRENT_INST 		= "current_inst";
+    
+    public final static  String INST_COOKIE_NAME 	= "mxk_inst";
 
     // SPRING_SECURITY_SAVED_REQUEST
     public static final  String FIRST_SAVED_REQUEST_PARAMETER 

+ 15 - 0
maxkey-core/src/main/java/org/maxkey/web/WebContext.java

@@ -28,8 +28,11 @@ import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
+
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.logging.LogFactory;
 import org.maxkey.configuration.ApplicationConfig;
+import org.maxkey.entity.Institutions;
 import org.maxkey.entity.UserInfo;
 import org.maxkey.util.DateUtils;
 import org.maxkey.util.IdGenerator;
@@ -120,6 +123,18 @@ public final class WebContext {
     public static UserInfo getUserInfo() {
         return ((UserInfo) getAttribute(WebConstants.CURRENT_USER));
     }
+    
+    public static String getInst(HttpServletRequest request) {
+    	String instId = "1";
+    	//from session
+    	if(getAttribute(WebConstants.CURRENT_INST) != null) {
+    		instId = ((Institutions)request.getSession().getAttribute(WebConstants.CURRENT_INST)).getId();
+    	}else {
+    	//from cookie
+    		instId = WebContext.readCookieByName(request, WebConstants.INST_COOKIE_NAME).getValue();
+    	}
+        return StringUtils.isBlank(instId) ? "1" : instId;
+    }
 
     /**
      * set Message to session,session id is Constants.MESSAGE

+ 1 - 4
maxkey-core/src/main/java/org/maxkey/web/WebInstRequestFilter.java

@@ -39,13 +39,10 @@ public class WebInstRequestFilter  extends GenericFilterBean {
 	
 	public final static String  HEADER_HOST = "host";
 	
-	public final static String  LOGO_COOKIE_NAME = "mxk_logo";
-
 	InstitutionService institutionService;
 	
 	ApplicationConfig applicationConfig;
 	
-	
 	@Override
 	public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
 			throws IOException, ServletException {
@@ -64,7 +61,7 @@ public class WebInstRequestFilter  extends GenericFilterBean {
 			Institutions institution =institutionService.findByDomain(host);
 			_logger.trace("{}" ,institution);
 			request.getSession().setAttribute(WebConstants.CURRENT_INST, institution);
-			WebContext.setCookie((HttpServletResponse)servletResponse, host, LOGO_COOKIE_NAME, institution.getLogo());
+			WebContext.setCookie((HttpServletResponse)servletResponse, host, WebConstants.INST_COOKIE_NAME, institution.getId());
 		}
         chain.doFilter(servletRequest, servletResponse);
 	}

+ 5 - 0
maxkey-core/src/main/java/org/maxkey/web/WebXssRequestFilter.java

@@ -39,14 +39,19 @@ public class WebXssRequestFilter  extends GenericFilterBean {
 	final static ConcurrentHashMap <String,String> skipUrlMap = new  ConcurrentHashMap <String,String>();
 	
 	static {
+		//add or update
 		skipUrlMap.put("/notices/add", "/notices/add");
 		skipUrlMap.put("/notices/update", "/notices/update");
+		skipUrlMap.put("/institutions/update","/institutions/update");
+		skipUrlMap.put("/localization/update","/localization/update");
+		//authz
 		skipUrlMap.put("/authz/cas", "/authz/cas");
 		skipUrlMap.put("/authz/cas/", "/authz/cas/");
 		skipUrlMap.put("/authz/cas/login", "/authz/cas/login");
 		skipUrlMap.put("/authz/oauth/v20/authorize", "/authz/oauth/v20/authorize");
 		//TENCENT_IOA
 		skipUrlMap.put("/oauth2/authorize", "/oauth2/authorize");
+		
 	}
 	
 	@Override

+ 26 - 17
maxkey-core/src/main/java/org/maxkey/web/tag/LocaleTagDirective.java

@@ -26,10 +26,8 @@ import java.io.IOException;
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 
-import org.maxkey.entity.Institutions;
-import org.maxkey.web.WebConstants;
+import org.maxkey.persistence.db.LocalizationService;
 import org.maxkey.web.WebContext;
-import org.maxkey.web.WebInstRequestFilter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -51,6 +49,9 @@ public class LocaleTagDirective implements TemplateDirectiveModel {
 
     @Autowired
     private HttpServletRequest request;
+    
+    @Autowired
+    LocalizationService localizationService;
 
     @SuppressWarnings("rawtypes")
     @Override
@@ -60,31 +61,39 @@ public class LocaleTagDirective implements TemplateDirectiveModel {
         WebApplicationContext webApplicationContext = 
                 RequestContextUtils.findWebApplicationContext(request);
         String message = "";
-        if (params.get("code") == null) {
+        String code = params.get("code") == null? null : params.get("code").toString();
+        String htmlTag = params.get("htmltag")==null ? null : params.get("htmltag").toString();
+        _logger.trace("message code {} , htmltag {}" , code , htmlTag);
+        
+        if (code == null) {
             message = RequestContextUtils.getLocale(request).getLanguage();
-        } else if (params.get("code").toString().equals("global.application.version")
-                || params.get("code").toString().equals("application.version")) {
+        } else if (code.equals("global.application.version")
+                || code.equals("application.version")) {
             message = WebContext.properties.getProperty("application.formatted-version");
-        } else if (params.get("code").toString().equals("global.logo")) {
-        	if(request.getSession().getAttribute(WebConstants.CURRENT_INST)!=null) {
-        		message = ((Institutions)request.getSession().getAttribute(WebConstants.CURRENT_INST)).getLogo();
-        	}else {
-        		message = WebContext.readCookieByName(request, WebInstRequestFilter.LOGO_COOKIE_NAME).getValue();
-        	}
-        	
+        } else if (code.equals("global.logo")) {
+        	message = localizationService.getLocale(
+        						code,
+        						htmlTag,
+        						WebContext.getLocale(),
+        						WebContext.getInst(request));
         	if(!message.startsWith("http")) {
             	message = request.getContextPath() + message;
             }
+        }else if (code.equals("global.title")
+        			||code.equals("global.consoleTitle")) {
+        	message = localizationService.getLocale(
+        						code,
+        						htmlTag,
+        						WebContext.getLocale(),
+        						WebContext.getInst(request));
         } else {
-            _logger.trace("message code " + params.get("code"));
             try {
                 message = webApplicationContext.getMessage(
-                                params.get("code").toString(), 
+                                code, 
                                 null,
                                 RequestContextUtils.getLocale(request));
-
             } catch (Exception e) {
-                _logger.error("message code " + params.get("code"), e);
+                _logger.error("message code " + code, e);
             }
         }
         env.getOut().append(message);

+ 1 - 1
maxkey-identitys/maxkey-synchronizers-activedirectory/src/main/java/org/maxkey/synchronizer/activedirectory/ActiveDirectoryOrganizationService.java

@@ -123,7 +123,7 @@ public class ActiveDirectoryOrganizationService  extends AbstractSynchronizerSer
 			org.setInstId(this.synchronizer.getInstId());
 			orgsNamePathMap.put(org.getNamePath(), org);
 			_logger.info("org " + org);
-			organizationsService.insert(org);
+			organizationsService.merge(org);
 			HistorySynchronizer historySynchronizer =new HistorySynchronizer();
             historySynchronizer.setId(historySynchronizer.generateId());
             historySynchronizer.setSyncId(this.synchronizer.getId());

+ 1 - 0
maxkey-identitys/maxkey-synchronizers-dingding/src/main/java/org/maxkey/synchronizer/dingding/DingdingOrganizationService.java

@@ -47,6 +47,7 @@ public class DingdingOrganizationService  extends AbstractSynchronizerService im
 			for(DeptBaseResponse dept : rsp.getResult()) {
 				_logger.info("dept : " + dept.getDeptId()+" "+ dept.getName()+" "+ dept.getParentId());
 				Organizations org = buildOrganization(dept);
+				this.organizationsService.merge(org);
 				_logger.info("Organizations : " + org);
 			}
 

+ 9 - 1
maxkey-identitys/maxkey-synchronizers-dingding/src/main/java/org/maxkey/synchronizer/dingding/DingdingUsersService.java

@@ -17,6 +17,7 @@
 
 package org.maxkey.synchronizer.dingding;
 
+import org.apache.commons.lang3.StringUtils;
 import org.joda.time.DateTime;
 import org.joda.time.format.DateTimeFormat;
 import org.maxkey.entity.UserInfo;
@@ -65,6 +66,13 @@ public class DingdingUsersService  extends AbstractSynchronizerService implement
 						_logger.info("name : " + user.getName()+" , "+user.getLoginId()+" , "+user.getUserid());
 						UserInfo userInfo  = buildUserInfo(user);
 						_logger.info("userInfo " + userInfo);
+						//if(userInfoService.findByUsername(userInfo.getUsername()) == null) {
+							userInfo.setPassword(userInfo.getUsername() + "Maxkey@888");
+							this.userInfoService.insert(userInfo);
+						//}else {
+						//	userInfoService.update(userInfo);
+						//}
+						
 					}
 				}
 			}
@@ -88,7 +96,7 @@ public class DingdingUsersService  extends AbstractSynchronizerService implement
 		userInfo.setDisplayName(user.getName());//鐢ㄦ埛鍚�
 		userInfo.setFormattedName(user.getName());//鐢ㄦ埛鍚�
 		
-		userInfo.setEmail(user.getEmail());
+		userInfo.setEmail(StringUtils.isBlank(user.getEmail())? user.getUserid() +"@maxkey.top":user.getEmail());
 		userInfo.setEntryDate(new DateTime(user.getHiredDate()).toString(DateTimeFormat.forPattern("yyyy-MM-dd")));
 		userInfo.setMobile(user.getMobile());//鎵嬫満
 		userInfo.setDepartmentId(user.getDeptIdList().get(0)+"");

+ 2 - 0
maxkey-identitys/maxkey-synchronizers-workweixin/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinOrganizationService.java

@@ -46,6 +46,8 @@ public class WorkweixinOrganizationService extends AbstractSynchronizerService i
 			
 			for(WorkWeixinDepts dept : rsp.getDepartment()) {
 				_logger.info("dept : " + dept.getId()+" "+ dept.getName()+" "+ dept.getParentid());
+				Organizations org = buildOrganization(dept);
+				this.organizationsService.merge(org);
 			}
 
 		} catch (Exception e) {

+ 1 - 0
maxkey-identitys/maxkey-synchronizers-workweixin/src/main/java/org/maxkey/synchronizer/workweixin/WorkweixinUsersService.java

@@ -54,6 +54,7 @@ public class WorkweixinUsersService extends AbstractSynchronizerService implemen
 				for(WorkWeixinUsers user : usersResponse.getUserlist()) {
 					UserInfo userInfo  = buildUserInfo(user);
 					_logger.info("userInfo : " + userInfo);
+					this.userInfoService.merge(userInfo);
 				}
 			}
 			

+ 1 - 1
maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/endpoint/LoginEndpoint.java

@@ -126,7 +126,7 @@ public class LoginEndpoint {
 		//modelAndView.addObject("jwtToken",jwtLoginService.buildLoginJwt());
 		//load Social Sign On Providers
 		if(applicationConfig.getLoginConfig().isSocialSignOn()){
-			_logger.debug("Load Social Sign On Providers ");
+			_logger.trace("Load Social Sign On Providers ");
 			Institutions inst = (Institutions)WebContext.getAttribute(WebConstants.CURRENT_INST);
 			modelAndView.addObject("sspLogin", socialSignOnProviderService.loadSocialsProviders(inst.getId()));
 		}

+ 1 - 1
maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/layout/header.ftl

@@ -1,5 +1,5 @@
     
-    <title><@locale code="global.title"/></title>
+    <title><@locale code="global.title"  htmltag="false"/></title>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 	<meta http-equiv="pragma" content="no-cache">

+ 1 - 1
maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/layout/nologintop.ftl

@@ -4,7 +4,7 @@
 			<div class="col-sm-6">
 				<div style="float:left;margin-left:20px;margin-top: 5px;"><IMG SRC="<@locale code="global.logo"/>" style="width:55px;heigth:55px"></div>
 				<div style="margin-top:15px;margin-left:10px;float:left">
-					<div style="letter-spacing:2px;font-size:24px;font-weight:bolder;"><@locale code="global.application"/></div>
+					<div style="letter-spacing:2px;font-size:24px;font-weight:bolder;"><@locale code="global.title"/></div>
 				</div>
 			</div>
 			<div class="col-sm-2"></div>

+ 1 - 1
maxkey-webs/maxkey-web-maxkey/src/main/resources/templates/views/layout/top.ftl

@@ -4,7 +4,7 @@
     	<div class="col-sm-5">
     		<div>
     		<div style="float:left;margin-top: 5px;"><IMG SRC="<@locale code="global.logo"/>" style="width:55px;heigth:55px"></div>
-    		<div style="letter-spacing:2px;font-size:24px;font-weight:bolder;margin-top: 16px;float:left;"><@locale code="global.application"/></div>
+    		<div style="letter-spacing:2px;font-size:24px;font-weight:bolder;margin-top: 16px;float:left;"><@locale code="global.title"/></div>
     		</div>
     	</div>
     	<div class="col-sm-4">

+ 1 - 0
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtMvcConfig.java

@@ -130,6 +130,7 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
                 .addPathPatterns("/socialsprovider/**")
                 .addPathPatterns("/accountsstrategy/**")
                 .addPathPatterns("/institutions/**")
+                .addPathPatterns("/localization/**")
                 
                 ;
         

+ 88 - 0
maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/contorller/LocalizationController.java

@@ -0,0 +1,88 @@
+/*
+ * Copyright [2022] [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.web.contorller;
+
+import org.apache.commons.lang3.StringUtils;
+import org.maxkey.constants.ConstantsOperateMessage;
+import org.maxkey.entity.Localization;
+import org.maxkey.persistence.db.LocalizationService;
+import org.maxkey.web.WebContext;
+import org.maxkey.web.message.Message;
+import org.maxkey.web.message.MessageType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.ModelAndView;
+
+@Controller
+@RequestMapping(value={"/localization"})
+public class LocalizationController {
+
+
+		final static Logger _logger = LoggerFactory.getLogger(LocalizationController.class);
+		
+		@Autowired
+		private LocalizationService localizationService;
+		
+		/**
+		 * 读取
+		 * @return
+		 */
+		@RequestMapping(value={"/forward/{property}"})
+		public ModelAndView forward(@PathVariable("property") String property){
+			Localization localization = localizationService.get(property,WebContext.getUserInfo().getInstId());
+			if(localization == null )localization = new Localization();
+			localization.setProperty(property);
+			localization.setInstId(WebContext.getUserInfo().getInstId());
+			return new ModelAndView("localization/updateLocalization","model",localization);
+		}
+		
+		/**
+		 * 更新
+		 * @param sysConfig
+		 * @return
+		 */
+		@RequestMapping(value={"/update"})
+		@ResponseBody
+		public Message updat(@ModelAttribute("localization") Localization localization,BindingResult result) {
+			_logger.debug("update  localization : "+localization);
+			localization.setInstId(WebContext.getUserInfo().getInstId());
+			if(StringUtils.isBlank(localization.getId())){
+				localization.setId(localization.generateId());
+				if(localizationService.insert(localization)) {
+					return new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_SUCCESS),MessageType.success);
+				} else {
+					return new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_ERROR),MessageType.error);
+				}
+			}else {
+				if(localizationService.update(localization)) {
+					return new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_SUCCESS),MessageType.success);
+				} else {
+					return new Message(WebContext.getI18nValue(ConstantsOperateMessage.UPDATE_ERROR),MessageType.error);
+				}
+			}
+		}
+		
+
+}

+ 7 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message.properties

@@ -56,6 +56,7 @@ common.text.status.delete=\u5220\u9664
 common.text.description=\u63CF\u8FF0
 common.text.yes=\u662F
 common.text.no=\u5426
+common.text.locale=\u591A\u8BED\u8A00
 
 main.rpt.newuser=\u5F53\u6708\u65B0\u7528\u6237
 main.rpt.activeuser=\u672C\u6708\u6D3B\u8DC3\u7528\u6237
@@ -494,6 +495,8 @@ synchronizers.syncStartTime=\u533A\u95F4
 institutions.name=\u7B80\u79F0
 institutions.fullName=\u5168\u79F0
 institutions.logo=\u56FE\u6807
+institutions.title=\u7CFB\u7EDF\u540D\u79F0
+institutions.consoleTitle=\u63A7\u5236\u53F0\u540D\u79F0
 institutions.domain=\u57DF\u540D
 institutions.division=\u5206\u652F\u673A\u6784
 institutions.contact=\u8054\u7CFB\u4EBA
@@ -506,6 +509,10 @@ institutions.locality=\u5E02
 institutions.street=\u8857\u9053
 institutions.address=\u5730\u5740
 institutions.postalcode=\u90AE\u7F16
+
+localization.property=\u5C5E\u6027
+localization.langZh=\u4E2D\u6587
+localization.langEn=\u82F1\u6587
 #button
 button.text.action=\u8BBF\u95EE
 button.text.visit=\u8BBF\u95EE

+ 8 - 1
maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_en.properties

@@ -56,6 +56,7 @@ common.text.status.delete=delete
 common.text.description=description
 common.text.yes=yes
 common.text.no=no
+common.text.locale=Locale
 
 
 main.rpt.newuser=New Users/Month
@@ -502,7 +503,9 @@ synchronizers.syncStartTime=During
 
 institutions.name=name
 institutions.fullName=fullName
-institutions.logo=Logo
+institutions.logo=logo
+institutions.title=Title
+institutions.consoleTitle=ConsoleTitle
 institutions.domain=domainName
 institutions.division=division
 institutions.contact=contact
@@ -515,6 +518,10 @@ institutions.locality=locality
 institutions.street=street
 institutions.address=address
 institutions.postalcode=postalcode
+
+localization.property=Property
+localization.langZh=Chinese
+localization.langEn=English
  
 button.text.action=Action
 button.text.visit=Visit

+ 6 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/messages/message_zh_CN.properties

@@ -56,6 +56,7 @@ common.text.status.delete=\u5220\u9664
 common.text.description=\u63CF\u8FF0
 common.text.yes=\u662F
 common.text.no=\u5426
+common.text.locale=\u591A\u8BED\u8A00
 
 main.rpt.newuser=\u5F53\u6708\u65B0\u7528\u6237
 main.rpt.activeuser=\u672C\u6708\u6D3B\u8DC3\u7528\u6237
@@ -494,6 +495,8 @@ synchronizers.syncStartTime=\u533A\u95F4
 institutions.name=\u7B80\u79F0
 institutions.fullName=\u5168\u79F0
 institutions.logo=\u56FE\u6807
+institutions.title=\u7CFB\u7EDF\u540D\u79F0
+institutions.consoleTitle=\u63A7\u5236\u53F0\u540D\u79F0
 institutions.domain=\u57DF\u540D
 institutions.division=\u5206\u652F\u673A\u6784
 institutions.contact=\u8054\u7CFB\u4EBA
@@ -507,6 +510,9 @@ institutions.street=\u8857\u9053
 institutions.address=\u5730\u5740
 institutions.postalcode=\u90AE\u7F16
 
+localization.property=\u5C5E\u6027
+localization.langZh=\u4E2D\u6587
+localization.langEn=\u82F1\u6587
 #button
 button.text.action=\u8BBF\u95EE
 button.text.visit=\u8BBF\u95EE

+ 34 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/institutions/updateInstitutions.ftl

@@ -60,6 +60,39 @@
 										</div>
 									</div>
 									<div class="row mb-3">
+                                        <div class="col-md-6">
+                                            <div class="form-group row">
+                                                <label class="col-sm-3 col-form-label"><@locale code="institutions.title" />:</label>
+                                                <div class="col-sm-7">
+                                                    <input  required="" class="form-control" type="text" id="title" name="title"  value="${model.title!}" />
+                                                    
+                                                </div>
+                                                <div class="col-sm-2">
+                                                    <input class="button btn btn-info mr-3 window" type="button" value="<@locale code="common.text.locale"/>" 
+                                                        wurl="<@base/>/localization/forward/global.title"
+                                                        wwidth="650"
+                                                        wheight="200"
+                                                        target="window"> 
+                                                </div>
+                                            </div>
+                                        </div>
+                                        <div class="col-md-6">
+                                            <div class="form-group row">
+                                                <label class="col-sm-3 col-form-label"><@locale code="institutions.consoleTitle" />:</label>
+                                                <div class="col-sm-7">
+                                                    <input  required="" class="form-control" type="text" id="consoleTitle" name="consoleTitle" value="${model.consoleTitle!}" />
+                                                </div>
+                                                <div class="col-sm-2">
+                                                    <input class="button btn btn-info mr-3 window" type="button" value="<@locale code="common.text.locale"/>" 
+                                                        wurl="<@base/>/localization/forward/global.consoleTitle"
+                                                        wwidth="650"
+                                                        wheight="200"
+                                                        target="window"> 
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </div>
+									<div class="row mb-3">
 										<div class="col-md-6">
 											<div class="form-group row">
 												<label class="col-sm-3 col-form-label"><@locale code="institutions.logo" />:</label>
@@ -198,6 +231,7 @@
 									<div class="row">
 										<div class="col-md-4"></div>
 										<div class="col-md-4">
+										      <input   class="form-control" type="hidden" id="status" name="status"  value="1" />
 											<button type="submit" class="button btn-primary btn btn-common btn-block mr-3"    id="submitBtn" ><@locale code="button.text.save" /></button>
 										</div>
 										<div class="col-md-4"></div>

+ 1 - 1
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/header.ftl

@@ -1,5 +1,5 @@
 
-    <title><@locale code="global.title"/></title>
+    <title><@locale code="global.consoleTitle" htmltag="false"/></title>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 	<meta http-equiv="pragma" content="no-cache">

+ 2 - 2
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/nologintop.ftl

@@ -4,9 +4,9 @@
     <div class="container">
     	<div  class="row" > 
     		<div class="col-sm-7">
-                <div style="float:left;;margin-top: 5px;"><IMG SRC="<@base/>/static/images/logo.jpg" style="width:55px;heigth:55px"></div>
+                <div style="float:left;;margin-top: 5px;"><IMG SRC="<@locale code="global.logo"/>" style="width:55px;heigth:55px"></div>
                 <div style="margin-top:15px;margin-left:10px;float:left">
-                    <div style="font-size:24px;font-weight:bolder;"><@locale code="global.application"/></div>
+                    <div style="font-size:24px;font-weight:bolder;"><@locale code="global.consoleTitle"/></div>
                 </div>
             </div>
     		<div class="col-sm-1"></div>

+ 2 - 2
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/layout/top.ftl

@@ -3,14 +3,14 @@
 <div class="header-container">
 	<div class="nav-logo">
 		<a href="<@base/>/main"> 
-			<IMG SRC="<@base/>/static/images/logo.jpg" alt="" style="width:40px;heigth:40px">	
+			<IMG SRC="<@locale code="global.logo"/>" alt="" style="width:40px;heigth:40px">	
 		</a>
 		<span class="logo">
 		
 		</span>
 	</div>
 	<ul class="nav-left"  style="letter-spacing:2px;font-size:20px;font-weight:bolder;margin-top: 14px;">
-		<@locale code="global.application"/>
+		<@locale code="global.consoleTitle"/>
 	</ul>
 	<ul class="nav-right">
 		<li style="font-size: 16px; margin-top: 10px;">

+ 54 - 0
maxkey-webs/maxkey-web-mgt/src/main/resources/templates/views/localization/updateLocalization.ftl

@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+	<#include  "../layout/header.ftl"/>
+	<#include  "../layout/common.cssjs.ftl"/>
+</head>
+<body> 
+<div class="card">
+    <div class="card-body">
+	<form  method="post" type="label" validate="true" action="<@base/>/localization/update" id="actionForm"   class="needs-validation" novalidate>
+		<div class="row mb-3">
+			<div class="col-md-6" style="display:none">
+				<div class="form-group row">
+					<label class="col-sm-3 col-form-label"><@locale code="localization.property" />:</label>
+					<div class="col-sm-9">
+						<input id="id" name="id" type="hidden" value="${model.id!}"/>
+						<input  required="" class="form-control" type="text" id="property" name="property"  value="${model.property!}" />
+					</div>
+				</div>
+			</div>
+			<div class="col-md-6">
+				<div class="form-group row">
+					<label class="col-sm-3 col-form-label"><@locale code="localization.langZh" />:</label>
+					<div class="col-sm-9">
+						<input required=""  class="form-control" type="text" id="langZh" name="langZh" value="${model.langZh!}" />
+					</div>
+					
+				</div>
+			</div>
+			<div class="col-md-6">
+                <div class="form-group row">
+                    <label class="col-sm-3 col-form-label"><@locale code="localization.langEn" />:</label>
+                    <div class="col-sm-9">
+                        <input  required="" class="form-control" type="text" id="langEn" name="langEn"  value="${model.langEn!}" />
+                        
+                    </div>
+                </div>
+            </div>
+		</div>
+		
+		<div class="row">
+			<div class="col-md-4"></div>
+			<div class="col-md-4">
+			      <input   class="form-control" type="hidden" id="status" name="status"  value="1" />
+				<button type="submit" class="button btn-primary btn btn-common btn-block mr-3"    id="submitBtn" ><@locale code="button.text.save" /></button>
+			</div>
+			<div class="col-md-4"></div>
+		</div>
+		
+	</form>
+    </div>
+    </div>							
+</body>
+</html>