Merge saas-dev into soundcloud

This commit is contained in:
Duncan Sommerville 2015-06-10 16:08:52 -04:00
commit 459f9494c3
373 changed files with 6577 additions and 10376 deletions

3
.gitignore vendored
View File

@ -1,10 +1,11 @@
.*
*.*~
*.pyc
vendor/*
composer.phar
*~$
*log.*
**/airtime_analyzer.egg-info/*
**/*.egg-info/*
**/build/*
**/dist/*
*~

59
README
View File

@ -3,7 +3,7 @@
=========================================================================
Airtime is an open source application that provides remote and
collaborative automation of a broadcast station.
collaborative automation of a broadcast radio station.
Home page: http://www.sourcefabric.org/en/airtime/
@ -18,28 +18,62 @@ Major features:
* Solid playout. Airtime uses the open source Liquidsoap streaming language
for reliable and precise playback to multiple outputs.
* Open, extensible architecture. Stations are free to extend and alter
all parts of the program code, under the GNU GPLv3 license.
all parts of the program code, under the GNU AGPLv3 license.
INSTALLATION
------------
Please see this chapter to begin a typical installation:
http://sourcefabric.booktype.pro/airtime-25-for-broadcasters/preparing-the-server/
Basic installation has two steps:
If you are a developer, please see this page:
http://wiki.sourcefabric.org/display/CC/Airtime+Dev+Site
1) Run the install script, located in the Airtime root directory.
For installation direct from a git checkout on Ubuntu 12.04 LTS, run:
For an interactive installation, run:
cd install_full/ubuntu
sudo ./airtime-full-install
sudo ./install
For installation from git on Debian wheezy, run:
If you're using a terminal that is not running Bash, you'll need to run
cd install_full/debian
sudo ./airtime-full-install
sudo /bin/bash ./install
instead. You may need to install Bash first.
The installer will then prompt you about how you want to set up your Airtime
installation.
For a non-interactive full installation (do this if you're installing Airtime from
scratch and don't have any of your own configuration set up), run
sudo ./install -fiap
What this means:
-f - force; non-interactive (no prompts)
-i - install the default Icecast 2 setup for Airtime
-a - install the default apache setup for Airtime
-p - create a default Airtime postgres user
This will install all components necessary for Airtime, and set up
/usr/share/airtime as your web root (where apache looks for your Airtime files)
There are several options for installation - to see them all, run
sudo ./install --help
2) Once you've run the installer, open a web browser to http://localhost to run
the interactive setup. (If you have a custom apache configuration, navigate to
your Airtime web host instead.)
If you just want to run Airtime with default settings, you won't need to change
anything, but if you have any custom configuration settings you'll be able to
specify them.
Once you finish the setup process, you'll be presented with a configuration
checklist so you can ensure that your Airtime installation is working
correctly. If anything was mis-configured, the checklist will provide some .
helpful tips to resolve the issue.
If your checklist is all green, you're ready to get started with Airtime!
Quick links to our resources
----------------------------
@ -48,4 +82,3 @@ Forums and mailing lists: http://forum.sourcefabric.org
Bug tracker: http://dev.sourcefabric.org
Source code: http://github.com/sourcefabric/Airtime
IRC chat: #airtime on Freenode

View File

@ -1,19 +1,19 @@
<?php
require_once __DIR__."/configs/conf.php";
require_once CONFIG_PATH . "conf.php";
$CC_CONFIG = Config::getConfig();
require_once __DIR__."/configs/ACL.php";
if (!@include_once('propel/propel1/runtime/lib/Propel.php'))
{
die('Error: Propel not found. Did you install Airtime\'s third-party dependencies with composer? (Check the README.)');
}
require_once CONFIG_PATH . "ACL.php";
Propel::init(__DIR__."/configs/airtime-conf-production.php");
// Since we initialize the database during the configuration check,
// check the $configRun global to avoid reinitializing unnecessarily
if (!isset($configRun) || !$configRun) {
Propel::init(CONFIG_PATH . 'airtime-conf-production.php');
}
//Composer's autoloader
require_once 'autoload.php';
require_once __DIR__."/configs/constants.php";
require_once CONFIG_PATH . "constants.php";
require_once 'Preference.php';
require_once 'Locale.php';
require_once "DateHelper.php";
@ -35,8 +35,9 @@ require_once __DIR__.'/controllers/plugins/Maintenance.php';
require_once __DIR__.'/controllers/plugins/ConversionTracking.php';
require_once __DIR__.'/modules/rest/controllers/ShowImageController.php';
require_once __DIR__.'/modules/rest/controllers/MediaController.php';
require_once __DIR__.'/upgrade/Upgrades.php';
require_once (APPLICATION_PATH."/logging/Logging.php");
require_once (APPLICATION_PATH . "/logging/Logging.php");
Logging::setLogPath('/var/log/airtime/zendphp.log');
// We need to manually route because we can't load Zend without the database being initialized first.
@ -47,7 +48,7 @@ if (array_key_exists("REQUEST_URI", $_SERVER) && (stripos($_SERVER["REQUEST_URI"
}
Config::setAirtimeVersion();
require_once __DIR__."/configs/navigation.php";
require_once (CONFIG_PATH . 'navigation.php');
Zend_Validate::setDefaultNamespaces("Zend");
@ -122,6 +123,18 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
$view->headScript()->appendScript("var COMPANY_NAME = '" . COMPANY_NAME . "';");
}
protected function _initUpgrade() {
/* We need to wrap this here so that we aren't checking when we're running the unit test suite
*/
if (getenv("AIRTIME_UNIT_TEST") != 1) {
//This will do the upgrade too if it's needed...
if (UpgradeManager::checkIfUpgradeIsNeeded()) {
$upgradeManager = new UpgradeManager();
$upgradeManager->doUpgrade();
}
}
}
protected function _initHeadLink()
{
$CC_CONFIG = Config::getConfig();
@ -196,12 +209,12 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
}
$view->headScript()->appendScript("var userType = '$userType';");
if (array_key_exists('REQUEST_URI', $_SERVER)) { //Doesn't exist for unit tests
if (strpos($_SERVER['REQUEST_URI'], $baseUrl . 'Dashboard/stream-player') === false
&& strpos($_SERVER['REQUEST_URI'], $baseUrl . 'audiopreview/audio-preview') === false
&& strpos($_SERVER['REQUEST_URI'], $baseUrl . 'audiopreview/playlist-preview') === false
&& strpos($_SERVER['REQUEST_URI'], $baseUrl . 'audiopreview/block-preview') === false
&& $_SERVER['REQUEST_URI'] != "/"
) {
$plan_level = strval(Application_Model_Preference::GetPlanLevel());
// Since the Hobbyist plan doesn't come with Live Chat support, don't enable it
@ -225,8 +238,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
protected function _initViewHelpers()
{
$view = $this->getResource('view');
$view->addHelperPath('../application/views/helpers', 'Airtime_View_Helper');
$view->addHelperPath(APPLICATION_PATH . 'views/helpers', 'Airtime_View_Helper');
$view->assign('suspended', (Application_Model_Preference::getProvisioningStatus() == PROVISIONING_STATUS_SUSPENDED));
}
@ -239,7 +251,7 @@ class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
protected function _initZFDebug()
{
Zend_Controller_Front::getInstance()->throwExceptions(true);
Zend_Controller_Front::getInstance()->throwExceptions(false);
/*
if (APPLICATION_ENV == "development") {

View File

@ -0,0 +1,110 @@
<?php
// Only enable cookie secure if we are supporting https.
// Ideally, this would always be on and we would force https,
// but the default installation configs are likely to be installed by
// amature users on the setup that does not have https. Forcing
// cookie_secure on non https would result in confusing login problems.
if(!empty($_SERVER['HTTPS'])) {
ini_set('session.cookie_secure', '1');
}
ini_set('session.cookie_httponly', '1');
error_reporting(E_ALL|E_STRICT);
function exception_error_handler($errno, $errstr, $errfile, $errline) {
//Check if the statement that threw this error wanted its errors to be
//suppressed. If so then return without with throwing exception.
if (0 === error_reporting()) return;
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
return false;
}
set_error_handler("exception_error_handler");
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
defined('VERBOSE_STACK_TRACE')
|| define('VERBOSE_STACK_TRACE', (getenv('VERBOSE_STACK_TRACE') ? getenv('VERBOSE_STACK_TRACE') : true));
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
get_include_path(),
realpath(LIB_PATH)
)));
set_include_path(APPLICATION_PATH . 'common' . PATH_SEPARATOR . get_include_path());
//Propel classes.
set_include_path(APPLICATION_PATH . 'models' . PATH_SEPARATOR . get_include_path());
//Controller plugins.
set_include_path(APPLICATION_PATH . 'controllers' . PATH_SEPARATOR . get_include_path());
//Controller plugins.
set_include_path(APPLICATION_PATH . 'controllers/plugins' . PATH_SEPARATOR . get_include_path());
//Zend framework
if (file_exists('/usr/share/php/libzend-framework-php')) {
set_include_path('/usr/share/php/libzend-framework-php' . PATH_SEPARATOR . get_include_path());
}
//Services.
set_include_path(APPLICATION_PATH . '/services/' . PATH_SEPARATOR . get_include_path());
//cloud storage directory
set_include_path(APPLICATION_PATH . '/cloud_storage' . PATH_SEPARATOR . get_include_path());
//Upgrade directory
set_include_path(APPLICATION_PATH . '/upgrade/' . PATH_SEPARATOR . get_include_path());
//Common directory
set_include_path(APPLICATION_PATH . '/common/' . PATH_SEPARATOR . get_include_path());
/** Zend_Application */
require_once 'Zend/Application.php';
$application = new Zend_Application(
APPLICATION_ENV,
CONFIG_PATH . 'application.ini'
);
require_once(APPLICATION_PATH . "logging/Logging.php");
Logging::setLogPath('/var/log/airtime/zendphp.log');
Logging::setupParseErrorLogging();
// Create application, bootstrap, and run
try {
$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cli') {
set_include_path(APPLICATION_PATH . PATH_SEPARATOR . get_include_path());
require_once("Bootstrap.php");
} else {
$application->bootstrap()->run();
}
} catch (Exception $e) {
if ($e->getCode() == 401)
{
header($_SERVER['SERVER_PROTOCOL'] . ' 401 Unauthorized', true, 401);
return;
}
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
Logging::error($e->getMessage());
if (VERBOSE_STACK_TRACE) {
echo $e->getMessage();
echo "<pre>";
echo $e->getTraceAsString();
echo "</pre>";
Logging::info($e->getMessage());
Logging::info($e->getTraceAsString());
} else {
Logging::info($e->getTrace());
}
throw $e;
}

View File

@ -0,0 +1,173 @@
<?php
define("DAYS_PER_WEEK", 7);
class WidgetHelper
{
public static function getWeekInfo($timezone)
{
//weekStart is in station time.
$weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime();
$dow = array("monday", "tuesday", "wednesday", "thursday", "friday",
"saturday", "sunday", "nextmonday", "nexttuesday", "nextwednesday",
"nextthursday", "nextfriday", "nextsaturday", "nextsunday");
$result = array();
// default to the station timezone
$timezone = Application_Model_Preference::GetDefaultTimezone();
$userDefinedTimezone = strtolower($timezone);
// if the timezone defined by the user exists, use that
if (array_key_exists($userDefinedTimezone, timezone_abbreviations_list())) {
$timezone = $userDefinedTimezone;
}
$utcTimezone = new DateTimeZone("UTC");
$weekStartDateTime->setTimezone($utcTimezone);
$utcDayStart = $weekStartDateTime->format("Y-m-d H:i:s");
for ($i = 0; $i < 14; $i++) {
//have to be in station timezone when adding 1 day for daylight savings.
$weekStartDateTime->setTimezone(new DateTimeZone($timezone));
$weekStartDateTime->add(new DateInterval('P1D'));
//convert back to UTC to get the actual timestamp used for search.
$weekStartDateTime->setTimezone($utcTimezone);
$utcDayEnd = $weekStartDateTime->format("Y-m-d H:i:s");
$shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd);
$utcDayStart = $utcDayEnd;
// convert to user-defined timezone, or default to station
Application_Common_DateHelper::convertTimestampsToTimezone(
$shows,
array("starts", "ends", "start_timestamp","end_timestamp"),
$timezone
);
$result[$dow[$i]] = $shows;
// XSS exploit prevention
self::convertSpecialChars($result, array("name", "url"));
// convert image paths to point to api endpoints
self::findAndConvertPaths($result);
}
return $result;
}
// Second version of this function.
// Removing "next" days and creating two weekly arrays
public static function getWeekInfoV2($timezone)
{
//weekStart is in station time.
//$weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime();
$weekStartDateTime = new DateTime("now", new DateTimeZone(Application_Model_Preference::GetTimezone()));
$maxNumOFWeeks = 2;
$result = array();
// default to the station timezone
$timezone = Application_Model_Preference::GetDefaultTimezone();
$userDefinedTimezone = strtolower($timezone);
// if the timezone defined by the user exists, use that
if (array_key_exists($userDefinedTimezone, timezone_abbreviations_list())) {
$timezone = $userDefinedTimezone;
}
$utcTimezone = new DateTimeZone("UTC");
$weekStartDateTime->setTimezone($utcTimezone);
$utcDayStart = $weekStartDateTime->format("Y-m-d H:i:s");
$weekCounter = 0;
while ($weekCounter < $maxNumOFWeeks) {
for ($dayOfWeekCounter = 0; $dayOfWeekCounter < DAYS_PER_WEEK; $dayOfWeekCounter++) {
$dateParse = date_parse($weekStartDateTime->format("Y-m-d H:i:s"));
$result[$weekCounter][$dayOfWeekCounter]["dayOfMonth"] = $dateParse["day"];
$result[$weekCounter][$dayOfWeekCounter]["dayOfWeek"] = strtoupper(date("D", $weekStartDateTime->getTimestamp()));
//have to be in station timezone when adding 1 day for daylight savings.
$weekStartDateTime->setTimezone(new DateTimeZone($timezone));
$weekStartDateTime->add(new DateInterval('P1D'));
//convert back to UTC to get the actual timestamp used for search.
$weekStartDateTime->setTimezone($utcTimezone);
$utcDayEnd = $weekStartDateTime->format("Y-m-d H:i:s");
$shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd);
$utcDayStart = $utcDayEnd;
// convert to user-defined timezone, or default to station
Application_Common_DateHelper::convertTimestampsToTimezone(
$shows,
array("starts", "ends", "start_timestamp", "end_timestamp"),
$timezone
);
foreach($shows as &$show) {
$startParseDate = date_parse($show['starts']);
$show["show_start_hour"] = str_pad($startParseDate["hour"], 2, "0", STR_PAD_LEFT).":".str_pad($startParseDate["minute"], 2, 0, STR_PAD_LEFT);
$endParseDate = date_parse($show['ends']);
$show["show_end_hour"] = str_pad($endParseDate["hour"], 2, 0, STR_PAD_LEFT).":".str_pad($endParseDate["minute"],2, 0, STR_PAD_LEFT);
}
$result[$weekCounter][$dayOfWeekCounter]["shows"] = $shows;
// XSS exploit prevention
self::convertSpecialChars($result, array("name", "url"));
// convert image paths to point to api endpoints
self::findAndConvertPaths($result);
}
$weekCounter += 1;
}
return $result;
}
/**
* Go through a given array and sanitize any potentially exploitable fields
* by passing them through htmlspecialchars
*
* @param unknown $arr the array to sanitize
* @param unknown $keys indexes of values to be sanitized
*/
public static function convertSpecialChars(&$arr, $keys)
{
foreach ($arr as &$a) {
if (is_array($a)) {
foreach ($keys as &$key) {
if (array_key_exists($key, $a)) {
$a[$key] = htmlspecialchars($a[$key]);
}
}
self::convertSpecialChars($a, $keys);
}
}
}
/**
* Recursively find image_path keys in the various $result subarrays,
* and convert them to point to the show-logo endpoint
*
* @param unknown $arr the array to search
*/
public static function findAndConvertPaths(&$arr)
{
$CC_CONFIG = Config::getConfig();
$baseDir = Application_Common_OsPath::formatDirectoryWithDirectorySeparators($CC_CONFIG['baseDir']);
foreach ($arr as &$a) {
if (is_array($a)) {
if (array_key_exists("image_path", $a)) {
$a["image_path"] = $a["image_path"] && $a["image_path"] !== '' ?
"http://".$_SERVER['HTTP_HOST'].$baseDir."api/show-logo?id=".$a["id"] : '';
} else {
self::findAndConvertPaths($a);
}
}
}
}
}

View File

@ -39,7 +39,8 @@ $ccAcl->add(new Zend_Acl_Resource('library'))
->add(new Zend_Acl_Resource('thank-you'))
->add(new Zend_Acl_Resource('provisioning'))
->add(new Zend_Acl_Resource('player'))
->add(new Zend_Acl_Resource('soundcloud'));
->add(new Zend_Acl_Resource('soundcloud'))
->add(new Zend_Acl_Resource('embeddablewidgets'));
/** Creating permissions */
$ccAcl->allow('G', 'index')
@ -74,6 +75,7 @@ $ccAcl->allow('G', 'index')
->allow('A', 'preference')
->allow('A', 'player')
->allow('A', 'soundcloud')
->allow('A', 'embeddablewidgets')
->allow('S', 'thank-you')
->allow('S', 'billing');

View File

@ -2,8 +2,6 @@
/* THIS FILE IS NOT MEANT FOR CUSTOMIZING.
* PLEASE EDIT THE FOLLOWING TO CHANGE YOUR CONFIG:
* /etc/airtime/airtime.conf
* /etc/airtime/pypo.cfg
* /etc/airtime/recorder.cfg
*/
class Config {
@ -72,9 +70,6 @@ class Config {
$CC_CONFIG['cache_ahead_hours'] = $values['general']['cache_ahead_hours'];
$CC_CONFIG['monit_user'] = $values['monit']['monit_user'];
$CC_CONFIG['monit_password'] = $values['monit']['monit_password'];
// Database config
$CC_CONFIG['dsn']['username'] = $values['database']['dbuser'];
$CC_CONFIG['dsn']['password'] = $values['database']['dbpass'];

View File

@ -0,0 +1,265 @@
<?php
/*
* We only get here after setup, or if there's an error in the configuration.
*
* Display a table to the user showing the necessary dependencies
* (both PHP and binary) and the status of any application services,
* along with steps to fix them if they're not found or misconfigured.
*/
$phpDependencies = checkPhpDependencies();
$externalServices = checkExternalServices();
$zend = $phpDependencies["zend"];
$postgres = $phpDependencies["postgres"];
$database = $externalServices["database"];
$rabbitmq = $externalServices["rabbitmq"];
$pypo = $externalServices["pypo"];
$liquidsoap = $externalServices["liquidsoap"];
$mediamonitor = $externalServices["media-monitor"];
$r1 = array_reduce($phpDependencies, "booleanReduce", true);
$r2 = array_reduce($externalServices, "booleanReduce", true);
$result = $r1 && $r2;
?>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap-3.3.1.min.css">
<link rel="stylesheet" type="text/css" href="css/setup/config-check.css">
</head>
<style>
/*
This is here because we're using the config-check css for
both this page and the system status page
*/
html {
background-color: #f5f5f5;
}
body {
padding: 2em;
min-width: 600px;
text-align: center;
margin: 3em ;
border: 1px solid lightgray;
border-radius: 5px;
}
</style>
<body>
<h2>
<img class="logo" src="css/images/airtime_logo_jp.png" /><br/>
<strong>Configuration Checklist</strong>
</h2>
<?php
if (!$result) {
?>
<br/>
<h3 class="error">Looks like something went wrong!</h3>
<p>
Take a look at the checklist below for possible solutions. If you're tried the suggestions and are
still experiencing issues, come
<a href="https://forum.sourcefabric.org/">visit our forums</a>
or <a href="http://www.sourcefabric.org/en/airtime/manuals/">check out the manual</a>.
</p>
<?php
} else {
?>
<p>
Your Airtime station is up and running! Get started by logging in with the default username and password: admin/admin
</p>
<button onclick="location = location.pathname;">Log in to Airtime!</button>
<?php
}
?>
<table class="table">
<thead>
<tr>
<th class="component">
Component
</th>
<th class="description">
<strong>Description</strong>
</th>
<th class="solution">
<strong>Status or Solution</strong>
</th>
</tr>
</thead>
</table>
<div class="checklist">
<table class="table table-striped">
<caption class="caption">
PHP Dependencies
</caption>
<tbody>
<tr class="<?=$zend ? 'success' : 'danger';?>">
<td class="component">
Zend
</td>
<td class="description">
Zend MVC Framework
</td>
<td class="solution <?php if ($zend) {echo 'check';?>">
<?php
} else {
?>">
<b>Ubuntu</b>: try running <code>sudo apt-get install libzend-framework-php</code>
<br/><b>Debian</b>: try running <code>sudo apt-get install zendframework</code>
<?php
}
?>
</td>
</tr>
<tr class="<?=$postgres ? 'success' : 'danger';?>">
<td class="component">
Postgres
</td>
<td class="description">
PDO and PostgreSQL libraries
</td>
<td class="solution <?php if ($postgres) {echo 'check';?>">
<?php
} else {
?>">
Try running <code>sudo apt-get install php5-pgsql</code>
<?php
}
?>
</td>
</tr>
</tbody>
</table>
<table class="table table-striped">
<caption class="caption">
External Services
</caption>
<tbody>
<tr class="<?=$database ? 'success' : 'danger';?>">
<td class="component">
Database
</td>
<td class="description">
Database configuration for Airtime
</td>
<td class="solution <?php if ($database) {echo 'check';?>">
<?php
} else {
?>">
Make sure you aren't missing any of the Postgres dependencies in the table above.
If your dependencies check out, make sure your database configuration settings in
<code>/etc/airtime.conf</code> are correct and the Airtime database was installed correctly.
<?php
}
?>
</td>
</tr>
<tr class="<?=$rabbitmq ? 'success' : 'danger';?>">
<td class="component">
RabbitMQ
</td>
<td class="description">
RabbitMQ configuration for Airtime
</td>
<td class="solution <?php if ($rabbitmq) {echo 'check';?>">
<?php
} else {
?>">
Make sure RabbitMQ is installed correctly, and that your settings in /etc/airtime/airtime.conf
are correct. Try using <code>sudo rabbitmqctl list_users</code> and <code>sudo rabbitmqctl list_vhosts</code>
to see if the airtime user (or your custom RabbitMQ user) exists, then checking that
<code>sudo rabbitmqctl list_exchanges</code> contains entries for airtime-media-monitor, airtime-pypo,
and airtime-uploads.
<?php
}
?>
</td>
</tr>
<tr class="<?=$mediamonitor ? 'success' : 'danger';?>">
<td class="component">
Media Monitor
</td>
<td class="description">
Airtime media-monitor service
</td>
<td class="solution <?php if ($mediamonitor) {echo 'check';?>">
<?php
} else {
?>">
Check that the airtime-media-monitor service is installed correctly in <code>/etc/init</code>,
and ensure that it's running with
<br/><code>initctl list | grep airtime-media-monitor</code><br/>
If not, try running <code>sudo service airtime-media-monitor start</code>
<?php
}
?>
</td>
</tr>
<tr class="<?=$pypo ? 'success' : 'danger';?>">
<td class="component">
Pypo
</td>
<td class="description">
Airtime playout service
</td>
<td class="solution <?php if ($pypo) {echo 'check';?>">
<?php
} else {
?>">
Check that the airtime-playout service is installed correctly in <code>/etc/init</code>,
and ensure that it's running with
<br/><code>initctl list | grep airtime-playout</code><br/>
If not, try running <code>sudo service airtime-playout restart</code>
<?php
}
?>
</td>
</tr>
<tr class="<?=$liquidsoap ? 'success' : 'danger';?>">
<td class="component">
Liquidsoap
</td>
<td class="description">
Airtime liquidsoap service
</td>
<td class="solution <?php if ($liquidsoap) {echo 'check';?>">
<?php
} else {
?>">
Check that the airtime-liquidsoap service is installed correctly in <code>/etc/init</code>,
and ensure that it's running with
<br/><code>initctl list | grep airtime-liquidsoap</code><br/>
If not, try running <code>sudo service airtime-liquidsoap restart</code>
<?php
}
?>
</td>
</tr>
</tbody>
</table>
</div>
<div class="footer">
<h3>
PHP Extension List
</h3>
<p>
<?php
global $extensions;
$first = true;
foreach ($extensions as $ext) {
if (!$first) {
echo " | ";
} else {
$first = false;
}
echo $ext;
}
?>
</p>
</div>

View File

@ -3,6 +3,8 @@
define('PRODUCT_NAME' , 'Airtime');
define('PRODUCT_SITE_URL' , 'http://airtime.sourcefabric.org');
define('SAAS_PRODUCT_BRANDING_NAME', 'Airtime Pro');
define('COMPANY_NAME' , 'Sourcefabric');
define('COMPANY_SUFFIX' , 'z.ú.');
define('COMPANY_SITE' , 'Sourcefabric.org');
@ -19,6 +21,10 @@ define('LICENSE_URL' , 'http://www.gnu.org/licenses/agpl-3.0-standalone.h
define('AIRTIME_COPYRIGHT_DATE' , '2010-2012');
define('AIRTIME_REST_VERSION' , '1.1');
define('AIRTIME_API_VERSION' , '1.1');
define('AIRTIME_CODE_VERSION' , '2.5.13');
define('DEFAULT_LOGO_PLACEHOLDER', 1);
define('DEFAULT_LOGO_FILE', 'airtime_logo.png');
// Metadata Keys for files
define('MDATA_KEY_FILEPATH' , 'filepath');

View File

@ -66,12 +66,6 @@ $pages = array(
'controller' => 'Preference',
'action' => 'stream-setting'
),
array(
'label' => _('Support Feedback'),
'module' => 'default',
'controller' => 'Preference',
'action' => 'support-setting'
),
array(
'label' => _('Status'),
'module' => 'default',
@ -87,10 +81,10 @@ $pages = array(
'resource' => 'listenerstat'
),
array(
'label' => _('Player'),
'label' => _('Widgets'),
'module' => 'default',
'controller' => 'player',
'action' => 'customize'
'controller' => 'embeddablewidgets',
'action' => 'index'
)
)
),

View File

@ -1,4 +1,5 @@
<?php
require_once('WidgetHelper.php');
require_once('TuneIn.php');
class ApiController extends Zend_Controller_Action
@ -200,7 +201,7 @@ class ApiController extends Zend_Controller_Action
}
// XSS exploit prevention
$this->convertSpecialChars($result, array("name", "url"));
WidgetHelper::convertSpecialChars($result, array("name", "url"));
// apply user-defined timezone, or default to station
Application_Common_DateHelper::convertTimestampsToTimezone(
$result['currentShow'],
@ -218,7 +219,7 @@ class ApiController extends Zend_Controller_Action
$result["timezone"] = $upcase ? strtoupper($timezone) : $timezone;
$result["timezoneOffset"] = Application_Common_DateHelper::getTimezoneOffset($timezone);
// convert image paths to point to api endpoints
$this->findAndConvertPaths($result);
WidgetHelper::findAndConvertPaths($result);
// used by caller to determine if the airtime they are running or widgets in use is out of date.
$result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION;
@ -288,11 +289,11 @@ class ApiController extends Zend_Controller_Action
$result = Application_Model_Schedule::GetPlayOrderRange($utcTimeEnd, $showsToRetrieve);
// XSS exploit prevention
$this->convertSpecialChars($result, array("name", "url"));
WidgetHelper::convertSpecialChars($result, array("name", "url"));
// apply user-defined timezone, or default to station
$this->applyLiveTimezoneAdjustments($result, $timezone, $upcase);
// convert image paths to point to api endpoints
$this->findAndConvertPaths($result);
WidgetHelper::findAndConvertPaths($result);
// used by caller to determine if the airtime they are running or widgets in use is out of date.
$result["station"]["AIRTIME_API_VERSION"] = AIRTIME_API_VERSION;
@ -366,55 +367,11 @@ class ApiController extends Zend_Controller_Action
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
//weekStart is in station time.
$weekStartDateTime = Application_Common_DateHelper::getWeekStartDateTime();
$dow = array("monday", "tuesday", "wednesday", "thursday", "friday",
"saturday", "sunday", "nextmonday", "nexttuesday", "nextwednesday",
"nextthursday", "nextfriday", "nextsaturday", "nextsunday");
$result = array();
// default to the station timezone
$timezone = Application_Model_Preference::GetDefaultTimezone();
$userDefinedTimezone = strtolower($this->getRequest()->getParam("timezone"));
// if the timezone defined by the user exists, use that
if (array_key_exists($userDefinedTimezone, timezone_abbreviations_list())) {
$timezone = $userDefinedTimezone;
}
$utcTimezone = new DateTimeZone("UTC");
$weekStartDateTime->setTimezone($utcTimezone);
$utcDayStart = $weekStartDateTime->format("Y-m-d H:i:s");
for ($i = 0; $i < 14; $i++) {
//have to be in station timezone when adding 1 day for daylight savings.
$weekStartDateTime->setTimezone(new DateTimeZone($timezone));
$weekStartDateTime->add(new DateInterval('P1D'));
//convert back to UTC to get the actual timestamp used for search.
$weekStartDateTime->setTimezone($utcTimezone);
$utcDayEnd = $weekStartDateTime->format("Y-m-d H:i:s");
$shows = Application_Model_Show::getNextShows($utcDayStart, "ALL", $utcDayEnd);
$utcDayStart = $utcDayEnd;
// convert to user-defined timezone, or default to station
Application_Common_DateHelper::convertTimestampsToTimezone(
$shows,
array("starts", "ends", "start_timestamp","end_timestamp"),
$timezone
);
$result[$dow[$i]] = $shows;
}
// XSS exploit prevention
$this->convertSpecialChars($result, array("name", "url"));
// convert image paths to point to api endpoints
$this->findAndConvertPaths($result);
$result = WidgetHelper::getWeekInfo($this->getRequest()->getParam("timezone"));
//used by caller to determine if the airtime they are running or widgets in use is out of date.
$result['AIRTIME_API_VERSION'] = AIRTIME_API_VERSION;
header("Content-type: text/javascript");
if (version_compare(phpversion(), '5.4.0', '<')) {
@ -431,50 +388,6 @@ class ApiController extends Zend_Controller_Action
}
}
/**
* Go through a given array and sanitize any potentially exploitable fields
* by passing them through htmlspecialchars
*
* @param array $arr the array to sanitize
* @param array $keys indexes of values to be sanitized
*/
private function convertSpecialChars(&$arr, $keys)
{
foreach ($arr as &$a) {
if (is_array($a)) {
foreach ($keys as &$key) {
if (array_key_exists($key, $a)) {
$a[$key] = htmlspecialchars($a[$key]);
}
}
$this->convertSpecialChars($a, $keys);
}
}
}
/**
* Recursively find image_path keys in the various $result subarrays,
* and convert them to point to the show-logo endpoint
*
* @param array $arr the array to search
*/
private function findAndConvertPaths(&$arr)
{
$CC_CONFIG = Config::getConfig();
$baseDir = Application_Common_OsPath::formatDirectoryWithDirectorySeparators($CC_CONFIG['baseDir']);
foreach ($arr as &$a) {
if (is_array($a)) {
if (array_key_exists("image_path", $a)) {
$a["image_path"] = $a["image_path"] && $a["image_path"] !== '' ?
"http://".$_SERVER['HTTP_HOST'].$baseDir."api/show-logo?id=".$a["id"] : '';
} else {
$this->findAndConvertPaths($a);
}
}
}
}
/**
* API endpoint to display the show logo
*/

View File

@ -1,4 +1,5 @@
<?php
require_once('WidgetHelper.php');
class EmbedController extends Zend_Controller_Action
{
@ -23,13 +24,21 @@ class EmbedController extends Zend_Controller_Action
$request = $this->getRequest();
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/player.css?".$CC_CONFIG['airtime_version'];
$this->view->mrp_js = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/mrp.js?".$CC_CONFIG['airtime_version'];
$this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "js/libs/jquery-1.10.2.js";
$this->view->muses_swf = Application_Common_HTTPHelper::getStationUrl() . "js/airtime/player/muses.swf";
$this->view->metadata_api_url = Application_Common_HTTPHelper::getStationUrl() . "api/live-info";
$this->view->player_title = json_encode($request->getParam('title'));
$styleParam = $request->getParam('style');
$player_style = isset($styleParam) ? $styleParam : "basic";
if ($player_style == "premium") {
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/radio-page/premium_player.css?".$CC_CONFIG['airtime_version'];
} else {
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "css/player.css?".$CC_CONFIG['airtime_version'];
}
$this->view->player_style = $player_style;
$stream = $request->getParam('stream');
$streamData = Application_Model_StreamSetting::getEnabledStreamData();
$availableMobileStreams = array();
@ -54,4 +63,44 @@ class EmbedController extends Zend_Controller_Action
$this->view->availableMobileStreams = json_encode($availableMobileStreams);
$this->view->availableDesktopStreams = json_encode($availableDesktopStreams);
}
public function currentDayProgramAction()
{
$this->view->layout()->disableLayout();
$CC_CONFIG = Config::getConfig();
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "widgets/css/airtime-widgets.css?".$CC_CONFIG['airtime_version'];
$this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "widgets/js/jquery-1.6.1.min.js?".$CC_CONFIG['airtime_version'];
$this->view->jquery_custom = Application_Common_HTTPHelper::getStationUrl() . "widgets/js/jquery-ui-1.8.10.custom.min.js?".$CC_CONFIG['airtime_version'];
$this->view->widget_js = Application_Common_HTTPHelper::getStationUrl() . "widgets/js/jquery.showinfo.js?".$CC_CONFIG['airtime_version'];
}
public function weeklyProgramAction()
{
$this->view->layout()->disableLayout();
$CC_CONFIG = Config::getConfig();
$request = $this->getRequest();
$widgetStyle = $request->getParam('style');
if ($widgetStyle == "premium") {
$this->view->widgetStyle = "premium";
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "/css/embed/weekly-schedule-widget.css?" . $CC_CONFIG['airtime_version'];
} else {
$this->view->widgetStyle = "basic";
$this->view->css = Application_Common_HTTPHelper::getStationUrl() . "/css/embed/weekly-schedule-widget-basic.css?" . $CC_CONFIG['airtime_version'];
}
$this->view->jquery = Application_Common_HTTPHelper::getStationUrl() . "widgets/js/jquery-1.6.1.min.js?".$CC_CONFIG['airtime_version'];
$weeklyScheduleData = WidgetHelper::getWeekInfoV2($this->getRequest()->getParam("timezone"));
// Return only the current week's schedule data. In the future we may use the next week's data.
$this->view->weeklyScheduleData = $weeklyScheduleData[0];
$currentDay = new DateTime("now", new DateTimeZone(Application_Model_Preference::GetTimezone()));
//day of the month without leading zeros (1 to 31)
$this->view->currentDayOfMonth = $currentDay->format("j");
}
}

View File

@ -1,13 +1,14 @@
<?php
class PlayerController extends Zend_Controller_Action
class EmbeddableWidgetsController extends Zend_Controller_Action
{
public function init()
{
}
public function customizeAction()
public function indexAction()
{
$CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir();
@ -20,12 +21,17 @@ class PlayerController extends Zend_Controller_Action
$numEnabledStreams = $form->getElement('player_stream_url')->getAttrib('numberOfEnabledStreams');
if ($numEnabledStreams > 0 && $apiEnabled) {
$this->view->form = $form;
$this->view->player_form = $form;
} else {
$this->view->errorMsg = "To configure and use the embeddable player you must:<br><br>
$this->view->player_error_msg = _("To configure and use the embeddable player you must:<br><br>
1. Enable at least one MP3, AAC, or OGG stream under System -> Streams<br>
2. Enable the Public Airtime API under System -> Preferences";
2. Enable the Public Airtime API under System -> Preferences");
}
if (!$apiEnabled) {
$this->view->weekly_schedule_error_msg = _("To use the embeddable weekly schedule you must:<br><br>
1. Enable the Public Airtime API under System -> Preferences");
}
}
}

View File

@ -38,9 +38,12 @@ class ErrorController extends Zend_Controller_Action {
}
// Log exception, if logger available
/* No idea why this doesn't work or why it was implemented like this. Disabling it -- Albert
if (($log = $this->getLog())) {
$log->crit($this->view->message, $errors->exception);
}
}*/
//Logging that actually works: -- Albert
Logging::error($this->view->message . ": " . $errors->exception);
// conditionally display exceptions
if ($this->getInvokeArg('displayExceptions') == true) {

View File

@ -10,7 +10,24 @@ class IndexController extends Zend_Controller_Action
public function indexAction()
{
$this->_redirect('Showbuilder');
$CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headLink()->setStylesheet($baseUrl.'css/radio-page/radio-page.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/embed/weekly-schedule-widget.css?'.$CC_CONFIG['airtime_version']);
$this->_helper->layout->setLayout('radio-page');
$this->view->stationLogo = Application_Model_Preference::GetStationLogo();
$stationName = Application_Model_Preference::GetStationName();
$this->view->stationName = $stationName;
$stationDescription = Application_Model_Preference::GetStationDescription();
$this->view->stationDescription = $stationDescription;
$this->view->stationUrl = Application_Common_HTTPHelper::getStationUrl();
$this->view->baseUrl = Application_Common_OsPath::getBaseDir();
}
public function mainAction()

View File

@ -13,18 +13,9 @@ class SystemstatusController extends Zend_Controller_Action
public function indexAction()
{
/*
$services = array(
"pypo"=>Application_Model_Systemstatus::GetPypoStatus(),
"liquidsoap"=>Application_Model_Systemstatus::GetLiquidsoapStatus(),
//"media-monitor"=>Application_Model_Systemstatus::GetMediaMonitorStatus(),
);
*/
$partitions = Application_Model_Systemstatus::GetDiskInfo();
$this->view->status = new StdClass;
//$this->view->status->services = $services;
$this->view->status->partitions = $partitions;
}
}

View File

@ -13,33 +13,18 @@ class UpgradeController extends Zend_Controller_Action
return;
}
// Get all upgrades dynamically (in declaration order!) so we don't have to add them explicitly each time
// TODO: explicitly sort classnames by ascending version suffix for safety
$upgraders = getUpgrades();
try {
$upgradeManager = new UpgradeManager();
$didWePerformAnUpgrade = $upgradeManager->doUpgrade();
$didWePerformAnUpgrade = false;
try
{
foreach ($upgraders as $upgrader)
{
/** @var $upgrader AirtimeUpgrader */
$upgrader = new $upgrader();
if ($upgrader->checkIfUpgradeSupported())
{
// pass __DIR__ to the upgrades, since __DIR__ returns parent dir of file, not executor
$upgrader->upgrade(__DIR__); //This will throw an exception if the upgrade fails.
$didWePerformAnUpgrade = true;
if (!$didWePerformAnUpgrade) {
$this->getResponse()
->setHttpResponseCode(200)
->appendBody("Upgrade to Airtime " . $upgrader->getNewVersion() . " OK<br>");
}
}
if (!$didWePerformAnUpgrade)
{
->appendBody("No upgrade was performed. The current schema version is " . Application_Model_Preference::GetSchemaVersion() . ".<br>");
} else {
$this->getResponse()
->setHttpResponseCode(200)
->appendBody("No upgrade was performed. The current Airtime version is " . AirtimeUpgrader::getCurrentVersion() . ".<br>");
->appendBody("Upgrade to Airtime schema version " . Application_Model_Preference::GetSchemaVersion() . " OK<br>");
}
}
catch (Exception $e)

View File

@ -102,7 +102,6 @@ class UserController extends Zend_Controller_Action
public function getHostsAction()
{
$search = $this->_getParam('term');
$res = Application_Model_User::getHosts($search);
$this->view->hosts = Application_Model_User::getHosts($search);
}

View File

@ -139,12 +139,15 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
}
else //Non-REST, regular Airtime web app requests
{
//Redirect you to the login screen since you have no session.
if ($controller !== 'login') {
// Redirect user to the landing page if they are trying to
// access a resource that requires a valid session.
// Skip the redirection if they are already on the landing page
// or the login page.
if ($controller !== 'index' && $controller !== 'login') {
if ($request->isXmlHttpRequest()) {
$url = 'http://'.$request->getHttpHost().'/login';
$url = 'http://'.$request->getHttpHost().'/';
$json = Zend_Json::encode(array('auth' => false, 'url' => $url));
// Prepare response
@ -157,7 +160,7 @@ class Zend_Controller_Plugin_Acl extends Zend_Controller_Plugin_Abstract
Zend_Controller_Action_HelperBroker::getStaticHelper('redirector')->redirectAndExit();
} else {
$r = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
$r->gotoSimpleAndExit('index', 'login', $request->getModuleName());
$r->gotoSimpleAndExit('index', 'index', $request->getModuleName());
}
}
}

View File

@ -0,0 +1,6 @@
-- Replacing system_version with schema_version
DELETE FROM cc_pref WHERE keystr = 'system_version';
INSERT INTO cc_pref (keystr, valstr) VALUES ('schema_version', '2.5.2');
ALTER TABLE cc_show ADD COLUMN image_path varchar(255) DEFAULT '';
ALTER TABLE cc_show_instances ADD COLUMN description varchar(255) DEFAULT '';

View File

@ -0,0 +1,17 @@
<?php echo $this->doctype() ?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href='http://fonts.googleapis.com/css?family=Roboto:400,100,300,700' rel='stylesheet' type='text/css'>
<?php echo $this->headTitle() ?>
<?php echo $this->headLink() ?>
<?php echo $this->headScript() ?>
</head>
<body>
<?php echo $this->layout()->content ?>
</body>
</html>

View File

@ -427,7 +427,12 @@ class Application_Model_Preference
public static function GetStationDescription()
{
return self::getValue("description");
$description = self::getValue("description");
if (!empty($description)) {
return $description;
} else {
return sprintf(_("Powered by %s"), SAAS_PRODUCT_BRANDING_NAME);
}
}
// Sets station default timezone (from preferences)
@ -531,7 +536,14 @@ class Application_Model_Preference
public static function GetStationLogo()
{
return self::getValue("logoImage");
$logoImage = self::getValue("logoImage");
if (!empty($logoImage)) {
return $logoImage;
} else {
// We return the Airtime logo if no logo is set in the database.
// airtime_logo.png is stored under the public directory
return DEFAULT_LOGO_PLACEHOLDER;
}
}
public static function SetUniqueId($id)
@ -792,9 +804,29 @@ class Application_Model_Preference
return self::getValue("enable_stream_conf");
}
public static function SetAirtimeVersion($version)
public static function GetSchemaVersion()
{
self::setValue("system_version", $version);
CcPrefPeer::clearInstancePool(); //Ensure we don't get a cached Propel object (cached DB results)
//because we're updating this version number within this HTTP request as well.
//New versions use schema_version
$pref = CcPrefQuery::create()
->filterByKeystr('schema_version')
->findOne();
if (empty($pref)) {
//Pre-2.5.2 releases all used this ambiguous "system_version" key to represent both the code and schema versions...
$pref = CcPrefQuery::create()
->filterByKeystr('system_version')
->findOne();
}
$schemaVersion = $pref->getValStr();
return $schemaVersion;
}
public static function SetSchemaVersion($version)
{
self::setValue("schema_version", $version);
}
public static function GetAirtimeVersion()

View File

@ -268,6 +268,7 @@ SQL;
$nextMedia = CcScheduleQuery::create()
->filterByDbStarts($currentMedia["starts"], Criteria::GREATER_THAN)
->filterByDbId($currentMedia["id"], Criteria::NOT_EQUAL)
->filterByDbPlayoutStatus(0, Criteria::GREATER_THAN)
->orderByDbStarts(Criteria::ASC)
->findOne();
if (isset($nextMedia)) {

View File

@ -1345,7 +1345,6 @@ SQL;
"starts" => $rows[$previousShowIndex]['starts'],
"ends" => $rows[$previousShowIndex]['ends'],
"record" => $rows[$previousShowIndex]['record'],
"image_path" => $rows[$previousShowIndex]['image_path'],
"type" => "show");
}

View File

@ -1027,14 +1027,16 @@ SQL;
$LIQUIDSOAP_ERRORS = array('TagLib: MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.');
// Ask Liquidsoap if file is playable
$ls_command = sprintf('/usr/bin/airtime-liquidsoap -v -c "output.dummy(audio_to_stereo(single(%s)))" 2>&1',
/* CC-5990/5991 - Changed to point directly to liquidsoap, removed PATH export */
$command = sprintf('liquidsoap -v -c "output.dummy(audio_to_stereo(single(%s)))" 2>&1',
escapeshellarg($audio_file));
$command = "export PATH=/usr/local/bin:/usr/bin:/bin/usr/bin/ && $ls_command";
exec($command, $output, $rv);
$isError = count($output) > 0 && in_array($output[0], $LIQUIDSOAP_ERRORS);
Logging::info("Is error?! : " . $isError);
Logging::info("ls playability response: " . $rv);
return ($rv == 0 && !$isError);
}

View File

@ -75,8 +75,13 @@ class Application_Model_StreamSetting
$host = $streamData[$prefix."host"];
$port = $streamData[$prefix."port"];
$mount = $streamData[$prefix."mount"];
if ($streamData[$prefix."output"] == "shoutcast") {
$url = "http://$host:$port/";
} else { //Icecast
$url = "http://$host:$port/$mount";
}
$streams[$id] = Array(
"url" => "http://$host:$port/$mount",
"url" => $url,
"codec" => $streamData[$prefix."type"],
"bitrate" => $streamData[$prefix."bitrate"],
"mobile" => $streamData[$prefix."mobile"]

View File

@ -6,15 +6,15 @@ class Application_Model_Systemstatus
public static function GetMonitStatus($p_ip)
{
$CC_CONFIG = Config::getConfig();
$monit_user = $CC_CONFIG['monit_user'];
$monit_password = $CC_CONFIG['monit_password'];
// $monit_user = $CC_CONFIG['monit_user'];
// $monit_password = $CC_CONFIG['monit_password'];
$url = "http://$p_ip:2812/_status?format=xml";
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_USERPWD, "$monit_user:$monit_password");
// curl_setopt($ch, CURLOPT_USERPWD, "$monit_user:$monit_password");
//wait a max of 3 seconds before aborting connection attempt
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
$result = curl_exec($ch);

View File

@ -282,7 +282,6 @@ class Application_Model_User
$con = Propel::getConnection();
$sql_gen = "SELECT login AS value, login AS label, id as index FROM cc_subjs ";
$sql = $sql_gen;
$types = array();
$params = array();
@ -296,13 +295,8 @@ class Application_Model_User
$sql = $sql_gen ." WHERE (". $sql_type.") ";
if (!is_null($search)) {
//need to use addslashes for 'LIKE' values
$search = addslashes($search);
$like = "login ILIKE '%{$search}%'";
$sql = $sql . " AND ".$like;
}
$sql .= " AND login ILIKE :search";
$params[":search"] = "%$search%";
$sql = $sql ." ORDER BY login";

View File

@ -20,22 +20,73 @@ function getUpgrades() {
return array_filter(get_declared_classes(), "isUpgrade");
}
class UpgradeManager
{
/** Used to determine if the database schema needs an upgrade in order for this version of the Airtime codebase to work correctly.
* @return array A list of schema versions that this version of the codebase supports.
*/
public static function getSupportedSchemaVersions()
{
//What versions of the schema does the code support today:
return array(AIRTIME_CODE_VERSION);
}
public static function checkIfUpgradeIsNeeded()
{
$schemaVersion = Application_Model_Preference::GetSchemaVersion();
$supportedSchemaVersions = self::getSupportedSchemaVersions();
$upgradeNeeded = !in_array($schemaVersion, $supportedSchemaVersions);
// We shouldn't run the upgrade as a side-effect of this function!
/*
if ($upgradeNeeded) {
self::doUpgrade();
}
*/
}
public function doUpgrade()
{
$didWePerformAnUpgrade = false;
// Get all upgrades dynamically (in declaration order!) so we don't have to add them explicitly each time
// TODO: explicitly sort classnames by ascending version suffix for safety
$upgraders = getUpgrades();
return $this->runUpgrades($upgraders, (dirname(__DIR__) . "/controllers"));
}
/**
* Run a given set of upgrades
*
* @param array $upgraders the upgrades to perform
* @param string $dir the directory containing the upgrade sql
* @return boolean whether or not an upgrade was performed
*/
public function runUpgrades($upgraders, $dir) {
$upgradePerformed = false;
foreach ($upgraders as $upgrader) {
/** @var $upgrader AirtimeUpgrader */
$upgrader = new $upgrader();
if ($upgrader->checkIfUpgradeSupported())
{
// pass the given directory to the upgrades, since __DIR__ returns parent dir of file, not executor
$upgrader->upgrade($dir); //This will throw an exception if the upgrade fails.
$didWePerformAnUpgrade = true;
}
}
return $upgradePerformed;
}
}
abstract class AirtimeUpgrader
{
/** Versions that this upgrader class can upgrade from (an array of version strings). */
abstract protected function getSupportedVersions();
/** The version that this upgrader class will upgrade to. (returns a version string) */
/** Schema versions that this upgrader class can upgrade from (an array of version strings). */
abstract protected function getSupportedSchemaVersions();
/** The schema version that this upgrader class will upgrade to. (returns a version string) */
abstract public function getNewVersion();
public static function getCurrentVersion()
public static function getCurrentSchemaVersion()
{
CcPrefPeer::clearInstancePool(); //Ensure we don't get a cached Propel object (cached DB results)
//because we're updating this version number within this HTTP request as well.
$pref = CcPrefQuery::create()
->filterByKeystr('system_version')
->findOne();
$airtime_version = $pref->getValStr();
return $airtime_version;
return Application_Model_Preference::GetSchemaVersion();
}
/**
@ -44,7 +95,7 @@ abstract class AirtimeUpgrader
*/
public function checkIfUpgradeSupported()
{
if (!in_array(AirtimeUpgrader::getCurrentVersion(), $this->getSupportedVersions())) {
if (!in_array(AirtimeUpgrader::getCurrentSchemaVersion(), $this->getSupportedSchemaVersions())) {
return false;
}
return true;
@ -78,9 +129,10 @@ abstract class AirtimeUpgrader
class AirtimeUpgrader253 extends AirtimeUpgrader
{
protected function getSupportedVersions()
protected function getSupportedSchemaVersions()
{
return array('2.5.1', '2.5.2');
}
public function getNewVersion()
{
@ -129,8 +181,8 @@ class AirtimeUpgrader253 extends AirtimeUpgrader
$database = $values['database']['dbname'];
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_".$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\"");
Application_Model_Preference::SetSchemaVersion($this->getNewVersion());
Application_Model_Preference::SetAirtimeVersion($this->getNewVersion());
//clear out the cache
Cache::clear();
@ -145,7 +197,7 @@ class AirtimeUpgrader253 extends AirtimeUpgrader
class AirtimeUpgrader254 extends AirtimeUpgrader
{
protected function getSupportedVersions()
protected function getSupportedSchemaVersions()
{
return array('2.5.3');
}
@ -215,7 +267,7 @@ class AirtimeUpgrader254 extends AirtimeUpgrader
}
//$con->commit();
Application_Model_Preference::SetAirtimeVersion($newVersion);
Application_Model_Preference::SetSchemaVersion($newVersion);
Cache::clear();
$this->toggleMaintenanceScreen(false);
@ -231,7 +283,7 @@ class AirtimeUpgrader254 extends AirtimeUpgrader
}
class AirtimeUpgrader255 extends AirtimeUpgrader {
protected function getSupportedVersions() {
protected function getSupportedSchemaVersions() {
return array (
'2.5.4'
);
@ -263,7 +315,7 @@ class AirtimeUpgrader255 extends AirtimeUpgrader {
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_"
.$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\"");
Application_Model_Preference::SetAirtimeVersion($newVersion);
Application_Model_Preference::SetSchemaVersion($newVersion);
Cache::clear();
$this->toggleMaintenanceScreen(false);
@ -277,7 +329,7 @@ class AirtimeUpgrader255 extends AirtimeUpgrader {
}
class AirtimeUpgrader259 extends AirtimeUpgrader {
protected function getSupportedVersions() {
protected function getSupportedSchemaVersions() {
return array (
'2.5.5'
);
@ -309,7 +361,7 @@ class AirtimeUpgrader259 extends AirtimeUpgrader {
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_"
.$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\"");
Application_Model_Preference::SetAirtimeVersion($newVersion);
Application_Model_Preference::SetSchemaVersion($newVersion);
Cache::clear();
$this->toggleMaintenanceScreen(false);
@ -322,7 +374,7 @@ class AirtimeUpgrader259 extends AirtimeUpgrader {
class AirtimeUpgrader2510 extends AirtimeUpgrader
{
protected function getSupportedVersions() {
protected function getSupportedSchemaVersions() {
return array (
'2.5.9'
);
@ -354,7 +406,7 @@ class AirtimeUpgrader2510 extends AirtimeUpgrader
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_"
.$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\"");
Application_Model_Preference::SetAirtimeVersion($newVersion);
Application_Model_Preference::SetSchemaVersion($newVersion);
Cache::clear();
$this->toggleMaintenanceScreen(false);
@ -367,7 +419,7 @@ class AirtimeUpgrader2510 extends AirtimeUpgrader
class AirtimeUpgrader2511 extends AirtimeUpgrader
{
protected function getSupportedVersions() {
protected function getSupportedSchemaVersions() {
return array (
'2.5.10'
);
@ -395,7 +447,7 @@ class AirtimeUpgrader2511 extends AirtimeUpgrader
$disk_usage = $queryResult[0];
Application_Model_Preference::setDiskUsage($disk_usage);
Application_Model_Preference::SetAirtimeVersion($newVersion);
Application_Model_Preference::SetSchemaVersion($newVersion);
Cache::clear();
$this->toggleMaintenanceScreen(false);
@ -411,7 +463,7 @@ class AirtimeUpgrader2511 extends AirtimeUpgrader
class AirtimeUpgrader2512 extends AirtimeUpgrader
{
protected function getSupportedVersions() {
protected function getSupportedSchemaVersions() {
return array (
'2.5.10',
'2.5.11'
@ -444,7 +496,7 @@ class AirtimeUpgrader2512 extends AirtimeUpgrader
passthru("export PGPASSWORD=$password && psql -h $host -U $username -q -f $dir/upgrade_sql/airtime_"
.$this->getNewVersion()."/upgrade.sql $database 2>&1 | grep -v -E \"will create implicit sequence|will create implicit index\"");
Application_Model_Preference::SetAirtimeVersion($newVersion);
Application_Model_Preference::SetSchemaVersion($newVersion);
Cache::clear();
$this->toggleMaintenanceScreen(false);

View File

@ -11,7 +11,7 @@ echo sprintf(_('%1$s %2$s, the open radio software for scheduling and remote sta
$this->airtime_version)
?>
<br />
<br />© 2013
<br /<?php echo(gmdate('Y')); ?>
<?php
$companySiteAnchor = "<a href='" . COMPANY_SITE_URL . "' target='_blank'>"
. COMPANY_NAME . " " . COMPANY_SUFFIX

View File

@ -0,0 +1,26 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="<?php echo $this->css?>" type="text/css">
<script src="<?php echo $this->jquery ?>" type="text/javascript"></script>
<script src="<?php echo $this->jquery_custom ?>" type="text/javascript"></script>
<script src="<?php echo $this->widget_js ?>" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#onAirToday").airtimeShowSchedule({
sourceDomain: "http://localhost",
text: {onAirToday:"On air today"},
updatePeriod: 5, //seconds
showLimit: 10
});
});
</script>
</head>
<body>
<div id="onAirToday"></div>
</body>
</html>

View File

@ -237,6 +237,18 @@
document.getElementById("stop_button").classList.toggle("hide_button");
}
$(document).ready(function() {
$(".play").click(function () {
if ($(this).hasClass("pause")) {
musesPlayer.stop();
} else {
musesPlayer.play();
}
$(this).toggleClass("pause");
});
});
// variables for updating the player's metadata
var time_to_next_track_starts = 0;
var metadataTimer = null;
@ -252,11 +264,11 @@
success: function(data) {
if (data.current === null) {
if (data.currentShow != null && data.currentShow.length != 0) {
// Master/show source have no current track but they do have a current show.
$("p.now_playing").html(data.currentShow[0].name);
if (data.currentShow != null && data.currentShow.length > 0) {
//Master/show source have no current track but they do have a current show.
$("p.now_playing").html("On Air:" + "<span>" + data.currentShow[0].name + "</span>");
} else {
$("p.now_playing").html("Offline");
$("p.now_playing").html("Off Air" + "<span>Offline</span>");
}
time_to_next_track_starts = 20000;
} else {
@ -294,6 +306,7 @@
metadataTimer = setTimeout(attachStreamMetadataToPlayer, time_to_next_track_starts+3000);
}
</script>
<style type="text/css">
@ -310,7 +323,8 @@
<body>
<div class="airtime_player">
<div id="player" <?php if ($this->player_style == "basic") echo "style='display:block;'"; else echo "style='display:none'"; ?>>
<div class="airtime_player">
<div class="airtime_header">
<p class="station_name">fff</p>
@ -336,6 +350,18 @@
</div>
<a class="airtime_pro" target="_blank" href="https://www.airtime.pro/">Powered by <span class="airtime-pro-orange">Airtime Pro</span></a>
</div>
</div>
<div id="premium_player" <?php if ($this->player_style == "premium") echo "style='display:block;'"; else echo "style='display:none'"; ?>>
<div class="bottom_bar">
<div class="play cont_btn"></div>
<div class="on_air">
<p class="now_playing"></p>
</div>
</div>
</div>
<div id="muses_skin">

View File

@ -0,0 +1,61 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" href="<?php echo $this->css?>" type="text/css">
<script src="<?php echo $this->jquery ?>" type="text/javascript"></script>
<script src="<?php echo $this->jquery_custom ?>" type="text/javascript"></script>
<script src="<?php echo $this->widget_js ?>" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('.tabs li').click(function(){
var tab_id = $(this).attr('data-tab');
$('.tabs li').removeClass('active');
$('.schedule_item').removeClass('active');
$(this).addClass('active');
$("#"+tab_id).addClass('active');
});
});
</script>
</head>
<body>
<div class="schedule tab_content current">
<ul class="tabs">
<?php
foreach($this->weeklyScheduleData as $day => $data) {
$activeClass = $this->currentDayOfMonth == $data["dayOfMonth"] ? "active" : "";
echo "<li class='".$activeClass."' data-tab='day-".$data["dayOfMonth"]."'>" . $data["dayOfWeek"] . "<span>" . $data["dayOfMonth"] . "</span></li>";
}?>
</ul>
<div class="schedule_content">
<?php
foreach($this->weeklyScheduleData as $day => $data) {
$activeClass = $this->currentDayOfMonth == $data["dayOfMonth"] ? "active" : "";
echo "<div id='day-".$data["dayOfMonth"]."' class='schedule_item ".$activeClass."'>";
foreach ($data["shows"] as $show => $showData) {
echo "<div class='row'>";
echo "<div class='time_grid'>".$showData["show_start_hour"].' - '.$showData["show_end_hour"]."</div>";
echo "<div class='name_grid'>".$showData["name"]."</div>";
echo "</div>";
}
echo "</div>";
}?>
</div>
<div class="weekly-schedule-widget-footer" <?php if ($this->widgetStyle == "premium") echo "style='display:none'"; ?>>
<a href="https://airtime.pro" target="_blank">Powered by <span>Airtime Pro</span></a>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,45 @@
<div id="widget-menu" class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong ">
<h2><?php echo _("Widgets:"); ?></h2>
<a href="#player-widget">Player</a>
<a href="#weekly-schedule-widget">Weekly Schedule</a>
</div>
<div id="player-widget" class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong ">
<?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?>
<form method="post" id="player_form" enctype="multipart/form-data">
<h2 style="float:left"><?php echo _("Player") ?></h2>
<div style="clear:both"></div>
<?php echo $this->player_error_msg; ?>
<?php echo $this->player_form; ?>
</form>
</div>
<br>
<div id="weekly-schedule-widget" class="ui-widget ui-widget-content block-shadow simple-formblock clearfix padded-strong ">
<?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?>
<h2 style="float:left"><?php echo _("Weekly Schedule") ?></h2>
<div style="clear:both"></div>
<div id="weekly-schedule-widget-error">
<?php echo $this->weekly_schedule_error_msg; ?>
</div>
<div id="weekly-schedule-widget-preview" <?php if (isset($this->weekly_schedule_error_msg)) echo "style=display:none"; ?>>
<label>Preview:</label>
<div class="blah">
<iframe id="schedule_iframe" height="400px" width="550px" scrolling="no" frameborder="0" src=<?php echo $this->stationUrl."embed/weekly-program"?>></iframe>
</div>
<div style="clear:both"></div>
<label>Embeddable Code:</label>
<textarea style="width:100%" rows="3" readonly="readonly"><iframe height="400px" width="550px" scrolling="no" frameborder="0" src=<?php echo $this->stationUrl."embed/weekly-program"?>></iframe>
</textarea>
Copy this code and paste it into your website's HTML to embed the weekly schedule in your site.
Adjust the height and width attributes to your desired size.
</div>
</div>

View File

@ -12,7 +12,15 @@
<!-- <button id="logo-remove-btn" value="Remove" class="btn">Remove</button>-->
<div id="Logo-img-container">
<img onError="this.onerror = '';this.style.visibility='hidden';$('#logo-remove-btn').hide();$('[id^=stationLogoRemove]').each(function(i,v){v.style.width=v.style.height=v.style.margin=v.style.padding='0px';});" id="logo-img" onload='resizeImg(this, 450, 450);' src="data:image/png;base64,<?php echo $this->element->getView()->logoImg ?>" />
<?php
$logoImg = $this->element->getView()->logoImg;
if ($logoImg === DEFAULT_LOGO_PLACEHOLDER) {
$src = DEFAULT_LOGO_FILE;
} else {
$src = "data:image/png;base64,".$logoImg;
}
?>
<img onError="this.onerror = '';this.style.visibility='hidden';$('#logo-remove-btn').hide();$('[id^=stationLogoRemove]').each(function(i,v){v.style.width=v.style.height=v.style.margin=v.style.padding='0px';});" id="logo-img" onload='resizeImg(this, 450, 450);' src="<?php echo $src ?>" />
</div>
<?php echo $this->element->getElement('locale')->render() ?>

View File

@ -0,0 +1,66 @@
<div class="wrapper">
<div class="bck_cover"></div>
<?php if ($this->stationLogo === DEFAULT_LOGO_PLACEHOLDER) {
echo "<a href='#' class='logo'><img src='".$this->baseUrl.DEFAULT_LOGO_FILE."'></a>";
} else {
echo "<a href='#' class='logo'><img src='data:image/png;base64," . $this->stationLogo . "'></a>";
} ?>
<div id="tab-1" class="schedule tab_content current">
<iframe id="schedule_iframe" height="400px" scrolling="no" frameborder="0" src=<?php echo $this->stationUrl."embed/weekly-program?style=premium"?>></iframe>
</div>
<div id="tab-2" class="about_us tab_content">
<h1><?php echo $this->stationName ?></h1>
<p><?php echo $this->stationDescription ?></p>
</div>
<div id="tab-3" class="login tab_content">
<h2>Login</h2>
<form action="demo_form.asp">
<input type="text" name="FirstName" value="username"><br>
<input type="text" name="Password" value="password"><br>
<input type="submit" value="Login">
</form>
</div>
<iframe id="player_iframe" frameborder="0" width="100%" style="bottom:0px; left:0px; position:fixed; right:0px;" src=<?php echo $this->stationUrl."embed/player?stream=auto&style=premium";?>></iframe>
</div>
<script type="text/javascript">
$(document).ready(function(){
$("#player_iframe").load(function () {
$("#player_iframe").contents().find('.bottom_bar').append("<a class='login_btn button' target='_parent' href='/login'></a>");
$("#player_iframe").contents().find('.bottom_bar').append("<div class='about_us_btn button' data-tab='tab-2'></div>");
$("#player_iframe").contents().find('.bottom_bar').append("<div class='schedule_btn button current' data-tab='tab-1'></div>");
$("#player_iframe").contents().find('.bottom_bar .button').not('.login_btn').click(function(){
var tab_id = $(this).attr('data-tab');
$("#player_iframe").contents().find('.bottom_bar .button').removeClass('current');
$('.tab_content').removeClass('current');
$(this).addClass('current');
$("#"+tab_id).addClass('current');
});
});
// schedule widget tabs
$('.tabs li').click(function(){
var tab_id = $(this).attr('data-tab');
$('.tabs li').removeClass('active');
$('.schedule_item').removeClass('active');
$(this).addClass('active');
$("#"+tab_id).addClass('active');
});
})
</script>

View File

@ -1,12 +0,0 @@
<div class="ui-widget ui-widget-content block-shadow simple-formblock embed-player-form clearfix padded-strong ">
<?php $baseUrl = Application_Common_OsPath::getBaseDir(); ?>
<form method="post" id="player_form" enctype="multipart/form-data">
<h2 style="float:left"><?php echo _("Embeddable Player") ?></h2>
<div style="clear:both"></div>
<?php echo $this->errorMsg; ?>
<?php echo $this->form; ?>
</form>
</div>

View File

@ -1,8 +1,178 @@
<table width="60%" cellpadding="0" cellspacing="0" border="0" class="statustable">
<head>
<link rel="stylesheet" type="text/css" href="css/setup/config-check.css">
</head>
<?php
$phpDependencies = checkPhpDependencies();
$externalServices = checkExternalServices();
$zend = $phpDependencies["zend"];
$postgres = $phpDependencies["postgres"];
$database = $externalServices["database"];
$rabbitmq = $externalServices["rabbitmq"];
$pypo = $externalServices["pypo"];
$liquidsoap = $externalServices["liquidsoap"];
$mediamonitor = $externalServices["media-monitor"];
$r1 = array_reduce($phpDependencies, "booleanReduce", true);
$r2 = array_reduce($externalServices, "booleanReduce", true);
$result = $r1 && $r2;
?>
<table width="60%" cellpadding="0" cellspacing="0" border="0" class="statustable">
<thead>
<tr class="ui-state-default strong">
<td><?php echo _("Service") ?></td>
<td><?php echo _("Description") ?></td>
<td><?php echo _("Status") ?></td>
</tr>
</thead>
<tbody>
<!--
<tr class="odd">
<td><?php echo sprintf(_("%s Version"), PRODUCT_NAME) ?></td>
<td>1.9.3</td>
<td>&nbsp;</td>
</tr>
-->
<tr>
<td class="component">
Zend
</td>
<td class="description">
Zend MVC Framework
</td>
<td class="solution <?php if ($zend) {echo 'check';?>">
<?php
} else {
?>">
<b>Ubuntu</b>: try running <code>sudo apt-get install libzend-framework-php</code>
<br/><b>Debian</b>: try running <code>sudo apt-get install zendframework</code>
<?php
}
?>
</td>
</tr>
<tr>
<td class="component">
Postgres
</td>
<td class="description">
PDO and PostgreSQL libraries
</td>
<td class="solution <?php if ($postgres) {echo 'check';?>">
<?php
} else {
?>">
Try running <code>sudo apt-get install php5-pgsql</code>
<?php
}
?>
</td>
</tr>
<tr>
<td class="component">
Database
</td>
<td class="description">
Database configuration for Airtime
</td>
<td class="solution <?php if ($database) {echo 'check';?>">
<?php
} else {
?>">
Make sure you aren't missing any of the Postgres dependencies in the table above.
If your dependencies check out, make sure your database configuration settings in
<code>/etc/airtime.conf</code> are correct and the Airtime database was installed correctly.
<?php
}
?>
</td>
</tr>
<tr>
<td class="component">
RabbitMQ
</td>
<td class="description">
RabbitMQ configuration for Airtime
</td>
<td class="solution <?php if ($rabbitmq) {echo 'check';?>">
<?php
} else {
?>">
Make sure RabbitMQ is installed correctly, and that your settings in /etc/airtime/airtime.conf
are correct. Try using <code>sudo rabbitmqctl list_users</code> and <code>sudo rabbitmqctl list_vhosts</code>
to see if the airtime user (or your custom RabbitMQ user) exists, then checking that
<code>sudo rabbitmqctl list_exchanges</code> contains entries for airtime-media-monitor, airtime-pypo,
and airtime-uploads.
<?php
}
?>
</td>
</tr>
<tr>
<td class="component">
Media Monitor
</td>
<td class="description">
Airtime media-monitor service
</td>
<td class="solution <?php if ($mediamonitor) {echo 'check';?>">
<?php
} else {
?>">
Check that the airtime-media-monitor service is installed correctly in <code>/etc/init</code>,
and ensure that it's running with
<br/><code>initctl list | grep airtime-media-monitor</code><br/>
If not, try <br/><code>sudo service airtime-media-monitor start</code>
<?php
}
?>
</td>
</tr>
<tr>
<td class="component">
Pypo
</td>
<td class="description">
Airtime playout service
</td>
<td class="solution <?php if ($pypo) {echo 'check';?>">
<?php
} else {
?>">
Check that the airtime-playout service is installed correctly in <code>/etc/init</code>,
and ensure that it's running with
<br/><code>initctl list | grep airtime-playout</code><br/>
If not, try <br/><code>sudo service airtime-playout restart</code>
<?php
}
?>
</td>
</tr>
<tr>
<td class="component">
Liquidsoap
</td>
<td class="description">
Airtime liquidsoap service
</td>
<td class="solution <?php if ($liquidsoap) {echo 'check';?>">
<?php
} else {
?>">
Check that the airtime-liquidsoap service is installed correctly in <code>/etc/init</code>,
and ensure that it's running with
<br/><code>initctl list | grep airtime-liquidsoap</code><br/>
If not, try <br/><code>sudo service airtime-liquidsoap restart</code>
<?php
}
?>
</td>
</tr>
<tr id="partitions" class="even">
<th colspan="5"><?php echo _("Disk Space") ?></th>
</tr>
</tbody>
</table>
</table>

View File

@ -0,0 +1,53 @@
<?php
$tempConfigPath = "/etc/airtime/airtime.conf.tmp";
if (file_exists($tempConfigPath)) {
$airtimeConfig = parse_ini_file($tempConfigPath, true);
$db = $airtimeConfig["database"];
}
?>
<form action="#" role="form" id="dbSettingsForm">
<h3 class="form-title">Database Settings</h3>
<span id="helpBlock" class="help-block help-message"></span>
<p>
Enter your Airtime database settings here. Empty or non-existent databases will be created and populated
if the given user has administrative permissions in postgres.
</p>
<div class="form-group">
<label class="control-label" for="dbUser">Username</label>
<input required class="form-control" type="text" name="dbUser" id="dbUser" placeholder="Username"
value="<?php echo (isset($db) ? $db["dbuser"] : "airtime"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="dbPass">Password</label>
<input required class="form-control" type="password" name="dbPass" id="dbPass" placeholder="Password"
value="<?php echo (isset($db) ? $db["dbpass"] : "airtime"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="dbName">Name</label>
<input required class="form-control" type="text" name="dbName" id="dbName" placeholder="Name"
value="<?php echo (isset($db) ? $db["dbname"] : "airtime"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="dbHost">Host</label>
<input required class="form-control" type="text" name="dbHost" id="dbHost" placeholder="Host"
value="<?php echo (isset($db) ? $db["host"] : "localhost"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<input class="form-control" type="hidden" name="dbErr" id="dbErr" aria-describedby="helpBlock"/>
<div>
<p style="text-align:right">
This may take up to 30 seconds to complete!
</p>
<input type="submit" formtarget="dbSettingsForm" class="btn btn-primary btn-next" value="Next &#10097;"/>
</div>
</form>
<script>
$("#dbSettingsForm").submit(function(e) {
submitForm(e, "DatabaseSetup");
});
</script>

View File

@ -0,0 +1,30 @@
<?php
?>
<form action="#" role="form" id="finishSettingsForm">
<h3 class="form-title">Manual Step: Start Airtime Services</h3>
<span id="helpBlock" class="help-block help-message"></span>
<p>
Looks like you're almost done! As a final step, please run the following commands from the terminal:
</p>
<pre style="text-align: left">sudo service airtime-playout start
sudo service airtime-liquidsoap start
sudo service airtime-media-monitor start</pre>
<p>
Click "Done!" to bring up the Airtime configuration checklist; if your configuration is all green,
you're ready to get started with your personal Airtime station!
</p>
<p>
If you need to re-run the web installer, just remove <code>/etc/airtime/airtime.conf</code>.
</p>
<div>
<input type="submit" formtarget="finishSettingsForm" class="btn btn-primary btn-next" value="Done!"/>
</div>
</form>
<script>
$("#finishSettingsForm").submit(function(e) {
e.preventDefault();
window.location.assign("/?config");
});
</script>

View File

@ -0,0 +1,33 @@
<?php
?>
<form action="#" role="form" id="generalSettingsForm">
<h3 class="form-title">General Settings</h3>
<p>
These values are automatically pulled from your webserver settings, under most circumstances you will not need to change them.
</p>
<span id="helpBlock" class="help-block help-message"></span>
<div id="generalFormBody">
<div class="form-group">
<label class="control-label" for="generalHost">Webserver Host</label>
<input required class="form-control" type="text" name="generalHost" id="generalHost" placeholder="Host" value="<?=$_SERVER["SERVER_NAME"]?>"/>
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="generalPort">Webserver Port</label>
<input required class="form-control" type="text" name="generalPort" id="generalPort" placeholder="Port" value="<?=$_SERVER["SERVER_PORT"]?>"/>
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<input class="form-control" type="hidden" name="generalErr" id="generalErr" aria-describedby="helpBlock"/>
</div>
<div>
<input type="submit" formtarget="generalSettingsForm" class="btn btn-primary btn-next" value="Next &#10097;"/>
<input type="button" class="btn btn-primary btn-back" value="&#10096; Back"/>
</div>
</form>
<script>
$("#generalSettingsForm").submit(function(e) {
submitForm(e, "GeneralSetup");
});
</script>

View File

@ -0,0 +1,30 @@
<?php
?>
<form action="#" role="form" id="mediaSettingsForm">
<h3 class="form-title">Media Settings</h3>
<span id="helpBlock" class="help-block help-message"></span>
<p>
Here you can set the default media storage directory for Airtime. If left blank, we'll create a new
directory located at <code>/srv/airtime/stor/</code> for you.
</p>
<div class="form-group">
<label class="control-label" for="mediaFolder">Media folder</label>
<input class="form-control" type="text" name="mediaFolder" id="mediaFolder" placeholder="/path/to/my/airtime/music/directory/"/>
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
<span id="mediaHelpBlock" class="help-block">
Note that you need to enter the <strong>fully qualified</strong> path name!
</span>
</div>
<input class="form-control" type="hidden" name="mediaErr" id="mediaErr" aria-describedby="helpBlock"/>
<div>
<input type="submit" formtarget="mediaSettingsForm" class="btn btn-primary btn-next" value="Next &#10097;"/>
<input type="button" class="btn btn-primary btn-back" value="&#10096; Back"/>
</div>
</form>
<script>
$("#mediaSettingsForm").submit(function(e) {
submitForm(e, "MediaSetup");
});
</script>

View File

@ -0,0 +1,75 @@
<?php
$tempConfigPath = "/etc/airtime/airtime.conf.tmp";
if (file_exists($tempConfigPath)) {
$airtimeConfig = parse_ini_file($tempConfigPath, true);
$rmq = $airtimeConfig["rabbitmq"];
}
?>
<form action="#" role="form" id="rmqSettingsForm">
<h3 class="form-title">RabbitMQ Settings</h3>
<span id="helpBlock" class="help-block help-message"></span>
<p>
RabbitMQ is an AMQP-based messaging system used by Airtime. You should only edit these settings
if you have changed the defaults since running the installer, or if you've opted to install RabbitMQ manually.
</p>
<p>
In either case, we recommend that you change at least the default password provided -
you can do this by running the following line from the command line:<br/>
<code>sudo rabbitmqctl change_password &lt;username&gt; &lt;newpassword&gt;</code>
</p>
<div id="rmqSlideToggle">
<span><strong>Advanced </strong></span><span id="advCaret" class="caret"></span><hr/>
</div>
<div id="rmqFormBody">
<div class="form-group">
<label class="control-label" for="rmqUser">Username</label>
<input required class="form-control" type="text" name="rmqUser" id="rmqUser" placeholder="Username"
value="<?php echo (isset($rmq) ? $rmq["user"] : "airtime"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="rmqPass">Password</label>
<input class="form-control" type="password" name="rmqPass" id="rmqPass" placeholder="Password"
value="<?php echo (isset($rmq) ? $rmq["password"] : "airtime"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
<span id="rmqHelpBlock" class="help-block">
You probably want to change this!
</span>
</div>
<div class="form-group">
<label class="control-label" for="rmqHost">Host</label>
<input required class="form-control" type="text" name="rmqHost" id="rmqHost" placeholder="Host"
value="<?php echo (isset($rmq) ? $rmq["host"] : "127.0.0.1"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="rmqPort">Port</label>
<input required class="form-control" type="text" name="rmqPort" id="rmqPort" placeholder="Port"
value="<?php echo (isset($rmq) ? $rmq["port"] : "5672"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<div class="form-group">
<label class="control-label" for="rmqVHost">Virtual Host</label>
<input required class="form-control" type="text" name="rmqVHost" id="rmqVHost" placeholder="VHost"
value="<?php echo (isset($rmq) ? $rmq["vhost"] : "/airtime"); ?>" />
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
</div>
<input class="form-control" type="hidden" name="rmqErr" id="rmqErr" aria-describedby="helpBlock"/>
</div>
<div>
<input type="submit" formtarget="rmqSettingsForm" class="btn btn-primary btn-next" value="Next &#10097;"/>
<input type="button" class="btn btn-primary btn-back" value="&#10096; Back"/>
</div>
</form>
<script>
$("#rmqSlideToggle").click(function() {
$("#rmqFormBody").slideToggle(500);
$("#advCaret").toggleClass("caret-up");
});
$("#rmqSettingsForm").submit(function(e) {
submitForm(e, "RabbitMQSetup");
});
</script>

View File

@ -0,0 +1,158 @@
<?php
define("RMQ_INI_SECTION", "rabbitmq");
require_once dirname(dirname( __DIR__)) . '/library/php-amqplib/amqp.inc';
function booleanReduce($a, $b) {
return $a && $b;
}
/**
* Check to see if Airtime is properly configured.
*
* @return boolean true if all Airtime dependencies and services are
* properly configured and running
*/
function checkConfiguration() {
$r1 = array_reduce(checkPhpDependencies(), "booleanReduce", true);
$r2 = array_reduce(checkExternalServices(), "booleanReduce", true);
return $r1 && $r2;
}
/**
* Check for Airtime's PHP dependencies and return an associative
* array with the results
*
* @return array associative array of dependency check results
*/
function checkPhpDependencies() {
return array(
"zend" => checkZendDependencies(),
"postgres" => checkDatabaseDependencies()
);
}
/**
* Check that the Zend framework libraries are installed
*
* @return boolean true if Zend exists in /usr/share/php
*/
function checkZendDependencies() {
return file_exists('/usr/share/php/libzend-framework-php')
|| file_exists('/usr/share/php/Zend'); // Debian version
}
/**
* Check that the PHP dependencies for the database exist
*
* @return boolean true if the database dependencies exist
*/
function checkDatabaseDependencies() {
global $extensions;
// Check the PHP extension list for the Postgres db extensions
return (in_array('pdo_pgsql', $extensions)
&& in_array('pgsql', $extensions));
}
/**
* Check that all external services are configured correctly and return an associative
* array with the results
*
* @return array associative array of external service check results
*/
function checkExternalServices() {
return array(
"database" => checkDatabaseConfiguration(),
"media-monitor" => checkMediaMonitorService(),
"pypo" => checkPlayoutService(),
"liquidsoap" => checkLiquidsoapService(),
"rabbitmq" => checkRMQConnection()
);
}
/**
* Check the database configuration by fetching a connection from Propel
*
* @return boolean true if a connection is made to the database
*/
function checkDatabaseConfiguration() {
configureDatabase();
try {
// Try to establish a database connection. If something goes
// wrong, the database is improperly configured
Propel::getConnection();
Propel::close();
} catch (Exception $e) {
return false;
}
return true;
}
/**
* Initialize Propel to configure the Airtime database
*/
function configureDatabase() {
Propel::init(CONFIG_PATH . 'airtime-conf-production.php');
}
/**
* Check that we can connect to RabbitMQ
*
* @return true if the RabbitMQ connection can be established
*/
function checkRMQConnection() {
// Check for airtime.conf in /etc/airtime/ first, then check in the build directory,
if (file_exists(AIRTIME_CONFIG_STOR . AIRTIME_CONFIG)) {
$ini = parse_ini_file(AIRTIME_CONFIG_STOR . AIRTIME_CONFIG, true);
} else {
$ini = parse_ini_file(BUILD_PATH . "airtime.example.conf", true);
}
$conn = new AMQPConnection($ini[RMQ_INI_SECTION]["host"],
$ini[RMQ_INI_SECTION]["port"],
$ini[RMQ_INI_SECTION]["user"],
$ini[RMQ_INI_SECTION]["password"],
$ini[RMQ_INI_SECTION]["vhost"]);
return isset($conn);
}
/**
* Check if airtime-media-monitor is currently running
*
* @return boolean true if airtime-media-monitor is running
*/
function checkMediaMonitorService() {
exec("pgrep -f -u www-data airtime-media-monitor", $out, $status);
if (array_key_exists(0, $out) && $status == 0) {
return posix_kill(rtrim($out[0]), 0);
}
return $status == 0;
}
/**
* Check if airtime-playout is currently running
*
* @return boolean true if airtime-playout is running
*/
function checkPlayoutService() {
exec("pgrep -f -u www-data airtime-playout", $out, $status);
if (array_key_exists(0, $out) && $status == 0) {
return posix_kill(rtrim($out[0]), 0);
}
return $status == 0;
}
/**
* Check if airtime-liquidsoap is currently running
*
* @return boolean true if airtime-liquidsoap is running
*/
function checkLiquidsoapService() {
exec("pgrep -f -u www-data airtime-liquidsoap", $out, $status);
if (array_key_exists(0, $out) && $status == 0) {
return posix_kill(rtrim($out[0]), 0);
}
return $status == 0;
}

View File

@ -0,0 +1,57 @@
<?php
?>
<html>
<head>
<script type="text/javascript" src="js/libs/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="js/libs/jquery-ui-1.8.24.min.js"></script>
<script type="text/javascript" src="js/setup/setup-config.js"></script>
<link rel="stylesheet" type="text/css" href="css/bootstrap-3.3.1.min.css" />
<link rel="stylesheet" type="text/css" href="css/setup/setup-config.css" />
</head>
<body>
<div class="header">
<h3 class="logo">
<img src="css/images/airtime_logo_jp.png" id="airtimeLogo" /><br/>
<strong>Setup</strong>
</h3>
<strong>Step <span id="stepCount">1</span> of 5</strong>
</div>
<div class="viewport">
<div class="form-slider">
<div id="databaseSettings" class="form-wrapper">
<?php
require_once SETUP_PATH . "forms/database-settings.php";
?>
</div>
<div id="rabbitmqSettings" class="form-wrapper">
<?php
require_once SETUP_PATH . "forms/rabbitmq-settings.php";
?>
</div>
<div id="generalSettings" class="form-wrapper">
<?php
require_once SETUP_PATH . "forms/general-settings.php";
?>
</div>
<div id="mediaSettings" class="form-wrapper">
<?php
require_once SETUP_PATH . "forms/media-settings.php";
?>
</div>
<div id="finishSettings" class="form-wrapper">
<?php
require_once SETUP_PATH . "forms/finish-settings.php";
?>
</div>
</div>
</div>
<script>
$(".btn-skip").click(nextSlide);
$(".btn-back").click(prevSlide);
</script>
</body>
</html>

View File

@ -1,32 +0,0 @@
[database]
host = localhost
dbname = airtime
dbuser = airtime
dbpass = airtime
[rabbitmq]
host = 127.0.0.1
port = 5672
user = guest
password = guest
vhost = /
[general]
api_key = AAA
web_server_user = www-data
airtime_dir = x
base_url = localhost
base_port = 80
base_dir = '/'
;How many hours ahead of time should Airtime playout engine (PYPO)
;cache scheduled media files.
cache_ahead_hours = 1
[monit]
monit_user = guest
monit_password = airtime
[soundcloud]
connection_retries = 3
time_between_retries = 60

View File

@ -0,0 +1,274 @@
# ----------------------------------------------------------------------
# A I R T I M E C O N F I G U R A T I O N
# ----------------------------------------------------------------------
#
# This is an example configuration for Airtime. If you just want to
# get started with a basic Airtime setup, or don't know if you should
# be reconfiguring any of the following values, just rename this file
# to 'airtime.conf'.
#
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# G E N E R A L S E T T I N G S
# ----------------------------------------------------------------------
#
# These settings are used for Airtime's webserver configuration, and
# for general-purpose properties.
#
# api_key: The API key for your Airtime installation.
# The value is generated the first time you use Airtime.
#
# web_server_user: The default webserver user.
# The default is www-data.
#
# base_url: The host name for your webserver.
# The default is localhost.
#
# base_port: The port for your webserver.
# The default is 80.
#
# base_dir: The root directory for your Airtime installation
# on your webserver, relative to the base_url.
# The default is /.
#
# cache_ahead_hours: How many hours ahead of time the Airtime playout
# engine (pypo) should cache scheduled media files.
# The default is 1.
#
[general]
api_key =
web_server_user = www-data
base_url = localhost
base_port = 80
base_dir = /
cache_ahead_hours = 1
#
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# D A T A B A S E
# ----------------------------------------------------------------------
#
# These settings are used to configure your database connection.
#
# host: The hostname of the database server.
# On a default Airtime installation, set this to localhost.
#
# dbname: The name of the Airtime database.
# The default is airtime.
#
# dbuser: The username for the Airtime database user.
# The default is airtime.
#
# dbpass: The password for the Airtime database user.
# The default is airtime.
#
[database]
host = localhost
dbname = airtime
dbuser = airtime
dbpass = airtime
#
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# R A B B I T M Q
# ----------------------------------------------------------------------
#
# These settings are used to configure the RabbitMQ messaging
# configuration for your Airtime installation.
#
# host: The IP address for the RabbitMQ service.
# The default is 127.0.0.1.
#
# port: The port for the RabbitMQ service.
# The default is 5672.
#
# user: The username for the RabbitMQ user.
# The default is airtime.
#
# password: The password for the RabbitMQ user.
# The default is airtime.
#
# vhost: The virtual host for the RabbitMQ service database.
# The default is /airtime.
#
[rabbitmq]
host = 127.0.0.1
port = 5672
user = airtime
password = airtime
vhost = /airtime
#
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# M E D I A M O N I T O R
# ----------------------------------------------------------------------
#
# check_filesystem_events: How long to queue up events performed on the
# files themselves, in seconds
# The default is 5
#
# check_airtime_events: How long to queue metadata input from airtime,
# in seconds
# The default is 30
#
# touch_interval:
# The default is 5
#
# chunking_number:
# The default is 450
#
# request_max_wait: The maximum request wait time, in seconds
# The default is 3.0
#
# rmq_event_wait: The RabbitMQ event wait time, in seconds
# The default is 0.1
#
# logpath: The media monitor log file path
# The default is '/var/log/airtime/media-monitor/media-monitor.log'
#
# index_path: The media monitor index path
# The default is '/var/tmp/airtime/media-monitor/last_index'
#
[media-monitor]
check_filesystem_events = 5
check_airtime_events = 30
touch_interval = 5
chunking_number = 450
request_max_wait = 3.0
rmq_event_wait = 0.1
logpath = '/var/log/airtime/media-monitor/media-monitor.log'
index_path = '/var/tmp/airtime/media-monitor/last_index'
#
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# P Y P O
# ----------------------------------------------------------------------
#
# api_client: Set the type of client you are using.
# Currently supported types:
# 1) 'obp' = Open Broadcast Platform
# 2) 'airtime'
# The default is 'airtime'
#
# cache_dir: The directory for pypo cache files
# The default is '/var/tmp/airtime/pypo/cache/'
#
# file_dir: The directory for pypo media files
# The default is '/var/tmp/airtime/pypo/files/'
#
# tmp_dir: The directory for pypo temp files
# The default is '/var/tmp/airtime/pypo/tmp/'
#
# cache_base_dir: The pypo base cache directory
# The default is '/var/tmp/airtime/pypo/'
#
# log_base_dir: The base directory for Airtime log files
# The default is '/var/log/airtime'
#
# pypo_log_dir: The directory for pypo log files
# The default is '/var/log/airtime/pypo'
#
# liquidsoap_log_dir: The directory for liquidsoap log files
# The default is '/var/log/airtime/pypo-liquidsoap'
#
# ls_host: Liquidsoap connection host
# The default is '127.0.0.1'
#
# ls_port: Liquidsoap connection port
# The default is '1234'
#
# poll_interval: Poll interval in seconds
#
# This will rarely need to be changed because any schedule
# changes are automatically sent to pypo immediately
# This is how often the poll script downloads new schedules
# and files from the server in the event that no changes
# are made to the schedule
# The default is 3600
#
# push_interval: Push interval in seconds
#
# This is how often the push script checks whether it has
# something new to push to liquidsoap
# The default is 1
#
# cue_style: Can be set to 'pre' or 'otf'
# 'pre' cues while playlist preparation
# 'otf' (on the fly) cues while loading into ls
# (needs the post_processor patch)
# The default is 'pre'
#
# record_bitrate: The bitrate for recordings
# The default is 256
#
# record_samplerate: The samplerate for recordings
# The default is 44100
#
# record_channels: The number of channels for recordings
# The default is 2
#
# record_sample_size: The sample size for recordings
# The default is 16
#
# record_file_type: Can be either ogg|mp3, mp3 recording requires
# installation of the package "lame"
# The default is ogg
#
# base_recorded_files: Base path to store recordered shows at
# The default is '/var/tmp/airtime/show-recorder/'
#
[pypo]
api_client = 'airtime'
# ---------- Cache directories - !! Include trailing slash !! ----------
cache_dir = '/var/tmp/airtime/pypo/cache/'
file_dir = '/var/tmp/airtime/pypo/files/'
tmp_dir = '/var/tmp/airtime/pypo/tmp/'
# ------- Setup directories - !! Don't include trailing slash !! -------
cache_base_dir = '/var/tmp/airtime/pypo'
log_base_dir = '/var/log/airtime'
pypo_log_dir = '/var/log/airtime/pypo'
liquidsoap_log_dir = '/var/log/airtime/pypo-liquidsoap'
# ------------------------ Liquidsoap Settings -------------------------
ls_host = '127.0.0.1'
ls_port = '1234'
# -------------------------- Pypo Preferences --------------------------
poll_interval = 3600
push_interval = 1
cue_style = 'pre'
# ---------------------- Recorded Audio Settings -----------------------
record_bitrate = 256
record_samplerate = 44100
record_channels = 2
record_sample_size = 16
record_file_type = 'ogg'
base_recorded_files = '/var/tmp/airtime/show-recorder/'
#
# ----------------------------------------------------------------------
# ----------------------------------------------------------------------
# S O U N D C L O U D
# ----------------------------------------------------------------------
#
# connection_retries: The number of times to retry the connection to
# Soundcloud.
# The default is 3.
#
# time_between_retries: The time between connection retries, in seconds.
# The default is 60.
#
[soundcloud]
connection_retries = 3
time_between_retries = 60
#
# ----------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
-- Schema version
INSERT INTO cc_pref("keystr", "valstr") VALUES('system_version', '2.5.9');
INSERT INTO cc_pref("keystr", "valstr") VALUES('schema_version', '2.5.12');
INSERT INTO cc_subjs ("login", "type", "pass") VALUES ('admin', 'A', md5('admin'));
-- added in 2.3

View File

@ -436,10 +436,13 @@ class AMQPConnection extends AbstractChannel
$len = strlen($data);
while(true)
{
if(false === ($written = fwrite($this->sock, $data)))
$written = fwrite($this->sock, $data);
if($written == false || $written <= 0)
{
throw new Exception ("Error sending data");
}
$len = $len - $written;
if($len>0)
$data=substr($data,0-$len);
@ -601,12 +604,17 @@ class AMQPConnection extends AbstractChannel
*/
public function close($reply_code=0, $reply_text="", $method_sig=array(0, 0))
{
try {
$args = new AMQPWriter();
$args->write_short($reply_code);
$args->write_shortstr($reply_text);
$args->write_short($method_sig[0]); // class_id
$args->write_short($method_sig[1]); // method_id
$this->send_method_frame(array(10, 60), $args);
} catch(Exception $e) {
return;
}
return $this->wait(array(
"10,61", // Connection.close_ok
));

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,196 @@
body {
font-family: 'Roboto', sans-serif;
margin: 0px;
}
.tab_content {
color: #fff;
text-align: center;
visibility: hidden;
max-width: 750px;
/*margin: 0 auto;*/
margin-bottom: 140px;
padding: 10px;
position: absolute;
/*left: 50%;
margin-left: -380px;*/
}
.tab_content.current {
/*display: block;*/
visibility: visible;
}
.schedule .tabs {
list-style: none;
padding-left: 0px;
margin: 0px;
background: rgba(170, 170, 170, 0.9);
}
.schedule .tabs li {
width: 105px;
height: 80px;
display: inline-block;
font-size: 14px;
padding: 15px;
box-sizing: border-box;
cursor: pointer;
}
.schedule .tabs li span {
font-size: 30px;
display: block;
}
.schedule .tabs li.active {
background: rgba(150, 150, 150, 0.9);
}
.schedule .tabs li.active:hover {
background: rgba(170, 170, 170, 0.9);
background: #aaa
}
.schedule .tabs li:hover {
background: rgba(160, 160, 160, 0.7);
}
.schedule_content {
background: rgba(53, 53, 53, 0.9);
max-height: 0px;
transition: max-height 2s ease;
overflow-y: auto;
overflow-x: hidden;
text-overflow: ellipsis;
/*transition-delay: 1s;*/
}
.schedule.current .schedule_content {
max-height: 300px;
}
/*.schedule_content table {
opacity: 0;
*//*transition: all 1s ease;*//*
}
.schedule.current .schedule_content table {
opacity: 1;
-webkit-transition-delay: 0.6s;
transition-delay: 0.6s;
}*/
.schedule_item {
display: none;
font-size: 17px;
text-align: left;
text-transform: uppercase;
padding: 10px 30px;
}
.schedule_item.active {
display: block;
}
.schedule_item .row {
padding-top: 10px;
padding-bottom: 10px;
}
.schedule_item div.time_grid {
/*padding-right: 10px;*/
width: 20%;
font-weight: 300;
color: #AAAAAA;
display: inline-block;
}
.schedule_item div.name_grid {
overflow: hidden;
text-overflow: ellipsis;
padding-left: 10px;
width: 77%;
display:inline-block;
vertical-align: middle;
}
.weekly-schedule-widget-footer {
border-top: 1px solid #aaaaaa;
text-align: right;
padding: 4px 16px;
background: rgba(53, 53, 53, 0.9);
}
.weekly-schedule-widget-footer a {
color: #fff;
text-decoration: none;
}
.weekly-schedule-widget-footer span {
color: #ff5d1a;
}
@media (max-width: 730px) {
.tab_content {
margin-top: 0px;
width: auto;
max-width: 100%;
margin-left: auto;
left: 10px;
right: 10px;
}
.schedule .tabs li {
width: 64px;
height: 64px;
padding: 6px;
}
}
@media (max-width: 630px) {
.schedule_item div.time_grid {
width: 25%;
}
.schedule_item div.name_grid {
width: 72%;
}
}
@media (max-width: 530px) {
.schedule_item {
padding: 10px 20px;
font-size: 14px;
}
.schedule_item div.time_grid {
width: 30%;
}
.schedule_item div.name_grid {
width: 67%;
}
}
@media (max-width: 500px) {
.schedule_item div.time_grid {
width: 35%;
}
.schedule_item div.name_grid {
width: 62%;
}
}
@media (max-width: 400px) {
.schedule_item div.time_grid {
width: 40%;
}
.schedule_item div.name_grid {
width: 50%;
}
}

View File

@ -0,0 +1,171 @@
body {
font-family: 'Roboto', sans-serif;
margin: 0px;
}
.tab_content {
color: #fff;
text-align: center;
visibility: hidden;
max-width: 750px;
/*margin: 0 auto;*/
margin-bottom: 140px;
padding: 10px;
position: absolute;
/*left: 50%;
margin-left: -380px;*/
}
.tab_content.current {
/*display: block;*/
visibility: visible;
}
.schedule .tabs {
list-style: none;
padding-left: 0px;
margin: 0px;
background: rgba(69, 155, 143, 0.8);
}
.schedule .tabs li {
width: 105px;
height: 80px;
display: inline-block;
font-size: 14px;
padding: 15px;
box-sizing: border-box;
cursor: pointer;
}
.schedule .tabs li span {
font-size: 30px;
display: block;
}
.schedule .tabs li.active {
background: #459B8F;
}
.schedule .tabs li.active:hover {
background: #459B8F;
}
.schedule .tabs li:hover {
background: rgba(69, 155, 143, 0.6)
}
.schedule_content {
background: rgba(0, 0, 0, 0.3);
max-height: 0px;
transition: max-height 2s ease;
overflow-y: auto;
overflow-x: hidden;
text-overflow: ellipsis;
/*transition-delay: 1s;*/
}
.schedule.current .schedule_content {
max-height: 300px;
}
.schedule_item {
display: none;
font-size: 17px;
text-align: left;
text-transform: uppercase;
padding: 30px 40px;
}
.schedule_item.active {
display: block;
}
.schedule_item .row {
padding-top: 10px;
padding-bottom: 10px;
}
.schedule_item div.time_grid {
/*padding-right: 10px;*/
width: 20%;
font-weight: 300;
color: #AAAAAA;
display: inline-block;
}
.schedule_item div.name_grid {
overflow: hidden;
text-overflow: ellipsis;
padding-left: 10px;
width: 77%;
display:inline-block;
vertical-align: middle;
}
@media (max-width: 730px) {
.tab_content {
margin-top: 0px;
width: auto;
max-width: 100%;
margin-left: auto;
left: 10px;
right: 10px;
}
.schedule .tabs li {
width: 64px;
height: 64px;
padding: 6px;
}
}
@media (max-width: 630px) {
.schedule_item div.time_grid {
width: 25%;
}
.schedule_item div.name_grid {
width: 72%;
}
}
@media (max-width: 530px) {
.schedule_item {
padding: 10px 20px;
font-size: 14px;
}
.schedule_item div.time_grid {
width: 30%;
}
.schedule_item div.name_grid {
width: 67%;
}
}
@media (max-width: 500px) {
.schedule_item div.time_grid {
width: 35%;
}
.schedule_item div.name_grid {
width: 62%;
}
}
@media (max-width: 400px) {
.schedule_item div.time_grid {
width: 40%;
}
.schedule_item div.name_grid {
width: 50%;
}
}

View File

@ -0,0 +1,32 @@
.landing-page-content {
margin: 0 auto;
width: 100%;
color: #fff;
padding-top: 60px;
/*position: relative;*/
}
.station-info {
/*position: absolute;*/
}
/*@media (min-width: 640px) {
.station-info {
float:left;
margin-right: 20px;
width: 200px;
}
.player {
overflow: hidden;
}
}
@media (max-width: 640px) {
.station-info {
float: none;
margin-right: 0px;
width: auto;
}
}*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -0,0 +1,148 @@
body {
font-family: 'Roboto', sans-serif;
margin: 0px;
}
.tab_content.current {
/*display: block;*/
visibility: visible;
}
.bottom_bar {
position: fixed;
left: 0px;
right: 0px;
bottom: 0px;
height: 120px;
background: #fff;
}
.bottom_bar .cont_btn {
width: 120px;
height: 120px;
display: inline-block;
cursor: pointer;
}
.bottom_bar .cont_btn:hover {
background-color: #409187;
}
.bottom_bar .play {
background: #459B8F url('img/play.png') no-repeat center center;
}
.bottom_bar .pause {
background: #459B8F url('img/pause.png') no-repeat center center;
}
.on_air {
display: inline-block;
margin-left: 40px;
}
.bottom_bar p.now_playing {
color: #222222;
font-size: 26px;
font-weight: normal;
float: left;
margin: 28px 0px;
}
.bottom_bar p.now_playing span {
display: block;
font-weight: lighter;
}
.bottom_bar .button {
width: 50px;
height: 50px;
display: inline-block;
margin: 35px 40px;
cursor: pointer;
float: right;
}
.bottom_bar .schedule_btn {
background: url('img/schedule.png') no-repeat center 0px;
}
.bottom_bar .schedule_btn.current {
background: url('img/schedule.png') no-repeat center -50px;
}
.bottom_bar .about_us_btn {
background: url('img/about_us.png') no-repeat center 0px;
}
.bottom_bar .about_us_btn.current {
background: url('img/about_us.png') no-repeat center -50px;
}
.bottom_bar .login_btn {
background: url('img/login.png') no-repeat center 0px;
}
.bottom_bar .login_btn.current {
background: url('img/login.png') no-repeat center -50px;
float: right;
}
@media (max-width: 780px) {
.bottom_bar {
height: 80px;
}
.bottom_bar .play {
width: 80px;
height: 80px;
float: left;
background-size: 60%;
}
.bottom_bar p.now_playing {
font-size: 18px;
margin: 10px 10px;
color: #fff;
white-space: nowrap;
}
.bottom_bar p.now_playing span {
display: inline-block;
padding-left: 10px;
}
/*.on_air {
margin-left: 20px;
}*/
.on_air {
margin-left: 0px;
position: absolute;
top: -82px;
left: 0px;
right: 0px;
padding-top: 40px;
background: rgba(0,0,0,0);
background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 69%, rgba(0,0,0,1) 100%);
background: -webkit-gradient(left top, left bottom, color-stop(0%, rgba(0,0,0,0)), color-stop(69%, rgba(0,0,0,1)), color-stop(100%, rgba(0,0,0,1)));
background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 69%, rgba(0,0,0,1) 100%);
background: -o-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 69%, rgba(0,0,0,1) 100%);
background: -ms-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 69%, rgba(0,0,0,1) 100%);
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 69%, rgba(0,0,0,1) 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000000', endColorstr='#000000', GradientType=0 );
}
.bottom_bar .button {
margin: 15px 20px;
}
.bottom_bar .schedule_btn.current {
top: 0px;
left: 0px;
}
.bottom_bar .about_us_btn {
top: 0px;
right: 0px;
}
}

View File

@ -0,0 +1,161 @@
html {
background: url("img/background.png") no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
overflow-y: hidden;
}
.bck_cover {
position: fixed;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
background: rgba(0, 0, 0, 0.7);
z-index: -1;
}
body {
font-family: 'Roboto', sans-serif;
margin: 0px;
}
.logo img {
margin: 40px 0px 0px 60px;
}
#schedule_iframe, .tab_content {
color: #fff;
text-align: center;
width: 770px;
max-width: 770px;
margin: 0 auto;
margin-bottom: 140px;
padding: 10px;
padding-left: 0px;
position: absolute;
left: 50%;
margin-left: -380px;
/* -webkit-transition-delay: 1s;
transition-delay: 1s;*/
}
#schedule_iframe.tab_content.current, .tab_content.current {
/*display: block;*/
visibility: visible;
}
.about_us {
-webkit-transition-delay: 2s;
transition-delay: 2s;
}
#schedule_iframe.schedule, .about_us, .login {
/* -webkit-transition-delay: 1s;
transition-delay: 1s;*/
transition: all 0.4s ease;
opacity: 0;
}
.about_us.current, .login.current {
opacity: 1;
-webkit-transition-delay: 0.4s;
transition-delay: 0.4s;
}
#schedule_iframe.schedule.current {
opacity: 1;
}
.about_us h1 {
font-size: 80px;
font-weight: 300;
margin: 10px;
}
.about_us p {
font-size: 24px;
font-weight: 300;
margin: 10px;
}
.login h2 {
font-size: 28px;
font-weight: 300;
}
input[type="text"] {
font-family: 'Roboto', sans-serif;
color: #fff;
font-size: 16px;
font-weight: 300;
padding: 20px;
background: rgba(255, 255, 255, 0.2);
border: none;
margin-bottom: 1px;
width: 100%;
max-width: 380px;
text-align: center;
box-sizing: border-box;
}
input[type="text"]:hover {
background: rgba(255, 255, 255, 0.3);
}
input[type="submit"] {
font-family: 'Roboto', sans-serif;
color: #fff;
font-size: 18px;
font-weight: 300;
background: #459B8F;
width: 100%;
max-width: 380px;
padding: 18px 20px;
margin-top: 20px;
border: none;
cursor: pointer;
box-sizing: border-box;
}
input[type="submit"]:hover {
background-color: #409187;
}
@media (max-width: 780px) {
.logo {
display: block;
text-align: center;
}
.logo img {
margin: 20px 0px;
}
.tab_content {
margin-top: 0px;
width: auto;
max-width: 100%;
margin-left: auto;
left: 10px;
right: 10px;
}
#schedule_iframe {
margin-top: 0px;
max-width: 100%;
margin-left: auto;
left: 0px;
right: 10px;
}
.about_us h1 {
font-size: 40px;
}
.about_us p {
font-size: 18px;
}
}

View File

@ -0,0 +1,130 @@
#scheduleTabs {
font-family: Arial,Helvetica,sans-serif;
}
#scheduleTabs ul {
overflow:hidden;
height:50px;
/*background: url('../widgets/css/widget-img/schedule-tabs-list-bgr.png') repeat-x left bottom;*/
background: #2E9AFE;
list-style-type:none;
margin:0;
padding:0;
}
#scheduleTabs ul li {
float:left;
height:50px;
margin:0;
padding:0;
}
#scheduleTabs ul li a {
width: 50px;
display:block;
float:left;
/*height:40px;*/
padding:0 10px;
font-size:12px;
font-weight:bold;
color:#ffffff;
/*line-height:40px;*/
/*border-right:1px solid #dbdbdb;
border-left:1px solid #f4f4f4;*/
text-decoration:none;
}
#scheduleTabs ul li a:hover {
color:#000;
text-decoration:none;
}
#scheduleTabs ul li a span {
position:relative;
display:block;
text-align: center;
}
.day-of-month{
font-size: 20px;
padding-bottom:10px;
}
.day-of-week{
font-size: 10px;
padding-top:10px;
}
#scheduleTabs ul li.ui-tabs-selected a {
/*height:40px;*/
margin-top:0px;
width: 50px;
/*line-height:40px;*/
color:#ffffff;
background: #2ECCFA;
/*border:1px solid #cac9c9; border-bottom:none;*/
}
#scheduleTabs table {
clear:both;
padding:0px;
border-collapse:collapse;
width:100%;
}
#scheduleTabs table thead {
height:35px;
font-size:12px; color:#333333; line-height:35px;
background-color: #fff;
background: -moz-linear-gradient(top, #ffffff 10%, #f4f4f4 100%);
background: -webkit-gradient(linear, left top, left bottom, color-stop(10%, #ffffff), color-stop(100%, #f4f4f4));
border-bottom:1px solid #d4d4d4;
}
#scheduleTabs table thead tr {
height:35px;
}
#scheduleTabs table thead tr td {
padding-left:10px;
font-size:11px; color:#999999; text-transform:uppercase;
}
#scheduleTabs table tfoot {
}
#scheduleTabs table tbody {
font-size:13px;
color:#ffffff;
font-weight:bold;
background: rgba(0, 0, 0, 1);
}
#scheduleTabs table tbody tr {
height:30px;
}
#scheduleTabs table tbody tr td {
height:30px;
padding-left:10px;
border-bottom:1px dotted #b3b3b3;
}
#scheduleTabs table tbody tr td h4 {
color:#ffffff; font-size:12px;
margin:0;
padding:0;
}
#scheduleTabs table tbody tr td ul {
background:none !important;
list-style-type:none;
}
#scheduleTabs table tbody tr td ul li {
float:left;
height:30px !important;
padding:0 2px;
font-size:12px; color:#cc0000; font-weight:normal; line-height:30px !important;
}
#scheduleTabs table tbody tr td ul li a {
height:30px !important;
margin:0px !important; padding:0px !important;
font-size:12px; color:#68BD44 !important; font-weight:normal !important; text-transform:uppercase; line-height:30px !important;
background:none !important;
border:none !important;
}
#scheduleTabs table tbody tr td ul li a:hover {
text-decoration:underline;
}
#scheduleTabs.ui-tabs .ui-tabs-hide {
display: none;
}
.embedScheduleWidget {
width: 490px;
}

View File

@ -0,0 +1,39 @@
.logo {
margin-bottom: .5em;
}
.table {
padding: 5px;
margin: 1em 0 0 0;
}
.checklist {
min-height: 200px;
}
.caption {
padding-left: .5em;
}
.component {
font-weight: bold;
width: 20%;
}
.error {
color: red;
}
.description {
width: 300px;
}
.check {
background: #dff0d8 url("../images/accept.png") no-repeat center;
}
.footer {
margin: inherit;
width: inherit;
bottom: 0;
}

View File

@ -0,0 +1,252 @@
html {
background: -webkit-linear-gradient(#444, black); /* For Safari 5.1 to 6.0 */
background: -o-linear-gradient(#444, black); /* For Opera 11.1 to 12.0 */
background: -moz-linear-gradient(#444, black); /* For Firefox 3.6 to 15 */
background: linear-gradient(#444, black); /* Standard syntax */
}
body {
background: rgba(0,0,0,0); /* Make the bg transparent */
color: white;
min-width: 400px;
width: 30%;
height: 100%;
text-align: center;
margin: auto;
}
p, h1, h2, h3, h4, h5, h6 {
text-shadow: 0px 1px 1px #000;
}
code {
text-shadow: none;
}
.header {
padding-top: 1em;
height: 10%;
min-height: 75px;
text-align: center;
}
.header strong {
float: right;
}
.logo {
margin: auto;
}
.logo strong {
padding-left: .5em;
float: left;
}
#airtimeLogo {
float: left;
padding-top: 0.6em;
}
.btn-primary {
text-shadow: 0px 1px 1px #000000;
background-color: #ff5d1a;
border-color: #8d3715;
}
.btn-primary:focus, .btn-primary:active, .form-control:focus
{
border-color: #ff5d1a;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 93, 26, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(255, 93, 26, 0.6);
}
.btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, .open>.dropdown-toggle.btn-primary,
.btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus, .btn-primary[disabled].focus, fieldset[disabled] .btn-primary.focus, .btn-primary.disabled:active,
.btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active
{
background-color: #dc5118;
border-color: #632910;
}
/* ############################################################################
*
* Form Styles
*
* ############################################################################ */
form {
margin: auto;
top: 0;
width: 99%;
position: absolute;
float: right;
}
form p {
text-align: left;
margin-bottom: 1em;
}
form .form-group {
text-align: left;
}
.viewport {
position: absolute;
width: 30%;
min-width: 400px;
height: 89%; /* Keep 1% off of 100 to avoid overflow */
overflow-x: hidden;
overflow-y: auto;
}
.form-wrapper {
overflow: auto;
}
.form-title {
margin: 1em 0;
}
.form-slider {
position: absolute;
width: 100%;
height: 100%;
}
.form-control-feedback {
display: none;
}
/* ############################################################################
*
* Form Help Box Styles
*
* ############################################################################ */
.help-block.help-message {
font-weight:bold;
margin-bottom: 15px;
}
.has-error, .has-error .control-label,
.has-error .form-control-feedback,
.has-error .form-control {
color: red;
}
.has-success {
color: lawngreen;
}
/* ############################################################################
*
* Overlay Styles
*
* ############################################################################ */
#overlay {
display: none;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
background-color: #000;
width: 100%;
height: 100%;
z-index: 5000;
}
#loadingImage {
display: none;
position: absolute;
top: 40%;
left: 49%; /* Shave 1% to account for half image width */
z-index: 5001;
}
/* ############################################################################
*
* Button Styles
*
* ############################################################################ */
.btn-primary {
font-weight: bold;
}
.btn-disabled {
disabled: disabled;
visibility: hidden;
display: block;
}
.btn-next {
float: right;
width: 49%;
}
.btn-skip {
margin-top: .5em;
width: 100%;
}
.btn-back {
float: left;
width: 49%;
}
.caret.caret-up {
border-top-width: 0;
border-bottom: 4px solid #fff;
}
/* ############################################################################
*
* RabbitMQ Settings Form Styles
*
* ############################################################################ */
#rmqSettingsForm {
left: 100%;
}
#rmqFormBody {
display: none;
}
/* ############################################################################
*
* General Settings Form Styles
*
* ############################################################################ */
#generalSettingsForm {
left: 200%;
}
/* ############################################################################
*
* Media Settings Form Styles
*
* ############################################################################ */
#mediaSettingsForm {
left: 300%;
}
/* ############################################################################
*
* Finish Settings Form Styles
*
* ############################################################################ */
#finishSettingsForm {
left: 400%;
}

View File

@ -3261,3 +3261,33 @@ dd .stream-status {
.enable-tunein {
font-weight:bold;
}
#weekly-schedule-widget {
margin: 0 auto;
width: 50%;
}
#widget-menu {
margin: 0 auto;
width: 40%;
}
#widget-menu a {
display: block;
margin-bottom: 5px;
font-size: 14px;
}
#weekly-schedule-widget label {
font-weight: bold;
}
.blah {
width: 100%;
float: left;
text-align: center;
}
#schedule_iframe {
margin: 0 auto;
}

View File

@ -0,0 +1,229 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata></metadata>
<defs>
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
<font-face units-per-em="1200" ascent="960" descent="-240" />
<missing-glyph horiz-adv-x="500" />
<glyph />
<glyph />
<glyph unicode="&#xd;" />
<glyph unicode=" " />
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
<glyph unicode="&#xa0;" />
<glyph unicode="&#x2000;" horiz-adv-x="652" />
<glyph unicode="&#x2001;" horiz-adv-x="1304" />
<glyph unicode="&#x2002;" horiz-adv-x="652" />
<glyph unicode="&#x2003;" horiz-adv-x="1304" />
<glyph unicode="&#x2004;" horiz-adv-x="434" />
<glyph unicode="&#x2005;" horiz-adv-x="326" />
<glyph unicode="&#x2006;" horiz-adv-x="217" />
<glyph unicode="&#x2007;" horiz-adv-x="217" />
<glyph unicode="&#x2008;" horiz-adv-x="163" />
<glyph unicode="&#x2009;" horiz-adv-x="260" />
<glyph unicode="&#x200a;" horiz-adv-x="72" />
<glyph unicode="&#x202f;" horiz-adv-x="260" />
<glyph unicode="&#x205f;" horiz-adv-x="326" />
<glyph unicode="&#x20ac;" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
<glyph unicode="&#x2212;" d="M200 400h900v300h-900v-300z" />
<glyph unicode="&#x25fc;" horiz-adv-x="500" d="M0 0z" />
<glyph unicode="&#x2601;" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
<glyph unicode="&#x2709;" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
<glyph unicode="&#x270f;" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
<glyph unicode="&#xe001;" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
<glyph unicode="&#xe002;" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
<glyph unicode="&#xe003;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
<glyph unicode="&#xe005;" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
<glyph unicode="&#xe006;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
<glyph unicode="&#xe007;" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
<glyph unicode="&#xe008;" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
<glyph unicode="&#xe009;" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
<glyph unicode="&#xe010;" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe011;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe012;" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe013;" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
<glyph unicode="&#xe014;" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
<glyph unicode="&#xe015;" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
<glyph unicode="&#xe016;" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
<glyph unicode="&#xe017;" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
<glyph unicode="&#xe018;" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
<glyph unicode="&#xe019;" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
<glyph unicode="&#xe020;" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
<glyph unicode="&#xe021;" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
<glyph unicode="&#xe022;" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
<glyph unicode="&#xe023;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
<glyph unicode="&#xe024;" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
<glyph unicode="&#xe025;" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
<glyph unicode="&#xe026;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
<glyph unicode="&#xe027;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
<glyph unicode="&#xe028;" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
<glyph unicode="&#xe029;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
<glyph unicode="&#xe030;" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
<glyph unicode="&#xe031;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
<glyph unicode="&#xe032;" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
<glyph unicode="&#xe033;" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
<glyph unicode="&#xe034;" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
<glyph unicode="&#xe035;" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
<glyph unicode="&#xe036;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
<glyph unicode="&#xe037;" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
<glyph unicode="&#xe038;" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
<glyph unicode="&#xe039;" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
<glyph unicode="&#xe040;" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
<glyph unicode="&#xe041;" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
<glyph unicode="&#xe042;" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
<glyph unicode="&#xe043;" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
<glyph unicode="&#xe044;" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
<glyph unicode="&#xe045;" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
<glyph unicode="&#xe046;" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
<glyph unicode="&#xe047;" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
<glyph unicode="&#xe048;" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
<glyph unicode="&#xe049;" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
<glyph unicode="&#xe050;" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
<glyph unicode="&#xe051;" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
<glyph unicode="&#xe052;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
<glyph unicode="&#xe053;" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
<glyph unicode="&#xe054;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe055;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe056;" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe057;" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
<glyph unicode="&#xe058;" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
<glyph unicode="&#xe059;" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
<glyph unicode="&#xe060;" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
<glyph unicode="&#xe062;" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
<glyph unicode="&#xe063;" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
<glyph unicode="&#xe064;" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
<glyph unicode="&#xe065;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
<glyph unicode="&#xe066;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
<glyph unicode="&#xe067;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
<glyph unicode="&#xe068;" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
<glyph unicode="&#xe069;" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe070;" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
<glyph unicode="&#xe071;" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
<glyph unicode="&#xe072;" d="M200 0l900 550l-900 550v-1100z" />
<glyph unicode="&#xe073;" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
<glyph unicode="&#xe074;" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
<glyph unicode="&#xe075;" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
<glyph unicode="&#xe076;" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
<glyph unicode="&#xe077;" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
<glyph unicode="&#xe078;" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
<glyph unicode="&#xe079;" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
<glyph unicode="&#xe080;" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
<glyph unicode="&#xe081;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
<glyph unicode="&#xe082;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
<glyph unicode="&#xe083;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
<glyph unicode="&#xe084;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
<glyph unicode="&#xe085;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
<glyph unicode="&#xe086;" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
<glyph unicode="&#xe087;" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
<glyph unicode="&#xe088;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
<glyph unicode="&#xe089;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
<glyph unicode="&#xe090;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
<glyph unicode="&#xe091;" d="M0 547l600 453v-300h600v-300h-600v-301z" />
<glyph unicode="&#xe092;" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
<glyph unicode="&#xe093;" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
<glyph unicode="&#xe094;" d="M104 600h296v600h300v-600h298l-449 -600z" />
<glyph unicode="&#xe095;" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
<glyph unicode="&#xe096;" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
<glyph unicode="&#xe097;" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
<glyph unicode="&#xe101;" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
<glyph unicode="&#xe102;" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
<glyph unicode="&#xe103;" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
<glyph unicode="&#xe104;" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
<glyph unicode="&#xe105;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
<glyph unicode="&#xe106;" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
<glyph unicode="&#xe107;" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
<glyph unicode="&#xe108;" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
<glyph unicode="&#xe109;" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
<glyph unicode="&#xe110;" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
<glyph unicode="&#xe111;" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
<glyph unicode="&#xe112;" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
<glyph unicode="&#xe113;" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
<glyph unicode="&#xe114;" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
<glyph unicode="&#xe115;" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
<glyph unicode="&#xe116;" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
<glyph unicode="&#xe117;" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
<glyph unicode="&#xe118;" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
<glyph unicode="&#xe119;" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
<glyph unicode="&#xe120;" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
<glyph unicode="&#xe121;" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
<glyph unicode="&#xe122;" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
<glyph unicode="&#xe123;" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
<glyph unicode="&#xe124;" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
<glyph unicode="&#xe125;" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
<glyph unicode="&#xe126;" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
<glyph unicode="&#xe127;" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
<glyph unicode="&#xe128;" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
<glyph unicode="&#xe129;" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
<glyph unicode="&#xe130;" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
<glyph unicode="&#xe131;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
<glyph unicode="&#xe132;" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
<glyph unicode="&#xe133;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
<glyph unicode="&#xe134;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
<glyph unicode="&#xe135;" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
<glyph unicode="&#xe136;" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
<glyph unicode="&#xe137;" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
<glyph unicode="&#xe138;" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
<glyph unicode="&#xe139;" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
<glyph unicode="&#xe140;" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
<glyph unicode="&#xe141;" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
<glyph unicode="&#xe142;" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
<glyph unicode="&#xe143;" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
<glyph unicode="&#xe144;" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
<glyph unicode="&#xe145;" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
<glyph unicode="&#xe146;" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
<glyph unicode="&#xe148;" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
<glyph unicode="&#xe149;" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
<glyph unicode="&#xe150;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
<glyph unicode="&#xe151;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
<glyph unicode="&#xe152;" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
<glyph unicode="&#xe153;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
<glyph unicode="&#xe154;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
<glyph unicode="&#xe155;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
<glyph unicode="&#xe156;" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
<glyph unicode="&#xe157;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
<glyph unicode="&#xe158;" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
<glyph unicode="&#xe159;" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
<glyph unicode="&#xe160;" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
<glyph unicode="&#xe161;" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
<glyph unicode="&#xe162;" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
<glyph unicode="&#xe163;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
<glyph unicode="&#xe164;" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
<glyph unicode="&#xe165;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
<glyph unicode="&#xe166;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe167;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe168;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe169;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe170;" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
<glyph unicode="&#xe171;" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
<glyph unicode="&#xe172;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
<glyph unicode="&#xe173;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
<glyph unicode="&#xe174;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
<glyph unicode="&#xe175;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
<glyph unicode="&#xe176;" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
<glyph unicode="&#xe177;" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
<glyph unicode="&#xe178;" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
<glyph unicode="&#xe179;" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
<glyph unicode="&#xe180;" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
<glyph unicode="&#xe181;" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
<glyph unicode="&#xe182;" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
<glyph unicode="&#xe183;" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
<glyph unicode="&#xe184;" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
<glyph unicode="&#xe185;" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
<glyph unicode="&#xe186;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
<glyph unicode="&#xe187;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
<glyph unicode="&#xe188;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
<glyph unicode="&#xe189;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
<glyph unicode="&#xe190;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
<glyph unicode="&#xe191;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
<glyph unicode="&#xe192;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
<glyph unicode="&#xe193;" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
<glyph unicode="&#xe194;" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
<glyph unicode="&#xe195;" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
<glyph unicode="&#xe197;" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
<glyph unicode="&#xe198;" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
<glyph unicode="&#xe199;" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
<glyph unicode="&#xe200;" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -1,117 +1,65 @@
<?php
// Only enable cookie secure if we are supporting https.
// Ideally, this would always be on and we would force https,
// but the default installation configs are likely to be installed by
// amature users on the setup that does not have https. Forcing
// cookie_secure on non https would result in confusing login problems.
if(!empty($_SERVER['HTTPS'])){
ini_set('session.cookie_secure', '1');
}
ini_set('session.cookie_httponly', '1');
$configRun = false;
$extensions = get_loaded_extensions();
$airtimeSetup = false;
error_reporting(E_ALL|E_STRICT);
function exception_error_handler($errno, $errstr, $errfile, $errline)
{
//Check if the statement that threw this error wanted its errors to be
//suppressed. If so then return without with throwing exception.
if (0 === error_reporting()) {
return;
function showConfigCheckPage() {
global $configRun;
if (!$configRun) {
// This will run any necessary setup we need if
// configuration hasn't been initialized
checkConfiguration();
}
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
return false;
require_once(CONFIG_PATH . 'config-check.php');
die();
}
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
function isApiCall() {
$path = $_SERVER['PHP_SELF'];
return strpos($path, "api") !== false;
}
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
// Define application path constants
define('ROOT_PATH', dirname( __DIR__) . '/');
define('LIB_PATH', ROOT_PATH . 'library/');
define('BUILD_PATH', ROOT_PATH . 'build/');
define('SETUP_PATH', BUILD_PATH . 'airtime-setup/');
define('APPLICATION_PATH', ROOT_PATH . 'application/');
define('CONFIG_PATH', APPLICATION_PATH . 'configs/');
define('VENDOR_PATH', ROOT_PATH . '../vendor/');
defined('VERBOSE_STACK_TRACE')
|| define('VERBOSE_STACK_TRACE', (getenv('VERBOSE_STACK_TRACE') ? getenv('VERBOSE_STACK_TRACE') : true));
define("AIRTIME_CONFIG_STOR", "/etc/airtime/");
//Vendors
set_include_path(realpath(dirname(__FILE__) . '/../../vendor'));
define('AIRTIME_CONFIG', 'airtime.conf');
//Vendors (Composer)
set_include_path(VENDOR_PATH . PATH_SEPARATOR . get_include_path());
// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
get_include_path(),
realpath(APPLICATION_PATH . '/../library')
)));
set_include_path(LIB_PATH . PATH_SEPARATOR . get_include_path());
set_include_path(APPLICATION_PATH . '/common' . PATH_SEPARATOR . get_include_path());
//Propel classes.
set_include_path(APPLICATION_PATH . '/models' . PATH_SEPARATOR . get_include_path());
//Controllers
set_include_path(APPLICATION_PATH . '/controllers' . PATH_SEPARATOR . get_include_path());
//Controller plugins.
set_include_path(APPLICATION_PATH . '/controllers/plugins' . PATH_SEPARATOR . get_include_path());
//Services.
set_include_path(APPLICATION_PATH . '/services/' . PATH_SEPARATOR . get_include_path());
//Zend framework
if (file_exists('/usr/share/php/libzend-framework-php')) {
set_include_path('/usr/share/php/libzend-framework-php' . PATH_SEPARATOR . get_include_path());
if (!@include_once(VENDOR_PATH . 'propel/propel1/runtime/lib/Propel.php'))
{
die('Error: Propel not found. Did you install Airtime\'s third-party dependencies with composer? (Check the README.)');
}
//cloud storage directory
set_include_path(APPLICATION_PATH . '/cloud_storage' . PATH_SEPARATOR . get_include_path());
require_once(CONFIG_PATH . 'conf.php');
require_once(SETUP_PATH . 'load.php');
//Upgrade directory
set_include_path(APPLICATION_PATH . '/upgrade/' . PATH_SEPARATOR . get_include_path());
//Common directory
set_include_path(APPLICATION_PATH . '/common/' . PATH_SEPARATOR . get_include_path());
/** Zend_Application */
require_once 'Zend/Application.php';
$application = new Zend_Application(
APPLICATION_ENV,
//$_SERVER["AIRTIME_APPINI"] // Old SaaS customization that's no longer needed -- Albert May 2, 2014
APPLICATION_PATH . '/configs/application.ini'
);
require_once (APPLICATION_PATH."/logging/Logging.php");
Logging::setLogPath('/var/log/airtime/zendphp.log');
Logging::setupParseErrorLogging();
// Create application, bootstrap, and run
try {
$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cli') {
set_include_path(APPLICATION_PATH . PATH_SEPARATOR . get_include_path());
require_once("Bootstrap.php");
} else {
$application->bootstrap()->run();
}
} catch (Exception $e) {
if ($e->getCode() == 401)
{
header($_SERVER['SERVER_PROTOCOL'] . ' 401 Unauthorized', true, 401);
return;
}
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
Logging::error($e->getMessage());
if (VERBOSE_STACK_TRACE) {
echo $e->getMessage() . '<br>';
echo "<pre>";
echo $e->getTraceAsString();
echo "</pre>";
Logging::error($e->getTraceAsString());
} else {
Logging::error($e->getTrace());
}
throw $e;
// This allows us to pass ?config as a parameter to any page
// and get to the config checklist.
if (array_key_exists('config', $_GET)) {
showConfigCheckPage();
}
// If a configuration file exists, forward to our boot script
if (file_exists(AIRTIME_CONFIG_STOR . AIRTIME_CONFIG)) {
require_once(APPLICATION_PATH . 'airtime-boot.php');
}
// Otherwise, we'll need to run our configuration setup
else {
$airtimeSetup = true;
require_once(SETUP_PATH . 'setup-config.php');
}

View File

@ -132,22 +132,6 @@ function setCollapsibleWidgetJsCode() {
$('#thirdPartyApi-element input').click(x);
}
//function setSoundCloudCheckBoxListener() {
// var subCheckBox= $("#UseSoundCloud,#SoundCloudDownloadbleOption");
// var mainCheckBox= $("#UploadToSoundcloudOption");
// subCheckBox.change(function(e){
// if (subCheckBox.is(':checked')) {
// mainCheckBox.attr("checked", true);
// }
// });
//
// mainCheckBox.change(function(e){
// if (!mainCheckBox.is(':checked')) {
// $("#UseSoundCloud,#SoundCloudDownloadbleOption").attr("checked", false);
// }
// });
//}
function removeLogo() {
$.post(baseUrl+'Preference/remove-logo', function(json){});
location.reload();
@ -190,7 +174,6 @@ $(document).ready(function() {
showErrorSections();
//setSoundCloudCheckBoxListener();
setMailServerInputReadonly();
setSystemFromEmailReadonly();
setConfigureMailServerListener();

View File

@ -195,31 +195,6 @@ function eventRender(event, element, view) {
$(element).find(".fc-event-content").append(div);
}
//add the record/rebroadcast/soundcloud icons if needed
//if (event.record === 1) {
// if (view.name === 'agendaDay' || view.name === 'agendaWeek') {
// if (event.soundcloud_id === -1) {
// $(element).find(".fc-event-time").before('<span class="small-icon recording"></span>');
// } else if ( event.soundcloud_id > 0) {
// $(element).find(".fc-event-time").before('<span class="small-icon recording"></span><span class="small-icon soundcloud"></span>');
// } else if (event.soundcloud_id === -2) {
// $(element).find(".fc-event-time").before('<span class="small-icon recording"></span><span class="small-icon progress"></span>');
// } else if (event.soundcloud_id === -3) {
// $(element).find(".fc-event-time").before('<span class="small-icon recording"></span><span class="small-icon sc-error"></span>');
// }
// } else if (view.name === 'month') {
// if(event.soundcloud_id === -1) {
// $(element).find(".fc-event-title").after('<span class="small-icon recording"></span>');
// } else if (event.soundcloud_id > 0) {
// $(element).find(".fc-event-title").after('<span class="small-icon recording"></span><span class="small-icon soundcloud"></span>');
// } else if (event.soundcloud_id === -2) {
// $(element).find(".fc-event-title").after('<span class="small-icon recording"></span><span class="small-icon progress"></span>');
// } else if (event.soundcloud_id === -3) {
// $(element).find(".fc-event-title").after('<span class="small-icon recording"></span><span class="small-icon sc-error"></span>');
// }
// }
//}
if (event.record === 0 && event.rebroadcast === 0) {
if (view.name === 'agendaDay' || view.name === 'agendaWeek') {
if (event.show_empty === 1) {

View File

@ -63,25 +63,6 @@ function confirmCancelRecordedShow(show_instance_id){
}
}
//function uploadToSoundCloud(show_instance_id, el){
//
// var url = baseUrl+"Schedule/upload-to-sound-cloud",
// $el = $(el),
// $span = $el.find(".soundcloud");
//
// $.post(url, {id: show_instance_id, format: "json"});
//
// //first upload to soundcloud.
// if ($span.length === 0){
// $span = $("<span/>", {"class": "progress"});
//
// $el.find(".fc-event-title").after($span);
// }
// else {
// $span.removeClass("soundcloud").addClass("progress");
// }
//}
function checkCalendarSCUploadStatus(){
var url = baseUrl+'Library/get-upload-to-soundcloud-status',
span,
@ -421,24 +402,6 @@ $(document).ready(function() {
oItems.content.callback = callback;
}
//define a soundcloud upload callback.
//if (oItems.soundcloud_upload !== undefined) {
//
// callback = function() {
// uploadToSoundCloud(data.id, this.context);
// };
// oItems.soundcloud_upload.callback = callback;
//}
//define a view on soundcloud callback.
//if (oItems.soundcloud_view !== undefined) {
//
// callback = function() {
// window.open(oItems.soundcloud_view.url);
// };
// oItems.soundcloud_view.callback = callback;
//}
//define a cancel recorded show callback.
if (oItems.cancel_recorded !== undefined) {

View File

@ -0,0 +1,151 @@
/**
* Do some cleanup when we get a success response from a POST request
* during setup
* @param data the POST request return data
* @param e the jquery event
*/
function cleanupStep(data, e) {
showFeedback(data);
// If there are no errors, we can continue with
// the installation process
if (data.errors.length == 0) {
// Call nextSlide from the submit button's context
nextSlide.call($(e.target));
}
removeOverlay();
}
/**
* Display the form feedback when we get POST results
* @param data the POST request return data
*/
function showFeedback(data) {
toggleMessage(data.message);
for (var i = 0; i < data.errors.length; i++) {
$("#" + data.errors[i]).parent().addClass("has-error has-feedback");
}
if (data.errors.length > 0) {
$(".help-message").addClass("has-error");
$(".has-error .form-control-feedback").show();
} else {
$(".help-message").addClass("has-success");
}
}
/**
* Reset form feedback when resubmitting
*/
function resetFeedback() {
$(".form-control-feedback").hide();
$(".has-success, .has-error, .has-feedback").removeClass("has-success has-error has-feedback");
}
/**
* Show the return message from the POST request, then set a timeout to hide it again
* @param msg the return message from the POST request
*/
function toggleMessage(msg) {
/*
* Since setting display:none; on this element causes odd behaviour
* with bootstrap, hide() the element so we can formSlide it in.
* This is only really only necessary the first time this
* function is called after page load.
*/
var help = $(".help-message");
help.html(msg).show();
}
/**
* Show the overlay and loading gif
*/
function addOverlay() {
$("body").append("<div id='overlay'></div><img src='css/images/file_import_loader.gif' id='loadingImage'/>");
}
/**
* Remove the overlay and loading gif
*/
function removeOverlay() {
var overlay = $("#overlay, #loadingImage");
$("#loadingImage").fadeOut(250);
$("#overlay").fadeOut(500, function() {
overlay.remove();
});
}
function formSlide(dir) {
var delta = (dir == "next") ? "-=100%" : "+=100%",
parent = $(this).parents("div.form-wrapper"),
toForm = (dir == "next") ? parent.next() : parent.prev();
parent.find(".btn").attr("disabled", "disabled");
toForm.find(".btn").removeAttr("disabled");
toForm.find(":input :first").focus();
$(".form-slider").animate({left: delta}, 500);
var stepCount = $("#stepCount"),
steps = parseInt(stepCount.html());
stepCount.html((dir == "next") ? (steps + 1) : (steps - 1));
hideRMQForm();
}
/**
* Fade out the previous setup step and fade in the next one
*/
function nextSlide() {
formSlide.call($(this), "next");
}
/**
* Fade out the current setup step and fade in the previous one
*/
function prevSlide() {
formSlide.call($(this), "prev");
}
/**
* Hide the RMQ form when the slider is called to avoid showing
* scrollbars on slider panels that fit vertically
*/
function hideRMQForm() {
$("#rmqFormBody").slideUp(500);
$("#advCaret").removeClass("caret-up");
}
function submitForm(e, obj) {
resetFeedback();
e.preventDefault();
var d = $(e.target).serializeArray();
addOverlay();
// Append .promise().done() rather than using a
// callback to avoid call duplication
$("#overlay, #loadingImage").fadeIn(500).promise().done(function() {
// Proxy function for passing the event to the cleanup function
var cleanupProxy = function(data) {
cleanupStep.call(this, data, e);
};
$.post('setup/setup-functions.php?obj=' + obj, d, cleanupProxy, "json");
});
}
$(function() {
// Stop the user from dragging the slider
$(".form-slider").draggable('disable');
$(".btn").attr("disabled", "disabled");
$("form:first .btn").removeAttr("disabled");
window.onresize = function() {
var headerHeight = $(".header").outerHeight(),
viewport = $(".viewport"),
viewportHeight = viewport.outerHeight();
// If the viewport would go outside the page bounds,
// shrink it to fit the window
if (viewportHeight + headerHeight > window.innerHeight) {
viewport.css("height", window.innerHeight - headerHeight);
}
// Otherwise, go back to what we have in the stylesheet
else {
viewport.css("height", "");
}
};
});

View File

@ -0,0 +1,196 @@
<?php
/**
* User: sourcefabric
* Date: 02/12/14
* Class DatabaseSetup
* Wrapper class for validating and installing the Airtime database during the installation process
*/
class DatabaseSetup extends Setup {
// airtime.conf section header
const SECTION = "[database]";
// Constant form field names for passing errors back to the front-end
const DB_USER = "dbUser",
DB_PASS = "dbPass",
DB_NAME = "dbName",
DB_HOST = "dbHost";
// Form field values
private $user, $pass, $name, $host;
// Array of key->value pairs for airtime.conf
static $properties;
static $dbh = null;
public function __construct($settings) {
$this->user = $settings[self::DB_USER];
$this->pass = $settings[self::DB_PASS];
$this->name = $settings[self::DB_NAME];
$this->host = $settings[self::DB_HOST];
self::$properties = array(
"host" => $this->host,
"dbname" => $this->name,
"dbuser" => $this->user,
"dbpass" => $this->pass,
);
}
private function setNewDatabaseConnection($dbName) {
self::$dbh = new PDO("pgsql:host=" . $this->host . ";dbname=" . $dbName . ";port=5432"
. ";user=" . $this->user . ";password=" . $this->pass);
$err = self::$dbh->errorInfo();
if ($err[1] != null) {
throw new PDOException();
}
}
/**
* Runs various database checks against the given settings. If a database with the given name already exists,
* we attempt to install the Airtime schema. If not, we first check if the user can create databases, then try
* to create the database. If we encounter errors, the offending fields are returned in an array to the browser.
* @return array associative array containing a display message and fields with errors
* @throws AirtimeDatabaseException
*/
public function runSetup() {
try {
$this->setNewDatabaseConnection("postgres");
if ($this->checkDatabaseExists()) {
$this->installDatabaseTables();
} else {
$this->checkUserCanCreateDb();
$this->createDatabase();
$this->installDatabaseTables();
}
} catch (PDOException $e) {
throw new AirtimeDatabaseException("Couldn't establish a connection to the database! "
. "Please check your credentials and try again. "
. "PDO Exception: " . $e->getMessage(),
array(
self::DB_NAME,
self::DB_USER,
self::DB_PASS,
));
}
$this->writeToTemp();
self::$dbh = null;
return array(
"message" => "Airtime database was created successfully!",
"errors" => array(),
);
}
protected function writeToTemp() {
parent::writeToTemp(self::SECTION, self::$properties);
}
private function installDatabaseTables() {
$this->checkDatabaseEncoding();
$this->setNewDatabaseConnection($this->name);
$this->checkSchemaExists();
$this->createDatabaseTables();
}
/**
* Check if the database settings and credentials given are valid
* @return boolean true if the database given exists and the user is valid and can access it
*/
private function checkDatabaseExists() {
$statement = self::$dbh->prepare("SELECT datname FROM pg_database WHERE datname = :dbname");
$statement->execute(array(":dbname" => $this->name));
$result = $statement->fetch();
return isset($result[0]);
}
/**
* Check if the database schema has already been set up
* @throws AirtimeDatabaseException
*/
private function checkSchemaExists() {
$statement = self::$dbh->prepare("SELECT EXISTS (SELECT relname FROM pg_class WHERE relname='cc_files')");
$statement->execute();
$result = $statement->fetch();
if (isset($result[0]) && $result[0] == "t") {
throw new AirtimeDatabaseException("Airtime is already installed in this database!", array());
}
}
/**
* Check if the given user has access on the given host to create a new database
* @throws AirtimeDatabaseException
*/
private function checkUserCanCreateDb() {
$statement = self::$dbh->prepare("SELECT 1 FROM pg_roles WHERE rolname=:dbuser AND rolcreatedb='t'");
$statement->execute(array(":dbuser" => $this->user));
$result = $statement->fetch();
if (!isset($result[0])) {
throw new AirtimeDatabaseException("No database " . $this->name . " exists; user '" . $this->user
. "' does not have permission to create databases on " . $this->host,
array(
self::DB_NAME,
self::DB_USER,
self::DB_PASS,
));
}
}
/**
* Creates the Airtime database using the given credentials
* @throws AirtimeDatabaseException
*/
private function createDatabase() {
$statement = self::$dbh->prepare("CREATE DATABASE " . pg_escape_string($this->name)
. " WITH ENCODING 'UTF8' TEMPLATE template0"
. " OWNER " . pg_escape_string($this->user));
if (!$statement->execute()) {
throw new AirtimeDatabaseException("There was an error creating the database!",
array(self::DB_NAME,));
}
}
/**
* Creates the Airtime database schema using the given credentials
* @throws AirtimeDatabaseException
*/
private function createDatabaseTables() {
$sqlDir = dirname(dirname(__DIR__)) . "/build/sql/";
$files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql");
foreach ($files as $f) {
try {
/*
* Unfortunately, we need to use exec here due to PDO's lack of support for importing
* multi-line .sql files. PDO->exec() almost works, but any SQL errors stop the import,
* so the necessary DROPs on non-existent tables make it unusable. Prepared statements
* have multiple issues; they similarly die on any SQL errors, fail to read in multi-line
* commands, and fail on any unescaped ? or $ characters.
*/
exec("export PGPASSWORD=" . $this->pass . " && psql -U " . $this->user . " --dbname "
. $this->name . " -h " . $this->host . " -f $sqlDir$f 2>/dev/null", $out, $status);
} catch (Exception $e) {
throw new AirtimeDatabaseException("There was an error setting up the Airtime schema!",
array(self::DB_NAME,));
}
}
}
/**
* Checks whether the newly-created database's encoding was properly set to UTF8
* @throws AirtimeDatabaseException
*/
private function checkDatabaseEncoding() {
$statement = self::$dbh->prepare("SELECT pg_encoding_to_char(encoding) "
. "FROM pg_database WHERE datname = :dbname");
$statement->execute(array(":dbname" => $this->name));
$encoding = $statement->fetch();
if (!($encoding && $encoding[0] == "UTF8")) {
throw new AirtimeDatabaseException("The database was installed with an incorrect encoding type!",
array(self::DB_NAME,));
}
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* User: sourcefabric
* Date: 08/12/14
*
* Class GeneralSetup
*
* Wrapper class for validating and setting up general settings during the installation process
*/
class GeneralSetup extends Setup {
// airtime.conf section header
const SECTION = "[general]";
// Constant form field names for passing errors back to the front-end
const GENERAL_PORT = "generalPort",
GENERAL_HOST = "generalHost";
// Form field values
static $user, $host, $port, $root;
// Array of key->value pairs for airtime.conf
static $properties;
// Message and error fields to return to the front-end
static $message = null;
static $errors = array();
function __construct($settings) {
self::$host = $settings[self::GENERAL_HOST];
self::$port = $settings[self::GENERAL_PORT];
self::$properties = array(
"api_key" => $this->generateRandomString(),
"base_url" => self::$host,
"base_port" => self::$port,
);
}
function writeToTemp() {
parent::writeToTemp(self::SECTION, self::$properties);
}
/**
* @return array associative array containing a display message and fields with errors
*/
function runSetup() {
// TODO Do we need to validate these settings?
if (count(self::$errors) <= 0) {
$this->writeToTemp();
}
return array(
"message" => self::$message,
"errors" => self::$errors
);
}
}

View File

@ -0,0 +1,141 @@
<?php
define("CONFIG_PATH", dirname(dirname( __DIR__)) . "/application/configs/");
define("DEFAULT_STOR_DIR", "/srv/airtime/stor/");
require_once(dirname(dirname( __DIR__)) . "/../vendor/propel/propel1/runtime/lib/Propel.php");
require_once(CONFIG_PATH . 'conf.php');
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/map/CcMusicDirsTableMap.php");
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/om/BaseCcMusicDirsQuery.php");
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/CcMusicDirsQuery.php");
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/om/BaseCcMusicDirs.php");
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/CcMusicDirs.php");
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/om/BaseCcMusicDirsPeer.php");
require_once(dirname(dirname( __DIR__)) . "/application/models/airtime/CcMusicDirsPeer.php");
/**
* Author: sourcefabric
* Date: 08/12/14
*
* Class MediaSetup
*
* Wrapper class for validating and setting up media folder during the installation process
*/
class MediaSetup extends Setup {
const MEDIA_FOLDER = "mediaFolder";
const AIRTIME_CONF_PATH = "/etc/airtime/airtime.conf";
static $path;
static $message = null;
static $errors = array();
function __construct($settings) {
self::$path = $settings[self::MEDIA_FOLDER];
}
/**
* @return array associative array containing a display message and fields with errors
*/
function runSetup() {
// If the path passed in is empty, set it to the default
if (strlen(self::$path) == 0) {
self::$path = DEFAULT_STOR_DIR;
if (!file_exists(DEFAULT_STOR_DIR)) {
mkdir(DEFAULT_STOR_DIR, 0755, true);
}
}
// Append a trailing / if they didn't
if (!(substr(self::$path, -1) == "/")) {
self::$path .= "/";
}
if (file_exists(self::$path)) {
$this->setupMusicDirectory();
} else {
self::$message = "Invalid path!";
self::$errors[] = self::MEDIA_FOLDER;
}
// Finalize and move airtime.conf.temp
if (file_exists("/etc/airtime/")) {
if (!$this->moveAirtimeConfig()) {
$message = "Error moving airtime.conf or deleting /tmp/airtime.conf.temp!";
$errors[] = "ERR";
}
/*
* If we're upgrading from an old Airtime instance (pre-2.5.2) we rename their old
* airtime.conf to airtime.conf.tmp during the setup process. Now that we're done,
* we can rename it to airtime.conf.bak to avoid confusion.
*/
if (file_exists(self::AIRTIME_CONF_PATH . ".tmp")) {
rename(self::AIRTIME_CONF_PATH . ".tmp", self::AIRTIME_CONF_PATH . ".bak");
}
} else {
$message = "Failed to move airtime.conf; /etc/airtime doesn't exist!";
$errors[] = "ERR";
}
return array(
"message" => self::$message,
"errors" => self::$errors
);
}
/**
* Moves /tmp/airtime.conf.temp to /etc/airtime.conf and then removes it to complete setup
* @return boolean false if either of the copy or removal operations fail
*/
function moveAirtimeConfig() {
return copy(AIRTIME_CONF_TEMP_PATH, self::AIRTIME_CONF_PATH)
&& unlink(AIRTIME_CONF_TEMP_PATH);
}
/**
* Add the given directory to cc_music_dirs
* TODO Should we check for an existing entry in cc_music_dirs?
*/
function setupMusicDirectory() {
try {
$_SERVER['AIRTIME_CONF'] = AIRTIME_CONF_TEMP_PATH;
Propel::init(CONFIG_PATH . "airtime-conf-production.php");
$con = Propel::getConnection();
} catch(Exception $e) {
self::$message = "Failed to insert media folder; database isn't configured properly!";
self::$errors[] = self::MEDIA_FOLDER;
return;
}
$this->runMusicDirsQuery($con);
}
function runMusicDirsQuery($con) {
try {
if ($this->checkMusicDirectoryExists($con)) {
$dir = CcMusicDirsQuery::create()->findPk(1, $con);
} else {
$dir = new CcMusicDirs();
}
$dir->setDirectory(self::$path)
->setType("stor")
->save();
self::$message = "Successfully set up media folder!";
Propel::close();
unset($_SERVER['AIRTIME_CONF']);
} catch (Exception $e) {
self::$message = "Failed to insert " . self::$path . " into cc_music_dirs";
self::$errors[] = self::MEDIA_FOLDER;
}
}
function checkMusicDirectoryExists($con) {
$entry = CcMusicDirsQuery::create()->findPk(1, $con);
return isset($entry) && $entry;
}
}

View File

@ -0,0 +1,100 @@
<?php
require_once dirname(dirname( __DIR__)) . '/library/php-amqplib/amqp.inc';
/**
* User: sourcefabric
* Date: 02/12/14
*
* Class RabbitMQSetup
*
* Wrapper class for validating and setting up RabbitMQ during the installation process
*/
class RabbitMQSetup extends Setup {
// airtime.conf section header
const SECTION = "[rabbitmq]";
// Constant form field names for passing errors back to the front-end
const RMQ_USER = "rmqUser",
RMQ_PASS = "rmqPass",
RMQ_PORT = "rmqPort",
RMQ_HOST = "rmqHost",
RMQ_VHOST = "rmqVHost";
// Form field values
static $user, $pass, $host, $port, $vhost;
// Array of key->value pairs for airtime.conf
static $properties;
// Message and error fields to return to the front-end
static $message = null;
static $errors = array();
function __construct($settings) {
self::$user = $settings[self::RMQ_USER];
self::$pass = $settings[self::RMQ_PASS];
self::$port = $settings[self::RMQ_PORT];
self::$host = $settings[self::RMQ_HOST];
self::$vhost = $settings[self::RMQ_VHOST];
self::$properties = array(
"host" => self::$host,
"port" => self::$port,
"user" => self::$user,
"password" => self::$pass,
"vhost" => self::$vhost,
);
}
/**
* @return array associative array containing a display message and fields with errors
*/
function runSetup() {
try {
if ($this->checkRMQConnection()) {
self::$message = "Connection successful!";
} else {
$this->identifyRMQConnectionError();
}
} catch(Exception $e) {
$this->identifyRMQConnectionError();
}
if (count(self::$errors) <= 0) {
$this->writeToTemp();
}
return array(
"message" => self::$message,
"errors" => self::$errors
);
}
function writeToTemp() {
parent::writeToTemp(self::SECTION, self::$properties);
}
function checkRMQConnection() {
$conn = new AMQPConnection(self::$host,
self::$port,
self::$user,
self::$pass,
self::$vhost);
return isset($conn);
}
function identifyRMQConnectionError() {
// It's impossible to identify errors coming out of amqp.inc without a major
// rewrite, so for now just tell the user ALL THE THINGS went wrong
self::$message = "Couldn't connect to RabbitMQ server! Please check if the server "
. "is running and your credentials are correct.";
self::$errors[] = self::RMQ_USER;
self::$errors[] = self::RMQ_PASS;
self::$errors[] = self::RMQ_HOST;
self::$errors[] = self::RMQ_PORT;
self::$errors[] = self::RMQ_VHOST;
}
}

View File

@ -0,0 +1,126 @@
<?php
define("BUILD_PATH", dirname(dirname(__DIR__)) . "/build/");
define("AIRTIME_CONF_TEMP_PATH", "/tmp/airtime.conf.temp");
/**
* Class Setup
*
* @author sourcefabric
*
* Abstract superclass for the setup and installation process
*/
abstract class Setup {
abstract function __construct($settings);
abstract function runSetup();
/**
* Write new property values to a given section in airtime.conf.temp
*
* @param string $section
* the configuration section to write to
* @param array $properties
* the configuration properties and values to overwrite
*/
protected function writeToTemp($section, $properties) {
if (!file_exists(AIRTIME_CONF_TEMP_PATH)) {
copy(BUILD_PATH . "airtime.example.conf", AIRTIME_CONF_TEMP_PATH);
}
$file = file(AIRTIME_CONF_TEMP_PATH);
$fileOutput = "";
$inSection = false;
foreach ($file as $line) {
if (strpos($line, $section) !== false) {
$inSection = true;
} else if (strpos($line, "[") !== false) {
$inSection = false;
}
if (substr(trim($line), 0, 1) == "#") {
/* Workaround to strip comments from airtime.conf.
* We need to do this because python's ConfigObj and PHP
* have different (and equally strict) rules about comment
* characters in configuration files.
*/
} else if ($inSection) {
$key = trim(@substr($line, 0, strpos($line, "=")));
$fileOutput .= $key && isset($properties[$key]) ? $key . " = " . $properties[$key] . "\n" : $line;
} else {
$fileOutput .= $line;
}
}
file_put_contents(AIRTIME_CONF_TEMP_PATH, $fileOutput);
}
/**
* Generates a random string.
*
* @param integer $p_len
* length of the output string
* @param string $p_chars
* characters to use in the output string
* @return string the generated random string
*/
protected function generateRandomString($p_len = 20, $p_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') {
$string = '';
for($i = 0; $i < $p_len; $i++) {
$pos = mt_rand(0, strlen($p_chars) - 1);
$string .= $p_chars{$pos};
}
return $string;
}
}
/**
* Class AirtimeDatabaseException
*
* @author sourcefabric
*
* Exception class for database setup errors
*/
class AirtimeDatabaseException extends Exception {
protected $message = "Unknown Airtime database exception";
protected $errors = array();
public function __construct($message = null, $errors = array(), $code = 0, Exception $previous = null) {
parent::__construct($message, $code, $previous);
$this->errors = $errors;
}
public function getErrorFields() {
return $this->errors;
}
}
// Import Setup subclasses
require_once ('database-setup.php');
require_once ('rabbitmq-setup.php');
require_once ('general-setup.php');
require_once ('media-setup.php');
// If airtime.conf exists, we shouldn't be here
if (!file_exists("/etc/airtime/airtime.conf")) {
if (isset($_GET["obj"]) && $objType = $_GET["obj"]) {
$obj = new $objType($_POST);
if ($obj instanceof Setup) {
try {
$response = $obj->runSetup();
} catch (AirtimeDatabaseException $e) {
$response = array(
"message" => $e->getMessage(),
"errors" => $e->getErrorFields()
);
}
echo json_encode($response);
}
}
}

View File

@ -234,3 +234,6 @@
display: none;
}
.embedScheduleWidget {
width: 500px;
}

View File

@ -3,7 +3,10 @@ error_reporting(E_ALL | E_STRICT);
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../application'));
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../../application/'));
// Define path to configs directory
define('CONFIG_PATH', APPLICATION_PATH . '/configs/');
// Define application environment
defined('APPLICATION_ENV')

View File

@ -5,25 +5,20 @@ if (file_exists('/usr/share/php/libzend-framework-php')){
set_include_path('/usr/share/php/libzend-framework-php' . PATH_SEPARATOR . get_include_path());
}
#require_once('Zend/Loader/Autoloader.php');
class AirtimeInstall
{
const CONF_DIR_BINARIES = "/usr/lib/airtime";
const CONF_DIR_WWW = "/usr/share/airtime";
const CONF_DIR_LOG = "/var/log/airtime";
public static $databaseTablesCreated = false;
public static function GetAirtimeSrcDir()
{
return __DIR__."/../../airtime_mvc";
return __DIR__."/../../..";
}
public static function GetUtilsSrcDir()
{
return __DIR__."/../../utils";
return __DIR__."/../../../../utils";
}
/**
* Ensures that the user is running this PHP script with root
* permissions. If not running with root permissions, causes the
@ -37,7 +32,6 @@ class AirtimeInstall
exit(1);
}
}
/**
* Return the version of Airtime currently installed.
* If not installed, return null.
@ -51,14 +45,12 @@ class AirtimeInstall
} catch (PropelException $e) {
return null;
}
if (file_exists('/etc/airtime/airtime.conf')) {
$values = parse_ini_file('/etc/airtime/airtime.conf', true);
}
else {
return null;
}
$sql = "SELECT valstr FROM cc_pref WHERE keystr = 'system_version' LIMIT 1";
try {
@ -70,7 +62,6 @@ class AirtimeInstall
//the database because it was busy, so it just removed the tables instead.
return null;
}
//if version is empty string, then version is older than version 1.8.0
if ($version == '') {
try {
@ -82,10 +73,8 @@ class AirtimeInstall
$version = null;
}
}
return $version;
}
public static function DbTableExists($p_name)
{
$con = Propel::getConnection();
@ -97,7 +86,6 @@ class AirtimeInstall
}
return true;
}
public static function InstallQuery($sql, $verbose = true)
{
$con = Propel::getConnection();
@ -112,7 +100,6 @@ class AirtimeInstall
echo " ".$sql."\n\n";
}
}
public static function DropSequence($p_sequenceName)
{
AirtimeInstall::InstallQuery("DROP SEQUENCE IF EXISTS $p_sequenceName", false);
@ -142,20 +129,15 @@ class AirtimeInstall
}
return true;
}
/* TODO: This function should be moved to the media-monitor
* install script. */
public static function InstallStorageDirectory()
{
$CC_CONFIG = Config::getConfig();
echo "* Storage directory setup".PHP_EOL;
$ini = parse_ini_file(__DIR__."/airtime-install.ini");
$stor_dir = $ini["storage_dir"];
$dirs = array($stor_dir, $stor_dir."/organize");
foreach ($dirs as $dir){
if (!file_exists($dir)) {
if (mkdir($dir, 02775, true)){
@ -175,24 +157,19 @@ class AirtimeInstall
echo "* Error: Directory already exists, but is not writable: $rp".PHP_EOL;
exit(1);
}
echo "* Giving Apache permission to access $rp".PHP_EOL;
$success = chown($rp, $CC_CONFIG["webServerUser"]);
$success = chgrp($rp, $CC_CONFIG["webServerUser"]);
$success = chmod($rp, 0775);
}
}
public static function CreateDatabaseUser()
{
$CC_CONFIG = Config::getConfig();
echo " * Creating Airtime database user".PHP_EOL;
$username = $CC_CONFIG['dsn']['username'];
$password = $CC_CONFIG['dsn']['password'];
$command = "echo \"CREATE USER $username ENCRYPTED PASSWORD '$password' LOGIN CREATEDB NOCREATEUSER;\" | su postgres -c psql 2>/dev/null";
@exec($command, $output, $results);
if ($results == 0) {
echo " * Database user '{$CC_CONFIG['dsn']['username']}' created.".PHP_EOL;
@ -206,13 +183,9 @@ class AirtimeInstall
}
}
}
public static function CreateDatabase()
{
$CC_CONFIG = Config::getConfig();
$database = $CC_CONFIG['dsn']['database'];
$username = $CC_CONFIG['dsn']['username'];
#$command = "echo \"CREATE DATABASE $database OWNER $username\" | su postgres -c psql 2>/dev/null";
@ -222,37 +195,30 @@ class AirtimeInstall
putenv("LC_ALL=en_CA.UTF-8"); //Squash warnings when running unit tests
$command = "su postgres -c \"psql -l | cut -f2 -d' ' | grep -w '{$database}'\";";
exec($command, $output, $rv);
if ($rv == 0) {
//database already exists
echo "Database already exists." . PHP_EOL;
return true;
}
$command = "sudo -i -u postgres psql postgres -c \"CREATE DATABASE ".$database." WITH ENCODING 'UTF8' TEMPLATE template0 OWNER ".$username."\"";
@exec($command, $output, $results);
if ($results == 0) {
echo " * Database '{$CC_CONFIG['dsn']['database']}' created.".PHP_EOL;
echo " * Database $database created.".PHP_EOL;
} else {
if (count($output) > 0) {
echo " * Could not create database '{$CC_CONFIG['dsn']['database']}': ".PHP_EOL;
echo " * Could not create database $database: ".PHP_EOL;
echo implode(PHP_EOL, $output);
}
else {
echo " * Database '{$CC_CONFIG['dsn']['database']}' already exists.".PHP_EOL;
echo " * Database $database already exists.".PHP_EOL;
}
}
$databaseExisted = ($results != 0);
return $databaseExisted;
}
public static function InstallPostgresScriptingLanguage()
{
$con = Propel::getConnection();
// Install postgres scripting language
$sql = 'SELECT COUNT(*) FROM pg_language WHERE lanname = \'plpgsql\'';
$langIsInstalled = $con->query($sql)->fetchColumn(0);
@ -264,25 +230,19 @@ class AirtimeInstall
echo " * Postgres scripting language already installed".PHP_EOL;
}
}
public static function CreateDatabaseTables($p_dbuser, $p_dbpasswd, $p_dbname, $p_dbhost)
{
echo " * Creating database tables".PHP_EOL;
// Put Propel sql files in Database
//$command = AirtimeInstall::CONF_DIR_WWW."/library/propel/generator/bin/propel-gen ".AirtimeInstall::CONF_DIR_WWW."/build/ insert-sql 2>/dev/null";
$dir = self::GetAirtimeSrcDir()."/build/sql/";
$files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql");
foreach ($files as $f){
$command = "export PGPASSWORD=$p_dbpasswd && psql --username $p_dbuser --dbname $p_dbname --host $p_dbhost --file $dir$f 2>/dev/null";
$command = "export PGPASSWORD=$p_dbpasswd && psql --username $p_dbuser --dbname $p_dbname --host $p_dbhost --file $dir$f 2>&1";
@exec($command, $output, $results);
}
AirtimeInstall::$databaseTablesCreated = true;
}
public static function BypassMigrations($dir, $version)
{
$appDir = AirtimeInstall::GetAirtimeSrcDir();
@ -292,7 +252,6 @@ class AirtimeInstall
"--no-interaction --add migrations:version $version";
system($command);
}
public static function MigrateTablesToVersion($dir, $version)
{
$appDir = AirtimeInstall::GetAirtimeSrcDir();
@ -302,22 +261,18 @@ class AirtimeInstall
"--no-interaction migrations:migrate $version";
system($command);
}
public static function SetAirtimeVersion($p_version)
{
$con = Propel::getConnection();
$sql = "DELETE FROM cc_pref WHERE keystr = 'system_version'";
$con->exec($sql);
Application_Model_Preference::SetAirtimeVersion($p_version);
}
public static function SetUniqueId()
{
$uniqueId = md5(uniqid("", true));
Application_Model_Preference::SetUniqueId($uniqueId);
}
public static function GetAirtimeVersion()
{
$con = Propel::getConnection();
@ -325,72 +280,23 @@ class AirtimeInstall
$version = $con->query($sql)->fetchColumn(0);
return $version;
}
public static function DeleteFilesRecursive($p_path)
{
$command = "rm -rf \"$p_path\"";
exec($command);
}
public static function CreateSymlinksToUtils()
{
echo "* Creating /usr/bin symlinks".PHP_EOL;
AirtimeInstall::RemoveSymlinks();
echo "* Installing airtime-import".PHP_EOL;
$dir = AirtimeInstall::CONF_DIR_BINARIES."/utils/airtime-import/airtime-import";
exec("ln -s $dir /usr/bin/airtime-import");
echo "* Installing airtime-check-system".PHP_EOL;
$dir = AirtimeInstall::CONF_DIR_BINARIES."/utils/airtime-check-system";
exec("ln -s $dir /usr/bin/airtime-check-system");
echo "* Installing airtime-user".PHP_EOL;
$dir = AirtimeInstall::CONF_DIR_BINARIES."/utils/airtime-user";
exec("ln -s $dir /usr/bin/airtime-user");
echo "* Installing airtime-log".PHP_EOL;
$dir = AirtimeInstall::CONF_DIR_BINARIES."/utils/airtime-log";
exec("ln -s $dir /usr/bin/airtime-log");
}
public static function RemoveSymlinks()
{
exec("rm -f /usr/bin/airtime-import");
exec("rm -f /usr/bin/airtime-check-system");
exec("rm -f /usr/bin/airtime-user");
exec("rm -f /usr/bin/airtime-log");
exec("rm -f /usr/bin/airtime-clean-storage");
}
public static function InstallPhpCode()
{
$CC_CONFIG = Config::getConfig();
echo "* Installing PHP code to ".AirtimeInstall::CONF_DIR_WWW.PHP_EOL;
exec("mkdir -p ".AirtimeInstall::CONF_DIR_WWW);
exec("cp -R ".AirtimeInstall::GetAirtimeSrcDir()."/* ".AirtimeInstall::CONF_DIR_WWW);
}
public static function UninstallPhpCode()
{
echo "* Removing PHP code from ".AirtimeInstall::CONF_DIR_WWW.PHP_EOL;
exec('rm -rf "'.AirtimeInstall::CONF_DIR_WWW.'"');
}
public static function InstallBinaries()
{
echo "* Installing binaries to ".AirtimeInstall::CONF_DIR_BINARIES.PHP_EOL;
exec("mkdir -p ".AirtimeInstall::CONF_DIR_BINARIES);
exec("cp -R ".AirtimeInstall::GetUtilsSrcDir()." ".AirtimeInstall::CONF_DIR_BINARIES);
}
public static function UninstallBinaries()
{
echo "* Removing Airtime binaries from ".AirtimeInstall::CONF_DIR_BINARIES.PHP_EOL;
exec('rm -rf "'.AirtimeInstall::CONF_DIR_BINARIES.'"');
}
public static function DirCheck()
{
echo "Legend: \"+\" means the dir/file exists, \"-\" means that it does not.".PHP_EOL;
@ -412,54 +318,44 @@ class AirtimeInstall
}
}
}
public static function CreateZendPhpLogFile(){
$CC_CONFIG = Config::getConfig();
$path = AirtimeInstall::CONF_DIR_LOG;
$file = $path.'/zendphp.log';
if (!file_exists($path)){
mkdir($path, 0755, true);
}
touch($file);
chmod($file, 0644);
chown($file, $CC_CONFIG['webServerUser']);
chgrp($file, $CC_CONFIG['webServerUser']);
}
public static function RemoveLogDirectories(){
$path = AirtimeInstall::CONF_DIR_LOG;
echo "* Removing logs directory ".$path.PHP_EOL;
exec("rm -rf \"$path\"");
}
public static function CreateCronFile(){
echo "* Creating Cron File".PHP_EOL;
// Create CRON task to run every day. Time of day is initialized to a random time.
$hour = rand(0,23);
$minute = rand(0,59);
$fp = fopen('/etc/cron.d/airtime-crons','w');
fwrite($fp, "$minute $hour * * * root /usr/lib/airtime/utils/phone_home_stat\n");
fclose($fp);
}
public static function removeVirtualEnvDistributeFile(){
echo "* Removing distribute-0.6.10.tar.gz".PHP_EOL;
if(file_exists('/usr/share/python-virtualenv/distribute-0.6.10.tar.gz')){
exec("rm -f /usr/share/python-virtualenv/distribute-0.6.10.tar.gz");
}
}
public static function printUsage($opts)
{
$msg = $opts->getUsageMessage();
echo PHP_EOL."Usage: airtime-install [options]";
echo substr($msg, strpos($msg, "\n")).PHP_EOL;
}
public static function getOpts()
{
try {
@ -482,7 +378,6 @@ class AirtimeInstall
}
return $opts;
}
public static function checkPHPVersion()
{
if (PHP_VERSION_ID < 50300)

View File

@ -62,7 +62,7 @@ class TestHelper
// cc_subjs - Most of Airtime requires an admin account to work, which has id=1,
// so don't clear it.
// cc_music_dirs - Has foreign key constraints against cc_files, so we clear cc_files
// first and ckear cc_music_dirs after
// first and clear cc_music_dirs after
$tablesToNotClear = array("cc_subjs", "cc_music_dirs");
$con->beginTransaction();
@ -128,7 +128,7 @@ class TestHelper
public static function setupZendBootstrap()
{
$application = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH .'/configs/application.ini');
$application = new Zend_Application(APPLICATION_ENV, CONFIG_PATH . 'application.ini');
$application->bootstrap();
return $application;
}

View File

@ -17,7 +17,7 @@ class PreferenceUnitTest extends PHPUnit_Framework_TestCase
public function testSetHeadTitle()
{
$title = "unit test";
//This function is confusing and doesn't really work so we're just gonna let it slide...
//This function is confusing and doesn't really work so we're just gonna let it formSlide...
Application_Model_Preference::SetHeadTitle($title);
$this->assertEquals(Application_Model_Preference::GetHeadTitle(), $title);
}

View File

@ -33,5 +33,5 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $DIR
#Run the unit tests
phpunit --log-junit test_results.xml
phpunit --verbose --log-junit test_results.xml

View File

@ -25,7 +25,7 @@ class AirtimeMediaMonitorBootstrap():
api_clients -- reference of api_clients to communicate with airtime-server
"""
def __init__(self):
config = ConfigObj('/etc/airtime/media-monitor.cfg')
config = ConfigObj('/etc/airtime/airtime.conf')
self.api_client = apc.api_client_factory(config)
"""

View File

@ -18,7 +18,7 @@ if [ "$1" = "--enable" ]; then
echo "Changing ownership to user $1"
chmod -R a+rw /var/log/airtime/pypo
chmod a+r /etc/airtime/pypo.cfg
chmod a+r /etc/airtime/airtime.conf
chown -Rv $user:$user /var/tmp/airtime/pypo/
chmod -v a+r /etc/airtime/api_client.cfg
elif [ "$1" = "--disable" ]; then
@ -26,7 +26,7 @@ elif [ "$1" = "--disable" ]; then
user="pypo"
echo "Changing ownership to user $1"
chmod 644 /etc/airtime/pypo.cfg
chmod 644 /etc/airtime/airtime.conf
chown -Rv $user:$user /var/tmp/airtime/pypo/
chmod -v a+r /etc/airtime/api_client.cfg

625
install Executable file
View File

@ -0,0 +1,625 @@
#!/bin/bash -e
#-e Causes bash script to exit if any of the installers
#return with a non-zero return value.
if [[ $EUID -ne 0 ]]; then
echo "Please run as root user."
exit 1
fi
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
AIRTIMEROOT=${SCRIPT_DIR}
showhelp () {
echo "Usage: sudo bash install [options]
-h, --help, -?
Display usage information
-V, --version
Display version information
-v, --verbose
More output
-q, --quiet, --silent
No output except errors
-f, --force
Turn off interactive prompts
--distribution=DISTRIBUTION
Linux distribution the installation is being run on
--release=RELEASE
Distribution release
-d, --ignore-dependencies
Don't install binary dependencies
-w, --web-user=WEB_USER
Set the apache web user. Defaults to www-data. Only change
this setting if you've changed the default apache web user
-r, --web-root=WEB_ROOT
Set the web root for Airtime files
This will copy the Airtime application files, but you will need
to give your web user access to the given directory if it is
not accessible
-I, --in-place
Set the current Airtime directory as the web root
Note that you will need to give your web user permission to
access this directory if it is not accessible
-p, --postgres
Create a default postgres user named 'airtime' with password
'airtime'
-a, --apache
Install apache and deploy a basic configuration for Airtime
-i, --icecast
Install Icecast 2 and deploy a basic configuration for Airtime"
exit 0
}
showversion () {
version=$(php -r 'require_once(__DIR__ . "/airtime_mvc/application/configs/constants.php"); echo AIRTIME_CODE_VERSION;')
echo "Airtime Version ${version}"
exit 0
}
web_user="www-data"
web_root=""
in_place="f"
postgres="f"
apache="f"
icecast="f"
ignore_dependencies="f"
# Interactive
_i=1
# Verbose
_v=0
# Quiet
_q=0
upgrade="f"
dist=""
code=""
function verbose() {
if [[ ${_v} -eq 1 ]]; then
echo -e "$@"
fi
}
function loud() {
if [[ ${_q} -eq 0 ]]; then
echo -e "$@"
fi
}
# Evaluate commands silently if quiet
function loudCmd() {
if [[ ${_q} -eq 0 ]]; then
eval $@
else
eval $@ > /dev/null
fi
}
function checkCommandExists() {
set +e
command=$@
eval hash ${command} 2>/dev/null
commandFound=$?
if [[ ! ${commandFound} -eq 0 ]]; then
echo -e "Error: ${command} not found. Please ensure you have the corresponding dependency installed."
exit
fi
set -e
}
while :; do
case "$1" in
--help)
showhelp
;;
--version)
showversion
;;
--verbose)
_v=1
;;
--quiet|--silent)
_q=1
;;
--force)
_i=0
;;
--distribution)
if [ "$2" ]; then
dist=$2
shift 2
continue
else
echo 'ERROR: Must specify a non-empty "--distribution DISTRIBUTION" argument.' >&2
exit 1
fi
;;
--distribution=?*)
dist=${1#*=} # Delete everything up to "=" and assign the remainder.
;;
--distribution=)
echo 'ERROR: Must specify a non-empty "--distribution DISTRIBUTION" argument.' >&2
exit 1
;;
--release)
if [ "$2" ]; then
code=$2
shift 2
continue
else
echo 'ERROR: Must specify a non-empty "--release RELEASE" argument.' >&2
exit 1
fi
;;
--release=?*)
code=${1#*=} # Delete everything up to "=" and assign the remainder.
;;
--release=)
echo 'ERROR: Must specify a non-empty "--release RELEASE" argument.' >&2
exit 1
;;
--ignore-dependencies)
ignore_dependencies="t"
;;
--apache)
apache="t"
;;
--icecast)
icecast="t"
;;
--postgres)
postgres="t"
;;
--in-place)
in_place="t"
;;
--web-user)
if [ "$2" ]; then
web_user=$2
shift 2
continue
else
echo 'ERROR: Must specify a non-empty "--web-user WEB_USER" argument.' >&2
exit 1
fi
;;
--web-user=?*)
web_user=${1#*=} # Delete everything up to "=" and assign the remainder.
;;
--web-user=)
echo 'ERROR: Must specify a non-empty "--web-user=WEB_USER" argument.' >&2
exit 1
;;
--web-root)
if [ "$2" ]; then
web_root=$(readlink -f $2)
shift 2
continue
else
echo 'ERROR: Must specify a non-empty "--web-root WEB_ROOT" argument.' >&2
exit 1
fi
;;
--web-root=?*)
web_root=${1#*=} # Delete everything up to "=" and assign the remainder.
;;
--web-root=)
echo 'ERROR: Must specify a non-empty "--web-root=WEB_ROOT" argument.' >&2
exit 1
;;
--)
shift
break
;;
-?*)
for ((i = 1; i < ${#1}; i++)); do
case "${1:$i:1}" in
h|\?)
showhelp
;;
V)
showversion
;;
v)
_v=1
;;
q)
_q=1
;;
f)
_i=0
;;
d)
ignore_dependencies="t"
;;
a)
apache="t"
;;
i)
icecast="t"
;;
p)
postgres="t"
;;
I)
in_place="t"
;;
w)
if [ "$2" ]; then
web_user=$2
continue
else
echo 'ERROR: Must specify a non-empty "-w WEB_USER" argument.' >&2
exit 1
fi
;;
r)
if [ "$2" ]; then
web_root=$(readlink -f $2)
continue
else
echo 'ERROR: Must specify a non-empty "-d WEB_ROOT" argument.' >&2
exit 1
fi
;;
*)
echo "$0: error - unrecognized option '${1:$i:1}'" >&2;
echo "Try 'install --help' for more information."
exit 1
esac
done
;;
*)
break
esac
shift
done
if [ -z web_root -a ! -d web_root ]; then
echo "$web_root doesn't exist!"
exit 1
fi
echo -e "\n _____ .________________________.___ _____ ___________ "
echo " / _ \ | \______ \__ ___/| | / \ \_ _____/ "
echo " / /_\ \| || _/ | | | |/ \ / \ | __)_ "
echo "/ | \ || | \ | | | / Y \| \ "
echo "\____|__ /___||____|_ / |____| |___\____|__ /_______ / "
echo -e " \/ \/ \/ \/ \n"
if [ "$ignore_dependencies" = "f" ]; then
set +e
if [ -z "${dist}" ]; then
loudCmd "apt-get -y --force-yes install lsb-release"
dist=`lsb_release -ds | awk '{print tolower($1);}'`
code=`lsb_release -cs`
fi
loud "\n-----------------------------------------------------"
loud " * Installing External Dependencies * "
loud "-----------------------------------------------------"
verbose "\n * Reading requirements-${dist,,}-${code,,}.apt..."
loudCmd "apt-get update"
if [ -f ${SCRIPT_DIR}/installer/lib/requirements-${dist,,}-${code,,}.apt ]; then
loudCmd "DEBIAN_FRONTEND=noninteractive apt-get -y -m --force-yes install $(grep -vE '^\s*#' ${SCRIPT_DIR}/installer/lib/requirements-${dist,,}-${code,,}.apt | tr '\n' ' ')"
else
loudCmd "DEBIAN_FRONTEND=noninteractive apt-get -y -m --force-yes install $(grep -vE '^\s*#' ${SCRIPT_DIR}/installer/lib/requirements-ubuntu-trusty.apt | tr '\n' ' ')"
fi
set -e
else
checkCommandExists "apache2"
checkCommandExists "rabbitmqctl"
checkCommandExists "psql"
fi
if [ -f /etc/airtime/airtime.conf ]; then
OLD_CONF=$(grep "[media-monitor]" /etc/airtime/airtime.conf)
if [ -n "${OLD_CONF}" ]; then
upgrade="t"
set +e
verbose "Stopping airtime services..."
loudCmd "service airtime-playout stop-with-monit"
loudCmd "service airtime-media-monitor stop-with-monit"
loudCmd "service airtime-liquidsoap stop-with-monit"
verbose "...Done"
echo "Looks like you have an old version of Airtime. Your current /etc/airtime/airtime.conf \
will be moved to /etc/airtime/airtime.conf.tmp"
# If we don't remove the existing python files in /usr/lib and the
# /etc/init.d startup scripts, services won't work properly
if [ -d /usr/lib/airtime/ ]; then
rm -rf /usr/lib/airtime/
fi
rm /etc/init.d/airtime-*
if [ "$apache" = "t" ]; then
# If the user selects an "in-place" install or passes in a web root,
# we need to replace the old apache airtime.conf
rm /etc/apache2/sites-available/airtime.conf
fi
if [ -d /usr/share/airtime -a web_root = /usr/share/airtime ]; then
rm -rf /usr/share/airtime
fi
mv /etc/airtime/airtime.conf /etc/airtime/airtime.conf.tmp
set -e
fi
fi
if [ "$apache" = "f" -a ${_i} -eq 1 ]; then
echo -e "Install default Airtime apache configuration? (Y/n): \c"
read IN
if [ "$IN" = "y" -o "$IN" = "Y" ]; then
apache="t"
fi
fi
if [ "$in_place" = "t" ]; then
verbose "\n * Setting current Airtime directory as web root..."
web_root=${AIRTIMEROOT}/airtime_mvc/public
elif [ -n "$web_root" ]; then
verbose "\n * Creating Apache web root directory..."
cp -R ${AIRTIMEROOT}/airtime_mvc/* ${web_root}
web_root=${web_root}/public/
else
verbose "\n * Creating default Apache web root directory /usr/share/airtime/..."
web_root="/usr/share/airtime"
mkdir -p ${web_root}
cp -R ${AIRTIMEROOT}/airtime_mvc/* ${web_root}
web_root=${web_root}/public/
fi
verbose "...Done"
if [ "$apache" = "t" ]; then
loud "\n-----------------------------------------------------"
loud " * Configuring Apache * "
loud "-----------------------------------------------------"
set +e
apache2 -v | grep "2\.4" > /dev/null
apacheversion=$?
set -e
if [ "$apacheversion" != "1" ]; then
airtimeconfigfile="airtime.conf"
oldconfigfile="airtime-vhost.conf"
else
airtimeconfigfile="airtime"
oldconfigfile="airtime-vhost"
fi
# If we're upgrading (installing over an existing Airtime install) and we've been told to
# install apache, we should overwrite any existing configuration. If we don't do this, doing
# an in-place installation over an old Airtime install (which installs to /usr/share by default)
# will fail
if [ "$upgrade" = "t" -o ! -f /etc/apache2/sites-available/${airtimeconfigfile} ]; then
verbose "\n * Creating Apache config for Airtime..."
if [ "$apacheversion" != "1" ]; then
sed -e "s@WEB_ROOT@${web_root}@g" ${SCRIPT_DIR}/installer/apache/airtime-vhost-2.4 > /etc/apache2/sites-available/${airtimeconfigfile}
else
sed -e "s@WEB_ROOT@${web_root}@g" ${SCRIPT_DIR}/installer/apache/airtime-vhost > /etc/apache2/sites-available/${airtimeconfigfile}
fi
loudCmd "a2dissite 000-default"
# If Airtime was previously installed with apt, the vhost file name is different,
# so we need to specifically disable it.
if [ -f "/etc/apache2/sites-available/${oldconfigfile}" ]; then
loudCmd "a2dissite airtime-vhost"
fi
loudCmd "a2ensite airtime"
else
verbose "\nApache config for Airtime already exists, skipping"
fi
fi
if [ "$icecast" = "f" -a ${_i} -eq 1 ]; then
echo -e "Install default Airtime Icecast configuration? (Y/n): \c"
read IN
if [ "$IN" = "y" -o "$IN" = "Y" ]; then
icecast="t"
fi
fi
if [ "$icecast" = "t" ]; then
loud "\n-----------------------------------------------------"
loud " * Configuring Icecast * "
loud "-----------------------------------------------------"
verbose "\n * Enabling Icecast 2..."
sed -i 's/ENABLE=false/ENABLE=true/g' /etc/default/icecast2
set +e
loudCmd "service icecast2 start"
set -e
verbose "...Done"
fi
loud "\n-----------------------------------------------------"
loud " * Installing Airtime Services * "
loud "-----------------------------------------------------"
verbose "\n * Installing necessary python services..."
loudCmd "pip install setuptools"
verbose "...Done"
verbose "\n * Creating /run/airtime..."
mkdir -p /run/airtime
chmod 755 /run/airtime
chown -R ${web_user}:${web_user} /run/airtime
verbose "...Done"
verbose "\n * Installing log writer..."
loudCmd "python ${AIRTIMEROOT}/python_apps/std_err_override/setup.py install --install-scripts=/usr/bin"
verbose "...Done"
verbose "\n * Installing API client..."
loudCmd "python ${AIRTIMEROOT}/python_apps/api_clients/setup.py install --install-scripts=/usr/bin"
verbose "...Done"
verbose "\n * Installing media-monitor..."
loudCmd "python ${AIRTIMEROOT}/python_apps/media-monitor/setup.py install --install-scripts=/usr/bin"
verbose "...Done"
verbose "\n * Installing pypo..."
loudCmd "python ${AIRTIMEROOT}/python_apps/pypo/setup.py install --install-scripts=/usr/bin"
verbose "...Done"
for i in /etc/init/airtime*.template; do
chmod 644 $i
sed -i "s/WEB_USER/${web_user}/g" $i
mv $i ${i%.template}
done
set +e
loudCmd "initctl reload-configuration"
loudCmd "systemctl daemon-reload" #systemd hipsters
loudCmd "update-rc.d airtime-playout defaults" # Start at bootup, on Debian
loudCmd "update-rc.d airtime-media-monitor defaults" # Start at bootup, on Debian
loudCmd "update-rc.d airtime-liquidsoap defaults" # Start at bootup, on Debian
set -e
if [ ! -d /var/log/airtime ]; then
loud "\n-----------------------------------------------------"
loud " * Installing Log Files * "
loud "-----------------------------------------------------"
verbose "\n * Creating /var/tmp/airtime..."
mkdir -p /var/tmp/airtime/show-recorder/
verbose "\n * Copying logrotate files..."
cp ${AIRTIMEROOT}/airtime_mvc/build/airtime-php.logrotate /etc/logrotate.d/airtime-php
cp ${AIRTIMEROOT}/python_apps/pypo/pypo/liquidsoap_scripts/airtime-liquidsoap.logrotate /etc/logrotate.d/airtime-liquidsoap
fi
verbose "\n * Setting permissions on /var/log/airtime..."
chmod -R a+x /var/log/airtime
chown -R ${web_user}:${web_user} /var/log/airtime/
verbose "\n * Setting permissions on /var/tmp/airtime..."
chmod -R a+x /var/tmp/airtime
chown -R ${web_user}:${web_user} /var/tmp/airtime/
# PHP Config File for Apache
if [ ! -f "/etc/php5/apache2/conf.d/airtime.ini" ]; then
verbose "\n * Creating Airtime PHP config for Apache..."
cp ${SCRIPT_DIR}/installer/php/airtime.ini /etc/php5/apache2/conf.d/airtime.ini
else
verbose "\nAirtime PHP config for Apache already exists, skipping"
fi
# Enable Apache modules
loudCmd "a2enmod rewrite php5"
loud "\n-----------------------------------------------------"
loud " * Configuring PostgreSQL * "
loud "-----------------------------------------------------"
# Ensure postgres is running - It isn't after you install the postgres package on Ubuntu 15.04
loudCmd "service postgresql start"
setupAirtimePostgresUser() {
# here-doc to execute this block as postgres user
su postgres <<'EOF'
set +e
psql -d postgres -tAc "CREATE USER airtime WITH ENCRYPTED PASSWORD 'airtime'; ALTER USER airtime CREATEDB;"
set -e
# don't indent this!
EOF
}
if [ "$postgres" = "t" ]; then
setupAirtimePostgresUser
elif [ ${_i} -eq 1 ]; then
echo -e "Create default airtime postgres user? (Y/n): \c"
read IN
if [ "$IN" = "y" -o "$IN" = "Y" ]; then
setupAirtimePostgresUser
fi
fi
loud "\n-----------------------------------------------------"
loud " * Configuring RabbitMQ * "
loud "-----------------------------------------------------"
RABBITMQ_VHOST=/airtime
RABBITMQ_USER=airtime
RABBITMQ_PASSWORD=airtime
EXCHANGES="airtime-pypo|pypo-fetch|airtime-media-monitor|media-monitor"
# Ignore errors in this check to avoid dying when vhost isn't found
set +e
rabbitmqctl list_vhosts | grep -w ${RABBITMQ_VHOST} > /dev/null
RESULT="$?"
set -e
# Only run these if the vhost doesn't exist
if [ "$RESULT" != "0" ]; then
verbose "\n * Creating RabbitMQ user ${RABBITMQ_USER}..."
rabbitmqctl add_vhost ${RABBITMQ_VHOST}
rabbitmqctl add_user ${RABBITMQ_USER} ${RABBITMQ_PASSWORD}
else
verbose "\nRabbitMQ user already exists, skipping creation"
fi
verbose "\n * Setting RabbitMQ user permissions..."
loudCmd "rabbitmqctl set_permissions -p ${RABBITMQ_VHOST} ${RABBITMQ_USER} \"$EXCHANGES\" \"$EXCHANGES\" \"$EXCHANGES\""
if [ ! -d "/etc/airtime" ]; then
loud "\n-----------------------------------------------------"
loud " * Installing Airtime * "
loud "-----------------------------------------------------"
verbose "\n * Creating /etc/airtime/ directory..."
mkdir /etc/airtime
fi
chown -R ${web_user}:${web_user} /etc/airtime
if [ ! -d "/srv/airtime" ]; then
mkdir -p /srv/airtime
fi
chown -R ${web_user}:${web_user} /srv/airtime
# We only generate the locales for Airtime if you're allowing us
# to install our dependencies, so that we won't automatically do this
# when this install script runs from our DEB package.
if [ "$ignore_dependencies" = "f" ]; then
loud "\n-----------------------------------------------------"
loud " * Installing Locales * "
loud "-----------------------------------------------------"
set +e
verbose "\n * Generating locales"
for i in `ls ${web_root}/../locale | grep ".._.."`; do
if [ "$dist" = "debian" ]; then
grep -qi "^$i" /etc/locale.gen
if [ $? -ne 0 ]; then
verbose "$i.UTF-8 UTF-8" >> /etc/locale.gen
fi
else
loudCmd "locale-gen \"$i.utf8\""
fi
done
set -e
if [ "$dist" = "debian" ]; then
loudCmd "/usr/sbin/locale-gen"
fi
fi
verbose "\n * Reloading apache..."
loudCmd "service apache2 reload 2>/dev/null"
IP=$(ifconfig eth0 2>/dev/null|awk '/inet addr:/ {print $2}'|sed 's/addr://')
echo -e "\n-----------------------------------------------------"
echo " * Basic Setup DONE! * "
echo " "
echo " To get started with Airtime, visit ${IP} "
echo " or, if you've set up your own web configuration, "
echo " the Airtime webroot on your webserver "
echo "-----------------------------------------------------"

View File

@ -1,14 +0,0 @@
#!/bin/bash
#
# Auto install script for airtime on Debian
#Current dir
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=`readlink -f $0`
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=`dirname $SCRIPT`
cd $SCRIPTPATH/../ubuntu
./airtime-full-install

View File

@ -1,56 +0,0 @@
# This file is contributed by Eugene MechanisM
# Link to the post:
# http://forum.sourcefabric.org/discussion/13563/first-step-to-run-airtime-via-nginx-based-on-airtime-2.0-beta-files
upstream php5-fpm {
ip_hash;
server unix:/var/run/airtime.php.sock;
}
server {
listen 80;
server_name airtime; #change to your host
include mime.types;
root /usr/share/airtime/public;
access_log /var/log/airtime.access.log;
error_log /var/log/airtime.error.log;
index index.php;
include fastcgi_params;
client_max_body_size 1G;
location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|eot|mp4|ogg|ogv|webm)$ {
expires max;
access_log off;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location / {
if (-e $request_filename){
set $rule_0 1;
}
if ($request_filename ~ "-l"){
set $rule_0 1;
}
if (-d $request_filename){
set $rule_0 1;
}
rewrite ^/.*$ /index.php last;
try_files $uri $uri/ index.php;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php5-fpm;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location ~ /\.ht {
deny all;
}
}

View File

@ -1,250 +0,0 @@
# NGINX changes are contributed by Eugene MechanisM
# Link to the post:
# http://forum.sourcefabric.org/discussion/13563/first-step-to-run-airtime-via-nginx-based-on-airtime-2.0-beta-files
; Start a new pool named 'airtime'.
; the variable $pool can we used in any directive and will be replaced by the
; pool name ('airtime' here)
[airtime]
; Per pool prefix
; It only applies on the following directives:
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or /usr) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses on a
; specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = '/var/run/airtime.php.sock'
; Set listen(2) backlog. A value of '-1' means unlimited.
; Default Value: 128 (-1 on FreeBSD and OpenBSD)
;listen.backlog = -1
; List of ipv4 addresses of FastCGI clients which are allowed to connect.
; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
; must be separated by a comma. If this value is left blank, connections will be
; accepted from any ip address.
; Default Value: any
;listen.allowed_clients = 127.0.0.1
; Set permissions for unix socket, if one is used. In Linux, read/write
; permissions must be set in order to allow connections from a web server. Many
; BSD-derived systems allow connections regardless of permissions.
; Default Values: user and group are set as the running user
; mode is set to 0666
;listen.owner = www-data
;listen.group = www-data
;listen.mode = 0666
; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
; will be used.
user = www-data
group = www-data
; Choose how the process manager will control the number of child processes.
; Possible Values:
; static - a fixed number (pm.max_children) of child processes;
; dynamic - the number of child processes are set dynamically based on the
; following directives:
; pm.max_children - the maximum number of children that can
; be alive at the same time.
; pm.start_servers - the number of children created on startup.
; pm.min_spare_servers - the minimum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is less than this
; number then some children will be created.
; pm.max_spare_servers - the maximum number of children in 'idle'
; state (waiting to process). If the number
; of 'idle' processes is greater than this
; number then some children will be killed.
; Note: This value is mandatory.
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes to be created when pm is set to 'dynamic'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI.
; Note: Used when pm is set to either 'static' or 'dynamic'
; Note: This value is mandatory.
pm.max_children = 10
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 4
; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 2
; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 6
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
;pm.max_requests = 500
; The URI to view the FPM status page. If this value is not set, no URI will be
; recognized as a status page. By default, the status page shows the following
; information:
; accepted conn - the number of request accepted by the pool;
; pool - the name of the pool;
; process manager - static or dynamic;
; idle processes - the number of idle processes;
; active processes - the number of active processes;
; total processes - the number of idle + active processes.
; max children reached - number of times, the process limit has been reached,
; when pm tries to start more children (works only for
; pm 'dynamic')
; The values of 'idle processes', 'active processes' and 'total processes' are
; updated each second. The value of 'accepted conn' is updated in real time.
; Example output:
; accepted conn: 12073
; pool: www
; process manager: static
; idle processes: 35
; active processes: 65
; total processes: 100
; max children reached: 1
; By default the status page output is formatted as text/plain. Passing either
; 'html' or 'json' as a query string will return the corresponding output
; syntax. Example:
; http://www.foo.bar/status
; http://www.foo.bar/status?json
; http://www.foo.bar/status?html
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;pm.status_path = /status
; The ping URI to call the monitoring page of FPM. If this value is not set, no
; URI will be recognized as a ping page. This could be used to test from outside
; that FPM is alive and responding, or to
; - create a graph of FPM availability (rrd or such);
; - remove a server from a group if it is not responding (load balancing);
; - trigger alerts for the operating team (24/7).
; Note: The value must start with a leading slash (/). The value can be
; anything, but it may not be a good idea to use the .php extension or it
; may conflict with a real PHP file.
; Default Value: not set
;ping.path = /ping
; This directive may be used to customize the response of a ping request. The
; response is formatted as text/plain with a 200 response code.
; Default Value: pong
;ping.response = pong
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_terminate_timeout = 0
; The timeout for serving a single request after which a PHP backtrace will be
; dumped to the 'slowlog' file. A value of '0s' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_slowlog_timeout = 0
; The log file for slow requests
; Default Value: not set
; Note: slowlog is mandatory if request_slowlog_timeout is set
;slowlog = log/$pool.log.slow
; Set open file descriptor rlimit.
; Default Value: system defined value
;rlimit_files = 1024
; Set max core size rlimit.
; Possible Values: 'unlimited' or an integer greater or equal to 0
; Default Value: system defined value
;rlimit_core = 0
; Chroot to this directory at the start. This value must be defined as an
; absolute path. When this value is not set, chroot is not used.
; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
; of its subdirectories. If the pool prefix is not set, the global prefix
; will be used instead.
; Note: chrooting is a great security feature and should be used whenever
; possible. However, all PHP paths will be relative to the chroot
; (error_log, sessions.save_path, ...).
; Default Value: not set
;chroot =
; Chdir to this directory at the start.
; Note: relative path can be used.
; Default Value: current directory or / when chroot
chdir = /
; Redirect worker stdout and stderr into main error log. If not set, stdout and
; stderr will be redirected to /dev/null according to FastCGI specs.
; Note: on highloaded environement, this can cause some delay in the page
; process time (several ms).
; Default Value: no
;catch_workers_output = yes
; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
; the current environment.
; Default Value: clean env
;env[HOSTNAME] = $HOSTNAME
;env[PATH] = /usr/local/bin:/usr/bin:/bin
;env[TMP] = /tmp
;env[TMPDIR] = /tmp
;env[TEMP] = /tmp
; Additional php.ini defines, specific to this pool of workers. These settings
; overwrite the values previously defined in the php.ini. The directives are the
; same as the PHP SAPI:
; php_value/php_flag - you can set classic ini defines which can
; be overwritten from PHP call 'ini_set'.
; php_admin_value/php_admin_flag - these directives won't be overwritten by
; PHP call 'ini_set'
; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
; Defining 'extension' will load the corresponding shared extension from
; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
; overwrite previously defined php.ini values, but will append the new value
; instead.
; Note: path INI options can be relative and will be expanded with the prefix
; (pool, global or /usr)
; Default Value: nothing is defined by default except the values in php.ini and
; specified at startup with the -d argument
;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/fpm-php.www.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M
php_admin_value[error_log] = /var/log/airtime.php-fpm.error.log
php_admin_value[post_max_size] = 500M
php_admin_value[upload_max_filesize] = 500M
php_admin_value[request_order] = GPC
php_admin_value[session.gc_probability] = 0
php_admin_value[upload_tmp_dir] = /tmp

View File

@ -1,216 +0,0 @@
#!/bin/bash -e
#
# Auto install script for airtime on Ubuntu
#
# NGINX changes are contributed by Eugene MechanisM
# Link to the post:
# http://forum.sourcefabric.org/discussion/13563/first-step-to-run-airtime-via-nginx-based-on-airtime-2.0-beta-files
exec > >(tee install_log.txt)
exec 2>&1
if [ "$(id -u)" != "0" ]; then
echo "Please run as root user."
exit 1
fi
server="$1"
#Current dir
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=`readlink -f $0`
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=`dirname $SCRIPT`
#Prerequisite
echo "----------------------------------------------------"
echo " 1. Install Packages"
echo "----------------------------------------------------"
dist=`lsb_release -is`
code=`lsb_release -cs`
#enable squeeze backports to get lame packages
if [ "$dist" = "Debian" -a "$code" = "squeeze" ]; then
set +e
grep -E "deb http://backports.debian.org/debian-backports squeeze-backports main" /etc/apt/sources.list
returncode=$?
set -e
if [ "$returncode" -ne "0" ]; then
echo "deb http://backports.debian.org/debian-backports squeeze-backports main" >> /etc/apt/sources.list
fi
fi
codename=`lsb_release -sc`
set +e
grep -E "deb +http://apt.sourcefabric.org/? +$codename +main" /etc/apt/sources.list
returncode=$?
set -e
if [ "$returncode" != "0" ]; then
echo "deb http://apt.sourcefabric.org/ $codename main" >> /etc/apt/sources.list
fi
apt-get update
# Updated package list
apt-get -y --force-yes install tar gzip curl php5-pgsql \
php-pear php5-gd postgresql odbc-postgresql python libsoundtouch-ocaml \
libtaglib-ocaml libao-ocaml libmad-ocaml ecasound \
libesd0 libportaudio2 libsamplerate0 rabbitmq-server patch \
php5-curl mpg123 monit python-virtualenv multitail libcamomile-ocaml-data \
libpulse0 vorbis-tools lsb-release lsof sudo mp3gain vorbisgain flac vorbis-tools \
pwgen libfaad2 php-apc
#install packages with --force-yes option (this is useful in the case
#of Debian, where these packages are unauthorized)
apt-get -y --force-yes install libmp3lame-dev lame icecast2
#Debian Squeeze only has zendframework package. Newer versions of Ubuntu have zend-framework package.
#Ubuntu Lucid has both zendframework and zend-framework. Difference appears to be that zendframework is for
#1.10 and zend-framework is 1.11
if [ "$dist" = "Debian" ]; then
apt-get -y --force-yes install zendframework
else
apt-get -y --force-yes install libzend-framework-php
fi
#get the "timeout" unix command
if [ "$code" = "lucid" ]; then
apt-get -y --force-yes install timeout
else
apt-get -y --force-yes install coreutils
fi
#install aac package only available in new distributions: (Debian Wheezy, Ubuntu Precise or newer)
if [ "$code" != "squeeze" -a "$code" != "lucid" ]; then
apt-get -y --force-yes install libvo-aacenc0
fi
#Install some plugin packages on Ubuntu Saucy.
if [ "$dist" = "Ubuntu" -a "$code" = "saucy" ]; then
apt-get -y --force-yes install php5-json
apt-get -y --force-yes install liquidsoap-plugin-alsa liquidsoap-plugin-ao liquidsoap-plugin-faad liquidsoap-plugin-flac liquidsoap-plugin-icecast liquidsoap-plugin-lame liquidsoap-plugin-mad liquidsoap-plugin-ogg liquidsoap-plugin-opus liquidsoap-plugin-portaudio liquidsoap-plugin-pulseaudio liquidsoap-plugin-taglib liquidsoap-plugin-voaacenc liquidsoap-plugin-vorbis
fi
#Install packages back-ported by Sourcefabric
apt-get -y --force-yes install sourcefabric-keyring
apt-get -y --force-yes install liquidsoap
apt-get -y --force-yes install silan
apt-get -y --force-yes install libopus0
if [ "$server" = "nginx" ]; then
apt-get -y --force-yes install nginx php5-fpm
# NGINX Config File
echo "----------------------------------------------------"
echo "2.1 NGINX Config File"
echo "----------------------------------------------------"
if [ ! -f /etc/nginx/sites-available/airtime ]; then
cp $SCRIPTPATH/../nginx/airtime-vhost /etc/nginx/sites-available/airtime
ln -s /etc/nginx/sites-available/airtime /etc/nginx/sites-enabled/airtime
service nginx reload
else
echo "NGINX config for Airtime already exists..."
fi
# php-fpm Airtime pool file
echo "----------------------------------------------------"
echo "2.2 Airtime php pool file"
echo "----------------------------------------------------"
if [ ! -f /etc/php5/fpm/pool.d/airtime.conf ]; then
cp $SCRIPTPATH/../php5-fpm/airtime.conf /etc/php5/fpm/pool.d/airtime.conf
service php5-fpm reload
else
echo "Airtime php pool file already exists..."
fi
else
apt-get -y --force-yes install apache2 libapache2-mod-php5
set +e
apache2 -v | grep "2\.4" > /dev/null
apacheversion=$?
set -e
# Apache Config File
echo "----------------------------------------------------"
echo "2.1 Apache Config File"
echo "----------------------------------------------------"
if [ "$apacheversion" != "1" ]; then
airtimeconfigfile="airtime.conf"
else
airtimeconfigfile="airtime"
fi
if [ ! -f /etc/apache2/sites-available/$airtimeconfigfile ]; then
echo "Creating Apache config for Airtime..."
cp $SCRIPTPATH/../apache/airtime-vhost /etc/apache2/sites-available/$airtimeconfigfile
a2dissite 000-default
a2ensite airtime
else
echo "Apache config for Airtime already exists..."
fi
if [ ! -d /usr/share/airtime/public ]; then
echo "Creating Apache web root directory..."
mkdir -p /usr/share/airtime/public/
else
echo "Airtime web root directory already exists..."
fi
# PHP Config File for Apache
echo "----------------------------------------------------"
echo "2.2 PHP Config File for Apache"
echo "----------------------------------------------------"
if [ ! -f /etc/php5/apache2/airtime.ini ]; then
echo "Creating Airtime PHP config for Apache..."
cp $SCRIPTPATH/../php5/airtime.ini /etc/php5/apache2/conf.d/airtime.ini
else
echo "Airtime PHP config for Apache already exists..."
fi
# Enable modules and restart Apache to enable any configuration changes
echo "----------------------------------------------------"
echo "2.3 Enable Apache Modules and Restart Apache"
echo "----------------------------------------------------"
a2enmod rewrite php5
service apache2 restart
fi
# Enable Icecast
echo "----------------------------------------------------"
echo "3. Enable Icecast"
echo "----------------------------------------------------"
cd /etc/default/
sed -i 's/ENABLE=false/ENABLE=true/g' icecast2
set +e
service icecast2 start
set -e
echo ""
# Enable Monit
echo "----------------------------------------------------"
echo "4. Enable Monit"
echo "----------------------------------------------------"
cd /etc/default/
sed -i 's/startup=0/startup=1/g' monit
set +e
grep -q "include /etc/monit/conf.d" /etc/monit/monitrc
RETVAL=$?
set -e
if [ $RETVAL -ne 0 ] ; then
mkdir -p /etc/monit/conf.d
echo "include /etc/monit/conf.d/*" >> /etc/monit/monitrc
fi
# Run Airtime Install
echo "----------------------------------------------------"
echo "5. Run Airtime Install"
echo "----------------------------------------------------"
cd $SCRIPTPATH/../../install_minimal
# Restart apache to clear php cache
service apache2 restart
./airtime-install

View File

@ -1,10 +0,0 @@
#!/bin/bash
# Auto install script for airtime + nginx
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=`readlink -f $0`
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=`dirname $SCRIPT`
$SCRIPTPATH/airtime-full-install nginx

View File

@ -1,19 +0,0 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version20110312121200 extends AbstractMigration
{
public function up(Schema $schema)
{
$schema->dropTable("cc_backup");
$schema->dropTable("cc_trans");
}
public function down(Schema $schema)
{
}
}

View File

@ -1,96 +0,0 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema,
Doctrine\DBAL\Schema\Table,
Doctrine\DBAL\Schema\Column,
Doctrine\DBAL\Types\Type;
class Version20110331111708 extends AbstractMigration
{
public function up(Schema $schema)
{
//start cc_show_instances modifications
$show_instances_table = $schema->getTable('cc_show_instances');
$show_instances_table->addColumn('record', 'smallint', array('notnull' => 0, 'default' => 0));
$show_instances_table->addColumn('rebroadcast', 'smallint', array('notnull' => 0, 'default' => 0));
$show_instances_table->addColumn('instance_id', 'integer', array('notnull' => 0));
$show_instances_table->addColumn('file_id', 'integer', array('notnull' => 0));
$show_instances_table->addColumn('soundcloud_id', 'integer', array('notnull' => 0));
$show_instances_table->addNamedForeignKeyConstraint('cc_original_show_instance_fkey', $show_instances_table, array('instance_id'), array('id'), array('onDelete' => 'CASCADE'));
$files_table = $schema->getTable('cc_files');
$show_instances_table->addNamedForeignKeyConstraint('cc_recorded_file_fkey', $files_table, array('file_id'), array('id'), array('onDelete' => 'CASCADE'));
//end cc_show_instances modifications
//start cc_show_days modifications
$show_days_table = $schema->getTable('cc_show_days');
$show_days_table->addColumn('record', 'smallint', array( 'notnull' => 0, 'default' => 0));
//end cc_show_days modifications
//start cc_show modifications
$show_table = $schema->getTable('cc_show');
$show_table->addColumn('url', 'string', array('notnull' => 0, 'length' => 255));
//end cc_show modifications
//start cc_schedule modifications
$schedule_table = $schema->getTable('cc_schedule');
$playlist_id_col = $schedule_table->getColumn('playlist_id');
$playlist_id_col->setNotnull(false);
//end cc_schedule modifications
//create cc_show_rebroadcast table
$cc_show_rebroadcast_table = $schema->createTable('cc_show_rebroadcast');
$cc_show_rebroadcast_table->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true));
$cc_show_rebroadcast_table->addColumn('day_offset', 'string', array('length' => 255));
$cc_show_rebroadcast_table->addColumn('start_time', 'datetime', array('notnull' => 1));
$cc_show_rebroadcast_table->addColumn('show_id', 'integer', array('notnull' => 1));
$cc_show_rebroadcast_table->setPrimaryKey(array('id'));
//end create cc_show_rebroadcast table
}
public function down(Schema $schema)
{
//start cc_show_instances modifications
$show_instances_table = $schema->getTable('cc_show_instances');
$show_instances_table->dropColumn('record');
$show_instances_table->dropColumn('rebroadcast');
$show_instances_table->dropColumn('instance_id');
$show_instances_table->dropColumn('file_id');
$show_instances_table->dropColumn('soundcloud_id');
//end cc_show_instances modifications
//start cc_show_days modifications
$show_days_table = $schema->getTable('cc_show_days');
$show_days_table->dropColumn('record');
//end cc_show_days modifications
//start cc_show modifications
$show_table = $schema->getTable('cc_show');
$show_table->dropColumn('url');
//end cc_show modifications
//start cc_schedule modifications
$schedule_table = $schema->getTable('cc_schedule');
$playlist_id_col = $schedule_table->getColumn('playlist_id');
$playlist_id_col->setNotnull(true);
//end cc_schedule modifications
//drop cc_show_rebroadcast table
$schema->dropTable('cc_show_rebroadcast');
//end drop cc_show_rebroadcast table
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version20110402164819 extends AbstractMigration
{
public function up(Schema $schema)
{
//start cc_show modifications
$show_table = $schema->getTable('cc_show');
$show_table->addColumn('genre', 'string', array('notnull' => 0, 'length' => 255, 'default' => ""));
//end cc_show modifications
}
public function down(Schema $schema)
{
//start cc_show modifications
$show_table = $schema->getTable('cc_show');
$show_table->dropColumn('genre');
//end cc_show modifications
}
}

View File

@ -1,47 +0,0 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version20110406182005 extends AbstractMigration
{
public function up(Schema $schema)
{
//start cc_show_instances modifications
$cc_show_instances = $schema->getTable('cc_show_instances');
$cc_show_instances->addColumn('time_filled', 'time', array('notnull' => false));
//end cc_show_instances modifications
//start cc_show_rebroadcast modifications
$cc_show_rebroadcast = $schema->getTable('cc_show_rebroadcast');
$type = $cc_show_rebroadcast->getColumn('start_time')->getType()->getName();
if($type == 'datetime') {
$cc_show_rebroadcast->dropColumn('start_time');
$cc_show_rebroadcast->addColumn('start_time', 'time', array('notnull' => true));
}
//end cc_show_rebroadcast modifications
}
public function down(Schema $schema)
{
//start cc_show_instances modifications
$cc_show_instances = $schema->getTable('cc_show_instances');
$cc_show_instances->dropColumn('time_filled');
//end cc_show_instances modifications
//start cc_show_rebroadcast modifications
$cc_show_rebroadcast = $schema->getTable('cc_show_rebroadcast');
$type = $cc_show_rebroadcast->getColumn('start_time')->getType()->getName();
if($type == 'datetime') {
$cc_show_rebroadcast->dropColumn('start_time');
$cc_show_rebroadcast->addColumn('start_time', 'datetime', array('notnull' => 1));
}
//end cc_show_rebroadcast modifications
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace DoctrineMigrations;
//CC-2279 Upgrade script for creating the cc_music_dirs table.
use Doctrine\DBAL\Migrations\AbstractMigration,
Doctrine\DBAL\Schema\Schema;
class Version20110629143017 extends AbstractMigration
{
public function up(Schema $schema)
{
//create cc_music_dirs table
$cc_music_dirs = $schema->createTable('cc_music_dirs');
$cc_music_dirs->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true));
$cc_music_dirs->addColumn('type', 'string', array('length' => 255));
$cc_music_dirs->addColumn('directory', 'text', array('unique' => true));
$cc_music_dirs->setPrimaryKey(array('id'));
//end create cc_music_dirs table
}
public function down(Schema $schema)
{
$schema->dropTable('cc_music_dirs');
}
}

Some files were not shown because too many files have changed in this diff Show More