目前分類:未分類文章 (14)

瀏覽方式: 標題列表 簡短摘要

 jquery 與 axios 對於 spring boot 的 ajax 請求處理 裡談到現行的網頁操作使用 ajax 技術來節省頻寛,若想與 Vue.js 結合,則不妨使用Axios。而在使用  ajax 技術常會遇到一件事是  session timeout,後端主機會反饋一個 Status Code: 302,且 post url 轉指向 login。這時操作畫面沒有變化,但已經無法與後端主機取得資料。

StatusCode-302

 

要解決這個問題需要前端 HTML 的 java script 與後端主機 spring boot 的 Interceptor 作著手。

首先,前端 ( Front-End ) HTML 裡的 java script 對 axios 送出前加入 "X-REQUEST-TYPE":"axios",以及收到後端主機 spring boot 的 Interceptor 的 401 錯誤作導向登入畫面。

繼續閱讀:

spring boot 對於 axios 執行 ajax 遇到 response.status:302 的處理方式

MIS 發表在 痞客邦 留言(0) 人氣()

 WordPress 實用外掛推薦 有提到 WP Mail SMTP by WPForms & Post SMTP Mailer/Email Log 都是 WordPress 的寄信外掛程式,它可以讓 WordPress 透過本機或外部的郵件主機來寄發信件。

  1. WP Mail SMTP by WPForms 功能說明
  2. WP Mail SMTP by WPForms 透過 Goole API 方式寄信的設定
  3. 建立 Google Cloud IAM 專案
  4. 建立 Google API 憑證
  5. 電子郵件測試驗證

 

  • WP Mail SMTP by WPForms 功能說明

WP Mail SMTP by WPForms 它不使用預設的 mail() 函式,而是重新設定 wp_mail() 函式,它在 設定->一般 畫面,可以點選 Gmail/Mailgun/SendGrid/SMTP 等其中一種郵件程式,經過相關設定就可以寄信了。

WP Mail SMTP plugin includes many different SMTP setup options:

  1. Sendinblue SMTP (Recommended)
  2. Mailgun SMTP
  3. SendGrid SMTP
  4. Gmail SMTP
  5. Microsoft SMTP (Outlook.com and Office 365) [Pro]
  6. Amazon SES SMTP [Pro]
  7. All Other SMTP

繼續閱讀: WordPress Useful Plugin – 使用 WP Mail SMTP 透過 GMail 來寄信

MIS 發表在 痞客邦 留言(0) 人氣()

WordPress 若要搬到新的主機要注意的事項,記錄如下:

用手動來搬家比較安全,因為這才知道你自已在作什麼,主要有下列幾個步驟:

  1. Upload your website to new host
  2. Export/Import Your WordPress Database
  3. Modify two records: siteurl & home on table: wp_options
  4. Modify wp-config.php File
  5. Change all the URL of guid & post content in the posts

 

  • Upload your website to new host

先找到安裝 WordPress 的根目錄

wordpress-root

然後將 WordPress 安裝目錄下的所有檔案如下圖,整個上傳到新網站的根目錄下。(可以使用 FileZilla 來上傳)

wordpress-ftp-to-new-url

繼續閱讀: WordPress 搬家 網站搬移到網址

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

利用 Spring Boot 可以快速開發客製化應用系統, 而這些由 Spring Boot 框架建立的應用系統則可以利用 Spring Boot Admin 來作統一的管理. 由這系統可以讓您知道 Application Server 的版本如: Java , Tomcat , session .... 等, 所以在管理這些服務系統架構中...監控管理是非常重要的一環

Server 端

1. 建立一個新的 Spring Starter Project : sbAdmin , 且只要選擇 Web 就好. 其它的相依套件如下:

buildscript {
        ext {
                springBootVersion = '2.1.1.RELEASE'
        }
        repositories {
                mavenCentral()
        }
        dependencies {
                classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.admin'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
        mavenCentral()
}


dependencies {
        implementation('org.springframework.boot:spring-boot-starter-web')
        compile('de.codecentric:spring-boot-admin-starter-server:2.1.1') // SpringBoot Admin
        testImplementation('org.springframework.boot:spring-boot-starter-test')
}

Server 端的 application.properties 設定啟動的 port: 8099


server.port=8099

Client 端

引入的相依套件

dependencies {
        compile('de.codecentric:spring-boot-admin-starter-client:2.1.1') // SpringBoot Admin Client
}

設定Spring Boot Admin 可以存取 Client 端的設定

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity                
                .csrf().disable()
                .authorizeRequests()
                
                // 首頁
                .antMatchers("/home").permitAll()
                .antMatchers("/actuator/**").permitAll()                
                .anyRequest().authenticated()   // 除了以上的 URL 外, 都需要認證才可以訪問
                .and()
                        .formLogin()
                                .loginPage("/login")
                                //.failureHandler(authFailureHandler) // 使用 Spring 預設
                                .successForwardUrl("/auth/home")
                                .permitAll()
                                .and()
                .logout()
                    .permitAll();
    }

Client 端的 application.properties 設定


# =================================
# Spring Boot Admin Client
# http://codecentric.github.io/spring-boot-admin/2.1.1/
# ================================
spring.boot.admin.client.url=http://localhost:8099
management.endpoint.health.show-details=always
management.endpoints.enabled-by-default=true
management.endpoints.web.exposure.include=*

以上, 啟動後連結 http://localhost:8099 就可以了. 很快速吧.

官方的介紹

Application Servers List

>Spring Boot Admin

Dashboard with desktop notifications

undefined

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

Document 解析 XML 檔案時, 解析的值總是為 NULL , 解決方法如下:

先把檔案用 StringBuilder 變成字串後, 再作解析就可以

 

import org.springframework.util.ResourceUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class TestFunc {

        public static void main(String[] args) throws Exception  {
                // TODO Auto-generated method stub
                try {
                        File xmlFile = ResourceUtils.getFile("classpath:ckfinder-config.xml");
                        readMXLFile(xmlFile);
                        
                } catch (FileNotFoundException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
        }
        
        public static void readMXLFile(File xmlFile) throws Exception {
                InputStream is = new FileInputStream(xmlFile);
                StringBuilder contentBuilder = new StringBuilder();
                try (Stream stream = Files.lines(Paths.get(xmlFile.getAbsolutePath()), StandardCharsets.UTF_8)) {
                        stream.forEach(s -> contentBuilder.append(s).append("\n"));
                } catch (IOException e) {
                        e.printStackTrace();
                }
                System.out.println(contentBuilder.toString());

                ByteArrayInputStream bis = new ByteArrayInputStream(contentBuilder.toString().getBytes());

                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                dbf.setIgnoringComments(true);
                dbf.setIgnoringElementContentWhitespace(true);
                DocumentBuilder db = dbf.newDocumentBuilder();
                Document doc = db.parse(bis);
                // Normalize the XML Structure; It's just too important !!
                doc.getDocumentElement().normalize();
                Node node = doc.getFirstChild();

                if (node != null) {
                        NodeList nodeList = node.getChildNodes();
                        boolean enabled = false;
                        for (int i = 0; i < nodeList.getLength(); ++i) {
                                Node childNode = nodeList.item(i);
                                if (childNode.getNodeName().equals("enabled"))
                                        enabled = Boolean.valueOf(childNode.getTextContent().trim()).booleanValue();
                        }
                }
        }
}

MIS 發表在 痞客邦 留言(0) 人氣()

在 Spring Boot 使用 Freemarker 模板來快速建立客製化的標籤 (taglib) 是非常簡單的事. 作下列幾個步驟即可.

Step 01:  設定一個 Freemarker Template:  /src/main/resources/templates/demo/ftlTagSample.ftl

<body>    

    <div><h2>WELCOME,${userName}</h2></div>

    <div>Title:${userTitle}</div>

</body>

Step 02: 建立 tag 的 class : FtlTemplateTag.java

public class FtlTemplateTag extends TagSupport {

        private static final long serialVersionUID = 1L;
        
        private String fileName = null;
    private String paramsStr = null;
    private String columnsStr = null;
    private ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    
        public void setFileName(String fileName) {
                this.fileName = fileName;
        }          
        public void setParamsStr(String paramsStr) {
                this.paramsStr = paramsStr;
        }
        public void setColumnsStr(String columnsStr) {
                this.columnsStr = columnsStr;
        }
        /**
         * JSON 字串轉換成 MAP
         * @return
         */
        private Map setParams() {
                Gson gson = new Gson();
                return gson.fromJson(paramsStr, new TypeToken>() {
                }.getType());
        }
        
        private Map setColumns(){
                Locale locale = LocaleContextHolder.getLocale();
                
                messageSource.setBasenames("i18n/messages");
                
                Gson gson = new Gson();
                Map columns = gson.fromJson(columnsStr, new TypeToken>() {}.getType() );
                
                //轉換多語系
                Map dataModel = new LinkedHashMap();
                columns.forEach( (k,v)-> dataModel.put(k, messageSource.getMessage(v, null, v, locale)) );
                        
                return dataModel;
        }
        
        
        @Override
        public int doStartTag() throws JspException {
                JspWriter out = pageContext.getOut();
                
                Configuration conf = new Configuration();
                conf.setClassForTemplateLoading(this.getClass(), "/templates/");
                conf.setDefaultEncoding("UTF-8");
                
                try {
                        Template  tl = conf.getTemplate(fileName);
                        Map dataModel = new HashMap();
                        dataModel.putAll(this.setParams());
                        dataModel.put("columns", this.setColumns());
                        

                        tl.process(dataModel, out);
                        
                } catch (Exception e) {
                        e.printStackTrace();
                }               
                return Tag.SKIP_BODY;
        }
}

Step 03 建立標籤文件 tld:  /src/main/resources/static/tags/polinwei.tld

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
        version="2.0">
    <description>Polin WEI Tag Library</description>

    <tlib-version>1.0</tlib-version>
    <short-name>pw</short-name>
    <uri>/polinwei/tags</uri>

    <tag>
        <description>Get the file name of freemarker template</description>
        <name>FtlSampleTemplate</name>
        <tag-class>com.spring.jwt.tablibs.FtlTemplateTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <description> Freemarker Template 的檔案</description>
            <name>fileName</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>parmsStr</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>columnsStr</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

Step 04: 註冊此 tag 

@Configuration
public class MvcConfig implements WebMvcConfigurer {
         @Autowired
         FreeMarkerConfigurer freeMarkerConfigurer;    
    /**
     *  
     *    加入 spring-security-taglibs 對 FreeMarker 的支援
     */
    @PostConstruct    
    public void freeMarkerConfigurer() {
        List tlds = new ArrayList();        
        tlds.add("/static/tags/polinwei.tld");
        TaglibFactory taglibFactory = freeMarkerConfigurer.getTaglibFactory();
        taglibFactory.setClasspathTlds(tlds);
        if(taglibFactory.getObjectWrapper() == null) {
            taglibFactory.setObjectWrapper(freeMarkerConfigurer.getConfiguration().getObjectWrapper());
        }
    }

}

Step 05: 在 FTL or JSP 中使用

<p>測試從模版中讀取信息</p>
<@pw.FtlSampleTemplate fileName="demo/ftlTagSample.ftl" 
    paramsStr="{'Avatar':'polin.jpg','userName':'Polin WEI','userTitle':'MIS'}" />

 

Ref: https://blog.csdn.net/chinoukin/article/details/46683223

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

build.gradle

buildscript {
        ext {
                springBootVersion = '2.0.4.RELEASE'
                jjwtVersion = '0.9.0'
                findbugsVersion='3.0.1'
                bootstrapVersion = '3.3.7'
                jqueryVersion = '3.3.1'
                vueVersion ='2.5.13'
                fontawesomeVersion = '5.2.0'
                jspapiVersion = '2.3.3'
        }
        repositories {
                mavenCentral()
        }
        dependencies {
                classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        }
}

/*
* 在這個段落中你可以聲明使用哪些外掛程式
* apply plugin: 'java' 代表這是一個Java專案,需要使用java外掛程式
* 如果想生成一個 `Intellij IDEA` 的工程,類似的如果要生成
* eclipse工程,就寫 apply plugin: 'eclipse'
* 同樣的我們要學的是Spring Boot,所以應用Spring Boot外掛程式
*/
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

// 在這個段落中你可以聲明編譯後的Jar檔資訊
bootJar {
        baseName = 'myspring'
        group = 'com.polinwei'
        version = '0.0.1-SNAPSHOT'
}

// 在這個段落中你可以聲明原始檔案和目標編譯後的Java版本相容性
sourceCompatibility = 1.8
targetCompatibility = 1.8

// 在這個段落中你可以聲明在哪裡可以找到你的項目依賴
repositories {
        mavenCentral()
        maven { url "https://repo.spring.io/snapshot" }
        maven { url "https://repo.spring.io/milestone" }
        maven { url "https://code.lds.org/nexus/content/groups/main-repo"}
        maven { url "http://maven.aliyun.com/nexus/content/repositories/central"}
}


dependencies {  
        compile("org.springframework.boot:spring-boot-starter-data-jpa")
        compile("org.springframework.boot:spring-boot-starter-thymeleaf")
        compile("org.springframework.boot:spring-boot-starter-freemarker")
        compile("org.springframework.boot:spring-boot-starter-web")
        compile("org.springframework.boot:spring-boot-starter-security")
        compile("org.springframework.security:spring-security-taglibs")
        compile("org.springframework.boot:spring-boot-devtools") // Class 程式有更改時, 自動重啟
        compile("org.hibernate.validator:hibernate-validator") //驗證
        compile("javax.servlet.jsp:javax.servlet.jsp-api:${jspapiVersion}")
        compile("org.springframework.session:spring-session-data-redis")
        compile("org.springframework.boot:spring-boot-starter-data-redis")
        runtime("mysql:mysql-connector-java")
        compileOnly("org.projectlombok:lombok")
        compile("com.maxmind.geoip2:geoip2:2.12.0")
        compile("io.jsonwebtoken:jjwt:${jjwtVersion}")
        compile("com.google.code.findbugs:findbugs:${findbugsVersion}")
        compile("org.webjars:bootstrap:${bootstrapVersion}")
        compile("org.webjars:jquery:${jqueryVersion}")
        compile("org.webjars:vue:${vueVersion}")
        compile("org.webjars:font-awesome:${fontawesomeVersion}")
        compile("org.webjars.bowergithub.lipis:flag-icon-css:3.1.0")
        compileOnly("org.springframework.boot:spring-boot-configuration-processor")
        testCompile("org.springframework.boot:spring-boot-starter-test")
        testCompile("org.springframework.security:spring-security-test")
}

model: Authority.java

/**
 * Authority generated by hbm2java
 */
@Entity
@Table(name = "authority", uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Authority implements java.io.Serializable {

        private Long id;
        private String name;
        @Size(min=2, max=30 , message = "{Size}")
        private String description;
        private Set users = new HashSet(0);

        public Authority() {
        }

        public Authority(String name, String description) {
                this.name = name;
                this.description = description;
        }

        public Authority(String name, String description, Set users) {
                this.name = name;
                this.description = description;
                this.users = users;
        }

        @Id
        @GeneratedValue(strategy = IDENTITY)

        @Column(name = "id", unique = true, nullable = false)
        public Long getId() {
                return this.id;
        }

        public void setId(Long id) {
                this.id = id;
        }

        @Column(name = "name", unique = true, nullable = false, length = 50)
        public String getName() {
                return this.name;
        }

        public void setName(String name) {
                this.name = name;
        }

        @Column(name = "description", nullable = false, length = 100)
        public String getDescription() {
                return this.description;
        }

        public void setDescription(String description) {
                this.description = description;
        }

        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name = "user_authority", joinColumns = {
                        @JoinColumn(name = "authority_id", nullable = false, updatable = false) }, inverseJoinColumns = {
                                        @JoinColumn(name = "user_id", nullable = false, updatable = false) })
        @JsonBackReference
        public Set getUsers() {
                return this.users;
        }

        public void setUsers(Set users) {
                this.users = users;
        }

}

Control: AuthorityController.java

@Controller
@RequestMapping(path = "/security")
public class AuthorityController {

        private final Logger logger = LoggerFactory.getLogger(this.getClass());
        
        @Autowired
        private AuthorityRepository authorityRepository;
        
        public void init(Model model) {
                model.addAttribute("programName", "Authority");
        }
        
        @RequestMapping("authority")
        public String crudAuthority(Model model) {
                init(model);
                model.addAttribute("authority", new Authority());
                model.addAttribute("authorityList",authorityRepository.findAll());              
                return "/security/authority";
        }
        
        /**
         * Get Authority Role from id
         * @param model
         * @param id
         * @return
         */
        @RequestMapping(value= {"authorityEdit","authorityEdit/{id}"} , method = RequestMethod.GET)
        public String crudAuthority(Model model, @PathVariable( required = false , name="id") Long id) {
                init(model);
                if (Objects.isNull(id)) {
                        model.addAttribute("authority", new Authority());
                } else {
                        model.addAttribute("authority", authorityRepository.findById(id).get());
                }               
                return "/security/authority";
        }
        
        /**
         * Save Authority Role
         * @param model
         * @param authority
         * @return
         */
        @RequestMapping(value="authorityEdit" , method = RequestMethod.POST)
        public String crudAuthority(Model model, @Valid Authority authority, BindingResult bindingResult) {
                init(model);
                if (bindingResult.hasErrors()) {
                        return "/security/authority";
                }
                        
                authorityRepository.save(authority);
                model.addAttribute("authority", new Authority());
                return "/security/authority";
        }
        
        /**
         * Delete Authority Role
         * @param model
         * @param id
         * @return
         */
        @RequestMapping(value="authorityDelete/{id}" , method = RequestMethod.GET)
        public String authorityDelete(Model model, @PathVariable( required = true, name = "id") Long id) {
                init(model);
                authorityRepository.deleteById(id);
                model.addAttribute("authority", new Authority());
                return "/security/authority";
        }
        
}

加入了@Valid注解 是為了實現JSR-303的驗證。

ftl: authority.ftl

<!-- form id="authorityForm" -->
    <form id="authorityForm" action="/security/authorityEdit" method="post" >
      <input type="hidden" id="authorityId" name="id" value='${authority.id!""}' >
      <div class="box box-danger">
        <div class="box-header with-border">
          <h3 class="box-title"><@spring.message "program.block.manipulate" /></h3>

          <div class="box-tools pull-right">
            <button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
            <button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-remove"></i></button>
          </div>
        </div>
        <!-- /.box-header -->
        <div class="box-body">
          <div class="row">
            <div class="col-md-6">
              <div class="form-group">
                <label for="authorityName"><@spring.message "program.authority.name" /></label>
                <input type="text" class="form-control" id="authorityName" placeholder="<@spring.message "program.authority.name" />" name="name" value='${authority.name!""}' required>
                
              </div>
              <!-- /.form-group -->
              <div class="form-group">                
                <label for="authorityDescription"><@spring.message "program.authority.description" /></label>
                <input type="text" class="form-control" id="authorityDescription" placeholder="<@spring.message "program.authority.description" />" name="description" value='${authority.description!""}' required>
                <#if authority??>  
                    <@spring.bind "authority.description" />
                    <@spring.showErrors "<br />" "color:red"/>
                </#if>
              </div>
              <!-- /.form-group -->
            </div><!-- /.col -->
            
          </div><!-- /.row -->
          <div class="row">
              <div class="col-xs-6">
                  <button id="btn-login" type="submit" class="btn btn-alert "><@spring.message "label.submit"/></button>
              </div>          
          </div> <!-- /.row -->
        </div>
        <!-- /.box-body -->
        <div class="box-footer">
          Visit <a href="/security/authority">Authority</a>
        </div>
      </div>
      <!-- /.box -->
<!-- /.form id="authorityForm" -->

值得一提的是:SpringMVC为FreeMarker提供了支持。

首先要在頁面中引入:<#import "spring.ftl" as spring />,才會有對表單驗證的支援


<div class="form-group">                
    <label for="authorityDescription"><@spring.message "program.authority.description" /></label>
    <input type="text" class="form-control" id="authorityDescription" placeholder="<@spring.message "program.authority.description" />" name="description" value='${authority.description!""}' required>
    <#if authority??>  
        <@spring.bind "authority.description" />
        <@spring.showErrors "<br />" "color:red"/>
    </#if>
</div>

spring.bind 指定要校驗綁定的欄位。格式是: <@spring.bind 屬性名 />
spring.showErrors 指定顯示錯誤消息。格式是:<@spring.showErrors 分隔符號 樣式 />

編寫驗證配置文件: 在src/main/resources 目前下建立 ValidationMessages.properties , ValidationMessages_en_US.properties , ValidationMessages_zh_TW.properties 這三個檔案, 並放入

Size = Size must be between {2} and {1}.

這樣子運行起就可以了有多語言錯誤訊息的顯示了

https://spring.io/guides/gs/validating-form-input/

https://my.oschina.net/mondayer/blog/879425

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

實現pdfmake使用中文本體主要就是編譯新的vfs_fonts.js代替原來vfs_fonts.js文檔引入到前端頁面中,為了編譯出新的字體文檔,下列是中文顯示的解決方法,供大家參考:

操作系統:Windows

操作步驟:

1. 安裝node.js

2. 下載pdfmake的源代碼、下載地址https://github.com/bpampuch/pdfmake

3. 在源代碼根目錄下安裝gulp:

打開cmd命令窗口,定位到源代碼根目錄,如:cd V:\pdfmake-master


npm install gulp
npm install -g gulp
npm i -g gulp-cli

執行gulp -v顯示gulp版本號則説明安裝成功

4. 利用gulp打包字體ttf文檔到vfs_fonts.js文檔中

gulp安裝成功後,查看源代碼根目錄下的gulpfile.js,發現gulp編譯還依賴了很多其他的模塊,所以需要一一安裝,沒辦法所以得一一安裝:npm install webpack-stream、npm install gulp-uglify 等等


V:\pdfmake-master>npm i -g gulp-cli

V:\node-v8.12.0-win-x64\gulp -> V:\node-v8.12.0-win-x64\node_modules\gulp-cli\bin\gulp.js

+ gulp-cli@2.0.1

added 235 packages from 147 contributors in 6.71s

V:\pdfmake-master>gulp buildFonts

[13:48:16] Using gulpfile V:\pdfmake-master\gulpfile.js

[13:48:16] Starting 'buildFonts'...

[13:48:17] Finished 'buildFonts' after 382 ms

 

從本地 C:\Windows\Fonts 下拷貝一箇中文本體到D:\download\chrome\pdfmake-master\examples\fonts目錄下,並刪除fonts目錄下原來的ttf文檔,這裏有一個問題,中文本體都很大,囧。

請選了一個最小的字體上:標楷體-標準 ,這一步完成之後,字體打包生成的vfs_fonts.js會覆蓋源代碼根目錄的build目錄下的vfs_fonts.js文檔,這時需要將vfs_fonts.js拷貝到你的項目當中替換原來的vfs_fonts.js,並引用的html頁面中

 

以 DataTables 為例

<button onclick="down('pdf文件中文顯示')">PDF下載</button>

      <!-- SELECT2 EXAMPLE -->
      <div class="box box-danger">
        <div class="box-header with-border">
          <h3 class="box-title">維護畫面</h3>

          <div class="box-tools pull-right">
            <button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i></button>
            <button type="button" class="btn btn-box-tool" data-widget="remove"><i class="fa fa-remove"></i></button>
          </div>
        </div>
        <!-- /.box-header -->
        <div class="box-body">
          <div class="row">
            <div class="col-md-6">
              <div class="form-group">
                <label for="authorityName">Name</label>
                <input type="text" class="form-control" id="authorityName" placeholder="Authority Name" name="name">
                
              </div>
              <!-- /.form-group -->
              <div class="form-group">                
                <label for="authorityDescription">description</label>
                <input type="text" class="form-control" id="authorityDescription" placeholder="Authority Description" name="description">
              </div>
              <!-- /.form-group -->
            </div><!-- /.col -->
            
          </div><!-- /.row -->
        </div>
        <!-- /.box-body -->
        <div class="box-footer">
          Visit <a href="/security/authority">Authority</a>
        </div>
      </div>
      <!-- /.box -->


      <!-- Default box -->
      <div class="box box-info">
        <div class="box-header">
          <h3 class="box-title">角色清單</h3>

          <div class="box-tools pull-right">
            <button type="button" class="btn btn-box-tool" data-widget="collapse" data-toggle="tooltip"
                    title="Collapse">
              <i class="fa fa-minus"></i></button>
            <button type="button" class="btn btn-box-tool" data-widget="remove" data-toggle="tooltip" title="Remove">
              <i class="fa fa-times"></i></button>
          </div>
        </div>
        <div class="box-body">
         
          <table id="tblAuthority" class="table table-bordered table-striped" style="width:100%">
            <thead>
            <tr>
              <th>id</th>
              <th>name</th>
              <th>description</th>
              <th>Options</th>
            </tr>
            </thead>
            
            <tfoot>
            <tr>
              <th>id</th>
              <th>name</th>
              <th>description</th>
              <th>Options</th>
            </tr>
            </tfoot>
          </table>
                                             
        </div>
        <!-- /.box-body -->
        <div class="box-footer">
          Footer
        </div>
        <!-- /.box-footer-->
      </div>
      <!-- /.box -->

<!-- page script -->
<script>
function down(data) {
    var dd = {
        content: [
            data,
            'Another paragraph, this time a little bit longer to make sure, this line will be divided into at least two lines'
        ],
        defaultStyle: {
            font: 'kaiu'
        }
    };
    pdfMake.fonts = {
                kaiu: {
            normal: 'kaiu.ttf',
            bold: 'kaiu.ttf',
            italics: 'kaiu.ttf',
            bolditalics: 'kaiu.ttf'
        }
    };
    pdfMake.createPdf(dd).download();
}

$(document).ready(function() {
        
        pdfMake.fonts = {
                Roboto: {
            normal: 'kaiu.ttf',
            bold: 'kaiu.ttf',
            italics: 'kaiu.ttf',
            bolditalics: 'kaiu.ttf'
        }
    };  
        
          $('#tblAuthority').DataTable({        
        ajax: {url:"/authentication/authorities",dataSrc:""},
        columns: [
                { data: "id", visible: false},
            { data: "name" },
            { data: "description" },
            {
              data: "id", render: function(data, type, row, meta) {                  
                  return '<a href=' data ' class="btn btn-xs btn-primary"><i class="fa fa-pencil"></i>Edit</a> <a href=' data ' class="btn btn-xs btn-danger"><i class="fa fa-trash-o">Delete</a>'
              },
              className: "center",              
            }
        ],
        dom: 'lrBtip',        
        buttons: [
                'copy','excel', 
                {
                        extend: 'pdf',
                        text: 'PDF',
                        className: "btn btn-xs btn-primary",
                        'title': 'Authority List',                 
                'download': 'open',//直接在視窗開啟 
                },
                {
                extend: 'csv',
                text: 'CSV',
                className: "btn btn-xs btn-primary",
                bom : true
            }, 
            {
                text: 'My button',
                className: "btn btn-xs btn-primary",
                action: function ( e, dt, node, config ) {
                    alert( 'Button activated' );
                }
            },
            {
                text: 'Reload',
                className: "btn btn-xs btn-primary",
                action: function ( e, dt, node, config ) {
                    dt.ajax.reload();
                }
            }            
        ]
        
    });   

  })
</script>

https://hk.saowen.com/a/b58fede7fdfaef23bb9236286cc6ac318607f9f6899fd684c6d2af2509a4be3f

https://blog.csdn.net/qq_30076899/article/details/79731508

https://github.com/bpampuch/pdfmake/wiki/Custom-Fonts---client-side

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

使用 Hibernate Tool 去產生 Domain Code ( Model ) 時, 當 User.java & Authority.java 有相互參考時, 在 spring boot 使用 @RestController 會發生 Direct self-reference leading to cycle 的錯誤訊息, 解決的方法可以在 Domain Code ( Model ) 上加上 @JsonManagedReference & @JsonBackReference 來防止錯誤.

 

User.java

/**
 * User generated by hbm2java
 */
@Entity
@Table(name = "user")
public class User implements java.io.Serializable {

        private Long id;
        private User userByCreateUser;
        private User userByUpdateUser;
        private String username;
        private String password;
        private String firstname;
        private String lastname;
        private String email;
        private Boolean enabled;
        private Date lastpasswordresetdate;
        private Date activeDate;
        private Date inactiveDate;
        private Date createDate;
        private Date updateDate;
        private Set authorities = new HashSet(0);
        private Set usersForCreateUser = new HashSet(0);
        private Set usersForUpdateUser = new HashSet(0);

        public User() {
        }

        public User(String username, String password, String firstname, String lastname, String email,
                        Date lastpasswordresetdate) {
                this.username = username;
                this.password = password;
                this.firstname = firstname;
                this.lastname = lastname;
                this.email = email;
                this.lastpasswordresetdate = lastpasswordresetdate;
        }

        public User(User userByCreateUser, User userByUpdateUser, String username, String password, String firstname,
                        String lastname, String email, Boolean enabled, Date lastpasswordresetdate, Date activeDate,
                        Date inactiveDate, Date createDate, Date updateDate, Set authorities,
                        Set usersForCreateUser, Set usersForUpdateUser) {
                this.userByCreateUser = userByCreateUser;
                this.userByUpdateUser = userByUpdateUser;
                this.username = username;
                this.password = password;
                this.firstname = firstname;
                this.lastname = lastname;
                this.email = email;
                this.enabled = enabled;
                this.lastpasswordresetdate = lastpasswordresetdate;
                this.activeDate = activeDate;
                this.inactiveDate = inactiveDate;
                this.createDate = createDate;
                this.updateDate = updateDate;
                this.authorities = authorities;
                this.usersForCreateUser = usersForCreateUser;
                this.usersForUpdateUser = usersForUpdateUser;
        }

        @Id
        @GeneratedValue(strategy = IDENTITY)

        @Column(name = "id", unique = true, nullable = false)
        public Long getId() {
                return this.id;
        }

        public void setId(Long id) {
                this.id = id;
        }

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "create_user")
        public User getUserByCreateUser() {
                return this.userByCreateUser;
        }

        public void setUserByCreateUser(User userByCreateUser) {
                this.userByCreateUser = userByCreateUser;
        }

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "update_user")
        public User getUserByUpdateUser() {
                return this.userByUpdateUser;
        }

        public void setUserByUpdateUser(User userByUpdateUser) {
                this.userByUpdateUser = userByUpdateUser;
        }

        @Column(name = "username", nullable = false, length = 50)
        public String getUsername() {
                return this.username;
        }

        public void setUsername(String username) {
                this.username = username;
        }

        @Column(name = "password", nullable = false, length = 100)
        public String getPassword() {
                return this.password;
        }

        public void setPassword(String password) {
                this.password = password;
        }

        @Column(name = "firstname", nullable = false, length = 50)
        public String getFirstname() {
                return this.firstname;
        }

        public void setFirstname(String firstname) {
                this.firstname = firstname;
        }

        @Column(name = "lastname", nullable = false, length = 50)
        public String getLastname() {
                return this.lastname;
        }

        public void setLastname(String lastname) {
                this.lastname = lastname;
        }

        @Column(name = "email", nullable = false, length = 50)
        public String getEmail() {
                return this.email;
        }

        public void setEmail(String email) {
                this.email = email;
        }

        @Column(name = "enabled")
        public Boolean getEnabled() {
                return this.enabled;
        }

        public void setEnabled(Boolean enabled) {
                this.enabled = enabled;
        }

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "lastpasswordresetdate", nullable = false, length = 19)
        public Date getLastpasswordresetdate() {
                return this.lastpasswordresetdate;
        }

        public void setLastpasswordresetdate(Date lastpasswordresetdate) {
                this.lastpasswordresetdate = lastpasswordresetdate;
        }

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "active_date", length = 19)
        public Date getActiveDate() {
                return this.activeDate;
        }

        public void setActiveDate(Date activeDate) {
                this.activeDate = activeDate;
        }

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "inactive_date", length = 19)
        public Date getInactiveDate() {
                return this.inactiveDate;
        }

        public void setInactiveDate(Date inactiveDate) {
                this.inactiveDate = inactiveDate;
        }

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "create_date", length = 19)
        public Date getCreateDate() {
                return this.createDate;
        }

        public void setCreateDate(Date createDate) {
                this.createDate = createDate;
        }

        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "update_date", length = 19)
        public Date getUpdateDate() {
                return this.updateDate;
        }

        public void setUpdateDate(Date updateDate) {
                this.updateDate = updateDate;
        }

        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name = "user_authority", catalog = "my_spring", joinColumns = {
                        @JoinColumn(name = "user_id", nullable = false, updatable = false) }, inverseJoinColumns = {
                                        @JoinColumn(name = "authority_id", nullable = false, updatable = false) })
        @JsonManagedReference
        public Set getAuthorities() {
                return this.authorities;
        }

        public void setAuthorities(Set authorities) {
                this.authorities = authorities;
        }

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "userByCreateUser")
        public Set getUsersForCreateUser() {
                return this.usersForCreateUser;
        }

        public void setUsersForCreateUser(Set usersForCreateUser) {
                this.usersForCreateUser = usersForCreateUser;
        }

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "userByUpdateUser")
        public Set getUsersForUpdateUser() {
                return this.usersForUpdateUser;
        }

        public void setUsersForUpdateUser(Set usersForUpdateUser) {
                this.usersForUpdateUser = usersForUpdateUser;
        }

}

 

Authority.java

/**
 * Authority generated by hbm2java
 */
@Entity
@Table(name = "authority")
public class Authority implements java.io.Serializable {

        private Long id;
        private String name;
        private String description;
        private Set users = new HashSet(0);

        public Authority() {
        }

        public Authority(String name, String description) {
                this.name = name;
                this.description = description;
        }

        public Authority(String name, String description, Set users) {
                this.name = name;
                this.description = description;
                this.users = users;
        }

        @Id
        @GeneratedValue(strategy = IDENTITY)

        @Column(name = "id", unique = true, nullable = false)
        public Long getId() {
                return this.id;
        }

        public void setId(Long id) {
                this.id = id;
        }

        @Column(name = "name", nullable = false, length = 50)
        public String getName() {
                return this.name;
        }

        public void setName(String name) {
                this.name = name;
        }

        @Column(name = "description", nullable = false, length = 100)
        public String getDescription() {
                return this.description;
        }

        public void setDescription(String description) {
                this.description = description;
        }

        @ManyToMany(fetch = FetchType.LAZY)
        @JoinTable(name = "user_authority", catalog = "my_spring", joinColumns = {
                        @JoinColumn(name = "authority_id", nullable = false, updatable = false) }, inverseJoinColumns = {
                                        @JoinColumn(name = "user_id", nullable = false, updatable = false) })
         @JsonBackReference
        public Set getUsers() {
                return this.users;
        }

        public void setUsers(Set users) {
                this.users = users;
        }
}

 

 

 

參考:

http://keenformatics.blogspot.com/2013/08/how-to-solve-json-infinite-recursion.html

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

SpringBoot+SpringSecurity+Freemarker專案中在頁面上使用security標籤控制按鈕顯示隱藏達到對按鈕級許可權控制還是比較方便的,如下配置即可。


1. gradle 引入依賴


dependencies {
        compile("org.springframework.security:spring-security-taglibs")
        compile("javax.servlet.jsp:javax.servlet.jsp-api:2.3.3")
}


2. 依賴引入後到spring-security-taglibs包中META-INF下security.tld複製出來,放到/resources/static下,最後建一個目錄tags,如下:

springboot-taglib.jpg
3. 建一個配置類


@Configuration
public class MvcConfig implements WebMvcConfigurer {
         @Autowired
         FreeMarkerConfigurer freeMarkerConfigurer;
    
    /**
     *  
     *    加入 spring-security-taglibs 對 FreeMarker 的支援
     */
    @PostConstruct
    public void freeMarkerConfigurer() {
        List tlds = new ArrayList();
        tlds.add("/static/tags/security.tld");
        TaglibFactory taglibFactory = freeMarkerConfigurer.getTaglibFactory();
        taglibFactory.setClasspathTlds(tlds);
        if(taglibFactory.getObjectWrapper() == null) {
            taglibFactory.setObjectWrapper(freeMarkerConfigurer.getConfiguration().getObjectWrapper());
        }
    }
}


4. 在freemarker頁面頂部引入標籤


<#assign security=JspTaglibs["http://www.springframework.org/security/tags"] />


5. 標籤應用如下


<@security.authorize access="hasRole('ADMIN')">

Admin can read
</@security.authorize>

<@security.authorize access="hasRole('USER')">

User can read
</@security.authorize>
文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

除了 痞客邦 PIXNET 使用 Syntax Highlighter 顯示程式碼 以外, 也可以使用 highlight.js 來讓 pixnet 顯示程式碼

在後台管理的 側欄管理, 選擇 頁尾描述加入

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/languages/go.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>


日後要展示程式碼的部分,先在撰寫貼上程式碼,轉到原始碼模式,用

<pre><code class="html">...</code></pre>

把程式碼包起來。

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

Spring Boot + Freemarker 多語言國際化的作法

1. 在 application.properties 增加

# ===============================
# Multi Language
# ===============================
spring.messages.basename=i18n/messages

# 為了取得當前頁面的URL (get current page url)
spring.freemarker.expose-spring-macro-helpers=true

2. 在 resource 目錄下建立一個目錄 i18n , 並新增三個檔

messages.properties , messages_zh_TW.properties , 內容為

label.login =  登入

messages_en_US.properties 內容為

label.login =  Login

3. 建立 WebMvcConfig : MvcConfig.class

@Configuration
public class MvcConfig implements WebMvcConfigurer {    
    //多語系設定
    @Bean(name = "localeResolver")
    public LocaleResolver getLocaleResolver()  {
        CookieLocaleResolver resolver= new CookieLocaleResolver();
        resolver.setCookieDomain("jwtLocaleCookie");
        // 60 minutes 
        resolver.setCookieMaxAge(60*60); 
        return resolver;
    } 
    //多語系設定 語系切換偵測
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
        localeInterceptor.setParamName("lang");
                  
        registry.addInterceptor(localeInterceptor).addPathPatterns("/*");
    }

}

4. Control file: MainController.class

@Controller
public class MainController {
        
        @RequestMapping(value = { "/","/home" })
    public String staticResource(Model model) {
        return "home";
    }

}

5. View: home.ftl

<#import "/spring.ftl" as spring/>





注意, FTL的第一行要 import "/spring.ftl" ,這樣子就可以輕鬆切換語系了

 

文章標籤

MIS 發表在 痞客邦 留言(0) 人氣()

使用 Hibernate Tool 產生 Model 時, FetchType 預設是 FetchType.LAZY , 但在執行時, 卻常會產生類似下列 no Session 的錯誤訊息

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.spring.jwt.db.maria.model.authentication.User.userAuthorities, could not initialize proxy - no Session

 

這時候需要在 Method 前加入 @Transactional 就可以解決

@Test
@Transactional
public void testUserAuthority() {
        User user = userRepository.findById(1L).get();
        JwtUser jwtUser = JwtUserFactory.create(user);
        logger.info(jwtUser.getUsername());
        
}

Ref: http://javasampleapproach.com/hibernate/use-hibernate-lazy-fetch-eager-fetch-type-spring-boot-mysql

MIS 發表在 痞客邦 留言(0) 人氣()

XAMPP 是一套整合 Apache、PHP、MySQL、Mercury Mail Transport System、FileZilla FTP Server、Tomcat...等的服務應用系統,讓您可以專心在系統程式開發上,若在開發的應用系統中有需要發信時而不想自行管理 Mail Server 時,可以利用內附的 sendmail 結合 gmail 來達成。

 

主要修改的地方有二個設定:

第一個設定檔:修改 \xampp\php\php.ini

[mail function]
; XAMPP: Comment out this if you want to work with an SMTP Server like Mercury
; SMTP = localhost
; smtp_port = 25

; For Win32 only.
; http://php.net/sendmail-from
;sendmail_from = postmaster@localhost

; XAMPP IMPORTANT NOTE (1): If XAMPP is installed in a base directory with spaces (e.g. c:\program files\xampp) fakemail and mailtodisk do not work correctly.
; XAMPP IMPORTANT NOTE (2): In this case please copy the sendmail or mailtodisk folder in your root folder (e.g. C:\sendmail) and use this for sendmail_path.  
 
; XAMPP: Comment out this if you want to work with fakemail for forwarding to your mailbox (sendmail.exe in the sendmail folder)
sendmail_path = "\"\xampp\sendmail\sendmail.exe\" -t"

; XAMPP: Comment out this if you want to work with mailToDisk, It writes all mails in the \xampp\mailoutput folder
;sendmail_path = "\xampp\mailtodisk\mailtodisk.exe"

; Force the addition of the specified parameters to be passed as extra parameters
; to the sendmail binary. These parameters will always replace the value of
; the 5th parameter to mail(), even in safe mode.
;mail.force_extra_parameters =

; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename
mail.add_x_header = Off

; Log all mail() calls including the full path of the script, line #, to address and headers
mail.log = "\xampp\php\logs\php_mail.log"

 

第二個設定檔:修改 \xampp\sendmail

; configuration for fake sendmail

; if this file doesn't exist, sendmail.exe will look for the settings in
; the registry, under HKLM\Software\Sendmail

[sendmail]

; you must change mail.mydomain.com to your smtp server,
; or to IIS's "pickup" directory.  (generally C:\Inetpub\mailroot\Pickup)
; emails delivered via IIS's pickup directory cause sendmail to
; run quicker, but you won't get error messages back to the calling
; application.

smtp_server=smtp.gmail.com

; smtp port (normally 25)

smtp_port=465

; SMTPS (SSL) support
;   auto = use SSL for port 465, otherwise try to use TLS
;   ssl  = alway use SSL
;   tls  = always use TLS
;   none = never try to use SSL

smtp_ssl=auto

; the default domain for this server will be read from the registry
; this will be appended to email addresses when one isn't provided
; if you want to override the value in the registry, uncomment and modify

default_domain=gmail.com

; log smtp errors to error.log (defaults to same directory as sendmail.exe)
; uncomment to enable logging

error_logfile=error.log

; create debug log as debug.log (defaults to same directory as sendmail.exe)
; uncomment to enable debugging

debug_logfile=debug.log

; if your smtp server requires authentication, modify the following two lines

auth_username=yourName@gmail.com
auth_password=yourPassword

; if your smtp server uses pop3 before smtp authentication, modify the
; following three lines.  do not enable unless it is required.

pop3_server=
pop3_username=
pop3_password=

; force the sender to always be the following email address
; this will only affect the "MAIL FROM" command, it won't modify
; the "From: " header of the message content

force_sender=yourName@gmail.com

; force the sender to always be the following email address
; this will only affect the "RCTP TO" command, it won't modify
; the "To: " header of the message content

force_recipient=

; sendmail will use your hostname and your default_domain in the ehlo/helo
; smtp greeting.  you can manually set the ehlo/helo name if required

hostname=

 

重新起動 Apache 服務即可。

PS: Windows 8 的 sendmail 不會正常運作

文章標籤

MIS 發表在 痞客邦 留言(1) 人氣()