mirror of
https://github.com/iiab/iiab.git
synced 2025-03-09 15:40:17 +00:00
Move 19 roles into roles/0-DEPRECATED-ROLES
This commit is contained in:
parent
0e39c42bbd
commit
2218d2334b
124 changed files with 5 additions and 1 deletions
139
roles/0-DEPRECATED-ROLES/activity-server/README.rst
Normal file
139
roles/0-DEPRECATED-ROLES/activity-server/README.rst
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
Serve XO activities from the XS school server
|
||||
=============================================
|
||||
|
||||
XO laptops can update their activities via HTTP, using specially
|
||||
microformatted html pages to determine what to download.
|
||||
|
||||
This package imports XO activities from a USB stick and generates the
|
||||
correct html to serve them, in as many languages as it knows how,
|
||||
using the localisation information from the activity bundles. Content
|
||||
negotiation between Apache and the laptops decides what html is seen.
|
||||
|
||||
The URL for this index is http://schoolserver/activities/.
|
||||
|
||||
A facility exists to add extra descriptive html to the generated
|
||||
indexes.
|
||||
|
||||
USB import
|
||||
----------
|
||||
|
||||
When a USB drive is inserted, the server looks for a directory in the
|
||||
root directory called xs-activity-server. In there it expects to find
|
||||
any number of activity bundles, a file called manifest.sha1, and
|
||||
optionally a file or files with the suffix ".info". Depending on the
|
||||
configuration of the school server, a file called "manifest.sha1.sig"
|
||||
might also be required.
|
||||
|
||||
Activity bundles are zip files with the suffix .xo and an internal
|
||||
layout described at http://wiki.laptop.org/go/Activity_bundles.
|
||||
|
||||
The manifest file should contain sha1sums for each activity bundle and
|
||||
the metadata files, as if you had run
|
||||
|
||||
sha1sum *.xo *.info > manifest.sha1
|
||||
|
||||
in the directory.
|
||||
|
||||
If full XS security is enabled (by the presence of /etc/xs-security-on
|
||||
-- see the xs-tools documentation), then manifest.sha1.sig should
|
||||
contain a detached GPG signature for manifest.sha1, signed by a key
|
||||
that the XS knows. If the school server lacks the /etc/xs-security-on
|
||||
flag, the manifest.sha1.sig file is ignored.
|
||||
|
||||
Multiple languages
|
||||
------------------
|
||||
|
||||
Activities can contain localisation information, which usually
|
||||
consists of a translated activity name. The localised information is
|
||||
found in the bundle in a directory like:
|
||||
|
||||
SomeWonderful.activity/locale/pt-BR/activity.linfo
|
||||
|
||||
where pt-BR is an RFC1788 language code. If any activity contains an
|
||||
activity.linfo file for a language, then an index is generated. The
|
||||
server has templates for indexes in some languages (currently Spanish
|
||||
and English); for other languages the indexes will be in English
|
||||
except for the localised names.
|
||||
|
||||
These index files are saved with names like
|
||||
activities/index.html.zh-es. You can choose to look at them directly
|
||||
that way, or let your browser decide which one is best for you by
|
||||
visiting activities/index.html.
|
||||
|
||||
If some activities lack localised information for a multi-part
|
||||
language code, the index will include information that exists for the
|
||||
corresponding single part code, before defaulting to English. For
|
||||
example, a zh-CN page will include zh localisation if need be. (This
|
||||
may not always be the best result: bn and bn-IN appear to use
|
||||
different scripts).
|
||||
|
||||
|
||||
Including extra descriptions
|
||||
----------------------------
|
||||
|
||||
The optional .info files in the xs-activation-server directory should
|
||||
consist of sections in this format:
|
||||
|
||||
[com.microsoft.Word]
|
||||
description = Write replacement, without distraction of collaboration.
|
||||
|
||||
[some.other.Something]
|
||||
description = another description, all on one line.
|
||||
|
||||
If a section heading (in square brackets) matches the bundle_id or
|
||||
service_name of an activity, the description will be displayed on the
|
||||
generated html page. This information is not used by automatic
|
||||
updates, but it might assist children in browsing and manually
|
||||
installing activities. Note: there is no clever localisation here.
|
||||
|
||||
Multiple versions
|
||||
-----------------
|
||||
|
||||
Over the course of a server deployment, an activity might be updated
|
||||
several times. To preserve disk space, only the 4 most recent
|
||||
versions of an activity are kept. Links to the second, third and
|
||||
fourth newest versions are presented in the activities html file, but
|
||||
these do not use the activity microformat and will not be visible to
|
||||
automated updaters.
|
||||
|
||||
To determine which activities are the most recent, the file's modification
|
||||
times (mtime) are examined. The version number is not considered here.
|
||||
|
||||
Note: If you plug in a USB stick with very out-of-date activities they
|
||||
will be deleted as soon as they get on the server.
|
||||
|
||||
HTML microformat
|
||||
----------------
|
||||
|
||||
The microformat is described at
|
||||
http://wiki.laptop.org/go/Activity_microformat.
|
||||
|
||||
Utility script
|
||||
--------------
|
||||
|
||||
/usr/bin/xs-check-activities will print statistics about a directory
|
||||
of activities to stderr. Its output is not particularly well
|
||||
formatted or explained, but it is there if you want it.
|
||||
|
||||
Files and directories
|
||||
---------------------
|
||||
|
||||
Activities are stored in /library/xs-activity-server/activities, with
|
||||
the html index being index.html in that directory. Apache is coaxed
|
||||
into serving this by /etc/httpd/conf.d/xs-activity-server.conf.
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
Old versions are only saved if the different versions have different
|
||||
file names. Most activity bundles have names like 'Maze-4.xo',
|
||||
'Maze-5.xo' and so on, but some lack the version number in the file
|
||||
name, so the most recently imported version ends up overwriting the
|
||||
older ones.
|
||||
|
||||
Source
|
||||
------
|
||||
|
||||
This role is based on the xs-activity-server rpm.
|
||||
|
||||
http://dev.laptop.org/git/users/martin/xs-activity-server/ v0.4 release
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
activity_server_enabled: False
|
||||
activity_server_install: True
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2008 One Laptop Per Child Association, Inc.
|
||||
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
|
||||
#
|
||||
# written by Douglas Bagnall <douglas@paradise.net.nz>
|
||||
|
||||
"""This script reads activity.info from bundle files and reports on
|
||||
their quality.
|
||||
"""
|
||||
|
||||
import xs_activities
|
||||
import sys, os
|
||||
|
||||
xs_activities.USE_STDERR = True
|
||||
|
||||
show_all = '--show-all' in sys.argv
|
||||
if show_all:
|
||||
sys.argv.remove('--show-all')
|
||||
|
||||
|
||||
try:
|
||||
directory = sys.argv[1]
|
||||
os.stat(directory)
|
||||
except (IndexError, OSError):
|
||||
print __doc__
|
||||
print "USAGE: %s DIRECTORY" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
|
||||
xs_activities.check_all_bundles(directory, show_all)
|
||||
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/python
|
||||
# Copyright (C) 2008 One Laptop Per Child Association, Inc.
|
||||
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
|
||||
#
|
||||
# written by Douglas Bagnall <douglas@paradise.net.nz>
|
||||
|
||||
"""Read activity.info files from a directory full of activity bundles
|
||||
and write an appropriate html manifest of the most recent versions.
|
||||
The manifest uses the OLPC activity microformat:
|
||||
|
||||
http://wiki.laptop.org/go/Activity_microformat
|
||||
|
||||
This is put in a place where apache can find it, and apache will serve
|
||||
it and the activities to the laptops. Messages go to /var/log/user.log.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from time import time
|
||||
|
||||
import xs_activities
|
||||
|
||||
INPUT_DIR = "/library/xs-activity-server/activities"
|
||||
OUTPUT_LINK = "/library/xs-activity-server/www"
|
||||
|
||||
try:
|
||||
CURRENT_DIR = os.readlink(OUTPUT_LINK)
|
||||
except OSError:
|
||||
CURRENT_DIR = None
|
||||
|
||||
|
||||
def create_dir_manifest(dir_path):
|
||||
manifest = []
|
||||
os.chdir(dir_path)
|
||||
for root, dirs, files in os.walk("."):
|
||||
for filename in files:
|
||||
if not filename.endswith(".xo") and not filename.endswith(".xol"):
|
||||
continue
|
||||
|
||||
path = os.path.join(root, filename)
|
||||
s = os.stat(path)
|
||||
manifest.append((path, s.st_ino))
|
||||
|
||||
manifest.sort()
|
||||
return manifest
|
||||
|
||||
|
||||
def input_changed():
|
||||
if CURRENT_DIR is None:
|
||||
return True
|
||||
|
||||
input_manifest = create_dir_manifest(INPUT_DIR)
|
||||
current_manifest = create_dir_manifest(CURRENT_DIR)
|
||||
return input_manifest != current_manifest
|
||||
|
||||
|
||||
if not input_changed():
|
||||
# no changes or nothing to do
|
||||
sys.exit(0)
|
||||
|
||||
# create new output dir
|
||||
OUTPUT_DIR = OUTPUT_LINK + "." + str(time())
|
||||
os.mkdir(OUTPUT_DIR)
|
||||
|
||||
# link in all activities
|
||||
os.chdir(INPUT_DIR)
|
||||
for root, dirs, files in os.walk("."):
|
||||
output_dir = os.path.join(OUTPUT_DIR, root)
|
||||
if not os.path.isdir(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
for filename in files:
|
||||
if not filename.endswith(".xo") and not filename.endswith(".xol"):
|
||||
continue
|
||||
|
||||
path = os.path.join(root, filename)
|
||||
output_path = os.path.join(output_dir, filename)
|
||||
os.link(path, output_path)
|
||||
|
||||
# create html index
|
||||
output_html = os.path.join(output_dir, 'index.html')
|
||||
xs_activities.htmlise_bundles(output_dir, output_html)
|
||||
|
||||
|
||||
# update symlink atomically
|
||||
link = OUTPUT_DIR + ".lnk"
|
||||
os.symlink(OUTPUT_DIR, link)
|
||||
os.rename(link, OUTPUT_LINK)
|
||||
|
||||
# remove old index
|
||||
if CURRENT_DIR is not None:
|
||||
shutil.rmtree(CURRENT_DIR, ignore_errors=True)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="olpc-activity-info">
|
||||
<h2>%(name)s</h2>
|
||||
%(description)s
|
||||
<ul>
|
||||
<li>Identifier: <span class="olpc-activity-id">%(bundle_id)s</span></li>
|
||||
<li>Version: <span class="olpc-activity-version">%(activity_version)s</span></li>
|
||||
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
|
||||
<li style="display: %(show_older_versions)s">Older versions: %(older_versions)s</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Locally available activities</h1>
|
||||
<p id="olpc-activity-group-desc">These activities are stored on the school server.</p>
|
||||
%(activities)s
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="olpc-activity-info">
|
||||
<h2>%(name)s</h2>
|
||||
%(description)s
|
||||
<ul>
|
||||
<li>Identificador: <span class="olpc-activity-id">%(bundle_id)s</span></li>
|
||||
<li>Versión: <span class="olpc-activity-version">%(activity_version)s</span></li>
|
||||
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
|
||||
<li style="display: %(show_older_versions)s">Versiones anteriores: %(older_versions)s</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Actividades disponibles localmente</h1>
|
||||
<p id="olpc-activity-group-desc">Estas actividades están almacenads en el servidor de la escuela.</p>
|
||||
%(activities)s
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="olpc-activity-info">
|
||||
<h2>%(name)s</h2>
|
||||
%(description)s
|
||||
<ul>
|
||||
<li>Identifiant: <span class="olpc-activity-id">%(bundle_id)s</span></li>
|
||||
<li>Version: <span class="olpc-activity-version">%(activity_version)s</span></li>
|
||||
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
|
||||
<li style="display: %(show_older_versions)s">Anciennes versions: %(older_versions)s</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Activités disponibles localement</h1>
|
||||
<p id="olpc-activity-group-desc">Ces activités sont stockées sur le serveur de l’école.</p>
|
||||
%(activities)s
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<div class="olpc-activity-info">
|
||||
<h2>%(name)s</h2>
|
||||
%(description)s
|
||||
<ul>
|
||||
<li>Pou idantifye: <span class="olpc-activity-id">%(bundle_id)s</span></li>
|
||||
<li>Vesyon: <span class="olpc-activity-version">%(activity_version)s</span></li>
|
||||
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
|
||||
<li style="display: %(show_older_versions)s">Vesyon ansyen: %(older_versions)s</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Aktivite ki disponib nan Bwat la</h1>
|
||||
<p id="olpc-activity-group-desc">Aktivite sa yo disponib sou sit lekòl la.</p>
|
||||
%(activities)s
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Locally available activities</h1>
|
||||
<p id="olpc-activity-group-desc">These activities are stored on the school server.</p>
|
||||
<div class="olpc-activity-info">
|
||||
There are currently no activities. Insert a USB drive with activities on it to add some.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Activités disponibles localement</h1>
|
||||
<p id="olpc-activity-group-desc">Ces activités sont stockées sur le serveur de l’école.</p>
|
||||
<div class="olpc-activity-info">
|
||||
Il n'y a pas d'activités. Insérez une clé USB avec des activités sur pour ajouter.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<body>
|
||||
<h1 id="olpc-activity-group-name">Aktivite ki disponib nan Bwat la</h1>
|
||||
<p id="olpc-activity-group-desc">Aktivite sa yo disponib sou sit lekòl la.</p>
|
||||
<div class="olpc-activity-info">
|
||||
Gen kounye a pa gen okenn aktivite. Antre yon USB ak aktivite sou li ajoute kèk.
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,535 @@
|
|||
# Copyright (C) 2008 One Laptop Per Child Association, Inc.
|
||||
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
|
||||
#
|
||||
# written by Douglas Bagnall <douglas@paradise.net.nz>
|
||||
|
||||
"""Functions for processing XO activities, either for indexing and
|
||||
presentaton to the laptops, or for diagnostics.
|
||||
"""
|
||||
|
||||
import os, sys, shutil
|
||||
import zipfile
|
||||
import re
|
||||
from cStringIO import StringIO
|
||||
#import traceback
|
||||
import syslog
|
||||
from ConfigParser import SafeConfigParser
|
||||
|
||||
# we no longer really have a default in that it is set in the conf file
|
||||
# we assume that we have a lang_template for the default language
|
||||
TEMPLATE_DIR = '/library/xs-activity-server/lang_templates'
|
||||
DEFAULT_LANG = 'en'
|
||||
|
||||
# how many versions before the latest are worth having around.
|
||||
KEEP_OLD_VERSIONS = 3
|
||||
|
||||
#print to stderr, rathe than syslog?
|
||||
USE_STDERR=False
|
||||
|
||||
REQUIRED_TAGS = ('bundle_id', 'activity_version', 'host_version', 'name', 'license')
|
||||
OPTIONAL_TAGS = ('show_launcher', 'exec', 'mime_types', 'icon')
|
||||
#XXX need either icon or show_launcher=no
|
||||
|
||||
def log(msg, level=syslog.LOG_NOTICE):
|
||||
if USE_STDERR:
|
||||
print >> sys.stderr, msg
|
||||
else:
|
||||
syslog.openlog( 'xs-activity-server', 0, syslog.LOG_USER )
|
||||
syslog.syslog(level, msg)
|
||||
syslog.closelog()
|
||||
|
||||
class BundleError(Exception):
|
||||
pass
|
||||
|
||||
class Bundle(object):
|
||||
def __init__(self, bundle):
|
||||
self.linfo = {}
|
||||
self.zf = zipfile.ZipFile(bundle)
|
||||
# The activity path will be 'Something.activity/activity/activity.info'
|
||||
for p in self.zf.namelist():
|
||||
if p.endswith(self.INFO_PATH):
|
||||
self.raw_data = read_info_file(self.zf, p, self.INFO_SECTION)
|
||||
|
||||
# the file name itself is needed for the URL
|
||||
self.url = os.path.basename(bundle)
|
||||
self.mtime = os.stat(bundle).st_mtime
|
||||
|
||||
self.name = self.raw_data.get('name')
|
||||
self.license = self.raw_data.get('license', None)
|
||||
|
||||
# child ctor should now call
|
||||
# _set_bundle_id
|
||||
# _set_version
|
||||
# _set_description
|
||||
def _set_bundle_id(self, id):
|
||||
if id is None:
|
||||
raise BundleError("bad bundle: No bundle ID")
|
||||
self.bundle_id = id
|
||||
if self.name is None:
|
||||
self.name = id
|
||||
|
||||
def _set_version(self, version):
|
||||
self.version = version
|
||||
|
||||
def _set_description(self, description):
|
||||
self.description = description
|
||||
|
||||
def __cmp__(self, other):
|
||||
"""Alphabetical sort (locale dependant of course)"""
|
||||
if self.bundle_id == other.bundle_id:
|
||||
return cmp(self.version, other.version)
|
||||
return cmp(self.name, other.name)
|
||||
|
||||
def set_older_versions(self, versions):
|
||||
"""Versions should be a list of (version number, version tuples)"""
|
||||
self.older_versions = ', '.join('<a href="%s">%s</a>' % (v.url, v.version) for v in versions)
|
||||
|
||||
def to_html(self, locale, template=None):
|
||||
"""Fill in the template with data approriate for the locale."""
|
||||
if template is None:
|
||||
template = read_template('activity', locale)
|
||||
|
||||
d = {'older_versions': self.older_versions,
|
||||
'bundle_id': self.bundle_id,
|
||||
'activity_version': self.version,
|
||||
'bundle_url': self.url,
|
||||
'name': self.name,
|
||||
'description': self.description,
|
||||
}
|
||||
|
||||
d.update(self.linfo.get(locale, {}))
|
||||
|
||||
if d['older_versions']:
|
||||
d['show_older_versions'] = 'inline'
|
||||
else:
|
||||
d['show_older_versions'] = 'none'
|
||||
|
||||
return template % d
|
||||
|
||||
def get_name(self, locale=None):
|
||||
return self.name
|
||||
|
||||
class Content(Bundle):
|
||||
INFO_PATH = "library/library.info"
|
||||
INFO_SECTION = "Library"
|
||||
|
||||
def __init__(self, bundle):
|
||||
super(Content, self).__init__(bundle)
|
||||
|
||||
d = self.raw_data
|
||||
# bundle_id is often missing; service name is used instead.
|
||||
self._set_bundle_id(d.get('global_name', None))
|
||||
self._set_version(d.get('library_version', 1))
|
||||
self._set_description(d.get('long_name', ''))
|
||||
|
||||
def debug(self, force_recheck=False):
|
||||
# FIXME: implement debug checking for content bundles
|
||||
return {}
|
||||
|
||||
class Activity(Bundle):
|
||||
INFO_PATH = "activity/activity.info"
|
||||
INFO_SECTION = "Activity"
|
||||
|
||||
#Activities appear to be looser than RFC3066, using e.g. _ in place of -.
|
||||
linfo_re = re.compile(r'/locale/([A-Za-z]+[\w-]*)/activity.linfo$')
|
||||
|
||||
def __init__(self, bundle):
|
||||
"""Takes a zipped .xo bundle name, returns a dictionary of its
|
||||
activity info. Can raise a variety of exceptions, all of
|
||||
which should indicate the bundle is invalid."""
|
||||
super(Activity, self).__init__(bundle)
|
||||
|
||||
# The locale info will be Something.activity/locale/xx_XX/activity.linfo
|
||||
for p in self.zf.namelist():
|
||||
linfo = self.linfo_re.search(p)
|
||||
if linfo:
|
||||
lang = canonicalise(linfo.group(1))
|
||||
self.linfo[lang] = read_info_file(self.zf, p, self.INFO_SECTION)
|
||||
|
||||
# Unfortunately the dictionary lacks some information, and
|
||||
# stores other bits in inconsistent ways.
|
||||
|
||||
d = self.raw_data
|
||||
# bundle_id is often missing; service name is used instead.
|
||||
self._set_bundle_id(d.get('bundle_id', d.get('service_name')))
|
||||
self._set_version(d.get('activity_version', 1))
|
||||
self._set_description(d.get('description', ''))
|
||||
|
||||
def debug(self, force_recheck=False):
|
||||
"""Make a copy of the raw data with added bits so we can work
|
||||
out what is going on. This is useful for diagnosing problems
|
||||
with odd activities and composing tut-tut-ing emails to their
|
||||
authors.
|
||||
|
||||
Not used in production."""
|
||||
if hasattr(self, '_debug_data') and not force_recheck:
|
||||
return self._debug_data
|
||||
|
||||
d = self.raw_data.copy()
|
||||
|
||||
correct_forms = {
|
||||
'name': str.upper,
|
||||
'activity_version': str.isdigit,
|
||||
'host_version': str.isdigit,
|
||||
'bundle_id': re.compile(r'^[\w.]+$').match,
|
||||
'service_name': re.compile(r'^[\w.]+$').match,
|
||||
'icon': re.compile(r'^[\S]+$').match,
|
||||
'exec': str.upper,
|
||||
'mime_types': re.compile(r'^([\w.+-]+/[\w.+-]+;?)*$').match,
|
||||
'update_url': re.compile(r'^http://([\w-]+\.?)+(:\d+)?(/[\w~%.-]+)*$').match,
|
||||
#'update_url': re.compile(r'^$').match,
|
||||
'show_launcher': re.compile(r'^(yes)|(no)$').match,
|
||||
'class': re.compile(r'^(\w+.?)+$').match,
|
||||
'license': str.upper,
|
||||
#'license': re.compile(r'^GPLv[23]\+?$').match,
|
||||
}
|
||||
|
||||
for k, v in d.items():
|
||||
if k in correct_forms:
|
||||
f = correct_forms.get(k, len)
|
||||
if not f(v):
|
||||
d['BAD ' + k] = v
|
||||
|
||||
rcount = 0
|
||||
for k in REQUIRED_TAGS:
|
||||
if k not in d:
|
||||
d['LACKS %s' % k] = True
|
||||
rcount += 1
|
||||
d['MISSING KEYS'] = rcount
|
||||
|
||||
for t in OPTIONAL_TAGS:
|
||||
if t not in d:
|
||||
d['NO ' + t] = True
|
||||
|
||||
if not 'icon' in d and d.get('show_launcher') != 'no':
|
||||
d['NO icon AND show_launcher'] = True
|
||||
|
||||
self._debug_data = d
|
||||
return d
|
||||
|
||||
def get_name(self, locale):
|
||||
"""Return the best guess at a name for the locale."""
|
||||
for loc in locale_search_path(locale):
|
||||
if loc in self.linfo and 'name' in self.linfo[loc]:
|
||||
return self.linfo[loc]['name']
|
||||
return super(Activity, self).get_name()
|
||||
|
||||
|
||||
|
||||
def check_all_bundles(directory, show_all_bundles=False):
|
||||
"""A verbose debug function."""
|
||||
all_bundles = []
|
||||
unique_bundles = {}
|
||||
counts = {}
|
||||
# watch for these tags and print out the lists
|
||||
bad_contents = {}
|
||||
all_linfo = {}
|
||||
unique_linfo = {}
|
||||
linfo_keys = {}
|
||||
log('Checking all activities in %s\n' % directory)
|
||||
for f in os.listdir(directory):
|
||||
if not f.endswith('.xo') and not f.endswith('.xol'):
|
||||
continue
|
||||
#log(f)
|
||||
try:
|
||||
if f.endswith('.xo'):
|
||||
bundle = Activity(os.path.join(directory, f))
|
||||
else:
|
||||
bundle = Content(os.path.join(directory, f))
|
||||
except Exception, e:
|
||||
log("IRREDEEMABLE bundle %-25s (Error: %s)" % (f, e), syslog.LOG_WARNING)
|
||||
|
||||
#Clump together bundles of the same ID
|
||||
x = unique_bundles.setdefault(bundle.bundle_id, [])
|
||||
x.append(bundle)
|
||||
all_bundles.append(bundle)
|
||||
|
||||
if not show_all_bundles:
|
||||
#only show the newest one of each set.
|
||||
bundles = []
|
||||
for versions in unique_bundles.values():
|
||||
versions.sort()
|
||||
bundles.append(versions[-1])
|
||||
|
||||
else:
|
||||
bundles = all_bundles
|
||||
|
||||
licenses = {}
|
||||
for bundle in bundles:
|
||||
bid = bundle.bundle_id
|
||||
for k, v in bundle.debug().iteritems():
|
||||
counts[k] = counts.get(k, 0) + 1
|
||||
if k.startswith('BAD '):
|
||||
bc = bad_contents.setdefault(k, {})
|
||||
bc[bid] = v
|
||||
for k, v in bundle.linfo.iteritems():
|
||||
linfo_l = all_linfo.setdefault(k, [])
|
||||
linfo_l.append(bundle)
|
||||
for x in v:
|
||||
linfo_keys[x] = linfo_keys.get(x, 0) + 1
|
||||
if v['name'] != bundle.name:
|
||||
linfo_l = unique_linfo.setdefault(k, [])
|
||||
linfo_l.append(bundle)
|
||||
|
||||
if bundle.license:
|
||||
lic = licenses.setdefault(bundle.license, [])
|
||||
lic.append(bundle.bundle_id)
|
||||
|
||||
citems = counts.items()
|
||||
rare_keys = [k for k, v in citems if v < 10]
|
||||
lack_counts = dict((k, v) for k, v in citems if k.startswith('LACKS '))
|
||||
bad_counts = dict((k, v) for k, v in citems if k.startswith('BAD '))
|
||||
no_counts = dict((k, v) for k, v in citems if k.startswith('NO '))
|
||||
tag_counts = dict((k, v) for k, v in citems if k not in lack_counts and
|
||||
k not in bad_counts and k not in no_counts and k != 'MISSING KEYS')
|
||||
|
||||
# flag whether the tag is needed, ok, or not
|
||||
tag_quality = dict((k, '*') for k in REQUIRED_TAGS)
|
||||
tag_quality.update((k, '+') for k in OPTIONAL_TAGS)
|
||||
linfo_counts = dict((k, len(v)) for k, v in all_linfo.iteritems())
|
||||
linfo_uniq_counts = dict((k, len(v)) for k, v in unique_linfo.iteritems())
|
||||
|
||||
log('\nFound: %s bundles\n %s unique bundles' % (len(all_bundles), len(unique_bundles)))
|
||||
for d, name, d2 in [(tag_counts, '\nattribute counts:', tag_quality),
|
||||
(lack_counts, '\nmissing required keys:', {}),
|
||||
(no_counts, '\nunused optional keys:', {}),
|
||||
(bad_counts, '\nill-formed values:', {}),
|
||||
(linfo_counts, '\nlinfo counts: total localised', linfo_uniq_counts),
|
||||
(linfo_keys, '\nlinfo keys:', {})]:
|
||||
log(name)
|
||||
counts_reversed = [(v, k) for (k, v) in d.iteritems()]
|
||||
counts_reversed.sort()
|
||||
counts_reversed.reverse()
|
||||
for (k, v) in counts_reversed:
|
||||
log("%-25s %4s %4s" % (v, k, d2.get(v, '')))
|
||||
|
||||
log("\nRare keys:")
|
||||
for k in rare_keys:
|
||||
if k.startswith('BAD '):
|
||||
continue
|
||||
log(k)
|
||||
for b in bundles:
|
||||
v = b.debug().get(k)
|
||||
if v:
|
||||
log(' %-25s %s' % (b.bundle_id, v))
|
||||
|
||||
|
||||
log("\nInteresting contents:")
|
||||
for k, v in bad_contents.iteritems():
|
||||
log(k)
|
||||
for x in v.iteritems():
|
||||
log(' %s: %s' % x)
|
||||
|
||||
log("\nInteresting linfo:")
|
||||
for k in ('pseudo',):
|
||||
log(k)
|
||||
for a in all_linfo[k]:
|
||||
if a in unique_linfo.get(k, []):
|
||||
log(' * %s (%s vs. %s)' % (a.bundle_id, a.name, a.linfo[k]['name']))
|
||||
else:
|
||||
log(' %s (%s)' % (a.bundle_id, a.name))
|
||||
|
||||
log("\nLicenses:")
|
||||
for lic, v in licenses.iteritems():
|
||||
log("%-20s %s" %(repr(lic), len(v)))
|
||||
|
||||
log("\nRare licenses:")
|
||||
for lic, v in licenses.iteritems():
|
||||
if len(v) < 3:
|
||||
log(' %s' % lic)
|
||||
for x in v:
|
||||
log(" %s" %(x))
|
||||
|
||||
|
||||
|
||||
log("\nAlmost valid activities:")
|
||||
for b in bundles:
|
||||
d = b.debug()
|
||||
if d['MISSING KEYS'] == 1:
|
||||
missing = ', '.join(x for x in d if x.startswith('LACKS'))
|
||||
bad_values = ', '. join(x for x in d if x.startswith('BAD'))
|
||||
log("%-20s %s %s" %(b.name, missing, bad_values))
|
||||
|
||||
log("\nValid activities (maybe):")
|
||||
for b in bundles:
|
||||
d = b.debug()
|
||||
bid = b.bundle_id
|
||||
if (d['MISSING KEYS'] == 0 and
|
||||
bid not in bad_contents['BAD mime_types']):
|
||||
log("%-20s - %s" %(b.name, bid))
|
||||
#log(a.raw_data)
|
||||
|
||||
|
||||
|
||||
|
||||
def read_info_file(zipfile, path, section):
|
||||
"""Return a dictionary matching the contents of the config file at
|
||||
path in zipfile"""
|
||||
cp = SafeConfigParser()
|
||||
info = StringIO(zipfile.read(path))
|
||||
cp.readfp(info)
|
||||
return dict(cp.items(section))
|
||||
|
||||
def canonicalise(lang):
|
||||
"""Make all equivalent language strings the same.
|
||||
>>> canonicalise('Zh-cN')
|
||||
zh-CN
|
||||
>>> canonicalise('zh_CN')
|
||||
zh-CN
|
||||
"""
|
||||
lang = lang.replace('_', '-').upper()
|
||||
bits = lang.split('-', 1)
|
||||
bits[0] = bits[0].lower()
|
||||
return '-'.join(bits)
|
||||
|
||||
def locale_search_path(locale):
|
||||
"""Find a series of sensible locales to try, including
|
||||
DEFAULT_LANG. For example 'zh-CN' would become ('zh-CN', 'zh',
|
||||
'DEFAULT_LANG')."""
|
||||
#XXX might be better to be storing locale as tuple
|
||||
if '-' in locale:
|
||||
return (locale, locale.split('-')[0], DEFAULT_LANG)
|
||||
return (locale, DEFAULT_LANG)
|
||||
|
||||
|
||||
|
||||
def read_metadata(bundle_dir):
|
||||
"""Attempt to read data in a metadata file. Raises expected
|
||||
exceptions if the metadata file isn't readable. The file should
|
||||
look something like this:
|
||||
|
||||
[org.laptop.Pippy]
|
||||
description = Succinct description of this activity.
|
||||
|
||||
[org.laptop.Develop]
|
||||
description = Succinct description of this activity.
|
||||
web_icon = develop.png
|
||||
"""
|
||||
md_files = [os.path.join(bundle_dir, x)
|
||||
for x in os.listdir(bundle_dir) if x.endswith('.info')]
|
||||
cp = SafeConfigParser()
|
||||
cp.read(md_files)
|
||||
metadata = {}
|
||||
for section in cp.sections():
|
||||
metadata[section] = dict(x for x in cp.items(section))
|
||||
return metadata
|
||||
|
||||
|
||||
def htmlise_bundles(bundle_dir, dest_html):
|
||||
"""Makes a nice html manifest for the bundles in a directory. The
|
||||
manifest only shows the newest version of each bundle.
|
||||
"""
|
||||
#so, we collect up a dictionary of lists, then sort each list on
|
||||
#the version number to find the newest.
|
||||
|
||||
bundles = [os.path.join(bundle_dir, x)
|
||||
for x in os.listdir(bundle_dir) if x.endswith('.xo') or x.endswith('.xol')]
|
||||
|
||||
try:
|
||||
metadata = read_metadata(bundle_dir)
|
||||
except Exception, e:
|
||||
log("had trouble reading metadata: %s" % e)
|
||||
metadata = {}
|
||||
|
||||
all_bundles = {}
|
||||
for filename in bundles:
|
||||
try:
|
||||
if filename.endswith('.xo'):
|
||||
bundle = Activity(filename)
|
||||
else:
|
||||
bundle = Content(filename)
|
||||
x = all_bundles.setdefault(bundle.bundle_id, [])
|
||||
x.append((bundle.mtime, bundle))
|
||||
except Exception, e:
|
||||
log("Couldn't find good activity/library info in %s (Error: %s)" % (filename, e))
|
||||
|
||||
newest = []
|
||||
# create an index for each language that has a template
|
||||
# but track any locales in bundles in case we do not have templates for them
|
||||
locales = [os.path.join(o) for o in os.listdir(TEMPLATE_DIR) if os.path.isdir(os.path.join(TEMPLATE_DIR,o))]
|
||||
locales_found = set ()
|
||||
for versions in all_bundles.values():
|
||||
versions = [x[1] for x in sorted(versions)]
|
||||
# end of list is the newest; beginning of list might need deleting
|
||||
latest = versions.pop()
|
||||
locales_found.update(latest.linfo)
|
||||
newest.append(latest)
|
||||
goners = versions[:-KEEP_OLD_VERSIONS]
|
||||
keepers = versions[-KEEP_OLD_VERSIONS:]
|
||||
for v in goners:
|
||||
fn = os.path.join(bundle_dir, v.url)
|
||||
os.remove(fn)
|
||||
latest.set_older_versions(keepers)
|
||||
|
||||
if latest.bundle_id in metadata:
|
||||
# we have extra metadata with which to fill out details
|
||||
# presumably this is mainly human-oriented description.
|
||||
d = metadata[latest.bundle_id]
|
||||
for k in ('description', 'name'):
|
||||
if k in d:
|
||||
setattr(latest, k, d[k])
|
||||
|
||||
log('found locales: %s' % locales)
|
||||
|
||||
# assume locales is not empty as we have at least the default language
|
||||
for locale in locales:
|
||||
try:
|
||||
make_html(newest, locale, '%s.%s' % (dest_html, locale))
|
||||
except Exception, e:
|
||||
log("Couldn't make page for %s (Error: %s)" % (locale, e), syslog.LOG_WARNING)
|
||||
|
||||
# make_varfile(locales, dest_html)- have switched to multiviews, so var not needed
|
||||
|
||||
|
||||
def make_varfile(locales, dest_html):
|
||||
f = open(dest_html + '.var', 'w')
|
||||
index = os.path.basename(dest_html)
|
||||
f.write('URI: %s\n\n' % index)
|
||||
for locale in locales:
|
||||
f.write('URI: %s.%s\n' % (index, locale))
|
||||
f.write('Content-type: text/html; charset=utf-8\n')
|
||||
f.write('Content-language: %s\n\n' % locale)
|
||||
# now the default, slightly higher qs
|
||||
f.write('URI: %s.DEFAULT\n' % index)
|
||||
f.write('Content-type: text/html; charset=utf-8\n')
|
||||
f.write('Content-language: en\n\n')
|
||||
|
||||
f.close()
|
||||
|
||||
def read_template(name, locale):
|
||||
"""Try to read the locale's template, falling back to the
|
||||
default."""
|
||||
#also try containing locales, eg 'zh' for 'zh-CN'
|
||||
for x in locale_search_path(locale):
|
||||
try:
|
||||
f = open(os.path.join(TEMPLATE_DIR, x, name))
|
||||
break
|
||||
except (OSError, IOError), e:
|
||||
#log(str(e))
|
||||
continue
|
||||
s = f.read()
|
||||
f.close()
|
||||
return s
|
||||
|
||||
|
||||
def make_html(bundles, locale, filename):
|
||||
"""Write a microformated index for the activities in the appropriate language,
|
||||
and save it to filename."""
|
||||
page_tmpl = read_template('page', locale)
|
||||
act_tmpl = read_template('activity', locale)
|
||||
|
||||
#bundles.sort() won't cut it.
|
||||
schwartzian = [ (x.get_name(locale), x.to_html(locale, act_tmpl)) for x in bundles ]
|
||||
schwartzian.sort()
|
||||
s = page_tmpl % {'activities': '\n'.join(x[1] for x in schwartzian)}
|
||||
|
||||
if os.path.exists(filename):
|
||||
shutil.move(filename, filename + '~')
|
||||
f = open(filename, 'w')
|
||||
f.write(s)
|
||||
f.close()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
134
roles/0-DEPRECATED-ROLES/activity-server/tasks/main.yml
Normal file
134
roles/0-DEPRECATED-ROLES/activity-server/tasks/main.yml
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
# assume apache in admin group
|
||||
|
||||
- name: Create xs-activity-server directory tree
|
||||
file: path={{ item }}
|
||||
mode=0755
|
||||
owner=root
|
||||
group=admin
|
||||
state=directory
|
||||
with_items:
|
||||
- /library/xs-activity-server
|
||||
- /library/xs-activity-server/activities
|
||||
- /library/xs-activity-server/lang_templates
|
||||
- /library/xs-activity-server/www.0
|
||||
- /library/xs-activity-server/tmp
|
||||
|
||||
# Wish synchronize worked, but it doesn't
|
||||
|
||||
- name: Copy language templates
|
||||
command: rsync -a {{ iiab_dir }}/roles/activity-server/files/lang_templates /library/xs-activity-server/
|
||||
|
||||
- name: Copy default index files
|
||||
copy: src={{ item }}
|
||||
dest=/library/xs-activity-server/www.0
|
||||
mode=0755
|
||||
owner=root
|
||||
group=root
|
||||
with_fileglob:
|
||||
- www.0/index.html.*
|
||||
|
||||
- name: Point www to www.0 as default
|
||||
file: src=/library/xs-activity-server/www.0
|
||||
dest=/library/xs-activity-server/www
|
||||
owner=root
|
||||
group=admin
|
||||
state=link
|
||||
|
||||
- name: Chown language templates
|
||||
file: path=/library/xs-activity-server/lang_templates
|
||||
mode=0644
|
||||
owner=root
|
||||
group=admin
|
||||
state=directory
|
||||
recurse=yes
|
||||
|
||||
# We should have a var for python site-packages directory
|
||||
|
||||
- name: Create xs-activity-server python site-packages directory
|
||||
file: path=/usr/lib/python2.7/site-packages/xs_activities
|
||||
mode=0755
|
||||
owner=root
|
||||
group=root
|
||||
state=directory
|
||||
|
||||
- name: Install Python module
|
||||
copy: src=xs_activities/__init__.py
|
||||
dest=/usr/lib/python2.7/site-packages/xs_activities
|
||||
mode=0644
|
||||
owner=root
|
||||
group=root
|
||||
|
||||
- name: Copy scripts to /usr/bin
|
||||
copy: src={{ item }}
|
||||
dest=/usr/bin
|
||||
mode=0755
|
||||
owner=root
|
||||
group=root
|
||||
with_items:
|
||||
- bin/xs-regenerate-activities
|
||||
- bin/xs-check-activities
|
||||
|
||||
# Do in ansible what was done in /etc/sysconfig/olpc-scripts/setup.d/xs-activity-server script
|
||||
|
||||
- name: Copy xs-activity-server config file
|
||||
template: src=xs-activity-server.conf
|
||||
dest=/etc/{{ apache_config_dir }}
|
||||
owner=root
|
||||
group=root
|
||||
mode=0644
|
||||
|
||||
# SEE ALSO THE apache2_module SECTION IN roles/httpd/tasks/main.yml
|
||||
- name: enable mod_expires for debian
|
||||
command: a2enmod expires
|
||||
when: is_debuntu | bool
|
||||
|
||||
- name: create the link which enables the site
|
||||
file: src=/etc/apache2/sites-available/xs-activity-server.conf
|
||||
dest=/etc/apache2/sites-enabled/xs-activity-server.conf
|
||||
state=link
|
||||
when: activity_server_enabled and is_debuntu
|
||||
|
||||
- name: delete the link which enables the site
|
||||
file: src=/etc/apache2/sites-available/xs-activity-server.conf
|
||||
dest=/etc/apache2/sites-enabled/xs-activity-server.conf
|
||||
state=absent
|
||||
when: not activity_server_enabled and is_debuntu
|
||||
|
||||
|
||||
- name: Copy xs-activity-server usbmount file
|
||||
template: src=usbmount-60-xs-activity-server-installcontent
|
||||
dest=/etc/usbmount/mount.d
|
||||
owner=root
|
||||
group=root
|
||||
mode=0755
|
||||
|
||||
# TODO: Fix multiview so it supports portal language menu
|
||||
# For it only supports client's language code
|
||||
|
||||
# TODO: Upload Activity via web interface
|
||||
# and figure out what to do with olpc_activities.service
|
||||
|
||||
# short term addition of link for upload-activity server
|
||||
# ln -sf /usr/share/xs-config/cfg/html/top/en/cntr_upl_activity.php {{ doc_root }}/upload_activity.php
|
||||
|
||||
|
||||
- name: Restart httpd
|
||||
service: name={{ apache_service }}
|
||||
enabled=yes
|
||||
state=restarted
|
||||
|
||||
- name: Add 'activity-server' variable values to {{ iiab_ini_file }}
|
||||
ini_file:
|
||||
path: "{{ iiab_ini_file }}"
|
||||
section: activity-server
|
||||
option: "{{ item.option }}"
|
||||
value: "{{ item.value | string }}"
|
||||
with_items:
|
||||
- option: name
|
||||
value: "Activity Server"
|
||||
- option: description
|
||||
value: "Download an Activity."
|
||||
- option: path
|
||||
value: /activities
|
||||
- option: enabled
|
||||
value: "{{ xo_services_enabled }}"
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
#!/bin/bash
|
||||
# Part of the xs-activity-server package
|
||||
#
|
||||
# based on a similarly named script in the xs-rsync package
|
||||
# by Martin Langhoff <martin@laptop.org>
|
||||
#
|
||||
# Adapted for xs-activity-server by Douglas Bagnall
|
||||
# <douglas@paradise.net.nz>
|
||||
#
|
||||
# Copyright: One Laptop per Child
|
||||
|
||||
set -e
|
||||
|
||||
VERBOSE=yes
|
||||
MAGIC_DIR=$UM_MOUNTPOINT/xs-activity-server
|
||||
|
||||
FINAL_DIR=/library/xs-activity-server/activities
|
||||
|
||||
FILES_TO_RM=""
|
||||
|
||||
# combined with set -e, error() is called if something fails.
|
||||
error(){
|
||||
logger -puser.err -t "xs-activity-server[$$]" "Error at line $(caller)"
|
||||
[ "$FILES_TO_RM" ] && rm -rf $FILES_TO_RM
|
||||
}
|
||||
trap error ERR
|
||||
|
||||
|
||||
# Log a string via the syslog facility.
|
||||
log()
|
||||
{
|
||||
if test $1 != debug || expr "$VERBOSE" : "[yY]" > /dev/null; then
|
||||
logger -p user.$1 -t "xs-activity-server[$$]" -- "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
STEPS=7
|
||||
|
||||
[ -d $MAGIC_DIR ] || exit 0
|
||||
|
||||
log notice 'Found activity install directory';
|
||||
log notice "[1/$STEPS] Checking whether it has a manifest";
|
||||
|
||||
if [ -r $MAGIC_DIR/manifest.sha1 ];then
|
||||
log notice "[2/$STEPS] Seems to have a manifest";
|
||||
else
|
||||
log err "[2/$STEPS] Missing manifest"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
## Do we have enough space?
|
||||
# note: we could use awk {'print $4'} instead of the
|
||||
# perl regex, but it breaks with long /dev nodes
|
||||
# such as those from LVMs -which wrap. The regex captures the
|
||||
# number just left of the number with the percentage sign.
|
||||
NEED=`du -s -B1M $MAGIC_DIR | awk {'print $1'}`
|
||||
HAVE=`df -B1M $FINAL_DIR | tail -n1 | \
|
||||
perl -pe 'm/(\d+)\s+\d+\%/; $_=($1-1);'`
|
||||
if [ $NEED -gt $HAVE ];then
|
||||
log err 'Not enough free space in /library for these activities - cancelling';
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
### Copy it first - as the media is bound to be slow
|
||||
# - make this atomic by cp'ing to a tmpdir, and mv'ing into place
|
||||
# to be fail-safe
|
||||
# - mv aside manifest.sha1 and its sig
|
||||
# - TODO? we could avoid cp'ing files we already have using
|
||||
# rsync --copy-dest instead of cp
|
||||
#
|
||||
log notice "[3/$STEPS] Copying activities to disk";
|
||||
TMPDEST=`mktemp -d -p /library/xs-activity-server/tmp`
|
||||
|
||||
#make sure the tmp directory goes
|
||||
FILES_TO_RM="$FILES_TO_RM '$TMPDEST'"
|
||||
|
||||
cp --preserve=timestamps $MAGIC_DIR/* $TMPDEST
|
||||
|
||||
# In a tmpdir we own, safe from race conditions
|
||||
# run the checksums...
|
||||
log notice "[4/$STEPS] Checking the manifest";
|
||||
# mv the manifest to a different dir
|
||||
TMPMANIF=`mktemp -d -p /library/xs-activity-server/tmp`
|
||||
|
||||
FILES_TO_RM="$FILES_TO_RM '$TMPMANIF'"
|
||||
|
||||
|
||||
mv $TMPDEST/manifest.sha1 $TMPMANIF/
|
||||
if [ -e $TMPDEST/manifest.sha1.sig ]; then
|
||||
mv $TMPDEST/manifest.sha1.sig $TMPMANIF/
|
||||
fi
|
||||
xs-sum -c $TMPMANIF/manifest.sha1 -d $TMPDEST
|
||||
|
||||
#Let syslog know what we're doing
|
||||
cd $TMPDEST
|
||||
log notice "found $(ls *.xo |wc -l) activities"
|
||||
log debug "found these activities: $(ls *.xo)"
|
||||
cd -
|
||||
|
||||
log notice "[5/$STEPS] Copy the directories into place";
|
||||
#XXX not checking whether this clobbers existing files.
|
||||
mv $TMPDEST/* $FINAL_DIR
|
||||
|
||||
#So, now all the activities are in place, but maybe they're not
|
||||
#newer than what we have. So xs-regenerate-activities has to work that out.
|
||||
|
||||
log notice "[6/$STEPS] Regenerating the list of available activities";
|
||||
|
||||
/usr/bin/xs-regenerate-activities $FINAL_DIR 2>&1 | logger -p user.debug -t "xs-activity-server[$$]"
|
||||
|
||||
log notice "[$STEPS/$STEPS] Finished - XOs can now update activities.";
|
||||
|
||||
|
||||
rm -fr $FILES_TO_RM
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# xs-activity-server
|
||||
#
|
||||
# Copyright: On Laptop per Child
|
||||
# GPL v2
|
||||
# written by Douglas Bagnall <douglas@paradise.net.nz>
|
||||
#
|
||||
# This belongs in the apache conf.d directory.
|
||||
# (probably /etc/httpd/conf.d/)
|
||||
|
||||
Alias /activities /library/xs-activity-server/www
|
||||
<Directory /library/xs-activity-server/www >
|
||||
# Languages are set in 010-iiab.conf
|
||||
|
||||
ExpiresActive On
|
||||
ExpiresDefault now
|
||||
Options +MultiViews
|
||||
Require all granted
|
||||
#NOTE: an index.html.var file is generated, which ought to make
|
||||
# MultiViews redundant (by my reading) but it doesn't seem to
|
||||
# work. Someone could look at that sometime.
|
||||
</Directory>
|
||||
|
||||
#<Directory /activities >
|
||||
# ExpiresActive On
|
||||
# ExpiresDefault now
|
||||
#</Directory>
|
||||
#<Location /activities >
|
||||
# ExpiresActive On
|
||||
# ExpiresDefault now
|
||||
#</Location>
|
||||
Loading…
Add table
Add a link
Reference in a new issue