forked from flinhong/flinhong.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
1 lines (1 loc) · 86.9 KB
/
feed.xml
1
<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xml" href="https://flinhong.github.io/feed.xslt.xml"?><feed xmlns="http://www.w3.org/2005/Atom"><generator uri="http://jekyllrb.com" version="3.3.0">Jekyll</generator><link href="https://flinhong.github.io/feed.xml" rel="self" type="application/atom+xml"/><link href="https://flinhong.github.io/" rel="alternate" type="text/html"/><updated>2016-10-16T23:17:51+08:00</updated><id>https://flinhong.github.io//</id><title type="html">Frank’s Blog at GitHub</title><subtitle>This blog is an exploration of my quirky thoughts and random adventures through life. I hope you enjoy reading and perusing my posts, please leave me a comment... I'd love to hear from you!</subtitle><author><name>Frank Lin</name></author><entry><title type="html">Some tips for Pokemon Go</title><link href="https://flinhong.github.io/tips/tips-for-pokemon-go/" rel="alternate" type="text/html" title="Some tips for Pokemon Go"/><published>2016-07-27T00:00:00+08:00</published><updated>2016-07-27T00:00:00+08:00</updated><id>https://flinhong.github.io/tips/tips-for-pokemon-go</id><content type="html" xml:base="https://flinhong.github.io/tips/tips-for-pokemon-go/"><p>Pokemon Go has enthralled the hearts of fans, encouraging them to explore their surroundings to capture Pokemon. Here it comes to Hong Kong, and there are some of the most essential <a href="http://www.gamespot.com/gallery/pokemon-go-20-essential-tips-and-tricks/2900-752/">tips and tricks</a> needed to understand and better play Pokemon Go.</p> <h2 id="headid-lure-module">Lure Module</h2> <p>If a PokeStop is raining pink petals, it means someone has used a <a href="http://www.bustle.com/articles/172078-how-to-use-lure-modules-in-pokemon-go">Lure Module</a>, which increases the frequency of wild Pokemon appearing near that particular stop. Stay close to a PokeStop if you want to get the most out of its Lure; even better, get between two overlapping PokeStops with Lures on them for a greatly increased rate of wild Pokemon showing up.</p> <h2 id="headid-tracking-nearby-pokemon">Tracking Nearby Pokemon</h2> <p>The graphic in the bottom right area of the screen indicates some of the types of Pokemon nearby, with the number of footsteps indicative of how far away they are from you. To track a Pokemon, tap the bar to bring up the full list, select a specific Pokemon to highlight it, and then start walking. The footsteps will either decrease, or increase until the Pokemon disappears completely. If the number of steps decreases, it means the Pokemon is getting closer. This method can be used to track a Pokemon until it appears.</p> <p>If you walk in a direction and more steps appear, try turning around and walking in another direction until the Pokemon gets closer. When the Pokemon is down to zero steps, it means it’s very close to you! Simply select it, and wait a few seconds for it to appear. You’ll also notice pulses that emit as you explore the map. They indicate when major changes have occurred in the availability of the area’s Pokemon.</p> <h2 id="headid-catching-pokemon">Catching Pokemon</h2> <p>When engaging a Pokemon in battle, throwing the ball at the inner circle when it’s at its smallest is the best way to go. The colour of the inner circle indicates how hard a Pokemon is to capture. Green is the easiest, orange is harder, and red is the hardest to catch. Razz Berries can be fed to the Pokemon to lower the difficulty for the next catch. If the Pokemon breaks free, you’ll have to feed it another Razz Berry to lower it again.</p> <p>Once a Poke Ball has been thrown, it cannot be picked up again. If a Poke Ball throw hits the inside of the color circle an experience bonus is granted, depending on the size of the circle. You receive 100 XP for Excellent throws (Small), 50 XP for Great (Half), and 10 XP for Nice (Full). You can also spin a Poke Ball around in circles quickly and toss it, which activates a curveball bonus if the throw lands.</p> <h2 id="headid-trainer-rank-makes-it-easier-to-find-rare-pokemon">Trainer Rank Makes it Easier to Find Rare Pokemon</h2> <p>While it’s thrilling to explore new environments in search of new and rare Pokemon, there’s actually a way to discover the hard-to-find creatures you seek without having to go to far off places. Leveling up your trainer level increases the chance of a rare Pokemon appearing, so the higher your level, the more likely you’ll find both a Pokemon you’ve never encountered before or one with higher CP.</p> <h2 id="headid-turn-off-ar-mode-for-easier-capture">Turn off AR Mode for Easier Capture</h2> <p>While Pokemon Go’s Augmented Reality mode is an attractive feature, it actually makes catching Pokemon more difficult. Turning off AR centers the Pokemon on screen, making it easier to judge the distance you need to throw your Poke Ball to catch it. AR mode can be disabled via the toggle on the top right of the battle screen.</p> <h2 id="headid-evolving-pokemon">Evolving Pokemon</h2> <p>When a wild Pokemon is caught, it also adds two things to the inventory: Stardust and Candy. Candy is specific to the earliest evolution stage of a Pokemon species. For example, catching a Pidgey gives Pidgey Candy, and catching a Pidgeotto also gives Pidgey Candy. A Pokemon can also be “transferred” (i.e. sold forever) to Professor Willow for one candy.</p> <p>Pokemon require an amount of their specific Candy type to evolve. The amount of Candy needed varies, but basically if you want to evolve a Pokemon, catch more of it to build up that Candy collection! Candy can also be used to strengthen a Pokemon by raising its Combat Power. This stat dictates how a Pokemon performs in battle. Evolution also raises the CP levels and CP cap of a Pokemon.</p> <h2 id="headid-transfer-your-duplicates">Transfer Your Duplicates</h2> <p>While it might not seem like it at first, having duplicate Pokemon can actually help you. Extra Pokemon of the same type can be traded to Professor Willow for candy, which can be used to evolve your Pokemon. To do this, tap a Pokemon that you want to get rid of in the Pokemon screen and hit ‘Transfer’ option at the bottom of the screen. Be wary; make sure you don’t transfer the most powerful version (highest CP) of a given Pokemon type in your rush to get rid of your duplicates.</p> <h2 id="headid-how-to-choose-what-your-eevee-evolves-into">How to Choose What Your Eevee Evolves Into</h2> <p>When you evolve Eevee, you have a 1:3 chance of evolving it into either a Jolteon, Flareon, or a Vaporeon. However, it’s possible to use a trick that evolves it into the evolution of your choosing. Simply rename the Eevee one of the names below to receive the corresponding evolution.</p> <ul> <li>Sparky: Jolteon</li> <li>Pyro: Flareon</li> <li>Rainer: Vaporeon</li> </ul> <p>This trick is a reference to the Eevee brothers from episode 40 of the Pokemon animated series.</p> <h2 id="headid-how-to-get-pikachu-early">How to Get Pikachu Early</h2> <p>While Pikachu is an iconic Pokemon, it’s surprisingly difficult to find it in Pokemon Go. However, there’s a trick that allows you to obtain a Pikachu as your starter. To do so, start a new game and get to the point where Professor Willow allows you to catch Charmander, Squirtle, or Bulbasaur as your starting Pokemon. Instead of catching any of these, walk away (in real life) until your phone vibrates and the starters reappear. Repeat this process multiple times until a Pikachu spawns in place of the three starters.</p> <h2 id="headid-how-to-hatch-an-egg">How to Hatch an Egg</h2> <p>As you encounter PokeStops, you’ll often be rewarded with Pokemon Eggs. These eggs contain a random Pokemon with more Candy and Stardust than what you’d normally get if you caught it in the wild. Pokemon eggs can be found on the second tab of your Pokemon roster page; you’ll notice each egg lists a specific walking distance that must be travelled before it hatches. To start the process of hatching an egg, simply choose one, select ‘Start Incubation,’ and choose which Incubator you wish to use. Then all you need to do is to start walking the required distance while the app is open to hatch the egg. It’s worth noting that an Incubator can only house one egg at a time, so if you want to incubate more than one, you’ll need additional Incubators, which can be bought at the Shop or gained through leveling up.</p> <p>If you want to accelerate the process, riding a bike in real life is a great way to quickly rack up the required kilometers. Be wary; driving your car does not have the same effect, as any speed beyond 15 mph isn’t tracked by the app.</p> <h2 id="headid-how-gyms-work">How Gyms Work</h2> <p>When you reach level five, Professor Willow introduces you to gyms and the option to choose your affiliation–Instinct, Mystic, or Valor. There is one of three scenarios that may appear when you are close enough and can tap on a Pokemon Gym. Either the Gym is unclaimed (white), owned by a rival team, or owned by your own chosen team.</p> <p>If you interact with a gym representing your team, you can either train your Pokemon for XP by battling others or store them there for bonus Poke Coins and Stardust, which can be claimed once every 21 hours. However, when you encounter an opposing team’s gym, you can only do battle with its Pokemon. If you defeat its entire roster, you’ll take a large amount of Prestige Points away from that gym. High-level gyms with a large amount of Prestige Points may need to be defeated multiple times before they can be claimed.</p> <h2 id="headid-the-gym-is-unclaimed">The Gym Is Unclaimed</h2> <p>If the Gym is white, it means it is unclaimed. Go and get it! You can then choose which Pokemon you’d like to leave to guard it. You’ll want something strong, as other trainers will challenge it. If a trainer from a rival faction defeats it, they will be able to claim the Gym for themselves. Be wary that if you want to continue battling other Gyms, it’s a good idea not to leave your only strong Pokemon in a Gym.</p> <h2 id="headid-store-pokemon-at-your-teams-gyms">Store Pokemon at Your Team’s Gyms</h2> <p>Aside from battling, your team’s gyms gives you the option to store your Pokemon, which adds it to the roster that defends the location from opposing trainers. You can only store one Pokemon at a single gym, but you can defend up to ten gyms. This feature provides you with 10 Poke Coins and 500 Stardust per stored Pokemon every 21 hours. To redeem these rewards, simply tap the shield icon that appears at the top right of the Shop section.</p> <p>Keep in mind; the Pokemon you store at a gym will be tied to that location, and cannot be used or recalled until it’s beaten. If it is defeated while defending a gym, it will be returned to you damaged, and in need of healing.</p> <h2 id="headid-how-to-save-battery-power">How to Save Battery Power</h2> <p>As you’ve noticed from playing Pokemon Go, it quickly drains your battery. To prolong your battery life, we recommend buying a battery pack for your phone. However, if that’s not an option, you can also turn on the app’s Battery Save mode, which you can switch on in the settings screen. However, this mode just fades your screen to black when not in use, though it may be helpful if you’re just walking to hatch eggs.</p></content><author><name>Frank Lin</name></author><category term="Tips"/><category term="Pokemon Go"/><summary type="html">Pokemon Go has enthralled the hearts of fans, encouraging them to explore their surroundings to capture Pokemon. Here it comes to Hong Kong, and there are some of the most essential tips and tricks needed to understand and better play Pokemon Go.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/pokemon-go.jpg"/></entry><entry><title type="html">Spaces or Tabs, and My Choice</title><link href="https://flinhong.github.io/tech/spaces-or-tabs/" rel="alternate" type="text/html" title="Spaces or Tabs, and My Choice"/><published>2016-07-25T00:00:00+08:00</published><updated>2016-07-25T00:00:00+08:00</updated><id>https://flinhong.github.io/tech/spaces-or-tabs</id><content type="html" xml:base="https://flinhong.github.io/tech/spaces-or-tabs/"><p>Traditionally source code has been rendered with a monospace font. This allows for manual horizontal positioning with spaces or tab characters. Of course the tab character doesn’t have a defined width so flame wars have erupted around <a href="https://www.jwz.org/doc/tabs-vs-spaces.html">spaces vs tabs</a>, on par with the great editor wars of the last century. Ultimately these are pointless arguments. Tabs vs spaces is an artifact of trying to render code into a monospace grid of characters. It’s the 21st century! We can do better than our dad’s 1970s terminal. In fact, they did better in the <em>19th century</em>!<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> <p>Althogh they have talked a lot about this topic, I have no ideas why these two styles have to fight. After watching the following video, I was shocked that I’m not adapt to the action to continous hit the space button as in the video… And I vote for tabs in most cases!</p> <iframe width="853" height="480" src="https://www.youtube.com/embed/SsoOG6ZeyUI" frameborder="0" allowfullscreen=""></iframe> <p><br /></p> <p>People generally don’t mind reading code that is consistently indented using tabs, or code that is consistently indented using spaces. The problems arise when some lines are indented with tabs, and others with spaces. In such cases, the code will only display nicely if the reader has the same tab stop settings as the authors and if all the authors used consistent settings. One way to solve this problem is to force everyone to be “tab people” or “space people”.<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup></p> <p>The case for tabs:</p> <ul> <li>Gives reader control over visual effect.</li> <li>A tab has a meaning of “indent one level” (disputed; see below), whereas spaces have many meanings.</li> <li>Fewer keystrokes required to get things aligned.</li> <li>Files are smaller.</li> <li>It’s the only way to get vertically aligned columns when we use proportional fonts.</li> </ul> <p>The case for spaces:</p> <ul> <li>Consistent display across all display hardware, text viewer/editor software, and user configuration options.</li> <li>Gives author control over visual effect.</li> <li>Tabs are not as “visible” (that is, a tab generally looks just like a bunch of spaces)</li> </ul> <p>The case for random mixture of tabs and spaces:</p> <ul> <li>Easy (you don’t have to think about it).</li> <li>People who don’t like it can use some sort of tool to automatically indent the code however they want.</li> </ul> <p>For me, <code class="highlighter-rouge">tabs</code> or <code class="highlighter-rouge">spaces</code> are both OK when it could make the codes looks nice as the following pictures. But the last style with mixture is totally intolerable!</p> <p class="hascaption"><img src="/images/201607/tabs.png" alt="tabs" title="TABS" /></p> <p class="hascaption"><img src="/images/201607/spaces.png" alt="spaces" title="SPACES" /></p> <p>In the olden days, back when we used manual typewriters, there was such a thing as a tabstop. These were vertical brackets along the page (well, along that metal bar at the bottom of the current line). These tiny pieces of metal literally stopped the tabs, thus giving them the name <strong>tabstops</strong>. We were so creative with names in those days. When you hit the tab key the <strong>cursor</strong> (a rapidly spinning metal ball imprinted with the noun: “Selectric”) would jump from the left edge of the paper to the first tabstop. Hit tab again and it will go to the next tabstop. Now of course, these tab stops were adjustable, so you could choose the indenting style you wanted for your particular document.<sup id="fnref:1:1"><a href="#fn:1" class="footnote">1</a></sup> This works really nice on the old machines, why not continue to use it in our visual codes?</p> <p>Just as stated in the blog post <a href="http://lea.verou.me/2012/01/why-tabs-are-clearly-superior/">Why tabs are clearly superior</a>, <code class="highlighter-rouge">tabs</code> have many advantages:</p> <ul> <li>Tabs take up less space</li> <li>Tabs can be personalized</li> <li>Tabs are better for collaboration</li> <li>You don’t depend on certain tools</li> <li>Tabs are easy to select</li> <li>Code indented with tabs is easier to copy &amp; paste</li> </ul> <p>So, I prefer <code class="highlighter-rouge">tabs</code> in cases it could work. And you can find other people’s choice in different programming languages at <a href="https://ukupat.github.io/tabs-or-spaces/">this page</a>.</p> <p>Refs:</p> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href="http://joshondesign.com/2014/09/02/bar">Tabs vs Spaces, the Pointless War</a> <a href="#fnref:1" class="reversefootnote">&#8617;</a> <a href="#fnref:1:1" class="reversefootnote">&#8617;<sup>2</sup></a></p> </li> <li id="fn:2"> <p><a href="http://c2.com/cgi/wiki?TabsVersusSpaces">Tabs Versus Spaces</a> <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> </ol> </div></content><author><name>Frank Lin</name></author><category term="Tech"/><category term="spaces"/><category term="tabs"/><summary type="html">Traditionally source code has been rendered with a monospace font. This allows for manual horizontal positioning with spaces or tab characters. Of course the tab character doesn’t have a defined width so flame wars have erupted around spaces vs tabs, on par with the great editor wars of the last century. Ultimately these are pointless arguments. Tabs vs spaces is an artifact of trying to render code into a monospace grid of characters. It’s the 21st century! We can do better than our dad’s 1970s terminal. In fact, they did better in the 19th century!1 Tabs vs Spaces, the Pointless War &#8617;</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/tabs-spaces.jpg"/></entry><entry><title type="html">去书展,一次走马观花</title><link href="https://flinhong.github.io/journal/book-fair/" rel="alternate" type="text/html" title="去书展,一次走马观花"/><published>2016-07-24T00:00:00+08:00</published><updated>2016-07-24T00:00:00+08:00</updated><id>https://flinhong.github.io/journal/book-fair</id><content type="html" xml:base="https://flinhong.github.io/journal/book-fair/"><p>今年书展的主题:“阅读江湖,亦狂亦侠亦温文”,我喜欢的主题,按捺不住就约着师兄师弟一起去了,也是第一次去书展混混。</p> <p>中午出门前,室友提醒说排队的人会非常多,当时心下没在意,毕竟平时看大家的读书情绪并不是那么高嘛。没想到一下地铁,就已经开始汇聚了很多行人去往会展中心,于是乎,就排了近一个小时才入了内场。</p> <p>一走进去就是三联的展区,因为学校附近商场里有个三联书店,时有路过,这时候遇到还是感觉挺亲切的。慢慢挤到人群中,左翻翻右翻翻。由于没有明确目标,只能随心逛逛了,真是走马观花的感觉。</p> <p>最开始的热情褪去,就剩下迷茫了,在各个出版社的展区进进出出,还能记得好些书不止一个出版社出版,频繁的出现,太迎合大众阅读,反倒是觉得那些小众的书更能勾起我的兴趣。几个小时的游逛,只入手了一本摄影书籍,勉强的一点收获。</p> <p>之后去往三楼的主题展区,相忘于江湖。</p> <p>从高中接触武侠小说开始,断断续续看了很多,一直钟情于武侠。不过老实交待,查老的书可是一本都没有看过,都是看电视剧的。其他几位出现在展览厅的几位大师的作品也是在电影或电视剧中看到的,于心有愧呀。</p> <p>很多当年出版的连载一一排出来的时候,甚是动人,恍若能看到多年前的出版风潮和阅读热情。现在武侠已经脱离那些书香气息,在网络盛行的年代,都留在了一个个字符串之间。</p> <p>这也算是去了一次书展了,走马观花,但解毒了。</p></content><author><name>Frank Lin</name></author><category term="Journal"/><category term="Hong Kong"/><category term="Book Fair"/><summary type="html">今年书展的主题:“阅读江湖,亦狂亦侠亦温文”,我喜欢的主题,按捺不住就约着师兄师弟一起去了,也是第一次去书展混混。</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://c1.staticflickr.com/9/8691/28457131301_22c9e1436b_b.jpg"/></entry><entry><title type="html">Bing 必应词典桌面版</title><link href="https://flinhong.github.io/tools/bing-dict-desktop/" rel="alternate" type="text/html" title="Bing 必应词典桌面版"/><published>2016-07-13T00:00:00+08:00</published><updated>2016-07-13T00:00:00+08:00</updated><id>https://flinhong.github.io/tools/bing-dict-desktop</id><content type="html" xml:base="https://flinhong.github.io/tools/bing-dict-desktop/"><p>最近终于把用了多年的老笔记本换下了,用上了 Full HD 的屏幕,但是很多词典应用都没有更新,不能适应屏幕 DPI 变化,当然使用体验很差啦。还好必应词典没有这个问题。</p> <p>但是 MSN 中文网停止运作后,Bing 词典桌面版的下载页面也不复存在。还好词典官方微博提供临时下载地址,我仅转载一下,方便大家使用。</p> <p><a href="http://dictionary.blob.core.chinacloudapi.cn/app/pc/BingDict_Setup.exe">Bing 词典桌面版(3.5.1 版)下载地址戳这里!</a>。</p> <p>Or,我的备份地址:<a href="https://mega.nz/#!QUh1QbID!3M4WmipOnK_wkcznss5jD_14VPvxHMJjnQsUM4Kep6M">mega.nz…</a>。也不知道这样做合适不😜。</p> <p>要接收更新,还是建议大家关注必应词典官方微博(<a href="http://www.weibo.com/msnbingdict">@必应词典</a>)。</p></content><author><name>Frank Lin</name></author><category term="Tools"/><category term="Bing"/><summary type="html">最近终于把用了多年的老笔记本换下了,用上了 Full HD 的屏幕,但是很多词典应用都没有更新,不能适应屏幕 DPI 变化,当然使用体验很差啦。还好必应词典没有这个问题。</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/bing.png"/></entry><entry><title type="html">Related posts in Jekyll</title><link href="https://flinhong.github.io/front-end/related-posts-in-jekyll/" rel="alternate" type="text/html" title="Related posts in Jekyll"/><published>2016-07-13T00:00:00+08:00</published><updated>2016-07-13T00:00:00+08:00</updated><id>https://flinhong.github.io/front-end/related-posts-in-jekyll</id><content type="html" xml:base="https://flinhong.github.io/front-end/related-posts-in-jekyll/"><p>Jekyll features a simple “Related posts” variable per post page with <code class="highlighter-rouge">site.related_posts</code>, which contains the 10 most recent posts in default. However, it only works perfectly when <code class="highlighter-rouge">lsi</code> (latent semantic indexing) option was enabled.<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> <p>As explained in Jekyll documents, with <code class="highlighter-rouge">lsi</code> features will slow down the build process and also not supported by GitHub Pages.</p> <p>So I find a <a href="https://anmonteiro.com/2015/08/jekyll-related-posts-revamped/">post</a> that solved this issue by using only Liquid tags to generate the related posts with <code class="highlighter-rouge">tags</code> or <code class="highlighter-rouge">categories</code>. It works by going through the related posts collection and selecting the posts that contain any tags (or categories) in common with the current post, up to a setted limit. If there are enough posts to fill that limit, fine, it stops there. Otherwise, it goes again through the most recent, possibly unrelated posts, and outputs them until the limit is finally reached.</p> <p>The full snippet is presented below.<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup></p> <div class="language-html highlighter-rouge"><pre class="highlight"><code>{% assign RELATED_POSTS_THRESHOLD = 3 %} <span class="nt">&lt;ul&gt;</span> {% assign related_post_count = 0 %} {% for post in site.related_posts %} {% if related_post_count == RELATED_POSTS_THRESHOLD %} {% break %} {% endif %} {% for tag in post.tags %} {% if page.tags contains tag %} <span class="nt">&lt;li&gt;</span> <span class="nt">&lt;h3&gt;</span> <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"{{ site.url }}{{ post.url }}"</span><span class="nt">&gt;</span> {{ post.title }} <span class="nt">&lt;small&gt;</span>{{ post.date | date_to_string }}<span class="nt">&lt;/small&gt;</span> <span class="nt">&lt;/a&gt;</span> <span class="nt">&lt;/h3&gt;</span> <span class="nt">&lt;/li&gt;</span> {% assign related_post_count = related_post_count | plus: 1 %} {% break %} {% endif %} {% endfor %} {% endfor %} {% assign posts_left = RELATED_POSTS_THRESHOLD | minus: related_post_count %} {% unless posts_left == 0 %} {% for post in site.related_posts %} {% if posts_left == 0 %} {% break %} {% endif %} {% assign already_related = false %} {% for tag in post.tags %} {% if page.tags contains tag %} {% assign already_related = true %} {% break %} {% endif %} {% endfor %} {% unless already_related %} {% assign posts_left = posts_left | minus: 1 %} <span class="nt">&lt;li&gt;</span> <span class="nt">&lt;h3&gt;</span> <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">"{{ site.url }}{{ post.url }}"</span><span class="nt">&gt;</span> {{ post.title }} <span class="nt">&lt;small&gt;</span>{{ post.date | date_to_string }}<span class="nt">&lt;/small&gt;</span> <span class="nt">&lt;/a&gt;</span> <span class="nt">&lt;/h3&gt;</span> <span class="nt">&lt;/li&gt;</span> {% endunless %} {% endfor %} {% endunless %} <span class="nt">&lt;/ul&gt;</span> </code></pre> </div> <p>You can also change the <code class="highlighter-rouge">site.related_posts</code> to <code class="highlighter-rouge">site.posts</code> if you want more posts beyond the ten recent posts.</p> <p>Refs:</p> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href="https://jekyllrb.com/docs/variables/">Variables - Jekll</a> <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p><a href="https://anmonteiro.com/2015/08/jekyll-related-posts-revamped/">Jekyll related posts revamped</a> <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> </ol> </div></content><author><name>Frank Lin</name></author><category term="Front-end"/><category term="Jekyll"/><summary type="html">Jekyll features a simple “Related posts” variable per post page with site.related_posts, which contains the 10 most recent posts in default. However, it only works perfectly when lsi (latent semantic indexing) option was enabled.1 Variables - Jekll &#8617;</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/related.jpg"/></entry><entry><title type="html">Better quality of Google Analytics data for Jekyll blogs</title><link href="https://flinhong.github.io/front-end/better-google-analytics-for-jekyll/" rel="alternate" type="text/html" title="Better quality of Google Analytics data for Jekyll blogs"/><published>2016-07-13T00:00:00+08:00</published><updated>2016-07-13T00:00:00+08:00</updated><id>https://flinhong.github.io/front-end/better-google-analytics-for-jekyll</id><content type="html" xml:base="https://flinhong.github.io/front-end/better-google-analytics-for-jekyll/"><p>The default Google Analytics extract the page information from <code class="highlighter-rouge">windows.location</code> and <code class="highlighter-rouge">document.title</code> by using its default code snippets. This works fine if the page is loaded directly from its origin file without any modification. However, for some cases, the visitor to the blog post may load the page from Google’s cache or use a service such as Google Translate that no longer contains the orginal URL or the title might be modified. As a result, this particular type of page view is still recorded.</p> <p>The solution for this problem is to pass the page information when sending the page view event. Of course, for this to work these values must appear as constants in the JS scripts returned by the site. With Liquid used by Jekyll, this is easy to achieve because the location and title are available from the YAML such as <code class="highlighter-rouge">page.url</code> or <code class="highlighter-rouge">page.title</code>.</p> <div class="language-js highlighter-rouge"><pre class="highlight"><code><span class="nx">ga</span><span class="p">(</span><span class="s1">'send'</span><span class="p">,</span> <span class="s1">'pageview'</span><span class="p">,</span> <span class="p">{</span> <span class="s1">'page'</span><span class="p">:</span> <span class="s1">'{{ page.url }}'</span><span class="p">,</span> <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'{{ page.title | replace: "'</span><span class="s2">", "</span><span class="err">\\</span><span class="s1">'" }}'</span> <span class="p">});</span> </code></pre> </div> <p>In the generated page these options will indeed appear as constants and they will be sent unmodified to Analytics, even if the page is loaded from Google’s cache or modified by Google Translate:</p> <div class="language-js highlighter-rouge"><pre class="highlight"><code><span class="nx">ga</span><span class="p">(</span><span class="s1">'send'</span><span class="p">,</span> <span class="s1">'pageview'</span><span class="p">,</span> <span class="p">{</span> <span class="s1">'page'</span><span class="p">:</span> <span class="s1">'/front-end/better-google-analytics-for-jekyll/'</span><span class="p">,</span> <span class="s1">'title'</span><span class="p">:</span> <span class="s1">'Better quality of Google Analytics data for Jekyll blogs'</span> <span class="p">});</span> </code></pre> </div></content><author><name>Frank Lin</name></author><category term="Front-end"/><category term="Jekyll"/><category term="Google Analytics"/><summary type="html">The default Google Analytics extract the page information from windows.location and document.title by using its default code snippets. This works fine if the page is loaded directly from its origin file without any modification. However, for some cases, the visitor to the blog post may load the page from Google’s cache or use a service such as Google Translate that no longer contains the orginal URL or the title might be modified. As a result, this particular type of page view is still recorded.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/ga.png"/></entry><entry><title type="html">American Society for Microbiology (ASM) no longer supports impact factors for its journals</title><link href="https://flinhong.github.io/tech/no-longer-if-support-for-asm/" rel="alternate" type="text/html" title="American Society for Microbiology (ASM) no longer supports impact factors for its journals"/><published>2016-07-12T00:00:00+08:00</published><updated>2016-07-12T00:00:00+08:00</updated><id>https://flinhong.github.io/tech/no-longer-if-support-for-asm</id><content type="html" xml:base="https://flinhong.github.io/tech/no-longer-if-support-for-asm/"><p>As the announcement posted on <strong>July 11, 2016</strong>, American Society for Microbiology (ASM) no longer supports impact factors for its journals:<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> <p>Washington, DC – July 11, 2016 - The editors-in-chief of ASM journals and ASM leadership have decided to no longer advertise the impact factors of ASM journals on the journals’ websites. This decision was made in order to avoid contributing to a distorted value system that inappropriately emphasizes high IFs. High-IF journals limit the number of accepted articles to create a perception of exclusivity, and individuals receive disproportionate rewards for articles in high IF journals, while science as a whole suffers from a distorted values system and delayed communication of research.</p> <p>It is the hope of ASM journal editors-in-chief and ASM leadership to move away from this system and the undue focus on journal IF, which detracts from the advancement of scientific research, by removing IFs from ASM journal websites. In doing so, ASM hopes to make a statement of principle that other journals will follow.</p> <p>Calculated by various companies and promoted by publishers, journal impact factors (JIFs) are a measure of the average number of citations that a journal’s articles receive over the past two years. They were designed to indicate the quality of journals, but researchers often use the metric to assess the quality of individual papers — and even, in some cases, their authors.<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup></p> <div class="clear-float do-the-split"></div> <p>“To me, what’s essential is to purge the conversation of the impact factor,” says ASM chief executive Stefano Bertuzzi, a prominent critic of the metric. “We want to make it so tacky that people will be embarrassed just to mention it.”</p> <p>Refs:</p> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href="https://www.asm.org/index.php/asm-newsroom2/press-releases/94299-asm-media-advisory-asm-no-longer-supports-impact-factors-for-its-journals">ASM Media Advisory: ASM No Longer Supports Impact Factors for its Journals</a> <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p><a href="http://www.nature.com/news/beat-it-impact-factor-publishing-elite-turns-against-controversial-metric-1.20224">Beat it, impact factor! Publishing elite turns against controversial metric</a> <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> </ol> </div></content><author><name>Frank Lin</name></author><category term="Tech"/><category term="Impact Factors"/><summary type="html">As the announcement posted on July 11, 2016, American Society for Microbiology (ASM) no longer supports impact factors for its journals:1 ASM Media Advisory: ASM No Longer Supports Impact Factors for its Journals &#8617;</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/impact_factor.jpg"/></entry><entry><title type="html">为了忘却的纪念 - 七堇年</title><link href="https://flinhong.github.io/reading/to-forget-to-memory/" rel="alternate" type="text/html" title="为了忘却的纪念 - 七堇年"/><published>2016-07-07T00:00:00+08:00</published><updated>2016-07-07T00:00:00+08:00</updated><id>https://flinhong.github.io/reading/to-forget-to-memory</id><content type="html" xml:base="https://flinhong.github.io/reading/to-forget-to-memory/"><p>不过是为了心头的虚妄去找来阅读的一篇文章,为了忘却一段时光,也为了纪念这个成长,还有些许是慰藉自找的伤。一开始阅读鲁迅先生的《为了忘却的纪念》,于己不合,才另寻了七堇年的这篇,同为《为了忘却的纪念》,才觉得自己也有那样的旧时光,也有那样的热泪盈眶。</p> <div class="breaking-line"></div> <p><strong>七堇年 - 为了忘却的纪念</strong></p> <p>回首那些错把倾诉冲动当作创作才华的无知年生,在兵荒马乱的晚自习上,在熄灯的宿舍里,我们总是在一堆堆耀武扬威的习题和试卷的缝隙间,在应急灯渐渐微弱下去的光线中,一手撑着深不可测的夜,一手写下无处倾诉的话。</p> <p>那是一种盲目的、消耗的状态,照管自己的生活,打理那些千头万绪的杂念,喝自己冲的咖啡,睡自己铺好的被窝,吃自己餐盘里的饭菜,写自己的作业,考自己的试,做自己的梦……世界的悲伤与灾难都太多,我们活在平静遥远的角落,无力怜悯。人间既非天堂又非地狱,末日尚远,我们惟能维护着自己的天地,“埋头做着功课做着世间的荣辱”……就算是洪荒滔天,也总有他人去担当……文字成为内心的形而上的依靠。</p> <p>那些执念,那样的旧时光,一晃就过去了。</p> <p>而今仿佛是站在一个青黄不接的尴尬路口,失去的是招摇撞骗的痛快诉说,未曾获得的,是笔走天涯的洗练淡定。已经再也不能随心所欲地写字,因为心里有了羞赧和踌躇,对纷繁复杂的眼之所见有了惧怕。不知道我应该怎样写,写这无法书写的自我,怎样诉说,诉说这无法诉说的世界。</p> <p>回过头去看看那些浸透在白纸黑字上的生动的悲喜,切肤地感觉到,在那样一个唯唯诺诺的苟且年纪,伤情似乎是装点生命的勋章,好像只有凭借那些,幻觉般的,被我们脆弱的主观承受力无限夸大的非难,我们才得以拥有热泪盈眶的青春。</p> <p>尽管,生命中的温暖一直都与我们遥遥在望,而我们只不过是拒绝路过。</p> <p>“之行,如果有天我们湮没在人潮中,庸碌一生,那是因为我们没有努力活得丰盛。”二十岁的时候,读到这样的句子。写这话的人又说,“世界之大,我却不知其折或远。”</p> <p>在我脚踏的这片狭小天地,经历的,不过是寻常的青春,看到的,不过是平凡的世界。在过去心高气傲的年头上,因不懂得该如何聪明地活着,所以总觉得连生命都是身外之物,“好像这个世界说不要就不要了。”</p> <p>前些日子在英文泛读课上看了一篇美国作家写的散文,他说:“杰斐逊总统在独立宣言里告诉我们,‘每个人都有追求幸福的权利’,但很多人把这句话误读成‘每个人都有幸福的权利’。”</p> <p>读到这里,我为这样一个美国式的小聪明笑了起来。这篇散文不过讲述了一个古老的真理,即幸福本身就是虚妄,它只存在于追求幸福的过程中。在所谓的终点你是看不到幸福的,因为它不存在。</p> <p>我因此想起了曾经不知天高地厚的年岁,因为一些小事踌躇满志,连走路的步伐都快了起来,仿佛急于直面人生;但是当鞋里掺进了一颗硌脚的石子儿,便又会呼天抢地,倒戈弃甲,觉得世不容我。但是终于——在其后的其后——我渐渐承认,活着的价值,在于要有一个饱满的人生。隐忍平凡的外壳下,要像果实般有着汁甜水蜜的肉瓤,以及一颗坚硬闪亮的内核。这样的种子,才能在人间深处生根发芽,把一段富有情致的人生传奇流传下去。</p> <p>因知道若干年之后的人世,再也不会有人惦记我们的存在,因此这段饱满的生命,是我们以生之为人而骄傲的唯一见证。</p> <p>这些年的时间,为着实现这样饱满的人生,断断续续地做着一些代价高昂的遥远的梦,断断续续地写些不叫文字的文字,断断续续地被生活的遗憾所打岔,跌入低谷,并且拒绝任何搭救,自己慢慢摸索着爬起来继续走。这青春,与世间任何一段青春无异——年月里那些朝生暮死的悲喜,也就这样野花般自生自灭地燃烧在茫茫命途上,装点了路人的梦。</p> <p>故人对我说,“要有最朴素的生活,与最遥远的梦想”……说这话的少年,早都成了记忆深处的那些花儿,走上了更远、更美的路。只是这样的话,我一直都唯唯诺诺地记得。我也是这样感激涕零地知晓,我何其所幸——“如果不是因了你们,我何以能这样平安成长,渐渐变成一个健全的人呢。”</p> <p>记录这旅途的大部分文字,从高一到高三毕业,用了整个成长的时间来完成它。</p> <p>印象深刻的,永远是书写它们的时候——某个十六岁的晴朗的秋天下午,某个心绪不平的高三的晚自习,某个毕业之后的夏天的深夜——而经过了这一切,我常常不解的是,为何我们而今常常惭愧当年的种种矫情,但却又暗地里明白,当初身临其境的时候,我们的体会的确是真实而切肤的。于是这只能归结为这样一个冷静的解释,那是因为我们长大了。那是因为,好多年前如锥子一般刻在我们心底的,所谓时光断裂的声音,成为了永远的回声。</p> <p>年华里,我们失却的是一种心情。</p> <p>未曾想到,在这样的一个过程中,我们的出生年代,成为了一个字正腔圆的集体烙印,被用作追捧和诟病的代名词,无论我们有着多么迥然不同的生存姿态。但是我仍然相信这些千姿百态的理想和悲哀,功名和败落的后面,有着本质上相同的,对世界和生命的勇敢诘问。这正是我们为何要紧紧抓住语言的权利去表达内心的最初的动机。无论是写作者还是阅读者,这都是光荣的事情。至少,我们有很多的孩子,愿意去思考和表达,即使无论这思考和表达的方式与内容怎样。我始终相信这是另一种意义上的殊途同归。</p> <p>所以。</p> <p>因了成长本身的不完美,我希望这些如原石一般尚经不起雕琢的文字,能够以一种最接近成长的本质的真实形式——即充满了热泪、过错、遗憾、美好、希望和绝望的姿态——纪念我业已逝去的那段珍贵岁月。那些我们等待着下课、等待着放学、等待着长大的少年时代。那曾是,也将是属于我们大多数孩子的一段最清澈最美好的时光,如同所有,所有——所有踏过了中学岁月,踏过了高考,踏过了命运的沼泽,在险些陷下去的时刻,被意志和希望重新拉回到一条更值得坚持下去的路上的孩子们——所亲身经历过的那样。</p> <p>看,在这个充满爱与被爱、伤害与被伤害的世界里,生命对我们是吝啬的,因为它总是让我们失望;可是,生命又是这么慷慨,总会在失望之后给予我们拯救。</p> <p>我想,因了这生命的慷慨,我们必须尊严地过下去。就如同生命本身,尊重我们的存在。</p> <p>之所以将本文集的名字命名为“被窝是青春的坟墓”,是因为这个名字对于我而言的重大意义。我非常怀念它。</p> <p>这是一句暗号。我们那些彼时笑容灿烂,而今四散天涯的孩子们,永远都会记得它。借这样一个温暖的名字,我只愿如此诚恳地,表达我对所有正在成长中的孩子们的祝福,就像我一直被祝福的那样……</p> <p>要有最朴素的生活,与最遥远的梦想。</p> <p>即使明日天寒地冻,路远马亡。</p> <div class="breaking-line"></div> <p>最后,放首歌在这里,我不知道合适不,但我愿意听。</p> <iframe width="560" height="315" src="https://www.youtube.com/embed/wTTmIE8Evqo" frameborder="0" allowfullscreen=""></iframe></content><author><name>Frank Lin</name></author><category term="Reading"/><category term="reading"/><summary type="html">不过是为了心头的虚妄去找来阅读的一篇文章,为了忘却一段时光,也为了纪念这个成长,还有些许是慰藉自找的伤。一开始阅读鲁迅先生的《为了忘却的纪念》,于己不合,才另寻了七堇年的这篇,同为《为了忘却的纪念》,才觉得自己也有那样的旧时光,也有那样的热泪盈眶。</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/post-mem.jpg"/></entry><entry><title type="html">Fix Time Difference Between Dual Boot Windows and Ubuntu</title><link href="https://flinhong.github.io/tips/fix-time-difference-bwtween-windows-and-ubuntu/" rel="alternate" type="text/html" title="Fix Time Difference Between Dual Boot Windows and Ubuntu"/><published>2016-07-06T00:00:00+08:00</published><updated>2016-07-06T00:00:00+08:00</updated><id>https://flinhong.github.io/tips/fix-time-difference-bwtween-windows-and-ubuntu</id><content type="html" xml:base="https://flinhong.github.io/tips/fix-time-difference-bwtween-windows-and-ubuntu/"><p>This post is a quick tip for users who dual boot Ubuntu and Windows: if the time is off on your computer when you reboot and switch between Ubuntu and Windows, here’s how to fix it.<sup id="fnref:1"><a href="#fn:1" class="footnote">1</a></sup></p> <p>If you dual boot and there are time conflicts between Windows and Ubuntu, this occurs because Ubuntu store the time on the hardware clock as UTC by default while Microsoft Windows stores the time as local time, thus causing conflicting times between Ubuntu and Windows. The fix is pretty easy and it can be applied from both Ubuntu and Windows.</p> <h2 id="headid-fix-time-differences-between-ubuntu-and-windows">Fix time differences between Ubuntu and Windows</h2> <p>A. <strong>To fix the UTC / local time difference between Ubuntu and Windows from Ubuntu</strong> by making Ubuntu use local time.</p> <p>Before proceeding, note that according to the Ubuntu wiki, “the advantage of having the hardware clock as UTC is that you don’t need to change the hardware clock when moving between timezones or when Daylight Savings Time (DST) begins or ends as UTC does not have DST or timezone offsets”. So this is not recommended and if you can, you should use method B., and fix this from Windows.</p> <p><strong>For Ubuntu 16.04 and newer</strong>, run the following command:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>timedatectl <span class="nb">set</span>-local-rtc 1 </code></pre> </div> <p>You can then check if Ubuntu uses local time, you can then use the following command:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>timedatectl </code></pre> </div> <p>Which should display the following “RTC in local TZ: yes”. A warning will also be displayed. Here’s the full command output:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>timedatectl Local <span class="nb">time</span>: Lu 2016-07-06 12:18:22 EEST Universal <span class="nb">time</span>: Lu 2016-07-06 09:18:22 UTC RTC <span class="nb">time</span>: Lu 2016-07-06 12:18:22 Time zone: Europe/Bucharest <span class="o">(</span>EEST, +0300<span class="o">)</span> Network <span class="nb">time </span>on: yes NTP synchronized: no RTC <span class="k">in </span><span class="nb">local </span>TZ: yes Warning: The system is configured to <span class="nb">read </span>the RTC <span class="nb">time </span><span class="k">in </span>the <span class="nb">local time </span>zone. This mode can not be fully supported. It will create various problems with <span class="nb">time </span>zone changes and daylight saving <span class="nb">time </span>adjustments. The RTC <span class="nb">time </span>is never updated, it relies on external facilities to maintain it. If at all possible, use RTC <span class="k">in </span>UTC by calling <span class="s1">'timedatectl set-local-rtc 0'</span>. </code></pre> </div> <p><strong>For Ubuntu versions older than 16.04</strong>: you must edit the /etc/default/rcS file and replace “UTC=yes” with “UTC=no” (both without the quotes). To do this automatically, simply copy/paste the following command in a terminal:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>sudo sed -i <span class="s1">'s/UTC=yes/UTC=no/'</span> /etc/default/rcS </code></pre> </div> <p>And then reboot.</p> <p>B. <strong>To fix this from Windows</strong> (it should work with Vista SP2, Windows 7, Server 2008 R2 and Windows 8/8.1), by making it use UTC instead of local time, download <a href="/assets/WindowsTimeFixUTC.reg">THIS</a> Windows registry file and simply double click it.<sup id="fnref:2"><a href="#fn:2" class="footnote">2</a></sup></p> <p>Then, to disable the Windows Time service (which still writes local time to RTC regardless of the registry setting above, on shutdown), run Command Prompt as Administrator and paste this command:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>sc config w32time <span class="nv">start</span><span class="o">=</span> disabled </code></pre> </div> <p>And reboot.</p> <h2 id="headid-how-to-revert-the-changes">How to revert the changes</h2> <p>A. <strong>From Ubuntu</strong>: reverting this change from Ubuntu is pretty easy.</p> <p><strong>Ubuntu 16.04 and newer</strong>: to revert the changes, simply run the following command:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>timedatectl <span class="nb">set</span>-local-rtc 0 </code></pre> </div> <p><strong>Ubuntu versions older than 16.04</strong>: all you have to do is replace “UTC=no” with “UTC=yes” in the /etc/default/rcS file. To do this automatically, copy/paste the command below in a terminal:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>sudo sed -i <span class="s1">'s/UTC=no/UTC=yes/'</span> /etc/default/rcS </code></pre> </div> <p>And then reboot your computer.</p> <p>B. <strong>From Windows</strong>: reverting this change is a bit more complicated from Windows.</p> <p>Firstly, open the <code class="highlighter-rouge">.reg</code> file downloaded when applying the fix for Windows (see download link above) with a text editor and change the “RealTimeIsUniversal” value from “dword:00000001” to “-“ (without the quotes). Here’s how the file should look like after making this change:</p> <div class="highlighter-rouge"><pre class="highlight"><code>Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation] "RealTimeIsUniversal"=- </code></pre> </div> <p>Then save the file and double click it.</p> <p>Next, run the following command in Command Prompt (which you need to run as Administrator) to re-enable the Windows Time service:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>sc config w32time <span class="nv">start</span><span class="o">=</span> demand </code></pre> </div> <p>And finally, reboot.</p> <p>Refs:</p> <div class="footnotes"> <ol> <li id="fn:1"> <p><a href="http://www.webupd8.org/2014/09/dual-boot-fix-time-differences-between.html">DUAL BOOT: FIX TIME DIFFERENCES BETWEEN UBUNTU AND WINDOWS</a> <a href="#fnref:1" class="reversefootnote">&#8617;</a></p> </li> <li id="fn:2"> <p><a href="https://help.ubuntu.com/community/UbuntuTime#Multiple_Boot_Systems_Time_Conflicts">Multiple Boot Systems Time Conflicts</a> <a href="#fnref:2" class="reversefootnote">&#8617;</a></p> </li> </ol> </div></content><author><name>Frank Lin</name></author><category term="Tips"/><category term="Windows"/><category term="Ubuntu"/><summary type="html">This post is a quick tip for users who dual boot Ubuntu and Windows: if the time is off on your computer when you reboot and switch between Ubuntu and Windows, here’s how to fix it.1 DUAL BOOT: FIX TIME DIFFERENCES BETWEEN UBUNTU AND WINDOWS &#8617;</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/dual-boot.jpg"/></entry><entry><title type="html">Free SSL with Let’s Encrypt on GitLab Pages</title><link href="https://flinhong.github.io/front-end/lets-encrypt-with-gitlab-pages/" rel="alternate" type="text/html" title="Free SSL with Let's Encrypt on GitLab Pages"/><published>2016-07-06T00:00:00+08:00</published><updated>2016-07-06T00:00:00+08:00</updated><id>https://flinhong.github.io/front-end/lets-encrypt-with-gitlab-pages</id><content type="html" xml:base="https://flinhong.github.io/front-end/lets-encrypt-with-gitlab-pages/"><p>In this post we will talk about HTTPS and how to add it to your GitLab Pages site with <a href="https://letsencrypt.org/">Let’s Encrypt</a>. And you can find the original post at <a href="https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/">Tutorial: Securing your GitLab Pages with TLS and Let’s Encrypt</a>. Here, I add my own exprience when following its guidance.</p> <h2 id="headid-why-tlsssl">Why TLS/SSL?</h2> <p>When discussing HTTPS, it’s common to hear people saying that a static website doesn’t need HTTPS, since it doesn’t receive any POST requests, or isn’t handling credit card transactions or any other secure request. But that’s not the whole story.</p> <p>TLS (<a href="https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.0">formerly SSL</a>) is a security protocol that can be added to HTTP to increase the security of your website by:</p> <ol> <li>properly authenticating yourself: the client can trust that you are really you. The TLS handshake that is made at the beginning of the connection ensures the client that no one is trying to impersonate you;</li> <li>data integrity: this ensures that no one has tampered with the data in a request/response cycle;</li> <li>encryption: this is the main selling point of TLS, but the other two are just as important. This protects the privacy of the communication between client and server.</li> </ol> <p>The TLS layer can be added to other protocols too, such as FTP (making it <a href="https://en.wikipedia.org/wiki/FTPS">FTPS</a>) or WebSockets (making <code class="highlighter-rouge">ws://</code> <code class="highlighter-rouge">wss://</code>).</p> <h2 id="headid-https-everywhere">HTTPS Everywhere</h2> <p>Nowadays, there is a strong push for using TLS on every website. The ultimate goal is to make the web safer, by adding those three components cited above to every website.</p> <p>The first big player was the <a href="https://www.eff.org/https-everywhere">HTTPS Everywhere</a> browser extension. Google has also been using HTTPS compliance to better rank websites since <a href="https://webmasters.googleblog.com/2014/08/https-as-ranking-signal.html">2014</a>.</p> <h2 id="headid-tls-certificates">TLS certificates</h2> <p>In order to add TLS to HTTP, one would need to get a certificate, and until 2015, one would need to either pay for it or figure out how to do it with one of the available <a href="https://en.wikipedia.org/wiki/Certificate_authority">Certificate Authorities</a>.</p> <p>Enter <a href="https://letsencrypt.org/">Let’s Encrypt</a>, a free, automated, and open Certificate Authority. <a href="https://letsencrypt.org/2015/12/03/entering-public-beta.html">Since December 2015</a> anyone can get a free certificate from this new Certificate Authority from the comfort of their terminal.</p> <h2 id="headid-implementation">Implementation</h2> <p>So, let’s suppose we’re going to create a static blog with <a href="https://jekyllrb.com/">Jekyll 3</a>. If you are not creating a blog or are not using Jekyll just follow along, it should be straightforward enough to translate the steps for different purposes.</p> <p>A simple example blog can be created with:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>jekyll new cool-blog New jekyll site installed <span class="k">in</span> ~/cool-blog. <span class="gp">$ </span><span class="nb">cd </span>cool-blog/ </code></pre> </div> <p>Now you have to create a GitLab project. Here we are going to create a “user page”, which means that it is a project created within a user account (not a group account), and that the name of the project looks like <code class="highlighter-rouge">YOURUSERNAME.gitlab.io</code>. Refer to the <a href="http://doc.gitlab.com/ee/pages/README.html#getting-started-with-gitlab-pages">“Getting started” section of the GitLab Pages manual</a> for more information on that.</p> <p>If you are using GitHub Pages, its not possible for you to get a SSL for your custom domain at present. So, GitLab might be a good choice if you need the <code class="highlighter-rouge">https</code>.</p> <p>From now on, remember to replace <code class="highlighter-rouge">YOURDOMAIN.com</code> with your custom domain and <code class="highlighter-rouge">YOURUSERNAME</code> with, well, your username. ;)</p> <p>Create a project named <code class="highlighter-rouge">YOURUSERNAME.gitlab.io</code> so that GitLab will identify the project correctly. After that, upload your code to GitLab:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>git remote add origin [email protected]:YOURUSERNAME/YOURUSERNAME.gitlab.io.git <span class="gp">$ </span>git push -u origin master </code></pre> </div> <p>OK, so far we have a project uploaded to GitLab, but we haven’t configured GitLab Pages yet. To configure it, just create a <code class="highlighter-rouge">.gitlab-ci.yml</code> file in the root directory of your repository with the following contents:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>pages: stage: deploy image: ruby:2.3 script: - gem install jekyll - jekyll build -d public/ artifacts: paths: - public only: - master </code></pre> </div> <p>This file instructs GitLab <code class="highlighter-rouge">Runner</code> to deploy by installing Jekyll and building your website under the <code class="highlighter-rouge">public/</code> folder (<code class="highlighter-rouge">jekyll build -d public/</code>). You can find a <a href="https://flinhong.github.io/tips/automated-blog-with-gitlab-ci/">previous post</a> about the GitLab CI for your Jekyll site.</p> <p>While you Wait for the build process to complete, you can track the progress in the Builds page of your project. Once it starts, it probably won’t take longer than a few minutes. Once the build is finished, your website will be available at <code class="highlighter-rouge">https://YOURUSERNAME.gitlab.io</code>. Note that GitLab already provides TLS certificates to all subdomains of <code class="highlighter-rouge">gitlab.io</code> (but it has some limitations, so please <a href="http://doc.gitlab.com/ee/pages/README.html#limitations">refer to the documentation for more</a>). So if you don’t want to add a custom domain, you’re done.</p> <h2 id="headid-configuring-the-tls-certificate-of-your-custom-domain">Configuring the TLS certificate of your custom domain</h2> <p>Once you buy a domain name and point that domain to your GitLab Pages website, you need to configure 2 things:</p> <ol> <li>add the domain to GitLab Pages configuration (<a href="http://doc.gitlab.com/ee/pages/README.html#add-a-custom-domain-to-your-pages-website">see documentation</a>);</li> <li>add your custom certificate to your website.</li> </ol> <p>Once you add your domain, your website will be available under both <code class="highlighter-rouge">http://YOURDOMAIN.com</code> and <code class="highlighter-rouge">https://YOURUSERNAME.gitlab.io</code>.</p> <p>But if you try to access your custom domain with HTTPS (<code class="highlighter-rouge">https://YOURDOMAIN.com</code> in this case), your browser will show that horrible page, saying that things are going wrong and someone is trying to steal your information. <em>Why is that?</em></p> <p>Since GitLab offers TLS certificates to all <code class="highlighter-rouge">gitlab.io</code> pages and your custom domain is just a CNAME over that same domain, GitLab serves the <code class="highlighter-rouge">gitlab.io</code> certificate, and your browser receives mixed messages: on one side, the browser is trying to access <code class="highlighter-rouge">YOURDOMAIN.com</code>, but on the other side it is getting a TLS certificate for <code class="highlighter-rouge">*.gitlab.io</code>, signaling that something is wrong. That’s the same case for GitHub Pages, you can visit your site with https for <code class="highlighter-rouge">*.github.io</code> but not for your custom domain.</p> <p>In order to fix it, you need to obtain a certificate for <code class="highlighter-rouge">YOURDOMAIN.com</code> and add it to GitLab Pages. For that we are going to use Let’s Encrypt.</p> <p>Let’s Encrypt is a new certificate authority that offers both <em>free</em> and <em>automated</em> certificates. That’s perfect for us: we don’t have to pay for having HTTPS and you can do everything within the comfort of your terminal.</p> <p>We begin with downloading the <code class="highlighter-rouge">letsencrypt-auto</code> utility. Open a new terminal window and type:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>git clone https://github.com/letsencrypt/letsencrypt <span class="gp">$ </span><span class="nb">cd </span>letsencrypt </code></pre> </div> <p><code class="highlighter-rouge">letsencrypt-auto</code> offers a lot of functionality. For example, if you have a web server running Apache, you could add <code class="highlighter-rouge">letsencrypt-auto --apache</code> inside your webserver and have everything done for you. <code class="highlighter-rouge">letsencrypt</code> targets primarily Unix-like webservers, so the <code class="highlighter-rouge">letsencrypt-auto</code> tool won’t work for Windows users. Check <a href="https://cultiv.nl/blog/lets-encrypt-on-windows/">this tutorial</a> to see how to get Let’s Encrypt certificates while running Windows. But I am not recommend you to use Windows for this process, I have tried but failed…</p> <p>Since we are running on GitLab’s servers instead, we have to do a bit of manual work:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>./letsencrypt-auto certonly -a manual -d YOURDOMAIN.com <span class="c">#</span> <span class="c"># If you want to support another domain, www.YOURDOMAIN.com, for example, you</span> <span class="c"># can add it to the domain list after -d like:</span> <span class="c"># ./letsencrypt-auto certonly -a manual -d YOURDOMAIN.com -d www.YOURDOMAIN.com</span> <span class="c">#</span> </code></pre> </div> <p>I only configured the www subdomain for my site. After you accept that your IP will be publicly logged, a message like the following will appear:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>Make sure your web server displays the following content at http://YOURDOMAIN.org/.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM before continuing: 5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.ewlbSYgvIxVOqiP1lD2zeDKWBGEZMRfO_4kJyLRP_4U <span class="c">#</span> <span class="c"># output omitted</span> <span class="c">#</span> Press ENTER to <span class="k">continue</span> </code></pre> </div> <p>Now it is waiting for the server to be correctly configured so it can go on. <strong>Leave this terminal window open</strong> for now.</p> <p>So, the goal is to the make our already-published static website return said token when said URL is requested. That’s easy: create a custom page! Just create a file in your blog folder that looks like this:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>--- layout: null permalink: /.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.html --- 5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.ewlbSYgvIxVOqiP1lD2zeDKWBGEZMRfO_4kJyLRP_4U </code></pre> </div> <p>This tells Jekyll to create a static page, which you can see at <code class="highlighter-rouge">cool-blog/_site/.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.html</code>, with no extra HTML, just the token in plain text. As we are using the <code class="highlighter-rouge">permalink</code> attribute in the front matter, you can name this file anyway you want and put it anywhere, too. Note that the behaviour of the <code class="highlighter-rouge">permalink</code> attribute has <a href="https://jekyllrb.com/docs/upgrading/2-to-3/#permalinks-no-longer-automatically-add-a-trailing-slash">changed</a> from Jekyll 2 to Jekyll 3, so make sure you have Jekyll 3.x installed. If you’re not using version 3 of Jekyll or if you’re using a different tool, just create the same file in the exact path, like <code class="highlighter-rouge">cool-blog/.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.html</code> or an equivalent path in your static site generator of choice. Here we’ll call it <code class="highlighter-rouge">letsencrypt-setup.html</code> and place it in the root folder of the blog. In order to check that everything is working as expected, start a local server with jekyll serve in a separate terminal window and try to access the URL:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>curl http://localhost:4000/.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM <span class="c"># response:</span> 5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.ewlbSYgvIxVOqiP1lD2zeDKWBGEZMRfO_4kJyLRP_4U </code></pre> </div> <p>Note that I just replaced the <code class="highlighter-rouge">http://YOURDOMAIN.com</code> (from the <code class="highlighter-rouge">letsencrypt-auto</code> instructions) with <code class="highlighter-rouge">http://localhost:4000</code>. Everything is working fine, so we just need to upload the new file to GitLab:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>git add letsencrypt-setup.html <span class="gp">$ </span>git commit -m <span class="s2">"add letsencypt-setup.html file"</span> <span class="gp">$ </span>git push </code></pre> </div> <p>Just because the <code class="highlighter-rouge">permalink</code> attribute, I cannot get the correct response from the url above, and get a <code class="highlighter-rouge">404</code> response. The right response can only curl from the url with <code class="highlighter-rouge">.html</code> ending.</p> <p>For fixing this problem, you can add the following lines in your <code class="highlighter-rouge">.gitlab-ci.yml</code> file after Jekyll build the site in the <code class="highlighter-rouge">public/</code> folder:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>- <span class="nb">cd </span>public - mkdir -p /.well-known/acme-challenge - <span class="nb">cd</span> .well-known - <span class="nb">cd </span>acme-challenge - <span class="nb">printf</span> <span class="s2">"%s"</span> 5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM.ewlbSYgvIxVOqiP1lD2zeDKWBGEZMRfO_4kJyLRP_4U &gt; 5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM </code></pre> </div> <p>Once the build finishes, test again if everything is working well:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="c"># Note that we're using the actual domain, not localhost anymore</span> <span class="gp">$ </span>curl http://YOURDOMAIN.com/.well-known/acme-challenge/5TBu788fW0tQ5EOwZMdu1Gv3e9C33gxjV58hVtWTbDM </code></pre> </div> <p>Now that everything is working as expected, go back to the terminal window that’s waiting for you and hit <code class="highlighter-rouge">ENTER</code>. This instructs the Let’s Encrypt’s servers to go to the URL we just created. If they get the response they were waiting for, we’ve proven that we actually own the domain and now they’ll send you the TLS certificates. After a while it responds:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/YOURDOMAIN.org/fullchain.pem. Your cert will expire on 2016-07-04. To obtain a new version of the certificate <span class="k">in </span>the future, simply run Let<span class="s1">'s Encrypt again. - If you like Let'</span>s Encrypt, please consider supporting our work by: Donating to ISRG / Let<span class="s1">'s Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le </span></code></pre> </div> <p>Success! We have correctly acquired a free TLS certificate for our domain!</p> <p>Note, however, that like any other TLS certificate, it has an expiration date, and in the case of certificates issued by Let’s Encrypt, the certificate will remain valid for <strong>90 days</strong>. When you finish setting up, just put in your calendar to remember to renew the certificate in time, otherwise it will become invalid, and the browser will reject it.</p> <p>Now we just need to upload the certificate and the key to GitLab. Go to <strong>Settings -&gt; Pages</strong> inside your project, remove the old CNAME and add a new one with the same domain, but now you’ll also upload the TLS certificate. Paste the contents of <code class="highlighter-rouge">/etc/letsencrypt/live/YOURDOMAIN.com/fullchain.pem</code> (you’ll need sudo to read the file) to the “Certificate (PEM)” field and <code class="highlighter-rouge">/etc/letsencrypt/live/YOURDOMAIN.com/privkey.pem</code> (also needs sudo) to the “Key (PEM)” field.</p> <p>You can access your <code class="highlighter-rouge">.pem</code> files by using <code class="highlighter-rouge">sudo su</code> in Ubuntu with <code class="highlighter-rouge">Gedit</code>.</p> <p><img src="/images/201607/gitlab-pages-cert-upload-screenshot.png" alt="GitLab configure" /></p> <p>And you’re done! You now have a fully working HTTPS website:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="gp">$ </span>curl -vX HEAD https://YOURDOMAIN.com/ <span class="c">#</span> <span class="c"># starting connection</span> <span class="c">#</span> <span class="k">*</span> TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA <span class="k">*</span> Server certificate: YOURDOMAIN.com <span class="k">*</span> Server certificate: Lets Encrypt Authority X3 <span class="k">*</span> Server certificate: DST Root CA X3 </code></pre> </div> <h2 id="headid-redirecting">Redirecting</h2> <p>Everything is working fine, but now we have an extra concern: we have two working versions of our website, both HTTP and HTTPS. We need a way to redirect all of our traffic to the HTTPS version, and tell search engines to do the same.</p> <h3 id="headid-search-engines">Search Engines</h3> <p>Instructing the search engines is really easy: just tell them that the HTTPS version is the “canonical” version, and they send all the users to it. And how do you do that? By adding a <code class="highlighter-rouge">link</code> tag to the header of the HTML:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"canonical"</span> <span class="na">href=</span><span class="s">"https://YOURDOMAIN.com/specific/page"</span> <span class="nt">/&gt;</span> </code></pre> </div> <p>Adding this to every header on a blog tells the search engine that the correct version is the HTTPS one, and they’ll comply.</p> <h3 id="headid-internal-links">Internal links</h3> <p>Remember to use HTTPS for your CSS or JavaScript file URLs, because when the browser accesses a secure website that relies on an insecure resource, it may block that resource.</p> <p>It is <a href="http://www.paulirish.com/2010/the-protocol-relative-url/">considered a good practice</a> to use the protocol-agnostic path:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"stylesheet"</span> <span class="na">href=</span><span class="s">"//YOURDOMAIN.com/styles.css"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"//YOURDOMAIN.com/script.js"</span><span class="nt">&gt;&lt;/script&gt;</span> </code></pre> </div> <h3 id="headid-javascript-based-redirect">JavaScript-based redirect</h3> <p>There is, however, a case where the user specifically types in the URL without using HTTPS, and they’ll access the HTTP version of your website.</p> <p>The correct way of handling that would be to respond with a 301 “Moved permanently” HTTP code, and the browser would remember it for the next request. However, that’s not a possibility we have here, since we’re running on GitLab’s servers.</p> <p>A small hack you can do is to redirect your users with a bit of JavaScript code:</p> <div class="language-js highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">host</span> <span class="o">=</span> <span class="s2">"YOURDOMAIN.com"</span><span class="p">;</span> <span class="k">if</span> <span class="p">((</span><span class="nx">host</span> <span class="o">==</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">host</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">protocol</span> <span class="o">!=</span> <span class="s1">'https:'</span><span class="p">))</span> <span class="p">{</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">toString</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/^http:/</span><span class="p">,</span> <span class="s2">"https:"</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p>This redirects the user to the HTTPS version, but there are a few problems with it:</p> <ol> <li>a user could have JavaScript disabled, and would not be affected by that;</li> <li>an attacker could simply remove that code and behave as a <a href="https://en.wikipedia.org/wiki/Man-in-the-middle_attack">Man in the Middle</a>;</li> <li>the browser won’t remember the redirect instruction, so every time the user types that same URL, the website will have to redirect him/her again.</li> </ol> <h2 id="headid-wrap-up">Wrap up</h2> <p><img src="/images/201607/https.png" alt="https" /></p> <p>That’s how easy it is to have a free HTTPS-enabled website. With these tools, I see no reason not to do it.</p> <p>If you want to check the status of your HTTPS enabled website, <a href="https://www.ssllabs.com/ssltest/">SSL Labs offers a free online service</a> that “performs a deep analysis of the configuration of any SSL web server on the public Internet”.</p> <p>I hope it helps you :)</p></content><author><name>Frank Lin</name></author><category term="Front-end"/><category term="Jekyll"/><category term="GitLab"/><summary type="html">In this post we will talk about HTTPS and how to add it to your GitLab Pages site with Let’s Encrypt. And you can find the original post at Tutorial: Securing your GitLab Pages with TLS and Let’s Encrypt. Here, I add my own exprience when following its guidance.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://flinhong.github.io/images/201607/lets-encrypt.png"/></entry></feed>