This commit is contained in:
Zhongwei Sun 2015-02-06 05:08:29 +00:00
commit 26e436fb9f
14 changed files with 487 additions and 55 deletions

View file

@ -0,0 +1,65 @@
Sitemap
-------
The sitemap plugin generates plain-text or XML sitemaps. You can use the
``SITEMAP`` variable in your settings file to configure the behavior of the
plugin.
The ``SITEMAP`` variable must be a Python dictionary and can contain three keys:
- ``format``, which sets the output format of the plugin (``xml`` or ``txt``)
- ``priorities``, which is a dictionary with three keys:
- ``articles``, the priority for the URLs of the articles and their
translations
- ``pages``, the priority for the URLs of the static pages
- ``indexes``, the priority for the URLs of the index pages, such as tags,
author pages, categories indexes, archives, etc...
All the values of this dictionary must be decimal numbers between ``0`` and ``1``.
- ``changefreqs``, which is a dictionary with three items:
- ``articles``, the update frequency of the articles
- ``pages``, the update frequency of the pages
- ``indexes``, the update frequency of the index pages
Valid frequency values are ``always``, ``hourly``, ``daily``, ``weekly``, ``monthly``,
``yearly`` and ``never``.
If a key is missing or a value is incorrect, it will be replaced with the
default value.
The sitemap is saved in ``<output_path>/sitemap.<format>``.
.. note::
``priorities`` and ``changefreqs`` are information for search engines.
They are only used in the XML sitemaps.
For more information: <http://www.sitemaps.org/protocol.html#xmlTagDefinitions>
**Example**
Here is an example configuration (it's also the default settings):
.. code-block:: python
PLUGINS=['pelican.plugins.sitemap',]
SITEMAP = {
'format': 'xml',
'priorities': {
'articles': 0.5,
'indexes': 0.5,
'pages': 0.5
},
'changefreqs': {
'articles': 'monthly',
'indexes': 'daily',
'pages': 'monthly'
}
}

View file

@ -0,0 +1 @@
from .sitemap import *

View file

@ -0,0 +1,243 @@
# -*- coding: utf-8 -*-
'''
Sitemap
-------
The sitemap plugin generates plain-text or XML sitemaps.
'''
from __future__ import unicode_literals
import collections
import os.path
from datetime import datetime
from logging import warning, info
from codecs import open
from pytz import timezone
from pelican import signals, contents
from pelican.utils import get_date
TXT_HEADER = """{0}/index.html
{0}/archives.html
{0}/tags.html
{0}/categories.html
"""
XML_HEADER = """<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
"""
XML_URL = """
<url>
<loc>{0}/{1}</loc>
<lastmod>{2}</lastmod>
<changefreq>{3}</changefreq>
<priority>{4}</priority>
</url>
"""
XML_FOOTER = """
</urlset>
"""
def format_date(date):
if date.tzinfo:
tz = date.strftime('%z')
tz = tz[:-2] + ':' + tz[-2:]
else:
tz = "-00:00"
return date.strftime("%Y-%m-%dT%H:%M:%S") + tz
class SitemapGenerator(object):
def __init__(self, context, settings, path, theme, output_path, *null):
self.output_path = output_path
self.context = context
self.now = datetime.now()
self.siteurl = settings.get('SITEURL')
self.default_timezone = settings.get('TIMEZONE', 'UTC')
self.timezone = getattr(self, 'timezone', self.default_timezone)
self.timezone = timezone(self.timezone)
self.format = 'xml'
self.changefreqs = {
'articles': 'monthly',
'indexes': 'daily',
'pages': 'monthly'
}
self.priorities = {
'articles': 0.5,
'indexes': 0.5,
'pages': 0.5
}
config = settings.get('SITEMAP', {})
if not isinstance(config, dict):
warning("sitemap plugin: the SITEMAP setting must be a dict")
else:
fmt = config.get('format')
pris = config.get('priorities')
chfreqs = config.get('changefreqs')
if fmt not in ('xml', 'txt'):
warning("sitemap plugin: SITEMAP['format'] must be `txt' or `xml'")
warning("sitemap plugin: Setting SITEMAP['format'] on `xml'")
elif fmt == 'txt':
self.format = fmt
return
valid_keys = ('articles', 'indexes', 'pages')
valid_chfreqs = ('always', 'hourly', 'daily', 'weekly', 'monthly',
'yearly', 'never')
if isinstance(pris, dict):
# We use items for Py3k compat. .iteritems() otherwise
for k, v in pris.items():
if k in valid_keys and not isinstance(v, (int, float)):
default = self.priorities[k]
warning("sitemap plugin: priorities must be numbers")
warning("sitemap plugin: setting SITEMAP['priorities']"
"['{0}'] on {1}".format(k, default))
pris[k] = default
self.priorities.update(pris)
elif pris is not None:
warning("sitemap plugin: SITEMAP['priorities'] must be a dict")
warning("sitemap plugin: using the default values")
if isinstance(chfreqs, dict):
# .items() for py3k compat.
for k, v in chfreqs.items():
if k in valid_keys and v not in valid_chfreqs:
default = self.changefreqs[k]
warning("sitemap plugin: invalid changefreq `{0}'".format(v))
warning("sitemap plugin: setting SITEMAP['changefreqs']"
"['{0}'] on '{1}'".format(k, default))
chfreqs[k] = default
self.changefreqs.update(chfreqs)
elif chfreqs is not None:
warning("sitemap plugin: SITEMAP['changefreqs'] must be a dict")
warning("sitemap plugin: using the default values")
def write_url(self, page, fd):
if getattr(page, 'status', 'published') != 'published':
return
# We can disable categories/authors/etc by using False instead of ''
if not page.save_as:
return
page_path = os.path.join(self.output_path, page.save_as)
if not os.path.exists(page_path):
return
lastdate = getattr(page, 'date', self.now)
try:
lastdate = self.get_date_modified(page, lastdate)
except ValueError:
warning("sitemap plugin: " + page.save_as + " has invalid modification date,")
warning("sitemap plugin: using date value as lastmod.")
lastmod = format_date(lastdate)
if isinstance(page, contents.Article):
pri = self.priorities['articles']
chfreq = self.changefreqs['articles']
elif isinstance(page, contents.Page):
pri = self.priorities['pages']
chfreq = self.changefreqs['pages']
else:
pri = self.priorities['indexes']
chfreq = self.changefreqs['indexes']
pageurl = '' if page.url == 'index.html' else page.url
if self.format == 'xml':
fd.write(XML_URL.format(self.siteurl, pageurl, lastmod, chfreq, pri))
else:
fd.write(self.siteurl + '/' + pageurl + '\n')
def get_date_modified(self, page, default):
if hasattr(page, 'modified'):
if isinstance(page.modified, datetime):
return page.modified
return get_date(page.modified)
else:
return default
def set_url_wrappers_modification_date(self, wrappers):
for (wrapper, articles) in wrappers:
lastmod = datetime.min.replace(tzinfo=self.timezone)
for article in articles:
lastmod = max(lastmod, article.date.replace(tzinfo=self.timezone))
try:
modified = self.get_date_modified(article, datetime.min).replace(tzinfo=self.timezone)
lastmod = max(lastmod, modified)
except ValueError:
# Supressed: user will be notified.
pass
setattr(wrapper, 'modified', str(lastmod))
def generate_output(self, writer):
path = os.path.join(self.output_path, 'sitemap.{0}'.format(self.format))
pages = self.context['pages'] + self.context['articles'] \
+ [ c for (c, a) in self.context['categories']] \
+ [ t for (t, a) in self.context['tags']] \
+ [ a for (a, b) in self.context['authors']]
self.set_url_wrappers_modification_date(self.context['categories'])
self.set_url_wrappers_modification_date(self.context['tags'])
self.set_url_wrappers_modification_date(self.context['authors'])
for article in self.context['articles']:
pages += article.translations
info('writing {0}'.format(path))
with open(path, 'w', encoding='utf-8') as fd:
if self.format == 'xml':
fd.write(XML_HEADER)
else:
fd.write(TXT_HEADER.format(self.siteurl))
FakePage = collections.namedtuple('FakePage',
['status',
'date',
'url',
'save_as'])
for standard_page_url in ['index.html',
'archives.html',
'tags.html',
'categories.html']:
fake = FakePage(status='published',
date=self.now,
url=standard_page_url,
save_as=standard_page_url)
self.write_url(fake, fd)
for page in pages:
self.write_url(page, fd)
if self.format == 'xml':
fd.write(XML_FOOTER)
def get_generators(generators):
return SitemapGenerator
def register():
signals.get_generators.connect(get_generators)

22
pelican/plugins/test.py Normal file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ----------------------------------------
# Just say "Hello world!".
# ----------------------------------------
import sitemap
# ----------------------------------------
# test cases
# ----------------------------------------
def run_doctest():
'''python -B <__file__> -v
'''
import doctest
doctest.testmod()
if '__main__' == __name__:
print "Hello world!"

View file

@ -76,12 +76,12 @@ em, i {font-style: italic;}
/* Lists */
ul {
list-style: outside disc;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
ol {
list-style: outside decimal;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
li { margin-top: 0.5em;

View file

@ -76,12 +76,12 @@ em, i {font-style: italic;}
/* Lists */
ul {
list-style: outside disc;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
ol {
list-style: outside decimal;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
li { margin-top: 0.5em;

View file

@ -76,12 +76,12 @@ em, i {font-style: italic;}
/* Lists */
ul {
list-style: outside disc;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
ol {
list-style: outside decimal;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
li { margin-top: 0.5em;

View file

@ -10,9 +10,10 @@
/* Imports */
@import url("reset.css");
/*
@import url("pygment.css");
*/
@import url("typogrify.css");
@import url(//fonts.googleapis.com/css?family=Yanone+Kaffeesatz&subset=latin);
/***** Global *****/
/* Body */
@ -20,7 +21,7 @@ body {
background: #F5F4EF;
color: #000305;
font-size: 87.5%; /* Base font size: 14px */
font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
font-family: 'Trebuchet MS', Trebuchet, 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
line-height: 1.429;
margin: 0;
padding: 0;
@ -39,7 +40,7 @@ h1, h2, h3, h4, h5, h6 {
font-weight: 400;
line-height: 1.1;
margin-bottom: .8em;
font-family: 'Yanone Kaffeesatz', arial, serif;
font-family: 'Yanone Kaffeesatz', arial, KaiTi, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
}
h3, h4, h5, h6 { margin-top: .8em; }
@ -71,17 +72,20 @@ p { margin-top: 1em;
margin-bottom: 1em;}
strong, b {font-weight: bold;}
em, i {font-style: italic;}
em, i {
font-style: italic;
background-color: yellow;
}
/* Lists */
ul {
list-style: outside disc;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
ol {
list-style: outside decimal;
margin: 0em 0 0 1.5em;
margin: 0em 0 1em 1.5em;
}
li { margin-top: 0.5em;
@ -98,19 +102,52 @@ li { margin-top: 0.5em;
margin-bottom: 1px;
}
.readmore { float: right }
.readmore {
}
dl {margin: 0 0 1.5em 0;}
dt {font-weight: bold;}
dd {margin-left: 1.5em;}
pre{background-color: rgb(238, 238, 238); padding: 10px; margin: 10px; overflow: auto;}
pre {
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
display: block;
padding: 0.5em;
background: #001f3f;
color: hsla(210, 100%, 75%, 1.0);
margin-bottom: 1em;
font-family: monospace, Arial, "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif;
overflow-x: auto;
}
.highlight {
overflow: auto;
}
/* Quotes */
blockquote {
margin: 20px;
font-style: italic;
background: #f9f9f9;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
overflow-x: auto;
overflow-y: hidden;
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote p {
display: inline;
}
cite {}
q {}
@ -122,23 +159,42 @@ div.note {
max-width: 300px;
}
/* Tables */
table {margin: .5em auto 1.5em auto; width: 98%;}
/* Thead */
thead th {padding: .5em .4em; text-align: left;}
thead td {}
/* Tbody */
tbody td {padding: .5em .4em;}
tbody th {}
tbody .alt td {}
tbody .alt th {}
/* Tfoot */
tfoot th {}
tfoot td {}
table {
background: white;
margin-bottom: 1.25rem;
border: solid 2px #ecf0f1;
}
table thead,
table tfoot {
background: whitesmoke;
}
table thead tr th,
table thead tr td,
table tfoot tr th,
table tfoot tr td {
padding: 0.5rem 0.625rem 0.625rem;
font-size: 0.875rem;
font-weight: bold;
color: #2c3e50;
text-align: left;
}
table tr th,
table tr td {
padding: 0.5625rem 0.625rem;
font-size: 0.875rem;
color: #34495e;
}
table tr.even, table tr.alt, table tr:nth-of-type(even) {
background: #f9f9f9;
}
table thead tr th,
table tfoot tr th,
table tbody tr td,
table tr td,
table tfoot tr td {
display: table-cell;
line-height: 1.125rem;
}
/* HTML5 tags */
header, section, footer,
@ -287,25 +343,39 @@ img.left, figure.left {float: left; margin: 0 2em 2em 0;}
#extras a:hover, #extras a:active {color: #fff;}
/* Blogroll */
#extras .blogroll {
float: left;
width: 615px;
/* Tags */
#extras .tags {
width: 800px;
}
#extras .blogroll li {float: left; margin: 0 20px 0 0; width: 185px;}
#extras .tags li {float: left; margin: 0 20px 0 0; width: 90px;}
/* Blogroll */
#extras .blogroll {
width: 550px;
margin-top: 2em;
float: left;
}
#extras .blogroll li {float: left; margin: 0 20px 0 0; width: 90px;}
/* Social */
#extras .social {
float: right;
width: 175px;
width: 150px;
margin-top: 2em;
float: left;
}
#extras div[class='social'] a {
background-repeat: no-repeat;
background-position: 3px 6px;
padding-left: 25px;
}
#extras .social li {
margin-top: 0;
}
/* Icons */
.social a[href*='about.me'] {background-image: url('../images/icons/aboutme.png');}
@ -373,6 +443,9 @@ img.left, figure.left {float: left; margin: 0 2em 2em 0;}
border-bottom: 1px solid #eee;
padding: 1.5em 0;
}
article img {
box-shadow: 10px 10px 5px #888888;
}
li:last-child .hentry, #content > .hentry {border: 0; margin: 0;}
#content > .hentry {padding: 1em 0;}
.hentry img{display : none ;}

View file

@ -6,7 +6,9 @@ color:#408090;
font-style:italic;
}
.err {
/*
border:1px solid #FF0000;
*/
}
.k {
color:#007020;

View file

@ -44,3 +44,14 @@
})();
</script>
{% endif %}
{% if BAIDU_ANALYTICS %}
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?{{ BAIDU_ANALYTICS }}";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
{% endif %}

View file

@ -1,17 +1,17 @@
<footer class="post-info">
<abbr class="published" title="{{ article.date.isoformat() }}">
Published: {{ article.locale_date }}
发布于: {{ article.locale_date }}
</abbr>
{% if article.modified %}
<br />
<abbr class="modified" title="{{ article.modified.isoformat() }}">
Updated: {{ article.locale_modified }}
更新于: {{ article.locale_modified }}
</abbr>
{% endif %}
{% if article.authors %}
<address class="vcard author">
By {% for author in article.authors %}
作者: {% for author in article.authors %}
<a class="url fn" href="{{ SITEURL }}/{{ author.url }}">{{ author }}</a>
{% endfor %}
</address>

View file

@ -39,9 +39,17 @@
{% block content %}
{% endblock %}
<section id="extras" class="body">
<div class="tags">
<h2>Tags</h2>
<ul class="tagcloud">
{% for tag in tag_cloud %}
<li class=""><a href="{{ SITEURL }}/{{ tag.0.url }}">{{ tag.0 }}</a></li>
{% endfor %}
</ul>
</div>
{% if LINKS %}
<div class="blogroll">
<h2>blogroll</h2>
<h2>Blogroll</h2>
<ul>
{% for name, link in LINKS %}
<li><a href="{{ link }}">{{ name }}</a></li>
@ -51,18 +59,18 @@
{% endif %}
{% if SOCIAL or FEED_ALL_ATOM or FEED_ALL_RSS %}
<div class="social">
<h2>social</h2>
<h2>Social</h2>
<ul>
{% if FEED_ALL_ATOM %}
<li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate">atom feed</a></li>
{% endif %}
{% if FEED_ALL_RSS %}
<li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate">rss feed</a></li>
{% endif %}
{% if FEED_ALL_ATOM %}
<li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_ATOM }}" type="application/atom+xml" rel="alternate">atom feed</a></li>
{% endif %}
{% if FEED_ALL_RSS %}
<li><a href="{{ FEED_DOMAIN }}/{{ FEED_ALL_RSS }}" type="application/rss+xml" rel="alternate">rss feed</a></li>
{% endif %}
{% for name, link in SOCIAL %}
<li><a href="{{ link }}">{{ name }}</a></li>
{% endfor %}
{% for name, link in SOCIAL %}
<li><a href="{{ link }}">{{ name }}</a></li>
{% endfor %}
</ul>
</div><!-- /.social -->
{% endif %}
@ -73,10 +81,12 @@
Proudly powered by <a href="http://getpelican.com/">Pelican</a>, which takes great advantage of <a href="http://python.org">Python</a>.
</address><!-- /#about -->
<script type="text/javascript">document.write(unescape('%3Cdiv id="bdcs"%3E%3C/div%3E%3Cscript charset="utf-8" src="http://znsv.baidu.com/customer_search/api/js?sid=3822573945392759965') + '&plate_url=' + (encodeURIComponent(window.location.href)) + '&t=' + (Math.ceil(new Date()/3600000)) + unescape('"%3E%3C/script%3E'));</script>
<p>The theme is by <a href="http://coding.smashingmagazine.com/2009/08/04/designing-a-html-5-layout-from-scratch/">Smashing Magazine</a>, thanks!</p>
{% include 'china_icp_license.html' %}
</footer><!-- /#contentinfo -->
{% include 'analytics.html' %}
{% include 'analytics.html' %}
{% include 'disqus_script.html' %}
</body>
</html>

View file

@ -0,0 +1,5 @@
{% if CHINA_ICP_LICENSE %}
<p>
<a href="http://www.miitbeian.gov.cn/">{{ CHINA_ICP_LICENSE }}</a>
</p>
{% endif %}

View file

@ -36,7 +36,7 @@
<div class="entry-content">
{% include 'article_infos.html' %}
{{ article.summary }}
<a class="readmore" href="{{ SITEURL }}/{{ article.url }}">read more</a>
<a class="readmore" href="{{ SITEURL }}/{{ article.url }}">阅读全文</a>
{% include 'comments.html' %}
</div><!-- /.entry-content -->
</article></li>