diff --git a/VERSION b/VERSION
index 8bcc8b3..130165b 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v3.5.5
+v3.6.0
diff --git a/adapters/AdapterInterface.php b/adapters/AdapterInterface.php
index c967bb2..5de6a5c 100644
--- a/adapters/AdapterInterface.php
+++ b/adapters/AdapterInterface.php
@@ -79,6 +79,11 @@ interface AdapterInterface
*/
public static function meta_support_read(): bool;
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool;
+
/**
* Does the implemented service support reception callback.
*/
@@ -152,6 +157,15 @@ interface AdapterInterface
*/
public function test(): bool;
+
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string;
+
+
/**
* Method called on reception of a status update notification for a SMS.
*
diff --git a/adapters/BenchmarkAdapter.php b/adapters/BenchmarkAdapter.php
index 493ad6a..5d03ce3 100644
--- a/adapters/BenchmarkAdapter.php
+++ b/adapters/BenchmarkAdapter.php
@@ -111,6 +111,14 @@ namespace adapters;
return false;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -226,6 +234,16 @@ namespace adapters;
return [];
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public static function status_change_callback()
{
return null;
diff --git a/adapters/GammuAdapter.php b/adapters/GammuAdapter.php
index da42b49..f1c15e4 100644
--- a/adapters/GammuAdapter.php
+++ b/adapters/GammuAdapter.php
@@ -121,6 +121,14 @@ namespace adapters;
return true;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -301,6 +309,16 @@ namespace adapters;
return $response;
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
//Always return true as we cannot test because we would be needing a root account
diff --git a/adapters/KannelAdapter.php b/adapters/KannelAdapter.php
index 4d63802..19782d3 100644
--- a/adapters/KannelAdapter.php
+++ b/adapters/KannelAdapter.php
@@ -209,6 +209,14 @@ class KannelAdapter implements AdapterInterface
return false;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -354,6 +362,16 @@ class KannelAdapter implements AdapterInterface
return [];
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
try
diff --git a/adapters/OctopushShortcodeAdapter.php b/adapters/OctopushShortcodeAdapter.php
index 2c70b97..59283a5 100644
--- a/adapters/OctopushShortcodeAdapter.php
+++ b/adapters/OctopushShortcodeAdapter.php
@@ -174,6 +174,14 @@ class OctopushShortcodeAdapter implements AdapterInterface
return false;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -325,6 +333,16 @@ class OctopushShortcodeAdapter implements AdapterInterface
return [];
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
try
diff --git a/adapters/OctopushVirtualNumberAdapter.php b/adapters/OctopushVirtualNumberAdapter.php
index 0096f72..f9238a6 100644
--- a/adapters/OctopushVirtualNumberAdapter.php
+++ b/adapters/OctopushVirtualNumberAdapter.php
@@ -173,6 +173,14 @@ class OctopushVirtualNumberAdapter implements AdapterInterface
return false;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -317,6 +325,16 @@ class OctopushVirtualNumberAdapter implements AdapterInterface
return [];
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
try
diff --git a/adapters/OdysseyMessagingAdapter.php b/adapters/OdysseyMessagingAdapter.php
new file mode 100644
index 0000000..f48203c
--- /dev/null
+++ b/adapters/OdysseyMessagingAdapter.php
@@ -0,0 +1,511 @@
+
+ *
+ * This source file is subject to the GPL-3.0 license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace adapters;
+
+use DateTime;
+
+/**
+ * Odyssey Messaging SMS service
+ */
+class OdysseyMessagingAdapter implements AdapterInterface
+{
+ const EVENT_TYPES = [
+ 'OPT_OUT' => 1,
+ 'SYSTEM_ERROR' => 2,
+ 'END_OF_ITEM' => 3,
+ 'END_OF_JOB' => 4,
+ 'JOB_STATUS_CHANGED' => 5,
+ 'REAL_TIME_STATUS' => 6,
+ 'RETRIEVE_FILE' => 7,
+ 'INBOUND_SMS' => 8,
+ 'ITEM_STATUS_CHANGED' => 9,
+ 'DATA_COLLECTION_FILLED' => 10,
+ ];
+
+ /**
+ * Data used to configure interaction with the implemented service. (e.g : Api credentials, ports numbers, etc.).
+ */
+ private $data;
+
+ /**
+ * Odyssey login.
+ */
+ private $login;
+
+ /**
+ * Odyssey password.
+ */
+ private $password;
+
+ /**
+ * Sender name to use instead of shortcode.
+ */
+ private $sender;
+
+ /**
+ * Odyssey api baseurl.
+ */
+ private $api_url = 'https://api.odyssey-services.fr/api/v1';
+
+ /**
+ * Adapter constructor, called when instanciated by RaspiSMS.
+ *
+ * @param json string $data : JSON string of the data to configure interaction with the implemented service
+ */
+ public function __construct(string $data)
+ {
+ $this->data = json_decode($data, true);
+
+ $this->login = $this->data['login'];
+ $this->password = $this->data['password'];
+
+ $this->sender = $this->data['sender'] ?? null;
+ }
+
+ /**
+ * Classname of the adapter.
+ */
+ public static function meta_classname(): string
+ {
+ return __CLASS__;
+ }
+
+ /**
+ * Uniq name of the adapter
+ * It should be the classname of the adapter un snakecase.
+ */
+ public static function meta_uid(): string
+ {
+ return 'odyssey_messaging_adapter';
+ }
+
+ /**
+ * Should this adapter be hidden in user interface for phone creation and
+ * available to creation through API only.
+ */
+ public static function meta_hidden(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Should this adapter data be hidden after creation
+ * this help to prevent API credentials to other service leak if an attacker gain access to RaspiSMS through user credentials.
+ */
+ public static function meta_hide_data(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Name of the adapter.
+ * It should probably be the name of the service it adapt (e.g : Gammu SMSD, OVH SMS, SIM800L, etc.).
+ */
+ public static function meta_name(): string
+ {
+ return 'Odyssey Messaging';
+ }
+
+ /**
+ * Description of the adapter.
+ * A short description of the service the adapter implements.
+ */
+ public static function meta_description(): string
+ {
+ return '
+ Envoi de SMS avec Odyssey Messaging.
+ Pour plus d\'information sur l\'utilisation de ce type de téléphone, reportez-vous à la documentation sur le téléphone "Odyssey Messaging".
+ ';
+ }
+
+ /**
+ * List of entries we want in data for the adapter.
+ *
+ * @return array : Every line is a field as an array with keys : name, title, description, required
+ */
+ public static function meta_data_fields(): array
+ {
+ return [
+ [
+ 'name' => 'login',
+ 'title' => 'Odyssey login',
+ 'description' => 'Login du compte Odyssey à employer.',
+ 'required' => true,
+ ],
+ [
+ 'name' => 'password',
+ 'title' => 'Mot de passe',
+ 'description' => 'Mot de passe du compte Odyssey à employer.',
+ 'required' => true,
+ ],
+ [
+ 'name' => 'sender',
+ 'title' => 'Nom de l\'expéditeur',
+ 'description' => 'Nom de l\'expéditeur à afficher à la place du numéro (11 caractères max).
+ Laissez vide pour ne pas utiliser d\'expéditeur nommé.
+ Si vous utilisez un expéditeur nommé, le destinataire ne pourra pas répondre.',
+ 'required' => false,
+ ],
+ ];
+ }
+
+ /**
+ * Does the implemented service support reading smss.
+ */
+ public static function meta_support_read(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Does the implemented service support flash smss.
+ */
+ public static function meta_support_flash(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Does the implemented service support status change.
+ */
+ public static function meta_support_status_change(): bool
+ {
+ return true;
+ }
+
+ /**
+ * Does the implemented service support reception callback.
+ */
+ public static function meta_support_reception(): bool
+ {
+ return true;
+ }
+
+ /**
+ * Does the implemented service support mms reception.
+ */
+ public static function meta_support_mms_reception(): bool
+ {
+ return false;
+ }
+
+ /**
+ * Does the implemented service support mms sending.
+ */
+ public static function meta_support_mms_sending(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_inbound_call_callback(): bool
+ {
+ return false;
+ }
+
+ public static function meta_support_end_call_callback(): bool
+ {
+ return false;
+ }
+
+ public function send(string $destination, string $text, bool $flash = false, bool $mms = false, array $medias = []): array
+ {
+ $response = [
+ 'error' => false,
+ 'error_message' => null,
+ 'uid' => null,
+ ];
+
+ try
+ {
+ $credentials = base64_encode($this->login . ':' . $this->password);
+ $headers = [
+ 'Authorization: Basic ' . $credentials,
+ 'Content-Type: application/json',
+ ];
+
+ $data = [
+ 'JobType' => 'SMS',
+ 'Text' => $text,
+ 'TrackingID' => uniqid(),
+ 'AdhocRecipients' => [['Name' => uniqid(), 'Address' => str_replace('+', '00', $destination)]],
+ ];
+
+ if ($this->sender)
+ {
+ $data['Parameter'] = ['Sender' => $this->sender, 'Media' => 1];
+ }
+
+ $data = json_encode($data);
+
+ $endpoint = $this->api_url . '/SMSJobs';
+
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $endpoint);
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_POST, true);
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+
+ $curl_response = curl_exec($curl);
+ $http_code = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+
+ if (false === $curl_response)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'HTTP query failed.';
+
+ return $response;
+ }
+
+ $response_decode = json_decode($curl_response, true);
+ if (null === $response_decode)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Invalid JSON for response.';
+
+ return $response;
+ }
+
+ if (200 !== $http_code)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Response indicate error : ' . $response_decode['Message'] . ' -> """' . json_encode($response_decode['ModelState']) . '""" AND HTTP CODE -> ' . $http_code;
+
+ return $response;
+ }
+
+ $uid = $response_decode['JobNumber'] ?? false;
+ if (!$uid)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Cannot extract SMS uid';
+
+ return $response;
+ }
+
+ $response['uid'] = $uid;
+
+ return $response;
+ }
+ catch (\Throwable $t)
+ {
+ $response['error'] = true;
+ $response['error_message'] = $t->getMessage();
+
+ return $response;
+ }
+ }
+
+ public function read(): array
+ {
+ return [];
+ }
+
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
+ public function test(): bool
+ {
+ try
+ {
+ if ($this->data['sender'] && (mb_strlen($this->data['sender']) < 3 || mb_strlen($this->data['sender'] > 11)))
+ {
+ return false;
+ }
+
+ if (!empty($this->data['sms_type']) && !in_array($this->data['sms_type'], ['premium', 'low cost']))
+ {
+ return false;
+ }
+
+ $credentials = base64_encode($this->login . ':' . $this->password);
+ $headers = [
+ 'Authorization: Basic ' . $credentials,
+ 'Content-Type: application/json',
+ ];
+
+ //Check service name
+ $endpoint = $this->api_url . '/JobTypes';
+ $curl = curl_init();
+ curl_setopt($curl, CURLOPT_URL, $endpoint);
+ curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
+ $response = curl_exec($curl);
+ $http_code = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ curl_close($curl);
+
+ if (200 !== $http_code)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ catch (\Throwable $t)
+ {
+ return false;
+ }
+ }
+
+ public static function status_change_callback()
+ {
+ header('Connection: close');
+ header('Content-Encoding: none');
+ header('Content-Length: 0');
+
+ $input = file_get_contents('php://input');
+ $content = json_decode($input, true);
+ if (null === $content)
+ {
+ return false;
+ }
+
+ $event_type = $content['EventType'] ?? false;
+ if ($event_type != self::EVENT_TYPES['ITEM_STATUS_CHANGED'])
+ {
+ return false;
+ }
+
+ $uid = $content['JobNumber'] ?? false;
+ $status = $content['Outcome'] ?? false;
+
+ if (false === $uid || false === $status)
+ {
+ return false;
+ }
+
+ switch ($status)
+ {
+ case 'S':
+ $status = \models\Sended::STATUS_DELIVERED;
+
+ break;
+
+ case 'B':
+ $status = \models\Sended::STATUS_UNKNOWN;
+
+ break;
+
+ default:
+ $status = \models\Sended::STATUS_FAILED;
+
+ break;
+ }
+
+ return ['uid' => $uid, 'status' => $status];
+ }
+
+ public static function reception_callback(): array
+ {
+ $response = [
+ 'error' => false,
+ 'error_message' => null,
+ 'sms' => null,
+ ];
+
+ header('Connection: close');
+ header('Content-Encoding: none');
+ header('Content-Length: 0');
+
+ $input = file_get_contents('php://input');
+ $content = json_decode($input, true);
+ if (null === $content)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Cannot read input data from callback request.';
+
+ return $response;
+ }
+
+ $event_type = $content['EventType'] ?? false;
+ if ($event_type != self::EVENT_TYPES['INBOUND_SMS'])
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Invalid event type : ' . $event_type . '.';
+
+ return $response;
+ }
+
+ $number = $content['From'] ?? false;
+ $text = $content['Message'] ?? false;
+ $at = $content['EventDateTime'] ?? false;
+
+ if (!$number || !$text || !$at)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'One required data of the callback is missing.';
+
+ return $response;
+ }
+
+ $matches = null;
+ $match = preg_match('#/Date\(([0-9]+)\+([0-9]+)\)/#', $at, $matches);
+ $timestamp = ($matches[1] ?? null);
+ if (!$match || !$timestamp)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Invalid date.';
+
+ return $response;
+ }
+
+ $at = DateTime::createFromFormat('U', $timestamp / 1000);
+ $at = $at->format('Y-m-d H:i:s');
+
+ $origin = \controllers\internals\Tool::parse_phone($number);
+ if (!$origin)
+ {
+ $response['error'] = true;
+ $response['error_message'] = 'Invalid origin number : ' . $number;
+
+ return $response;
+ }
+
+ $response['sms'] = [
+ 'at' => $at,
+ 'text' => $text,
+ 'origin' => $origin,
+ ];
+
+ return $response;
+ }
+
+ public function inbound_call_callback(): array
+ {
+ return [];
+ }
+
+ public function end_call_callback(): array
+ {
+ return [];
+ }
+}
diff --git a/adapters/OvhSmsShortcodeAdapter.php b/adapters/OvhSmsShortcodeAdapter.php
index 91a28a3..cf16795 100644
--- a/adapters/OvhSmsShortcodeAdapter.php
+++ b/adapters/OvhSmsShortcodeAdapter.php
@@ -162,6 +162,14 @@ namespace adapters;
return true;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -327,6 +335,16 @@ namespace adapters;
}
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
try
diff --git a/adapters/OvhSmsVirtualNumberAdapter.php b/adapters/OvhSmsVirtualNumberAdapter.php
index b1078f9..cfc1b2a 100644
--- a/adapters/OvhSmsVirtualNumberAdapter.php
+++ b/adapters/OvhSmsVirtualNumberAdapter.php
@@ -166,6 +166,14 @@ namespace adapters;
return true;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -317,6 +325,16 @@ namespace adapters;
}
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
try
diff --git a/adapters/TestAdapter.php b/adapters/TestAdapter.php
index 048e81c..2ad3c48 100644
--- a/adapters/TestAdapter.php
+++ b/adapters/TestAdapter.php
@@ -116,6 +116,14 @@ namespace adapters;
return true;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -287,6 +295,16 @@ namespace adapters;
}
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
return true;
diff --git a/adapters/TwilioVirtualNumberAdapter.php b/adapters/TwilioVirtualNumberAdapter.php
index 5ad0cc9..a17722c 100644
--- a/adapters/TwilioVirtualNumberAdapter.php
+++ b/adapters/TwilioVirtualNumberAdapter.php
@@ -160,6 +160,14 @@ class TwilioVirtualNumberAdapter implements AdapterInterface
return true;
}
+ /**
+ * Does the implemented service support updating phone status.
+ */
+ public static function meta_support_phone_status(): bool
+ {
+ return false;
+ }
+
/**
* Does the implemented service support flash smss.
*/
@@ -295,6 +303,16 @@ class TwilioVirtualNumberAdapter implements AdapterInterface
}
}
+ /**
+ * Method called to verify phone status
+ *
+ * @return string : Return one phone status among 'available', 'unavailable', 'no_credit'
+ */
+ public function check_phone_status(): string
+ {
+ return \models\Phone::STATUS_AVAILABLE;
+ }
+
public function test(): bool
{
try
diff --git a/assets/font/fonts-awesome/css/font-awesome.css b/assets/font/fonts-awesome/css/font-awesome.css
index 4040b3c..ee906a8 100644
--- a/assets/font/fonts-awesome/css/font-awesome.css
+++ b/assets/font/fonts-awesome/css/font-awesome.css
@@ -1,13 +1,13 @@
/*!
- * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'FontAwesome';
- src: url('../fonts/fontawesome-webfont.eot?v=4.2.0');
- src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');
+ src: url('../fonts/fontawesome-webfont.eot?v=4.7.0');
+ src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -64,6 +64,19 @@
border: solid 0.08em #eeeeee;
border-radius: .1em;
}
+.fa-pull-left {
+ float: left;
+}
+.fa-pull-right {
+ float: right;
+}
+.fa.fa-pull-left {
+ margin-right: .3em;
+}
+.fa.fa-pull-right {
+ margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
.pull-right {
float: right;
}
@@ -80,6 +93,10 @@
-webkit-animation: fa-spin 2s infinite linear;
animation: fa-spin 2s infinite linear;
}
+.fa-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
@@ -101,31 +118,31 @@
}
}
.fa-rotate-90 {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.fa-rotate-180 {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.fa-rotate-270 {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
.fa-flip-horizontal {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
-ms-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.fa-flip-vertical {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
-ms-transform: scale(1, -1);
transform: scale(1, -1);
@@ -610,6 +627,7 @@
.fa-twitter:before {
content: "\f099";
}
+.fa-facebook-f:before,
.fa-facebook:before {
content: "\f09a";
}
@@ -622,6 +640,7 @@
.fa-credit-card:before {
content: "\f09d";
}
+.fa-feed:before,
.fa-rss:before {
content: "\f09e";
}
@@ -1259,7 +1278,8 @@
.fa-male:before {
content: "\f183";
}
-.fa-gittip:before {
+.fa-gittip:before,
+.fa-gratipay:before {
content: "\f184";
}
.fa-sun-o:before {
@@ -1363,7 +1383,7 @@
.fa-digg:before {
content: "\f1a6";
}
-.fa-pied-piper:before {
+.fa-pied-piper-pp:before {
content: "\f1a7";
}
.fa-pied-piper-alt:before {
@@ -1489,6 +1509,7 @@
content: "\f1ce";
}
.fa-ra:before,
+.fa-resistance:before,
.fa-rebel:before {
content: "\f1d0";
}
@@ -1502,6 +1523,8 @@
.fa-git:before {
content: "\f1d3";
}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
.fa-hacker-news:before {
content: "\f1d4";
}
@@ -1670,3 +1693,645 @@
.fa-meanpath:before {
content: "\f20c";
}
+.fa-buysellads:before {
+ content: "\f20d";
+}
+.fa-connectdevelop:before {
+ content: "\f20e";
+}
+.fa-dashcube:before {
+ content: "\f210";
+}
+.fa-forumbee:before {
+ content: "\f211";
+}
+.fa-leanpub:before {
+ content: "\f212";
+}
+.fa-sellsy:before {
+ content: "\f213";
+}
+.fa-shirtsinbulk:before {
+ content: "\f214";
+}
+.fa-simplybuilt:before {
+ content: "\f215";
+}
+.fa-skyatlas:before {
+ content: "\f216";
+}
+.fa-cart-plus:before {
+ content: "\f217";
+}
+.fa-cart-arrow-down:before {
+ content: "\f218";
+}
+.fa-diamond:before {
+ content: "\f219";
+}
+.fa-ship:before {
+ content: "\f21a";
+}
+.fa-user-secret:before {
+ content: "\f21b";
+}
+.fa-motorcycle:before {
+ content: "\f21c";
+}
+.fa-street-view:before {
+ content: "\f21d";
+}
+.fa-heartbeat:before {
+ content: "\f21e";
+}
+.fa-venus:before {
+ content: "\f221";
+}
+.fa-mars:before {
+ content: "\f222";
+}
+.fa-mercury:before {
+ content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+ content: "\f224";
+}
+.fa-transgender-alt:before {
+ content: "\f225";
+}
+.fa-venus-double:before {
+ content: "\f226";
+}
+.fa-mars-double:before {
+ content: "\f227";
+}
+.fa-venus-mars:before {
+ content: "\f228";
+}
+.fa-mars-stroke:before {
+ content: "\f229";
+}
+.fa-mars-stroke-v:before {
+ content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+ content: "\f22b";
+}
+.fa-neuter:before {
+ content: "\f22c";
+}
+.fa-genderless:before {
+ content: "\f22d";
+}
+.fa-facebook-official:before {
+ content: "\f230";
+}
+.fa-pinterest-p:before {
+ content: "\f231";
+}
+.fa-whatsapp:before {
+ content: "\f232";
+}
+.fa-server:before {
+ content: "\f233";
+}
+.fa-user-plus:before {
+ content: "\f234";
+}
+.fa-user-times:before {
+ content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+ content: "\f236";
+}
+.fa-viacoin:before {
+ content: "\f237";
+}
+.fa-train:before {
+ content: "\f238";
+}
+.fa-subway:before {
+ content: "\f239";
+}
+.fa-medium:before {
+ content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+ content: "\f23b";
+}
+.fa-optin-monster:before {
+ content: "\f23c";
+}
+.fa-opencart:before {
+ content: "\f23d";
+}
+.fa-expeditedssl:before {
+ content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery:before,
+.fa-battery-full:before {
+ content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+ content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+ content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+ content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+ content: "\f244";
+}
+.fa-mouse-pointer:before {
+ content: "\f245";
+}
+.fa-i-cursor:before {
+ content: "\f246";
+}
+.fa-object-group:before {
+ content: "\f247";
+}
+.fa-object-ungroup:before {
+ content: "\f248";
+}
+.fa-sticky-note:before {
+ content: "\f249";
+}
+.fa-sticky-note-o:before {
+ content: "\f24a";
+}
+.fa-cc-jcb:before {
+ content: "\f24b";
+}
+.fa-cc-diners-club:before {
+ content: "\f24c";
+}
+.fa-clone:before {
+ content: "\f24d";
+}
+.fa-balance-scale:before {
+ content: "\f24e";
+}
+.fa-hourglass-o:before {
+ content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+ content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+ content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+ content: "\f253";
+}
+.fa-hourglass:before {
+ content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+ content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+ content: "\f256";
+}
+.fa-hand-scissors-o:before {
+ content: "\f257";
+}
+.fa-hand-lizard-o:before {
+ content: "\f258";
+}
+.fa-hand-spock-o:before {
+ content: "\f259";
+}
+.fa-hand-pointer-o:before {
+ content: "\f25a";
+}
+.fa-hand-peace-o:before {
+ content: "\f25b";
+}
+.fa-trademark:before {
+ content: "\f25c";
+}
+.fa-registered:before {
+ content: "\f25d";
+}
+.fa-creative-commons:before {
+ content: "\f25e";
+}
+.fa-gg:before {
+ content: "\f260";
+}
+.fa-gg-circle:before {
+ content: "\f261";
+}
+.fa-tripadvisor:before {
+ content: "\f262";
+}
+.fa-odnoklassniki:before {
+ content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+ content: "\f264";
+}
+.fa-get-pocket:before {
+ content: "\f265";
+}
+.fa-wikipedia-w:before {
+ content: "\f266";
+}
+.fa-safari:before {
+ content: "\f267";
+}
+.fa-chrome:before {
+ content: "\f268";
+}
+.fa-firefox:before {
+ content: "\f269";
+}
+.fa-opera:before {
+ content: "\f26a";
+}
+.fa-internet-explorer:before {
+ content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+ content: "\f26c";
+}
+.fa-contao:before {
+ content: "\f26d";
+}
+.fa-500px:before {
+ content: "\f26e";
+}
+.fa-amazon:before {
+ content: "\f270";
+}
+.fa-calendar-plus-o:before {
+ content: "\f271";
+}
+.fa-calendar-minus-o:before {
+ content: "\f272";
+}
+.fa-calendar-times-o:before {
+ content: "\f273";
+}
+.fa-calendar-check-o:before {
+ content: "\f274";
+}
+.fa-industry:before {
+ content: "\f275";
+}
+.fa-map-pin:before {
+ content: "\f276";
+}
+.fa-map-signs:before {
+ content: "\f277";
+}
+.fa-map-o:before {
+ content: "\f278";
+}
+.fa-map:before {
+ content: "\f279";
+}
+.fa-commenting:before {
+ content: "\f27a";
+}
+.fa-commenting-o:before {
+ content: "\f27b";
+}
+.fa-houzz:before {
+ content: "\f27c";
+}
+.fa-vimeo:before {
+ content: "\f27d";
+}
+.fa-black-tie:before {
+ content: "\f27e";
+}
+.fa-fonticons:before {
+ content: "\f280";
+}
+.fa-reddit-alien:before {
+ content: "\f281";
+}
+.fa-edge:before {
+ content: "\f282";
+}
+.fa-credit-card-alt:before {
+ content: "\f283";
+}
+.fa-codiepie:before {
+ content: "\f284";
+}
+.fa-modx:before {
+ content: "\f285";
+}
+.fa-fort-awesome:before {
+ content: "\f286";
+}
+.fa-usb:before {
+ content: "\f287";
+}
+.fa-product-hunt:before {
+ content: "\f288";
+}
+.fa-mixcloud:before {
+ content: "\f289";
+}
+.fa-scribd:before {
+ content: "\f28a";
+}
+.fa-pause-circle:before {
+ content: "\f28b";
+}
+.fa-pause-circle-o:before {
+ content: "\f28c";
+}
+.fa-stop-circle:before {
+ content: "\f28d";
+}
+.fa-stop-circle-o:before {
+ content: "\f28e";
+}
+.fa-shopping-bag:before {
+ content: "\f290";
+}
+.fa-shopping-basket:before {
+ content: "\f291";
+}
+.fa-hashtag:before {
+ content: "\f292";
+}
+.fa-bluetooth:before {
+ content: "\f293";
+}
+.fa-bluetooth-b:before {
+ content: "\f294";
+}
+.fa-percent:before {
+ content: "\f295";
+}
+.fa-gitlab:before {
+ content: "\f296";
+}
+.fa-wpbeginner:before {
+ content: "\f297";
+}
+.fa-wpforms:before {
+ content: "\f298";
+}
+.fa-envira:before {
+ content: "\f299";
+}
+.fa-universal-access:before {
+ content: "\f29a";
+}
+.fa-wheelchair-alt:before {
+ content: "\f29b";
+}
+.fa-question-circle-o:before {
+ content: "\f29c";
+}
+.fa-blind:before {
+ content: "\f29d";
+}
+.fa-audio-description:before {
+ content: "\f29e";
+}
+.fa-volume-control-phone:before {
+ content: "\f2a0";
+}
+.fa-braille:before {
+ content: "\f2a1";
+}
+.fa-assistive-listening-systems:before {
+ content: "\f2a2";
+}
+.fa-asl-interpreting:before,
+.fa-american-sign-language-interpreting:before {
+ content: "\f2a3";
+}
+.fa-deafness:before,
+.fa-hard-of-hearing:before,
+.fa-deaf:before {
+ content: "\f2a4";
+}
+.fa-glide:before {
+ content: "\f2a5";
+}
+.fa-glide-g:before {
+ content: "\f2a6";
+}
+.fa-signing:before,
+.fa-sign-language:before {
+ content: "\f2a7";
+}
+.fa-low-vision:before {
+ content: "\f2a8";
+}
+.fa-viadeo:before {
+ content: "\f2a9";
+}
+.fa-viadeo-square:before {
+ content: "\f2aa";
+}
+.fa-snapchat:before {
+ content: "\f2ab";
+}
+.fa-snapchat-ghost:before {
+ content: "\f2ac";
+}
+.fa-snapchat-square:before {
+ content: "\f2ad";
+}
+.fa-pied-piper:before {
+ content: "\f2ae";
+}
+.fa-first-order:before {
+ content: "\f2b0";
+}
+.fa-yoast:before {
+ content: "\f2b1";
+}
+.fa-themeisle:before {
+ content: "\f2b2";
+}
+.fa-google-plus-circle:before,
+.fa-google-plus-official:before {
+ content: "\f2b3";
+}
+.fa-fa:before,
+.fa-font-awesome:before {
+ content: "\f2b4";
+}
+.fa-handshake-o:before {
+ content: "\f2b5";
+}
+.fa-envelope-open:before {
+ content: "\f2b6";
+}
+.fa-envelope-open-o:before {
+ content: "\f2b7";
+}
+.fa-linode:before {
+ content: "\f2b8";
+}
+.fa-address-book:before {
+ content: "\f2b9";
+}
+.fa-address-book-o:before {
+ content: "\f2ba";
+}
+.fa-vcard:before,
+.fa-address-card:before {
+ content: "\f2bb";
+}
+.fa-vcard-o:before,
+.fa-address-card-o:before {
+ content: "\f2bc";
+}
+.fa-user-circle:before {
+ content: "\f2bd";
+}
+.fa-user-circle-o:before {
+ content: "\f2be";
+}
+.fa-user-o:before {
+ content: "\f2c0";
+}
+.fa-id-badge:before {
+ content: "\f2c1";
+}
+.fa-drivers-license:before,
+.fa-id-card:before {
+ content: "\f2c2";
+}
+.fa-drivers-license-o:before,
+.fa-id-card-o:before {
+ content: "\f2c3";
+}
+.fa-quora:before {
+ content: "\f2c4";
+}
+.fa-free-code-camp:before {
+ content: "\f2c5";
+}
+.fa-telegram:before {
+ content: "\f2c6";
+}
+.fa-thermometer-4:before,
+.fa-thermometer:before,
+.fa-thermometer-full:before {
+ content: "\f2c7";
+}
+.fa-thermometer-3:before,
+.fa-thermometer-three-quarters:before {
+ content: "\f2c8";
+}
+.fa-thermometer-2:before,
+.fa-thermometer-half:before {
+ content: "\f2c9";
+}
+.fa-thermometer-1:before,
+.fa-thermometer-quarter:before {
+ content: "\f2ca";
+}
+.fa-thermometer-0:before,
+.fa-thermometer-empty:before {
+ content: "\f2cb";
+}
+.fa-shower:before {
+ content: "\f2cc";
+}
+.fa-bathtub:before,
+.fa-s15:before,
+.fa-bath:before {
+ content: "\f2cd";
+}
+.fa-podcast:before {
+ content: "\f2ce";
+}
+.fa-window-maximize:before {
+ content: "\f2d0";
+}
+.fa-window-minimize:before {
+ content: "\f2d1";
+}
+.fa-window-restore:before {
+ content: "\f2d2";
+}
+.fa-times-rectangle:before,
+.fa-window-close:before {
+ content: "\f2d3";
+}
+.fa-times-rectangle-o:before,
+.fa-window-close-o:before {
+ content: "\f2d4";
+}
+.fa-bandcamp:before {
+ content: "\f2d5";
+}
+.fa-grav:before {
+ content: "\f2d6";
+}
+.fa-etsy:before {
+ content: "\f2d7";
+}
+.fa-imdb:before {
+ content: "\f2d8";
+}
+.fa-ravelry:before {
+ content: "\f2d9";
+}
+.fa-eercast:before {
+ content: "\f2da";
+}
+.fa-microchip:before {
+ content: "\f2db";
+}
+.fa-snowflake-o:before {
+ content: "\f2dc";
+}
+.fa-superpowers:before {
+ content: "\f2dd";
+}
+.fa-wpexplorer:before {
+ content: "\f2de";
+}
+.fa-meetup:before {
+ content: "\f2e0";
+}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+}
diff --git a/assets/font/fonts-awesome/css/font-awesome.min.css b/assets/font/fonts-awesome/css/font-awesome.min.css
index ec53d4d..540440c 100644
--- a/assets/font/fonts-awesome/css/font-awesome.min.css
+++ b/assets/font/fonts-awesome/css/font-awesome.min.css
@@ -1,4 +1,4 @@
/*!
- * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}
\ No newline at end of file
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/assets/font/fonts-awesome/fonts/FontAwesome.otf b/assets/font/fonts-awesome/fonts/FontAwesome.otf
index 81c9ad9..401ec0f 100644
Binary files a/assets/font/fonts-awesome/fonts/FontAwesome.otf and b/assets/font/fonts-awesome/fonts/FontAwesome.otf differ
diff --git a/assets/font/fonts-awesome/fonts/fontawesome-webfont.eot b/assets/font/fonts-awesome/fonts/fontawesome-webfont.eot
index 84677bc..e9f60ca 100644
Binary files a/assets/font/fonts-awesome/fonts/fontawesome-webfont.eot and b/assets/font/fonts-awesome/fonts/fontawesome-webfont.eot differ
diff --git a/assets/font/fonts-awesome/fonts/fontawesome-webfont.svg b/assets/font/fonts-awesome/fonts/fontawesome-webfont.svg
index d907b25..855c845 100644
--- a/assets/font/fonts-awesome/fonts/fontawesome-webfont.svg
+++ b/assets/font/fonts-awesome/fonts/fontawesome-webfont.svg
@@ -1,520 +1,2671 @@
-
diff --git a/assets/font/fonts-awesome/fonts/fontawesome-webfont.ttf b/assets/font/fonts-awesome/fonts/fontawesome-webfont.ttf
index 96a3639..35acda2 100644
Binary files a/assets/font/fonts-awesome/fonts/fontawesome-webfont.ttf and b/assets/font/fonts-awesome/fonts/fontawesome-webfont.ttf differ
diff --git a/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff b/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff
index 628b6a5..400014a 100644
Binary files a/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff and b/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff differ
diff --git a/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff2 b/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff2
new file mode 100644
index 0000000..4d13fc6
Binary files /dev/null and b/assets/font/fonts-awesome/fonts/fontawesome-webfont.woff2 differ
diff --git a/assets/font/fonts-awesome/less/spinning.less b/assets/font/fonts-awesome/less/animated.less
similarity index 79%
rename from assets/font/fonts-awesome/less/spinning.less
rename to assets/font/fonts-awesome/less/animated.less
index 6e1564e..66ad52a 100644
--- a/assets/font/fonts-awesome/less/spinning.less
+++ b/assets/font/fonts-awesome/less/animated.less
@@ -1,4 +1,4 @@
-// Spinning Icons
+// Animated Icons
// --------------------------
.@{fa-css-prefix}-spin {
@@ -6,6 +6,11 @@
animation: fa-spin 2s infinite linear;
}
+.@{fa-css-prefix}-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
diff --git a/assets/font/fonts-awesome/less/bordered-pulled.less b/assets/font/fonts-awesome/less/bordered-pulled.less
index 0c90eb5..f1c8ad7 100644
--- a/assets/font/fonts-awesome/less/bordered-pulled.less
+++ b/assets/font/fonts-awesome/less/bordered-pulled.less
@@ -7,6 +7,15 @@
border-radius: .1em;
}
+.@{fa-css-prefix}-pull-left { float: left; }
+.@{fa-css-prefix}-pull-right { float: right; }
+
+.@{fa-css-prefix} {
+ &.@{fa-css-prefix}-pull-left { margin-right: .3em; }
+ &.@{fa-css-prefix}-pull-right { margin-left: .3em; }
+}
+
+/* Deprecated as of 4.4.0 */
.pull-right { float: right; }
.pull-left { float: left; }
diff --git a/assets/font/fonts-awesome/less/core.less b/assets/font/fonts-awesome/less/core.less
index 01d1910..c577ac8 100644
--- a/assets/font/fonts-awesome/less/core.less
+++ b/assets/font/fonts-awesome/less/core.less
@@ -3,9 +3,10 @@
.@{fa-css-prefix} {
display: inline-block;
- font: normal normal normal 14px/1 FontAwesome; // shortening font declaration
+ font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+
}
diff --git a/assets/font/fonts-awesome/less/font-awesome.less b/assets/font/fonts-awesome/less/font-awesome.less
index 195fd46..c3677de 100644
--- a/assets/font/fonts-awesome/less/font-awesome.less
+++ b/assets/font/fonts-awesome/less/font-awesome.less
@@ -1,5 +1,5 @@
/*!
- * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@@ -11,7 +11,8 @@
@import "fixed-width.less";
@import "list.less";
@import "bordered-pulled.less";
-@import "spinning.less";
+@import "animated.less";
@import "rotated-flipped.less";
@import "stacked.less";
@import "icons.less";
+@import "screen-reader.less";
diff --git a/assets/font/fonts-awesome/less/icons.less b/assets/font/fonts-awesome/less/icons.less
index b5c26c7..159d600 100644
--- a/assets/font/fonts-awesome/less/icons.less
+++ b/assets/font/fonts-awesome/less/icons.less
@@ -158,10 +158,12 @@
.@{fa-css-prefix}-bookmark-o:before { content: @fa-var-bookmark-o; }
.@{fa-css-prefix}-phone-square:before { content: @fa-var-phone-square; }
.@{fa-css-prefix}-twitter:before { content: @fa-var-twitter; }
+.@{fa-css-prefix}-facebook-f:before,
.@{fa-css-prefix}-facebook:before { content: @fa-var-facebook; }
.@{fa-css-prefix}-github:before { content: @fa-var-github; }
.@{fa-css-prefix}-unlock:before { content: @fa-var-unlock; }
.@{fa-css-prefix}-credit-card:before { content: @fa-var-credit-card; }
+.@{fa-css-prefix}-feed:before,
.@{fa-css-prefix}-rss:before { content: @fa-var-rss; }
.@{fa-css-prefix}-hdd-o:before { content: @fa-var-hdd-o; }
.@{fa-css-prefix}-bullhorn:before { content: @fa-var-bullhorn; }
@@ -397,7 +399,8 @@
.@{fa-css-prefix}-trello:before { content: @fa-var-trello; }
.@{fa-css-prefix}-female:before { content: @fa-var-female; }
.@{fa-css-prefix}-male:before { content: @fa-var-male; }
-.@{fa-css-prefix}-gittip:before { content: @fa-var-gittip; }
+.@{fa-css-prefix}-gittip:before,
+.@{fa-css-prefix}-gratipay:before { content: @fa-var-gratipay; }
.@{fa-css-prefix}-sun-o:before { content: @fa-var-sun-o; }
.@{fa-css-prefix}-moon-o:before { content: @fa-var-moon-o; }
.@{fa-css-prefix}-archive:before { content: @fa-var-archive; }
@@ -435,7 +438,7 @@
.@{fa-css-prefix}-stumbleupon:before { content: @fa-var-stumbleupon; }
.@{fa-css-prefix}-delicious:before { content: @fa-var-delicious; }
.@{fa-css-prefix}-digg:before { content: @fa-var-digg; }
-.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; }
+.@{fa-css-prefix}-pied-piper-pp:before { content: @fa-var-pied-piper-pp; }
.@{fa-css-prefix}-pied-piper-alt:before { content: @fa-var-pied-piper-alt; }
.@{fa-css-prefix}-drupal:before { content: @fa-var-drupal; }
.@{fa-css-prefix}-joomla:before { content: @fa-var-joomla; }
@@ -485,11 +488,14 @@
.@{fa-css-prefix}-life-ring:before { content: @fa-var-life-ring; }
.@{fa-css-prefix}-circle-o-notch:before { content: @fa-var-circle-o-notch; }
.@{fa-css-prefix}-ra:before,
+.@{fa-css-prefix}-resistance:before,
.@{fa-css-prefix}-rebel:before { content: @fa-var-rebel; }
.@{fa-css-prefix}-ge:before,
.@{fa-css-prefix}-empire:before { content: @fa-var-empire; }
.@{fa-css-prefix}-git-square:before { content: @fa-var-git-square; }
.@{fa-css-prefix}-git:before { content: @fa-var-git; }
+.@{fa-css-prefix}-y-combinator-square:before,
+.@{fa-css-prefix}-yc-square:before,
.@{fa-css-prefix}-hacker-news:before { content: @fa-var-hacker-news; }
.@{fa-css-prefix}-tencent-weibo:before { content: @fa-var-tencent-weibo; }
.@{fa-css-prefix}-qq:before { content: @fa-var-qq; }
@@ -550,3 +556,234 @@
.@{fa-css-prefix}-sheqel:before,
.@{fa-css-prefix}-ils:before { content: @fa-var-ils; }
.@{fa-css-prefix}-meanpath:before { content: @fa-var-meanpath; }
+.@{fa-css-prefix}-buysellads:before { content: @fa-var-buysellads; }
+.@{fa-css-prefix}-connectdevelop:before { content: @fa-var-connectdevelop; }
+.@{fa-css-prefix}-dashcube:before { content: @fa-var-dashcube; }
+.@{fa-css-prefix}-forumbee:before { content: @fa-var-forumbee; }
+.@{fa-css-prefix}-leanpub:before { content: @fa-var-leanpub; }
+.@{fa-css-prefix}-sellsy:before { content: @fa-var-sellsy; }
+.@{fa-css-prefix}-shirtsinbulk:before { content: @fa-var-shirtsinbulk; }
+.@{fa-css-prefix}-simplybuilt:before { content: @fa-var-simplybuilt; }
+.@{fa-css-prefix}-skyatlas:before { content: @fa-var-skyatlas; }
+.@{fa-css-prefix}-cart-plus:before { content: @fa-var-cart-plus; }
+.@{fa-css-prefix}-cart-arrow-down:before { content: @fa-var-cart-arrow-down; }
+.@{fa-css-prefix}-diamond:before { content: @fa-var-diamond; }
+.@{fa-css-prefix}-ship:before { content: @fa-var-ship; }
+.@{fa-css-prefix}-user-secret:before { content: @fa-var-user-secret; }
+.@{fa-css-prefix}-motorcycle:before { content: @fa-var-motorcycle; }
+.@{fa-css-prefix}-street-view:before { content: @fa-var-street-view; }
+.@{fa-css-prefix}-heartbeat:before { content: @fa-var-heartbeat; }
+.@{fa-css-prefix}-venus:before { content: @fa-var-venus; }
+.@{fa-css-prefix}-mars:before { content: @fa-var-mars; }
+.@{fa-css-prefix}-mercury:before { content: @fa-var-mercury; }
+.@{fa-css-prefix}-intersex:before,
+.@{fa-css-prefix}-transgender:before { content: @fa-var-transgender; }
+.@{fa-css-prefix}-transgender-alt:before { content: @fa-var-transgender-alt; }
+.@{fa-css-prefix}-venus-double:before { content: @fa-var-venus-double; }
+.@{fa-css-prefix}-mars-double:before { content: @fa-var-mars-double; }
+.@{fa-css-prefix}-venus-mars:before { content: @fa-var-venus-mars; }
+.@{fa-css-prefix}-mars-stroke:before { content: @fa-var-mars-stroke; }
+.@{fa-css-prefix}-mars-stroke-v:before { content: @fa-var-mars-stroke-v; }
+.@{fa-css-prefix}-mars-stroke-h:before { content: @fa-var-mars-stroke-h; }
+.@{fa-css-prefix}-neuter:before { content: @fa-var-neuter; }
+.@{fa-css-prefix}-genderless:before { content: @fa-var-genderless; }
+.@{fa-css-prefix}-facebook-official:before { content: @fa-var-facebook-official; }
+.@{fa-css-prefix}-pinterest-p:before { content: @fa-var-pinterest-p; }
+.@{fa-css-prefix}-whatsapp:before { content: @fa-var-whatsapp; }
+.@{fa-css-prefix}-server:before { content: @fa-var-server; }
+.@{fa-css-prefix}-user-plus:before { content: @fa-var-user-plus; }
+.@{fa-css-prefix}-user-times:before { content: @fa-var-user-times; }
+.@{fa-css-prefix}-hotel:before,
+.@{fa-css-prefix}-bed:before { content: @fa-var-bed; }
+.@{fa-css-prefix}-viacoin:before { content: @fa-var-viacoin; }
+.@{fa-css-prefix}-train:before { content: @fa-var-train; }
+.@{fa-css-prefix}-subway:before { content: @fa-var-subway; }
+.@{fa-css-prefix}-medium:before { content: @fa-var-medium; }
+.@{fa-css-prefix}-yc:before,
+.@{fa-css-prefix}-y-combinator:before { content: @fa-var-y-combinator; }
+.@{fa-css-prefix}-optin-monster:before { content: @fa-var-optin-monster; }
+.@{fa-css-prefix}-opencart:before { content: @fa-var-opencart; }
+.@{fa-css-prefix}-expeditedssl:before { content: @fa-var-expeditedssl; }
+.@{fa-css-prefix}-battery-4:before,
+.@{fa-css-prefix}-battery:before,
+.@{fa-css-prefix}-battery-full:before { content: @fa-var-battery-full; }
+.@{fa-css-prefix}-battery-3:before,
+.@{fa-css-prefix}-battery-three-quarters:before { content: @fa-var-battery-three-quarters; }
+.@{fa-css-prefix}-battery-2:before,
+.@{fa-css-prefix}-battery-half:before { content: @fa-var-battery-half; }
+.@{fa-css-prefix}-battery-1:before,
+.@{fa-css-prefix}-battery-quarter:before { content: @fa-var-battery-quarter; }
+.@{fa-css-prefix}-battery-0:before,
+.@{fa-css-prefix}-battery-empty:before { content: @fa-var-battery-empty; }
+.@{fa-css-prefix}-mouse-pointer:before { content: @fa-var-mouse-pointer; }
+.@{fa-css-prefix}-i-cursor:before { content: @fa-var-i-cursor; }
+.@{fa-css-prefix}-object-group:before { content: @fa-var-object-group; }
+.@{fa-css-prefix}-object-ungroup:before { content: @fa-var-object-ungroup; }
+.@{fa-css-prefix}-sticky-note:before { content: @fa-var-sticky-note; }
+.@{fa-css-prefix}-sticky-note-o:before { content: @fa-var-sticky-note-o; }
+.@{fa-css-prefix}-cc-jcb:before { content: @fa-var-cc-jcb; }
+.@{fa-css-prefix}-cc-diners-club:before { content: @fa-var-cc-diners-club; }
+.@{fa-css-prefix}-clone:before { content: @fa-var-clone; }
+.@{fa-css-prefix}-balance-scale:before { content: @fa-var-balance-scale; }
+.@{fa-css-prefix}-hourglass-o:before { content: @fa-var-hourglass-o; }
+.@{fa-css-prefix}-hourglass-1:before,
+.@{fa-css-prefix}-hourglass-start:before { content: @fa-var-hourglass-start; }
+.@{fa-css-prefix}-hourglass-2:before,
+.@{fa-css-prefix}-hourglass-half:before { content: @fa-var-hourglass-half; }
+.@{fa-css-prefix}-hourglass-3:before,
+.@{fa-css-prefix}-hourglass-end:before { content: @fa-var-hourglass-end; }
+.@{fa-css-prefix}-hourglass:before { content: @fa-var-hourglass; }
+.@{fa-css-prefix}-hand-grab-o:before,
+.@{fa-css-prefix}-hand-rock-o:before { content: @fa-var-hand-rock-o; }
+.@{fa-css-prefix}-hand-stop-o:before,
+.@{fa-css-prefix}-hand-paper-o:before { content: @fa-var-hand-paper-o; }
+.@{fa-css-prefix}-hand-scissors-o:before { content: @fa-var-hand-scissors-o; }
+.@{fa-css-prefix}-hand-lizard-o:before { content: @fa-var-hand-lizard-o; }
+.@{fa-css-prefix}-hand-spock-o:before { content: @fa-var-hand-spock-o; }
+.@{fa-css-prefix}-hand-pointer-o:before { content: @fa-var-hand-pointer-o; }
+.@{fa-css-prefix}-hand-peace-o:before { content: @fa-var-hand-peace-o; }
+.@{fa-css-prefix}-trademark:before { content: @fa-var-trademark; }
+.@{fa-css-prefix}-registered:before { content: @fa-var-registered; }
+.@{fa-css-prefix}-creative-commons:before { content: @fa-var-creative-commons; }
+.@{fa-css-prefix}-gg:before { content: @fa-var-gg; }
+.@{fa-css-prefix}-gg-circle:before { content: @fa-var-gg-circle; }
+.@{fa-css-prefix}-tripadvisor:before { content: @fa-var-tripadvisor; }
+.@{fa-css-prefix}-odnoklassniki:before { content: @fa-var-odnoklassniki; }
+.@{fa-css-prefix}-odnoklassniki-square:before { content: @fa-var-odnoklassniki-square; }
+.@{fa-css-prefix}-get-pocket:before { content: @fa-var-get-pocket; }
+.@{fa-css-prefix}-wikipedia-w:before { content: @fa-var-wikipedia-w; }
+.@{fa-css-prefix}-safari:before { content: @fa-var-safari; }
+.@{fa-css-prefix}-chrome:before { content: @fa-var-chrome; }
+.@{fa-css-prefix}-firefox:before { content: @fa-var-firefox; }
+.@{fa-css-prefix}-opera:before { content: @fa-var-opera; }
+.@{fa-css-prefix}-internet-explorer:before { content: @fa-var-internet-explorer; }
+.@{fa-css-prefix}-tv:before,
+.@{fa-css-prefix}-television:before { content: @fa-var-television; }
+.@{fa-css-prefix}-contao:before { content: @fa-var-contao; }
+.@{fa-css-prefix}-500px:before { content: @fa-var-500px; }
+.@{fa-css-prefix}-amazon:before { content: @fa-var-amazon; }
+.@{fa-css-prefix}-calendar-plus-o:before { content: @fa-var-calendar-plus-o; }
+.@{fa-css-prefix}-calendar-minus-o:before { content: @fa-var-calendar-minus-o; }
+.@{fa-css-prefix}-calendar-times-o:before { content: @fa-var-calendar-times-o; }
+.@{fa-css-prefix}-calendar-check-o:before { content: @fa-var-calendar-check-o; }
+.@{fa-css-prefix}-industry:before { content: @fa-var-industry; }
+.@{fa-css-prefix}-map-pin:before { content: @fa-var-map-pin; }
+.@{fa-css-prefix}-map-signs:before { content: @fa-var-map-signs; }
+.@{fa-css-prefix}-map-o:before { content: @fa-var-map-o; }
+.@{fa-css-prefix}-map:before { content: @fa-var-map; }
+.@{fa-css-prefix}-commenting:before { content: @fa-var-commenting; }
+.@{fa-css-prefix}-commenting-o:before { content: @fa-var-commenting-o; }
+.@{fa-css-prefix}-houzz:before { content: @fa-var-houzz; }
+.@{fa-css-prefix}-vimeo:before { content: @fa-var-vimeo; }
+.@{fa-css-prefix}-black-tie:before { content: @fa-var-black-tie; }
+.@{fa-css-prefix}-fonticons:before { content: @fa-var-fonticons; }
+.@{fa-css-prefix}-reddit-alien:before { content: @fa-var-reddit-alien; }
+.@{fa-css-prefix}-edge:before { content: @fa-var-edge; }
+.@{fa-css-prefix}-credit-card-alt:before { content: @fa-var-credit-card-alt; }
+.@{fa-css-prefix}-codiepie:before { content: @fa-var-codiepie; }
+.@{fa-css-prefix}-modx:before { content: @fa-var-modx; }
+.@{fa-css-prefix}-fort-awesome:before { content: @fa-var-fort-awesome; }
+.@{fa-css-prefix}-usb:before { content: @fa-var-usb; }
+.@{fa-css-prefix}-product-hunt:before { content: @fa-var-product-hunt; }
+.@{fa-css-prefix}-mixcloud:before { content: @fa-var-mixcloud; }
+.@{fa-css-prefix}-scribd:before { content: @fa-var-scribd; }
+.@{fa-css-prefix}-pause-circle:before { content: @fa-var-pause-circle; }
+.@{fa-css-prefix}-pause-circle-o:before { content: @fa-var-pause-circle-o; }
+.@{fa-css-prefix}-stop-circle:before { content: @fa-var-stop-circle; }
+.@{fa-css-prefix}-stop-circle-o:before { content: @fa-var-stop-circle-o; }
+.@{fa-css-prefix}-shopping-bag:before { content: @fa-var-shopping-bag; }
+.@{fa-css-prefix}-shopping-basket:before { content: @fa-var-shopping-basket; }
+.@{fa-css-prefix}-hashtag:before { content: @fa-var-hashtag; }
+.@{fa-css-prefix}-bluetooth:before { content: @fa-var-bluetooth; }
+.@{fa-css-prefix}-bluetooth-b:before { content: @fa-var-bluetooth-b; }
+.@{fa-css-prefix}-percent:before { content: @fa-var-percent; }
+.@{fa-css-prefix}-gitlab:before { content: @fa-var-gitlab; }
+.@{fa-css-prefix}-wpbeginner:before { content: @fa-var-wpbeginner; }
+.@{fa-css-prefix}-wpforms:before { content: @fa-var-wpforms; }
+.@{fa-css-prefix}-envira:before { content: @fa-var-envira; }
+.@{fa-css-prefix}-universal-access:before { content: @fa-var-universal-access; }
+.@{fa-css-prefix}-wheelchair-alt:before { content: @fa-var-wheelchair-alt; }
+.@{fa-css-prefix}-question-circle-o:before { content: @fa-var-question-circle-o; }
+.@{fa-css-prefix}-blind:before { content: @fa-var-blind; }
+.@{fa-css-prefix}-audio-description:before { content: @fa-var-audio-description; }
+.@{fa-css-prefix}-volume-control-phone:before { content: @fa-var-volume-control-phone; }
+.@{fa-css-prefix}-braille:before { content: @fa-var-braille; }
+.@{fa-css-prefix}-assistive-listening-systems:before { content: @fa-var-assistive-listening-systems; }
+.@{fa-css-prefix}-asl-interpreting:before,
+.@{fa-css-prefix}-american-sign-language-interpreting:before { content: @fa-var-american-sign-language-interpreting; }
+.@{fa-css-prefix}-deafness:before,
+.@{fa-css-prefix}-hard-of-hearing:before,
+.@{fa-css-prefix}-deaf:before { content: @fa-var-deaf; }
+.@{fa-css-prefix}-glide:before { content: @fa-var-glide; }
+.@{fa-css-prefix}-glide-g:before { content: @fa-var-glide-g; }
+.@{fa-css-prefix}-signing:before,
+.@{fa-css-prefix}-sign-language:before { content: @fa-var-sign-language; }
+.@{fa-css-prefix}-low-vision:before { content: @fa-var-low-vision; }
+.@{fa-css-prefix}-viadeo:before { content: @fa-var-viadeo; }
+.@{fa-css-prefix}-viadeo-square:before { content: @fa-var-viadeo-square; }
+.@{fa-css-prefix}-snapchat:before { content: @fa-var-snapchat; }
+.@{fa-css-prefix}-snapchat-ghost:before { content: @fa-var-snapchat-ghost; }
+.@{fa-css-prefix}-snapchat-square:before { content: @fa-var-snapchat-square; }
+.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; }
+.@{fa-css-prefix}-first-order:before { content: @fa-var-first-order; }
+.@{fa-css-prefix}-yoast:before { content: @fa-var-yoast; }
+.@{fa-css-prefix}-themeisle:before { content: @fa-var-themeisle; }
+.@{fa-css-prefix}-google-plus-circle:before,
+.@{fa-css-prefix}-google-plus-official:before { content: @fa-var-google-plus-official; }
+.@{fa-css-prefix}-fa:before,
+.@{fa-css-prefix}-font-awesome:before { content: @fa-var-font-awesome; }
+.@{fa-css-prefix}-handshake-o:before { content: @fa-var-handshake-o; }
+.@{fa-css-prefix}-envelope-open:before { content: @fa-var-envelope-open; }
+.@{fa-css-prefix}-envelope-open-o:before { content: @fa-var-envelope-open-o; }
+.@{fa-css-prefix}-linode:before { content: @fa-var-linode; }
+.@{fa-css-prefix}-address-book:before { content: @fa-var-address-book; }
+.@{fa-css-prefix}-address-book-o:before { content: @fa-var-address-book-o; }
+.@{fa-css-prefix}-vcard:before,
+.@{fa-css-prefix}-address-card:before { content: @fa-var-address-card; }
+.@{fa-css-prefix}-vcard-o:before,
+.@{fa-css-prefix}-address-card-o:before { content: @fa-var-address-card-o; }
+.@{fa-css-prefix}-user-circle:before { content: @fa-var-user-circle; }
+.@{fa-css-prefix}-user-circle-o:before { content: @fa-var-user-circle-o; }
+.@{fa-css-prefix}-user-o:before { content: @fa-var-user-o; }
+.@{fa-css-prefix}-id-badge:before { content: @fa-var-id-badge; }
+.@{fa-css-prefix}-drivers-license:before,
+.@{fa-css-prefix}-id-card:before { content: @fa-var-id-card; }
+.@{fa-css-prefix}-drivers-license-o:before,
+.@{fa-css-prefix}-id-card-o:before { content: @fa-var-id-card-o; }
+.@{fa-css-prefix}-quora:before { content: @fa-var-quora; }
+.@{fa-css-prefix}-free-code-camp:before { content: @fa-var-free-code-camp; }
+.@{fa-css-prefix}-telegram:before { content: @fa-var-telegram; }
+.@{fa-css-prefix}-thermometer-4:before,
+.@{fa-css-prefix}-thermometer:before,
+.@{fa-css-prefix}-thermometer-full:before { content: @fa-var-thermometer-full; }
+.@{fa-css-prefix}-thermometer-3:before,
+.@{fa-css-prefix}-thermometer-three-quarters:before { content: @fa-var-thermometer-three-quarters; }
+.@{fa-css-prefix}-thermometer-2:before,
+.@{fa-css-prefix}-thermometer-half:before { content: @fa-var-thermometer-half; }
+.@{fa-css-prefix}-thermometer-1:before,
+.@{fa-css-prefix}-thermometer-quarter:before { content: @fa-var-thermometer-quarter; }
+.@{fa-css-prefix}-thermometer-0:before,
+.@{fa-css-prefix}-thermometer-empty:before { content: @fa-var-thermometer-empty; }
+.@{fa-css-prefix}-shower:before { content: @fa-var-shower; }
+.@{fa-css-prefix}-bathtub:before,
+.@{fa-css-prefix}-s15:before,
+.@{fa-css-prefix}-bath:before { content: @fa-var-bath; }
+.@{fa-css-prefix}-podcast:before { content: @fa-var-podcast; }
+.@{fa-css-prefix}-window-maximize:before { content: @fa-var-window-maximize; }
+.@{fa-css-prefix}-window-minimize:before { content: @fa-var-window-minimize; }
+.@{fa-css-prefix}-window-restore:before { content: @fa-var-window-restore; }
+.@{fa-css-prefix}-times-rectangle:before,
+.@{fa-css-prefix}-window-close:before { content: @fa-var-window-close; }
+.@{fa-css-prefix}-times-rectangle-o:before,
+.@{fa-css-prefix}-window-close-o:before { content: @fa-var-window-close-o; }
+.@{fa-css-prefix}-bandcamp:before { content: @fa-var-bandcamp; }
+.@{fa-css-prefix}-grav:before { content: @fa-var-grav; }
+.@{fa-css-prefix}-etsy:before { content: @fa-var-etsy; }
+.@{fa-css-prefix}-imdb:before { content: @fa-var-imdb; }
+.@{fa-css-prefix}-ravelry:before { content: @fa-var-ravelry; }
+.@{fa-css-prefix}-eercast:before { content: @fa-var-eercast; }
+.@{fa-css-prefix}-microchip:before { content: @fa-var-microchip; }
+.@{fa-css-prefix}-snowflake-o:before { content: @fa-var-snowflake-o; }
+.@{fa-css-prefix}-superpowers:before { content: @fa-var-superpowers; }
+.@{fa-css-prefix}-wpexplorer:before { content: @fa-var-wpexplorer; }
+.@{fa-css-prefix}-meetup:before { content: @fa-var-meetup; }
diff --git a/assets/font/fonts-awesome/less/mixins.less b/assets/font/fonts-awesome/less/mixins.less
index b7bfadc..beef231 100644
--- a/assets/font/fonts-awesome/less/mixins.less
+++ b/assets/font/fonts-awesome/less/mixins.less
@@ -3,23 +3,58 @@
.fa-icon() {
display: inline-block;
- font: normal normal normal 14px/1 FontAwesome; // shortening font declaration
+ font: normal normal normal @fa-font-size-base/@fa-line-height-base FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+
}
.fa-icon-rotate(@degrees, @rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})";
-webkit-transform: rotate(@degrees);
-ms-transform: rotate(@degrees);
transform: rotate(@degrees);
}
.fa-icon-flip(@horiz, @vert, @rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)";
-webkit-transform: scale(@horiz, @vert);
-ms-transform: scale(@horiz, @vert);
transform: scale(@horiz, @vert);
}
+
+
+// Only display content to screen readers. A la Bootstrap 4.
+//
+// See: http://a11yproject.com/posts/how-to-hide-content/
+
+.sr-only() {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0,0,0,0);
+ border: 0;
+}
+
+// Use in conjunction with .sr-only to only display content when it's focused.
+//
+// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+//
+// Credit: HTML5 Boilerplate
+
+.sr-only-focusable() {
+ &:active,
+ &:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+ }
+}
diff --git a/assets/font/fonts-awesome/less/path.less b/assets/font/fonts-awesome/less/path.less
index c5a6912..835be41 100644
--- a/assets/font/fonts-awesome/less/path.less
+++ b/assets/font/fonts-awesome/less/path.less
@@ -5,10 +5,11 @@
font-family: 'FontAwesome';
src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
+ url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
-// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
+ // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
font-weight: normal;
font-style: normal;
}
diff --git a/assets/font/fonts-awesome/less/screen-reader.less b/assets/font/fonts-awesome/less/screen-reader.less
new file mode 100644
index 0000000..11c1881
--- /dev/null
+++ b/assets/font/fonts-awesome/less/screen-reader.less
@@ -0,0 +1,5 @@
+// Screen Readers
+// -------------------------
+
+.sr-only { .sr-only(); }
+.sr-only-focusable { .sr-only-focusable(); }
diff --git a/assets/font/fonts-awesome/less/variables.less b/assets/font/fonts-awesome/less/variables.less
index ccf939d..7ddbbc0 100644
--- a/assets/font/fonts-awesome/less/variables.less
+++ b/assets/font/fonts-awesome/less/variables.less
@@ -2,20 +2,29 @@
// --------------------------
@fa-font-path: "../fonts";
-//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts"; // for referencing Bootstrap CDN font files directly
+@fa-font-size-base: 14px;
+@fa-line-height-base: 1;
+//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts"; // for referencing Bootstrap CDN font files directly
@fa-css-prefix: fa;
-@fa-version: "4.2.0";
+@fa-version: "4.7.0";
@fa-border-color: #eee;
@fa-inverse: #fff;
@fa-li-width: (30em / 14);
+@fa-var-500px: "\f26e";
+@fa-var-address-book: "\f2b9";
+@fa-var-address-book-o: "\f2ba";
+@fa-var-address-card: "\f2bb";
+@fa-var-address-card-o: "\f2bc";
@fa-var-adjust: "\f042";
@fa-var-adn: "\f170";
@fa-var-align-center: "\f037";
@fa-var-align-justify: "\f039";
@fa-var-align-left: "\f036";
@fa-var-align-right: "\f038";
+@fa-var-amazon: "\f270";
@fa-var-ambulance: "\f0f9";
+@fa-var-american-sign-language-interpreting: "\f2a3";
@fa-var-anchor: "\f13d";
@fa-var-android: "\f17b";
@fa-var-angellist: "\f209";
@@ -46,16 +55,35 @@
@fa-var-arrows-alt: "\f0b2";
@fa-var-arrows-h: "\f07e";
@fa-var-arrows-v: "\f07d";
+@fa-var-asl-interpreting: "\f2a3";
+@fa-var-assistive-listening-systems: "\f2a2";
@fa-var-asterisk: "\f069";
@fa-var-at: "\f1fa";
+@fa-var-audio-description: "\f29e";
@fa-var-automobile: "\f1b9";
@fa-var-backward: "\f04a";
+@fa-var-balance-scale: "\f24e";
@fa-var-ban: "\f05e";
+@fa-var-bandcamp: "\f2d5";
@fa-var-bank: "\f19c";
@fa-var-bar-chart: "\f080";
@fa-var-bar-chart-o: "\f080";
@fa-var-barcode: "\f02a";
@fa-var-bars: "\f0c9";
+@fa-var-bath: "\f2cd";
+@fa-var-bathtub: "\f2cd";
+@fa-var-battery: "\f240";
+@fa-var-battery-0: "\f244";
+@fa-var-battery-1: "\f243";
+@fa-var-battery-2: "\f242";
+@fa-var-battery-3: "\f241";
+@fa-var-battery-4: "\f240";
+@fa-var-battery-empty: "\f244";
+@fa-var-battery-full: "\f240";
+@fa-var-battery-half: "\f242";
+@fa-var-battery-quarter: "\f243";
+@fa-var-battery-three-quarters: "\f241";
+@fa-var-bed: "\f236";
@fa-var-beer: "\f0fc";
@fa-var-behance: "\f1b4";
@fa-var-behance-square: "\f1b5";
@@ -69,12 +97,17 @@
@fa-var-bitbucket: "\f171";
@fa-var-bitbucket-square: "\f172";
@fa-var-bitcoin: "\f15a";
+@fa-var-black-tie: "\f27e";
+@fa-var-blind: "\f29d";
+@fa-var-bluetooth: "\f293";
+@fa-var-bluetooth-b: "\f294";
@fa-var-bold: "\f032";
@fa-var-bolt: "\f0e7";
@fa-var-bomb: "\f1e2";
@fa-var-book: "\f02d";
@fa-var-bookmark: "\f02e";
@fa-var-bookmark-o: "\f097";
+@fa-var-braille: "\f2a1";
@fa-var-briefcase: "\f0b1";
@fa-var-btc: "\f15a";
@fa-var-bug: "\f188";
@@ -83,10 +116,15 @@
@fa-var-bullhorn: "\f0a1";
@fa-var-bullseye: "\f140";
@fa-var-bus: "\f207";
+@fa-var-buysellads: "\f20d";
@fa-var-cab: "\f1ba";
@fa-var-calculator: "\f1ec";
@fa-var-calendar: "\f073";
+@fa-var-calendar-check-o: "\f274";
+@fa-var-calendar-minus-o: "\f272";
@fa-var-calendar-o: "\f133";
+@fa-var-calendar-plus-o: "\f271";
+@fa-var-calendar-times-o: "\f273";
@fa-var-camera: "\f030";
@fa-var-camera-retro: "\f083";
@fa-var-car: "\f1b9";
@@ -98,9 +136,13 @@
@fa-var-caret-square-o-right: "\f152";
@fa-var-caret-square-o-up: "\f151";
@fa-var-caret-up: "\f0d8";
+@fa-var-cart-arrow-down: "\f218";
+@fa-var-cart-plus: "\f217";
@fa-var-cc: "\f20a";
@fa-var-cc-amex: "\f1f3";
+@fa-var-cc-diners-club: "\f24c";
@fa-var-cc-discover: "\f1f2";
+@fa-var-cc-jcb: "\f24b";
@fa-var-cc-mastercard: "\f1f1";
@fa-var-cc-paypal: "\f1f4";
@fa-var-cc-stripe: "\f1f5";
@@ -122,12 +164,14 @@
@fa-var-chevron-right: "\f054";
@fa-var-chevron-up: "\f077";
@fa-var-child: "\f1ae";
+@fa-var-chrome: "\f268";
@fa-var-circle: "\f111";
@fa-var-circle-o: "\f10c";
@fa-var-circle-o-notch: "\f1ce";
@fa-var-circle-thin: "\f1db";
@fa-var-clipboard: "\f0ea";
@fa-var-clock-o: "\f017";
+@fa-var-clone: "\f24d";
@fa-var-close: "\f00d";
@fa-var-cloud: "\f0c2";
@fa-var-cloud-download: "\f0ed";
@@ -136,19 +180,26 @@
@fa-var-code: "\f121";
@fa-var-code-fork: "\f126";
@fa-var-codepen: "\f1cb";
+@fa-var-codiepie: "\f284";
@fa-var-coffee: "\f0f4";
@fa-var-cog: "\f013";
@fa-var-cogs: "\f085";
@fa-var-columns: "\f0db";
@fa-var-comment: "\f075";
@fa-var-comment-o: "\f0e5";
+@fa-var-commenting: "\f27a";
+@fa-var-commenting-o: "\f27b";
@fa-var-comments: "\f086";
@fa-var-comments-o: "\f0e6";
@fa-var-compass: "\f14e";
@fa-var-compress: "\f066";
+@fa-var-connectdevelop: "\f20e";
+@fa-var-contao: "\f26d";
@fa-var-copy: "\f0c5";
@fa-var-copyright: "\f1f9";
+@fa-var-creative-commons: "\f25e";
@fa-var-credit-card: "\f09d";
+@fa-var-credit-card-alt: "\f283";
@fa-var-crop: "\f125";
@fa-var-crosshairs: "\f05b";
@fa-var-css3: "\f13c";
@@ -157,27 +208,39 @@
@fa-var-cut: "\f0c4";
@fa-var-cutlery: "\f0f5";
@fa-var-dashboard: "\f0e4";
+@fa-var-dashcube: "\f210";
@fa-var-database: "\f1c0";
+@fa-var-deaf: "\f2a4";
+@fa-var-deafness: "\f2a4";
@fa-var-dedent: "\f03b";
@fa-var-delicious: "\f1a5";
@fa-var-desktop: "\f108";
@fa-var-deviantart: "\f1bd";
+@fa-var-diamond: "\f219";
@fa-var-digg: "\f1a6";
@fa-var-dollar: "\f155";
@fa-var-dot-circle-o: "\f192";
@fa-var-download: "\f019";
@fa-var-dribbble: "\f17d";
+@fa-var-drivers-license: "\f2c2";
+@fa-var-drivers-license-o: "\f2c3";
@fa-var-dropbox: "\f16b";
@fa-var-drupal: "\f1a9";
+@fa-var-edge: "\f282";
@fa-var-edit: "\f044";
+@fa-var-eercast: "\f2da";
@fa-var-eject: "\f052";
@fa-var-ellipsis-h: "\f141";
@fa-var-ellipsis-v: "\f142";
@fa-var-empire: "\f1d1";
@fa-var-envelope: "\f0e0";
@fa-var-envelope-o: "\f003";
+@fa-var-envelope-open: "\f2b6";
+@fa-var-envelope-open-o: "\f2b7";
@fa-var-envelope-square: "\f199";
+@fa-var-envira: "\f299";
@fa-var-eraser: "\f12d";
+@fa-var-etsy: "\f2d7";
@fa-var-eur: "\f153";
@fa-var-euro: "\f153";
@fa-var-exchange: "\f0ec";
@@ -185,16 +248,21 @@
@fa-var-exclamation-circle: "\f06a";
@fa-var-exclamation-triangle: "\f071";
@fa-var-expand: "\f065";
+@fa-var-expeditedssl: "\f23e";
@fa-var-external-link: "\f08e";
@fa-var-external-link-square: "\f14c";
@fa-var-eye: "\f06e";
@fa-var-eye-slash: "\f070";
@fa-var-eyedropper: "\f1fb";
+@fa-var-fa: "\f2b4";
@fa-var-facebook: "\f09a";
+@fa-var-facebook-f: "\f09a";
+@fa-var-facebook-official: "\f230";
@fa-var-facebook-square: "\f082";
@fa-var-fast-backward: "\f049";
@fa-var-fast-forward: "\f050";
@fa-var-fax: "\f1ac";
+@fa-var-feed: "\f09e";
@fa-var-female: "\f182";
@fa-var-fighter-jet: "\f0fb";
@fa-var-file: "\f15b";
@@ -220,6 +288,8 @@
@fa-var-filter: "\f0b0";
@fa-var-fire: "\f06d";
@fa-var-fire-extinguisher: "\f134";
+@fa-var-firefox: "\f269";
+@fa-var-first-order: "\f2b0";
@fa-var-flag: "\f024";
@fa-var-flag-checkered: "\f11e";
@fa-var-flag-o: "\f11d";
@@ -232,8 +302,13 @@
@fa-var-folder-open: "\f07c";
@fa-var-folder-open-o: "\f115";
@fa-var-font: "\f031";
+@fa-var-font-awesome: "\f2b4";
+@fa-var-fonticons: "\f280";
+@fa-var-fort-awesome: "\f286";
+@fa-var-forumbee: "\f211";
@fa-var-forward: "\f04e";
@fa-var-foursquare: "\f180";
+@fa-var-free-code-camp: "\f2c5";
@fa-var-frown-o: "\f119";
@fa-var-futbol-o: "\f1e3";
@fa-var-gamepad: "\f11b";
@@ -242,45 +317,87 @@
@fa-var-ge: "\f1d1";
@fa-var-gear: "\f013";
@fa-var-gears: "\f085";
+@fa-var-genderless: "\f22d";
+@fa-var-get-pocket: "\f265";
+@fa-var-gg: "\f260";
+@fa-var-gg-circle: "\f261";
@fa-var-gift: "\f06b";
@fa-var-git: "\f1d3";
@fa-var-git-square: "\f1d2";
@fa-var-github: "\f09b";
@fa-var-github-alt: "\f113";
@fa-var-github-square: "\f092";
+@fa-var-gitlab: "\f296";
@fa-var-gittip: "\f184";
@fa-var-glass: "\f000";
+@fa-var-glide: "\f2a5";
+@fa-var-glide-g: "\f2a6";
@fa-var-globe: "\f0ac";
@fa-var-google: "\f1a0";
@fa-var-google-plus: "\f0d5";
+@fa-var-google-plus-circle: "\f2b3";
+@fa-var-google-plus-official: "\f2b3";
@fa-var-google-plus-square: "\f0d4";
@fa-var-google-wallet: "\f1ee";
@fa-var-graduation-cap: "\f19d";
+@fa-var-gratipay: "\f184";
+@fa-var-grav: "\f2d6";
@fa-var-group: "\f0c0";
@fa-var-h-square: "\f0fd";
@fa-var-hacker-news: "\f1d4";
+@fa-var-hand-grab-o: "\f255";
+@fa-var-hand-lizard-o: "\f258";
@fa-var-hand-o-down: "\f0a7";
@fa-var-hand-o-left: "\f0a5";
@fa-var-hand-o-right: "\f0a4";
@fa-var-hand-o-up: "\f0a6";
+@fa-var-hand-paper-o: "\f256";
+@fa-var-hand-peace-o: "\f25b";
+@fa-var-hand-pointer-o: "\f25a";
+@fa-var-hand-rock-o: "\f255";
+@fa-var-hand-scissors-o: "\f257";
+@fa-var-hand-spock-o: "\f259";
+@fa-var-hand-stop-o: "\f256";
+@fa-var-handshake-o: "\f2b5";
+@fa-var-hard-of-hearing: "\f2a4";
+@fa-var-hashtag: "\f292";
@fa-var-hdd-o: "\f0a0";
@fa-var-header: "\f1dc";
@fa-var-headphones: "\f025";
@fa-var-heart: "\f004";
@fa-var-heart-o: "\f08a";
+@fa-var-heartbeat: "\f21e";
@fa-var-history: "\f1da";
@fa-var-home: "\f015";
@fa-var-hospital-o: "\f0f8";
+@fa-var-hotel: "\f236";
+@fa-var-hourglass: "\f254";
+@fa-var-hourglass-1: "\f251";
+@fa-var-hourglass-2: "\f252";
+@fa-var-hourglass-3: "\f253";
+@fa-var-hourglass-end: "\f253";
+@fa-var-hourglass-half: "\f252";
+@fa-var-hourglass-o: "\f250";
+@fa-var-hourglass-start: "\f251";
+@fa-var-houzz: "\f27c";
@fa-var-html5: "\f13b";
+@fa-var-i-cursor: "\f246";
+@fa-var-id-badge: "\f2c1";
+@fa-var-id-card: "\f2c2";
+@fa-var-id-card-o: "\f2c3";
@fa-var-ils: "\f20b";
@fa-var-image: "\f03e";
+@fa-var-imdb: "\f2d8";
@fa-var-inbox: "\f01c";
@fa-var-indent: "\f03c";
+@fa-var-industry: "\f275";
@fa-var-info: "\f129";
@fa-var-info-circle: "\f05a";
@fa-var-inr: "\f156";
@fa-var-instagram: "\f16d";
@fa-var-institution: "\f19c";
+@fa-var-internet-explorer: "\f26b";
+@fa-var-intersex: "\f224";
@fa-var-ioxhost: "\f208";
@fa-var-italic: "\f033";
@fa-var-joomla: "\f1aa";
@@ -294,6 +411,7 @@
@fa-var-lastfm: "\f202";
@fa-var-lastfm-square: "\f203";
@fa-var-leaf: "\f06c";
+@fa-var-leanpub: "\f212";
@fa-var-legal: "\f0e3";
@fa-var-lemon-o: "\f094";
@fa-var-level-down: "\f149";
@@ -307,6 +425,7 @@
@fa-var-link: "\f0c1";
@fa-var-linkedin: "\f0e1";
@fa-var-linkedin-square: "\f08c";
+@fa-var-linode: "\f2b8";
@fa-var-linux: "\f17c";
@fa-var-list: "\f03a";
@fa-var-list-alt: "\f022";
@@ -318,32 +437,58 @@
@fa-var-long-arrow-left: "\f177";
@fa-var-long-arrow-right: "\f178";
@fa-var-long-arrow-up: "\f176";
+@fa-var-low-vision: "\f2a8";
@fa-var-magic: "\f0d0";
@fa-var-magnet: "\f076";
@fa-var-mail-forward: "\f064";
@fa-var-mail-reply: "\f112";
@fa-var-mail-reply-all: "\f122";
@fa-var-male: "\f183";
+@fa-var-map: "\f279";
@fa-var-map-marker: "\f041";
+@fa-var-map-o: "\f278";
+@fa-var-map-pin: "\f276";
+@fa-var-map-signs: "\f277";
+@fa-var-mars: "\f222";
+@fa-var-mars-double: "\f227";
+@fa-var-mars-stroke: "\f229";
+@fa-var-mars-stroke-h: "\f22b";
+@fa-var-mars-stroke-v: "\f22a";
@fa-var-maxcdn: "\f136";
@fa-var-meanpath: "\f20c";
+@fa-var-medium: "\f23a";
@fa-var-medkit: "\f0fa";
+@fa-var-meetup: "\f2e0";
@fa-var-meh-o: "\f11a";
+@fa-var-mercury: "\f223";
+@fa-var-microchip: "\f2db";
@fa-var-microphone: "\f130";
@fa-var-microphone-slash: "\f131";
@fa-var-minus: "\f068";
@fa-var-minus-circle: "\f056";
@fa-var-minus-square: "\f146";
@fa-var-minus-square-o: "\f147";
+@fa-var-mixcloud: "\f289";
@fa-var-mobile: "\f10b";
@fa-var-mobile-phone: "\f10b";
+@fa-var-modx: "\f285";
@fa-var-money: "\f0d6";
@fa-var-moon-o: "\f186";
@fa-var-mortar-board: "\f19d";
+@fa-var-motorcycle: "\f21c";
+@fa-var-mouse-pointer: "\f245";
@fa-var-music: "\f001";
@fa-var-navicon: "\f0c9";
+@fa-var-neuter: "\f22c";
@fa-var-newspaper-o: "\f1ea";
+@fa-var-object-group: "\f247";
+@fa-var-object-ungroup: "\f248";
+@fa-var-odnoklassniki: "\f263";
+@fa-var-odnoklassniki-square: "\f264";
+@fa-var-opencart: "\f23d";
@fa-var-openid: "\f19b";
+@fa-var-opera: "\f26a";
+@fa-var-optin-monster: "\f23c";
@fa-var-outdent: "\f03b";
@fa-var-pagelines: "\f18c";
@fa-var-paint-brush: "\f1fc";
@@ -353,19 +498,24 @@
@fa-var-paragraph: "\f1dd";
@fa-var-paste: "\f0ea";
@fa-var-pause: "\f04c";
+@fa-var-pause-circle: "\f28b";
+@fa-var-pause-circle-o: "\f28c";
@fa-var-paw: "\f1b0";
@fa-var-paypal: "\f1ed";
@fa-var-pencil: "\f040";
@fa-var-pencil-square: "\f14b";
@fa-var-pencil-square-o: "\f044";
+@fa-var-percent: "\f295";
@fa-var-phone: "\f095";
@fa-var-phone-square: "\f098";
@fa-var-photo: "\f03e";
@fa-var-picture-o: "\f03e";
@fa-var-pie-chart: "\f200";
-@fa-var-pied-piper: "\f1a7";
+@fa-var-pied-piper: "\f2ae";
@fa-var-pied-piper-alt: "\f1a8";
+@fa-var-pied-piper-pp: "\f1a7";
@fa-var-pinterest: "\f0d2";
+@fa-var-pinterest-p: "\f231";
@fa-var-pinterest-square: "\f0d3";
@fa-var-plane: "\f072";
@fa-var-play: "\f04b";
@@ -376,28 +526,36 @@
@fa-var-plus-circle: "\f055";
@fa-var-plus-square: "\f0fe";
@fa-var-plus-square-o: "\f196";
+@fa-var-podcast: "\f2ce";
@fa-var-power-off: "\f011";
@fa-var-print: "\f02f";
+@fa-var-product-hunt: "\f288";
@fa-var-puzzle-piece: "\f12e";
@fa-var-qq: "\f1d6";
@fa-var-qrcode: "\f029";
@fa-var-question: "\f128";
@fa-var-question-circle: "\f059";
+@fa-var-question-circle-o: "\f29c";
+@fa-var-quora: "\f2c4";
@fa-var-quote-left: "\f10d";
@fa-var-quote-right: "\f10e";
@fa-var-ra: "\f1d0";
@fa-var-random: "\f074";
+@fa-var-ravelry: "\f2d9";
@fa-var-rebel: "\f1d0";
@fa-var-recycle: "\f1b8";
@fa-var-reddit: "\f1a1";
+@fa-var-reddit-alien: "\f281";
@fa-var-reddit-square: "\f1a2";
@fa-var-refresh: "\f021";
+@fa-var-registered: "\f25d";
@fa-var-remove: "\f00d";
@fa-var-renren: "\f18b";
@fa-var-reorder: "\f0c9";
@fa-var-repeat: "\f01e";
@fa-var-reply: "\f112";
@fa-var-reply-all: "\f122";
+@fa-var-resistance: "\f1d0";
@fa-var-retweet: "\f079";
@fa-var-rmb: "\f157";
@fa-var-road: "\f018";
@@ -410,13 +568,18 @@
@fa-var-rub: "\f158";
@fa-var-ruble: "\f158";
@fa-var-rupee: "\f156";
+@fa-var-s15: "\f2cd";
+@fa-var-safari: "\f267";
@fa-var-save: "\f0c7";
@fa-var-scissors: "\f0c4";
+@fa-var-scribd: "\f28a";
@fa-var-search: "\f002";
@fa-var-search-minus: "\f010";
@fa-var-search-plus: "\f00e";
+@fa-var-sellsy: "\f213";
@fa-var-send: "\f1d8";
@fa-var-send-o: "\f1d9";
+@fa-var-server: "\f233";
@fa-var-share: "\f064";
@fa-var-share-alt: "\f1e0";
@fa-var-share-alt-square: "\f1e1";
@@ -425,16 +588,29 @@
@fa-var-shekel: "\f20b";
@fa-var-sheqel: "\f20b";
@fa-var-shield: "\f132";
+@fa-var-ship: "\f21a";
+@fa-var-shirtsinbulk: "\f214";
+@fa-var-shopping-bag: "\f290";
+@fa-var-shopping-basket: "\f291";
@fa-var-shopping-cart: "\f07a";
+@fa-var-shower: "\f2cc";
@fa-var-sign-in: "\f090";
+@fa-var-sign-language: "\f2a7";
@fa-var-sign-out: "\f08b";
@fa-var-signal: "\f012";
+@fa-var-signing: "\f2a7";
+@fa-var-simplybuilt: "\f215";
@fa-var-sitemap: "\f0e8";
+@fa-var-skyatlas: "\f216";
@fa-var-skype: "\f17e";
@fa-var-slack: "\f198";
@fa-var-sliders: "\f1de";
@fa-var-slideshare: "\f1e7";
@fa-var-smile-o: "\f118";
+@fa-var-snapchat: "\f2ab";
+@fa-var-snapchat-ghost: "\f2ac";
+@fa-var-snapchat-square: "\f2ad";
+@fa-var-snowflake-o: "\f2dc";
@fa-var-soccer-ball-o: "\f1e3";
@fa-var-sort: "\f0dc";
@fa-var-sort-alpha-asc: "\f15d";
@@ -467,13 +643,20 @@
@fa-var-step-backward: "\f048";
@fa-var-step-forward: "\f051";
@fa-var-stethoscope: "\f0f1";
+@fa-var-sticky-note: "\f249";
+@fa-var-sticky-note-o: "\f24a";
@fa-var-stop: "\f04d";
+@fa-var-stop-circle: "\f28d";
+@fa-var-stop-circle-o: "\f28e";
+@fa-var-street-view: "\f21d";
@fa-var-strikethrough: "\f0cc";
@fa-var-stumbleupon: "\f1a4";
@fa-var-stumbleupon-circle: "\f1a3";
@fa-var-subscript: "\f12c";
+@fa-var-subway: "\f239";
@fa-var-suitcase: "\f0f2";
@fa-var-sun-o: "\f185";
+@fa-var-superpowers: "\f2dd";
@fa-var-superscript: "\f12b";
@fa-var-support: "\f1cd";
@fa-var-table: "\f0ce";
@@ -483,6 +666,8 @@
@fa-var-tags: "\f02c";
@fa-var-tasks: "\f0ae";
@fa-var-taxi: "\f1ba";
+@fa-var-telegram: "\f2c6";
+@fa-var-television: "\f26c";
@fa-var-tencent-weibo: "\f1d5";
@fa-var-terminal: "\f120";
@fa-var-text-height: "\f034";
@@ -490,6 +675,18 @@
@fa-var-th: "\f00a";
@fa-var-th-large: "\f009";
@fa-var-th-list: "\f00b";
+@fa-var-themeisle: "\f2b2";
+@fa-var-thermometer: "\f2c7";
+@fa-var-thermometer-0: "\f2cb";
+@fa-var-thermometer-1: "\f2ca";
+@fa-var-thermometer-2: "\f2c9";
+@fa-var-thermometer-3: "\f2c8";
+@fa-var-thermometer-4: "\f2c7";
+@fa-var-thermometer-empty: "\f2cb";
+@fa-var-thermometer-full: "\f2c7";
+@fa-var-thermometer-half: "\f2c9";
+@fa-var-thermometer-quarter: "\f2ca";
+@fa-var-thermometer-three-quarters: "\f2c8";
@fa-var-thumb-tack: "\f08d";
@fa-var-thumbs-down: "\f165";
@fa-var-thumbs-o-down: "\f088";
@@ -499,6 +696,8 @@
@fa-var-times: "\f00d";
@fa-var-times-circle: "\f057";
@fa-var-times-circle-o: "\f05c";
+@fa-var-times-rectangle: "\f2d3";
+@fa-var-times-rectangle-o: "\f2d4";
@fa-var-tint: "\f043";
@fa-var-toggle-down: "\f150";
@fa-var-toggle-left: "\f191";
@@ -506,10 +705,15 @@
@fa-var-toggle-on: "\f205";
@fa-var-toggle-right: "\f152";
@fa-var-toggle-up: "\f151";
+@fa-var-trademark: "\f25c";
+@fa-var-train: "\f238";
+@fa-var-transgender: "\f224";
+@fa-var-transgender-alt: "\f225";
@fa-var-trash: "\f1f8";
@fa-var-trash-o: "\f014";
@fa-var-tree: "\f1bb";
@fa-var-trello: "\f181";
+@fa-var-tripadvisor: "\f262";
@fa-var-trophy: "\f091";
@fa-var-truck: "\f0d1";
@fa-var-try: "\f195";
@@ -517,26 +721,45 @@
@fa-var-tumblr: "\f173";
@fa-var-tumblr-square: "\f174";
@fa-var-turkish-lira: "\f195";
+@fa-var-tv: "\f26c";
@fa-var-twitch: "\f1e8";
@fa-var-twitter: "\f099";
@fa-var-twitter-square: "\f081";
@fa-var-umbrella: "\f0e9";
@fa-var-underline: "\f0cd";
@fa-var-undo: "\f0e2";
+@fa-var-universal-access: "\f29a";
@fa-var-university: "\f19c";
@fa-var-unlink: "\f127";
@fa-var-unlock: "\f09c";
@fa-var-unlock-alt: "\f13e";
@fa-var-unsorted: "\f0dc";
@fa-var-upload: "\f093";
+@fa-var-usb: "\f287";
@fa-var-usd: "\f155";
@fa-var-user: "\f007";
+@fa-var-user-circle: "\f2bd";
+@fa-var-user-circle-o: "\f2be";
@fa-var-user-md: "\f0f0";
+@fa-var-user-o: "\f2c0";
+@fa-var-user-plus: "\f234";
+@fa-var-user-secret: "\f21b";
+@fa-var-user-times: "\f235";
@fa-var-users: "\f0c0";
+@fa-var-vcard: "\f2bb";
+@fa-var-vcard-o: "\f2bc";
+@fa-var-venus: "\f221";
+@fa-var-venus-double: "\f226";
+@fa-var-venus-mars: "\f228";
+@fa-var-viacoin: "\f237";
+@fa-var-viadeo: "\f2a9";
+@fa-var-viadeo-square: "\f2aa";
@fa-var-video-camera: "\f03d";
+@fa-var-vimeo: "\f27d";
@fa-var-vimeo-square: "\f194";
@fa-var-vine: "\f1ca";
@fa-var-vk: "\f189";
+@fa-var-volume-control-phone: "\f2a0";
@fa-var-volume-down: "\f027";
@fa-var-volume-off: "\f026";
@fa-var-volume-up: "\f028";
@@ -544,17 +767,33 @@
@fa-var-wechat: "\f1d7";
@fa-var-weibo: "\f18a";
@fa-var-weixin: "\f1d7";
+@fa-var-whatsapp: "\f232";
@fa-var-wheelchair: "\f193";
+@fa-var-wheelchair-alt: "\f29b";
@fa-var-wifi: "\f1eb";
+@fa-var-wikipedia-w: "\f266";
+@fa-var-window-close: "\f2d3";
+@fa-var-window-close-o: "\f2d4";
+@fa-var-window-maximize: "\f2d0";
+@fa-var-window-minimize: "\f2d1";
+@fa-var-window-restore: "\f2d2";
@fa-var-windows: "\f17a";
@fa-var-won: "\f159";
@fa-var-wordpress: "\f19a";
+@fa-var-wpbeginner: "\f297";
+@fa-var-wpexplorer: "\f2de";
+@fa-var-wpforms: "\f298";
@fa-var-wrench: "\f0ad";
@fa-var-xing: "\f168";
@fa-var-xing-square: "\f169";
+@fa-var-y-combinator: "\f23b";
+@fa-var-y-combinator-square: "\f1d4";
@fa-var-yahoo: "\f19e";
+@fa-var-yc: "\f23b";
+@fa-var-yc-square: "\f1d4";
@fa-var-yelp: "\f1e9";
@fa-var-yen: "\f157";
+@fa-var-yoast: "\f2b1";
@fa-var-youtube: "\f167";
@fa-var-youtube-play: "\f16a";
@fa-var-youtube-square: "\f166";
diff --git a/assets/font/fonts-awesome/scss/_spinning.scss b/assets/font/fonts-awesome/scss/_animated.scss
similarity index 81%
rename from assets/font/fonts-awesome/scss/_spinning.scss
rename to assets/font/fonts-awesome/scss/_animated.scss
index 002c5d5..8a020db 100644
--- a/assets/font/fonts-awesome/scss/_spinning.scss
+++ b/assets/font/fonts-awesome/scss/_animated.scss
@@ -6,6 +6,11 @@
animation: fa-spin 2s infinite linear;
}
+.#{$fa-css-prefix}-pulse {
+ -webkit-animation: fa-spin 1s infinite steps(8);
+ animation: fa-spin 1s infinite steps(8);
+}
+
@-webkit-keyframes fa-spin {
0% {
-webkit-transform: rotate(0deg);
diff --git a/assets/font/fonts-awesome/scss/_bordered-pulled.scss b/assets/font/fonts-awesome/scss/_bordered-pulled.scss
index 9d3fdf3..d4b85a0 100644
--- a/assets/font/fonts-awesome/scss/_bordered-pulled.scss
+++ b/assets/font/fonts-awesome/scss/_bordered-pulled.scss
@@ -7,6 +7,15 @@
border-radius: .1em;
}
+.#{$fa-css-prefix}-pull-left { float: left; }
+.#{$fa-css-prefix}-pull-right { float: right; }
+
+.#{$fa-css-prefix} {
+ &.#{$fa-css-prefix}-pull-left { margin-right: .3em; }
+ &.#{$fa-css-prefix}-pull-right { margin-left: .3em; }
+}
+
+/* Deprecated as of 4.4.0 */
.pull-right { float: right; }
.pull-left { float: left; }
diff --git a/assets/font/fonts-awesome/scss/_core.scss b/assets/font/fonts-awesome/scss/_core.scss
index ca46d37..7425ef8 100644
--- a/assets/font/fonts-awesome/scss/_core.scss
+++ b/assets/font/fonts-awesome/scss/_core.scss
@@ -3,9 +3,10 @@
.#{$fa-css-prefix} {
display: inline-block;
- font: normal normal normal 14px/1 FontAwesome; // shortening font declaration
+ font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+
}
diff --git a/assets/font/fonts-awesome/scss/_icons.scss b/assets/font/fonts-awesome/scss/_icons.scss
index 8dc2939..e63e702 100644
--- a/assets/font/fonts-awesome/scss/_icons.scss
+++ b/assets/font/fonts-awesome/scss/_icons.scss
@@ -158,10 +158,12 @@
.#{$fa-css-prefix}-bookmark-o:before { content: $fa-var-bookmark-o; }
.#{$fa-css-prefix}-phone-square:before { content: $fa-var-phone-square; }
.#{$fa-css-prefix}-twitter:before { content: $fa-var-twitter; }
+.#{$fa-css-prefix}-facebook-f:before,
.#{$fa-css-prefix}-facebook:before { content: $fa-var-facebook; }
.#{$fa-css-prefix}-github:before { content: $fa-var-github; }
.#{$fa-css-prefix}-unlock:before { content: $fa-var-unlock; }
.#{$fa-css-prefix}-credit-card:before { content: $fa-var-credit-card; }
+.#{$fa-css-prefix}-feed:before,
.#{$fa-css-prefix}-rss:before { content: $fa-var-rss; }
.#{$fa-css-prefix}-hdd-o:before { content: $fa-var-hdd-o; }
.#{$fa-css-prefix}-bullhorn:before { content: $fa-var-bullhorn; }
@@ -397,7 +399,8 @@
.#{$fa-css-prefix}-trello:before { content: $fa-var-trello; }
.#{$fa-css-prefix}-female:before { content: $fa-var-female; }
.#{$fa-css-prefix}-male:before { content: $fa-var-male; }
-.#{$fa-css-prefix}-gittip:before { content: $fa-var-gittip; }
+.#{$fa-css-prefix}-gittip:before,
+.#{$fa-css-prefix}-gratipay:before { content: $fa-var-gratipay; }
.#{$fa-css-prefix}-sun-o:before { content: $fa-var-sun-o; }
.#{$fa-css-prefix}-moon-o:before { content: $fa-var-moon-o; }
.#{$fa-css-prefix}-archive:before { content: $fa-var-archive; }
@@ -435,7 +438,7 @@
.#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; }
.#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; }
.#{$fa-css-prefix}-digg:before { content: $fa-var-digg; }
-.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; }
+.#{$fa-css-prefix}-pied-piper-pp:before { content: $fa-var-pied-piper-pp; }
.#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; }
.#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; }
.#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; }
@@ -485,11 +488,14 @@
.#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; }
.#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; }
.#{$fa-css-prefix}-ra:before,
+.#{$fa-css-prefix}-resistance:before,
.#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; }
.#{$fa-css-prefix}-ge:before,
.#{$fa-css-prefix}-empire:before { content: $fa-var-empire; }
.#{$fa-css-prefix}-git-square:before { content: $fa-var-git-square; }
.#{$fa-css-prefix}-git:before { content: $fa-var-git; }
+.#{$fa-css-prefix}-y-combinator-square:before,
+.#{$fa-css-prefix}-yc-square:before,
.#{$fa-css-prefix}-hacker-news:before { content: $fa-var-hacker-news; }
.#{$fa-css-prefix}-tencent-weibo:before { content: $fa-var-tencent-weibo; }
.#{$fa-css-prefix}-qq:before { content: $fa-var-qq; }
@@ -550,3 +556,234 @@
.#{$fa-css-prefix}-sheqel:before,
.#{$fa-css-prefix}-ils:before { content: $fa-var-ils; }
.#{$fa-css-prefix}-meanpath:before { content: $fa-var-meanpath; }
+.#{$fa-css-prefix}-buysellads:before { content: $fa-var-buysellads; }
+.#{$fa-css-prefix}-connectdevelop:before { content: $fa-var-connectdevelop; }
+.#{$fa-css-prefix}-dashcube:before { content: $fa-var-dashcube; }
+.#{$fa-css-prefix}-forumbee:before { content: $fa-var-forumbee; }
+.#{$fa-css-prefix}-leanpub:before { content: $fa-var-leanpub; }
+.#{$fa-css-prefix}-sellsy:before { content: $fa-var-sellsy; }
+.#{$fa-css-prefix}-shirtsinbulk:before { content: $fa-var-shirtsinbulk; }
+.#{$fa-css-prefix}-simplybuilt:before { content: $fa-var-simplybuilt; }
+.#{$fa-css-prefix}-skyatlas:before { content: $fa-var-skyatlas; }
+.#{$fa-css-prefix}-cart-plus:before { content: $fa-var-cart-plus; }
+.#{$fa-css-prefix}-cart-arrow-down:before { content: $fa-var-cart-arrow-down; }
+.#{$fa-css-prefix}-diamond:before { content: $fa-var-diamond; }
+.#{$fa-css-prefix}-ship:before { content: $fa-var-ship; }
+.#{$fa-css-prefix}-user-secret:before { content: $fa-var-user-secret; }
+.#{$fa-css-prefix}-motorcycle:before { content: $fa-var-motorcycle; }
+.#{$fa-css-prefix}-street-view:before { content: $fa-var-street-view; }
+.#{$fa-css-prefix}-heartbeat:before { content: $fa-var-heartbeat; }
+.#{$fa-css-prefix}-venus:before { content: $fa-var-venus; }
+.#{$fa-css-prefix}-mars:before { content: $fa-var-mars; }
+.#{$fa-css-prefix}-mercury:before { content: $fa-var-mercury; }
+.#{$fa-css-prefix}-intersex:before,
+.#{$fa-css-prefix}-transgender:before { content: $fa-var-transgender; }
+.#{$fa-css-prefix}-transgender-alt:before { content: $fa-var-transgender-alt; }
+.#{$fa-css-prefix}-venus-double:before { content: $fa-var-venus-double; }
+.#{$fa-css-prefix}-mars-double:before { content: $fa-var-mars-double; }
+.#{$fa-css-prefix}-venus-mars:before { content: $fa-var-venus-mars; }
+.#{$fa-css-prefix}-mars-stroke:before { content: $fa-var-mars-stroke; }
+.#{$fa-css-prefix}-mars-stroke-v:before { content: $fa-var-mars-stroke-v; }
+.#{$fa-css-prefix}-mars-stroke-h:before { content: $fa-var-mars-stroke-h; }
+.#{$fa-css-prefix}-neuter:before { content: $fa-var-neuter; }
+.#{$fa-css-prefix}-genderless:before { content: $fa-var-genderless; }
+.#{$fa-css-prefix}-facebook-official:before { content: $fa-var-facebook-official; }
+.#{$fa-css-prefix}-pinterest-p:before { content: $fa-var-pinterest-p; }
+.#{$fa-css-prefix}-whatsapp:before { content: $fa-var-whatsapp; }
+.#{$fa-css-prefix}-server:before { content: $fa-var-server; }
+.#{$fa-css-prefix}-user-plus:before { content: $fa-var-user-plus; }
+.#{$fa-css-prefix}-user-times:before { content: $fa-var-user-times; }
+.#{$fa-css-prefix}-hotel:before,
+.#{$fa-css-prefix}-bed:before { content: $fa-var-bed; }
+.#{$fa-css-prefix}-viacoin:before { content: $fa-var-viacoin; }
+.#{$fa-css-prefix}-train:before { content: $fa-var-train; }
+.#{$fa-css-prefix}-subway:before { content: $fa-var-subway; }
+.#{$fa-css-prefix}-medium:before { content: $fa-var-medium; }
+.#{$fa-css-prefix}-yc:before,
+.#{$fa-css-prefix}-y-combinator:before { content: $fa-var-y-combinator; }
+.#{$fa-css-prefix}-optin-monster:before { content: $fa-var-optin-monster; }
+.#{$fa-css-prefix}-opencart:before { content: $fa-var-opencart; }
+.#{$fa-css-prefix}-expeditedssl:before { content: $fa-var-expeditedssl; }
+.#{$fa-css-prefix}-battery-4:before,
+.#{$fa-css-prefix}-battery:before,
+.#{$fa-css-prefix}-battery-full:before { content: $fa-var-battery-full; }
+.#{$fa-css-prefix}-battery-3:before,
+.#{$fa-css-prefix}-battery-three-quarters:before { content: $fa-var-battery-three-quarters; }
+.#{$fa-css-prefix}-battery-2:before,
+.#{$fa-css-prefix}-battery-half:before { content: $fa-var-battery-half; }
+.#{$fa-css-prefix}-battery-1:before,
+.#{$fa-css-prefix}-battery-quarter:before { content: $fa-var-battery-quarter; }
+.#{$fa-css-prefix}-battery-0:before,
+.#{$fa-css-prefix}-battery-empty:before { content: $fa-var-battery-empty; }
+.#{$fa-css-prefix}-mouse-pointer:before { content: $fa-var-mouse-pointer; }
+.#{$fa-css-prefix}-i-cursor:before { content: $fa-var-i-cursor; }
+.#{$fa-css-prefix}-object-group:before { content: $fa-var-object-group; }
+.#{$fa-css-prefix}-object-ungroup:before { content: $fa-var-object-ungroup; }
+.#{$fa-css-prefix}-sticky-note:before { content: $fa-var-sticky-note; }
+.#{$fa-css-prefix}-sticky-note-o:before { content: $fa-var-sticky-note-o; }
+.#{$fa-css-prefix}-cc-jcb:before { content: $fa-var-cc-jcb; }
+.#{$fa-css-prefix}-cc-diners-club:before { content: $fa-var-cc-diners-club; }
+.#{$fa-css-prefix}-clone:before { content: $fa-var-clone; }
+.#{$fa-css-prefix}-balance-scale:before { content: $fa-var-balance-scale; }
+.#{$fa-css-prefix}-hourglass-o:before { content: $fa-var-hourglass-o; }
+.#{$fa-css-prefix}-hourglass-1:before,
+.#{$fa-css-prefix}-hourglass-start:before { content: $fa-var-hourglass-start; }
+.#{$fa-css-prefix}-hourglass-2:before,
+.#{$fa-css-prefix}-hourglass-half:before { content: $fa-var-hourglass-half; }
+.#{$fa-css-prefix}-hourglass-3:before,
+.#{$fa-css-prefix}-hourglass-end:before { content: $fa-var-hourglass-end; }
+.#{$fa-css-prefix}-hourglass:before { content: $fa-var-hourglass; }
+.#{$fa-css-prefix}-hand-grab-o:before,
+.#{$fa-css-prefix}-hand-rock-o:before { content: $fa-var-hand-rock-o; }
+.#{$fa-css-prefix}-hand-stop-o:before,
+.#{$fa-css-prefix}-hand-paper-o:before { content: $fa-var-hand-paper-o; }
+.#{$fa-css-prefix}-hand-scissors-o:before { content: $fa-var-hand-scissors-o; }
+.#{$fa-css-prefix}-hand-lizard-o:before { content: $fa-var-hand-lizard-o; }
+.#{$fa-css-prefix}-hand-spock-o:before { content: $fa-var-hand-spock-o; }
+.#{$fa-css-prefix}-hand-pointer-o:before { content: $fa-var-hand-pointer-o; }
+.#{$fa-css-prefix}-hand-peace-o:before { content: $fa-var-hand-peace-o; }
+.#{$fa-css-prefix}-trademark:before { content: $fa-var-trademark; }
+.#{$fa-css-prefix}-registered:before { content: $fa-var-registered; }
+.#{$fa-css-prefix}-creative-commons:before { content: $fa-var-creative-commons; }
+.#{$fa-css-prefix}-gg:before { content: $fa-var-gg; }
+.#{$fa-css-prefix}-gg-circle:before { content: $fa-var-gg-circle; }
+.#{$fa-css-prefix}-tripadvisor:before { content: $fa-var-tripadvisor; }
+.#{$fa-css-prefix}-odnoklassniki:before { content: $fa-var-odnoklassniki; }
+.#{$fa-css-prefix}-odnoklassniki-square:before { content: $fa-var-odnoklassniki-square; }
+.#{$fa-css-prefix}-get-pocket:before { content: $fa-var-get-pocket; }
+.#{$fa-css-prefix}-wikipedia-w:before { content: $fa-var-wikipedia-w; }
+.#{$fa-css-prefix}-safari:before { content: $fa-var-safari; }
+.#{$fa-css-prefix}-chrome:before { content: $fa-var-chrome; }
+.#{$fa-css-prefix}-firefox:before { content: $fa-var-firefox; }
+.#{$fa-css-prefix}-opera:before { content: $fa-var-opera; }
+.#{$fa-css-prefix}-internet-explorer:before { content: $fa-var-internet-explorer; }
+.#{$fa-css-prefix}-tv:before,
+.#{$fa-css-prefix}-television:before { content: $fa-var-television; }
+.#{$fa-css-prefix}-contao:before { content: $fa-var-contao; }
+.#{$fa-css-prefix}-500px:before { content: $fa-var-500px; }
+.#{$fa-css-prefix}-amazon:before { content: $fa-var-amazon; }
+.#{$fa-css-prefix}-calendar-plus-o:before { content: $fa-var-calendar-plus-o; }
+.#{$fa-css-prefix}-calendar-minus-o:before { content: $fa-var-calendar-minus-o; }
+.#{$fa-css-prefix}-calendar-times-o:before { content: $fa-var-calendar-times-o; }
+.#{$fa-css-prefix}-calendar-check-o:before { content: $fa-var-calendar-check-o; }
+.#{$fa-css-prefix}-industry:before { content: $fa-var-industry; }
+.#{$fa-css-prefix}-map-pin:before { content: $fa-var-map-pin; }
+.#{$fa-css-prefix}-map-signs:before { content: $fa-var-map-signs; }
+.#{$fa-css-prefix}-map-o:before { content: $fa-var-map-o; }
+.#{$fa-css-prefix}-map:before { content: $fa-var-map; }
+.#{$fa-css-prefix}-commenting:before { content: $fa-var-commenting; }
+.#{$fa-css-prefix}-commenting-o:before { content: $fa-var-commenting-o; }
+.#{$fa-css-prefix}-houzz:before { content: $fa-var-houzz; }
+.#{$fa-css-prefix}-vimeo:before { content: $fa-var-vimeo; }
+.#{$fa-css-prefix}-black-tie:before { content: $fa-var-black-tie; }
+.#{$fa-css-prefix}-fonticons:before { content: $fa-var-fonticons; }
+.#{$fa-css-prefix}-reddit-alien:before { content: $fa-var-reddit-alien; }
+.#{$fa-css-prefix}-edge:before { content: $fa-var-edge; }
+.#{$fa-css-prefix}-credit-card-alt:before { content: $fa-var-credit-card-alt; }
+.#{$fa-css-prefix}-codiepie:before { content: $fa-var-codiepie; }
+.#{$fa-css-prefix}-modx:before { content: $fa-var-modx; }
+.#{$fa-css-prefix}-fort-awesome:before { content: $fa-var-fort-awesome; }
+.#{$fa-css-prefix}-usb:before { content: $fa-var-usb; }
+.#{$fa-css-prefix}-product-hunt:before { content: $fa-var-product-hunt; }
+.#{$fa-css-prefix}-mixcloud:before { content: $fa-var-mixcloud; }
+.#{$fa-css-prefix}-scribd:before { content: $fa-var-scribd; }
+.#{$fa-css-prefix}-pause-circle:before { content: $fa-var-pause-circle; }
+.#{$fa-css-prefix}-pause-circle-o:before { content: $fa-var-pause-circle-o; }
+.#{$fa-css-prefix}-stop-circle:before { content: $fa-var-stop-circle; }
+.#{$fa-css-prefix}-stop-circle-o:before { content: $fa-var-stop-circle-o; }
+.#{$fa-css-prefix}-shopping-bag:before { content: $fa-var-shopping-bag; }
+.#{$fa-css-prefix}-shopping-basket:before { content: $fa-var-shopping-basket; }
+.#{$fa-css-prefix}-hashtag:before { content: $fa-var-hashtag; }
+.#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; }
+.#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; }
+.#{$fa-css-prefix}-percent:before { content: $fa-var-percent; }
+.#{$fa-css-prefix}-gitlab:before { content: $fa-var-gitlab; }
+.#{$fa-css-prefix}-wpbeginner:before { content: $fa-var-wpbeginner; }
+.#{$fa-css-prefix}-wpforms:before { content: $fa-var-wpforms; }
+.#{$fa-css-prefix}-envira:before { content: $fa-var-envira; }
+.#{$fa-css-prefix}-universal-access:before { content: $fa-var-universal-access; }
+.#{$fa-css-prefix}-wheelchair-alt:before { content: $fa-var-wheelchair-alt; }
+.#{$fa-css-prefix}-question-circle-o:before { content: $fa-var-question-circle-o; }
+.#{$fa-css-prefix}-blind:before { content: $fa-var-blind; }
+.#{$fa-css-prefix}-audio-description:before { content: $fa-var-audio-description; }
+.#{$fa-css-prefix}-volume-control-phone:before { content: $fa-var-volume-control-phone; }
+.#{$fa-css-prefix}-braille:before { content: $fa-var-braille; }
+.#{$fa-css-prefix}-assistive-listening-systems:before { content: $fa-var-assistive-listening-systems; }
+.#{$fa-css-prefix}-asl-interpreting:before,
+.#{$fa-css-prefix}-american-sign-language-interpreting:before { content: $fa-var-american-sign-language-interpreting; }
+.#{$fa-css-prefix}-deafness:before,
+.#{$fa-css-prefix}-hard-of-hearing:before,
+.#{$fa-css-prefix}-deaf:before { content: $fa-var-deaf; }
+.#{$fa-css-prefix}-glide:before { content: $fa-var-glide; }
+.#{$fa-css-prefix}-glide-g:before { content: $fa-var-glide-g; }
+.#{$fa-css-prefix}-signing:before,
+.#{$fa-css-prefix}-sign-language:before { content: $fa-var-sign-language; }
+.#{$fa-css-prefix}-low-vision:before { content: $fa-var-low-vision; }
+.#{$fa-css-prefix}-viadeo:before { content: $fa-var-viadeo; }
+.#{$fa-css-prefix}-viadeo-square:before { content: $fa-var-viadeo-square; }
+.#{$fa-css-prefix}-snapchat:before { content: $fa-var-snapchat; }
+.#{$fa-css-prefix}-snapchat-ghost:before { content: $fa-var-snapchat-ghost; }
+.#{$fa-css-prefix}-snapchat-square:before { content: $fa-var-snapchat-square; }
+.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; }
+.#{$fa-css-prefix}-first-order:before { content: $fa-var-first-order; }
+.#{$fa-css-prefix}-yoast:before { content: $fa-var-yoast; }
+.#{$fa-css-prefix}-themeisle:before { content: $fa-var-themeisle; }
+.#{$fa-css-prefix}-google-plus-circle:before,
+.#{$fa-css-prefix}-google-plus-official:before { content: $fa-var-google-plus-official; }
+.#{$fa-css-prefix}-fa:before,
+.#{$fa-css-prefix}-font-awesome:before { content: $fa-var-font-awesome; }
+.#{$fa-css-prefix}-handshake-o:before { content: $fa-var-handshake-o; }
+.#{$fa-css-prefix}-envelope-open:before { content: $fa-var-envelope-open; }
+.#{$fa-css-prefix}-envelope-open-o:before { content: $fa-var-envelope-open-o; }
+.#{$fa-css-prefix}-linode:before { content: $fa-var-linode; }
+.#{$fa-css-prefix}-address-book:before { content: $fa-var-address-book; }
+.#{$fa-css-prefix}-address-book-o:before { content: $fa-var-address-book-o; }
+.#{$fa-css-prefix}-vcard:before,
+.#{$fa-css-prefix}-address-card:before { content: $fa-var-address-card; }
+.#{$fa-css-prefix}-vcard-o:before,
+.#{$fa-css-prefix}-address-card-o:before { content: $fa-var-address-card-o; }
+.#{$fa-css-prefix}-user-circle:before { content: $fa-var-user-circle; }
+.#{$fa-css-prefix}-user-circle-o:before { content: $fa-var-user-circle-o; }
+.#{$fa-css-prefix}-user-o:before { content: $fa-var-user-o; }
+.#{$fa-css-prefix}-id-badge:before { content: $fa-var-id-badge; }
+.#{$fa-css-prefix}-drivers-license:before,
+.#{$fa-css-prefix}-id-card:before { content: $fa-var-id-card; }
+.#{$fa-css-prefix}-drivers-license-o:before,
+.#{$fa-css-prefix}-id-card-o:before { content: $fa-var-id-card-o; }
+.#{$fa-css-prefix}-quora:before { content: $fa-var-quora; }
+.#{$fa-css-prefix}-free-code-camp:before { content: $fa-var-free-code-camp; }
+.#{$fa-css-prefix}-telegram:before { content: $fa-var-telegram; }
+.#{$fa-css-prefix}-thermometer-4:before,
+.#{$fa-css-prefix}-thermometer:before,
+.#{$fa-css-prefix}-thermometer-full:before { content: $fa-var-thermometer-full; }
+.#{$fa-css-prefix}-thermometer-3:before,
+.#{$fa-css-prefix}-thermometer-three-quarters:before { content: $fa-var-thermometer-three-quarters; }
+.#{$fa-css-prefix}-thermometer-2:before,
+.#{$fa-css-prefix}-thermometer-half:before { content: $fa-var-thermometer-half; }
+.#{$fa-css-prefix}-thermometer-1:before,
+.#{$fa-css-prefix}-thermometer-quarter:before { content: $fa-var-thermometer-quarter; }
+.#{$fa-css-prefix}-thermometer-0:before,
+.#{$fa-css-prefix}-thermometer-empty:before { content: $fa-var-thermometer-empty; }
+.#{$fa-css-prefix}-shower:before { content: $fa-var-shower; }
+.#{$fa-css-prefix}-bathtub:before,
+.#{$fa-css-prefix}-s15:before,
+.#{$fa-css-prefix}-bath:before { content: $fa-var-bath; }
+.#{$fa-css-prefix}-podcast:before { content: $fa-var-podcast; }
+.#{$fa-css-prefix}-window-maximize:before { content: $fa-var-window-maximize; }
+.#{$fa-css-prefix}-window-minimize:before { content: $fa-var-window-minimize; }
+.#{$fa-css-prefix}-window-restore:before { content: $fa-var-window-restore; }
+.#{$fa-css-prefix}-times-rectangle:before,
+.#{$fa-css-prefix}-window-close:before { content: $fa-var-window-close; }
+.#{$fa-css-prefix}-times-rectangle-o:before,
+.#{$fa-css-prefix}-window-close-o:before { content: $fa-var-window-close-o; }
+.#{$fa-css-prefix}-bandcamp:before { content: $fa-var-bandcamp; }
+.#{$fa-css-prefix}-grav:before { content: $fa-var-grav; }
+.#{$fa-css-prefix}-etsy:before { content: $fa-var-etsy; }
+.#{$fa-css-prefix}-imdb:before { content: $fa-var-imdb; }
+.#{$fa-css-prefix}-ravelry:before { content: $fa-var-ravelry; }
+.#{$fa-css-prefix}-eercast:before { content: $fa-var-eercast; }
+.#{$fa-css-prefix}-microchip:before { content: $fa-var-microchip; }
+.#{$fa-css-prefix}-snowflake-o:before { content: $fa-var-snowflake-o; }
+.#{$fa-css-prefix}-superpowers:before { content: $fa-var-superpowers; }
+.#{$fa-css-prefix}-wpexplorer:before { content: $fa-var-wpexplorer; }
+.#{$fa-css-prefix}-meetup:before { content: $fa-var-meetup; }
diff --git a/assets/font/fonts-awesome/scss/_mixins.scss b/assets/font/fonts-awesome/scss/_mixins.scss
index a139dfb..c3bbd57 100644
--- a/assets/font/fonts-awesome/scss/_mixins.scss
+++ b/assets/font/fonts-awesome/scss/_mixins.scss
@@ -3,23 +3,58 @@
@mixin fa-icon() {
display: inline-block;
- font: normal normal normal 14px/1 FontAwesome; // shortening font declaration
+ font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
font-size: inherit; // can't have font-size inherit on line above, so need to override
text-rendering: auto; // optimizelegibility throws things off #1094
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+
}
@mixin fa-icon-rotate($degrees, $rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})";
-webkit-transform: rotate($degrees);
-ms-transform: rotate($degrees);
transform: rotate($degrees);
}
@mixin fa-icon-flip($horiz, $vert, $rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
+ -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)";
-webkit-transform: scale($horiz, $vert);
-ms-transform: scale($horiz, $vert);
transform: scale($horiz, $vert);
}
+
+
+// Only display content to screen readers. A la Bootstrap 4.
+//
+// See: http://a11yproject.com/posts/how-to-hide-content/
+
+@mixin sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0,0,0,0);
+ border: 0;
+}
+
+// Use in conjunction with .sr-only to only display content when it's focused.
+//
+// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
+//
+// Credit: HTML5 Boilerplate
+
+@mixin sr-only-focusable {
+ &:active,
+ &:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
+ }
+}
diff --git a/assets/font/fonts-awesome/scss/_path.scss b/assets/font/fonts-awesome/scss/_path.scss
index fd21c35..bb457c2 100644
--- a/assets/font/fonts-awesome/scss/_path.scss
+++ b/assets/font/fonts-awesome/scss/_path.scss
@@ -5,10 +5,11 @@
font-family: 'FontAwesome';
src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
+ url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'),
url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
- //src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
+// src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
font-weight: normal;
font-style: normal;
}
diff --git a/assets/font/fonts-awesome/scss/_screen-reader.scss b/assets/font/fonts-awesome/scss/_screen-reader.scss
new file mode 100644
index 0000000..637426f
--- /dev/null
+++ b/assets/font/fonts-awesome/scss/_screen-reader.scss
@@ -0,0 +1,5 @@
+// Screen Readers
+// -------------------------
+
+.sr-only { @include sr-only(); }
+.sr-only-focusable { @include sr-only-focusable(); }
diff --git a/assets/font/fonts-awesome/scss/_variables.scss b/assets/font/fonts-awesome/scss/_variables.scss
index 669c307..498fc4a 100644
--- a/assets/font/fonts-awesome/scss/_variables.scss
+++ b/assets/font/fonts-awesome/scss/_variables.scss
@@ -2,20 +2,29 @@
// --------------------------
$fa-font-path: "../fonts" !default;
-//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.2.0/fonts" !default; // for referencing Bootstrap CDN font files directly
+$fa-font-size-base: 14px !default;
+$fa-line-height-base: 1 !default;
+//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.7.0/fonts" !default; // for referencing Bootstrap CDN font files directly
$fa-css-prefix: fa !default;
-$fa-version: "4.2.0" !default;
+$fa-version: "4.7.0" !default;
$fa-border-color: #eee !default;
$fa-inverse: #fff !default;
$fa-li-width: (30em / 14) !default;
+$fa-var-500px: "\f26e";
+$fa-var-address-book: "\f2b9";
+$fa-var-address-book-o: "\f2ba";
+$fa-var-address-card: "\f2bb";
+$fa-var-address-card-o: "\f2bc";
$fa-var-adjust: "\f042";
$fa-var-adn: "\f170";
$fa-var-align-center: "\f037";
$fa-var-align-justify: "\f039";
$fa-var-align-left: "\f036";
$fa-var-align-right: "\f038";
+$fa-var-amazon: "\f270";
$fa-var-ambulance: "\f0f9";
+$fa-var-american-sign-language-interpreting: "\f2a3";
$fa-var-anchor: "\f13d";
$fa-var-android: "\f17b";
$fa-var-angellist: "\f209";
@@ -46,16 +55,35 @@ $fa-var-arrows: "\f047";
$fa-var-arrows-alt: "\f0b2";
$fa-var-arrows-h: "\f07e";
$fa-var-arrows-v: "\f07d";
+$fa-var-asl-interpreting: "\f2a3";
+$fa-var-assistive-listening-systems: "\f2a2";
$fa-var-asterisk: "\f069";
$fa-var-at: "\f1fa";
+$fa-var-audio-description: "\f29e";
$fa-var-automobile: "\f1b9";
$fa-var-backward: "\f04a";
+$fa-var-balance-scale: "\f24e";
$fa-var-ban: "\f05e";
+$fa-var-bandcamp: "\f2d5";
$fa-var-bank: "\f19c";
$fa-var-bar-chart: "\f080";
$fa-var-bar-chart-o: "\f080";
$fa-var-barcode: "\f02a";
$fa-var-bars: "\f0c9";
+$fa-var-bath: "\f2cd";
+$fa-var-bathtub: "\f2cd";
+$fa-var-battery: "\f240";
+$fa-var-battery-0: "\f244";
+$fa-var-battery-1: "\f243";
+$fa-var-battery-2: "\f242";
+$fa-var-battery-3: "\f241";
+$fa-var-battery-4: "\f240";
+$fa-var-battery-empty: "\f244";
+$fa-var-battery-full: "\f240";
+$fa-var-battery-half: "\f242";
+$fa-var-battery-quarter: "\f243";
+$fa-var-battery-three-quarters: "\f241";
+$fa-var-bed: "\f236";
$fa-var-beer: "\f0fc";
$fa-var-behance: "\f1b4";
$fa-var-behance-square: "\f1b5";
@@ -69,12 +97,17 @@ $fa-var-birthday-cake: "\f1fd";
$fa-var-bitbucket: "\f171";
$fa-var-bitbucket-square: "\f172";
$fa-var-bitcoin: "\f15a";
+$fa-var-black-tie: "\f27e";
+$fa-var-blind: "\f29d";
+$fa-var-bluetooth: "\f293";
+$fa-var-bluetooth-b: "\f294";
$fa-var-bold: "\f032";
$fa-var-bolt: "\f0e7";
$fa-var-bomb: "\f1e2";
$fa-var-book: "\f02d";
$fa-var-bookmark: "\f02e";
$fa-var-bookmark-o: "\f097";
+$fa-var-braille: "\f2a1";
$fa-var-briefcase: "\f0b1";
$fa-var-btc: "\f15a";
$fa-var-bug: "\f188";
@@ -83,10 +116,15 @@ $fa-var-building-o: "\f0f7";
$fa-var-bullhorn: "\f0a1";
$fa-var-bullseye: "\f140";
$fa-var-bus: "\f207";
+$fa-var-buysellads: "\f20d";
$fa-var-cab: "\f1ba";
$fa-var-calculator: "\f1ec";
$fa-var-calendar: "\f073";
+$fa-var-calendar-check-o: "\f274";
+$fa-var-calendar-minus-o: "\f272";
$fa-var-calendar-o: "\f133";
+$fa-var-calendar-plus-o: "\f271";
+$fa-var-calendar-times-o: "\f273";
$fa-var-camera: "\f030";
$fa-var-camera-retro: "\f083";
$fa-var-car: "\f1b9";
@@ -98,9 +136,13 @@ $fa-var-caret-square-o-left: "\f191";
$fa-var-caret-square-o-right: "\f152";
$fa-var-caret-square-o-up: "\f151";
$fa-var-caret-up: "\f0d8";
+$fa-var-cart-arrow-down: "\f218";
+$fa-var-cart-plus: "\f217";
$fa-var-cc: "\f20a";
$fa-var-cc-amex: "\f1f3";
+$fa-var-cc-diners-club: "\f24c";
$fa-var-cc-discover: "\f1f2";
+$fa-var-cc-jcb: "\f24b";
$fa-var-cc-mastercard: "\f1f1";
$fa-var-cc-paypal: "\f1f4";
$fa-var-cc-stripe: "\f1f5";
@@ -122,12 +164,14 @@ $fa-var-chevron-left: "\f053";
$fa-var-chevron-right: "\f054";
$fa-var-chevron-up: "\f077";
$fa-var-child: "\f1ae";
+$fa-var-chrome: "\f268";
$fa-var-circle: "\f111";
$fa-var-circle-o: "\f10c";
$fa-var-circle-o-notch: "\f1ce";
$fa-var-circle-thin: "\f1db";
$fa-var-clipboard: "\f0ea";
$fa-var-clock-o: "\f017";
+$fa-var-clone: "\f24d";
$fa-var-close: "\f00d";
$fa-var-cloud: "\f0c2";
$fa-var-cloud-download: "\f0ed";
@@ -136,19 +180,26 @@ $fa-var-cny: "\f157";
$fa-var-code: "\f121";
$fa-var-code-fork: "\f126";
$fa-var-codepen: "\f1cb";
+$fa-var-codiepie: "\f284";
$fa-var-coffee: "\f0f4";
$fa-var-cog: "\f013";
$fa-var-cogs: "\f085";
$fa-var-columns: "\f0db";
$fa-var-comment: "\f075";
$fa-var-comment-o: "\f0e5";
+$fa-var-commenting: "\f27a";
+$fa-var-commenting-o: "\f27b";
$fa-var-comments: "\f086";
$fa-var-comments-o: "\f0e6";
$fa-var-compass: "\f14e";
$fa-var-compress: "\f066";
+$fa-var-connectdevelop: "\f20e";
+$fa-var-contao: "\f26d";
$fa-var-copy: "\f0c5";
$fa-var-copyright: "\f1f9";
+$fa-var-creative-commons: "\f25e";
$fa-var-credit-card: "\f09d";
+$fa-var-credit-card-alt: "\f283";
$fa-var-crop: "\f125";
$fa-var-crosshairs: "\f05b";
$fa-var-css3: "\f13c";
@@ -157,27 +208,39 @@ $fa-var-cubes: "\f1b3";
$fa-var-cut: "\f0c4";
$fa-var-cutlery: "\f0f5";
$fa-var-dashboard: "\f0e4";
+$fa-var-dashcube: "\f210";
$fa-var-database: "\f1c0";
+$fa-var-deaf: "\f2a4";
+$fa-var-deafness: "\f2a4";
$fa-var-dedent: "\f03b";
$fa-var-delicious: "\f1a5";
$fa-var-desktop: "\f108";
$fa-var-deviantart: "\f1bd";
+$fa-var-diamond: "\f219";
$fa-var-digg: "\f1a6";
$fa-var-dollar: "\f155";
$fa-var-dot-circle-o: "\f192";
$fa-var-download: "\f019";
$fa-var-dribbble: "\f17d";
+$fa-var-drivers-license: "\f2c2";
+$fa-var-drivers-license-o: "\f2c3";
$fa-var-dropbox: "\f16b";
$fa-var-drupal: "\f1a9";
+$fa-var-edge: "\f282";
$fa-var-edit: "\f044";
+$fa-var-eercast: "\f2da";
$fa-var-eject: "\f052";
$fa-var-ellipsis-h: "\f141";
$fa-var-ellipsis-v: "\f142";
$fa-var-empire: "\f1d1";
$fa-var-envelope: "\f0e0";
$fa-var-envelope-o: "\f003";
+$fa-var-envelope-open: "\f2b6";
+$fa-var-envelope-open-o: "\f2b7";
$fa-var-envelope-square: "\f199";
+$fa-var-envira: "\f299";
$fa-var-eraser: "\f12d";
+$fa-var-etsy: "\f2d7";
$fa-var-eur: "\f153";
$fa-var-euro: "\f153";
$fa-var-exchange: "\f0ec";
@@ -185,16 +248,21 @@ $fa-var-exclamation: "\f12a";
$fa-var-exclamation-circle: "\f06a";
$fa-var-exclamation-triangle: "\f071";
$fa-var-expand: "\f065";
+$fa-var-expeditedssl: "\f23e";
$fa-var-external-link: "\f08e";
$fa-var-external-link-square: "\f14c";
$fa-var-eye: "\f06e";
$fa-var-eye-slash: "\f070";
$fa-var-eyedropper: "\f1fb";
+$fa-var-fa: "\f2b4";
$fa-var-facebook: "\f09a";
+$fa-var-facebook-f: "\f09a";
+$fa-var-facebook-official: "\f230";
$fa-var-facebook-square: "\f082";
$fa-var-fast-backward: "\f049";
$fa-var-fast-forward: "\f050";
$fa-var-fax: "\f1ac";
+$fa-var-feed: "\f09e";
$fa-var-female: "\f182";
$fa-var-fighter-jet: "\f0fb";
$fa-var-file: "\f15b";
@@ -220,6 +288,8 @@ $fa-var-film: "\f008";
$fa-var-filter: "\f0b0";
$fa-var-fire: "\f06d";
$fa-var-fire-extinguisher: "\f134";
+$fa-var-firefox: "\f269";
+$fa-var-first-order: "\f2b0";
$fa-var-flag: "\f024";
$fa-var-flag-checkered: "\f11e";
$fa-var-flag-o: "\f11d";
@@ -232,8 +302,13 @@ $fa-var-folder-o: "\f114";
$fa-var-folder-open: "\f07c";
$fa-var-folder-open-o: "\f115";
$fa-var-font: "\f031";
+$fa-var-font-awesome: "\f2b4";
+$fa-var-fonticons: "\f280";
+$fa-var-fort-awesome: "\f286";
+$fa-var-forumbee: "\f211";
$fa-var-forward: "\f04e";
$fa-var-foursquare: "\f180";
+$fa-var-free-code-camp: "\f2c5";
$fa-var-frown-o: "\f119";
$fa-var-futbol-o: "\f1e3";
$fa-var-gamepad: "\f11b";
@@ -242,45 +317,87 @@ $fa-var-gbp: "\f154";
$fa-var-ge: "\f1d1";
$fa-var-gear: "\f013";
$fa-var-gears: "\f085";
+$fa-var-genderless: "\f22d";
+$fa-var-get-pocket: "\f265";
+$fa-var-gg: "\f260";
+$fa-var-gg-circle: "\f261";
$fa-var-gift: "\f06b";
$fa-var-git: "\f1d3";
$fa-var-git-square: "\f1d2";
$fa-var-github: "\f09b";
$fa-var-github-alt: "\f113";
$fa-var-github-square: "\f092";
+$fa-var-gitlab: "\f296";
$fa-var-gittip: "\f184";
$fa-var-glass: "\f000";
+$fa-var-glide: "\f2a5";
+$fa-var-glide-g: "\f2a6";
$fa-var-globe: "\f0ac";
$fa-var-google: "\f1a0";
$fa-var-google-plus: "\f0d5";
+$fa-var-google-plus-circle: "\f2b3";
+$fa-var-google-plus-official: "\f2b3";
$fa-var-google-plus-square: "\f0d4";
$fa-var-google-wallet: "\f1ee";
$fa-var-graduation-cap: "\f19d";
+$fa-var-gratipay: "\f184";
+$fa-var-grav: "\f2d6";
$fa-var-group: "\f0c0";
$fa-var-h-square: "\f0fd";
$fa-var-hacker-news: "\f1d4";
+$fa-var-hand-grab-o: "\f255";
+$fa-var-hand-lizard-o: "\f258";
$fa-var-hand-o-down: "\f0a7";
$fa-var-hand-o-left: "\f0a5";
$fa-var-hand-o-right: "\f0a4";
$fa-var-hand-o-up: "\f0a6";
+$fa-var-hand-paper-o: "\f256";
+$fa-var-hand-peace-o: "\f25b";
+$fa-var-hand-pointer-o: "\f25a";
+$fa-var-hand-rock-o: "\f255";
+$fa-var-hand-scissors-o: "\f257";
+$fa-var-hand-spock-o: "\f259";
+$fa-var-hand-stop-o: "\f256";
+$fa-var-handshake-o: "\f2b5";
+$fa-var-hard-of-hearing: "\f2a4";
+$fa-var-hashtag: "\f292";
$fa-var-hdd-o: "\f0a0";
$fa-var-header: "\f1dc";
$fa-var-headphones: "\f025";
$fa-var-heart: "\f004";
$fa-var-heart-o: "\f08a";
+$fa-var-heartbeat: "\f21e";
$fa-var-history: "\f1da";
$fa-var-home: "\f015";
$fa-var-hospital-o: "\f0f8";
+$fa-var-hotel: "\f236";
+$fa-var-hourglass: "\f254";
+$fa-var-hourglass-1: "\f251";
+$fa-var-hourglass-2: "\f252";
+$fa-var-hourglass-3: "\f253";
+$fa-var-hourglass-end: "\f253";
+$fa-var-hourglass-half: "\f252";
+$fa-var-hourglass-o: "\f250";
+$fa-var-hourglass-start: "\f251";
+$fa-var-houzz: "\f27c";
$fa-var-html5: "\f13b";
+$fa-var-i-cursor: "\f246";
+$fa-var-id-badge: "\f2c1";
+$fa-var-id-card: "\f2c2";
+$fa-var-id-card-o: "\f2c3";
$fa-var-ils: "\f20b";
$fa-var-image: "\f03e";
+$fa-var-imdb: "\f2d8";
$fa-var-inbox: "\f01c";
$fa-var-indent: "\f03c";
+$fa-var-industry: "\f275";
$fa-var-info: "\f129";
$fa-var-info-circle: "\f05a";
$fa-var-inr: "\f156";
$fa-var-instagram: "\f16d";
$fa-var-institution: "\f19c";
+$fa-var-internet-explorer: "\f26b";
+$fa-var-intersex: "\f224";
$fa-var-ioxhost: "\f208";
$fa-var-italic: "\f033";
$fa-var-joomla: "\f1aa";
@@ -294,6 +411,7 @@ $fa-var-laptop: "\f109";
$fa-var-lastfm: "\f202";
$fa-var-lastfm-square: "\f203";
$fa-var-leaf: "\f06c";
+$fa-var-leanpub: "\f212";
$fa-var-legal: "\f0e3";
$fa-var-lemon-o: "\f094";
$fa-var-level-down: "\f149";
@@ -307,6 +425,7 @@ $fa-var-line-chart: "\f201";
$fa-var-link: "\f0c1";
$fa-var-linkedin: "\f0e1";
$fa-var-linkedin-square: "\f08c";
+$fa-var-linode: "\f2b8";
$fa-var-linux: "\f17c";
$fa-var-list: "\f03a";
$fa-var-list-alt: "\f022";
@@ -318,32 +437,58 @@ $fa-var-long-arrow-down: "\f175";
$fa-var-long-arrow-left: "\f177";
$fa-var-long-arrow-right: "\f178";
$fa-var-long-arrow-up: "\f176";
+$fa-var-low-vision: "\f2a8";
$fa-var-magic: "\f0d0";
$fa-var-magnet: "\f076";
$fa-var-mail-forward: "\f064";
$fa-var-mail-reply: "\f112";
$fa-var-mail-reply-all: "\f122";
$fa-var-male: "\f183";
+$fa-var-map: "\f279";
$fa-var-map-marker: "\f041";
+$fa-var-map-o: "\f278";
+$fa-var-map-pin: "\f276";
+$fa-var-map-signs: "\f277";
+$fa-var-mars: "\f222";
+$fa-var-mars-double: "\f227";
+$fa-var-mars-stroke: "\f229";
+$fa-var-mars-stroke-h: "\f22b";
+$fa-var-mars-stroke-v: "\f22a";
$fa-var-maxcdn: "\f136";
$fa-var-meanpath: "\f20c";
+$fa-var-medium: "\f23a";
$fa-var-medkit: "\f0fa";
+$fa-var-meetup: "\f2e0";
$fa-var-meh-o: "\f11a";
+$fa-var-mercury: "\f223";
+$fa-var-microchip: "\f2db";
$fa-var-microphone: "\f130";
$fa-var-microphone-slash: "\f131";
$fa-var-minus: "\f068";
$fa-var-minus-circle: "\f056";
$fa-var-minus-square: "\f146";
$fa-var-minus-square-o: "\f147";
+$fa-var-mixcloud: "\f289";
$fa-var-mobile: "\f10b";
$fa-var-mobile-phone: "\f10b";
+$fa-var-modx: "\f285";
$fa-var-money: "\f0d6";
$fa-var-moon-o: "\f186";
$fa-var-mortar-board: "\f19d";
+$fa-var-motorcycle: "\f21c";
+$fa-var-mouse-pointer: "\f245";
$fa-var-music: "\f001";
$fa-var-navicon: "\f0c9";
+$fa-var-neuter: "\f22c";
$fa-var-newspaper-o: "\f1ea";
+$fa-var-object-group: "\f247";
+$fa-var-object-ungroup: "\f248";
+$fa-var-odnoklassniki: "\f263";
+$fa-var-odnoklassniki-square: "\f264";
+$fa-var-opencart: "\f23d";
$fa-var-openid: "\f19b";
+$fa-var-opera: "\f26a";
+$fa-var-optin-monster: "\f23c";
$fa-var-outdent: "\f03b";
$fa-var-pagelines: "\f18c";
$fa-var-paint-brush: "\f1fc";
@@ -353,19 +498,24 @@ $fa-var-paperclip: "\f0c6";
$fa-var-paragraph: "\f1dd";
$fa-var-paste: "\f0ea";
$fa-var-pause: "\f04c";
+$fa-var-pause-circle: "\f28b";
+$fa-var-pause-circle-o: "\f28c";
$fa-var-paw: "\f1b0";
$fa-var-paypal: "\f1ed";
$fa-var-pencil: "\f040";
$fa-var-pencil-square: "\f14b";
$fa-var-pencil-square-o: "\f044";
+$fa-var-percent: "\f295";
$fa-var-phone: "\f095";
$fa-var-phone-square: "\f098";
$fa-var-photo: "\f03e";
$fa-var-picture-o: "\f03e";
$fa-var-pie-chart: "\f200";
-$fa-var-pied-piper: "\f1a7";
+$fa-var-pied-piper: "\f2ae";
$fa-var-pied-piper-alt: "\f1a8";
+$fa-var-pied-piper-pp: "\f1a7";
$fa-var-pinterest: "\f0d2";
+$fa-var-pinterest-p: "\f231";
$fa-var-pinterest-square: "\f0d3";
$fa-var-plane: "\f072";
$fa-var-play: "\f04b";
@@ -376,28 +526,36 @@ $fa-var-plus: "\f067";
$fa-var-plus-circle: "\f055";
$fa-var-plus-square: "\f0fe";
$fa-var-plus-square-o: "\f196";
+$fa-var-podcast: "\f2ce";
$fa-var-power-off: "\f011";
$fa-var-print: "\f02f";
+$fa-var-product-hunt: "\f288";
$fa-var-puzzle-piece: "\f12e";
$fa-var-qq: "\f1d6";
$fa-var-qrcode: "\f029";
$fa-var-question: "\f128";
$fa-var-question-circle: "\f059";
+$fa-var-question-circle-o: "\f29c";
+$fa-var-quora: "\f2c4";
$fa-var-quote-left: "\f10d";
$fa-var-quote-right: "\f10e";
$fa-var-ra: "\f1d0";
$fa-var-random: "\f074";
+$fa-var-ravelry: "\f2d9";
$fa-var-rebel: "\f1d0";
$fa-var-recycle: "\f1b8";
$fa-var-reddit: "\f1a1";
+$fa-var-reddit-alien: "\f281";
$fa-var-reddit-square: "\f1a2";
$fa-var-refresh: "\f021";
+$fa-var-registered: "\f25d";
$fa-var-remove: "\f00d";
$fa-var-renren: "\f18b";
$fa-var-reorder: "\f0c9";
$fa-var-repeat: "\f01e";
$fa-var-reply: "\f112";
$fa-var-reply-all: "\f122";
+$fa-var-resistance: "\f1d0";
$fa-var-retweet: "\f079";
$fa-var-rmb: "\f157";
$fa-var-road: "\f018";
@@ -410,13 +568,18 @@ $fa-var-rss-square: "\f143";
$fa-var-rub: "\f158";
$fa-var-ruble: "\f158";
$fa-var-rupee: "\f156";
+$fa-var-s15: "\f2cd";
+$fa-var-safari: "\f267";
$fa-var-save: "\f0c7";
$fa-var-scissors: "\f0c4";
+$fa-var-scribd: "\f28a";
$fa-var-search: "\f002";
$fa-var-search-minus: "\f010";
$fa-var-search-plus: "\f00e";
+$fa-var-sellsy: "\f213";
$fa-var-send: "\f1d8";
$fa-var-send-o: "\f1d9";
+$fa-var-server: "\f233";
$fa-var-share: "\f064";
$fa-var-share-alt: "\f1e0";
$fa-var-share-alt-square: "\f1e1";
@@ -425,16 +588,29 @@ $fa-var-share-square-o: "\f045";
$fa-var-shekel: "\f20b";
$fa-var-sheqel: "\f20b";
$fa-var-shield: "\f132";
+$fa-var-ship: "\f21a";
+$fa-var-shirtsinbulk: "\f214";
+$fa-var-shopping-bag: "\f290";
+$fa-var-shopping-basket: "\f291";
$fa-var-shopping-cart: "\f07a";
+$fa-var-shower: "\f2cc";
$fa-var-sign-in: "\f090";
+$fa-var-sign-language: "\f2a7";
$fa-var-sign-out: "\f08b";
$fa-var-signal: "\f012";
+$fa-var-signing: "\f2a7";
+$fa-var-simplybuilt: "\f215";
$fa-var-sitemap: "\f0e8";
+$fa-var-skyatlas: "\f216";
$fa-var-skype: "\f17e";
$fa-var-slack: "\f198";
$fa-var-sliders: "\f1de";
$fa-var-slideshare: "\f1e7";
$fa-var-smile-o: "\f118";
+$fa-var-snapchat: "\f2ab";
+$fa-var-snapchat-ghost: "\f2ac";
+$fa-var-snapchat-square: "\f2ad";
+$fa-var-snowflake-o: "\f2dc";
$fa-var-soccer-ball-o: "\f1e3";
$fa-var-sort: "\f0dc";
$fa-var-sort-alpha-asc: "\f15d";
@@ -467,13 +643,20 @@ $fa-var-steam-square: "\f1b7";
$fa-var-step-backward: "\f048";
$fa-var-step-forward: "\f051";
$fa-var-stethoscope: "\f0f1";
+$fa-var-sticky-note: "\f249";
+$fa-var-sticky-note-o: "\f24a";
$fa-var-stop: "\f04d";
+$fa-var-stop-circle: "\f28d";
+$fa-var-stop-circle-o: "\f28e";
+$fa-var-street-view: "\f21d";
$fa-var-strikethrough: "\f0cc";
$fa-var-stumbleupon: "\f1a4";
$fa-var-stumbleupon-circle: "\f1a3";
$fa-var-subscript: "\f12c";
+$fa-var-subway: "\f239";
$fa-var-suitcase: "\f0f2";
$fa-var-sun-o: "\f185";
+$fa-var-superpowers: "\f2dd";
$fa-var-superscript: "\f12b";
$fa-var-support: "\f1cd";
$fa-var-table: "\f0ce";
@@ -483,6 +666,8 @@ $fa-var-tag: "\f02b";
$fa-var-tags: "\f02c";
$fa-var-tasks: "\f0ae";
$fa-var-taxi: "\f1ba";
+$fa-var-telegram: "\f2c6";
+$fa-var-television: "\f26c";
$fa-var-tencent-weibo: "\f1d5";
$fa-var-terminal: "\f120";
$fa-var-text-height: "\f034";
@@ -490,6 +675,18 @@ $fa-var-text-width: "\f035";
$fa-var-th: "\f00a";
$fa-var-th-large: "\f009";
$fa-var-th-list: "\f00b";
+$fa-var-themeisle: "\f2b2";
+$fa-var-thermometer: "\f2c7";
+$fa-var-thermometer-0: "\f2cb";
+$fa-var-thermometer-1: "\f2ca";
+$fa-var-thermometer-2: "\f2c9";
+$fa-var-thermometer-3: "\f2c8";
+$fa-var-thermometer-4: "\f2c7";
+$fa-var-thermometer-empty: "\f2cb";
+$fa-var-thermometer-full: "\f2c7";
+$fa-var-thermometer-half: "\f2c9";
+$fa-var-thermometer-quarter: "\f2ca";
+$fa-var-thermometer-three-quarters: "\f2c8";
$fa-var-thumb-tack: "\f08d";
$fa-var-thumbs-down: "\f165";
$fa-var-thumbs-o-down: "\f088";
@@ -499,6 +696,8 @@ $fa-var-ticket: "\f145";
$fa-var-times: "\f00d";
$fa-var-times-circle: "\f057";
$fa-var-times-circle-o: "\f05c";
+$fa-var-times-rectangle: "\f2d3";
+$fa-var-times-rectangle-o: "\f2d4";
$fa-var-tint: "\f043";
$fa-var-toggle-down: "\f150";
$fa-var-toggle-left: "\f191";
@@ -506,10 +705,15 @@ $fa-var-toggle-off: "\f204";
$fa-var-toggle-on: "\f205";
$fa-var-toggle-right: "\f152";
$fa-var-toggle-up: "\f151";
+$fa-var-trademark: "\f25c";
+$fa-var-train: "\f238";
+$fa-var-transgender: "\f224";
+$fa-var-transgender-alt: "\f225";
$fa-var-trash: "\f1f8";
$fa-var-trash-o: "\f014";
$fa-var-tree: "\f1bb";
$fa-var-trello: "\f181";
+$fa-var-tripadvisor: "\f262";
$fa-var-trophy: "\f091";
$fa-var-truck: "\f0d1";
$fa-var-try: "\f195";
@@ -517,26 +721,45 @@ $fa-var-tty: "\f1e4";
$fa-var-tumblr: "\f173";
$fa-var-tumblr-square: "\f174";
$fa-var-turkish-lira: "\f195";
+$fa-var-tv: "\f26c";
$fa-var-twitch: "\f1e8";
$fa-var-twitter: "\f099";
$fa-var-twitter-square: "\f081";
$fa-var-umbrella: "\f0e9";
$fa-var-underline: "\f0cd";
$fa-var-undo: "\f0e2";
+$fa-var-universal-access: "\f29a";
$fa-var-university: "\f19c";
$fa-var-unlink: "\f127";
$fa-var-unlock: "\f09c";
$fa-var-unlock-alt: "\f13e";
$fa-var-unsorted: "\f0dc";
$fa-var-upload: "\f093";
+$fa-var-usb: "\f287";
$fa-var-usd: "\f155";
$fa-var-user: "\f007";
+$fa-var-user-circle: "\f2bd";
+$fa-var-user-circle-o: "\f2be";
$fa-var-user-md: "\f0f0";
+$fa-var-user-o: "\f2c0";
+$fa-var-user-plus: "\f234";
+$fa-var-user-secret: "\f21b";
+$fa-var-user-times: "\f235";
$fa-var-users: "\f0c0";
+$fa-var-vcard: "\f2bb";
+$fa-var-vcard-o: "\f2bc";
+$fa-var-venus: "\f221";
+$fa-var-venus-double: "\f226";
+$fa-var-venus-mars: "\f228";
+$fa-var-viacoin: "\f237";
+$fa-var-viadeo: "\f2a9";
+$fa-var-viadeo-square: "\f2aa";
$fa-var-video-camera: "\f03d";
+$fa-var-vimeo: "\f27d";
$fa-var-vimeo-square: "\f194";
$fa-var-vine: "\f1ca";
$fa-var-vk: "\f189";
+$fa-var-volume-control-phone: "\f2a0";
$fa-var-volume-down: "\f027";
$fa-var-volume-off: "\f026";
$fa-var-volume-up: "\f028";
@@ -544,17 +767,33 @@ $fa-var-warning: "\f071";
$fa-var-wechat: "\f1d7";
$fa-var-weibo: "\f18a";
$fa-var-weixin: "\f1d7";
+$fa-var-whatsapp: "\f232";
$fa-var-wheelchair: "\f193";
+$fa-var-wheelchair-alt: "\f29b";
$fa-var-wifi: "\f1eb";
+$fa-var-wikipedia-w: "\f266";
+$fa-var-window-close: "\f2d3";
+$fa-var-window-close-o: "\f2d4";
+$fa-var-window-maximize: "\f2d0";
+$fa-var-window-minimize: "\f2d1";
+$fa-var-window-restore: "\f2d2";
$fa-var-windows: "\f17a";
$fa-var-won: "\f159";
$fa-var-wordpress: "\f19a";
+$fa-var-wpbeginner: "\f297";
+$fa-var-wpexplorer: "\f2de";
+$fa-var-wpforms: "\f298";
$fa-var-wrench: "\f0ad";
$fa-var-xing: "\f168";
$fa-var-xing-square: "\f169";
+$fa-var-y-combinator: "\f23b";
+$fa-var-y-combinator-square: "\f1d4";
$fa-var-yahoo: "\f19e";
+$fa-var-yc: "\f23b";
+$fa-var-yc-square: "\f1d4";
$fa-var-yelp: "\f1e9";
$fa-var-yen: "\f157";
+$fa-var-yoast: "\f2b1";
$fa-var-youtube: "\f167";
$fa-var-youtube-play: "\f16a";
$fa-var-youtube-square: "\f166";
diff --git a/assets/font/fonts-awesome/scss/font-awesome.scss b/assets/font/fonts-awesome/scss/font-awesome.scss
index f300c09..f1c83aa 100644
--- a/assets/font/fonts-awesome/scss/font-awesome.scss
+++ b/assets/font/fonts-awesome/scss/font-awesome.scss
@@ -1,5 +1,5 @@
/*!
- * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@@ -11,7 +11,8 @@
@import "fixed-width";
@import "list";
@import "bordered-pulled";
-@import "spinning";
+@import "animated";
@import "rotated-flipped";
@import "stacked";
@import "icons";
+@import "screen-reader";
diff --git a/controllers/internals/Media.php b/controllers/internals/Media.php
index 25fb988..c9d2642 100644
--- a/controllers/internals/Media.php
+++ b/controllers/internals/Media.php
@@ -48,7 +48,7 @@ class Media extends StandardController
if (!file_put_contents($new_file_path, 'a'))
{
- throw new \Exception('pute de merde');
+ throw new \Exception('Cannot write file ' . $new_file_path);
}
if (!rename($tmpfile_path, $new_file_path))
@@ -56,16 +56,6 @@ class Media extends StandardController
throw new \Exception('Cannot create file ' . $new_file_path);
}
- if (!chown($new_file_path, fileowner($user_path)))
- {
- throw new \Exception('Cannot give file ' . $new_file_path . ' to user : ' . fileowner($user_path));
- }
-
- if (!chgrp($new_file_path, filegroup($user_path)))
- {
- throw new \Exception('Cannot give file ' . $new_file_path . ' to group : ' . filegroup($user_path));
- }
-
if (!chmod($new_file_path, self::DEFAULT_CHMOD))
{
throw new \Exception('Cannot give file ' . $new_file_path . ' rights : ' . self::DEFAULT_CHMOD);
diff --git a/controllers/internals/Phone.php b/controllers/internals/Phone.php
index 9bdeac7..a2cebf5 100644
--- a/controllers/internals/Phone.php
+++ b/controllers/internals/Phone.php
@@ -236,6 +236,19 @@ namespace controllers\internals;
return true;
}
+ /**
+ * Update a phone status.
+ *
+ * @param int $id : Phone id
+ * @param string $status : The new status of the phone
+ *
+ * @return bool : false on error, true on success
+ */
+ public function update_status(int $id, string $status) : bool
+ {
+ return (bool) $this->get_model()->update($id, ['status' => $status]);
+ }
+
/**
* Get the model for the Controller.
*/
diff --git a/controllers/internals/PhoneGroup.php b/controllers/internals/PhoneGroup.php
new file mode 100644
index 0000000..5ce213e
--- /dev/null
+++ b/controllers/internals/PhoneGroup.php
@@ -0,0 +1,139 @@
+
+ *
+ * This source file is subject to the GPL-3.0 license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace controllers\internals;
+
+ /**
+ * Classe des groups.
+ */
+ class PhoneGroup extends StandardController
+ {
+ protected $model;
+
+ /**
+ * Create a new phone group for a user.
+ *
+ * @param int $id_user : user id
+ * @param stirng $name : Group name
+ * @param array $phones_ids : Ids of the phones of the group
+ *
+ * @return mixed bool|int : false on error, new group id
+ */
+ public function create(int $id_user, string $name, array $phones_ids)
+ {
+ $group = [
+ 'id_user' => $id_user,
+ 'name' => $name,
+ ];
+
+ $id_group = $this->get_model()->insert($group);
+ if (!$id_group)
+ {
+ return false;
+ }
+
+ $internal_phone = new Phone($this->bdd);
+ foreach ($phones_ids as $phone_id)
+ {
+ $phone = $internal_phone->get_for_user($id_user, $phone_id);
+ if (!$phone)
+ {
+ continue;
+ }
+
+ $this->get_model()->insert_phone_group_phone_relation($id_group, $phone_id);
+ }
+
+ $internal_event = new Event($this->bdd);
+ $internal_event->create($id_user, 'PHONE_GROUP_ADD', 'Ajout phone group : ' . $name);
+
+ return $id_group;
+ }
+
+ /**
+ * Update a phone group for a user.
+ *
+ * @param int $id_user : User id
+ * @param int $id_group : Group id
+ * @param stirng $name : Group name
+ * @param array $phones_ids : Ids of the phones of the group
+ *
+ * @return bool : False on error, true on success
+ */
+ public function update_for_user(int $id_user, int $id_group, string $name, array $phones_ids)
+ {
+ $group = [
+ 'name' => $name,
+ ];
+
+ $result = $this->get_model()->update_for_user($id_user, $id_group, $group);
+
+ $this->get_model()->delete_phone_group_phone_relations($id_group);
+
+ $internal_phone = new Phone($this->bdd);
+ $nb_phone_insert = 0;
+ foreach ($phones_ids as $phone_id)
+ {
+ $phone = $internal_phone->get_for_user($id_user, $phone_id);
+ if (!$phone)
+ {
+ continue;
+ }
+
+ if ($this->get_model()->insert_phone_group_phone_relation($id_group, $phone_id))
+ {
+ ++$nb_phone_insert;
+ }
+ }
+
+ if (!$result && $nb_phone_insert !== \count($phones_ids))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Return a group by his name for a user.
+ *
+ * @param int $id_user : User id
+ * @param string $name : Group name
+ *
+ * @return array
+ */
+ public function get_by_name_for_user(int $id_user, string $name)
+ {
+ return $this->get_model()->get_by_name_for_user($id_user, $name);
+ }
+
+ /**
+ * Get groups phones.
+ *
+ * @param int $id_group : Group id
+ *
+ * @return array : phones of the group
+ */
+ public function get_phones($id_group)
+ {
+ return $this->get_model()->get_phones($id_group);
+ }
+
+ /**
+ * Get the model for the Controller.
+ */
+ protected function get_model(): \models\PhoneGroup
+ {
+ $this->model = $this->model ?? new \models\PhoneGroup($this->bdd);
+
+ return $this->model;
+ }
+ }
diff --git a/controllers/internals/Scheduled.php b/controllers/internals/Scheduled.php
index 4f0c3f7..1b61a90 100644
--- a/controllers/internals/Scheduled.php
+++ b/controllers/internals/Scheduled.php
@@ -23,27 +23,31 @@ use Monolog\Logger;
*
* @param int $id_user : User to insert scheduled for
* @param $at : Scheduled date to send
- * @param string $text : Text of the message
- * @param ?int $id_phone : Id of the phone to send message with, null by default
- * @param bool $flash : Is the sms a flash sms, by default false
- * @param bool $mms : Is the sms a mms, by default false
- * @param array $numbers : Array of numbers to send message to, a number is an array ['number' => '+33XXX', 'data' => '{"key":"value", ...}']
- * @param array $contacts_ids : Contact ids to send message to
- * @param array $groups_ids : Group ids to send message to
- * @param array $conditional_group_ids : Conditional Groups ids to send message to
- * @param array $media_ids : Ids of the medias to link to scheduled message
+ * @param string $text : Text of the message
+ * @param ?int $id_phone : Id of the phone to send message with, null by default
+ * @param ?int $id_phone_group : Id of the phone group to send message with, null by default
+ * @param bool $flash : Is the sms a flash sms, by default false
+ * @param bool $mms : Is the sms a mms, by default false
+ * @param ?string $tag : A string tag to associate to sended SMS
+ * @param array $numbers : Array of numbers to send message to, a number is an array ['number' => '+33XXX', 'data' => '{"key":"value", ...}']
+ * @param array $contacts_ids : Contact ids to send message to
+ * @param array $groups_ids : Group ids to send message to
+ * @param array $conditional_group_ids : Conditional Groups ids to send message to
+ * @param array $media_ids : Ids of the medias to link to scheduled message
*
* @return bool : false on error, new id on success
*/
- public function create(int $id_user, $at, string $text, ?int $id_phone = null, bool $flash = false, bool $mms = false, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = [])
+ public function create(int $id_user, $at, string $text, ?int $id_phone = null, ?int $id_phone_group = null, bool $flash = false, bool $mms = false, ?string $tag = null, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = [])
{
$scheduled = [
'id_user' => $id_user,
'at' => $at,
'text' => $text,
'id_phone' => $id_phone,
+ 'id_phone_group' => $id_phone_group,
'flash' => $flash,
'mms' => $mms,
+ 'tag' => $tag,
];
if ('' === $text)
@@ -62,6 +66,17 @@ use Monolog\Logger;
}
}
+ if (null !== $id_phone_group)
+ {
+ $internal_phone_group = new PhoneGroup($this->bdd);
+ $find_phone_group = $internal_phone_group->get_for_user($id_user, $id_phone_group);
+
+ if (!$find_phone_group)
+ {
+ return false;
+ }
+ }
+
//Use transaction to garanty atomicity
$this->bdd->beginTransaction();
@@ -147,8 +162,10 @@ use Monolog\Logger;
* @param $at : Scheduled date to send
* @param string $text : Text of the message
* @param ?int $id_phone : Id of the phone to send message with, null by default
+ * @param ?int $id_phone_group : Id of the phone group to send message with, null by default
* @param bool $flash : Is the sms a flash sms, by default false
* @param bool $mms : Is the sms a mms, by default false
+ * @param ?string $tag : A string tag to associate to sended SMS
* @param array $numbers : Array of numbers to send message to, a number is an array ['number' => '+33XXX', 'data' => '{"key":"value", ...}']
* @param array $contacts_ids : Contact ids to send message to
* @param array $groups_ids : Group ids to send message to
@@ -157,15 +174,17 @@ use Monolog\Logger;
*
* @return bool : false on error, true on success
*/
- public function update_for_user(int $id_user, int $id_scheduled, $at, string $text, ?string $id_phone = null, bool $flash = false, bool $mms = false, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = [])
+ public function update_for_user(int $id_user, int $id_scheduled, $at, string $text, ?int $id_phone = null, ?int $id_phone_group = null, bool $flash = false, bool $mms = false, ?string $tag = null, array $numbers = [], array $contacts_ids = [], array $groups_ids = [], array $conditional_group_ids = [], array $media_ids = [])
{
$scheduled = [
'id_user' => $id_user,
'at' => $at,
'text' => $text,
'id_phone' => $id_phone,
+ 'id_phone_group' => $id_phone_group,
'mms' => $mms,
'flash' => $flash,
+ 'tag' => $tag,
];
if (null !== $id_phone)
@@ -179,6 +198,17 @@ use Monolog\Logger;
}
}
+ if (null !== $id_phone_group)
+ {
+ $internal_phone_group = new PhoneGroup($this->bdd);
+ $find_phone_group = $internal_phone_group->get_for_user($id_user, $id_phone_group);
+
+ if (!$find_phone_group)
+ {
+ return false;
+ }
+ }
+
//Ensure atomicity
$this->bdd->beginTransaction();
@@ -413,13 +443,14 @@ use Monolog\Logger;
$internal_group = new \controllers\internals\Group($this->bdd);
$internal_conditional_group = new \controllers\internals\ConditionalGroup($this->bdd);
$internal_phone = new \controllers\internals\Phone($this->bdd);
+ $internal_phone_group = new \controllers\internals\PhoneGroup($this->bdd);
$internal_smsstop = new \controllers\internals\SmsStop($this->bdd);
$internal_sended = new \controllers\internals\Sended($this->bdd);
$users_smsstops = [];
$users_settings = [];
$users_phones = [];
- $users_mms_phones = [];
+ $users_phone_groups = [];
$now = new \DateTime();
$now = $now->format('Y-m-d H:i:s');
@@ -457,7 +488,6 @@ use Monolog\Logger;
if (!isset($users_phones[$id_user]))
{
$users_phones[$id_user] = [];
- $users_mms_phones[$id_user] = [];
$phones = $internal_phone->gets_for_user($id_user);
foreach ($phones as &$phone)
@@ -475,25 +505,25 @@ use Monolog\Logger;
$phone['remaining_volume'] = $remaining_volume;
$users_phones[$id_user][$phone['id']] = $phone;
}
-
- $mms_phones = $internal_phone->gets_phone_supporting_mms_for_user($id_user, $internal_phone::MMS_SENDING);
- foreach ($mms_phones as &$mms_phone)
- {
- $limits = $internal_phone->get_limits($mms_phone['id']);
-
- $remaining_volume = PHP_INT_MAX;
- foreach ($limits as $limit)
- {
- $startpoint = new \DateTime($limit['startpoint']);
- $consumed = $internal_sended->count_since_for_phone_and_user($id_user, $mms_phone['id'], $startpoint);
- $remaining_volume = min(($limit['volume'] - $consumed), $remaining_volume);
- }
-
- $mms_phone['remaining_volume'] = $remaining_volume;
- $users_mms_phones[$id_user][$mms_phone['id']] = $mms_phone;
- }
}
+ if (!isset($users_phone_groups[$id_user]))
+ {
+ $users_phone_groups[$id_user] = [];
+
+ $phone_groups = $internal_phone_group->gets_for_user($id_user);
+ foreach ($phone_groups as $phone_group)
+ {
+ $phones = $internal_phone_group->get_phones($phone_group['id']);
+ $phone_group['phones'] = [];
+ foreach ($phones as $phone)
+ {
+ $phone_group['phones'][] = $phone['id'];
+ }
+
+ $users_phone_groups[$id_user][$phone_group['id']] = $phone_group;
+ }
+ }
//Add medias to mms
$scheduled['medias'] = [];
@@ -509,6 +539,12 @@ use Monolog\Logger;
$phone_to_use = $users_phones[$id_user][$scheduled['id_phone']] ?? null;
}
+ $phone_group_to_use = null;
+ if ($scheduled['id_phone_group'])
+ {
+ $phone_group_to_use = $users_phone_groups[$id_user][$scheduled['id_phone_group']] ?? null;
+ }
+
// We turn all contacts, groups and conditional groups into just contacts
$contacts = $this->get_contacts($id_scheduled);
@@ -620,36 +656,63 @@ use Monolog\Logger;
if (null === $phone_to_use)
{
$phones_subset = $users_phones[$id_user];
- if ($scheduled['mms'])
+
+ if ($phone_group_to_use)
{
- $phones_subset = $users_mms_phones[$id_user] ?: $phones_subset;
+ $phones_subset = array_filter($phones_subset, function ($phone) use ($phone_group_to_use) {
+ return in_array($phone['id'], $phone_group_to_use['phones']);
+ });
}
+ if ($scheduled['mms'])
+ {
+ $mms_only = array_filter($phones_subset, function ($phone) {
+ return $phone['adapter']::meta_support_mms_sending();
+ });
+
+ $phones_subset = $mms_only ?: $phones_subset;
+ }
+
+ // Keep only available phones
$remaining_volume_phones = array_filter($phones_subset, function ($phone) {
- return $phone['remaining_volume'] > 0;
+ return $phone['status'] == \models\Phone::STATUS_AVAILABLE;
});
$phones_subset = $remaining_volume_phones ?: $phones_subset;
- $max_priority_phones = [];
- $max_priority = PHP_INT_MIN;
- foreach ($phones_subset as $phone)
+
+ // Keep only phones with remaining volume
+ if ((int) ($users_settings[$id_user]['phone_limit'] ?? false))
{
- if ($phone['priority'] < $max_priority)
- {
- continue;
- }
- elseif ($phone['priority'] == $max_priority)
- {
- $max_priority_phones[] = $phone;
- }
- elseif ($phone['priority'] > $max_priority)
- {
- $max_priority_phones = [$phone];
- $max_priority = $phone['priority'];
- }
+ $remaining_volume_phones = array_filter($phones_subset, function ($phone) {
+ return $phone['remaining_volume'] > 0;
+ });
+ $phones_subset = $remaining_volume_phones ?: $phones_subset;
}
- $phones_subset = $max_priority_phones;
+ if ((int) ($users_settings[$id_user]['phone_priority'] ?? false))
+ {
+ $max_priority_phones = [];
+ $max_priority = PHP_INT_MIN;
+ foreach ($phones_subset as $phone)
+ {
+ if ($phone['priority'] < $max_priority)
+ {
+ continue;
+ }
+ elseif ($phone['priority'] == $max_priority)
+ {
+ $max_priority_phones[] = $phone;
+ }
+ elseif ($phone['priority'] > $max_priority)
+ {
+ $max_priority_phones = [$phone];
+ $max_priority = $phone['priority'];
+ }
+ }
+
+ $phones_subset = $max_priority_phones;
+ }
+
if ($phones_subset)
{
$random_phone = $phones_subset[array_rand($phones_subset)];
@@ -670,16 +733,13 @@ use Monolog\Logger;
'destination' => $target['number'],
'flash' => $scheduled['flash'],
'mms' => $scheduled['mms'],
+ 'tag' => $scheduled['tag'],
'medias' => $scheduled['medias'],
'text' => $text,
];
- // Consume one sms from remaining volume of phone, dont forget to do the same for the entry in mms phones
+ // Consume one sms from remaining volume of phone
$users_phones[$id_user][$id_phone]['remaining_volume'] --;
- if ($users_mms_phones[$id_user][$id_phone] ?? false)
- {
- $users_mms_phones[$id_user][$id_phone] --;
- }
}
}
diff --git a/controllers/internals/Sended.php b/controllers/internals/Sended.php
index 6a9276c..e58577c 100644
--- a/controllers/internals/Sended.php
+++ b/controllers/internals/Sended.php
@@ -11,6 +11,8 @@
namespace controllers\internals;
+use Exception;
+
class Sended extends StandardController
{
protected $model;
@@ -44,13 +46,14 @@ namespace controllers\internals;
* @param string $adapter : Name of the adapter service used to send the message
* @param bool $flash : Is the sms a flash
* @param bool $mms : Is the sms a MMS. By default false.
+ * @param ?string $tag : A string tag to associate to sended SMS
* @param array $medias : Array of medias to link to the MMS
* @param ?int $originating_scheduled : Id of the scheduled message that was responsible for sending this message. By default null.
* @param string $status : Status of a the sms. By default \models\Sended::STATUS_UNKNOWN
*
* @return mixed : false on error, new sended id else
*/
- public function create(int $id_user, int $id_phone, $at, string $text, string $destination, string $uid, string $adapter, bool $flash = false, bool $mms = false, array $medias = [], ?int $originating_scheduled = null, ?string $status = \models\Sended::STATUS_UNKNOWN)
+ public function create(int $id_user, int $id_phone, $at, string $text, string $destination, string $uid, string $adapter, bool $flash = false, bool $mms = false, ?string $tag = null, array $medias = [], ?int $originating_scheduled = null, ?string $status = \models\Sended::STATUS_UNKNOWN)
{
$sended = [
'id_user' => $id_user,
@@ -62,6 +65,7 @@ namespace controllers\internals;
'adapter' => $adapter,
'flash' => $flash,
'mms' => $mms,
+ 'tag' => $tag,
'status' => $status,
'originating_scheduled' => $originating_scheduled,
];
@@ -184,13 +188,15 @@ namespace controllers\internals;
*
* @param int $id_user : User id
* @param int $id_phone : Phone id we want the number of sended message for
- * @param \DateTime $since : Date since which we want sended number
+ * @param ?\DateTime $since : Date since which we want sended number. Default to null.
+ * @param ?\DateTime $before : Date up to which we want sended number. Default to null.
+ * @param ?string $tag_like : Tag to filter sms by, this is not a = but a LIKE operator
*
* @return int
*/
- public function count_since_for_phone_and_user(int $id_user, int $id_phone, \DateTime $since): int
+ public function count_since_for_phone_and_user(int $id_user, int $id_phone, ?\DateTime $since, ?\DateTime $before = null, ?string $tag_like = null): int
{
- return $this->get_model()->count_since_for_phone_and_user($id_user, $id_phone, $since);
+ return $this->get_model()->count_since_for_phone_and_user($id_user, $id_phone, $since, $before, $tag_like);
}
/**
@@ -236,6 +242,7 @@ namespace controllers\internals;
* @param $text : Text of the message
* @param string $destination : Number of the receiver
* @param bool $flash : Is the sms a flash. By default false.
+ * @param ?string $tag : A string tag to associate to sended SMS
* @param bool $mms : Is the sms a MMS. By default false.
* @param array $medias : Array of medias to link to the MMS
* @param string $status : Status of a the sms. By default \models\Sended::STATUS_UNKNOWN
@@ -246,13 +253,16 @@ namespace controllers\internals;
* ?string 'error_message' => null if success, error message else
* ]
*/
- public function send(\adapters\AdapterInterface $adapter, int $id_user, int $id_phone, string $text, string $destination, bool $flash = false, bool $mms = false, array $medias = [], $originating_scheduled = null, string $status = \models\Sended::STATUS_UNKNOWN): array
+ public function send(\adapters\AdapterInterface $adapter, int $id_user, int $id_phone, string $text, string $destination, bool $flash = false, bool $mms = false, ?string $tag = null, array $medias = [], $originating_scheduled = null, string $status = \models\Sended::STATUS_UNKNOWN): array
{
$return = [
'error' => false,
'error_message' => null,
];
+ $internal_setting = new Setting();
+ $user_settings = $internal_setting->gets_for_user($id_user);
+
$at = (new \DateTime())->format('Y-m-d H:i:s');
$media_uris = [];
foreach ($medias as $media)
@@ -275,71 +285,87 @@ namespace controllers\internals;
$text .= "\n" . join(' - ', $media_urls);
}
- //If we reached our max quota, do not send the message
- $internal_quota = new Quota($this->bdd);
- $nb_credits = $internal_quota::compute_credits_for_message($text); //Calculate how much credit the message require
- if (!$internal_quota->has_enough_credit($id_user, $nb_credits))
+ try
{
- $return['error'] = true;
- $return['error_message'] = 'Not enough credit to send message.';
- }
+ //If we reached our max quota, do not send the message
+ $internal_quota = new Quota($this->bdd);
+ $nb_credits = $internal_quota::compute_credits_for_message($text); //Calculate how much credit the message require
+ if (!$internal_quota->has_enough_credit($id_user, $nb_credits))
+ {
+ throw new Exception('Not enough credit to send message.');
+ }
- //If we reached limit for this phone, do not send the message
- $internal_phone = new Phone($this->bdd);
- $internal_sended = new Sended($this->bdd);
- $limits = $internal_phone->get_limits($id_phone);
+ // If this phone status indicate it is not available
+ $internal_phone = new Phone($this->bdd);
+ $phone = $internal_phone->get_for_user($id_user, $id_phone);
+ if (!$phone || $phone['status'] != \models\Phone::STATUS_AVAILABLE)
+ {
+ throw new Exception('Invalid phone status : ' . $phone['status']);
+ }
- $remaining_volume = PHP_INT_MAX;
- foreach ($limits as $limit)
- {
- $startpoint = new \DateTime($limit['startpoint']);
- $consumed = $internal_sended->count_since_for_phone_and_user($id_user, $id_phone, $startpoint);
- $remaining_volume = min(($limit['volume'] - $consumed), $remaining_volume);
- }
+ //If we reached limit for this phone and phone limits are enabled, do not send the message
+ if ((int) ($user_settings['phone_limit'] ?? false))
+ {
+ $limits = $internal_phone->get_limits($id_phone);
- if ($remaining_volume < 1)
- {
- $return['error'] = true;
- $return['error_message'] = 'Phone send limit have been reached.';
- }
+ $remaining_volume = PHP_INT_MAX;
+ foreach ($limits as $limit)
+ {
+ $startpoint = new \DateTime($limit['startpoint']);
+ $consumed = $this->count_since_for_phone_and_user($id_user, $id_phone, $startpoint);
+ $remaining_volume = min(($limit['volume'] - $consumed), $remaining_volume);
+ }
+
+ if ($remaining_volume < 1)
+ {
+ throw new Exception('Phone send limit have been reached.');
+ }
+ }
- $uid = uniqid();
- if (!$return['error'])
- {
$response = $adapter->send($destination, $text, $flash, $mms, $media_uris);
- $uid = $response['uid'] ?? $uid;
if ($response['error'])
{
- $return['error'] = true;
- $return['error_message'] = $response['error_message'];
- }
- else // If send with success, consume credit
- {
- $internal_quota->consume_credit($id_user, $nb_credits);
+ throw new Exception($response['error_message']);
}
+
+ $uid = $response['uid'];
+ $status = \models\Sended::STATUS_UNKNOWN;
+
+ // If send with success, consume credit
+ $internal_quota->consume_credit($id_user, $nb_credits);
}
+ catch (Exception $e)
+ {
+ $return['error'] = true;
+ $return['error_message'] = $e->getMessage();
- // If we fail to send or not, we will always save message as sended, only the status will change.
- $status = $return['error'] ? \models\Sended::STATUS_FAILED : \models\Sended::STATUS_UNKNOWN;
- $sended_id = $this->create($id_user, $id_phone, $at, $text, $destination, $uid, $adapter->meta_classname(), $flash, $mms, $medias, $originating_scheduled, $status);
-
- $webhook_body = [
- 'id' => $sended_id,
- 'at' => $at,
- 'status' => $status,
- 'text' => $text,
- 'destination' => $destination,
- 'origin' => $id_phone,
- 'mms' => $mms,
- 'medias' => $medias,
- 'originating_scheduled' => $originating_scheduled,
- ];
+ $status = \models\Sended::STATUS_FAILED;
+
+ return $return;
+ }
+ finally
+ {
+ $uid = $uid ?? uniqid();
+ $sended_id = $this->create($id_user, $id_phone, $at, $text, $destination, $uid, $adapter->meta_classname(), $flash, $mms, $tag, $medias, $originating_scheduled, $status);
+
+ $webhook_body = [
+ 'id' => $sended_id,
+ 'at' => $at,
+ 'status' => $status,
+ 'text' => $text,
+ 'destination' => $destination,
+ 'origin' => $id_phone,
+ 'mms' => $mms,
+ 'medias' => $medias,
+ 'originating_scheduled' => $originating_scheduled,
+ ];
- $internal_webhook = new Webhook($this->bdd);
- $internal_webhook->trigger($id_user, \models\Webhook::TYPE_SEND_SMS, $webhook_body);
+ $internal_webhook = new Webhook($this->bdd);
+ $internal_webhook->trigger($id_user, \models\Webhook::TYPE_SEND_SMS, $webhook_body);
- return $return;
+ return $return;
+ }
}
/**
diff --git a/controllers/publics/Api.php b/controllers/publics/Api.php
index 7ee0d36..c2e0636 100644
--- a/controllers/publics/Api.php
+++ b/controllers/publics/Api.php
@@ -49,6 +49,7 @@ namespace controllers\publics;
private $internal_user;
private $internal_phone;
+ private $internal_phone_group;
private $internal_received;
private $internal_sended;
private $internal_scheduled;
@@ -72,6 +73,7 @@ namespace controllers\publics;
$bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
$this->internal_user = new \controllers\internals\User($bdd);
$this->internal_phone = new \controllers\internals\Phone($bdd);
+ $this->internal_phone_group = new \controllers\internals\PhoneGroup($bdd);
$this->internal_received = new \controllers\internals\Received($bdd);
$this->internal_sended = new \controllers\internals\Sended($bdd);
$this->internal_scheduled = new \controllers\internals\Scheduled($bdd);
@@ -118,14 +120,14 @@ namespace controllers\publics;
/**
* List all entries of a certain type for the current user, sorted by id.
*
- * @param string $entry_type : Type of entries we want to list ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'media']
+ * @param string $entry_type : Type of entries we want to list ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'phone_group', 'media']
* @param int $page : Pagination number, Default = 0. Group of 25 results.
*
* @return : List of entries
*/
public function get_entries(string $entry_type, int $page = 0)
{
- $entry_types = ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'media'];
+ $entry_types = ['sended', 'received', 'scheduled', 'contact', 'group', 'conditional_group', 'phone', 'phone_group', 'media'];
if (!\in_array($entry_type, $entry_types, true))
{
@@ -191,6 +193,26 @@ namespace controllers\publics;
unset($entries[$key]['adapter_data']);
}
}
+ // Special case for phone group we must add phones because its a join
+ elseif ('phone_group' === $entry_type)
+ {
+ foreach ($entries as $key => $entry)
+ {
+ $phones = $this->internal_phone_group->get_phones($entry['id']);
+ // Hide meta data of phones if needed
+ foreach ($phones as &$phone)
+ {
+ if (!$phone['adapter']::meta_hide_data())
+ {
+ continue;
+ }
+
+ unset($phone['adapter_data']);
+ }
+
+ $entries[$key]['phones'] = $phones;
+ }
+ }
$return = self::DEFAULT_RETURN;
$return['response'] = $entries;
@@ -210,14 +232,84 @@ namespace controllers\publics;
return $this->json($return);
}
+ /**
+ * Return info about volume of sms sended for a period
+ *
+ * @param ?string $_POST['start'] : Date from which to get sms volume, format Y-m-d H:i:s. Default to null.
+ * @param ?string $_POST['end'] : Date up to which to get sms volume, format Y-m-d H:i:s. Default to null.
+ * @param ?string $_POST['tag'] : Tag to filter SMS by. If set, only sended sms with a matching tag will be counted. Default to null.
+ *
+ * @return : List of entries
+ */
+ public function get_usage()
+ {
+ $start = $_GET['start'] ?? null;
+ $end = $_GET['end'] ?? null;
+ $tag = $_GET['tag'] ?? null;
+
+ $return = self::DEFAULT_RETURN;
+
+ if ($start)
+ {
+ if (!\controllers\internals\Tool::validate_date($start, 'Y-m-d H:i:s'))
+ {
+ $return = self::DEFAULT_RETURN;
+ $return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
+ $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'start must be a date of format "Y-m-d H:i:s".';
+ $this->auto_http_code(false);
+
+ return $this->json($return);
+ }
+
+ $start = new \DateTime($start);
+ }
+
+ if ($end)
+ {
+ if (!\controllers\internals\Tool::validate_date($end, 'Y-m-d H:i:s'))
+ {
+ $return = self::DEFAULT_RETURN;
+ $return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
+ $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'end must be a date of format "Y-m-d H:i:s".';
+ $this->auto_http_code(false);
+
+ return $this->json($return);
+ }
+
+ $end = new \DateTime($end);
+ }
+
+ $total_sended = 0;
+ $phones_volumes = [];
+
+ $phones = $this->internal_phone->gets_for_user($this->user['id']);
+ foreach ($phones as $phone)
+ {
+ $nb_sended = $this->internal_sended->count_since_for_phone_and_user($this->user['id'], $phone['id'], $start, $end, $tag);
+ $total_sended += $nb_sended;
+ $phones_volumes[$phone['id']] = $nb_sended;
+ }
+
+ $return['response'] = [
+ 'total' => $total_sended,
+ 'phones_volumes' => $phones_volumes,
+ ];
+
+ $this->auto_http_code(true);
+
+ return $this->json($return);
+ }
+
/**
* Schedule a message to be send.
*
* @param string $_POST['at'] : Date to send message at format Y-m-d H:i:s
* @param string $_POST['text'] : Text of the message to send
- * @param string $_POST['id_phone'] : Default null. Id of phone to send the message from. If null use a random phone
+ * @param string $_POST['id_phone'] : Default null. Id of phone to send the message from. If null and id_phone_group null, use a random phone
+ * @param string $_POST['id_phone_group'] : Default null. Id of phone group to send the message from. If null abd id_phone null, use a random phone
* @param string $_POST['flash'] : Default false. Is the sms a flash sms.
* @param string $_POST['mms'] : Default false. Is the sms a mms.
+ * @param string $_POST['tag'] : Default null. Tag to associate to every sms of the campaign.
* @param string $_POST['numbers'] : Array of numbers to send message to
* @param string $_POST['contacts'] : Array of ids of contacts to send message to
* @param string $_POST['groups'] : Array of ids of groups to send message to
@@ -231,8 +323,10 @@ namespace controllers\publics;
$at = $_POST['at'] ?? false;
$text = $_POST['text'] ?? false;
$id_phone = empty($_POST['id_phone']) ? null : $_POST['id_phone'];
+ $id_phone_group = empty($_POST['id_phone_group']) ? null : $_POST['id_phone_group'];
$flash = (bool) ($_POST['flash'] ?? false);
$mms = (bool) ($_POST['mms'] ?? false);
+ $tag = $_POST['tag'] ?? null;
$numbers = $_POST['numbers'] ?? [];
$contacts = $_POST['contacts'] ?? [];
$groups = $_POST['groups'] ?? [];
@@ -417,6 +511,16 @@ namespace controllers\publics;
return $this->json($return);
}
+ if ($id_phone && $id_phone_group)
+ {
+ $return = self::DEFAULT_RETURN;
+ $return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
+ $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'id_phone, id_phone_group : You must specify at most one of id_phone or id_phone_group, not both.';
+ $this->auto_http_code(false);
+
+ return $this->json($return);
+ }
+
$phone = null;
if ($id_phone)
{
@@ -433,6 +537,22 @@ namespace controllers\publics;
return $this->json($return);
}
+ $phone_group = null;
+ if ($id_phone_group)
+ {
+ $phone_group = $this->internal_phone_group->get_for_user($this->user['id'], $id_phone_group);
+ }
+
+ if ($id_phone_group && !$phone_group)
+ {
+ $return = self::DEFAULT_RETURN;
+ $return['error'] = self::ERROR_CODES['INVALID_PARAMETER'];
+ $return['message'] = self::ERROR_MESSAGES['INVALID_PARAMETER'] . 'id_phone_group : You must specify an id_phone_group number among thoses of user phone groups.';
+ $this->auto_http_code(false);
+
+ return $this->json($return);
+ }
+
if ($mms)
{
foreach ($files_arrays as $file)
@@ -455,7 +575,7 @@ namespace controllers\publics;
}
}
- $scheduled_id = $this->internal_scheduled->create($this->user['id'], $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
+ $scheduled_id = $this->internal_scheduled->create($this->user['id'], $at, $text, $id_phone, $id_phone_group, $flash, $mms, $tag, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
if (!$scheduled_id)
{
$return = self::DEFAULT_RETURN;
@@ -710,7 +830,7 @@ namespace controllers\publics;
$priority = $_POST['priority'] ?? $phone['priority'];
$priority = max(((int) $priority), 0);
$adapter = $_POST['adapter'] ?? $phone['adapter'];
- $adapter_data = !empty($_POST['adapter_data']) ? $_POST['adapter_data'] : json_decode($phone['adapter_data']);
+ $adapter_data = !empty($_POST['adapter_data']) ? $_POST['adapter_data'] : json_decode($phone['adapter_data'], true);
$adapter_data = is_array($adapter_data) ? $adapter_data : [$adapter_data];
$limits = $_POST['limits'] ?? $limits;
$limits = is_array($limits) ? $limits : [$limits];
@@ -890,4 +1010,35 @@ namespace controllers\publics;
return $this->json($return);
}
+
+ /**
+ * Trigger re-checking of a phone status
+ *
+ * @param int $id : Id of phone to re-check status
+ */
+ public function post_update_phone_status ($id)
+ {
+ $return = self::DEFAULT_RETURN;
+
+ $phone = $this->internal_phone->get_for_user($this->user['id'], $id);
+ if (!$phone)
+ {
+ $return['error'] = self::ERROR_CODES['CANNOT_UPDATE'];
+ $return['message'] = self::ERROR_MESSAGES['CANNOT_UPDATE'];
+ $this->auto_http_code(false);
+
+ return $this->json($return);
+ }
+
+ //Check adapter is working correctly with thoses names and data
+ $adapter_classname = $phone['adapter'];
+ $adapter_instance = new $adapter_classname($phone['adapter_data']);
+ $new_status = $adapter_instance->check_phone_status();
+
+ $status_update = $this->internal_phone->update_status($id, $new_status);
+ $return['response'] = $new_status;
+ $this->auto_http_code(true);
+
+ return $this->json($return);
+ }
}
diff --git a/controllers/publics/Discussion.php b/controllers/publics/Discussion.php
index 7729908..77e6829 100644
--- a/controllers/publics/Discussion.php
+++ b/controllers/publics/Discussion.php
@@ -234,6 +234,7 @@ namespace controllers\publics;
$at = $now;
$text = $_POST['text'] ?? '';
$destination = $_POST['destination'] ?? false;
+ $tag = $_POST['tag'] ?? null;
$id_phone = $_POST['id_phone'] ?? false;
$files = $_FILES['medias'] ?? false;
@@ -315,7 +316,7 @@ namespace controllers\publics;
//Destinations must be an array of number
$destinations = [['number' => $destination, 'data' => '[]']];
- if (!$this->internal_scheduled->create($id_user, $at, $text, $id_phone, false, $mms, $destinations, [], [], [], $media_ids))
+ if (!$this->internal_scheduled->create($id_user, $at, $text, $id_phone, null, false, $mms, $tag, $destinations, [], [], [], $media_ids))
{
$return['success'] = false;
$return['message'] = 'Impossible de créer le Sms';
diff --git a/controllers/publics/Phone.php b/controllers/publics/Phone.php
index 7451288..3ce81e0 100644
--- a/controllers/publics/Phone.php
+++ b/controllers/publics/Phone.php
@@ -88,6 +88,11 @@ class Phone extends \descartes\Controller
{
$phone['callback_end_call'] = \descartes\Router::url('Callback', 'end_call', ['id_phone' => $phone['id']], ['api_key' => $api_key]);
}
+
+ if ($adapter['meta_support_phone_status'])
+ {
+ $phone['support_phone_status'] = true;
+ }
}
header('Content-Type: application/json');
@@ -425,7 +430,14 @@ class Phone extends \descartes\Controller
continue;
}
- if ($find_adapter['meta_hidden'])
+ $current_phone = $this->internal_phone->get_for_user($id_user, $id_phone);
+ if (!$current_phone)
+ {
+ continue;
+ }
+
+ // We can only use an hidden adapter if it was already the adapter we was using
+ if ($find_adapter['meta_hidden'] && $adapter != $current_phone['adapter'])
{
continue;
}
@@ -499,4 +511,53 @@ class Phone extends \descartes\Controller
return $this->redirect(\descartes\Router::url('Phone', 'list'));
}
+
+
+ /**
+ * Re-check phone status
+ * @param array int $_GET['ids'] : ids of phones we want to update status
+ * @param $csrf : CSRF token
+ */
+ public function update_status ($csrf)
+ {
+ if (!$this->verify_csrf($csrf))
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
+
+ return $this->redirect(\descartes\Router::url('Phone', 'add'));
+ }
+
+ $ids = $_GET['ids'] ?? [];
+ $id_user = $_SESSION['user']['id'];
+
+ foreach ($ids as $id)
+ {
+ $phone = $this->internal_phone->get_for_user($id_user, $id);
+
+ //Check adapter is working correctly with thoses names and data
+ $adapter_classname = $phone['adapter'];
+ if (!call_user_func([$adapter_classname, 'meta_support_phone_status']))
+ {
+ continue;
+ }
+
+ $adapter_instance = new $adapter_classname($phone['adapter_data']);
+ $new_status = $adapter_instance->check_phone_status();
+
+ $status_update = $this->internal_phone->update_status($id, $new_status);
+ }
+
+ \FlashMessage\FlashMessage::push('success', 'Les status des téléphones ont bien été mis à jour.');
+
+ return $this->redirect(\descartes\Router::url('Phone', 'list'));
+ }
+
+ /**
+ * Return a list of phones as a JSON array
+ */
+ public function json_list()
+ {
+ header('Content-Type: application/json');
+ echo json_encode($this->internal_phone->list_for_user($_SESSION['user']['id']));
+ }
}
diff --git a/controllers/publics/PhoneGroup.php b/controllers/publics/PhoneGroup.php
new file mode 100644
index 0000000..57d6fb7
--- /dev/null
+++ b/controllers/publics/PhoneGroup.php
@@ -0,0 +1,245 @@
+
+ *
+ * This source file is subject to the GPL-3.0 license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace controllers\publics;
+
+ /**
+ * Page of phone groups.
+ */
+ class PhoneGroup extends \descartes\Controller
+ {
+ private $internal_phone_group;
+ private $internal_phone;
+ private $internal_event;
+
+ /**
+ * Call before any other func to check user is connected
+ *
+ * @return void;
+ */
+ public function __construct()
+ {
+ $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
+
+ $this->internal_phone_group = new \controllers\internals\PhoneGroup($bdd);
+ $this->internal_phone = new \controllers\internals\Phone($bdd);
+ $this->internal_event = new \controllers\internals\Event($bdd);
+
+ \controllers\internals\Tool::verifyconnect();
+ }
+
+ /**
+ * Return all groups as an array for administration.
+ */
+ public function list()
+ {
+ $this->render('phone_group/list');
+ }
+
+ /**
+ * Return groups as json.
+ */
+ public function list_json()
+ {
+ $entities = $this->internal_phone_group->list_for_user($_SESSION['user']['id']);
+ header('Content-Type: application/json');
+ echo json_encode(['data' => $entities]);
+ }
+
+ /**
+ * Delete a list of phone groups
+ *
+ * @param array int $_GET['ids'] : Ids of phone groups to delete
+ * @param mixed $csrf
+ *
+ * @return boolean;
+ */
+ public function delete($csrf)
+ {
+ if (!$this->verify_csrf($csrf))
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'list'));
+ }
+
+ $ids = $_GET['ids'] ?? [];
+ foreach ($ids as $id)
+ {
+ $this->internal_phone_group->delete_for_user($_SESSION['user']['id'], $id);
+ }
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'list'));
+ }
+
+ /**
+ * Return the creation page of a group
+ */
+ public function add()
+ {
+ $this->render('phone_group/add');
+ }
+
+ /**
+ * Return the edition page for phone groups
+ *
+ * @param array $_GET['ids'] : Ids of phone groups to edit
+ */
+ public function edit()
+ {
+ $ids = $_GET['ids'] ?? [];
+
+ $groups = $this->internal_phone_group->gets_in_for_user($_SESSION['user']['id'], $ids);
+
+ foreach ($groups as $key => $group)
+ {
+ $groups[$key]['phones'] = $this->internal_phone_group->get_phones($group['id']);
+ }
+
+ $this->render('phone_group/edit', [
+ 'phone_groups' => $groups,
+ ]);
+ }
+
+ /**
+ * Create a new phone group
+ *
+ * @param $csrf : CSRF token
+ * @param string $_POST['name'] : Name of phone group
+ * @param array $_POST['phones'] : Ids of phones to put in the group
+ */
+ public function create($csrf)
+ {
+ if (!$this->verify_csrf($csrf))
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'add'));
+ }
+
+ $name = $_POST['name'] ?? false;
+ $phones_ids = $_POST['phones'] ?? false;
+
+ if (!$name || !$phones_ids)
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Des champs sont manquants !');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'add'));
+ }
+
+ $id_group = $this->internal_phone_group->create($_SESSION['user']['id'], $name, $phones_ids);
+ if (!$id_group)
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Impossible de créer ce groupe.');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'add'));
+ }
+
+ \FlashMessage\FlashMessage::push('success', 'Le groupe a bien été créé.');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'list'));
+ }
+
+ /**
+ * Update a list of phone groups
+ *
+ * @param $csrf : CSRF token
+ * @param array $_POST['phone_groups'] : An array of phone groups with group id as keys
+ *
+ * @return boolean;
+ */
+ public function update($csrf)
+ {
+ if (!$this->verify_csrf($csrf))
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'list'));
+ }
+
+ $groups = $_POST['phone_groups'] ?? [];
+
+ $nb_groups_update = 0;
+ foreach ($groups as $id => $group)
+ {
+ foreach ($group['phones_ids'] as $key => $value)
+ {
+ $group['phones_ids'][$key] = (int) $value;
+ }
+
+ $nb_groups_update += (int) $this->internal_phone_group->update_for_user($_SESSION['user']['id'], $id, $group['name'], $group['phones_ids']);
+ }
+
+ if ($nb_groups_update !== \count($groups))
+ {
+ \FlashMessage\FlashMessage::push('danger', 'Certains groupes n\'ont pas pu êtres mis à jour.');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'list'));
+ }
+
+ \FlashMessage\FlashMessage::push('success', 'Tous les groupes ont été modifiés avec succès.');
+
+ return $this->redirect(\descartes\Router::url('PhoneGroup', 'list'));
+ }
+
+ /**
+ * Return phones of a group as json array
+ * @param int $id_group = PhoneGroup id
+ *
+ * @return json
+ */
+ public function preview (int $id_group)
+ {
+ $return = [
+ 'success' => false,
+ 'result' => 'Une erreur inconnue est survenue.',
+ ];
+
+ $group = $this->internal_phone_group->get_for_user($_SESSION['user']['id'], $id_group);
+
+ if (!$group)
+ {
+ $return['result'] = 'Ce groupe n\'existe pas.';
+ echo json_encode($return);
+
+ return false;
+ }
+
+ $phones = $this->internal_phone_group->get_phones($id_group);
+ if (!$phones)
+ {
+ $return['result'] = 'Aucun téléphone dans le groupe.';
+ echo json_encode($return);
+
+ return false;
+ }
+
+ foreach ($phones as &$phone)
+ {
+ $phone['adapter_name'] = call_user_func([$phone['adapter'], 'meta_name']);
+ }
+
+ $return['success'] = true;
+ $return['result'] = $phones;
+ echo json_encode($return);
+
+ return true;
+ }
+
+ /**
+ * Cette fonction retourne la liste des groups sous forme JSON.
+ */
+ public function json_list()
+ {
+ header('Content-Type: application/json');
+ echo json_encode($this->internal_phone_group->list_for_user($_SESSION['user']['id']));
+ }
+ }
diff --git a/controllers/publics/Scheduled.php b/controllers/publics/Scheduled.php
index 2c6e0e3..eb2decb 100644
--- a/controllers/publics/Scheduled.php
+++ b/controllers/publics/Scheduled.php
@@ -18,6 +18,7 @@ namespace controllers\publics;
{
private $internal_scheduled;
private $internal_phone;
+ private $internal_phone_group;
private $internal_contact;
private $internal_group;
private $internal_conditional_group;
@@ -34,6 +35,7 @@ namespace controllers\publics;
$bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
$this->internal_scheduled = new \controllers\internals\Scheduled($bdd);
$this->internal_phone = new \controllers\internals\Phone($bdd);
+ $this->internal_phone_group = new \controllers\internals\PhoneGroup($bdd);
$this->internal_contact = new \controllers\internals\Contact($bdd);
$this->internal_group = new \controllers\internals\Group($bdd);
$this->internal_conditional_group = new \controllers\internals\ConditionalGroup($bdd);
@@ -118,6 +120,7 @@ namespace controllers\publics;
$contacts = $this->internal_contact->gets_for_user($id_user);
$phones = $this->internal_phone->gets_for_user($id_user);
+ $phone_groups = $this->internal_phone_group->gets_for_user($id_user);
$contact_ids = (isset($_GET['contact_ids']) && \is_array($_GET['contact_ids'])) ? $_GET['contact_ids'] : [];
$group_ids = (isset($_GET['group_ids']) && \is_array($_GET['group_ids'])) ? $_GET['group_ids'] : [];
@@ -153,6 +156,7 @@ namespace controllers\publics;
'now' => $now->format('Y-m-d H:i'),
'contacts' => $contacts,
'phones' => $phones,
+ 'phone_groups' => $phone_groups,
'prefilled_contacts' => $prefilled_contacts,
'prefilled_groups' => $prefilled_groups,
'prefilled_conditional_groups' => $prefilled_conditional_groups,
@@ -179,6 +183,7 @@ namespace controllers\publics;
$all_contacts = $this->internal_contact->gets_for_user($_SESSION['user']['id']);
$phones = $this->internal_phone->gets_for_user($_SESSION['user']['id']);
+ $phone_groups = $this->internal_phone_group->gets_for_user($id_user);
$scheduleds = $this->internal_scheduled->gets_in_for_user($_SESSION['user']['id'], $ids);
//Pour chaque message on ajoute les numéros, les contacts & les groups
@@ -226,6 +231,7 @@ namespace controllers\publics;
$this->render('scheduled/edit', [
'scheduleds' => $scheduleds,
'phones' => $phones,
+ 'phone_groups' => $phone_groups,
'contacts' => $all_contacts,
]);
}
@@ -238,7 +244,7 @@ namespace controllers\publics;
* @param string $_POST['at'] : Date to send message for
* @param string $_POST['text'] : Text of the message
* @param ?bool $_POST['flash'] : Is the message a flash message (by default false)
- * @param ?int $_POST['id_phone'] : Id of the phone to send message from, if null use random phone
+ * @param ?int $_POST['id_phone'] : Id of the phone or phone group to send message from. id will be preceed by phone_ of phonegroup_ depending on type of ressource to use, if null use random phone
* @param ?array $_POST['numbers'] : Numbers to send the message to
* @param ?array $_POST['contacts'] : Numbers to send the message to
* @param ?array $_POST['groups'] : Numbers to send the message to
@@ -258,6 +264,7 @@ namespace controllers\publics;
$at = $_POST['at'] ?? false;
$text = $_POST['text'] ?? false;
$flash = (bool) ($_POST['flash'] ?? false);
+ $tag = ($_POST['tag'] ?? null) ?: null;
$id_phone = empty($_POST['id_phone']) ? null : $_POST['id_phone'];
$numbers = $_POST['numbers'] ?? [];
$numbers = is_array($numbers) ? $numbers : [$numbers];
@@ -433,7 +440,12 @@ namespace controllers\publics;
$mms = (bool) count($media_ids);
- $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
+ // Check if we must send message to a phone or a phone_group based on if id_phone start with 'phone_' or 'phonegroup_'
+ $original_id_phone = $id_phone;
+ $id_phone = str_starts_with($original_id_phone, 'phone_') ? mb_substr($original_id_phone, mb_strlen('phone_')) : null;
+ $id_phone_group = str_starts_with($original_id_phone, 'phonegroup_') ? mb_substr($original_id_phone, mb_strlen('phonegroup_')) : null;
+
+ $scheduled_id = $this->internal_scheduled->create($id_user, $at, $text, $id_phone, $id_phone_group, $flash, $mms, $tag, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
if (!$scheduled_id)
{
\FlashMessage\FlashMessage::push('danger', 'Impossible de créer le Sms.');
@@ -473,6 +485,7 @@ namespace controllers\publics;
$text = $scheduled['text'] ?? false;
$id_phone = empty($scheduled['id_phone']) ? null : $scheduled['id_phone'];
$flash = (bool) ($scheduled['flash'] ?? false);
+ $tag = ($scheduled['tag'] ?? null) ?: null;
$numbers = $scheduled['numbers'] ?? [];
$contacts = $scheduled['contacts'] ?? [];
$groups = $scheduled['groups'] ?? [];
@@ -650,7 +663,11 @@ namespace controllers\publics;
$mms = (bool) count($media_ids);
- $this->internal_scheduled->update_for_user($id_user, $id_scheduled, $at, $text, $id_phone, $flash, $mms, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
+ $original_id_phone = $id_phone;
+ $id_phone = str_starts_with($original_id_phone, 'phone_') ? mb_substr($original_id_phone, mb_strlen('phone_')) : null;
+ $id_phone_group = str_starts_with($original_id_phone, 'phonegroup_') ? mb_substr($original_id_phone, mb_strlen('phonegroup_')) : null;
+
+ $this->internal_scheduled->update_for_user($id_user, $id_scheduled, $at, $text, $id_phone, $id_phone_group, $flash, $mms, $tag, $numbers, $contacts, $groups, $conditional_groups, $media_ids);
++$nb_update;
}
diff --git a/controllers/publics/Sended.php b/controllers/publics/Sended.php
index d82bed6..7761352 100644
--- a/controllers/publics/Sended.php
+++ b/controllers/publics/Sended.php
@@ -59,8 +59,9 @@ namespace controllers\publics;
0 => 'phone_name',
1 => 'searchable_destination',
2 => 'text',
- 3 => 'at',
- 4 => 'status',
+ 3 => 'tag',
+ 4 => 'at',
+ 5 => 'status',
];
$search = $_GET['search']['value'] ?? null;
diff --git a/daemons/Phone.php b/daemons/Phone.php
index 585ce20..1348a9e 100644
--- a/daemons/Phone.php
+++ b/daemons/Phone.php
@@ -144,7 +144,7 @@ class Phone extends AbstractDaemon
//Do message sending
$this->logger->info('Try send message : ' . json_encode($message));
- $response = $internal_sended->send($this->adapter, $this->phone['id_user'], $this->phone['id'], $message['text'], $message['destination'], $message['flash'], $message['mms'], $message['medias'], $message['id_scheduled']);
+ $response = $internal_sended->send($this->adapter, $this->phone['id_user'], $this->phone['id'], $message['text'], $message['destination'], $message['flash'], $message['mms'], $message['tag'], $message['medias'], $message['id_scheduled']);
if ($response['error'])
{
$this->logger->error('Failed send message : ' . json_encode($message) . ' with error : ' . $response['error_message']);
diff --git a/daemons/Sender.php b/daemons/Sender.php
index d462739..9acb00a 100644
--- a/daemons/Sender.php
+++ b/daemons/Sender.php
@@ -80,6 +80,7 @@ class Sender extends AbstractDaemon
'destination' => $sms['destination'],
'flash' => $sms['flash'],
'mms' => $sms['mms'],
+ 'tag' => $sms['tag'],
'medias' => $sms['medias'] ?? [],
];
diff --git a/db/migrations/20230218113648_add_status_phone.php b/db/migrations/20230218113648_add_status_phone.php
new file mode 100644
index 0000000..97c397e
--- /dev/null
+++ b/db/migrations/20230218113648_add_status_phone.php
@@ -0,0 +1,38 @@
+table('phone');
+ $table->addColumn('status', 'enum', ['values' => ['available', 'unavailable', 'no_credit'], 'default' => 'available']);
+ $table->update();
+ }
+}
diff --git a/db/migrations/20230219014653_create_phone_group.php b/db/migrations/20230219014653_create_phone_group.php
new file mode 100644
index 0000000..d8e3b4b
--- /dev/null
+++ b/db/migrations/20230219014653_create_phone_group.php
@@ -0,0 +1,52 @@
+table('phone_group')
+ ->addColumn('id_user', 'integer', ['null' => false])
+ ->addColumn('name', 'string', ['null' => false, 'limit' => 100])
+ ->addColumn('created_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP'])
+ ->addColumn('updated_at', 'timestamp', ['null' => true, 'update' => 'CURRENT_TIMESTAMP'])
+ ->addForeignKey('id_user', 'user', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
+ ->create();
+
+ $this->table('phone_group_phone')
+ ->addColumn('id_phone_group', 'integer', ['null' => false])
+ ->addColumn('id_phone', 'integer', ['null' => false])
+ ->addColumn('created_at', 'timestamp', ['null' => false, 'default' => 'CURRENT_TIMESTAMP'])
+ ->addColumn('updated_at', 'timestamp', ['null' => true, 'update' => 'CURRENT_TIMESTAMP'])
+ ->addForeignKey('id_phone_group', 'phone_group', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
+ ->addForeignKey('id_phone', 'phone', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
+ ->create();
+ }
+}
diff --git a/db/migrations/20230219102411_add_id_phone_group_to_scheduled.php b/db/migrations/20230219102411_add_id_phone_group_to_scheduled.php
new file mode 100644
index 0000000..ee12008
--- /dev/null
+++ b/db/migrations/20230219102411_add_id_phone_group_to_scheduled.php
@@ -0,0 +1,39 @@
+table('scheduled')
+ ->addColumn('id_phone_group', 'integer', ['null' => true, 'default' => null, 'after' => 'id_phone'])
+ ->addForeignKey('id_phone_group', 'phone_group', 'id', ['delete' => 'CASCADE', 'update' => 'CASCADE'])
+ ->update();
+ }
+}
diff --git a/db/migrations/20230224133515_add_scheduled_tag.php b/db/migrations/20230224133515_add_scheduled_tag.php
new file mode 100644
index 0000000..2ca4e08
--- /dev/null
+++ b/db/migrations/20230224133515_add_scheduled_tag.php
@@ -0,0 +1,42 @@
+table('scheduled');
+ $table->addColumn('tag', 'string', ['default' => NULL, 'null' => true, 'limit' => 1000])
+ ->update();
+
+ $table = $this->table('sended');
+ $table->addColumn('tag', 'string', ['default' => NULL, 'null' => true, 'limit' => 255])
+ ->update();
+ }
+}
diff --git a/descartes/Model.php b/descartes/Model.php
index ad14075..0580cde 100644
--- a/descartes/Model.php
+++ b/descartes/Model.php
@@ -144,7 +144,7 @@
/**
* Generate IN query params and values
- * @param string $values : Values to generate in array from
+ * @param array $values : Values to generate in array from
* @return array : Array ['QUERY' => string 'IN(...)', 'PARAMS' => [parameters to pass to execute]]
*/
protected function _generate_in_from_array ($values)
@@ -214,6 +214,11 @@
$operator = '>';
break;
+ case ('%' == $first_char) :
+ $true_fieldname = mb_substr($fieldname, 1);
+ $operator = 'LIKE';
+ break;
+
case ('=' == $first_char) :
$true_fieldname = mb_substr($fieldname, 1);
$operator = '=';
@@ -227,8 +232,11 @@
//Protect against injection in fieldname
$true_fieldname = preg_replace('#[^a-zA-Z0-9_]#', '', $true_fieldname);
- $query = '`' . $true_fieldname . '` ' . $operator . ' :where_' . $true_fieldname;
- $param = ['where_' . $true_fieldname => $value];
+ // Add a uid to fieldname so we can combine multiple rules on same field
+ $uid = uniqid();
+
+ $query = '`' . $true_fieldname . '` ' . $operator . ' :where_' . $true_fieldname . '_' . $uid;
+ $param = ['where_' . $true_fieldname . '_' . $uid => $value];
return ['QUERY' => $query, 'PARAM' => $param];
}
@@ -358,7 +366,6 @@
}
$query = "SELECT COUNT(*) as `count` FROM `" . $table . "` WHERE 1 " . (count($wheres) ? 'AND ' : '') . implode(' AND ', $wheres);
-
$query = $this->pdo->prepare($query);
foreach ($params as $label => &$param)
diff --git a/env.php.dist b/env.php.dist
index c544ba0..2c13aab 100644
--- a/env.php.dist
+++ b/env.php.dist
@@ -83,6 +83,8 @@
'alert_quota_limit_close' => 0.9,
'hide_menus' => '',
'force_gsm_alphabet' => 0,
+ 'phone_limit' => 0,
+ 'phone_priority' => 0,
],
];
diff --git a/models/Phone.php b/models/Phone.php
index 4579b22..41fde4d 100644
--- a/models/Phone.php
+++ b/models/Phone.php
@@ -14,6 +14,10 @@ namespace models;
class Phone extends StandardModel
{
+ const STATUS_AVAILABLE = 'available';
+ const STATUS_UNAVAILABLE = 'unavailable';
+ const STATUS_NO_CREDIT = 'no_credit';
+
/**
* Return all phones that belongs to active users
*
diff --git a/models/PhoneGroup.php b/models/PhoneGroup.php
new file mode 100644
index 0000000..0a71a24
--- /dev/null
+++ b/models/PhoneGroup.php
@@ -0,0 +1,123 @@
+
+ *
+ * This source file is subject to the GPL-3.0 license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace models;
+
+ class PhoneGroup extends StandardModel
+ {
+ /**
+ * Return a list of phone groups for a user.
+ * Add a column nb_phone.
+ *
+ * @param int $id_user : user id
+ * @param ?int $limit : Number of entry to return or null
+ * @param ?int $offset : Number of entry to ignore or null
+ *
+ * @return array
+ */
+ public function list_for_user(int $id_user, $limit, $offset)
+ {
+ $query = '
+ SELECT pg.*, COUNT(pgp.id) as nb_phone
+ FROM `phone_group` as pg
+ LEFT JOIN phone_group_phone as pgp
+ ON pg.id = pgp.id_phone_group
+ WHERE pg.id_user = :id_user
+ GROUP BY pg.id
+ ';
+
+ if (null !== $limit)
+ {
+ $limit = (int) $limit;
+
+ $query .= ' LIMIT ' . $limit;
+ if (null !== $offset)
+ {
+ $offset = (int) $offset;
+ $query .= ' OFFSET ' . $offset;
+ }
+ }
+
+ $params = [
+ 'id_user' => $id_user,
+ ];
+
+ return $this->_run_query($query, $params);
+ }
+
+ /**
+ * Return a phone group by his name for a user.
+ *
+ * @param int $id_user : User id
+ * @param string $name : Group name
+ *
+ * @return array
+ */
+ public function get_by_name_for_user(int $id_user, string $name)
+ {
+ return $this->_select_one($this->get_table_name(), ['id_user' => $id_user, 'name' => $name]);
+ }
+
+ /**
+ * Delete relations between phone group and phone for a group.
+ *
+ * @param int $id_phone_group : Group id
+ *
+ * @return int : Number of deleted rows
+ */
+ public function delete_phone_group_phone_relations(int $id_phone_group)
+ {
+ return $this->_delete('phone_group_phone', ['id_phone_group' => $id_phone_group]);
+ }
+
+ /**
+ * Insert a relation between a phone group and a phone.
+ *
+ * @param int $id_phone_group : Phone Group id
+ * @param int $id_phone : Phone id
+ *
+ * @return mixed (bool|int) : False on error, new row id else
+ */
+ public function insert_phone_group_phone_relation(int $id_phone_group, int $id_phone)
+ {
+ $success = (bool) $this->_insert('phone_group_phone', ['id_phone_group' => $id_phone_group, 'id_phone' => $id_phone]);
+
+ return $success ? $this->_last_id() : false;
+ }
+
+ /**
+ * Get phone groups phones.
+ *
+ * @param int $id_phone_group : Phone Group id
+ *
+ * @return array : Phones of the group
+ */
+ public function get_phones(int $id_phone_group)
+ {
+ $query = '
+ SELECT *
+ FROM `phone`
+ WHERE id IN (SELECT id_phone FROM `phone_group_phone` WHERE id_phone_group = :id_phone_group)
+ ';
+
+ $params = ['id_phone_group' => $id_phone_group];
+
+ return $this->_run_query($query, $params);
+ }
+
+ /**
+ * Return table name.
+ */
+ protected function get_table_name(): string
+ {
+ return 'phone_group';
+ }
+ }
diff --git a/models/Sended.php b/models/Sended.php
index 0acdb20..3644dee 100644
--- a/models/Sended.php
+++ b/models/Sended.php
@@ -183,13 +183,35 @@ namespace models;
*
* @param int $id_user : User id
* @param int $id_phone : Phone id we want the number of sended message for
- * @param \DateTime $since : Date since which we want sended number
+ * @param ?\DateTime $since : Date since which we want sended Number. Default to null.
+ * @param ?\DateTime $before : Date up to which we want sended number. Default to null.
+ * @param ?string $tag_like : Tag to filter sms by, this is not a = but a LIKE operator
*
* @return int
*/
- public function count_since_for_phone_and_user(int $id_user, int $id_phone, \DateTime $since) : int
+ public function count_since_for_phone_and_user(int $id_user, int $id_phone, ?\DateTime $since = null, ?\DateTime $before = null, ?string $tag_like = null) : int
{
- return $this->_count('sended', ['id_user' => $id_user, 'id_phone' => $id_phone, '>=at' => $since->format('c')]);
+ $data = [
+ 'id_user' => $id_user,
+ 'id_phone' => $id_phone,
+ ];
+
+ if ($since)
+ {
+ $data['>=at'] = $since->format('c');
+ }
+
+ if ($before)
+ {
+ $data['<=at'] = $before->format('c');
+ }
+
+ if ($tag_like)
+ {
+ $data['%tag'] = $tag_like;
+ }
+
+ return $this->_count('sended', $data);
}
/**
diff --git a/routes.php b/routes.php
index a5e87cd..ccb7279 100644
--- a/routes.php
+++ b/routes.php
@@ -164,6 +164,20 @@
'delete' => '/phone/delete/{csrf}/',
'edit' => '/phone/edit/',
'update' => '/phone/update/{csrf}/',
+ 'update_status' => '/phone/update_status/{csrf}/',
+ 'json_list' => '/phones.json/',
+ ],
+
+ 'PhoneGroup' => [
+ 'list' => '/phone_group/',
+ 'list_json' => '/phone_group/json/',
+ 'add' => '/phone_group/add/',
+ 'create' => '/phone_group/create/{csrf}/',
+ 'delete' => '/phone_group/delete/{csrf}/',
+ 'edit' => '/phone_group/edit/',
+ 'update' => '/phone_group/update/{csrf}/',
+ 'preview' => '/phone_group/preview/{id_group}/',
+ 'json_list' => '/phone_group.json/',
],
'Call' => [
@@ -194,6 +208,7 @@
'/api/list/{entry_type}/',
'/api/list/{entry_type}/{page}/',
],
+ 'get_usage' => '/api/usage/',
'post_scheduled' => [
'/api/scheduled/',
],
@@ -206,6 +221,9 @@
'post_update_phone' => [
'/api/phone/{id}/',
],
+ 'post_update_phone_status' => [
+ '/api/phone/{id}/status/',
+ ],
'delete_phone' => [
'/api/phone/{id}/',
],
diff --git a/templates/group/list.php b/templates/group/list.php
index 5da0452..2b7728c 100644
--- a/templates/group/list.php
+++ b/templates/group/list.php
@@ -111,7 +111,7 @@ jQuery(document).ready(function ()
{
data: '_',
render: function (data, type, row, meta) {
- return '';
+ return '';
},
},
{
@@ -145,7 +145,6 @@ jQuery(document).ready(function ()
html += '
' + jQuery.fn.dataTable.render.text().display(contact.data) + '
'
html += '';
- console.log(contact);
}
jQuery('#preview-text-modal').find('.modal-body').html(html);
diff --git a/templates/incs/nav.php b/templates/incs/nav.php
index 91e2340..b232d1b 100644
--- a/templates/incs/nav.php
+++ b/templates/incs/nav.php
@@ -110,8 +110,16 @@
- - Lors de l'envoi de SMS sans téléphone spécifié, les téléphones avec la plus haute priorité seront utilisés en premier. -
-+ Lors de l'envoi de SMS sans téléphone spécifié, les téléphones avec la plus haute priorité seront utilisés en premier. +
+@@ -87,15 +91,19 @@
- Défini le nombre maximum de SMS qui pourront être envoyés avec ce téléphone sur des périodes de temps données. -
-+ Défini le nombre maximum de SMS qui pourront être envoyés avec ce téléphone sur des périodes de temps données. +
+- Défini le nombre maximum de SMS qui pourront être envoyés avec ce téléphone sur des périodes de temps données. -
-+ Vous pouvez renseigner une chaine de caractère qui sera associée à tous les SMS envoyés. Utile pour associer un identifiant interne à vos systèmes. Laissez vide si vous ne voulez pas associé de chaine. +
++ Vous pouvez renseigner une chaine de caractère qui sera associée à tous les SMS envoyés. Utile pour associer un identifiant interne à vos systèmes. Laissez vide si vous ne voulez pas associé de chaine. +
+