diff --git a/.travis.yml b/.travis.yml index 2df9353..3ff0fca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,8 @@ php: - 5.3 services: - mysql +env: + - KOHANA_ENV=testing before_script: - composer install --no-interaction --prefer-source # Have to prefer source or hit github rate limit - git submodule update --init --recursive diff --git a/application/bootstrap.php b/application/bootstrap.php index 01252aa..8b69da7 100644 --- a/application/bootstrap.php +++ b/application/bootstrap.php @@ -79,20 +79,29 @@ /** * Set Kohana::$environment if a 'KOHANA_ENV' environment variable has been supplied. * - * Note: If you supply an invalid environment name, a PHP warning will be thrown - * saying "Couldn't find constant Kohana::" + * Note: If you supply an invalid environment name 'development' will be used instead */ -if (isset($_SERVER['KOHANA_ENV'])) +if (($env = getenv('KOHANA_ENV')) === FALSE OR defined('Kohana::'.strtoupper($env)) === FALSE) { - Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV'])); + $env = 'development'; } +// Ignoring code standards error about constant case +// @codingStandardsIgnoreStart +Kohana::$environment = constant('Kohana::'.strtoupper($env)); +// @codingStandardsIgnoreEnd + /** * Attach a file reader to config. Multiple readers are supported. */ Kohana::$config = new Config; Kohana::$config->attach(new Config_File); +/** + * Attach the environment specific configuration file reader to config + */ +Kohana::$config->attach(new Config_File('config/environments/'.$env)); + /** * Initialize Kohana, setting the default options. */ diff --git a/application/classes/Controller/Groups.php b/application/classes/Controller/Groups.php new file mode 100644 index 0000000..4645a8b --- /dev/null +++ b/application/classes/Controller/Groups.php @@ -0,0 +1,110 @@ + + * @package Ushahidi\Application\Controllers + * @copyright Ushahidi - http://www.ushahidi.com + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License Version 3 (GPLv3) + */ +class Controller_Groups extends Controller_PingApp { + + /** + * List all Groups + * + * @return void + */ + public function action_index() + { + $this->template->content = View::factory('pages/groups/list') + ->bind('groups', $groups); + + $groups = $this->user->groups + ->select(array(DB::expr('COUNT(gp.person_id)'), 'people')) + ->join(array('groups_people', 'gp'), 'LEFT') + ->on('gp.group_id', '=', 'group.id') + ->join(array('people', 'p'), 'LEFT') + ->on('p.id', '=', 'gp.person_id') + ->group_by('group.id') + ->order_by('name', 'ASC') + ->find_all(); + } + + /** + * Add/Edit Group + * + * @return void + */ + public function action_edit() + { + $this->template->content = View::factory('pages/groups/edit') + ->bind('group', $group) + ->bind('post', $post) + ->bind('errors', $errors) + ->bind('done', $done); + + $group_id = $this->request->param('id', 0); + $group = ORM::factory('Group') + ->where('id', '=', $group_id) + ->where('user_id', '=', $this->user->id) + ->find(); + + if ( ! empty($_POST) ) + { + $post = $_POST; + + try + { + // Save Group + $group->values($post, array( + 'name', + )); + $group->check(); + $group->user_id = $this->user->id; + $group->save(); + + // Redirect to prevent repost + HTTP::redirect('groups/edit/'.$group->id.'?done'); + } + catch (ORM_Validation_Exception $e) + { + $errors = Arr::flatten($e->errors('models')); + } + } + else + { + if ( $group->loaded() ) + { + $post = $group->as_array(); + } + + $done = (isset($_GET['done'])) ? TRUE : FALSE; + } + } + + /** + * Delete A Group + * + * @return void + */ + public function action_delete() + { + $group_id = $this->request->param('id', 0); + + $group = ORM::factory('Group') + ->where('id', '=', $group_id) + ->where('user_id', '=', $this->user->id) + ->find(); + + if ( $group->loaded() ) + { + $group->delete(); + HTTP::redirect('groups'); + } + else + { + HTTP::redirect('groups'); + } + } +} \ No newline at end of file diff --git a/application/classes/Controller/Person.php b/application/classes/Controller/Person.php index 1c28fa4..fd8b76e 100644 --- a/application/classes/Controller/Person.php +++ b/application/classes/Controller/Person.php @@ -18,12 +18,18 @@ class Controller_Person extends Controller_PingApp { public function action_edit() { $this->template->content = View::factory('pages/person/edit') + ->bind('user', $this->user) + ->bind('groups', $groups) ->bind('person', $person) ->bind('parent', $parent) ->bind('post', $post) ->bind('errors', $errors) ->bind('done', $done); + $groups = $this->user->groups + ->order_by('name', 'ASC') + ->find_all(); + $this->template->footer->js = View::factory('pages/person/js/edit'); $person_id = $this->request->param('id', 0); @@ -54,6 +60,7 @@ public function action_edit() if ( ! empty($_POST) ) { $post = $_POST; + print_r($post); $extra_validation = Validation::factory($post) ->rule('contact', 'not_empty') ->rule('contact', 'is_array') @@ -75,7 +82,28 @@ public function action_edit() $person->user_id = $this->user->id; $person->save(); - // 2. Delete A Contact + // 2. Save Group Info + // Drop relationships first + foreach ($person->groups->find_all() as $group) + { + $person->remove('groups', $group); + } + // Re-Attach Groups + foreach ($post['group'] as $key => $group_id) + { + $group = ORM::factory('Group') + ->where('id', '=', (int) $group_id) + ->where('user_id', '=', $this->user->id) + ->find(); + + if( $group->loaded() AND ! $person->has('groups', $group)) + { + // Add Relationship + $person->add('groups', $group); + } + } + + // 3. Delete A Contact foreach ($post['delete'] as $delete_id) { @@ -87,9 +115,9 @@ public function action_edit() // Remove Relationship $person->remove('contacts', $contact); } - } + } - // 3. Save Contact Info + // 4. Save Contact Info foreach ($post['contact'] as $key => $_contact) { // Clean Contact Before Comparing @@ -134,6 +162,12 @@ public function action_edit() 'name' => $person->name, ); + // Get Person Groups + foreach ($person->groups->find_all() as $group) + { + $post['group'][] = $group->id; + } + // Get Person Contacts foreach ($person->contacts->find_all() as $contact) { diff --git a/application/classes/Model/Group.php b/application/classes/Model/Group.php index 5e8c81a..a7022c2 100644 --- a/application/classes/Model/Group.php +++ b/application/classes/Model/Group.php @@ -14,7 +14,7 @@ class Model_Group extends ORM { * A group has many people */ protected $_has_many = array( - 'people' => array(), + 'people' => array('through' => 'groups_people'), ); /** @@ -27,4 +27,15 @@ class Model_Group extends ORM { // Insert/Update Timestamps protected $_created_column = array('column' => 'created', 'format' => 'Y-m-d H:i:s'); protected $_updated_column = array('column' => 'updated', 'format' => 'Y-m-d H:i:s'); + + public function rules() + { + return array( + 'name' => array( + array('not_empty'), + array('min_length', array(':value', 3)), + array('max_length', array(':value', 150)), + ) + ); + } } \ No newline at end of file diff --git a/application/classes/Model/Person.php b/application/classes/Model/Person.php index 0a61fb3..a9b7a4a 100644 --- a/application/classes/Model/Person.php +++ b/application/classes/Model/Person.php @@ -18,6 +18,7 @@ class Model_Person extends ORM { 'pings' => array(), 'pongs' => array(), 'contacts' => array('through' => 'contacts_people'), + 'groups' => array('through' => 'groups_people'), 'children' => array( 'model' => 'Person', 'foreign_key' => 'parent_id', @@ -25,7 +26,7 @@ class Model_Person extends ORM { ); /** - * A person belongs to a parent, user + * A person belongs to a parent, user and a group */ protected $_belongs_to = array( 'user' => array(), diff --git a/application/classes/PingApp/Form.php b/application/classes/PingApp/Form.php index 9fe98a6..b129a79 100644 --- a/application/classes/PingApp/Form.php +++ b/application/classes/PingApp/Form.php @@ -37,7 +37,7 @@ public static function contact_types($select = TRUE) * Get People for Send Form * * @param int $user_id - * @returnarray + * @return array */ public static function people($user) { diff --git a/application/config/environments/testing/auth.php b/application/config/environments/testing/auth.php new file mode 100644 index 0000000..97d5529 --- /dev/null +++ b/application/config/environments/testing/auth.php @@ -0,0 +1,18 @@ + 'ORM', + 'hash_method' => 'sha256', + 'hash_key' => 'somereallylongkey', + 'lifetime' => 1209600, + 'session_type' => Session::$default, + 'session_key' => 'auth_user', + + // Username/password combinations for the Auth File driver + 'users' => array( + // 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02', + 'admin' => '' + ), + +); diff --git a/application/config/environments/testing/database.php b/application/config/environments/testing/database.php new file mode 100644 index 0000000..b3123c4 --- /dev/null +++ b/application/config/environments/testing/database.php @@ -0,0 +1,29 @@ + + * @package Ushahidi\Application + * @copyright Ushahidi - http://www.ushahidi.com + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License Version 3 (GPLv3) + */ + +return array +( + 'default' => array + ( + 'type' => 'MySQL', + 'connection' => array( + 'hostname' => 'localhost', + 'database' => 'pingapp_test', + 'username' => 'root', + 'password' => '', + 'persistent' => FALSE, + ), + 'table_prefix' => '', + 'charset' => 'utf8', + 'caching' => TRUE, + 'profiling' => TRUE, + ) +); diff --git a/application/migrations/1/20131001183143.php b/application/migrations/1/20131001183143.php new file mode 100644 index 0000000..37e628b --- /dev/null +++ b/application/migrations/1/20131001183143.php @@ -0,0 +1,42 @@ +query(NULL, "DROP TABLE IF EXISTS `groups_people`;"); + $db->query(NULL, "CREATE TABLE `groups_people` ( + `group_id` int(11) unsigned NOT NULL DEFAULT '0', + `person_id` int(11) unsigned NOT NULL, + PRIMARY KEY (`group_id`,`person_id`), + CONSTRAINT `fk_groups_people_group_id` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_groups_people_person_id` FOREIGN KEY (`person_id`) REFERENCES `people` (`id`) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + + // Drop group_id column from people + $db->query(NULL, "ALTER TABLE `people` DROP `group_id`;"); + + } + + /** + * Run queries needed to remove this migration + * + * @param Kohana_Database $db Database connection + */ + public function down(Kohana_Database $db) + { + $db->query(NULL, "ALTER TABLE `people` ADD `group_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `user_id`;"); + $db->query(NULL, "ALTER TABLE `people` ADD INDEX `idx_group_id` (`group_id`);"); + + $db->query(NULL, "DROP TABLE IF EXISTS `groups_people`;"); + } + +} diff --git a/application/tests/classes/models/GroupModelTest.php b/application/tests/classes/models/GroupModelTest.php new file mode 100644 index 0000000..7852841 --- /dev/null +++ b/application/tests/classes/models/GroupModelTest.php @@ -0,0 +1,114 @@ + + * @package Ushahidi\Application\Tests + * @copyright Ushahidi - http://www.ushahidi.com + * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License Version 3 (GPLv3) + */ + +class GroupModelTest extends Unittest_TestCase { + /** + * Provider for test_validate_valid + * + * @access public + * @return array + */ + public function provider_validate_valid() + { + return array( + array( + // Valid group data + array( + 'name' => 'Team Ushahidi', + ) + ), + array( + // Valid group data + array( + 'name' => 'Pirates', + ) + ), + array( + // Valid group data + array( + 'name' => 'Ninjas', + ) + ) + ); + } + + /** + * Provider for test_validate_invalid + * + * @access public + * @return array + */ + public function provider_validate_invalid() + { + return array( + array( + // Invalid group data set 1 - No Data + array() + ), + array( + // Invalid group data set 2 - Invalid Name + array( + 'name' => 'me', + ) + ), + array( + // Invalid group data set 3 - Invalid Name + array( + 'name' => '', + ) + ) + ); + } + + /** + * Test Validate Valid Entries + * + * @dataProvider provider_validate_valid + * @return void + */ + public function test_validate_valid($set) + { + $group = ORM::factory('Group'); + $group->values($set); + + try + { + $group->check(); + } + catch (ORM_Validation_Exception $e) + { + $this->fail('This entry qualifies as invalid when it should be valid: '. json_encode($e->errors('models'))); + } + } + + /** + * Test Validate Invalid Entries + * + * @dataProvider provider_validate_invalid + * @return void + */ + public function test_validate_invalid($set) + { + $group = ORM::factory('Group'); + $group->values($set); + + try + { + $group->check(); + } + catch (ORM_Validation_Exception $e) + { + return; + } + + $this->fail('This entry qualifies as valid when it should be invalid'); + } +} \ No newline at end of file diff --git a/application/views/common/nav.php b/application/views/common/nav.php index 4910eb6..68fbe9b 100644 --- a/application/views/common/nav.php +++ b/application/views/common/nav.php @@ -7,6 +7,7 @@