forked from github/pelican
Force world-readable permission on files and directory of the themes installed by pelican-themes. Only on posix system i.e. mostly non Windows Rationale: If the theme's files have only -rw------- permissions, once installed system wide and owned by root, they will not be accessible to any user but only root.
240 lines
7.5 KiB
Python
Executable file
240 lines
7.5 KiB
Python
Executable file
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import sys
|
|
|
|
try:
|
|
import pelican
|
|
except:
|
|
err('Cannot import pelican.\nYou must install Pelican in order to run this script.', -1)
|
|
|
|
|
|
global _THEMES_PATH
|
|
_THEMES_PATH = os.path.join(
|
|
os.path.dirname(
|
|
os.path.abspath(
|
|
pelican.__file__
|
|
)
|
|
),
|
|
'themes'
|
|
)
|
|
|
|
__version__ = '0.2'
|
|
_BUILTIN_THEMES = ['simple', 'notmyidea']
|
|
|
|
|
|
def err(msg, die=None):
|
|
"""Print an error message and exits if an exit code is given"""
|
|
sys.stderr.write(str(msg) + '\n')
|
|
if die:
|
|
sys.exit((die if type(die) is int else 1))
|
|
|
|
|
|
def main():
|
|
"""Main function"""
|
|
|
|
parser = argparse.ArgumentParser(description="""Install themes for Pelican""")
|
|
|
|
excl= parser.add_mutually_exclusive_group()
|
|
excl.add_argument('-l', '--list', dest='action', action="store_const", const='list',
|
|
help="Show the themes already installed and exit")
|
|
excl.add_argument('-p', '--path', dest='action', action="store_const", const='path',
|
|
help="Show the themes path and exit")
|
|
excl.add_argument('-V', '--version', action='version', version='pelican-themes v{0}'.format(__version__),
|
|
help='Print the version of this script')
|
|
|
|
|
|
parser.add_argument('-i', '--install', dest='to_install', nargs='+', metavar="theme path",
|
|
help='The themes to install')
|
|
parser.add_argument('-r', '--remove', dest='to_remove', nargs='+', metavar="theme name",
|
|
help='The themes to remove')
|
|
parser.add_argument('-U', '--upgrade', dest='to_upgrade', nargs='+',
|
|
metavar="theme path", help='The themes to upgrade')
|
|
parser.add_argument('-s', '--symlink', dest='to_symlink', nargs='+', metavar="theme path",
|
|
help="Same as `--install', but create a symbolic link instead of copying the theme. Useful for theme development")
|
|
parser.add_argument('-c', '--clean', dest='clean', action="store_true",
|
|
help="Remove the broken symbolic links of the theme path")
|
|
|
|
|
|
parser.add_argument('-v', '--verbose', dest='verbose', action="store_true",
|
|
help="Verbose output")
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
to_install = args.to_install or args.to_upgrade
|
|
to_sym = args.to_symlink or args.clean
|
|
|
|
|
|
if args.action:
|
|
if args.action is 'list':
|
|
list_themes(args.verbose)
|
|
elif args.action is 'path':
|
|
print(_THEMES_PATH)
|
|
elif to_install or args.to_remove or to_sym:
|
|
if args.to_remove:
|
|
if args.verbose:
|
|
print('Removing themes...')
|
|
|
|
for i in args.to_remove:
|
|
remove(i, v=args.verbose)
|
|
|
|
if args.to_install:
|
|
if args.verbose:
|
|
print('Installing themes...')
|
|
|
|
for i in args.to_install:
|
|
install(i, v=args.verbose)
|
|
|
|
if args.to_upgrade:
|
|
if args.verbose:
|
|
print('Upgrading themes...')
|
|
|
|
for i in args.to_upgrade:
|
|
install(i, v=args.verbose, u=True)
|
|
|
|
if args.to_symlink:
|
|
if args.verbose:
|
|
print('Linking themes...')
|
|
|
|
for i in args.to_symlink:
|
|
symlink(i, v=args.verbose)
|
|
|
|
if args.clean:
|
|
if args.verbose:
|
|
print('Cleaning the themes directory...')
|
|
|
|
clean(v=args.verbose)
|
|
else:
|
|
print('No argument given... exiting.')
|
|
|
|
|
|
def themes():
|
|
"""Returns the list of the themes"""
|
|
for i in os.listdir(_THEMES_PATH):
|
|
e = os.path.join(_THEMES_PATH, i)
|
|
|
|
if os.path.isdir(e):
|
|
if os.path.islink(e):
|
|
yield (e, os.readlink(e))
|
|
else:
|
|
yield (e, None)
|
|
|
|
|
|
def list_themes(v=False):
|
|
"""Display the list of the themes"""
|
|
for t, l in themes():
|
|
if not v:
|
|
t = os.path.basename(t)
|
|
if l:
|
|
if v:
|
|
print(t + (" (symbolic link to `" + l + "')"))
|
|
else:
|
|
print(t + '@')
|
|
else:
|
|
print(t)
|
|
|
|
|
|
def remove(theme_name, v=False):
|
|
"""Removes a theme"""
|
|
|
|
theme_name = theme_name.replace('/','')
|
|
target = os.path.join(_THEMES_PATH, theme_name)
|
|
|
|
if theme_name in _BUILTIN_THEMES:
|
|
err(theme_name + ' is a builtin theme.\nYou cannot remove a builtin theme with this script, remove it by hand if you want.')
|
|
elif os.path.islink(target):
|
|
if v:
|
|
print('Removing link `' + target + "'")
|
|
os.remove(target)
|
|
elif os.path.isdir(target):
|
|
if v:
|
|
print('Removing directory `' + target + "'")
|
|
shutil.rmtree(target)
|
|
elif os.path.exists(target):
|
|
err(target + ' : not a valid theme')
|
|
else:
|
|
err(target + ' : no such file or directory')
|
|
|
|
|
|
def install(path, v=False, u=False):
|
|
"""Installs a theme"""
|
|
if not os.path.exists(path):
|
|
err(path + ' : no such file or directory')
|
|
elif not os.path.isdir(path):
|
|
err(path + ' : not a directory')
|
|
else:
|
|
theme_name = os.path.basename(os.path.normpath(path))
|
|
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
|
exists = os.path.exists(theme_path)
|
|
if exists and not u:
|
|
err(path + ' : already exists')
|
|
elif exists and u:
|
|
remove(theme_name, v)
|
|
install(path, v)
|
|
else:
|
|
if v:
|
|
print("Copying `{p}' to `{t}' ...".format(p=path, t=theme_path))
|
|
try:
|
|
shutil.copytree(path, theme_path)
|
|
|
|
if os.name == 'posix':
|
|
for root, dirs, files in os.walk(theme_path):
|
|
for d in dirs:
|
|
dname = os.path.join(root, d)
|
|
os.chmod(dname, 0755)
|
|
for f in files:
|
|
fname = os.path.join(root, f)
|
|
os.chmod(fname, 0644)
|
|
except shutil.Error, e:
|
|
err("Cannot copy `{p}' to `{t}':\n{e}".format(p=path, t=theme_path, e=str(e)))
|
|
except OSError, e:
|
|
err("Cannot change permissions of files or directory in `{r}':\n{e}".format(r=theme_path, e=str(e)), die=False)
|
|
|
|
|
|
def symlink(path, v=False):
|
|
"""Symbolically link a theme"""
|
|
if not os.path.exists(path):
|
|
err(path + ' : no such file or directory')
|
|
elif not os.path.isdir(path):
|
|
err(path + ' : not a directory')
|
|
else:
|
|
theme_name = os.path.basename(os.path.normpath(path))
|
|
theme_path = os.path.join(_THEMES_PATH, theme_name)
|
|
if os.path.exists(theme_path):
|
|
err(path + ' : already exists')
|
|
else:
|
|
if v:
|
|
print("Linking `{p}' to `{t}' ...".format(p=path, t=theme_path))
|
|
try:
|
|
os.symlink(path, theme_path)
|
|
except Exception, e:
|
|
err("Cannot link `{p}' to `{t}':\n{e}".format(p=path, t=theme_path, e=str(e)))
|
|
|
|
|
|
def is_broken_link(path):
|
|
"""Returns True if the path given as is a broken symlink"""
|
|
path = os.readlink(path)
|
|
return not os.path.exists(path)
|
|
|
|
|
|
def clean(v=False):
|
|
"""Removes the broken symbolic links"""
|
|
c=0
|
|
for path in os.listdir(_THEMES_PATH):
|
|
path = os.path.join(_THEMES_PATH, path)
|
|
if os.path.islink(path):
|
|
if is_broken_link(path):
|
|
if v:
|
|
print('Removing {0}'.format(path))
|
|
try:
|
|
os.remove(path)
|
|
except OSError, e:
|
|
print('Error: cannot remove {0}'.format(path))
|
|
else:
|
|
c+=1
|
|
|
|
print("\nRemoved {0} broken links".format(c))
|