...
 
Commits (28)
before_script:
- '[[ -d /srv/piphone/ ]] || mkdir -p /srv/piphone/'
- 'which git || (apt-get update -yq && apt-get install git -yqq)'
- 'which curl ||(apt-get update -yq && apt-get install curl -yqq)'
job composer install:
environment:
preprod
script:
- composer install
- composer update
artifacts:
paths:
- vendor/
expire_in: 1 day
stage: build
tags: [preprod]
only:
- master
job deploy preprod:
variables:
BASE_PATH: /srv/piphone/frontend/
environment:
preprod
script:
- rsync -ruvC ./ ${BASE_PATH}
stage:
deploy
tags: [preprod]
only:
- master
job install:
job deploy production:
variables:
BASE_PATH: /srv/piphone/frontend
environment:
production
script:
- chmod a+x ci/install.sh
- ./ci/install.sh
stage: deploy
tags:
- preprod
- rsync -ruvC ./ ${BASE_PATH}
stage:
deploy
only:
- master
when: manual
tags: [piphone]
.PHONY: help doctor install reset-db translations
.DEFAULT_GOAL := help
help:
@echo "\033[33mUsage:\033[0m"
@echo " make [command]"
@echo ""
@echo "\033[33mAvailable commands:\033[0m"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile | sort \
| awk 'BEGIN {FS = ":.*?## "}; {printf " \033[32m%s\033[0m___%s\n", $$1, $$2}' | column -ts___
doctor: ## Check that everything is installed to use this application
@echo "\033[1m\033[36m==> Check required dependencies\033[0m\033[21m"
@which composer >/dev/null 2>&1 && echo "\033[32mcomposer installed\033[0m" || echo "\033[31mcomposer not installed\033[0m"
@echo "\033[1m\033[36m==> Check configuration\033[0m\033[21m"
@test -s ./app/config.php && echo "\033[32mConfiguration OK\033[0m" || echo "\033[31mYou need to copy app/config.php.sample to app/config.php in order to configure your application.\033[0m"
@echo "\033[1m\033[36m==> Check optional dependencies\033[0m\033[21m"
@which msgmerge >/dev/null 2>&1 && echo "\033[32mmsgmerge installed\033[0m" || echo "\033[31mmsgmerge not installed\033[0m"
@which msgfmt >/dev/null 2>&1 && echo "\033[32mmsgfmt installed\033[0m" || echo "\033[31mmsgfmt not installed\033[0m"
@which xgettext >/dev/null 2>&1 && echo "\033[32mxgettext installed\033[0m" || echo "\033[31mxgettext not installed\033[0m"
install: ## Install the application
@echo "\033[1m\033[36m==> Install Composer dependencies\033[0m\033[21m"
@composer install
server-start: server-stop ## Launch a local server
@php -S 127.0.0.1:8000 >> .server.log &
@echo "\033[32mServer running. (http://127.0.0.1:8000)\033[0m"
server-stop: ## Stop local server if running
@ps -aux | grep "[p]hp -S 127.0.0.1:8000" | grep -v grep | awk '{print $$2}' | xargs -r -n 1 kill
@echo "\033[32mServer stopped. (http://127.0.0.1:8000)\033[0m"
translations: locales/fr_FR/LC_MESSAGES/messages.mo locales/en_US/LC_MESSAGES/messages.mo ## Generate translations
messages.pot: app/*.php templates/*.html
[ -r $@ ] || touch $@
xgettext --package-name=LQDNCampaign --package-version=2016.1 --force-po -o $@ --keyword=__ --keyword=_ --from-code=UTF-8 $^
locales/%/LC_MESSAGES/messages.po: messages.pot
msgmerge -v -U $@ $^
locales/fr_FR/LC_MESSAGES/messages.mo: locales/fr_FR/LC_MESSAGES/messages.po
msgfmt $^ -o $@
locales/en_US/LC_MESSAGES/messages.mo: locales/en_US/LC_MESSAGES/messages.po
msgfmt $^ -o $@
......@@ -90,23 +90,5 @@ class Api {
$json = Api::post("campaigns/" . CAMPAIGN_ID . "/feedbacks", $data);
return json_decode($json, true);
}
// Getting a call from the SIP_API
static function get_sip_call($call_id, $token) {
$data = array('api' => JWT_TOKEN,
'token' => $token
);
$client = new GuzzleHttp\Client(['base_uri' => SIP_ARI]);
try {
$res = $client->get("calls/$call_id", ['query' => $data]);
return (GuzzleHttp\Psr7\copy_to_string($res->getBody()));
} catch (RequestException $e) {
return Psr7\str($e->getRequest());
if ($e->hasResponse()) {
echo Psr7\str($e->getResponse());
}
}
}
};
......@@ -63,40 +63,22 @@ class Main extends Controller {
function call($f3, $args) {
//GET
if ($f3->get('VERB') == 'GET') {
$f3->set('contact', Api::get_contact($args['id']));
$f3->set('block_content', 'call.html');
$categories = Api::get_feedback_categories();
$f3->set('feedback_categories', $categories);
$f3->set('contact_id', $args['id']);
$f3->set('block_content', 'feedbackform.html');
$f3->set("contact_id", $contact_id);
$arguments = Api::get_arguments();
$f3->set('arguments', $arguments);
$f3->set('block_content', 'feedbackform.html');
}
//POST
elseif ($f3->get('VERB') == 'POST'){
// Create the call
// Generate a jwt token
$token = JWT::encode(array('api' => JWT_TOKEN, 'nbf' => time() - 5 , 'exp' => time() + 10 * 60, 'iat' => time()), JWT_KEY);
// To get the callee, we have the callee_id in the form. Using that
// to load the callee and retrieve its number.
$contact = Api::get_contact($args['id']);
$contact = Api::get_contact($args['contact_id']);
$f3->set('contact', $contact);
$data = array('api' => JWT_TOKEN,
'caller' => $f3->get('POST.phone'),
'callee' => $contact['phone'],
'token' => $token);
// We want to generate a UNIQUE-ID (doesn't need to be cryptogaphically unique though
$call_id = str_replace('.', '-', uniqid('', true));
$f3->set('CALL_ID', $call_id);
// Create the call
$client = new GuzzleHttp\Client(['base_uri' => SIP_API]);
try {
$res = $client->post("calls/$call_id", ['query' => $data]);
$f3->set('call', GuzzleHttp\Psr7\copy_to_string($res->getBody()));
$f3->set('VERB', 'GET');
$this->feedbackform($f3, $args);
} catch (RequestException $e) {
$block_content = Psr7\str($e->getRequest());
if ($e->hasResponse()) {
$block_content .= Psr7\str($e->getResponse());
}
$f3->set('block_content', $block_content);
}
}
}
......
......@@ -10,9 +10,9 @@ fi
export CI_PROJECT_NAME=$(basename $CI_PROJECT_DIR)
if [ -z "$CI_BUILD_REPO" ]
if [ -z "$CI_REPOSITORY_URL" ]
then
echo "CI_BUILD_REPO undefined"
echo "CI_REPOSITORY_URL undefined"
exit 1
fi
......@@ -22,7 +22,7 @@ then
cd $CI_PROJECT_NAME
git pull origin master
else
git clone $CI_BUILD_REPO $CI_PROJECT_NAME
git clone $CI_REPOSITORY_URL $CI_PROJECT_NAME
cd $CI_PROJECT_NAME
fi
......
#: templates/campaign.html:5 templates/intro.html:11
msgid "Act Now!"
msgstr ""
#: templates/campaign.html:12 templates/intro.html:23
msgid "Visit Us!"
msgstr ""
#: templates/feedbackform.html:4
msgid "0. Initialising the call"
msgstr ""
#: templates/feedbackform.html:5
msgid "1. Calling you"
msgstr ""
#: templates/feedbackform.html:6
msgid "2. When ready, please dial 1 on your phone"
msgstr ""
#: templates/feedbackform.html:7
msgid "3. Calling "
msgstr ""
#: templates/feedbackform.html:8
msgid "4. Connected to "
msgstr ""
#: templates/feedbackform.html:27
msgid "Phone number"
msgstr ""
#: templates/feedbackform.html:60
msgid "Feedback Form"
msgstr ""
#: templates/feedbackform.html:61
msgid ""
"Please tell us what happened if you were able to talk to someone. Your "
"feedback is important to us."
msgstr ""
#: templates/footer.html:6
msgid "This campaign is powered by the piphone"
msgstr ""
#: templates/footer.html:14
msgid "Get in touch with LQDN"
msgstr ""
#: templates/footer.html:15
msgid "Code licensed under the AGPL v3.0+"
msgstr ""
#: templates/footer.html:7
msgid ""
"The piphone is a campaigning tool developped by <a href=\"https://www."
"laquadrature.net/\" class=\"blue-grey-text text-darken-2\">La Quadrature du "
"Net</a>"
msgstr ""
#: templates/contact.html:30 templates/home.html:36
msgid "Call for free"
msgstr ""
#: templates/contact.html:36
msgid "Your phone number:"
msgstr ""
#: templates/contact.html:46
msgid "More info"
msgstr ""
#: templates/contact.html:33
msgid ""
"If you want to call for free, you must provide us with your phone number "
msgstr ""
#: templates/toolbar.html:4
msgid "Act"
msgstr ""
#: templates/toolbar.html:5
msgid "About the campaign"
msgstr ""
#: templates/toolbar.html:6
msgid "About the piphone"
msgstr ""
#: templates/sidebar.html:18
msgid "Home"
msgstr ""
#: templates/sidebar.html:19
msgid "Choose a representative to call"
msgstr ""
#: templates/sidebar.html:20 templates/argumentation.html:3
msgid "Arguments"
msgstr ""
#: templates/argumentation.html:5
msgid ""
"Those are arguments we provide you to help you convince the representative "
"you will call."
msgstr ""
#: templates/call.html:6
msgid "Reach out to "
msgstr ""
#: templates/call.html:11
msgid "Call at your expense"
msgstr ""
#: templates/call.html:12
msgid ""
"Here is the number of the selected MEP - you can either dial it from your "
"phone or push the button if any VoIP client is installed on your device."
msgstr ""
#: templates/thankyou.html:4
msgid "Thank you for your call! &lt;3"
msgstr ""
#: templates/thankyou.html:6
msgid "Call another representative"
msgstr ""
#: templates/piphone.html:4
msgid "What is the piPhone?"
msgstr ""
#: templates/piphone.html:7
msgid ""
"The piphone is a tool developped by <a href=\"https://www.laquadrature.net"
"\">La Quadrature du Net</a>, a French NGO specialised in digital rights "
"issues."
msgstr ""
#: templates/piphone.html:8
msgid ""
"Its purpose is to ease mobilisation of people toward influencing the "
"representatives elements of democracy, by removing the cost of such calls "
"and gathering all needed data."
msgstr ""
#: templates/piphone.html:11
msgid ""
"The source code is distributed under the term of the AGPL v3.0+ and is open "
"to contribution on <a href=\"https://git.laquadrature.net\">LQDN's own "
"gitlab</a>"
msgstr ""
#: templates/home.html:5
msgid "Call a representative and make your voice heard!"
msgstr ""
#: templates/home.html:40
msgid "We need your phone number to initiate the call"
msgstr ""
#: templates/campaign.html:5 templates/intro.html:11
msgid "Act Now!"
msgstr ""
#: templates/campaign.html:12 templates/intro.html:23
msgid "Visit Us!"
msgstr ""
#: templates/feedbackform.html:4
msgid "0. Initialising the call"
msgstr ""
#: templates/feedbackform.html:5
msgid "1. Calling you"
msgstr ""
#: templates/feedbackform.html:6
msgid "2. When ready, please dial 1 on your phone"
msgstr ""
#: templates/feedbackform.html:7
msgid "3. Calling "
msgstr ""
#: templates/feedbackform.html:8
msgid "4. Connected to "
msgstr ""
#: templates/feedbackform.html:27
msgid "Phone number"
msgstr ""
#: templates/feedbackform.html:60
msgid "Feedback Form"
msgstr ""
#: templates/feedbackform.html:61
msgid ""
"Please tell us what happened if you were able to talk to someone. Your "
"feedback is important to us."
msgstr ""
#: templates/footer.html:6
msgid "This campaign is powered by the piphone"
msgstr ""
#: templates/footer.html:14
msgid "Get in touch with LQDN"
msgstr ""
#: templates/footer.html:15
msgid "Code licensed under the AGPL v3.0+"
msgstr ""
#: templates/footer.html:7
msgid ""
"The piphone is a campaigning tool developped by <a href=\"https://www."
"laquadrature.net/\" class=\"blue-grey-text text-darken-2\">La Quadrature du "
"Net</a>"
msgstr ""
#: templates/contact.html:30 templates/home.html:36
msgid "Call for free"
msgstr ""
#: templates/contact.html:36
msgid "Your phone number:"
msgstr ""
#: templates/contact.html:46
msgid "More info"
msgstr ""
#: templates/contact.html:33
msgid ""
"If you want to call for free, you must provide us with your phone number "
msgstr ""
#: templates/toolbar.html:4
msgid "Act"
msgstr ""
#: templates/toolbar.html:5
msgid "About the campaign"
msgstr ""
#: templates/toolbar.html:6
msgid "About the piphone"
msgstr ""
#: templates/sidebar.html:18
msgid "Home"
msgstr ""
#: templates/sidebar.html:19
msgid "Choose a representative to call"
msgstr ""
#: templates/sidebar.html:20 templates/argumentation.html:3
msgid "Arguments"
msgstr ""
#: templates/argumentation.html:5
msgid ""
"Those are arguments we provide you to help you convince the representative "
"you will call."
msgstr ""
#: templates/call.html:6
msgid "Reach out to "
msgstr ""
#: templates/call.html:11
msgid "Call at your expense"
msgstr ""
#: templates/call.html:12
msgid ""
"Here is the number of the selected MEP - you can either dial it from your "
"phone or push the button if any VoIP client is installed on your device."
msgstr ""
#: templates/thankyou.html:4
msgid "Thank you for your call! &lt;3"
msgstr ""
#: templates/thankyou.html:6
msgid "Call another representative"
msgstr ""
#: templates/piphone.html:4
msgid "What is the piPhone?"
msgstr ""
#: templates/piphone.html:7
msgid ""
"The piphone is a tool developped by <a href=\"https://www.laquadrature.net"
"\">La Quadrature du Net</a>, a French NGO specialised in digital rights "
"issues."
msgstr ""
#: templates/piphone.html:8
msgid ""
"Its purpose is to ease mobilisation of people toward influencing the "
"representatives elements of democracy, by removing the cost of such calls "
"and gathering all needed data."
msgstr ""
#: templates/piphone.html:11
msgid ""
"The source code is distributed under the term of the AGPL v3.0+ and is open "
"to contribution on <a href=\"https://git.laquadrature.net\">LQDN's own "
"gitlab</a>"
msgstr ""
#: templates/home.html:5
msgid "Call a representative and make your voice heard!"
msgstr ""
#: templates/home.html:40
msgid "We need your phone number to initiate the call"
msgstr ""
......@@ -8,7 +8,7 @@
<repeat group="{{ @arguments }}" value="{{ @argument }}" counter="{{ @key }}">
<li>
<div class="collapsible-header">{{ @argument.title}}</div>
<div class="collapsible-body"><span>{{ @argument.text }}</span></div>
<div class="collapsible-body">{{ @argument.text | raw }}</div>
</li>
</repeat>
</ul>
......
<div class="modal fade" tabindex="-1" role="dialog" aria-labelled-by="callModalLabel" id="callModal">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="callModalLabel">{{ _("Reach out to ");}} {{ @contact.first_name }}&nbsp;{{ @contact.last_name }}</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-sm-6">
<h2>{{ _("Call for free") }}</h2>
<form method="post" action="/call/{{ @contact.id }}">
<input type="text" id="contact_id" name="contact_id" hidden="hidden" value="{{ @contact.id }}">
<p>{{ _("If you want to call for free, you must provide us with your phone number (the PiPhone will call that number to initiate the communication).") }}</p>
<p>
<label for="phone">{{_("Your phone number:") }}</label>
<input type="text" name="phone" id="phone" placeholder="+33123456789001" />
</p>
<p>
{{ _('Starting with your <a href="https://en.wikipedia.org/wiki/List_of_country_calling_codes#Zones_3-4:_Europe">country code</a>, without the initial 0') }}
</p>
<input class="btn btn-default" type="submit" value="{{ _("I'm ready, call me") }}" />
</form>
</div>
<div class="col-sm-6">
<h2>{{ _("Call at your expense") }}</h2>
<p>{{ _("If you don't want to call for free, here is the number of the current MEP - you can either dial it from your phone or push the button if any VoIP client is installed on your device.") }}</p>
<a class="btn btn-default" href="tel:{{ @contact.phone }}" target="_blank">☎ {{ @contact.phone }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
......@@ -27,24 +27,12 @@
<span class="card-title grey-text text-darken-4">{{ @contact.first_name }} {{ @contact.last_name }}
<i class="material-icons right">close</i>
</span>
<h5>{{ _("Call for free") }}</h5>
<h5>{{ _("Call this representative" }}</h5>
<form method="post" action="/call/{{ @contact.id }}">
<input type="text" id="contact_id" name="contact_id" hidden="hidden" value="{{ @contact.id }}">
<p>{{ _("If you want to call for free, you must provide us with your phone number
(the PiPhone will call that number to initiate the communication).") }}</p>
<div class="input-field col s12">
<label for="phone">{{_("Your phone number:") }}</label>
<input type="text" name="phone" id="phone" />
</div>
<p>
{{ _('Starting with your <a href="https://en.wikipedia.org/wiki/List_of_country_calling_codes#Zones_3-4:_Europe">country code</a>, without the initial 0') }}
</p>
<input class="btn btn-default" type="submit" value="{{ _('Call me') }}" />
<a type="button" href="tel:{{ @contact.phone }}" target="_blank">{{ @contact.phone }}</a>
<input class="btn btn-default" type="submit" value="{{ _('Make the call') }}" />
</form>
</div>
<div class="card-action">
<a type="button" href="#">{{ _("More info") }}</a>
<a type="button" href="tel:{{ @contact.phone }}" target="_blank">{{ @contact.phone }}</a>
</div>
</div>
<!-- contact page -->
......@@ -9,9 +9,10 @@
<repeat group="{{ @group_types }}" value="{{ @group_type }}">
<div class="input-field">
<select class="filter" id="{{ @group_type.name }}" name="{{ @group_type.name }}">
<option value="-1">-- {{ @group_type.name }}</option>
<repeat group="{{ @groups }}" value="{{ @group }}">
<check if="{{ @group.type == @group_type.name }}">
<option value="{{ @group.id }}">{{ @group.name }}</option>
<option value="{{ @group.id }}"> {{ @group.name }}</option>
</check>
</repeat>
</select>
......@@ -39,16 +40,15 @@ $(document).ready(function() {
});
$('select.filter').change(function() {
var filter_group = $(this).val();
contacts = contacts.filter(function(contact) {
var keep = false
contact['groups'].forEach(function(group) {
if (group['id'] === Number(filter_group)) {
keep = true;
};
});
return keep;
});
// We must get the contact list as it is before apllying this specific filter
// Meaning, the full contact list plus all the other filter.
// We just need to call it on all the select elements.
contacts = JSON.parse(contacts_json);
$('select.filter').each(function() {
var filter = $(this);
var filter_group = $(this).val();
contacts = contacts.filter(filter_mep, filter);
});
list_meps(contacts, wrapper);
});
......@@ -69,6 +69,20 @@ $(document).ready(function() {
$('select').material_select();
});
function filter_mep(contact) {
var filter_group = $(this).val();
if (Number(filter_group) === -1) {
return true
};
var keep = false
contact['groups'].forEach(function(group) {
if (group['id'] === Number(filter_group)) {
keep = true;
};
});
return keep;
}
function random_mep() {
var random = Math.floor((Math.random() * $('.collection-item').length));
update_mep($('.collection-item').eq(random));
......
<!-- Let's split this page in two -->
<div class="row green accent-4">
<div class="col s12">
<h4 id="step_init" class="step_text bold center white-text">{{ _("0. Initialising the call") }}</h5>
<h4 id="step_caller" class="step_text bold center white-text">{{ _("1. Calling you") }}</h5>
<h4 id="step_wait" class="step_text bold center white-text">{{ _("2. When ready, please dial 1 on your phone") }}</h3>
<h4 id="step_callee" class="step_text bold center white-text">{{ _("3. Calling ")}}{{ @contact.full_name }}</h5>
<h4 id="step_connected" class="step_text bold center white-text">{{ _("4. Connected to ") }}{{ @contact.full_name }}</h5>
</div>
</div>
<div class="row">
<div class="container">
<div class="col m6 s12">
......@@ -68,79 +58,3 @@
</div>
</div>
</div>
<!-- Let's include a script -->
<script type="text/javascript">
var call = JSON.parse('{{ @call | raw }}');
var call_url = call['data']['url'];
var interval = '';
function normalize_phone(phone) {
if (phone.startsWith('+')) {
return phone.replace('+', '00');
}
return phone;
}
function update_call(response) {
var call_history = response['data']['history'];
console.log(call_history);
var call_state = call_history.sort(function(a, b) {
var date1 = new Date(a[1]);
var date2 = new Date(b[1]);
return date2 - date1;
})[0];
$('.step_text').hide();
if (call_state[0].startsWith('Created:Init')) {
$('#step_init').show();
};
if (call_state[0].startsWith('Ringing:')) {
phone = normalize_phone(call_state[0].split(':')[1]);
caller = normalize_phone(response['data']['caller']);
callee = normalize_phone(response['data']['callee']);
if ( phone === caller) {
$('#step_caller').show();
} else {
$('#step_callee').show();
};
};
if (call_state[0].startsWith('Up:')) {
// Need to check if we're caller or callee
phone = normalize_phone(call_state[0].split(':')[1]);
caller = normalize_phone(response['data']['caller']);
callee = normalize_phone(response['data']['callee']);
if ( phone === caller) {
$('#step_wait').addClass('bold');
$('#step_wait').addClass('text-darken-4');
$('#step_wait').removeClass('light');
$('#step_wait').removeClass('text-lighten-2');
} else {
$('#step_connected').addClass('bold');
$('#step_connected').addClass('text-darken-4');
$('#step_connected').removeClass('light');
$('#step_connected').removeClass('text-lighten-2');
};
};
if (call_state[0].startsWith('Down:')) {
// The call as ended
clearInterval(interval);
};
console.log(call_state[0]);
};
function fetch() {
$.ajax({
method: 'GET',
url: call_url,
dataType: 'json',
complete: function(xhr, status) {
update_call(xhr.responseJSON);
}
});
};
$(document).ready(function() {
update_call(call);
interval = window.setInterval(fetch, 3000);
$('select').material_select();
});
</script>
......@@ -33,17 +33,11 @@
<span id="contact-full_name-2" class="card-title grey-text text-darken-4">John Doe
<i class="material-icons">close</i>
</span>
<h5>{{ _("Call for free") }}</h5>
<h5>{{ _("Call this representative") }}</h5>
<form id="form-contact" method="post" action="">
<input type="text" id="contact_id" name="contact_id" hidden="hidden" value="">
<div class="input-field col s12">
<label for="phone">{{ _("We need your phone number to initiate the call") }}</label>
<input type="text" name="phone" required id="phone" />
</div>
<p>
{{ _('Starting with your <a href="https://en.wikipedia.org/wiki/List_of_country_calling_codes#Zones_3-4:_Europe">country code</a>, without the initial 0') }}
</p>
<input class="btn btn-default" type="submit" value="{{ _('Call me') }}" />
<p>{{ _("When you are ready to call this representative, go on the arguments and feedback page to find some help and talking points.") }}</p>
<input class="btn btn-default" type="submit" value="{{ _("I'm ready to call") }}" />
</form>
</div>
</div>
......
......@@ -5,21 +5,21 @@
</div>
<div class="row">
<div class="col s12 m6">
<p class="flow-text blue-grey-text text-lighten-4">
{{ @campaign.description }}
</p>
<div class="flow-text blue-grey-text text-lighten-4">
{{ @campaign.description | raw }}
</div>
<a href="/#navbar" class="btn waves-effect waves-light leat accent-2 right">{{ _("Act Now!") }}</a>
</div>
<div class="col s12 m6">
<check if="{{ @organization.logo }}">
<img src="{{ @organization.logo }}">
</check>
<h4 class="light header blue-grey-text text-darken-2">
<check if="{{ @organization.logo }}">
<img src="{{ @organization.logo }}">
</check>
{{ @organization.name }}
</h4>
<p class="flow-text blue-grey-text text-lighten-4">
{{ @organization.description }}
</p>
<div class="flow-text blue-grey-text text-lighten-4">
{{ @organization.description | raw }}
</div>
<a href="{{ @organization.website }}" class="btn waves-effect waves-light blue accent-2 right">{{ _("Visit Us!") }}</a>
</div>
</div>
......