f06905520353e42994014eb6c24eccc0f532255f
[tine20] / tests / tine20 / Courses / JsonTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Courses
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  * 
10  */
11
12 /**
13  * Test helper
14  */
15 require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
16
17 /**
18  * Test class for Tinebase_Group
19  */
20 class Courses_JsonTest extends PHPUnit_Framework_TestCase
21 {
22     /**
23      * @var Courses_Frontend_Json
24      */
25     protected $_json = array();
26     
27     /**
28      * config groups
29      * 
30      * @var array
31      */
32     protected $_configGroups = array();
33     
34     /**
35      * test department
36      * 
37      * @var Tinebase_Model_Department
38      */
39     protected $_department = NULL;
40     
41     /**
42      * Runs the test methods of this class.
43      *
44      * @access public
45      * @static
46      */
47     public static function main()
48     {
49         $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Courses Json Tests');
50         PHPUnit_TextUI_TestRunner::run($suite);
51     }
52
53     /**
54      * Sets up the fixture.
55      * This method is called before a test is executed.
56      *
57      * @access protected
58      */
59     protected function setUp()
60     {
61         Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
62         
63         $this->_json = new Courses_Frontend_Json();
64         
65         $this->_department = Tinebase_Department::getInstance()->create(new Tinebase_Model_Department(array(
66             'name'  => Tinebase_Record_Abstract::generateUID()
67         )));
68         
69         foreach (array(Courses_Config::INTERNET_ACCESS_GROUP_ON, Courses_Config::INTERNET_ACCESS_GROUP_FILTERED, Courses_Config::STUDENTS_GROUP) as $configgroup) {
70             $this->_configGroups[$configgroup] = Tinebase_Group::getInstance()->create(new Tinebase_Model_Group(array(
71                 'name'   => $configgroup
72             )));
73             Courses_Config::getInstance()->set($configgroup, $this->_configGroups[$configgroup]->getId());
74         }
75         
76         Courses_Config::getInstance()->set(Courses_Config::SAMBA, new Tinebase_Config_Struct(array(
77             'basehomepath' => '\\\\jo\\',
78             'homedrive' => 'X:',
79             'logonscript_postfix_teacher' => '-lehrer.cmd',
80             'logonscript_postfix_member' => '.cmd',
81             'baseprofilepath' => '\\\\jo\\profiles\\',
82         )));
83     }
84
85     /**
86      * Tears down the fixture
87      * This method is called after a test is executed.
88      *
89      * @access protected
90      */
91     protected function tearDown()
92     {
93         Tinebase_TransactionManager::getInstance()->rollBack();
94     }
95     
96     /**
97      * try to add a Course
98      */
99     public function testAddCourse()
100     {
101         $course = $this->_getCourseData();
102         $courseData = $this->_json->saveCourse($course);
103         
104         // checks
105         $this->assertEquals($course['description'], $courseData['description']);
106         $this->assertEquals($course['type'], $courseData['type']['value']);
107         $this->assertEquals(Tinebase_Core::getUser()->getId(), $courseData['created_by'], 'Created by has not been set.');
108         $this->assertTrue(! empty($courseData['group_id']));
109         $this->assertGreaterThan(0, count($courseData['members']));
110         
111         // cleanup
112         $this->_json->deleteCourses($courseData['id']);
113
114         // check if it got deleted
115         $this->setExpectedException('Tinebase_Exception_NotFound');
116         Courses_Controller_Course::getInstance()->get($courseData['id']);
117     }
118     
119     /**
120      * try to get a Course
121      */
122     public function testGetCourse()
123     {
124         $course = $this->_getCourseData();
125         $courseData = $this->_json->saveCourse($course);
126         $courseData = $this->_json->getCourse($courseData['id']);
127         
128         // checks
129         $this->assertEquals($course['description'], $courseData['description']);
130         $this->assertEquals(Tinebase_Core::getUser()->getId(), $courseData['created_by']);
131         
132         // cleanup
133         $this->_json->deleteCourses($courseData['id']);
134     }
135
136     /**
137      * try to update a Course
138      */
139     public function testUpdateCourse()
140     {
141         $course = $this->_getCourseData();
142         $courseData = $this->_json->saveCourse($course);
143
144         // update Course
145         $courseData['description'] = "blubbblubb";
146         $courseData['members'] = array();
147         $courseData['type'] = $courseData['type']['value'];
148         $courseUpdated = $this->_json->saveCourse($courseData);
149         
150         // check
151         $this->assertEquals($courseData['id'], $courseUpdated['id']);
152         $this->assertEquals($courseData['description'], $courseUpdated['description']);
153         $this->assertEquals(Tinebase_Core::getUser()->getId(), $courseUpdated['last_modified_by']);
154         $this->assertEquals($courseData['members'], $courseUpdated['members']);
155         
156         // cleanup
157         $this->_json->deleteCourses($courseData['id']);
158     }
159     
160     /**
161      * try to get a Course
162      */
163     public function testSearchCourses()
164     {
165         // create
166         $course = $this->_getCourseData();
167         $courseData = $this->_json->saveCourse($course);
168         
169         // search & check
170         $search = $this->_json->searchCourses($this->_getCourseFilter($courseData['name']), $this->_getPaging());
171         $this->assertEquals($course['description'], $search['results'][0]['description']);
172         $this->assertEquals(1, $search['totalcount']);
173         
174         // cleanup
175         $this->_json->deleteCourses($courseData['id']);
176     }
177        
178     /**
179      * test for import of members (1)
180      */
181     public function testImportMembersIntoCourse1()
182     {
183         $definition = Tinebase_ImportExportDefinition::getInstance()->getByName('admin_user_import_csv');
184         $result = $this->_importHelper(dirname(dirname(__FILE__)) . '/Admin/files/testHeadline.csv', $definition);
185
186         $this->assertEquals(4, count($result['members']), print_r($result, TRUE));
187     }
188
189     /**
190      * test for import of members (2)
191      */
192     public function testImportMembersIntoCourse2()
193     {
194         $result = $this->_importHelper(dirname(__FILE__) . '/files/import.txt');
195         
196         $this->assertEquals(5, count($result['members']), print_r($result, TRUE));
197         
198         // find philipp lahm
199         $lahm = array();
200         foreach ($result['members'] as $member) {
201             if ($member['name'] == 'Lahm, Philipp') {
202                 $lahm = $member;
203             }
204         }
205         $this->assertTrue(! empty($lahm));
206         $this->assertEquals('lahmph', $lahm['data']);
207         
208         // get user and check email
209         $testConfig = Zend_Registry::get('testConfig');
210         $maildomain = ($testConfig->maildomain) ? $testConfig->maildomain : 'school.org';
211         $user = Tinebase_User::getInstance()->getFullUserById($lahm['id']);
212         $this->assertEquals('lahmph', $user->accountLoginName);
213         $this->assertEquals('lahmph@' . $maildomain, $user->accountEmailAddress);
214         $this->assertEquals('//base/school/' . $result['name'] . '/' . $user->accountLoginName, $user->accountHomeDirectory);
215     }
216     
217     /**
218      * test for import of members (3) / json import
219      */
220     public function testImportMembersIntoCourse3()
221     {
222         $result = $this->_importHelper(dirname(__FILE__) . '/files/import.txt', NULL, TRUE);
223         $this->assertEquals(5, count($result['members']), 'import failed');
224         $this->assertEquals(5, count(Tinebase_Group::getInstance()->getGroupMembers($this->_configGroups[Courses_Config::STUDENTS_GROUP])), 'imported users not added to students group');
225     }
226
227     /**
228      * test for import of members (4) / json import
229      * 
230      * @see 0006672: allow to import (csv) files with only CR linebreaks
231      */
232     public function testImportMembersIntoCourse4()
233     {
234         $result = $this->_importHelper(dirname(__FILE__) . '/files/testklasse.csv', $this->_getCourseImportDefinition2(), TRUE);
235         $this->assertEquals(25, count($result['members']), 'import failed');
236         $found = FALSE;
237         foreach($result['members'] as $member) {
238             if ($member['name'] === 'Kućuk, Orkide' && $member['data'] === 'kuukor') {
239                 $found = TRUE;
240             }
241         }
242         $this->assertTrue($found, 'Member "Kućuk, Orkide" not found in result: ' . print_r($result['members'], TRUE));
243         $this->assertEquals(25, count(Tinebase_Group::getInstance()->getGroupMembers($this->_configGroups[Courses_Config::STUDENTS_GROUP])), 'imported users not added to students group');
244     }
245
246     /**
247      * test for import of members (5) / json import
248      * 
249      * @see 0006942: group memberships and login shell missing for new users
250      */
251     public function testImportMembersIntoCourse5()
252     {
253         $result = $this->_importHelper(dirname(__FILE__) . '/files/tah2a.txt', $this->_getCourseImportDefinition3('iso-8859-1'), TRUE);
254         $this->assertEquals(3, count($result['members']), 'import failed');
255         
256         // check group memberships
257         $userId = NULL;
258         foreach ($result['members'] as $result) {
259             if ($result['name'] === 'Uffbass, Umud') {
260                 $userId = $result['id'];
261             }
262         }
263         $this->assertTrue($userId !== NULL);
264         
265         $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($userId);
266         $this->assertEquals(3, count($groupMemberships), 'new user should have 3 group memberships');
267         $this->assertTrue(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_ON]->getId(), $groupMemberships), $userId . ' not member of the internet group ' . print_r($groupMemberships, TRUE));
268         
269         $user = Tinebase_User::getInstance()->getFullUserById($userId);
270         $this->assertEquals('/bin/false', $user->accountLoginShell);
271     }
272     
273     /**
274      * testGetCoursesPreferences
275      * 
276      * @see 0006436: Courses preferences do not work (in pref panel)
277      */
278     public function testGetCoursesPreferences()
279     {
280         $tinebaseJson = new Tinebase_Frontend_Json();
281         $coursesPrefs = $tinebaseJson->searchPreferencesForApplication('Courses', array());
282         
283         $this->assertTrue($coursesPrefs['totalcount'] > 0);
284         $pref = $coursesPrefs['results'][0];
285         
286         $this->assertEquals(Tinebase_Preference_Abstract::DEFAULTPERSISTENTFILTER, $pref['name']);
287         $this->assertGreaterThanOrEqual(2, count($pref['options']));
288     }
289
290     /**
291      * testImportWithMissingList
292      * 
293      * @see 0007460: check existence of group/list before user import
294      */
295     public function testImportWithMissingList()
296     {
297         $result = $this->_importHelper(dirname(__FILE__) . '/files/tah2a.txt', $this->_getCourseImportDefinition3('iso-8859-1'), TRUE, TRUE);
298         $this->assertEquals(3, count($result['members']), 'import failed');
299     }
300     
301     /**
302      * test internet access on/off/filtered
303      * 
304      * @todo remove some code duplication
305      */
306     public function testInternetAccess()
307     {
308         // create new course with internet access
309         $course = $this->_getCourseData();
310         $courseData = $this->_json->saveCourse($course);
311         $userId = $courseData['members'][0]['id'];
312         $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($userId);
313         $this->assertTrue(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_ON]->getId(), $groupMemberships), $userId . ' not member of the internet group ' . print_r($groupMemberships, TRUE));
314         
315         // filtered internet access
316         $courseData['internet'] = 'FILTERED';
317         $courseData['type'] = $courseData['type']['value'];
318         $courseData = $this->_json->saveCourse($courseData);
319         $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($userId);
320         $this->assertTrue(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_FILTERED]->getId(), $groupMemberships), 'not member of the filtered internet group ' . print_r($groupMemberships, TRUE));
321         $this->assertFalse(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_ON]->getId(), $groupMemberships), 'member of the internet group ' . print_r($groupMemberships, TRUE));
322         
323         // remove internet access
324         $courseData['internet'] = 'OFF';
325         $courseData['type'] = $courseData['type']['value'];
326         $courseData = $this->_json->saveCourse($courseData);
327         $groupMemberships = Tinebase_Group::getInstance()->getGroupMemberships($userId);
328         $this->assertFalse(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_ON]->getId(), $groupMemberships), 'member of the internet group ' . print_r($groupMemberships, TRUE));
329         $this->assertFalse(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_FILTERED]->getId(), $groupMemberships), 'member of the filtered internet group ' . print_r($groupMemberships, TRUE));
330     }
331     
332     /**
333      * testAddNewMember
334      * 
335      * @see 0006372: add new course member with a button
336      * @see 0006878: set primary group for manually added users
337      */
338     public function testAddNewMember()
339     {
340         $course = $this->_getCourseData();
341         $courseData = $this->_json->saveCourse($course);
342         
343         $result = $this->_json->addNewMember(array(
344             'accountFirstName' => 'jams',
345             'accountLastName'  => 'hot',
346         ), $courseData);
347         
348         $this->assertEquals(2, count($result['results']));
349         
350         $id = NULL;
351         foreach ($result['results'] as $result) {
352             if ($result['name'] === 'hot, jams') {
353                 $id = $result['id'];
354             }
355         }
356         $this->assertTrue($id !== NULL);
357         
358         $newUser = Tinebase_User::getInstance()->getFullUserById($id);
359         $this->assertEquals('hotja', $newUser->accountLoginName);
360         $this->assertEquals('/bin/false', $newUser->accountLoginShell);
361         
362         $newUserMemberships = Tinebase_Group::getInstance()->getGroupMemberships($newUser);
363         $this->assertEquals(3, count($newUserMemberships), 'new user should have 3 group memberships');
364         $this->assertTrue(in_array(Tinebase_Group::getInstance()->getDefaultGroup()->getId(), $newUserMemberships),
365             'could not find default group in memberships: ' . print_r($newUserMemberships, TRUE));
366         $this->assertTrue(in_array($this->_configGroups[Courses_Config::INTERNET_ACCESS_GROUP_ON]->getId(), $newUserMemberships),
367             $id . ' not member of the internet group ' . print_r($newUserMemberships, TRUE));
368     }
369     
370     /**
371      * testApplySambaSettings
372      * 
373      * @see 0006910: new manual users have no samba settings
374      */
375     public function testApplySambaSettings()
376     {
377         $user = Tinebase_Core::getUser();
378         $config = Courses_Config::getInstance()->samba;
379         $profilePath = $config->baseprofilepath . 'school' . '\\' . 'coursexy' . '\\';
380         $user->applyOptionsAndGeneratePassword(array('samba' => array(
381             'homePath'      => $config->basehomepath,
382             'homeDrive'     => $config->homedrive,
383             'logonScript'   => 'coursexy' . $config->logonscript_postfix_member,
384             'profilePath'   => $profilePath,
385             'pwdCanChange'  => new Tinebase_DateTime('@1'),
386             'pwdMustChange' => new Tinebase_DateTime('@1')
387         )));
388
389         // check samba settings
390         $this->assertEquals($profilePath . $user->accountLoginName, $user->sambaSAM->profilePath);
391     }
392     
393     /**
394      * testTeacherDefaultFavorite
395      * 
396      * @see 0006876: create "my course" default favorite for new teachers
397      */
398     public function testTeacherDefaultFavorite()
399     {
400         $course = $this->_getCourseData();
401         $courseData = $this->_json->saveCourse($course);
402         $teacher = Tinebase_User::getInstance()->getFullUserById($courseData['members'][0]['id']);
403         
404         $filter = Tinebase_PersistentFilter::getInstance()->getFilterById(
405             Tinebase_Core::getPreference('Courses')->getValueForUser(Courses_Preference::DEFAULTPERSISTENTFILTER, $teacher->getId())
406         );
407         $this->assertEquals(array(array('field' => 'name', 'operator' => 'equals', 'value' => $course['name'])), $filter->toArray());
408     }
409     
410     /************ protected helper funcs *************/
411     
412     /**
413      * get Course
414      *
415      * @return array
416      */
417     protected function _getCourseData()
418     {
419         return array(
420             'name'          => Tinebase_Record_Abstract::generateUID(),
421             'description'   => 'blabla',
422             'type'          => $this->_department->getId(),
423             'internet'      => 'ON',
424         );
425     }
426         
427     /**
428      * get paging
429      *
430      * @return array
431      */
432     protected function _getPaging()
433     {
434         return array(
435             'start' => 0,
436             'limit' => 50,
437             'sort' => 'creation_time',
438             'dir' => 'ASC',
439         );
440     }
441
442     /**
443      * get Course filter
444      *
445      * @return array
446      */
447     protected function _getCourseFilter($_courseName)
448     {
449         return array(
450             array(
451                 'field' => 'name', 
452                 'operator' => 'contains', 
453                 'value' => $_courseName
454             ),
455         );
456     }
457     
458     /**
459      * import file
460      * 
461      * @param string $_filename
462      * @param Tinebase_Model_ImportExportDefinition $_definition
463      * @param boolean $_useJsonImportFn
464      * @param boolean $removeGroupList
465      * @return array course data
466      */
467     protected function _importHelper($_filename, Tinebase_Model_ImportExportDefinition $_definition = NULL, $_useJsonImportFn = FALSE, $removeGroupList = FALSE)
468     {
469         $definition = ($_definition !== NULL) ? $_definition : $this->_getCourseImportDefinition();
470         
471         $course = $this->_getCourseData();
472         $courseData = $this->_json->saveCourse($course);
473         
474         if ($removeGroupList) {
475             $group = Admin_Controller_Group::getInstance()->get($courseData['group_id']);
476             Addressbook_Controller_List::getInstance()->delete($group->list_id);
477         }
478         
479         $this->_coursesToDelete[] = $courseData['id'];
480         
481         if ($_useJsonImportFn) {
482             $tempFileBackend = new Tinebase_TempFile();
483             $tempFile = $tempFileBackend->createTempFile($_filename);
484             Courses_Config::getInstance()->set(Courses_Config::STUDENTS_IMPORT_DEFINITION, $definition->name);
485             $result = $this->_json->importMembers($tempFile->getId(), $courseData['group_id'], $courseData['id']);
486             
487             $this->assertGreaterThan(0, $result['results']);
488             
489         } else {
490             $testConfig = Zend_Registry::get('testConfig');
491             $maildomain = ($testConfig->maildomain) ? $testConfig->maildomain : 'school.org';
492             
493             $importer = call_user_func($definition->plugin . '::createFromDefinition', $definition, array(
494                     'group_id'                  => $courseData['group_id'],
495                     'accountHomeDirectoryPrefix' => '//base/school/' . $courseData['name'] . '/',
496                     'accountEmailDomain'        => $maildomain,
497                     'password'                  => $courseData['name'],
498                     'samba'                     => array(
499                         'homePath'    => '//basehome/',
500                         'homeDrive'   => 'H:',
501                         'logonScript' => 'logon.bat',
502                         'profilePath' => '\\\\profile\\',
503                     )
504                 )
505             );
506             $tempFilename = TestServer::replaceEmailDomainInFile($_filename);
507             $importer->importFile($tempFilename);
508         }
509         $courseData = $this->_json->getCourse($courseData['id']);
510
511         return $courseData;
512     }
513     
514     /**
515      * returns course import definition
516      * 
517      * @return Tinebase_Model_ImportExportDefinition
518      */
519     protected function _getCourseImportDefinition()
520     {
521         try {
522             $definition = Tinebase_ImportExportDefinition::getInstance()->getByName('course_user_import_csv');
523         } catch (Tinebase_Exception_NotFound $e) {
524             $definition = Tinebase_ImportExportDefinition::getInstance()->create(new Tinebase_Model_ImportExportDefinition(array(
525                     'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Admin')->getId(),
526                     'name'              => 'course_user_import_csv',
527                     'type'              => 'import',
528                     'model'             => 'Tinebase_Model_FullUser',
529                     'plugin'            => 'Admin_Import_Csv',
530                     'plugin_options'    => '<?xml version="1.0" encoding="UTF-8"?>
531             <config>
532                 <headline>1</headline>
533                 <use_headline>0</use_headline>
534                 <dryrun>0</dryrun>
535                 <encoding>UTF-8</encoding>
536                 <delimiter>;</delimiter>
537                 <mapping>
538                     <field>
539                         <source>lastname</source>
540                         <destination>accountLastName</destination>
541                     </field>
542                     <field>
543                         <source>firstname</source>
544                         <destination>accountFirstName</destination>
545                     </field>
546                 </mapping>
547             </config>')
548             ));
549         }
550         
551         return $definition;
552     }
553     
554         /**
555      * returns course import definition
556      * 
557      * @return Tinebase_Model_ImportExportDefinition
558      */
559     protected function _getCourseImportDefinition2()
560     {
561         try {
562             $definition = Tinebase_ImportExportDefinition::getInstance()->getByName('course_user_import_csv2');
563         } catch (Tinebase_Exception_NotFound $e) {
564             $definition = Tinebase_ImportExportDefinition::getInstance()->create(new Tinebase_Model_ImportExportDefinition(array(
565                     'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Admin')->getId(),
566                     'name'              => 'course_user_import_csv2',
567                     'type'              => 'import',
568                     'model'             => 'Tinebase_Model_FullUser',
569                     'plugin'            => 'Admin_Import_Csv',
570                     'plugin_options'    => '<?xml version="1.0" encoding="UTF-8"?>
571             <config>
572                 <headline>1</headline>
573                 <use_headline>0</use_headline>
574                 <dryrun>0</dryrun>
575                 <encoding>MAC-CENTRALEUROPE</encoding>
576                 <delimiter>;</delimiter>
577                 <mapping>
578                     <field>
579                         <source>VORNAME</source>
580                         <destination>accountFirstName</destination>
581                     </field>
582                     <field>
583                         <source>NAME</source>
584                         <destination>accountLastName</destination>
585                     </field>
586                     </mapping>
587             </config>')
588             ));
589         }
590         
591         return $definition;
592     }
593     
594     /**
595      * returns course import definition
596      * 
597      * @param string $encoding
598      * @return Tinebase_Model_ImportExportDefinition
599      */
600     protected function _getCourseImportDefinition3($encoding = 'UTF-8')
601     {
602         try {
603             $definition = Tinebase_ImportExportDefinition::getInstance()->getByName('course_user_import_csv');
604         } catch (Tinebase_Exception_NotFound $e) {
605             $definition = Tinebase_ImportExportDefinition::getInstance()->create(new Tinebase_Model_ImportExportDefinition(array(
606                     'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Admin')->getId(),
607                     'name'              => 'course_user_import_csv',
608                     'type'              => 'import',
609                     'model'             => 'Tinebase_Model_FullUser',
610                     'plugin'            => 'Admin_Import_Csv',
611                     'plugin_options'    => '<?xml version="1.0" encoding="UTF-8"?>
612             <config>
613                 <headline>1</headline>
614                 <use_headline>0</use_headline>
615                 <dryrun>0</dryrun>
616                 <encoding>' . $encoding . '</encoding>
617                 <delimiter>;</delimiter>
618                 <mapping>
619                     <field>
620                         <source>Name</source>
621                         <destination>accountLastName</destination>
622                     </field>
623                     <field>
624                         <source>Vorname</source>
625                         <destination>accountFirstName</destination>
626                     </field>
627                 </mapping>
628             </config>')
629             ));
630         }
631         
632         
633         return $definition;
634     }
635 }