Merge pull request #31 from petterreinholdtsen/app-owncloud

Add support for owncloud in the apps menu
This commit is contained in:
Nick Daly 2013-09-28 10:48:39 -07:00
commit d722ca950f
12 changed files with 148 additions and 65 deletions

View File

@ -29,9 +29,9 @@ install: default
mkdir -p $(DESTDIR)$(PYDIR) $(DESTDIR)$(DATADIR) $(DESTDIR)/usr/bin \ mkdir -p $(DESTDIR)$(PYDIR) $(DESTDIR)$(DATADIR) $(DESTDIR)/usr/bin \
$(DESTDIR)/usr/share/doc/plinth $(DESTDIR)/usr/share/man/man1 $(DESTDIR)/usr/share/doc/plinth $(DESTDIR)/usr/share/man/man1
cp -a static themes $(DESTDIR)$(DATADIR)/ cp -a static themes $(DESTDIR)$(DATADIR)/
cp -a actions $(DESTDIR)$(DATADIR)/
cp -a sudoers.d $(DESTDIR)/etc/sudoers.d
cp -a *.py modules templates $(DESTDIR)$(PYDIR)/ cp -a *.py modules templates $(DESTDIR)$(PYDIR)/
mkdir -p $(DESTDIR)$(PYDIR)/exmachina
cp -a vendor/exmachina/exmachina.py $(DESTDIR)$(PYDIR)/exmachina/.
cp share/init.d/plinth $(DESTDIR)/etc/init.d cp share/init.d/plinth $(DESTDIR)/etc/init.d
install plinth $(DESTDIR)/usr/bin/ install plinth $(DESTDIR)/usr/bin/
mkdir -p $(DESTDIR)/var/lib/plinth/cherrypy_sessions $(DESTDIR)/var/log/plinth $(DESTDIR)/var/run mkdir -p $(DESTDIR)/var/lib/plinth/cherrypy_sessions $(DESTDIR)/var/log/plinth $(DESTDIR)/var/run

3
README
View File

@ -43,9 +43,6 @@ get down into the details and configure things the average user never
thinks about. For example, experts can turn off ntp or switch ntp thinks about. For example, experts can turn off ntp or switch ntp
servers. Basic users should never even know those options exist. servers. Basic users should never even know those options exist.
See comments in exmachina/exmachina.py for more details about the
configuration management process seperation scheme.
## Getting Started ## Getting Started
See the INSTALL file for additional details. Run: See the INSTALL file for additional details. Run:

10
actions/hostname-change Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
hostname="$1"
echo "$hostname" > /etc/hostname
if [ -x /etc/init.d/hostname.sh ] ; then
service hostname.sh start
else
service hostname start
fi

48
actions/owncloud-setup Executable file
View File

@ -0,0 +1,48 @@
#!/bin/sh
if [ -e /etc/apache2/conf-enabled/owncloud.conf ] ; then
owncloud_enable=true
else
owncloud_enable=false
fi
owncloud_enable_cur=$owncloud_enable
export owncloud_enable
while [ "$1" ] ; do
arg="$1"
shift
case "$arg" in
enable|noenable) # Not using disable for consistency with other options
if [ 'enable' = "$arg" ] ; then
owncloud_enable=true
else
owncloud_enable=false
fi
export owncloud_enable
;;
status)
printstatus() {
if $2 ; then
echo $1
else
echo no$1
fi
}
printstatus enable $owncloud_enable_cur
exit 0
;;
*)
;;
esac
done
if [ "$owncloud_enable" != "$owncloud_enable_cur" ] ; then
if $owncloud_enable ; then
DEBIAN_FRONTEND=noninteractive apt-get install -y owncloud 2>&1 | logger -t owncloud-setup
a2enconf owncloud 2>&1 | logger -t owncloud-setup
else
a2disconf owncloud 2>&1 | logger -t owncloud-setup
fi
service apache2 restart 2>&1 | logger -t owncloud-setup
fi

14
actions/timezone-change Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
zonename="$1"
tzpath="/usr/share/zoneinfo/$zonename"
if [ -e "$tzpath" ] ; then
cp "$tzpath" /etc/localtime
echo "$zonename" > /etc/timezone
exit 0
else
echo "timezone not valid" 1>&2
exit 1
fi

View File

@ -1,6 +1,9 @@
import cherrypy import cherrypy
from gettext import gettext as _
from modules.auth import require from modules.auth import require
from plugin_mount import PagePlugin from plugin_mount import PagePlugin
from forms import Form
from privilegedactions import privilegedaction_run
import cfg import cfg
class Apps(PagePlugin): class Apps(PagePlugin):
@ -9,6 +12,7 @@ class Apps(PagePlugin):
self.register_page("apps") self.register_page("apps")
self.menu = cfg.main_menu.add_item("Apps", "icon-download-alt", "/apps", 80) self.menu = cfg.main_menu.add_item("Apps", "icon-download-alt", "/apps", 80)
self.menu.add_item("Photo Gallery", "icon-picture", "/apps/photos", 35) self.menu.add_item("Photo Gallery", "icon-picture", "/apps/photos", 35)
self.menu.add_item("Owncloud", "icon-picture", "/apps/owncloud", 35)
@cherrypy.expose @cherrypy.expose
def index(self): def index(self):
@ -33,3 +37,47 @@ investment in the sentimental value of your family snaps? Keep those
photos local, backed up, easily accessed and free from the whims of photos local, backed up, easily accessed and free from the whims of
some other websites business model.</p> some other websites business model.</p>
""") """)
@cherrypy.expose
@require()
def owncloud(self, submitted=False, **kwargs):
checkedinfo = {
'enable' : False,
}
if submitted:
opts = []
for k in kwargs.keys():
if 'on' == kwargs[k]:
shortk = k.split("owncloud_").pop()
checkedinfo[shortk] = True
for key in checkedinfo.keys():
if checkedinfo[key]:
opts.append(key)
else:
opts.append('no'+key)
privilegedaction_run("owncloud-setup", opts)
output, error = privilegedaction_run("owncloud-setup", ['status'])
if error:
raise Exception("something is wrong: " + error)
for option in output.split():
checkedinfo[option] = True
main="""
"""
form = Form(title="Configuration",
action="/apps/owncloud",
name="configure_owncloud",
message='')
form.checkbox(_("Enable Owncloud"), name="owncloud_enable", id="owncloud_enable", checked=checkedinfo['enable'])
form.hidden(name="submitted", value="True")
form.html(_("<p>When enabled, the owncloud installation will be available from /owncloud/ on the web server.</p>"))
form.submit(_("Update setup"))
main += form.render()
sidebar_right="""
<strong>Owncloud</strong><p>gives you universal access to your files through a web interface or WebDAV. It also provides a platform to easily view & sync your contacts, calendars and bookmarks across all your devices and enables basic editing right on the web. Installation has minimal server requirements, doesnt need special permissions and is quick. ownCloud is extendable via a simple but powerful API for applications and plugins.
</p>
"""
return self.fill_template(title="Owncloud", main=main, sidebar_right=sidebar_right)

View File

@ -9,6 +9,7 @@ from gettext import gettext as _
from filedict import FileDict from filedict import FileDict
from modules.auth import require from modules.auth import require
from plugin_mount import PagePlugin, FormPlugin from plugin_mount import PagePlugin, FormPlugin
from privilegedactions import privilegedaction_run
import cfg import cfg
from forms import Form from forms import Form
from model import User from model import User
@ -48,20 +49,14 @@ def get_hostname():
def set_hostname(hostname): def set_hostname(hostname):
"Sets machine hostname to hostname" "Sets machine hostname to hostname"
cfg.log.info("Writing '%s' to /etc/hostname with exmachina" % hostname) cfg.log.info("Changing hostname to '%s'" % hostname)
try: try:
cfg.exmachina.augeas.set("/files/etc/hostname/*", hostname) privilegedaction_run("hostname-change", [hostname])
cfg.exmachina.augeas.save()
# don't persist/cache change unless it was saved successfuly # don't persist/cache change unless it was saved successfuly
sys_store = filedict_con(cfg.store_file, 'sys') sys_store = filedict_con(cfg.store_file, 'sys')
sys_store['hostname'] = hostname sys_store['hostname'] = hostname
if platform.linux_distribution()[0]=="Ubuntu" :
cfg.exmachina.service.start("hostname")
else:
cfg.exmachina.initd.start("hostname.sh") # is hostname.sh debian-only?
except OSError, e: except OSError, e:
raise cherrypy.HTTPError(500, "Hostname restart failed: %s" % e) raise cherrypy.HTTPError(500, "Updating hostname failed: %s" % e)
class general(FormPlugin, PagePlugin): class general(FormPlugin, PagePlugin):
url = ["/sys/config"] url = ["/sys/config"]
@ -79,7 +74,7 @@ class general(FormPlugin, PagePlugin):
return '<p>' + _('Only members of the expert group are allowed to see and modify the system setup.') + '</p>' return '<p>' + _('Only members of the expert group are allowed to see and modify the system setup.') + '</p>'
sys_store = filedict_con(cfg.store_file, 'sys') sys_store = filedict_con(cfg.store_file, 'sys')
hostname = cfg.exmachina.augeas.get("/files/etc/hostname/*") hostname = get_hostname()
# this layer of persisting configuration in sys_store could/should be # this layer of persisting configuration in sys_store could/should be
# removed -BLN # removed -BLN
defaults = {'time_zone': "slurp('/etc/timezone').rstrip()", defaults = {'time_zone': "slurp('/etc/timezone').rstrip()",
@ -139,10 +134,10 @@ class general(FormPlugin, PagePlugin):
raise raise
else: else:
message += msg message += msg
time_zone = time_zone.strip()
if time_zone != sys_store['time_zone']: if time_zone != sys_store['time_zone']:
src = os.path.join("/usr/share/zoneinfo", time_zone)
cfg.log.info("Setting timezone to %s" % time_zone) cfg.log.info("Setting timezone to %s" % time_zone)
cfg.exmachina.misc.set_timezone(time_zone) privilegedaction_run("timezone-change", [time_zone])
sys_store['time_zone'] = time_zone sys_store['time_zone'] = time_zone
return message or "Settings updated." return message or "Settings updated."

View File

@ -18,7 +18,6 @@ from logger import Logger
#from modules.auth import AuthController, require, member_of, name_is #from modules.auth import AuthController, require, member_of, name_is
from withsqlite.withsqlite import sqlite_db from withsqlite.withsqlite import sqlite_db
from exmachina.exmachina import ExMachinaClient
import socket import socket
__version__ = "0.2.14" __version__ = "0.2.14"
@ -84,8 +83,6 @@ def parse_arguments():
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.') parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
parser.add_argument('--pidfile', default="", parser.add_argument('--pidfile', default="",
help='specify a file in which the server may write its pid') help='specify a file in which the server may write its pid')
parser.add_argument('--listen-exmachina-key', default=False, action='store_true',
help='listen for JSON-RPC shared secret key on stdin at startup')
args=parser.parse_args() args=parser.parse_args()
if args.pidfile: if args.pidfile:
cfg.pidfile = args.pidfile cfg.pidfile = args.pidfile
@ -96,13 +93,6 @@ def parse_arguments():
except AttributeError: except AttributeError:
cfg.pidfile = "plinth.pid" cfg.pidfile = "plinth.pid"
if args.listen_exmachina_key:
# this is where we optionally try to read in a shared secret key to
# authenticate connections to exmachina
cfg.exmachina_secret_key = sys.stdin.readline().strip()
else:
cfg.exmachina_secret_key = None
def setup(): def setup():
parse_arguments() parse_arguments()
@ -113,19 +103,6 @@ def setup():
except AttributeError: except AttributeError:
pass pass
try:
from vendor.exmachina.exmachina import ExMachinaClient
except ImportError:
cfg.exmachina = None
print "unable to import exmachina client library, but continuing anyways..."
else:
try:
cfg.exmachina = ExMachinaClient(
secret_key=cfg.exmachina_secret_key or None)
except socket.error:
cfg.exmachina = None
print "couldn't connect to exmachina daemon, but continuing anyways..."
os.chdir(cfg.python_root) os.chdir(cfg.python_root)
cherrypy.config.update({'error_page.404': error_page_404}) cherrypy.config.update({'error_page.404': error_page_404})
cherrypy.config.update({'error_page.500': error_page_500}) cherrypy.config.update({'error_page.500': error_page_500})

15
privilegedactions.py Normal file
View File

@ -0,0 +1,15 @@
import sys
import subprocess
import cfg
def privilegedaction_run(action, options):
cmd = ['sudo', '-n', "/usr/share/plinth/actions/%s" % action]
if options:
cmd.extend(options)
cfg.log.info('running: %s ' % ' '.join(cmd))
output, error = \
subprocess.Popen(cmd,
stdout = subprocess.PIPE,
stderr= subprocess.PIPE).communicate()
return output, error

View File

@ -7,21 +7,17 @@
# Default-Stop: 0 1 6 # Default-Stop: 0 1 6
# Short-Description: plinth web frontend # Short-Description: plinth web frontend
# Description: # Description:
# Control the exmachina privileged execution daemon and the plinth # Control the plinth web frontend.
# web frontend.
### END INIT INFO ### END INIT INFO
# This file is /etc/init.d/plinth # This file is /etc/init.d/plinth
DAEMON=/usr/local/bin/plinth.py DAEMON=/usr/local/bin/plinth.py
EXMACHINA_DAEMON=/usr/local/bin/exmachina.py
PID_FILE=/var/run/plinth.pid PID_FILE=/var/run/plinth.pid
EXMACHINA_PID_FILE=/var/run/exmachina.pid
PLINTH_USER=www-data PLINTH_USER=www-data
PLINTH_GROUP=www-data PLINTH_GROUP=www-data
test -x $DAEMON || exit 0 test -x $DAEMON || exit 0
test -x $EXMACHINA_DAEMON || exit 0
set -e set -e
@ -31,17 +27,9 @@ start_plinth (){
if [ -f $PID_FILE ]; then if [ -f $PID_FILE ]; then
echo Already running with a pid of `cat $PID_FILE`. echo Already running with a pid of `cat $PID_FILE`.
else else
if [ -f $EXMACHINA_PID_FILE ]; then
echo exmachina was already running with a pid of `cat $EXMACHINA_PID_FILE`.
kill -15 `cat $EXMACHINA_PID_FILE`
rm -rf $EXMACHINA_PID_FILE
fi
SHAREDKEY=`$EXMACHINA_DAEMON --random-key`
touch $PID_FILE touch $PID_FILE
chown $PLINTH_USER:$PLINTH_GROUP $PID_FILE chown $PLINTH_USER:$PLINTH_GROUP $PID_FILE
echo $SHAREDKEY | $EXMACHINA_DAEMON --pidfile=$EXMACHINA_PID_FILE --group=$PLINTH_GROUP || rm $PID_FILE sudo -u $PLINTH_USER -g $PLINTH_GROUP $DAEMON --pidfile=$PID_FILE
sleep 0.5
echo $SHAREDKEY | sudo -u $PLINTH_USER -g $PLINTH_GROUP $DAEMON --pidfile=$PID_FILE
fi fi
} }
@ -53,13 +41,6 @@ stop_plinth () {
else else
echo "No pid file at $PID_FILE suggests plinth is not running." echo "No pid file at $PID_FILE suggests plinth is not running."
fi fi
if [ -f $EXMACHINA_PID_FILE ]; then
kill -15 `cat $EXMACHINA_PID_FILE` || true
rm -rf $EXMACHINA_PID_FILE
echo "killed exmachina"
else
echo "No pid file at $EXMACHINA_PID_FILE suggests exmachina is not running."
fi
} }
test -x $DAEMON || exit 0 test -x $DAEMON || exit 0

View File

@ -1,10 +1,7 @@
#! /bin/sh #! /bin/sh
#PYTHONPATH=vendor/exmachina:$PYTHONPATH #PYTHONPATH=$PYTHONPATH
export PYTHONPATH export PYTHONPATH
sudo killall exmachina.py
sudo vendor/exmachina/exmachina.py -v &
python plinth.py python plinth.py
sudo killall exmachina.py

1
sudoers.d/plinth Normal file
View File

@ -0,0 +1 @@
plinth ALL=(ALL:ALL) NOPASSWD:/usr/share/plinth/actions/*