Xây dựng trang cá nhân với Jekyll

Duc A. Hoang 24 Nov 2018 16 Dec 2018 jekyll, linux, windows

Dự định

Những dự định của tôi khi xây dựng một trang cá nhân với Jekyll:

  • Sử dụng GitHub để host trang cá nhân.
    • Khi bạn đăng ký tài khoản GitHub, bạn có thể tạo một trang cá nhân ở địa chỉ username.github.io, trong đó username là tên đăng nhập bạn đăng ký vói GitHub.
    • Để tạo trang cá nhân, bạn cần tạo một repository với tên username.github.io. Tôi dự định tạo hai branch: mastersource.
      • Branch master chứa các sản phẩm sinh bởi Jekyll.
      • Branch source chứa mã nguồn sẽ được sử dụng bởi Jekyll.
  • Cài đặt Jekyll trên máy tính cá nhân để sinh và kiểm tra nội dung của trang cá nhân trước khi đưa lên GitHub.
  • (Tùy chọn) Sử dụng Travis CI để sinh tự động trang cá nhân mỗi khi có cập nhật từ branch source. Để làm được điều này, bạn cần đăng nhập Travis CI với tài khoản GitHub.

Môi trường làm việc

Jekyll

Tôi sử dụng hệ điều hành Arch Linux phiên bản 64-bit. Việc cài đặt Jekyll trong Arch Linux được tiến hành như sau:

sudo pacman -S --needed --noconfirm ruby rubygems ruby-docs ruby-rdoc
echo "export PATH=$PATH:/home/hoanganhduc/.gem/ruby/2.5.0/bin;" >> .bashrc 
source .bashrc
gem install bundler
wget https://raw.githubusercontent.com/username/username.github.io/source/Gemfile
wget https://raw.githubusercontent.com/username/username.github.io/source/Gemfile.lock
bundle install

Các bạn cũng có thể tham khảo quá trình tôi cài Jekyll trong Ubuntu 16.04 LTS hoặc Windows 10. Tuy nhiên, tôi đề nghị các bạn nên cài đặt Ubuntu 16.04 trong Windows 10 thông qua Windows Subsystem for Linux. Tôi ghi lại các bước cài đặt trong Ubuntu ở đây.

sudo apt install ruby rubygems
gem install bundler
wget https://raw.githubusercontent.com/username/username.github.io/source/Gemfile
wget https://raw.githubusercontent.com/username/username.github.io/source/Gemfile.lock
bundle install

Nội dung các tệp GemfileGemfile.lock tính đến thời gian tôi viết bài này là như sau.

Tệp: Gemfile
source 'https://rubygems.org'

gem 'jekyll'
gem 'rouge'
gem 'kramdown'
gem 'rake'
gem 'jekyll-scholar'
gem 'pygments.rb'
gem 'jekyll-sitemap'
gem 'jekyll-feed'
gem 'jekyll-email-protect'
gem 'jekyll-last-modified-at'
Tệp: Gemfile.lock
GEM
  remote: https://rubygems.org/
  specs:
    addressable (2.5.2)
      public_suffix (>= 2.0.2, < 4.0)
    bibtex-ruby (4.4.7)
      latex-decode (~> 0.0)
    citeproc (1.0.9)
      namae (~> 1.0)
    citeproc-ruby (1.1.10)
      citeproc (~> 1.0, >= 1.0.9)
      csl (~> 1.5)
    colorator (1.1.0)
    concurrent-ruby (1.1.3)
    csl (1.5.0)
      namae (~> 1.0)
    csl-styles (1.0.1.9)
      csl (~> 1.0)
    em-websocket (0.5.1)
      eventmachine (>= 0.12.9)
      http_parser.rb (~> 0.6.0)
    eventmachine (1.2.7)
    eventmachine (1.2.7-x64-mingw32)
    eventmachine (1.2.7-x86-mingw32)
    ffi (1.9.25)
    ffi (1.9.25-x64-mingw32)
    ffi (1.9.25-x86-mingw32)
    forwardable-extended (2.6.0)
    http_parser.rb (0.6.0)
    i18n (0.9.5)
      concurrent-ruby (~> 1.0)
    jekyll (3.8.5)
      addressable (~> 2.4)
      colorator (~> 1.0)
      em-websocket (~> 0.5)
      i18n (~> 0.7)
      jekyll-sass-converter (~> 1.0)
      jekyll-watch (~> 2.0)
      kramdown (~> 1.14)
      liquid (~> 4.0)
      mercenary (~> 0.3.3)
      pathutil (~> 0.9)
      rouge (>= 1.7, < 4)
      safe_yaml (~> 1.0)
    jekyll-email-protect (1.1.0)
    jekyll-feed (0.11.0)
      jekyll (~> 3.3)
    jekyll-last-modified-at (1.0.1)
      jekyll (~> 3.3)
      posix-spawn (~> 0.3.9)
    jekyll-sass-converter (1.5.2)
      sass (~> 3.4)
    jekyll-scholar (5.14.0)
      bibtex-ruby (~> 4.0, >= 4.0.13)
      citeproc-ruby (~> 1.0)
      csl-styles (~> 1.0)
      jekyll (~> 3.0)
    jekyll-sitemap (1.2.0)
      jekyll (~> 3.3)
    jekyll-watch (2.1.2)
      listen (~> 3.0)
    kramdown (1.17.0)
    latex-decode (0.3.1)
    liquid (4.0.1)
    listen (3.1.5)
      rb-fsevent (~> 0.9, >= 0.9.4)
      rb-inotify (~> 0.9, >= 0.9.7)
      ruby_dep (~> 1.2)
    mercenary (0.3.6)
    multi_json (1.13.1)
    namae (1.0.1)
    pathutil (0.16.2)
      forwardable-extended (~> 2.6)
    posix-spawn (0.3.13)
    public_suffix (3.0.3)
    pygments.rb (1.2.1)
      multi_json (>= 1.0.0)
    rake (12.3.1)
    rb-fsevent (0.10.3)
    rb-inotify (0.9.10)
      ffi (>= 0.5.0, < 2)
    rouge (3.3.0)
    ruby_dep (1.5.0)
    safe_yaml (1.0.4)
    sass (3.7.2)
      sass-listen (~> 4.0.0)
    sass-listen (4.0.0)
      rb-fsevent (~> 0.9, >= 0.9.4)
      rb-inotify (~> 0.9, >= 0.9.7)

PLATFORMS
  ruby
  x64-mingw32
  x86-mingw32

DEPENDENCIES
  jekyll
  jekyll-email-protect
  jekyll-feed
  jekyll-last-modified-at
  jekyll-scholar
  jekyll-sitemap
  kramdown
  pygments.rb
  rake
  rouge

BUNDLED WITH
   1.17.1

Chú ý là việc cài đặt các gem này là để phục vụ cho việc sinh trang cá nhân của tôi. Các bạn có thể cài thêm các gem khác, hoặc bỏ bớt đi, tùy theo nhu cầu của mỗi người.

Git + GnuPG + OpenSSH + Travis CI CLI

  • Trong Arch Linux, việc cài đặt các gói Git, GnuPG, và OpenSSH được thực hiện đơn giản thông qua lệnh sudo pacman -S --needed --noconfirm git gnupg openssh. Trong Ubuntu, bạn sử dụng lệnh sudo apt install git gnupg2 ssh. Việc sử dụng GnuPG và OpenSSH hay không là tùy bạn.
    • Sử dụng SSH key với GitHub:
      • Tạo một SSH key bằng lệnh ssh-keygen -t rsa -b 4096 -C "your_email@example.com" (thay your_email@example.com bằng địa chỉ email của bạn) nếu bạn chưa có. Khi bạn được hỏi Enter a file in which to save the key, nhấn Enter để chấp nhận vị trí lưu key mặc định $HOME/.ssh/id_rsa. Tiếp đó, nhập passphrase bất kỳ để kết thúc quá trình tạo key. Để sử dụng key với GitHub, mở tệp $HOME/.ssh/id_rsa bằng một trình soạn thảo văn bản nào đó (tôi dùng gedit) và chép tất cả nội dung vào GitHub (bằng cách đăng nhập vào GitHub, sau đó truy cập tới địa chỉ https://github.com/settings/keys và nhấn vào nút New SSH key).
      • Để kiểm tra, gõ lệnh ssh -T git@github.com và nhập passphrase bạn khởi tạo khi tạo SSH key. Nếu bạn thấy dòng Hi username! You've successfully authenticated, but GitHub does not provide shell access thì tức là bạn đã cài đặt thành công.
    • Sử dụng GnuPG với GitHub:
      • Tạo một GPG key bằng lệnh gpg --full-generate-key (với gnupg >= 2.1.17) nếu bạn chưa có.
      • Để liệt kê tất cả các GPG secret key (tức là gồm cả private và public key) bạn đang có, sử dụng lệnh gpg --list-secret-keys --keyid-format LONG.
      • Xác định ID của key bạn muốn sử dụng với GitHub, ví dụ 3AA5C34371567BD2, sau đó export public key của key này bằng lệnh gpg --armor --export 3AA5C34371567BD2, và chép phần bắt đầu từ -----BEGIN PGP PUBLIC KEY BLOCK----- và kết thúc bởi -----END PGP PUBLIC KEY BLOCK----- vào GitHub (bằng cách đăng nhập vào GitHub, sau đó truy cập tới địa chỉ https://github.com/settings/keys và nhấn vào nút New GPG key).
      • Chú ý là trong Ubuntu, bạn cần dùng lệnh gpg2 thay vì gpg.
    • Một số cài đặt mặc định cho Git.
git config --global user.name "username" # pick any name you like
git config --global user.email "your_email@example.com" # pick any email address you like
git config --global gpg.program gpg2 # if you use GnuPG
git config --global user.signingkey your-gpg-key-id # if you use GnuPG
  • Tôi cũng sử dụng Travis CI CLI. Việc cài đặt được thực hiện thông qua lệnh gem install travis.

Một số cài đặt khác

  • Thư mục gốc của tôi được đặt ở Dropbox.
  • Tôi tạo thư mục $HOME/username.github.io và hai thư mục con $HOME/username.github.io/master$HOME/username.github.io/source tương ứng với các branch mastersource tôi tạo ra trên GitHub. Branch master được mặc định khi bạn tạo repository username.github.io của mình. Để tạo branch source, bạn mở trang chính của repossitory (URL thường có dạng https://github.com/username/username.github.io/), chọn menu thả xuống Branch và gõ tên source để tạo branch mới.
  • Trong thư mục $HOME/username.github.io/master, tôi khởi tạo như sau:
cd $HOME/username.github.io/master
git init
git remote add origin git@github.com:username/username.github.io.git
git pull origin master
  • Trong thư mục $HOME/username.github.io/source, tôi khởi tạo như sau:
cd $HOME/username.github.io/source
git init
git remote add origin git@github.com:username/username.github.io.git
git symbolic-ref HEAD refs/heads/source
git clean -fdx
git pull origin source

Xây dựng trang cá nhân

Các bạn có thể tham khảo mã nguồn của trang cá nhân của tôi ở https://github.com/username/username.github.io/tree/source. Để tự tạo trang cá nhân cho mình, các bạn nên tải mã nguồn về, tự sinh trang web thử bằng Jekyll, và tự sửa cho phù hợp với bản thân mình. Ở đây tôi chỉ giải thích qua một số phần tôi cho là quan trọng.

Cấu trúc

Mã nguồn trang cá nhân của tôi tính đến thời điểm hiện tại có cấu trúc như sau:

.
├── _bibliography
│   ├── publications
│   │   ├── conferences.bib
│   │   ├── journals.bib
│   │   ├── preprints.bib
│   │   └── thesis.bib
│   └── references.bib
├── build.sh
├── co-authors.md
├── _config.yml
├── _courses
│   ├── Toan-roi-rac-K61CNTN-Toan-HK12018.md
│   └── Toan-roi-rac-K61CN-Toan-HK12018.md
├── courses
│   └── 2018
│       ├── MAT3302
│       │   ├── basiccounting.pdf
│       │   ├── exam1.pdf
│       │   ├── genpermcomb.pdf
│       │   ├── graph1.pdf
│       │   ├── graph2.pdf
│       │   ├── graph3.pdf
│       │   ├── pigeon_inclexcl.pdf
│       │   ├── rec.pdf
│       │   └── tree.pdf
│       └── MAT3302-2TNT
│           ├── KiemTraCNTN_20181010.pdf
│           ├── ps10.pdf
│           ├── ps1.pdf
│           ├── ps2.pdf
│           ├── ps3.pdf
│           ├── ps4.pdf
│           ├── ps5.pdf
│           ├── ps6.pdf
│           ├── ps7.pdf
│           ├── ps8.pdf
│           └── ps9.pdf
├── CV.pdf
├── CV-vi.pdf
├── _data
│   ├── coauthors.yaml
│   ├── navbar.yaml
│   └── news.yaml
├── _drafts
│   └── .gitignore
├── events
│   ├── CoRe2015
│   │   ├── abstract.pdf
│   │   ├── poster.pdf
│   │   └── slide.pptx
│   ├── ISAAC2014
│   │   └── slide.pptx
│   ├── ISAAC2015
│   │   └── slide.pdf
│   ├── ISAAC2016
│   │   └── slide.pdf
│   ├── JCDCGG2014
│   │   ├── abstract.pdf
│   │   └── slide.pptx
│   ├── LASymposium2015winter
│   │   ├── paper.pdf
│   │   └── slide.pdf
│   ├── PhD-Thesis-Defense
│   │   ├── Duc_FinalDefense_20180507_handout.pdf
│   │   └── Duc_FinalDefense_20180507.pdf
│   ├── SIGAL-157
│   │   └── slide.pdf
│   ├── WAAC2018
│   │   ├── abstract.pdf
│   │   └── slide.pdf
│   └── WALCOM2017
│       └── slide.pdf
├── events.md
├── Gemfile
├── Gemfile.lock
├── _includes
│   ├── author-info.html
│   ├── disqus.html
│   ├── files.html
│   ├── footer.html
│   ├── header.html
│   ├── head.html
│   ├── image.html
│   ├── intensedebate.html
│   ├── news.html
│   ├── search-form.html
│   └── version.html
├── index.md
├── keybase.txt
├── _latex-template
│   ├── Beamer-Santiago.md
│   ├── HUS-Beamer.md
│   ├── JAIST-Doctoral-Thesis.md
│   ├── LIPIcs-Template.md
│   ├── MIT-Lecture-Note.md
│   ├── Name-Card.md
│   ├── Preprint-Elsevier.md
│   ├── Springer-LNCS.md
│   └── Vietnamese-CV.md
├── latex-template
│   ├── Beamer-Santiago
│   │   ├── Beamer-Santiago.zip
│   │   ├── .DS_Store
│   │   └── Santiago2008.pdf
│   ├── HUS-Beamer
│   │   ├── HUS-Beamer.pdf
│   │   └── HUS-Beamer.zip
│   ├── JAIST-Doctoral-Thesis
│   │   ├── abstract.pdf
│   │   ├── doctor-is.zip
│   │   └── paper.pdf
│   ├── MIT-Lecture-Note
│   │   ├── alg-comb-lecture-n.pdf
│   │   └── alg-comb-lecture-n.zip
│   ├── Name-Card
│   │   ├── main.pdf
│   │   └── Name-Card.zip
│   ├── Preprint-Elsevier
│   │   ├── main.pdf
│   │   └── Preprint-Elsevier.zip
│   └── Vietnamese-CV
│       └── Vietnamese-CV.zip
├── latex-template.md
├── _layouts
│   ├── bib.html
│   ├── blog-post.html
│   ├── collection_item.html
│   ├── collection_main.html
│   ├── page.html
│   ├── post.html
│   ├── pub_style.html
│   └── search.html
├── LICENSE.txt
├── misc.html
├── pdf
│   ├── .gitignore
│   └── ShortestReconfigurationSequenceforSlidingTokensonSpiders.pdf
├── _plugins
│   ├── hook-add-last-modified-date.rb
│   └── version_reporter.rb
├── _posts
│   ├── 2018-05-26-install-and-configure-ubuntu-16-04-lts.md
│   ├── 2018-05-26-install-latex2html-in-windows.md
│   ├── 2018-05-26-setup-jekyll-in-windows.md
│   ├── 2018-05-26-some-beamer-tips.md
│   ├── 2018-05-26-some-notes-on-installing-arch-linux.md
│   ├── 2018-06-11-download-windows-iso.md
│   ├── 2018-06-24-phd-degree-conferment-ceremony.md
│   ├── 2018-06-29-mẫu-lý-lịch-khoa-học.md
│   ├── 2018-08-08-my-first-experience-with-android-phones.md
│   ├── 2018-11-24-xây-dựng-trang-cá-nhân-với-jekyll.md
│   └── .gitignore
├── publications.md
├── pub_style.csl
├── README.md
├── robots.txt
├── search.html
├── services.md
├── slides
│   ├── .gitignore
│   ├── HUS_20180831.pdf
│   └── HUS_20181012.pdf
├── springer-lncs.csl
├── static
│   ├── css
│   │   ├── custom.css
│   │   ├── LaTeXMLstyle.css
│   │   ├── solarized-dark.css
│   │   └── solarized-light.css
│   ├── files
│   │   ├── other-writings
│   │   │   └── Needleman_2009_Làm toán để tìm được công việc tốt.pdf
│   │   └── posts
│   │       ├── mẫu-lý-lịch-khoa-học
│   │       │   └── ly-lich-khoa-hoc.pdf
│   │       └── some-notes-on-installing-arch-linux
│   │           └── linuxsslvpn.gz
│   ├── img
│   │   ├── Duc.ico
│   │   ├── Duc.jpg
│   │   └── posts
│   │       ├── my-first-experience-with-android-phones
│   │       │   ├── screen1.png
│   │       │   ├── screen2.png
│   │       │   ├── screen3.png
│   │       │   └── screen4.png
│   │       ├── phd-degree-conferment-ceremony
│   │       │   ├── Duc_Uehara_01.JPG
│   │       │   ├── Duc_Uehara_02.JPG
│   │       │   ├── Duc_Uehara_03.JPG
│   │       │   ├── Duc_Uehara_04.JPG
│   │       │   ├── Duc_Uehara_05.JPG
│   │       │   ├── Uehara_Duc_Asano.JPG
│   │       │   └── With_Tung_and_Others.jpg
│   │       └── some-notes-on-installing-arch-linux
│   │           └── fp_jaistvpn1and2.png
│   └── js
│       ├── lunr.min.js
│       └── search.js
├── teaching.md
└── .travis.yml

48 directories, 153 files

Ở đây, tôi giải thích vai trò của một số tệp và thư mục.

Tệp/Thư mục Vai trò
_config.yml Chứa các thông tin cấu hình trang cá nhân. Khi sinh (generate) trang cá nhân, Jekyll sẽ sử dụng các cấu hình trong tệp này
static Chứa các tệp và thư mục cần thiết để xây dựng trang cá nhân. Các tệp trong thư mục static/css được sử dụng để tùy chỉnh các cách hiển thị trang cá nhân (font chữ, màu, v.v…). Các tệp trong thư mục static/js là một số mã Javascript được sử dụng trong trang cá nhân. Các tệp trong thư mục static/img chứa các hình ảnh được sử dụng trong trang cá nhân. Các tệp trong thư mục static/files chứa các tệp liên quan đến các bài viết (blog posts).
_includes Chứa các thành phần cần thiết (tệp dạng *.html), ví dụ như đầu trang (head.html), thanh di chuyển (navbar, được đặt trong header.html), cuối trang (footer.html), v.v… để xây dựng một cấu trúc hoàn chỉnh, ví dụ như một trang (page), một bài viết (blog post), v.v… Bạn có thể thêm nội dung bất kỳ một tệp nào trong thư mục này vào một tệp khác bằng cách sử dụng Liquid tag, ví dụ như {% include head.html %}
_layouts Các cấu trúc hoàn chỉnh (tệp dạng *.html), ví dụ như một trang (page), một bài viết (blog post), được xây dựng phần lớn các cấu trúc ở thư mục _includes
_plugins Chứa các plugins tôi cần: hook-add-last-modified-date.rb để lấy thời gian chỉnh sửa tệp cuối cùng, và version_reporter.rb để lấy phiên bản Jekyll tôi đang sử dụng
index.md Trang chủ (Home)
_posts Chứa các bài viết (blog post). Tên tệp thường có dạng YYYY-MM-DD-name-of-blog-post.md
search.html Chứa bộ máy tìm kiếm nội dung các bài viết trong trang cá nhân.
_drafts Chứa bản nháp (chưa publish) của các bài viết (blog post). Tên tệp thường có định dạng name-of-blog-post.md
misc.html Liệt kê các bài viết (blog posts) và các thứ linh tinh khác mà tôi thích
_data Chứa một số tệp dữ liệu nhỏ định dạng *.yml để xây dựng một số phần, ví dụ như danh sách các tin tức mới (news), hay danh sách các đồng tác giả (co-authors), hay danh sách các thành phần của thanh di chuyển (navigation bar items), v.v…
_bibliography Chứa các tệp dạng *.bib phục vụ cho mục đích trích dẫn tài liệu tham khảo (lấy từ tệp _bibliography/references.bib) hoặc xây dựng danh sách các ấn phẩm khoa học (list of publications) của tôi (lấy từ các tệp trong thư mục _bibliography/publications) sử dụng Jekyll Scholar
pub_style.csl, springer-lncs.csl Các tệp cấu hình cách hiển thị các ấn phẩm và trích dẫn thông qua Jekyll Scholar
publications.md Liệt kê các ấn phẩm (publications) và tiền ấn phẩm (preprints) của tôi thông qua Jekyll Scholar (lấy thông tin từ các tệp trong thư mục _bibliography/publications)
events.md Liệt kê các sự kiện (ví dụ như hội thảo, seminar, v.v…) mà tôi đã tham gia, cùng với một số tài liệu liên quan
pdf, slides Các thư mục chứa các tài liệu dạng PDF (chủ yếu là các bản tiền ấn phẩm của tôi) và các tệp trình chiếu (Beamer, PowerPoint)
co-authors.md Danh sách các đồng tác giả (co-authors) của tôi
services.md Danh sách các vai trò (sub-reviewer, reviewer, v.v…) tôi đã đảm nhận
_latex-template Thư mục chứa các mô tả về các mẫu LaTeX tôi thường dùng. Thư mục này là một phần để xây dựng bộ sưu tập (collection) các mẫu LaTeX tôi thường dùng
latex-template Thư mục chứa các mẫu LaTeX tôi thường dùng. Thư mục này là một phần để xây dựng bộ sưu tập (collection) các mẫu LaTeX tôi thường dùng
latex-template.md Tệp liệt kê các mẫu trong bộ sưu tập (collection) các mẫu LaTeX tôi thường dùng
_courses Thư mục chứa các mô tả về một số môn học tôi tham gia giảng dạy hoặc trợ giảng
courses Thư mục chứa các tài liệu liên quan đến một số môn học tôi tham gia giảng dạy hoặc trợ giảng
teaching.md Liệt kê một số môn học tôi tham gia giảng dạy hoặc trợ giảng
.travis.yml, build.sh Cấu hình tự sinh trang cá nhân thông qua Travis CI
keybase.txt Tệp này được sử dụng để kiểm chứng (verify) trang cá nhân thuộc sở hữu của tôi khi tôi đăng ký tài khoản Keybase.io

Boostrap + Font Awesome + Academicons + Solarized Dark Code Highlight

Trang cá nhân của tôi được xây dựng dựa trên nền tảng các thư viện HTML, CSS, và JS của Bootstrap. Sử dụng các thư viện này giúp tôi tiết kiệm khá nhiều thời gian khi không phải tự tạo các thành phần và các hiệu ứng tôi cần. Tôi cũng sử dụng Font AwesomeAcademicons cho các icon trong trang cá nhân của mình, và sử dụng Solarized Dark CSS Style (với một số chỉnh sửa nhỏ) để đánh dấu các đoạn mã.

Tệp _config.yml

  • Định nghĩa vai trò của một số thư mục
# Where things are
source:       .
destination:  ./_site
plugins_dir:  ./_plugins
layouts_dir:  ./_layouts
includes_dir: ./_includes
  • Định nghĩa phần đuôi của các tệp mã nguồn.
# Handling Reading
markdown_ext: "markdown,mkdown,mkdn,mkd,md"
  • Các plugin cần dùng
# Plugins
plugins: ['jekyll-sitemap','jekyll-feed','jekyll/scholar','pygments.rb','jekyll-email-protect','jekyll-last-modified-at']
  • Một số cấu hình khác
# Conversion
markdown: kramdown
highlighter: pygments

# Markdown Processors
kramdown:
  input: GFM
  syntax_highlighter: pygments

# Serving
port:    4000
host:    127.0.0.1
baseurl: "" # does not include hostname
  • Thông tin cá nhân (sẽ sử dụng ở các phần sau).

  • Thông thường, Jekyll sẽ bỏ qua các tệp và thư mục có tên bắt đầu bằng _. Đối với các thư mục khác, để Jekyll không sao chép các thư mục này khi xây dựng trang cá nhân, các bạn có thể thêm tên tệp/thư mục vào phần exclude trong tệp _config.yml, ví dụ như sau

# Exclude file/folder
exclude: ['Gemfile*','Makefile','Curriculum Vitae','Business Card','*.bat','*.csl','exclude_copy*','gitlab-ci.yml','README*','LICENSE.txt','combinatorial-reconfiguration','Demo-LaTeXML','travis.yml','vendor','build.sh']

Chú ý rằng việc thêm vendor vào mục exclude là cần thiết nếu bạn muốn làm việc với Travis CI.

  • Liệt kê các collection.
collections:
  posts:
    output: true
    permalink: "/misc/:categories/:year/:title/"
  courses:
    output: true
  latex-template:
    output: true
# Jekyll-scholar configuration
scholar:
  style: springer-lncs
  locale: en

  sort_by: year
  order: asscending
  bibliography_list_tag: ol

  group_by: none
  group_order: ascending

  source: ./_bibliography
  bibliography: references.bib
  bibliography_template: bib

  replace_strings: true
  join_strings:    true

  reference_tagname: div

  use_raw_bibtex_entry: true
  bibtex_filters:
  - superscript
  - latex

  query: "@*"

Thư mục _includes

Tệp head.html

  • Tất cả các phần sau đây được đặt giữa <head></head>.

  • Phần đầu tiên

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

nhằm mục đích phục vụ tốt hơn việc hiển thị trang cá nhân trên các loại thiết bị và trình duyệt khác nhau.

  • Tiếp đó là phần hiển thị tiêu đề trang và các thông tin cần thiết (meta data) để phục vụ việc truy xuất thông tin từ các bộ máy tìm kiếm như Google, Bing, Yahoo, DuckDuckGo, v.v… Ở đây tôi sử dụng Liquid Tag để phân biệt việc hiển thị thông tin trên trang chủ và trên các trang khác.
<title> {{ page.title }} </title>
{% if page.url == "/" %}
<meta name="description" content="{{ site.author }}'s personal webpage" />
<meta name="author" content="{{ site.author }}" />
<meta name="keywords" content="{{ site.keywords }}" />
{% else %}
{% if page.description %}
<meta name="description" content="{{ page.description }}" />
{% endif %}
{% if page.author %}
<meta name="author" content="{{ page.author }}" />
{% endif %}
{% if page.keywords %}
<meta name="keywords" content="{{ page.keywords }}" />
{% endif %}
{% endif %}
<meta name="generator" content="Jekyll {% include version.html %}, Bootstrap 4.1.1, Font Awsome, MathJax" />

Chú ý rằng các thông tin này sẽ được lấy từ phần nằm ngay đầu tiên của mỗi tệp nguồn (giữa hai dấu ---) và có thể là cả từ tệp _config.yml. Một ví dụ từ tệp nguồn của bài viết này (_posts/2018-11-24-xây-dựng-trang-cá-nhân-với-jekyll.md) là:

---
layout: blog-post
title: Xây dựng trang cá nhân với Jekyll
author: Duc A. Hoang
categories:
  - "jekyll"
  - "linux"
  - "windows"
comment: true
description: Bài viết này mô tả quá trình xây dựng trang cá nhân với Jekyll
keywords: trang cá nhân, jekyll, Duc A. Hoang
<!--published: false-->
---

Trong ví dụ trên, bài viết này sẽ được sinh với layout blog-post (nằm ở _layouts/blog-post.html). Các thông tin meta data về tiêu đề (title), tác giả (author), mô tả (description), từ khóa (keywords) đều có thể được truy cập lần lượt thông qua Liquid Tag tương ứng {{ page.title }}, {{ page.author }}, {{ page.description }}, {{ page.keywords }}. Thông tin về thể loại (categories) được sử dụng khi liệt kê các bài viết (xem tệp misc.html). Để hiển thị hộp thoại comment cuối trang/bài viết, đặt comment: true (ngược lại, đặt comment: false, hoặc đặt comment: true giữa hai dấu <!---->). Tương tự như thế, nếu bạn không muốn hiển thị trang/bài viết (chẳng hạn do bạn chưa hoàn thành quá trình xây dựng trang/bài viết đó), thì bạn có thể đặt published: false. Chú ý rằng việc hiển thị bài viết hay không được cài đặt mặc định trong Jekyll, và bạn có thể sử dụng published: false hoặc published: true (mặc định) trong tất cả các trang/bài viết, trong khi phần hộp thoại comment thì do tôi cài đặt trong các layout tôi tự xây dựng.

  • Dòng tiếp theo
<meta name="google-site-verification" content="kVN3FDkRoOErNs0auO3-jBDYtW4xzFqVGBCiugmymHM" />

được sử dụng để kiểm tra (verify) rằng tôi là chủ của trang cá nhân khi tôi đăng ký với Google Search Console. Các bạn có thể bỏ dòng này đi.

  • Tiếp theo đó là phần khai báo các thư viện JS và CSS cần thiết. Một phần không thể thiếu là sử dụng MathJax để hiển thị các công thức toán.
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>

<!-- Fonts -->
<!--	<link href="https://fonts.googleapis.com/css?family=Cousine|Nunito" rel="stylesheet">-->

<!-- Personal CSS configuration -->
<link rel="stylesheet" href="/static/css/custom.css">

<!-- MathJax -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/x-mathjax-config">
	MathJax.Hub.Config({
	tex2jax: {
	inlineMath: [['$','$'], ['\\(','\\)']],
	processEscapes: true
	}
	});
</script>

<!-- Enable Popover -->
<script>
$(function () {
$('[data-toggle="popover"]').popover()
})
</script>

<!-- Enable Tooltips -->
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>

Chú ý rằng phần sử dụng Popover và Tooltip chỉ hoạt động nếu các bạn sử dụng các thư viện của Bootstrap. Việc khai báo jquery-3.3.1.slim.min.js trước bootstrap.min.js cũng là cần thiết để tránh một số xung đột giữa hai thư viện. Tất cả các cấu hình liên quan đến CSS đều nằm trong tệp static/css/custom.css.

  • Phần
<!-- Favicon -->
<link rel="shortcut icon" href="/static/img/Duc.ico" type='image/x-icon'>

<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
  <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->

được sử dụng để hiện thị favicon (ở đây tôi sử dụng tệp static/img/Duc.ico) và cấu hình khi xem trang cá nhân với trình duyệt Internet Explorer.

  • Phần cuối
{% feed_meta %}

được sử dụng bởi plugin jekyll-feed để sinh tệp RSS feed feed.xml cho các bài viết (blog posts).

Tệp header.html

  • Tất cả các phần sau đây được đặt giữa <header></header>.

  • Phần

<nav class="navbar-expand-lg navbar-light fixed-top">
<div class="container">
	<!-- Brand and toggle get grouped for better mobile display -->
<!--	<a class="navbar-brand" href="#">Navbar</a>-->
	<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
	<span class="navbar-toggler-icon"></span>
	</button>
	
	<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
	<ul class="navbar-nav mr-auto">
	{% for nav in site.data.navbar %}
	{% if nav.sub-item != null %}
	<li class="nav-item dropdown">
		<a class="nav-link dropdown-toggle" href="{{ nav.href }}" id="navbarDropdown{{ nav.title | replace:' ','' }}" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
		<i class="{{ nav.fa-icon }}" aria-hidden="true"></i> {{ nav.title }}
		</a>
		<div class="dropdown-menu" aria-labelledby="navbarDropdown{{ nav.title | replace:' ','' }}">
		{% for item in nav.sub-item %}
		<a class="dropdown-item" href="{{ item.sub-href }}"><i class="{{ item.sub-fa-icon }}" aria-hidden="true"></i> {{ item.sub-title }}</a>
		{% endfor %}
		</div>
	</li>
	{% else %}
		{% if page.url == nav.href %}
		<li class="nav-item active"><a class="nav-link" href="{{ nav.href }}"><i class="{{ nav.fa-icon }}" aria-hidden="true"></i> {{ nav.title }}</a></li>
		{% else %}
		<li class="nav-item"><a class="nav-link" href="{{ nav.href }}"><i class="{{ nav.fa-icon }}" aria-hidden="true"></i> {{ nav.title }}</a></li>
		{% endif %}
	{% endif %}
	{% endfor %}
	</ul>
<!--	<ul class="navbar-nav">-->
<!--	<li class="nav-item"><a class="nav-link" href="https://github.com/username/username.github.io/tree/source" data-toggle="tooltip" data-placement="bottom" title="Source"><i class="fas fa-download"></i></a></li>-->
<!--	<li class="nav-item"><a class="nav-link" href="{{ site.baseurl }}/feed.xml" data-toggle="tooltip" data-placement="bottom" title="Blog RSS Feed"><i class="fas fa-rss-square"></i></a></li>-->
<!--	<li class="nav-item"><a class="nav-link" href="{{ site.baseurl }}/sitemap.xml" data-toggle="tooltip" data-placement="bottom" title="Sitemap"><i class="fas fa-sitemap"></i></a></li>-->
<!--	</ul>-->
	</div>
</div>
</nav> 

được sử dụng để xây dựng navigation bar với các thư viện của Bootstrap và các icon từ Font Awesome. Các dữ liệu cần thiết được lấy từ _data/navbar.yml thông qua site.data.navbar. Ví dụ tệp _data/navbar.yml của tôi chứa các dữ liệu như sau

Tệp: _data/navbar.yml
- title: "Home"
  fa-icon: "fa fa-home"
  href: "/"
- title: "Research"
  fa-icon: "fa fa-folder"
  href: "#"
  sub-item:
    - sub-title: "PhD Thesis (2018-06)"
      sub-fa-icon: "fa fa-file-pdf"
      sub-href: "http://hdl.handle.net/10119/15431"
    - sub-title: "Publications"
      sub-fa-icon: "fa fa-book"
      sub-href: "/publications/"
    - sub-title: "Co-authors"
      sub-fa-icon: "fa fa-group"
      sub-href: "/co-authors/"
    - sub-title: "Participated Events"
      sub-fa-icon: "fa fa-calendar"
      sub-href: "/events/"
    - sub-title: "Professional Services"
      sub-fa-icon: "fa fa-pencil-square"
      sub-href: "/services/"
    - sub-title: "Reconfiguration Bibliography"
      sub-fa-icon: "fa fa-list"
      sub-href: "/combinatorial-reconfiguration/"
- title: "Teaching"
  fa-icon: "fa fa-graduation-cap"
  href: "/teaching/"
- title: "Misc"
  fa-icon: "fa fa-box-open"
  href: "/misc/"

Tệp author-info.html

Tệp này được sử dụng để cài đặt việc hiển thị các thông tin cơ bản được lấy từ trong tệp _config.yml của tôi.

(Một phần) Tệp: _config.yml
# Personal information
url: http://hoanganhduc.github.io
author: Duc A. Hoang
title: Welcome to Đức's personal webpage!
auth_current_position:
email_personal_user: anhduc.hoang1990
email_personal_domain: gmail.com
email_work_user: hoanganhduc
email_work_domain: jaist.ac.jp
website: hoanganhduc.github.io
website_full: http://hoanganhduc.github.io
keywords: Duc A. Hoang, JAIST, anhduc.hoang1990, hoanganhduc, personal webpage
orcid: 0000-0002-8635-8462
institute_address: Japan Advanced Institute of Science and Technology, 1-1 Asahidai, Nomi, Ishikawa, 923-1292 Japan

Việc lấy từng thông tin tương ứng, ví dụ như url, được thực hiện thông qua Liquid Tag {{ site.url }}. Nội dung của tệp là

Tệp: _includes/author-info.html
<div class="row">
<div class="text-center col-xs-12 col-sm-3 col md-3 col-lg-3">
<img src="{{ site.baseurl }}/static/img/Duc.jpg" class="img-thumbnail" alt="" title="Duc A. Hoang" style="width:190px;">
</div>
<div class="col-xs-12 col-sm-9 col-md-9 col-lg-9" style="margin-top:0px;">
<h2>{{ site.auth_current_position }}</h2>
<p>
 <i class="fa fa-envelope" aria-hidden="true"></i> 
  <a href="mailto:{{ site.email_work_user | append:'@' | append: site.email_work_domain | encode_email }}">  
 {{ site.email_work_user }}<i class="fa fa-at"></i>{{ site.email_work_domain }}</a>.  
<br /><i class="fa fa-envelope" aria-hidden="true"></i>
 <a href="mailto:{{ site.email_personal_user | append:'@' | append: site.email_personal_domain | encode_email }}">
{{ site.email_personal_user }}<i class="fa fa-at"></i>{{ site.email_personal_domain }}</a>.
</p>
<p>
<i class="fa fa-globe" aria-hidden="true"></i> <a href="{{ site.website_full }}">{{ site.website }}</a>.
</p>
 <address> 
 <i class="fa fa-institution" aria-hidden="true"></i> {{ site.institute_address }}. 
 </address> 
<p>
<i class="fa fa-file-pdf-o" aria-hidden="true"></i> Duc A. Hoang's Curriculum Vitae [<a href="{{ site.baseurl }}/CV.pdf">English</a>] [<a href="{{ site.baseurl }}/CV-vi.pdf">Tiếng Việt</a>].
</p>
<p>
<i class="ai ai-orcid" aria-hidden="true"></i> <a href="https://orcid.org/{{ site.orcid }}" target="orcid.widget" rel="noopener noreferrer" style="vertical-align:top;">orcid.org/{{ site.orcid }}</a>.
</p>
</div>

</div>

Chú ý rằng ở đây tôi sử dụng Boostrap Grid System. Nếu bạn không sử dụng Boostrap thì việc hiển thị trang cá nhân của bạn có thể không giống với trang cá nhân của tôi.

Tệp news.html

Tệp này được sử dụng để hiển thị các tin tức tôi muốn. Ở đây tôi sử dụng Bootstrap List Group để hiển thị từng thông tin. Các thông tin cần thiết được lấy từ _data/news.yml.

Tệp: _includes/news.html
<ul class="list-group" style="height:250px; overflow:auto; overflow-y:scroll;">
{% for new in site.data.news %}
{% case new.pub-type %}
  {% when "conference" %}
  <li class="list-group-item list-group-item-secondary">
  {% when "journal" %}
  <li class="list-group-item list-group-item-success">
  {% when "award" %}
  <li class="list-group-item list-group-item-primary">
  {% when "info" %}
  <li class="list-group-item list-group-item-info">
{% endcase %}
  <strong>{{ new.time }}: </strong> {{ new.text }}
  </li>
{% endfor %}
</ul>
<br />
Tệp: _data/news.yml
- time: "July 17, 2018"
  text: "A list of some publications related to \"Combinatorial Reconfiguration\" is <a href='https://hoanganhduc.github.io/combinatorial-reconfiguration/'>available online</a>. Prior to this date, it was hosted at <a href='http://www.jaist.ac.jp/~s1520016/combinatorial-reconfiguration/'>my personal webpage at JAIST</a>."
  pub-type: "info"
- time: "June 22, 2018"
  text: "Awarded JAIST <a href='http://www.jaist.ac.jp/english/education/degree/awards.html' data-toggle='tooltip' data-placement='top' title='The outstanding performance award will be granted to the students who complete the master’s/doctoral program with excellent academic performance'>Outstanding Performance Award</a> for doctoral students."
  pub-type: "award"
- time: "May 07, 2018"
  text: "Completed PhD final defense at JAIST. Download my presentation slide <a href='/events/PhD-Thesis-Defense/Duc_FinalDefense_20180507.pdf'>here</a> (or <a href='/events/PhD-Thesis-Defense/Duc_FinalDefense_20180507_handout.pdf'>here</a> for a compressed version with unnecessary animation removed)."
  pub-type: "info"
- time: "March 14, 2018"
  text: "Mariana Teatini Ribeiro and <a href='http://homepages.dcc.ufmg.br/%7eviniciussantos/'>Vinícius Fernandes dos Santos</a> informed us about a flaw in the proof of Proposition 6 of our paper \"Sliding Tokens on Block Graphs\"."
  pub-type: "info"
- time: "December 04, 2016"
  text: "A manuscript entitled \"Sliding Tokens on Block Graphs\" has been accepted to <a href='http://walcom2017.nctu.edu.tw/' data-toggle='tooltip' data-placement='top' title='The 11th International Conference and Workshops on Algorithms and Computation, Hsinchu, Taiwan, March 29-31, 2017'>WALCOM 2017</a> (joint work with Eli Fox-Epstein and Ryuhei Uehara)."
  pub-type: "conference"
- time: "August 31, 2016"
  text: "A manuscript entitled \"Sliding Tokens on a Cactus\" has been accepted to <a href='http://rp-www.cs.usyd.edu.au/~visual/isaac2016/' data-toggle='tooltip' data-placement='top' title='The 27th International Symposium on Algorithms and Computation, Sydney, Australia, December 12-14, 2016'>ISAAC 2016</a> (joint work with Ryuhei Uehara)."
  pub-type: "conference"
- time: "August 31, 2015"
  text: "A manuscript entitled \"Sliding Token on Bipartite Permutation Graphs\" has been accepted to <a href='http://www.al.cm.is.nagoya-u.ac.jp/isaac2015/' data-toggle='tooltip' data-placement='top' title='The 26th International Symposium on Algorithms and Computation, Nagoya, Japan, December 9-11, 2015'>ISAAC 2015</a> (joint work with Eli Fox-Epstein, Yota Otachi, and Ryuhei Uehara)."
  pub-type: "conference"
- time: "July 16, 2015"
  text: "A manuscript entitled \"Linear-Time Algorithm for Sliding Tokens on Trees\" has been accepted to <a href='http://www.journals.elsevier.com/theoretical-computer-science/' data-toggle='tooltip' data-placement='top' title='Theoretical Computer Science'>Theoretical Computer Science</a> (joint work with Erik D. Demaine, Martin L. Demaine, Eli Fox-Epstein, Takehiro Ito, Hirotaka Ono, Yota Otachi, Ryuhei Uehara, and Takeshi Yamada)."
  pub-type: "journal"
- time: "March 20, 2015"
  text: "Awarded JAIST <a href='http://www.jaist.ac.jp/english/education/degree/awards.html' data-toggle='tooltip' data-placement='top' title='The outstanding performance award will be granted to the students who complete the master’s/doctoral program with excellent academic performance'>Outstanding Performance Award</a> for master’s students."
  pub-type: "award"
- time: "August 29, 2014"
  text: "A manuscript entitled \"Polynomial-Time Algorithm for Sliding Tokens on Trees\" has been accepted to <a href='http://tcs.postech.ac.kr/isaac2014/' data-toggle='tooltip' data-placement='top' title='The 25th International Symposium on Algorithms and Computation, Jeonju, Korea, December 15-17, 2014'>ISAAC 2014</a> (joint work with Erik D. Demaine, Martin L. Demaine, Eli Fox-Epstein, Takehiro Ito, Hirotaka Ono, Yota Otachi, Ryuhei Uehara, and Takeshi Yamada)."
  pub-type: "conference"  

Tệp disqus.htmlintensedebate.html

Các tệp này sử dụng để cấu hình các hộp thoại comment từ DisqusIntenseDebate. Tôi trước kia sử dụng Disqus nhưng sau đó chuyển sang IntenseDebate do IntenseDebate có hỗ trợ comment và hiển thị các công thức toán học (mặc dù vẫn có lỗi và bạn thường phải refresh trang/bài viết để hiển thị đúng). Việc sử dụng các công cụ này khá đơn giản. Bạn chỉ cần đăng ký tài khoản và chèn đoạn mã HTML họ cung cấp vào các trang/bài viết bạn muốn. Ở đây tôi chèn các đoạn mã này vào các tệp tương ứng, và chèn vào các trang/bài viết thông qua Liquid Tag, ví dụ như

{% if page.comment %}
{% include intensedebate.html %} 
{% endif %} 

Tệp files.htmlimage.html

Tệp files.html được sử dụng để chèn các đường dẫn đến các tệp và thư mục con của thư mục static/files/posts/name-of-blog-post, với name-of-blog-post là một phần tên bài viết bạn đặt (chú ý là mỗi bài viết có mã nguồn với tên dạng YYYY-MM-DD-name-of-blog-post.md). Ví dụ nếu sử dụng đoạn mã {% include files.html name="your-file.pdf" text="your file" %} trong bài viết này thì nó sẽ chèn đường dẫn đến tệp static/files/posts/xây-dựng-trang-cá-nhân-với-jekyll/your-file.pdf, cụ thể như your-file .

Tương tự như files.html, tệp image.html được sử dụng để chèn các tệp hình ảnh của thư mục static/img/posts/name-of-blog-post, với name-of-blog-post là một phần tên bài viết bạn đặt. Một ví dụ về sử dụng image.html {% include image.html name=your-figure.png" caption="This is the caption for your figure" width="70%" %} .

Tệp footer.html

Chứa các thông tin ở phần cuối của trang/bài viết.

Tệp: _includes/footer.html
<footer class="footer text-right">
	<span class="text-muted credit">
		<a href="#" class="back-to-top" style="display: inline; text-decoration:none;">
		Back to Top <i class="fa fa-arrow-circle-up"></i>
		</a>
		<br />Last Modified: {{ site.time | date: '%B %-d, %Y' }}
		<br />The content on this site is shared under a <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0 license</a>, unless otherwise noted
	</span>
</footer>

Tệp search-form.html

Chứa search form để tìm kiếm trong nội dung của các bài viết.

Tệp: _includes/search-form.html
<form action="/search/index.html" method="get">
<div class="form-group">
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8">
<input type="text" class="form-control form-control-lg" id="search-box" name="query" placeholder="type any keyword here">
</div>
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4">
<input type="submit" role="button" class="btn btn-lg btn-primary" value="search all posts">
</div>
</div>
</form>
<br />

Thư mục _layouts

Chứa các layout định sẵn cho từng trang/bài viết.

Tệp blog-post.html, page.html, post.html, collection_main.html, và collection_item.html

Một ví dụ về việc xây dựng layout là

Tệp: _layouts/blog-post.html
<!DOCTYPE html>
<html lang="en">

{% include head.html %}

<body>

<div class="container">
{% include header.html %}
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<h1 class="display-4">{{ page.title }}</h1>
<h5 class="text-monospace">
{% if page.author %}
<i class="fas fa-user"></i> {{ page.author }}
{% endif %}
<i class="fas fa-user-clock"></i> {{ page.date | date_to_string }}
<i class="fas fa-user-edit"></i> {{ page.last_modified_at | date_to_string }} 
{% if page.categories %}
<i class="fas fa-user-tag"></i> {{ page.categories | join: ', ' }}
{% endif %}
</h5>
<hr>
{{ content }}

{% if page.comment %}
{% include intensedebate.html %} 
{% endif %} 

<hr>

{% include footer.html %}
	
</div>
</div>
</div>

</body>
</html>

Hoàn toàn tương tự, bạn có thể xây dựng các layout khác (_layouts/page.html, _layouts/post.html, _layouts/collection_main.html, _layouts/collection_item.html) bằng cách thêm hoặc bớt các phần không cần thiết, hoặc là tạo các tệp mới trong thư mục _includes và thêm vào layout, hoặc thêm trực tiếp các đoạn mã HTML vào layout.

Tệp pub_style.htmlbib.html

Một dạng layout đặc biệt là layout sử dụng để liệt kê các ấn phẩm (publications) của tôi. Layout này được xây dựng trong tệp _layouts/pub_style.html như sau.

Tệp: _layouts/pub_style.html
---
---

{{ reference }}

{% if entry.note %}
<p><span style="color:#ff0000">Note:</span> {{ entry.note }}</p>
{% endif %}

<p>
{% if entry.abstract %}
<a class="btn-lg my-btn-link" data-toggle="collapse" href="#{{ key }}-abstract"> Abstract </a>
{% endif %}
{% if entry.doi %}
<a class="btn-lg my-btn-link" href="{{ entry.doi | prepend: 'http://dx.doi.org/'}}" data-toggle="tooltip" title="{{ entry.doi | prepend: 'doi:'}}">DOI</a>
{% endif %}
{% if entry.eprint %}
<a class="btn-lg my-btn-link" href="{{ entry.eprint | prepend: 'https://arxiv.org/abs/'}}" data-toggle="tooltip" title="{{ entry.eprint | prepend: 'arXiv:'}}">ArXiv</a>
{% endif %}
{% if entry.hdl %}
<a class="btn-lg my-btn-link" href="{{ entry.hdl | prepend: 'http://hdl.handle.net/'}}" data-toggle="tooltip" title="{{ entry.hdl | prepend: 'hdl:'}}">HDL</a>
{% endif %}
{% if entry.slide %}
<a class="btn-lg my-btn-link" href="{{ site.baseurl }}/events/{{ entry.slide }}">
Slide 
</a>
{% endif %}
{% if entry.pdf %}
<a class="btn-lg my-btn-link" href="{{ site.baseurl }}/pdf/{{ entry.pdf }}">
PDF 
</a>
{% endif %}
</p> 
{% if entry.abstract %}
<p id="{{ key }}-abstract" class="collapse text-muted">
	{{ entry.abstract }}
</p>
{% endif %}

Tương tự, bạn có thể xây dựng layout để hiển thị các tài liệu tham khảo khi trích dẫn chúng trong một bài viết.

Tệp: _layouts/bib.html
---
---

{{reference}}

<span>
{% if entry.doi %}
<span><a href="{{ entry.doi | prepend: 'https://doi.org/' }}">{{entry.doi | prepend: 'doi:'}}</a>.</span>
{% endif %}
{% if entry.eprint %}
<span><a href="{{ entry.eprint | prepend: 'https://arxiv.org/abs/' }}">{{entry.eprint | prepend: 'arXiv:'}}</a>.</span>
{% endif %}
{% if entry.hdl %}
<span><a href="{{ entry.hdl | prepend: 'http://hdl.handle.net/' }}">{{entry.hdl | prepend: 'hdl:'}}</a>.</span>
{% endif %}
{% if entry.hal_id %}
<span><a href="{{ entry.hal_id | prepend: 'http://hal.inria.fr/' }}">{{entry.hal_id | prepend: 'hal:'}}</a>.</span>
{% endif %}
</span>

% endraw %}
{% endhighlight %}
</figure>

### Tệp `search.html`

Một layout khác là layout được sử dụng để hiển thị kết quả tìm kiếm nội dung các bài viết.

<figure>
<figcaption class="figure-caption text-center"> Tệp: <code>_layouts/search.html</code> </figcaption>
{% highlight html %}
{% raw %}

---
layout: page
---
<div class="search">
	{{ content }}
</div>

Tệp misc.html – Liệt kê danh sách các bài viết

Một phần quan trọng trong tệp này là phần liệt kê các bài viết theo thể loại.

{% for category in site.categories %}
  <h3 id="{{ category[0] }}"><i class="fas fa-user-tag"></i> {{ category[0] }} ({{ category[1].size }})</h3>
  <ul>
    {% assign pages_list = category[1] %}
    {% for post in pages_list %}
      {% if post.title != null %}
      {% if group == null or group == post.group %}
      <li><a href="{{ site.url }}{{ post.url }}">{{ post.title }}</a> <time datetime="{{ post.date | date_to_xmlschema }}" itemprop="datePublished" class="text-muted">({{ post.date | date: "%B %d, %Y" }})</time></li>
      {% endif %}
      {% endif %}
    {% endfor %}
    {% assign pages_list = nil %}
    {% assign group = nil %}
  </ul>
{% endfor %}

Tệp publications.md – Liệt kê danh sách các ấn phẩm (publications)

Ví dụ, để liệt kê danh sách các tiền ấn phẩm (preprints), tôi làm như sau:

<div class="publication">

{% bibliography --template pub_style --style pub_style --file publications/preprints.bib %}

</div>

Tên tệp pub_style sau --template là để chỉ tệp _layouts/pub_style.html, còn tên tệp pub_style sau --style là để chỉ tệp pub_style.csl. Tất cả các tiền ấn phẩm của tôi được liệt kê trong tệp _bibliography/publications/preprints.bib. Bạn có thể làm hoàn toàn tương tự với các phần khác.

Tệp latex-template.md – Liệt kê các phần tử trong một collection

Để liệt kê các LaTeX template tôi thường dùng, tôi làm như sau (trong tệp latex-template.md)

<div class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
	<th>Template</th>
	<th>Type</th>
</tr>
</thead>
<tbody>
{% assign latex-template = site.latex-template | sort: 'date' | reverse %}
{% for template in latex-template %}
<tr>
	<td><a href="{{ template.url }}">{{ template.title }}</a></td>
	<td>{{ template.type }}</td>
</tr>
{% endfor %}
</tbody>
</table>

Sinh (generate) trang cá nhân với Jekyll và host trang cá nhân với GitHub

  • Sinh trang cá nhân từ mã nguồn đã tạo.
bundle exec jekyll build --config _config.yml

Bạn cũng có thể sử dụng lệnh bundle exec jekyll serve và truy cập tới địa chỉ http://localhost:4000 để kiểm tra nội dung trang cá nhân trước khi tiến hành các bước tiếp theo.

  • Đồng bộ mã nguồn với thư mục $HOME/username.github.io/source và commit nội dung lên branch source của GitHub repository username.github.io. Ở đây, tôi sử dụng rsync.
rsync -arv --exclude-from exclude_copy.txt . $HOME/username.github.io/source
cd $HOME/username.github.io/source
git add --all .
git commit -S -m "Source Files @ $(date +'%Y-%m-%d  %H:%M:%S')"
git push -u origin source

Ở đây, tệp exclude_copy.txt chứa các thư mục và tệp tôi không muốn commit lên branch source (bao gồm chính bản thân tệp đó, do đó bạn sẽ không tìm được tệp này trong mã nguồn của trang cá nhân của tôi).

  • Đồng bộ nội dung trang cá nhân với thư mục $HOME/username.github.io/master và commit nội dung lên branch master của GitHub repository username.github.io. Ở đây, tôi sử dụng rsynctree.
rsync -arv --exclude "README*" --exclude "LICENSE.txt" ./_site/* $HOME/username.github.io/master
cd $HOME/username.github.io/master
tree -H '.' --noreport --charset utf-8 -h -D -T "HTML Sitemap" -I "*.html" --timefmt "%b %d, %Y %H:%M" -t > sitemap.html
git add --all .
git commit -S -m "Local Build @ $(date +'%Y-%m-%d  %H:%M:%S')"
git push -u origin master

Cài đặt quá trình tự động với Travis CI

  • Tạo tệp .travis.yml có nội dung như sau:
Tệp: .travis.yml
dist: xenial
language: ruby
rvm:
  - 2.5
addons:
  apt:
    packages:
    - libcurl4-openssl-dev
    - tree
sudo: false # route your build to the container-based infrastructure for a faster build
cache: bundler
before_script:
 - "chmod +x build.sh" # or do this locally and commit
 - "gem install bundler"
 - "bundle install"
 - "git config --global user.name 'your-username'"
 - "git config --global user.email 'your_email@example.com'"
script:
- "./build.sh"
  • Tạo tệp build.sh có nội dung như sau.
Tệp: build.sh
#! /bin/bash

set -e

DEPLOY_REPO="https://${DEPLOY_BLOG_TOKEN}@github.com/username/username.github.io.git"

function clone_source_repo () {
	git clone https://${DEPLOY_BLOG_TOKEN}@github.com/username/username.github.io.git site
}

function build_jekyll_site () {
	bundle exec jekyll build --config _config.yml
}

function deploy () {
	rsync -arv --exclude "README*" --exclude "site" ./_site/* site
	cd site
	tree -H '.' --noreport --charset utf-8 -h -D -T "HTML Sitemap" -I "*.html" --timefmt "%b %d, %Y %H:%M" -t > sitemap.html
	git add --all .
	git commit -m "Travis CI Build $TRAVIS_BUILD_NUMBER @ $(TZ=':Asia/Ho_Chi_Minh' date +'%Y-%m-%d  %H:%M:%S')"
	git push -u origin master
	cd ..
}

function clean () {
	rm -rf *
}

main () {
	build_jekyll_site
	clone_source_repo
	deploy
	clean
}

main "$@"

trong đó ${DEPLOY_BLOG_TOKEN} là một khóa bí mật được thêm vào Travis CI. Khóa này được tạo từ GitHub bằng cách đăng nhập, và sau đó truy cập tới địa chỉ https://github.com/settings/tokens và chọn Generate new token. Trong phần Select scopes, đánh dấu chọn mục repo và tất cả các mục con của nó. Sao chép lại token được sinh ra và thêm vào Travis CI bằng cách đăng nhập Travis CI với GitHub, truy cập tới địa chỉ https://travis-ci.org/username/username.github.io/settings, và thêm vào mục Environment Variables một khóa với tên (Name) là DEPLOY_BLOG_TOKEN và giá trị (Value) là token đã sao chép từ GitHub. Chú ý rằng để Travis CI tự động sinh trang cá nhân mỗi khi có commit từ branch source, trước hết bạn cần khởi động repository bằng cách truy cập tới địa chỉ https://travis-ci.org/username/username.github.io/settings, chọn mục Current, và nhấn vào nút Activate repository ở cuối dòng thông báo This is not an active repository.

Sign commit với PGP secret key trong Travis CI

Tôi muốn sign mỗi commit từ Travis CI với PGP public key của tôi. Để thực hiện điều này, tôi làm các bước sau:

  • Bỏ passphrase từ secret key sử dụng gói gnupg1 trong Arch Linux. Xem chi tiết hướng dẫn ở đây. Giả sử kết quả của quá trình này là một tệp tên PGP-key.asc.
  • Mã hóa PGP-key.asc với Travis CI CLI.
travis encrypt-file PGP-key.asc -r username/username.github.io

trong đó username là tên đăng nhập tài khoản GitHub của bạn. Kết quả thu được có dạng như sau:

encrypting PGP-key.asc for username/username.github.io
storing result as PGP-key.asc.enc
storing secure env variables for decryption

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_0a6446eb3ae3_key -iv $encrypted_0a6446eb3ae3_iv -in PGP-key.asc.enc -out PGP-key.asc -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add PGP-key.asc.enc to the git repository.
Make sure not to add PGP-key.asc to the git repository.
Commit all changes to your .travis.yml.

Sau khi thu được tệp đã mã hóa PGP-key.asc.enc, bạn có thể sao chép tệp này vào GitHub repository. Chú ý rằng không sao chép tệp PGP-key.asc vào GitHub repository. Trong quá trình mã hóa, bạn cũng sẽ thu được một thông báo yêu cầu bạn thêm dòng có dạng

openssl aes-256-cbc -K $encrypted_0a6446eb3ae3_key -iv $encrypted_0a6446eb3ae3_iv -in PGP-key.asc.enc -out PGP-key.asc -d

vào before_install stage của .travis.yml để giải mã PGP-key.asc.enc. Tóm lại, tệp .travis.yml mới sẽ có dạng như sau

Tệp: .travis.yml
dist: xenial
language: ruby
rvm:
  - 2.5
addons:
  apt:
    packages:
    - libcurl4-openssl-dev
    - tree
sudo: false # route your build to the container-based infrastructure for a faster build
cache: bundler
before_script:
 - "chmod +x build.sh" # or do this locally and commit
 - "gem install bundler"
 - "bundle install"
 - openssl aes-256-cbc -K $encrypted_0a6446eb3ae3_key -iv $encrypted_0a6446eb3ae3_iv -in PGP-key.asc.enc -out PGP-key.asc -d
 - gpg2 --import PGP-key.asc
 - "git config --global user.name 'your-username'"
 - "git config --global user.email 'your_email@example.com'"
 - git config --global gpg.program gpg2
 - git config --global user.signingkey [your-PGP-key-fingerprint]
script:
- "./build.sh"