188 lines
8.9 KiB
Python
188 lines
8.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
|
||
# Copyright © 2014–2015, Chris Warrick.
|
||
# Copyright © 2018, Felix Fontein.
|
||
|
||
# Permission is hereby granted, free of charge, to any
|
||
# person obtaining a copy of this software and associated
|
||
# documentation files (the "Software"), to deal in the
|
||
# Software without restriction, including without limitation
|
||
# the rights to use, copy, modify, merge, publish,
|
||
# distribute, sublicense, and/or sell copies of the
|
||
# Software, and to permit persons to whom the Software is
|
||
# furnished to do so, subject to the following conditions:
|
||
#
|
||
# The above copyright notice and this permission notice
|
||
# shall be included in all copies or substantial portions of
|
||
# the Software.
|
||
#
|
||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
||
from __future__ import unicode_literals
|
||
import io
|
||
import os
|
||
import sys
|
||
from nikola.plugin_categories import Command
|
||
from nikola import utils
|
||
|
||
|
||
class UpgradeMetadata(Command):
|
||
"""Convert special tags (draft, private, mathjax) to status and has_math metadata. Also removes sections."""
|
||
|
||
name = 'upgrade_metadata_v8'
|
||
doc_purpose = 'Convert special tags (draft, private, mathjax) to metadata'
|
||
cmd_options = [
|
||
{
|
||
'name': 'yes',
|
||
'short': 'y',
|
||
'long': 'yes',
|
||
'type': bool,
|
||
'default': False,
|
||
'help': 'Proceed without confirmation',
|
||
},
|
||
]
|
||
|
||
def _execute(self, options, args):
|
||
L = utils.get_logger('upgrade_metadata_v8', utils.STDERR_HANDLER)
|
||
|
||
if not self.site.config['USE_TAG_METADATA']:
|
||
L.error('This plugin can only be used if USE_TAG_METADATA is set to True.')
|
||
sys.exit(-1)
|
||
self.site.config['WARN_ABOUT_TAG_METADATA'] = False
|
||
|
||
# scan posts
|
||
self.site.scan_posts()
|
||
flagged = []
|
||
for post in self.site.timeline:
|
||
flag = False
|
||
if post.has_oldstyle_metadata_tags:
|
||
flag = True
|
||
for lang in self.site.config['TRANSLATIONS'].keys():
|
||
if 'section' in post.meta[lang]:
|
||
flag = True
|
||
if flag:
|
||
flagged.append(post)
|
||
if flagged:
|
||
if len(flagged) == 1:
|
||
L.info('1 post (and/or its translations) contains old-style metadata or has section metadata:')
|
||
else:
|
||
L.info('{0} posts (and/or their translations) contain old-style metadata or have section metadata:'.format(len(flagged)))
|
||
for post in flagged:
|
||
L.info(' ' + (post.metadata_path if post.is_two_file else post.source_path))
|
||
L.warn('Please make a backup before running this plugin. It might eat your data.')
|
||
if not options['yes']:
|
||
yesno = utils.ask_yesno("Proceed with metadata upgrade?")
|
||
if options['yes'] or yesno:
|
||
number_converted = 0
|
||
number_converted_partial = 0
|
||
for post in flagged:
|
||
converted = False
|
||
fully_converted = True
|
||
for lang in self.site.config['TRANSLATIONS'].keys():
|
||
# Get file names and extractor
|
||
extractor = post.used_extractor[lang]
|
||
is_two_file = post.is_two_file
|
||
if lang == post.default_lang:
|
||
fname = post.metadata_path if is_two_file else post.source_path
|
||
else:
|
||
meta_path = os.path.splitext(post.source_path)[0] + '.meta' if is_two_file else post.source_path
|
||
fname = utils.get_translation_candidate(post.config, meta_path, lang)
|
||
|
||
# We don't handle compilers which extract metadata for now
|
||
if post.compiler is extractor:
|
||
L.warn('Cannot convert {0} (language {1}), as metadata was extracted by compiler.'.format(fname, lang))
|
||
fully_converted = False
|
||
continue
|
||
|
||
# Read metadata and text from post file
|
||
if not os.path.exists(fname):
|
||
L.debug("File {0} does not exist, skipping.".format(fname))
|
||
continue
|
||
|
||
with io.open(fname, "r", encoding="utf-8-sig") as meta_file:
|
||
source_text = meta_file.read()
|
||
if not is_two_file:
|
||
_, content_str = extractor.split_metadata_from_text(source_text)
|
||
meta = extractor.extract_text(source_text)
|
||
|
||
# Consider metadata mappings
|
||
sources = {}
|
||
for m in ('tags', 'status', 'has_math', 'section', 'category'):
|
||
sources[m] = m
|
||
for foreign, ours in self.site.config.get('METADATA_MAPPING', {}).get(extractor.map_from, {}).items():
|
||
if ours in sources:
|
||
sources[ours] = foreign
|
||
for meta_key, hook in self.site.config.get('METADATA_VALUE_MAPPING', {}).get(extractor.map_from, {}).items():
|
||
if meta_key in sources.values():
|
||
L.warn('Cannot convert {0} (language {1}): a metadata value mapping is defined for "{2}"!'.format(fname, lang, meta_key))
|
||
|
||
# Update metadata
|
||
updated = False
|
||
tags = meta.get(sources['tags'], [])
|
||
tags_are_string = False
|
||
if not isinstance(tags, list):
|
||
tags_are_string = True
|
||
tags = [tag.strip() for tag in tags.split(',') if tag.strip()]
|
||
|
||
if 'draft' in [_.lower() for _ in tags]:
|
||
tags.remove('draft')
|
||
meta[sources['status']] = 'draft'
|
||
updated = True
|
||
|
||
if 'private' in tags:
|
||
tags.remove('private')
|
||
meta[sources['status']] = 'private'
|
||
updated = True
|
||
|
||
if 'mathjax' in tags:
|
||
tags.remove('mathjax')
|
||
meta[sources['has_math']] = 'yes'
|
||
updated = True
|
||
|
||
if meta.get(sources['section']):
|
||
if meta.get(sources['category']):
|
||
L.warn('Cannot completely {0} (language {1}): both section and category are specified. Please determine the correct category to use yourself!'.format(fname, lang))
|
||
fully_converted = False
|
||
else:
|
||
meta[sources['category']] = meta[sources['section']]
|
||
del meta[sources['section']]
|
||
updated = True
|
||
|
||
if tags_are_string:
|
||
meta[sources['tags']] = ', '.join(tags)
|
||
|
||
if not updated:
|
||
# Nothing to do (but successful)!
|
||
converted = True
|
||
continue
|
||
|
||
# Recombine metadata with post text if necessary, and write back to file
|
||
meta_str = utils.write_metadata(meta, metadata_format=extractor.name, compiler=post.compiler,
|
||
comment_wrap=(post.compiler.name != 'rest'), site=self.site)
|
||
final_str = meta_str if is_two_file else (meta_str + content_str)
|
||
|
||
with io.open(fname, "w", encoding="utf-8") as meta_file:
|
||
meta_file.write(final_str)
|
||
converted = True
|
||
|
||
if converted:
|
||
if fully_converted:
|
||
number_converted += 1
|
||
else:
|
||
number_converted_partial += 1
|
||
|
||
L.info('{0} out of {2} posts upgraded; {1} only converted partially '
|
||
'(see above output).'.format(number_converted + number_converted_partial, number_converted_partial, len(flagged)))
|
||
else:
|
||
L.info('Metadata not upgraded.')
|
||
else:
|
||
L.info('No posts found with special tags or section metadata. No action is required.')
|
||
L.info('You can safely set the USE_TAG_METADATA and the WARN_ABOUT_TAG_METADATA settings to False.')
|