Commit 3302bfff authored by Aegir user's avatar Aegir user
parents 5c09d935 ee398d14
<?php
class CRM_Volunteer_Angular {
private static $loaded = FALSE;
/**
* @return boolean
*/
public static function isLoaded() {
return self::$loaded;
}
/**
* Loads dependencies for CiviVolunteer Angular app.
*
* @param string $defaultRoute
* If the base page is loaded with no route, show this one.
*/
public static function load($defaultRoute) {
if (self::isLoaded()) {
return;
}
CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'packages/jquery/plugins/jquery.notify.min.js', 10, 'html-header');
$loader = new \Civi\Angular\AngularLoader();
$loader->setModules(array('volunteer'));
$loader->setPageName('civicrm/vol');
$loader->load();
\Civi::resources()->addSetting(array(
'crmApp' => array(
'defaultRoute' => $defaultRoute,
),
));
self::$loaded = TRUE;
}
}
<?php
class CRM_Volunteer_Angular_Tab_Event extends CRM_Core_Page {
/**
* Initializes a placeholder volunteer project.
*
* Used in cases where no project yet exists for the event, to prepopulate the
* "create" form.
*
* @return \CRM_Volunteer_BAO_Project
*/
protected static function initializeProject($eventId) {
$project = new CRM_Volunteer_BAO_Project();
$project->id = 0;
$project->entity_id = $eventId;
$project->entity_table = CRM_Event_DAO_Event::$_tableName;
return $project;
}
/**
* Sets the stage for the CiviVolunteer Angular app to be loaded in a tab.
*
* Called from hook_civicrm_tabset().
*
* @param int|string $eventId
*/
public static function prepareTab($eventId) {
CRM_Core_Region::instance('page-footer')->add(array(
'template' => 'CRM/Volunteer/Page/Angular.tpl',
));
$project = current(CRM_Volunteer_BAO_Project::retrieve(array(
'entity_id' => $eventId,
'entity_table' => CRM_Event_DAO_Event::$_tableName,
)));
if (!$project) {
$project = self::initializeProject($eventId);
}
CRM_Volunteer_Angular::load('/volunteer/manage/' . $project->id);
$event = $project->getEntityAttributes();
$entityTitle = $event['title'];
CRM_Core_Resources::singleton()
->addStyleFile('org.civicrm.volunteer', 'css/volunteer_app.css')
->addStyleFile('org.civicrm.volunteer', 'css/volunteer_events.css')
->addVars('org.civicrm.volunteer', array(
'hash' => '#/volunteer/manage/' . $project->id,
'projectId' => $project->id,
'entityTable' => $project->entity_table,
'entityId' => $project->entity_id,
'entityTitle' => $entityTitle,
'context' => 'eventTab',
));
}
}
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.4 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2013 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
class CRM_Volunteer_Form_Manage {
/**
* Load needed JS, CSS and settings for the backend Volunteer Management UI
*/
public static function addResources($entity_id, $entity_table) {
static $loaded = FALSE;
$ccr = CRM_Core_Resources::singleton();
if ($loaded || $ccr->isAjaxMode()) {
return;
}
$loaded = TRUE;
$config = CRM_Core_Config::singleton();
// Vendor libraries
$ccr->addScriptFile('civicrm', 'packages/backbone/json2.js', 100, 'html-header', FALSE);
$ccr->addScriptFile('civicrm', 'packages/backbone/backbone-min.js', 120, 'html-header', FALSE);
$ccr->addScriptFile('civicrm', 'packages/backbone/backbone.marionette.min.js', 125, 'html-header', FALSE);
// Our stylesheet
$ccr->addStyleFile('org.civicrm.volunteer', 'css/volunteer_app.css');
// Add all scripts for our js app
$weight = 0;
$baseDir = CRM_Extension_System::singleton()->getMapper()->keyToBasePath('org.civicrm.volunteer') . '/';
// This glob pattern will recurse the js directory up to 4 levels deep
foreach (glob($baseDir . 'js/backbone/{*,*/*,*/*/*,*/*/*/*}.js', GLOB_BRACE) as $file) {
$fileName = substr($file, strlen($baseDir));
$ccr->addScriptFile('org.civicrm.volunteer', $fileName, $weight++);
}
// Add our template
CRM_Core_Smarty::singleton()->assign('isModulePermissionSupported',
CRM_Core_Config::singleton()->userPermissionClass->isModulePermissionSupported());
CRM_Core_Region::instance('page-header')->add(array(
'template' => 'CRM/Volunteer/Form/Manage.tpl',
));
// Fetch event so we can set the default start time for needs
// FIXME: Not the greatest for supporting non-events
$entity = civicrm_api3(str_replace('civicrm_', '', $entity_table), 'getsingle', array('id' => $entity_id));
// Static variables
$ccr->addSetting(array(
'pseudoConstant' => array(
'volunteer_need_visibility' => array_flip(CRM_Volunteer_BAO_Need::buildOptions('visibility_id', 'validate')),
'volunteer_role' => CRM_Volunteer_BAO_Need::buildOptions('role_id', 'get'),
'volunteer_status' => CRM_Activity_BAO_Activity::buildOptions('status_id', 'validate'),
),
'volunteer' => array(
'default_date' => CRM_Utils_Array::value('start_date', $entity),
),
'config' => array(
'timeInputFormat' => $config->timeInputFormat,
),
'constants' => array(
'CRM_Core_Action' => array(
'NONE' => 0,
'ADD' => 1,
'UPDATE' => 2,
'VIEW' => 4,
'DELETE' => 8,
'BROWSE' => 16,
'ENABLE' => 32,
'DISABLE' => 64,
'EXPORT' => 128,
'BASIC' => 256,
'ADVANCED' => 512,
'PREVIEW' => 1024,
'FOLLOWUP' => 2048,
'MAP' => 4096,
'PROFILE' => 8192,
'COPY' => 16384,
'RENEW' => 32768,
'DETACH' => 65536,
'REVERT' => 131072,
'CLOSE' => 262144,
'REOPEN' => 524288,
'MAX_ACTION' => 1048575,
),
),
));
// Check for problems
_volunteer_checkResourceUrl();
}
}
......@@ -34,150 +34,11 @@
*
*/
/**
* This class generates form components for processing Event
*
*/
class CRM_Volunteer_Form_Volunteer extends CRM_Event_Form_ManageEvent {
/**
* The ID of the entity (in case, the event) associated with the volunteer project.
*
* @var int
* @see getEntityId()
*/
private $entityId = NULL;
/**
* A flag used to indicate whether or not Angular should be loaded.
*
* @var boolean
* @see isLoadingTabContent()
*/
private $loadAngular = FALSE;
/**
* The project the form is acting on
*
* @var mixed CRM_Volunteer_BAO_Project if a project has been set, else boolean FALSE
*/
private $_project;
/**
* Returns the ID of the entity (in this case, the event) associated with the volunteer project.
*
* @return int
*/
protected function getEntityId() {
if ($this->entityId === NULL) {
$this->entityId = $this->_id ? $this->_id : CRM_Utils_Request::retrieve('id', 'Positive', $this, TRUE);
}
return $this->entityId;
}
/***
* Get the civiVolunteer Project for this Event.
* CAUTION: Returns only the first if there are multiple.
*
* @returns $project CRM_Volunteer_BAO_Project
*/
protected function getProject() {
if ($this->_project === NULL) {
$this->_project = current(CRM_Volunteer_BAO_Project::retrieve(array(
'entity_id' => $this->getEntityId(),
'entity_table' => CRM_Event_DAO_Event::$_tableName,
)));
}
return $this->_project;
}
/**
* Initializes a placeholder volunteer project.
*
* Used in cases where no project yet exists for the event, to prepopulate the
* "create" form.
*
* @return \CRM_Volunteer_BAO_Project
*/
protected function initializeProject() {
$project = new CRM_Volunteer_BAO_Project();
$project->id = 0;
$project->entity_id = $this->getEntityId();
$project->entity_table = CRM_Event_DAO_Event::$_tableName;
return $project;
}
/**
* Set variables up before form is built.
*
* @return void
*/
public function preProcess() {
if ($this->isLoadingTabContent()) {
$this->loadAngular = TRUE;
$project = $this->getProject();
if (!$project) {
$project = $this->initializeProject();
}
$entity = $project->getEntityAttributes();
$entityTitle = $entity['title'];
CRM_Core_Resources::singleton()->addVars('org.civicrm.volunteer', array(
"hash" => "#/volunteer/manage/" . $project->id,
"projectId" => $project->id,
"entityTable" => $project->entity_table,
"entityId" => $project->entity_id,
"entityTitle" => $entityTitle,
"context" => 'eventTab',
));
} else {
parent::preProcess();
}
}
/**
* Distinguishes between different invocations of the form class.
*
* It is possible for this form class to be loaded twice in what the user
* perceives as a single page load. If the Event tabset is not already loaded
* (i.e., the user clicks Configure > Volunteers from the Manage Events
* screen), then the class is called twice: the first time to defer to its
* parent for the construction of the event tabset; the second time via AJAX
* to load content into the frame for the Volunteer tab.
*
* @return boolean
*/
private function isLoadingTabContent() {
return CRM_Utils_Request::retrieve('snippet', 'String') === "json";
}
/**
* Build the form object
*
* @return None
* @access public
*/
public function buildQuickForm() {
if ($this->loadAngular) {
$ang = new CRM_Volunteer_Page_Angular(null, null, CRM_Core_Resources::singleton());
$ang->registerResources('ajax-snippet', false);
CRM_Core_Resources::singleton()->addScriptFile('org.civicrm.volunteer', 'js/CRM_Volunteer_Form_Volunteer.js', -1000, 'ajax-snippet');
CRM_Core_Resources::singleton()->addStyleFile('org.civicrm.volunteer', 'css/volunteer_events.css');
// Low weight, go before all the other Angular scripts. The trick is only needed in snippet mode.
CRM_Core_Resources::singleton()->addScript("CRM.origJQuery = window.jQuery; window.jQuery = CRM.$;", -1001, 'ajax-snippet');
//High weight, go after all the other Angular scripts. The trick is only needed in snippet mode.
CRM_Core_Resources::singleton()->addScript("window.jQuery = CRM.origJQuery; delete CRM.origJQuery", 1000, 'ajax-snippet');
} else {
parent::buildQuickForm();
}
CRM_Core_Resources::singleton()->addScriptFile('org.civicrm.volunteer', 'js/CRM_Volunteer_Form_Volunteer.js', -1000, 'ajax-snippet');
parent::preProcess();
}
}
......@@ -3,18 +3,10 @@
class CRM_Volunteer_Page_Angular extends \CRM_Core_Page {
public function run() {
CRM_Core_Resources::singleton()->addScriptFile('civicrm', 'packages/jquery/plugins/jquery.notify.min.js', 10, 'html-header');
$loader = new \Civi\Angular\AngularLoader();
$loader->setModules(array('volunteer'));
$loader->setPageName('civicrm/vol');
$loader->load();
\Civi::resources()->addSetting(array(
'crmApp' => array(
'defaultRoute' => '/volunteer/manage',
),
CRM_Core_Region::instance('page-footer')->add(array(
'template' => 'CRM/common/notifications.tpl',
));
CRM_Volunteer_Angular::load('/volunteer/manage');
parent::run();
}
......
......@@ -5,10 +5,6 @@ class CRM_Volunteer_Page_Backbone extends CRM_Core_Page {
// Add our template
CRM_Core_Smarty::singleton()->assign('isModulePermissionSupported',
CRM_Core_Config::singleton()->userPermissionClass->isModulePermissionSupported());
CRM_Core_Region::instance('page-header')->add(array(
// TODO: consider renaming this TPL
'template' => 'CRM/Volunteer/Form/Manage.tpl',
));
parent::run();
}
......
......@@ -34,7 +34,7 @@
<div class="crm-vol-campaign" crm-ui-field='{name: "projectForm.campaign_id", title: ts("Campaign")}'>
<select class="big crm-form-select crm-vol-campaign" crm-ui-select="{placeholder: '', allowClear:true}"
ng-options="key as value.title for (key , value) in campaigns" ng-model="project.campaign_id">
ng-options="key as value.title for (key , value) in campaigns track by key" ng-model="project.campaign_id">
<option />
</select>
</div>
......@@ -42,7 +42,7 @@
<div class="crm-vol-location" crm-ui-field='{name: "projectForm.locBlock", title: ts("Location")}'>
<select class="big crm-form-select crm-vol-location-block-id"
crm-ui-select="{placeholder: '', allowClear:true}"
ng-options="key as value for (key , value) in locationBlocks" ng-model="project.loc_block_id">
ng-options="key as value for (key , value) in locationBlocks track by key" ng-model="project.loc_block_id">
<option />
</select>
<div id="crm-vol-location-block" class="crm-vol-extra-top" crm-ui-accordion="{title: ts('Edit Location'), collapsed: project.loc_block_id !==0}" ng-hide="!project.loc_block_id">
......@@ -73,7 +73,7 @@
crm-ui-select="{allowClear: false}"
ng-change="locBlockDirty()"
ng-model="locBlock.address.country_id"
ng-options="k as v.name for (k, v) in countries">
ng-options="k as v.name for (k, v) in countries track by k">
</select>
</div>
</fieldset>
......
......@@ -131,6 +131,7 @@
switch ($scope.formContext) {
case 'eventTab':
volBackbone.load();
var cancelCallback = function (projectId) {
CRM.$("body").trigger("volunteerProjectCancel");
};
......
/* Rendered on load of the tabset and should remain hidden initially. */
#crm_volunteer_angular_frame {
display: none;
}
.crm-volunteer-event-action-items-all {
margin: 10px 5px;
}
......
......@@ -8,8 +8,8 @@
<author>Ginkgo Street Labs</author>
<email>inquire@ginkgostreet.com</email>
</maintainer>
<releaseDate>2017-07-30</releaseDate>
<version>4.7.21-2.2.2-beta1</version>
<releaseDate>2017-09-10</releaseDate>
<version>4.7.21-2.2.2-beta2</version>
<compatibility>
<ver>4.7</ver>
</compatibility>
......
CRM.$(function($) {
//Defer loading of Angular
//Because we are loading Angular through an ajax interface
//we need to defer loading until everything is ready.
//The batarang module employs this method to defer loading
//see: https://docs.angularjs.org/guide/bootstrap
//maybeBootstrap modified from that found at: https://github.com/angular/batarang
var DEFER_LABEL = 'NG_DEFER_BOOTSTRAP!';
window.name = DEFER_LABEL + window.name;
function maybeBootstrap() {
if (typeof angular === 'undefined' || !angular.resumeBootstrap) {
return setTimeout(maybeBootstrap, 1);
}
window.name = window.name.substring(DEFER_LABEL.length);
angular.resumeBootstrap();
}
maybeBootstrap();
CRM.$(function ($) {
// Moving a CKEditor instance breaks it because of its use of iframes
// (https://stackoverflow.com/a/28650844); so we must destroy and re-create it
var description = $('#crm-vol-form-textarea-wrapper textarea');
CRM.wysiwyg.destroy(description);
if (CRM.vars['org.civicrm.volunteer'].hash) {
location.hash = CRM.vars['org.civicrm.volunteer'].hash;
}
var angFrame = $('#crm_volunteer_angular_frame');
$('.crm-volunteer-event-action-items-all').after(angFrame);
CRM.wysiwyg.create(description);
angFrame.show();
//Handle Project Save
// Handle Project Save
$("body").on("volunteerProjectSaveComplete", function(event, projectId) {
......@@ -36,22 +24,22 @@ CRM.$(function($) {
CRM.vars['org.civicrm.volunteer'].projectId = projectId;
CRM.vars['org.civicrm.volunteer'].hash = CRM.vars['org.civicrm.volunteer'].hash.replace(/[0-9]*$/, projectId);
//Hide the editor frame
$("#crm_volunteer_angular_frame").slideUp();
// Hide the editor frame
angFrame.slideUp();
//show the "edit" button
$("#crm-volunteer-event-edit").fadeIn();
});
//Handle Project Edit Cancel
// Handle Project Edit Cancel
$("body").on("volunteerProjectCancel", function() {
//Hide the editor frame
$("#crm_volunteer_angular_frame").slideUp();
angFrame.slideUp();
//show the "edit" button
$("#crm-volunteer-event-edit").fadeIn();
});
//Wire up the edit settings button
// Wire up the edit settings button
$("#crm-volunteer-event-edit").click(function(event) {
//hide the "edit" button
$("#crm-volunteer-event-edit").fadeOut();
......@@ -61,25 +49,25 @@ CRM.$(function($) {
CRM.$("body").trigger("volunteerProjectRefresh");
//Show the editor frame
$("#crm_volunteer_angular_frame").slideDown();
angFrame.slideDown();
});
//Wire up the define button
// Wire up the define button
$("#crm-volunteer-event-define").click(function(event) {
if (CRM.vars['org.civicrm.volunteer'].projectId != 0) {
CRM.volunteerPopup(ts('Define Volunteer Opportunities'), 'Define', CRM.vars['org.civicrm.volunteer'].projectId);
}
});
//Wire up the assign button
// Wire up the assign button
$("#crm-volunteer-event-assign").click(function(event) {
if (CRM.vars['org.civicrm.volunteer'].projectId != 0) {
CRM.volunteerPopup(ts('Assign Volunteers'), 'Assign', CRM.vars['org.civicrm.volunteer'].projectId);
}
});
//wire up the log Hours button
// wire up the log Hours button
$("#crm-volunteer-event-log-hours").click(function(event) {
if (CRM.vars['org.civicrm.volunteer'].projectId != 0) {
var url = CRM.url("civicrm/volunteer/loghours", "reset=1&action=add&vid=" + CRM.vars['org.civicrm.volunteer'].projectId);
......@@ -105,7 +93,7 @@ CRM.$(function($) {
}
});
//Hide the Edit button by defult
// Hide the Edit button by default
$("#crm-volunteer-event-edit").hide();
if (CRM.vars['org.civicrm.volunteer'].projectId == 0) {
......
{*
+--------------------------------------------------------------------+
| CiviCRM version 4.4 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2013 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*}
{* Contains js templates for backbone-based volunteer management *}
{* Main container for dialog *}
<div id="crm-volunteer-dialog"></div>
{* Container for search dialog *}
<div id="crm-volunteer-search-dialog"></div>
{include file="RenderUtils.tpl"}
{* Template file for each sub application *}
{include file="CRM/Volunteer/Form/Manage/Assign.tpl"}
{include file = "CRM/Volunteer/Form/Manage/Define.tpl"}
{include file = "CRM/Volunteer/Form/Manage/Search.tpl"}
\ No newline at end of file
......@@ -31,4 +31,3 @@
<div class="clear"></div>
</div>
{include file="CRM/Volunteer/Page/Angular.tpl" location="bottom"}
\ No newline at end of file
......@@ -3,5 +3,3 @@
<div ng-view></div>
</div>
{/literal}
{include file="CRM/common/notifications.tpl" location="bottom"}
{*
+--------------------------------------------------------------------+
| CiviCRM version 4.4 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2013 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*}
{* Contains js templates for backbone-based volunteer management *}
{* Main container for dialog *}
<div id="crm-volunteer-dialog"></div>
{* Container for search dialog *}
<div id="crm-volunteer-search-dialog"></div>
{include file="RenderUtils.tpl"}
{* Template file for each sub application *}
{include file="CRM/Volunteer/Page/Backbone/Assign.tpl"}
{include file = "CRM/Volunteer/Page/Backbone/Define.tpl"}
{include file = "CRM/Volunteer/Page/Backbone/Search.tpl"}
\ No newline at end of file
......@@ -30,7 +30,7 @@
<div id="help">
{* VOL-47: The following is on one line intentionally. *}
{ts domain='org.civicrm.volunteer'}Use this form to specify the number of volunteers needed for each role and time slot. If no opportunities are specified, volunteers will be considered to be generally available.{/ts}
{help id="volunteer-define" file="CRM/Volunteer/Form/Manage/Define.hlp" isModulePermissionSupported=`$isModulePermissionSupported`}
{help id="volunteer-define" file="CRM/Volunteer/Page/Backbone/Define.hlp" isModulePermissionSupported=`$isModulePermissionSupported`}
</div>
<form class="crm-block crm-form-block crm-event-manage-volunteer-form-block">
<div id="crm-vol-define-scheduled-needs-region">
......@@ -87,7 +87,7 @@
{ts domain='org.civicrm.volunteer'}Open-Ended{/ts}
</option>
</select>
{help id="volunteer-define-schedule_type" file="CRM/Volunteer/Form/Manage/Define.hlp"}
{help id="volunteer-define-schedule_type" file="CRM/Volunteer/Page/Backbone/Define.hlp"}
</label>
<table class="time_components">
<thead>
......
......@@ -209,14 +209,27 @@ function volunteer_civicrm_xmlMenu(&$files) {
/**
* Implementation of hook_civicrm_tabset
*
* Insert the "Volunteer" tab into the event edit workflow
* Insert the "Volunteer" tab into the event edit workflow and load Angular when
* appropriate.
*/