diff options
Diffstat (limited to 'sites/all/modules/file_entity/file_entity.test')
-rw-r--r-- | sites/all/modules/file_entity/file_entity.test | 1635 |
1 files changed, 1635 insertions, 0 deletions
diff --git a/sites/all/modules/file_entity/file_entity.test b/sites/all/modules/file_entity/file_entity.test new file mode 100644 index 000000000..2c72cbd86 --- /dev/null +++ b/sites/all/modules/file_entity/file_entity.test @@ -0,0 +1,1635 @@ +<?php + +/** + * @file + * Test integration for the file_entity module. + */ + +class FileEntityTestHelper extends DrupalWebTestCase { + function setUp() { + $modules = func_get_args(); + if (isset($modules[0]) && is_array($modules[0])) { + $modules = $modules[0]; + } + $modules[] = 'file_entity'; + parent::setUp($modules); + } + + /** + * Retrieves a sample file of the specified type. + */ + function getTestFile($type_name, $size = NULL) { + // Get a file to upload. + $file = current($this->drupalGetTestFiles($type_name, $size)); + + // Add a filesize property to files as would be read by file_load(). + $file->filesize = filesize($file->uri); + + return $file; + } + + /** + * Retrieves the fid of the last inserted file. + */ + function getLastFileId() { + return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField(); + } + + /** + * Get a file from the database based on its filename. + * + * @param $filename + * A file filename, usually generated by $this->randomName(). + * @param $reset + * (optional) Whether to reset the internal file_load() cache. + * + * @return + * A file object matching $filename. + */ + function getFileByFilename($filename, $reset = FALSE) { + $files = file_load_multiple(array(), array('filename' => $filename), $reset); + // Load the first file returned from the database. + $returned_file = reset($files); + return $returned_file; + } + + protected function createFileEntity($settings = array()) { + // Populate defaults array. + $settings += array( + 'filepath' => 'Файл для тестирования ' . $this->randomName(), // Prefix with non-latin characters to ensure that all file-related tests work with international filenames. + 'filemime' => 'text/plain', + 'uid' => 1, + 'timestamp' => REQUEST_TIME, + 'status' => FILE_STATUS_PERMANENT, + 'contents' => "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.", + 'scheme' => file_default_scheme(), + 'type' => NULL, + ); + + $filepath = $settings['scheme'] . '://' . $settings['filepath']; + + file_put_contents($filepath, $settings['contents']); + $this->assertTrue(is_file($filepath), t('The test file exists on the disk.'), 'Create test file'); + + $file = new stdClass(); + $file->uri = $filepath; + $file->filename = drupal_basename($file->uri); + $file->filemime = $settings['filemime']; + $file->uid = $settings['uid']; + $file->timestamp = $settings['timestamp']; + $file->filesize = filesize($file->uri); + $file->status = $settings['status']; + $file->type = $settings['type']; + + // The file type is used as a bundle key, and therefore, must not be NULL. + if (!isset($file->type)) { + $file->type = FILE_TYPE_NONE; + } + + // If the file isn't already assigned a real type, determine what type should + // be assigned to it. + if ($file->type === FILE_TYPE_NONE) { + $type = file_get_type($file); + if (isset($type)) { + $file->type = $type; + } + } + + // Write the record directly rather than calling file_save() so we don't + // invoke the hooks. + $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, t('The file was added to the database.'), 'Create test file'); + + return $file; + } + + protected function createFileType($overrides = array()) { + $type = new stdClass(); + $type->type = 'test'; + $type->label = "Test"; + $type->description = ''; + $type->mimetypes = array('image/jpeg', 'image/gif', 'image/png', 'image/tiff'); + + foreach ($overrides as $k => $v) { + $type->$k = $v; + } + + file_type_save($type); + return $type; + } + + /** + * Overrides DrupalWebTestCase::drupalGetToken() to support the hash salt. + * + * @todo Remove when http://drupal.org/node/1555862 is fixed in core. + */ + protected function drupalGetToken($value = '') { + $private_key = drupal_get_private_key(); + return drupal_hmac_base64($value, $this->session_id . $private_key . drupal_get_hash_salt()); + } +} + +/** + * Tests file type classification functionality. + */ +class FileEntityFileTypeClassificationTestCase extends DrupalWebTestCase { + public static function getInfo() { + return array( + 'name' => 'File entity classification', + 'description' => 'Test existing file entity classification functionality.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + } + + /** + * Test that existing files are properly classified by file type. + */ + function testFileTypeClassification() { + // Get test text and image files. + $file = current($this->drupalGetTestFiles('text')); + $text_file = file_save($file); + $file = current($this->drupalGetTestFiles('image')); + $image_file = file_save($file); + + // Enable file entity which adds adds a file type property to files and + // queues up existing files for classification. + module_enable(array('file_entity')); + + // Existing files have yet to be classified and should have an undefined + // file type. + $file_type = $this->getFileType($text_file); + $this->assertEqual($file_type['type'], 'undefined', t('The text file has an undefined file type.')); + $file_type = $this->getFileType($image_file); + $this->assertEqual($file_type['type'], 'undefined', t('The image file has an undefined file type.')); + + // The classification queue is processed during cron runs. Run cron to + // trigger the classification process. + $this->cronRun(); + + // The classification process should assign a file type to any file whose + // MIME type is assigned to a file type. Check to see if each file was + // assigned a proper file type. + $file_type = $this->getFileType($text_file); + $this->assertEqual($file_type['type'], 'document', t('The text file was properly assigned the Document file type.')); + $file_type = $this->getFileType($image_file); + $this->assertEqual($file_type['type'], 'image', t('The image file was properly assigned the Image file type.')); + } + + /** + * Get the file type of a given file. + * + * @param $file + * A file object. + * + * @return + * The file's file type as a string. + */ + function getFileType($file) { + $type = db_select('file_managed', 'fm') + ->fields('fm', array('type')) + ->condition('fid', $file->fid, '=') + ->execute() + ->fetchAssoc(); + + return $type; + } +} + +/** + * Tests basic file entity functionality. + */ +class FileEntityUnitTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File entity unit tests', + 'description' => 'Test basic file entity functionality.', + 'group' => 'File entity', + ); + } + + /** + * Regression tests for core issue http://drupal.org/node/1239376. + */ + function testMimeTypeMappings() { + $tests = array( + 'public://test.ogg' => 'audio/ogg', + 'public://test.mkv' => 'video/x-m4v', + 'public://test.mka' => 'audio/x-matroska', + 'public://test.mkv' => 'video/x-matroska', + 'public://test.webp' => 'image/webp', + ); + foreach ($tests as $input => $expected) { + $this->assertEqual(file_get_mimetype($input), $expected); + } + } + + /** + * Tests basic file entity properties. + */ + function testFileEntity() { + // Save a raw file, turning it into a file entity. + $file = $this->getTestFile('text'); + $file->uid = 1; + $file->status = FILE_STATUS_PERMANENT; + file_save($file); + + // Test entity ID, revision ID, and bundle. + $ids = entity_extract_ids('file', $file); + $this->assertIdentical($ids, array($file->fid, NULL, 'document')); + + // Test the entity URI callback. + $uri = entity_uri('file', $file); + $this->assertEqual($uri['path'], "file/{$file->fid}"); + } + + /** + * Tests storing image height and width as file metadata. + */ + function testImageDimensions() { + // Test hook_file_insert(). + $file = current($this->drupalGetTestFiles('image')); + $image_file = file_save($file); + $this->assertTrue(isset($image_file->metadata['height']), 'Image height retrieved on file_save() for an image file.'); + $this->assertTrue(isset($image_file->metadata['width']), 'Image width retrieved on file_save() for an image file.'); + + $file = current($this->drupalGetTestFiles('text')); + $text_file = file_save($file); + $this->assertFalse(isset($text_file->metadata['height']), 'No image height retrieved on file_save() for an text file.'); + $this->assertFalse(isset($text_file->metadata['width']), 'No image width retrieved on file_save() for an text file.'); + + // Test hook_file_load(). + // Clear the cache and load fresh files objects to test file_load behavior. + entity_get_controller('file')->resetCache(); + + $file = file_load($image_file->fid); + $this->assertTrue(isset($file->metadata['height']), 'Image dimensions retrieved on file_load() for an image file.'); + $this->assertTrue(isset($file->metadata['width']), 'Image dimensions retrieved on file_load() for an image file.'); + + $this->assertEqual($file->metadata['height'], $image_file->metadata['height'], 'Loaded image height is equal to saved image height.'); + $this->assertEqual($file->metadata['width'], $image_file->metadata['width'], 'Loaded image width is equal to saved image width.'); + + $file = file_load($text_file->fid); + $this->assertFalse(isset($file->metadata['height']), 'No image height retrieved on file_load() for an text file.'); + $this->assertFalse(isset($file->metadata['width']), 'No image width retrieved on file_load() for an text file.'); + + // Test hook_file_update(). + // Load the first image file and resize it. + $height = $image_file->metadata['width'] / 2; + $width = $image_file->metadata['height'] / 2; + $image = image_load($image_file->uri); + image_resize($image, $width, $height); + image_save($image); + file_save($image_file); + + $this->assertEqual($image_file->metadata['height'], $height, 'Image file height updated by file_save().'); + $this->assertEqual($image_file->metadata['width'], $width, 'Image file width updated by file_save().'); + + // Clear the cache and reload the file. + entity_get_controller('file')->resetCache(); + + $file = file_load($image_file->fid); + $this->assertEqual($file->metadata['height'], $height, 'Updated image height retrieved by file_load().'); + $this->assertEqual($file->metadata['width'], $width, 'Updated image width retrieved by file_load().'); + + // Verify that the image dimension metadata is removed on file deletion. + file_delete($file, TRUE); + $this->assertFalse(db_query('SELECT COUNT(*) FROM {file_metadata} WHERE fid = :fid', array(':fid' => 'fid'))->fetchField(), 'Row deleted in {file_metadata} on file_delete().'); + } +} + +/** + * Tests editing existing file entities. + */ +class FileEntityEditTestCase extends FileEntityTestHelper { + protected $web_user; + protected $admin_user; + + public static function getInfo() { + return array( + 'name' => 'File entity edit', + 'description' => 'Create a file and test file edit functionality.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + + $this->web_user = $this->drupalCreateUser(array('edit own document files', 'create files')); + $this->admin_user = $this->drupalCreateUser(array('bypass file access', 'administer files')); + } + + /** + * Check file edit functionality. + */ + function testFileEntityEdit() { + $this->drupalLogin($this->web_user); + + $test_file = $this->getTestFile('text'); + $name_key = "filename"; + + // Create file to edit. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + if ($this->xpath('//input[@name="scheme"]')) { + $this->drupalPost(NULL, array(), t('Next')); + } + + // Check that the file exists in the database. + $file = $this->getFileByFilename($test_file->filename); + $this->assertTrue($file, t('File found in database.')); + + // Check that "edit" link points to correct page. + $this->clickLink(t('Edit')); + $edit_url = url("file/$file->fid/edit", array('absolute' => TRUE)); + $actual_url = $this->getURL(); + $this->assertEqual($edit_url, $actual_url, t('On edit page.')); + + // Check that the name field is displayed with the correct value. + $active = '<span class="element-invisible">' . t('(active tab)') . '</span>'; + $link_text = t('!local-task-title!active', array('!local-task-title' => t('Edit'), '!active' => $active)); + $this->assertText(strip_tags($link_text), 0, t('Edit tab found and marked active.')); + $this->assertFieldByName($name_key, $file->filename, t('Name field displayed.')); + + // The user does not have "delete" permissions so no delete button should be found. + $this->assertNoFieldByName('op', t('Delete'), 'Delete button not found.'); + + // Edit the content of the file. + $edit = array(); + $edit[$name_key] = $this->randomName(8); + // Stay on the current page, without reloading. + $this->drupalPost(NULL, $edit, t('Save')); + + // Check that the name field is displayed with the updated values. + $this->assertText($edit[$name_key], t('Name displayed.')); + } + + /** + * Check changing file associated user fields. + */ + function testFileEntityAssociatedUser() { + $this->drupalLogin($this->admin_user); + + // Create file to edit. + $test_file = $this->getTestFile('text'); + $name_key = "filename"; + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Check that the file was associated with the currently logged in user. + $file = $this->getFileByFilename($test_file->filename); + $this->assertIdentical($file->uid, $this->admin_user->uid, 'File associated with admin user.'); + + // Try to change the 'associated user' field to an invalid user name. + $edit = array( + 'name' => 'invalid-name', + ); + $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save')); + $this->assertText('The username invalid-name does not exist.'); + + // Change the associated user field to an empty string, which should assign + // association to the anonymous user (uid 0). + $edit['name'] = ''; + $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save')); + $file = file_load($file->fid); + $this->assertIdentical($file->uid, '0', 'File associated with anonymous user.'); + + // Change the associated user field to another user's name (that is not + // logged in). + $edit['name'] = $this->web_user->name; + $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save')); + $file = file_load($file->fid); + $this->assertIdentical($file->uid, $this->web_user->uid, 'File associated with normal user.'); + + // Check that normal users cannot change the associated user information. + $this->drupalLogin($this->web_user); + $this->drupalGet('file/' . $file->fid . '/edit'); + $this->assertNoFieldByName('name'); + } +} + +/** + * Tests creating new file entities through the file upload wizard. + */ +class FileEntityUploadWizardTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File entity upload wizard', + 'description' => 'Upload a file using the multi-step wizard.', + 'group' => 'File entity', + 'dependencies' => array('token'), + ); + } + + function setUp() { + parent::setUp('token'); + + // Disable the private file system which is automatically enabled by + // DrupalTestCase so we can test the upload wizard correctly. + variable_del('file_private_path'); + + $web_user = $this->drupalCreateUser(array('create files', 'view own private files')); + $this->drupalLogin($web_user); + } + + /** + * Test the basic file upload wizard functionality. + */ + function testFileEntityUploadWizardBasic() { + $test_file = $this->getTestFile('text'); + + // Step 1: Upload a basic document file. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Check that the file exists in the database. + $fid = $this->getLastFileId(); + $file = file_load($fid); + $this->assertTrue($file, t('File found in database.')); + + // Check that the document file has been uploaded. + $this->assertRaw(t('!type %name was uploaded.', array('!type' => 'Document', '%name' => $file->filename)), t('Document file uploaded.')); + } + + /** + * Test the file upload wizard type step. + */ + function testFileEntityUploadWizardTypes() { + $test_file = $this->getTestFile('text'); + + // Create multiple file types with the same mime types. + $this->createFileType(array('type' => 'document1', 'label' => 'Document 1', 'mimetypes' => array('text/plain'))); + $this->createFileType(array('type' => 'document2', 'label' => 'Document 2', 'mimetypes' => array('text/plain'))); + + // Step 1: Upload a basic document file. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Step 2: File type selection. + $edit = array(); + $edit['type'] = 'document2'; + $this->drupalPost(NULL, $edit, t('Next')); + + // Check that the file exists in the database. + $fid = $this->getLastFileId(); + $file = file_load($fid); + $this->assertTrue($file, t('File found in database.')); + + // Check that the document file has been uploaded. + $this->assertRaw(t('!type %name was uploaded.', array('!type' => 'Document 2', '%name' => $file->filename)), t('Document 2 file uploaded.')); + } + + /** + * Test the file upload wizard scheme step. + */ + function testFileEntityUploadWizardSchemes() { + $test_file = $this->getTestFile('text'); + + // Enable the private file system. + variable_set('file_private_path', $this->private_files_directory); + + // Step 1: Upload a basic document file. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Step 3: Scheme selection. + $edit = array(); + $edit['scheme'] = 'private'; + $this->drupalPost(NULL, $edit, t('Next')); + + // Check that the file exists in the database. + $fid = $this->getLastFileId(); + $file = file_load($fid); + $this->assertTrue($file, t('File found in database.')); + + // Check that the document file has been uploaded. + $this->assertRaw(t('!type %name was uploaded.', array('!type' => 'Document', '%name' => $file->filename)), t('Document file uploaded.')); + } + + /** + * Test the file upload wizard field step. + */ + function testFileEntityUploadWizardFields() { + $test_file = $this->getTestFile('image'); + $filename = $this->randomName(); + $alt = $this->randomName(); + $title = $this->randomName(); + + // Step 1: Upload a basic image file. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Step 4: Attached fields. + $edit = array(); + $edit['filename'] = $filename; + $edit['field_file_image_alt_text[' . LANGUAGE_NONE . '][0][value]'] = $alt; + $edit['field_file_image_title_text[' . LANGUAGE_NONE . '][0][value]'] = $title; + $this->drupalPost(NULL, $edit, t('Save')); + + // Check that the file exists in the database. + $fid = $this->getLastFileId(); + $file = file_load($fid); + $this->assertTrue($file, t('File found in database.')); + + // Check that the image file has been uploaded. + $this->assertRaw(t('!type %name was uploaded.', array('!type' => 'Image', '%name' => $filename)), t('Image file uploaded.')); + + // Check that the alt and title text was loaded from the fields. + $this->assertEqual($file->alt, $alt, t('Alt text was stored as file metadata.')); + $this->assertEqual($file->title, $title, t('Title text was stored as file metadata.')); + } + + /** + * Test skipping each of the file upload wizard steps. + */ + function testFileEntityUploadWizardStepSkipping() { + $test_file = $this->getTestFile('image'); + $filename = $this->randomName(); + + // Ensure that the file is affected by every step. + variable_set('file_private_path', $this->private_files_directory); + + $this->createFileType(array('type' => 'image1', 'label' => 'Image 1', 'mimetypes' => array('image/jpeg', 'image/gif', 'image/png', 'image/tiff'))); + $this->createFileType(array('type' => 'image2', 'label' => 'Image 2', 'mimetypes' => array('image/jpeg', 'image/gif', 'image/png', 'image/tiff'))); + + $field_name = drupal_strtolower($this->randomName() . '_field_name'); + $field = array('field_name' => $field_name, 'type' => 'text'); + field_create_field($field); + $instance = array( + 'field_name' => $field_name, + 'entity_type' => 'file', + 'bundle' => 'image2', + 'label' => $this->randomName() . '_label', + ); + field_create_instance($instance); + + // Test skipping each upload wizard step. + foreach (array('types', 'schemes', 'fields') as $step) { + // Step to skip. + switch ($step) { + case 'types': + variable_set('file_entity_file_upload_wizard_skip_file_type', TRUE); + break; + case 'schemes': + variable_set('file_entity_file_upload_wizard_skip_scheme', TRUE); + break; + case 'fields': + variable_set('file_entity_file_upload_wizard_skip_fields', TRUE); + break; + } + + // Step 1: Upload a basic image file. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Step 2: File type selection. + if ($step != 'types') { + $edit = array(); + $edit['type'] = 'image2'; + $this->drupalPost(NULL, $edit, t('Next')); + } + + // Step 3: Scheme selection. + if ($step != 'schemes') { + $edit = array(); + $edit['scheme'] = 'private'; + $this->drupalPost(NULL, $edit, t('Next')); + } + + // Step 4: Attached fields. + if ($step != 'fields') { + // Skipping file type selection essentially skips this step as well + // because the file will not be assigned a type so no fields will be + // available. + if ($step != 'types') { + $edit = array(); + $edit['filename'] = $filename; + $edit[$field_name . '[' . LANGUAGE_NONE . '][0][value]'] = $this->randomName(); + $this->drupalPost(NULL, $edit, t('Save')); + } + } + + // Check that the file exists in the database. + $fid = $this->getLastFileId(); + $file = file_load($fid); + $this->assertTrue($file, t('File found in database.')); + + // Determine the file's file type. + $type = file_type_load($file->type); + + // Check that the image file has been uploaded. + $this->assertRaw(t('!type %name was uploaded.', array('!type' => $type->label, '%name' => $file->filename)), t('Image file uploaded.')); + + // Reset 'skip' variables. + variable_del('file_entity_file_upload_wizard_skip_file_type'); + variable_del('file_entity_file_upload_wizard_skip_scheme'); + variable_del('file_entity_file_upload_wizard_skip_fields'); + } + } +} + +/** + * Test file administration page functionality. + */ +class FileEntityAdminTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File administration', + 'description' => 'Test file administration page functionality.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + + // Remove the "view files" permission which is set + // by default for all users so we can test this permission + // correctly. + $roles = user_roles(); + foreach ($roles as $rid => $role) { + user_role_revoke_permissions($rid, array('view files')); + } + + $this->admin_user = $this->drupalCreateUser(array('administer files', 'bypass file access')); + $this->base_user_1 = $this->drupalCreateUser(array('administer files')); + $this->base_user_2 = $this->drupalCreateUser(array('administer files', 'view own private files')); + $this->base_user_3 = $this->drupalCreateUser(array('administer files', 'view private files')); + $this->base_user_4 = $this->drupalCreateUser(array('administer files', 'edit any document files', 'delete any document files', 'edit any image files', 'delete any image files')); + } + + /** + * Tests that the table sorting works on the files admin pages. + */ + function testFilesAdminSort() { + $this->drupalLogin($this->admin_user); + $i = 0; + foreach (array('dd', 'aa', 'DD', 'bb', 'cc', 'CC', 'AA', 'BB') as $prefix) { + $this->createFileEntity(array('filepath' => $prefix . $this->randomName(6), 'timestamp' => $i)); + $i++; + } + + // Test that the default sort by file_managed.timestamp DESC actually fires properly. + $files_query = db_select('file_managed', 'fm') + ->fields('fm', array('fid')) + ->orderBy('timestamp', 'DESC') + ->execute() + ->fetchCol(); + + $files_form = array(); + $this->drupalGet('admin/content/file'); + foreach ($this->xpath('//table/tbody/tr/td/div/input/@value') as $input) { + $files_form[] = $input; + } + $this->assertEqual($files_query, $files_form, 'Files are sorted in the form according to the default query.'); + + // Compare the rendered HTML node list to a query for the files ordered by + // filename to account for possible database-dependent sort order. + $files_query = db_select('file_managed', 'fm') + ->fields('fm', array('fid')) + ->orderBy('filename') + ->execute() + ->fetchCol(); + + $files_form = array(); + $this->drupalGet('admin/content/file', array('query' => array('sort' => 'asc', 'order' => 'Title'))); + foreach ($this->xpath('//table/tbody/tr/td/div/input/@value') as $input) { + $files_form[] = $input; + } + $this->assertEqual($files_query, $files_form, 'Files are sorted in the form the same as they are in the query.'); + } + + /** + * Tests files overview with different user permissions. + */ + function testFilesAdminPages() { + $this->drupalLogin($this->admin_user); + + $files['public_image'] = $this->createFileEntity(array('scheme' => 'public', 'uid' => $this->base_user_1->uid, 'type' => 'image')); + $files['public_document'] = $this->createFileEntity(array('scheme' => 'public', 'uid' => $this->base_user_2->uid, 'type' => 'document')); + $files['private_image'] = $this->createFileEntity(array('scheme' => 'private', 'uid' => $this->base_user_1->uid, 'type' => 'image')); + $files['private_document'] = $this->createFileEntity(array('scheme' => 'private', 'uid' => $this->base_user_2->uid, 'type' => 'document')); + + // Verify view, usage, edit, and delete links for any file. + $this->drupalGet('admin/content/file'); + $this->assertResponse(200); + foreach ($files as $file) { + $this->assertLinkByHref('file/' . $file->fid); + $this->assertLinkByHref('file/' . $file->fid . '/usage'); + $this->assertLinkByHref('file/' . $file->fid . '/edit'); + $this->assertLinkByHref('file/' . $file->fid . '/delete'); + // Verify tableselect. + $this->assertFieldByName('files[' . $file->fid . ']', '', t('Tableselect found.')); + } + + // Verify no operation links are displayed for regular users. + $this->drupalLogout(); + $this->drupalLogin($this->base_user_1); + $this->drupalGet('admin/content/file'); + $this->assertResponse(200); + $this->assertLinkByHref('file/' . $files['public_image']->fid); + $this->assertLinkByHref('file/' . $files['public_document']->fid); + $this->assertNoLinkByHref('file/' . $files['public_image']->fid . '/edit'); + $this->assertNoLinkByHref('file/' . $files['public_image']->fid . '/delete'); + $this->assertNoLinkByHref('file/' . $files['public_document']->fid . '/edit'); + $this->assertNoLinkByHref('file/' . $files['public_document']->fid . '/delete'); + + // Verify no tableselect. + $this->assertNoFieldByName('files[' . $files['public_image']->fid . ']', '', t('No tableselect found.')); + + // Verify private file is displayed with permission. + $this->drupalLogout(); + $this->drupalLogin($this->base_user_2); + $this->drupalGet('admin/content/file'); + $this->assertResponse(200); + $this->assertLinkByHref('file/' . $files['private_document']->fid); + // Verify no operation links are displayed. + $this->assertNoLinkByHref('file/' . $files['private_document']->fid . '/edit'); + $this->assertNoLinkByHref('file/' . $files['private_document']->fid . '/delete'); + + // Verify user cannot see private file of other users. + $this->assertNoLinkByHref('file/' . $files['private_image']->fid); + $this->assertNoLinkByHref('file/' . $files['private_image']->fid . '/edit'); + $this->assertNoLinkByHref('file/' . $files['private_image']->fid . '/delete'); + + // Verify no tableselect. + $this->assertNoFieldByName('files[' . $files['private_document']->fid . ']', '', t('No tableselect found.')); + + // Verify private file is displayed with permission. + $this->drupalLogout(); + $this->drupalLogin($this->base_user_3); + $this->drupalGet('admin/content/file'); + $this->assertResponse(200); + + // Verify user can see private file of other users. + $this->assertLinkByHref('file/' . $files['private_document']->fid); + $this->assertLinkByHref('file/' . $files['private_image']->fid); + + // Verify operation links are displayed for users with appropriate permission. + $this->drupalLogout(); + $this->drupalLogin($this->base_user_4); + $this->drupalGet('admin/content/file'); + $this->assertResponse(200); + foreach ($files as $file) { + $this->assertLinkByHref('file/' . $file->fid); + $this->assertLinkByHref('file/' . $file->fid . '/usage'); + $this->assertLinkByHref('file/' . $file->fid . '/edit'); + $this->assertLinkByHref('file/' . $file->fid . '/delete'); + } + + // Verify file access can be bypassed. + $this->drupalLogout(); + $this->drupalLogin($this->admin_user); + $this->drupalGet('admin/content/file'); + $this->assertResponse(200); + foreach ($files as $file) { + $this->assertLinkByHref('file/' . $file->fid); + $this->assertLinkByHref('file/' . $file->fid . '/usage'); + $this->assertLinkByHref('file/' . $file->fid . '/edit'); + $this->assertLinkByHref('file/' . $file->fid . '/delete'); + } + } +} + +/** + * Tests the file usage page. + */ +class FileEntityUsageTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File entity usage', + 'description' => 'Create a file and verify its usage.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + + $web_user = $this->drupalCreateUser(array('create files', 'bypass file access', 'edit own article content')); + $this->drupalLogin($web_user); + } + + /** + * Create a file and verify its usage information. + */ + function testFileEntityUsagePage() { + $image_field = 'field_image'; + $image = $this->getTestFile('image'); + + // Create a node, save it, then edit it to upload a file. + $edit = array( + "files[" . $image_field . "_" . LANGUAGE_NONE . "_0]" => drupal_realpath($image->uri), + ); + $node = $this->drupalCreateNode(array('type' => 'article')); + $this->drupalPost('node/' . $node->nid . '/edit', $edit, t('Save')); + + // Load the uploaded file. + $fid = $this->getLastFileId(); + $file = file_load($fid); + + // View the file's usage page. + $this->drupalGet('file/' . $file->fid . '/usage'); + + // Verify that a link to the entity is available. + $this->assertLink($node->title); + $this->assertLinkByHref('node/' . $node->nid); + + // Verify that the entity type and use count information is also present. + $expected_values = array( + 'type' => 'node', + 'count' => 1, + ); + foreach ($expected_values as $name => $value) { + $this->assertTrue($this->xpath('//table/tbody/tr/td[normalize-space(text())=:text]', array(':text' => $value)), t('File usage @name was found in the table.', array('@name' => $name))); + } + + // Add a reference to the file from the same entity but registered by a + // different module to ensure that the usage count is incremented and no + // additional table rows are created. + file_usage_add($file, 'example_module', 'node', $node->nid, 2); + + // Reload the page and verify that the expected values are present. + $this->drupalGet('file/' . $file->fid . '/usage'); + $expected_values['count'] = 3; + foreach ($expected_values as $name => $value) { + $this->assertTrue($this->xpath('//table/tbody/tr/td[normalize-space(text())=:text]', array(':text' => $value)), t('File usage @name was found in the table.', array('@name' => $name))); + } + + // Add a reference to the file from an entity that doesn't exist to ensure + // that this case is handled. + file_usage_add($file, 'test_module', 'imaginary', 1); + + // Reload the page. + $this->drupalGet('file/' . $file->fid . '/usage'); + + // Verify that the module name is used in place of a link to the entity. + $this->assertNoLink('test_module'); + $this->assertRaw('test_module', 'Module name used in place of link to the entity.'); + + // Verify that the entity type and use count information is also present. + $expected_values = array( + 'type' => 'imaginary', + 'count' => 1, + ); + foreach ($expected_values as $name => $value) { + $this->assertTrue($this->xpath('//table/tbody/tr/td[normalize-space(text())=:text]', array(':text' => $value)), t('File usage @name was found in the table.', array('@name' => $name))); + } + } +} + +/** + * Tests image alt and title text. + */ +class FileEntityAltTitleTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File entity alt and title text', + 'description' => 'Create an image file with alt and title text.', + 'group' => 'File entity', + 'dependencies' => array('token'), + ); + } + + function setUp() { + parent::setUp('token'); + + $web_user = $this->drupalCreateUser(array('create files', 'edit own image files')); + $this->drupalLogin($web_user); + } + + /** + * Create an "image" file and verify its associated alt and title text. + */ + function testFileEntityAltTitle() { + $test_file = $this->getTestFile('image'); + $alt_field_name = 'field_file_image_alt_text'; // Name of the default alt text field added to the image file type. + $title_field_name = 'field_file_image_title_text'; // Name of the default title text field added to the image file type. + + // Create a file. + $edit = array(); + $edit['files[upload]'] = drupal_realpath($test_file->uri); + $this->drupalPost('file/add', $edit, t('Next')); + + // Step 2: Scheme selection. + if ($this->xpath('//input[@name="scheme"]')) { + $this->drupalPost(NULL, array(), t('Next')); + } + + // Step 3: Attached fields. + $alt = 'Quote" Amp& ' . 'Файл для тестирования ' . $this->randomName(); // Generate alt text containing HTML entities, spaces and non-latin characters. + $title = 'Quote" Amp& ' . 'Файл для тестирования ' . $this->randomName(); // Generate title text containing HTML entities, spaces and non-latin characters. + + $edit = array(); + $edit[$alt_field_name . '[' . LANGUAGE_NONE . '][0][value]'] = $alt; + $edit[$title_field_name . '[' . LANGUAGE_NONE . '][0][value]'] = $title; + $this->drupalPost(NULL, $edit, t('Save')); + + // Check that the image file has been uploaded. + $this->assertRaw(t('!type %name was uploaded.', array('!type' => 'Image', '%name' => $test_file->filename)), t('Image file uploaded.')); + + // Check that the file exists in the database. + $file = $this->getFileByFilename($test_file->filename); + $this->assertTrue($file, t('File found in database.')); + + // Check that the alt and title text was loaded from the fields. + $this->assertEqual($file->alt, $alt, t('Alt text was stored as file metadata.')); + $this->assertEqual($file->title, $title, t('Title text was stored as file metadata.')); + + // Verify that the alt and title text is present on the page. + $image_info = array( + 'path' => $file->uri, + 'alt' => $alt, + 'title' => $title, + 'width' => $file->width, + 'height' => $file->height, + ); + $default_output = theme('image', $image_info); + $this->assertRaw($default_output, 'Image displayed using user supplied alt and title attributes.'); + + // Verify that the alt and title text can be edited. + $new_alt = $this->randomName(); + $new_title = $this->randomName(); + + $edit = array(); + $edit[$alt_field_name . '[' . LANGUAGE_NONE . '][0][value]'] = $new_alt; + $edit[$title_field_name . '[' . LANGUAGE_NONE . '][0][value]'] = $new_title; + $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save')); + + $image_info = array( + 'path' => $file->uri, + 'alt' => $new_alt, + 'title' => $new_title, + 'width' => $file->width, + 'height' => $file->height, + ); + $default_output = theme('image', $image_info); + $this->assertRaw($default_output, 'Image displayed using updated alt and title attributes.'); + } +} + +/** + * Tests replacing the file associated with a file entity. + */ +class FileEntityReplaceTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File replacement', + 'description' => 'Test file replace functionality.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + } + + /** + * @todo Test image dimensions for an image field are reset when a file is replaced. + * @todo Test image styles are cleared when an image is updated. + */ + function testReplaceFile() { + // Select the first text test file to use. + $file = $this->createFileEntity(array('type' => 'document')); + + // Create a user with file edit permissions. + $user = $this->drupalCreateUser(array('edit any document files')); + $this->drupalLogin($user); + + // Test that the Upload widget appears for a local file. + $this->drupalGet('file/' . $file->fid . '/edit'); + $this->assertFieldByName('files[replace_upload]'); + + // Test that file saves without uploading a file. + $this->drupalPost(NULL, array(), t('Save')); + $this->assertText(t('Document @file has been updated.', array('@file' => $file->filename)), 'File was updated without file upload.'); + + // Get a text file to use as a replacement. + $original = clone $file; + $replacement = $this->getTestFile('text'); + + // Test that the file saves when uploading a replacement file. + $edit = array(); + $edit['files[replace_upload]'] = drupal_realpath($replacement->uri); + $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save')); + $this->assertText(t('Document @file has been updated.', array('@file' => $file->filename)), 'File was updated with file upload.'); + + // Re-load the file from the database. + $file = file_load($file->fid); + + // Test how file properties changed after the file has been replaced. + $this->assertEqual($file->filename, $original->filename, 'Updated file name did not change.'); + $this->assertNotEqual($file->filesize, $original->filesize, 'Updated file size changed from previous file.'); + $this->assertEqual($file->filesize, $replacement->filesize, 'Updated file size matches uploaded file.'); + $this->assertEqual(file_get_contents($file->uri), file_get_contents($replacement->uri), 'Updated file contents matches uploaded file.'); + $this->assertFalse(entity_load('file', FALSE, array('status' => 0)), 'Temporary file used for replacement was deleted.'); + + // Get an image file. + $image = $this->getTestFile('image'); + $edit['files[replace_upload]'] = drupal_realpath($image->uri); + + // Test that validation works by uploading a non-text file as a replacement. + $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save')); + $this->assertRaw(t('The specified file %file could not be uploaded. Only files with the following extensions are allowed:', array('%file' => $image->filename)), 'File validation works, upload failed correctly.'); + + // Create a non-local file record. + $file2 = new stdClass(); + $file2->uri = 'oembed://' . $this->randomName(); + $file2->filename = drupal_basename($file2->uri); + $file2->filemime = 'image/oembed'; + $file2->type = 'image'; + $file2->uid = 1; + $file2->timestamp = REQUEST_TIME; + $file2->filesize = 0; + $file2->status = 0; + // Write the record directly rather than calling file_save() so we don't + // invoke the hooks. + $this->assertTrue(drupal_write_record('file_managed', $file2), 'Non-local file was added to the database.'); + + // Test that Upload widget does not appear for non-local file. + $this->drupalGet('file/' . $file2->fid . '/edit'); + $this->assertNoFieldByName('files[replace_upload]'); + } +} + +/** + * Tests file entity tokens. + */ +class FileEntityTokenTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File entity tokens', + 'description' => 'Test the file entity tokens.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + } + + function testFileEntityTokens() { + $file = $this->createFileEntity(array('type' => 'document')); + $tokens = array( + 'type' => 'Document', + 'type:name' => 'Document', + 'type:machine-name' => 'document', + 'type:count' => 1, + ); + $this->assertTokens('file', array('file' => $file), $tokens); + + $file = $this->createFileEntity(array('type' => 'image')); + $tokens = array( + 'type' => 'Image', + 'type:name' => 'Image', + 'type:machine-name' => 'image', + 'type:count' => 1, + ); + $this->assertTokens('file', array('file' => $file), $tokens); + } + + function assertTokens($type, array $data, array $tokens, array $options = array()) { + $token_input = drupal_map_assoc(array_keys($tokens)); + $values = token_generate($type, $token_input, $data, $options); + foreach ($tokens as $token => $expected) { + if (!isset($expected)) { + $this->assertTrue(!isset($values[$token]), t("Token value for [@type:@token] was not generated.", array('@type' => $type, '@token' => $token))); + } + elseif (!isset($values[$token])) { + $this->fail(t("Token value for [@type:@token] was not generated.", array('@type' => $type, '@token' => $token))); + } + elseif (!empty($options['regex'])) { + $this->assertTrue(preg_match('/^' . $expected . '$/', $values[$token]), t("Token value for [@type:@token] was '@actual', matching regular expression pattern '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $values[$token], '@expected' => $expected))); + } + else { + $this->assertIdentical($values[$token], $expected, t("Token value for [@type:@token] was '@actual', expected value '@expected'.", array('@type' => $type, '@token' => $token, '@actual' => $values[$token], '@expected' => $expected))); + } + } + + return $values; + } +} + +/** + * Tests adding support for bundles to the core 'file' entity. + */ +class FileEntityTypeTestCase extends FileEntityTestHelper { + public static function getInfo() { + return array( + 'name' => 'File entity types', + 'description' => 'Test the file entity types.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + } + + /** + * Test admin pages access and functionality. + */ + function testAdminPages() { + // Create a user with file type administration access. + $user = $this->drupalCreateUser(array('administer file types')); + $this->drupalLogin($user); + + $this->drupalGet('admin/structure/file-types'); + $this->assertResponse(200, 'File types admin page is accessible'); + } + + /** + * Test creating a new type. Basic CRUD. + */ + function testCreate() { + $type_machine_type = 'foo'; + $type_machine_label = 'foobar'; + $type = $this->createFileType(array('type' => $type_machine_type, 'label' => $type_machine_label)); + $loaded_type = file_type_load($type_machine_type); + $this->assertEqual($loaded_type->label, $type_machine_label, "Was able to create a type and retreive it."); + } + + /** + * Test file types CRUD UI. + */ + function testTypesCrudUi() { + $this->drupalGet('admin/structure/file-types'); + $this->assertResponse(403, 'File types UI page is not accessible to unauthorized users.'); + + $user = $this->drupalCreateUser(array('administer file types')); + $this->drupalLogin($user); + + $this->drupalGet('admin/structure/file-types'); + $this->assertResponse(200, 'File types UI page is accessible to users with adequate permission.'); + + // Create new file type. + $edit = array( + 'label' => t('Test type'), + 'type' => 'test_type', + 'description' => t('This is dummy file type used just for testing.'), + 'mimetypes' => 'image/png', + ); + $this->drupalGet('admin/structure/file-types/add'); + $this->drupalPost(NULL, $edit, t('Save')); + $this->assertText(t('The file type @type has been updated.', array('@type' => $edit['label'])), 'New file type successfully created.'); + $this->assertText($edit['label'], 'New file type created: label found.'); + $this->assertText($edit['description'], 'New file type created: description found.'); + $this->assertFieldByXPath("//table//tr[1]//td[7]", t('Normal'), 'Newly created file type is stored in DB.'); + $this->assertLink(t('disable'), 0, 'Able to disable newly created file type.'); + $this->assertLink(t('delete'), 0, 'Able to delete newly created file type.'); + $this->assertLinkByHref('admin/structure/file-types/manage/' . $edit['type'] . '/disable', 0, 'Disable link points to disable confirmation page.'); + $this->assertLinkByHref('admin/structure/file-types/manage/' . $edit['type'] . '/delete', 0, 'Delete link points to delete confirmation page.'); + + // Edit file type. + $this->drupalGet('admin/structure/file-types/manage/' . $edit['type'] . '/edit'); + $this->assertRaw(t('Save'), 'Save button found on edit page.'); + $this->assertRaw(t('Delete'), 'Delete button found on edit page.'); + $this->assertRaw($edit['label'], 'Label found on file type edit page'); + $this->assertText($edit['description'], 'Description found on file type edit page'); + $this->assertText($edit['mimetypes'], 'Mime-type configuration found on file type edit page'); + $this->assertText(t('Mimetype List'), 'Mimetype list present on edit form.'); + + // Modify file type. + $edit['label'] = t('New type label'); + $this->drupalPost(NULL, array('label' => $edit['label']), t('Save')); + $this->assertText(t('The file type @type has been updated.', array('@type' => $edit['label'])), 'File type was modified.'); + $this->assertText($edit['label'], 'Modified label found on file types list.'); + + // Disable and re-enable file type. + $this->drupalGet('admin/structure/file-types/manage/' . $edit['type'] . '/disable'); + $this->assertText(t('Are you sure you want to disable the file type @type?', array('@type' => $edit['label'])), 'Disable confirmation page found.'); + $this->drupalPost(NULL, array(), t('Disable')); + $this->assertText(t('The file type @type has been disabled.', array('@type' => $edit['label'])), 'Disable confirmation message found.'); + $this->assertFieldByXPath("//table//tr[5]//td[1]", $edit['label'], 'Disabled type moved to the tail of the list.'); + $this->assertLink(t('enable'), 0, 'Able to re-enable newly created file type.'); + $this->assertLinkByHref('admin/structure/file-types/manage/' . $edit['type'] . '/enable', 0, 'Enable link points to enable confirmation page.'); + $this->drupalGet('admin/structure/file-types/manage/' . $edit['type'] . '/enable'); + $this->assertText(t('Are you sure you want to enable the file type @type?', array('@type' => $edit['label'])), 'Enable confirmation page found.'); + $this->drupalPost(NULL, array(), t('Enable')); + $this->assertText(t('The file type @type has been enabled.', array('@type' => $edit['label'])), 'Enable confirmation message found.'); + $this->assertFieldByXPath("//table//tr[1]//td[1]", $edit['label'], 'Enabled type moved to the top of the list.'); + + // Delete newly created type. + $this->drupalGet('admin/structure/file-types/manage/' . $edit['type'] . '/delete'); + $this->assertText(t('Are you sure you want to delete the file type @type?', array('@type' => $edit['label'])), 'Delete confirmation page found.'); + $this->drupalPost(NULL, array(), t('Delete')); + $this->assertText(t('The file type @type has been deleted.', array('@type' => $edit['label'])), 'Delete confirmation message found.'); + $this->drupalGet('admin/structure/file-types'); + $this->assertNoText($edit['label'], 'File type successfully deleted.'); + + // Edit exported file type. + $this->drupalGet('admin/structure/file-types/manage/image/edit'); + $this->assertRaw(t('Image'), 'Label found on file type edit page'); + $this->assertText("image/*", 'Mime-type configuration found on file type edit page'); + $this->drupalPost(NULL, array('label' => t('Funky images')), t('Save')); + $this->assertText(t('The file type @type has been updated.', array('@type' => t('Funky images'))), 'File type was modified.'); + $this->assertText(t('Funky image'), 'Modified label found on file types list.'); + $this->assertFieldByXPath("//table//tr[1]//td[7]", t('Overridden'), 'Modified type overrides configuration from code.'); + $this->assertLink(t('revert'), 0, 'Able to revert overridden file type.'); + $this->assertLinkByHref('admin/structure/file-types/manage/image/revert', 0, 'Revert link points to revert confirmation page.'); + + // Revert file type. + $this->drupalGet('admin/structure/file-types/manage/image/revert'); + $this->assertText(t('Are you sure you want to revert the file type @type?', array('@type' => t('Funky images'))), 'Revert confirmation page found.'); + $this->drupalPost(NULL, array(), t('Revert')); + $this->assertText(t('The file type @type has been reverted.', array('@type' => t('Funky images'))), 'Revert confirmation message found.'); + $this->assertText(t('Image'), 'Reverted file type found in list.'); + $this->assertFieldByXPath("//table//tr[1]//td[7]", t('Default'), 'Reverted file type shows correct state.'); + } +} + +/** + * Tests the file entity access API. + */ +class FileEntityAccessTestCase extends FileEntityTestHelper { + + public static function getInfo() { + return array( + 'name' => 'File entity access', + 'description' => 'Test the access aspects of file entity.', + 'group' => 'File entity', + ); + } + + function setUp() { + parent::setUp(); + + // Remove the "view files" permission which is set by default for all users + // so we can test this permission correctly. + $roles = user_roles(); + foreach ($roles as $rid => $role) { + user_role_revoke_permissions($rid, array('view files')); + } + } + + /** + * Runs basic tests for file_entity_access function. + */ + function testFileEntityAccess() { + $file = $this->createFileEntity(array('type' => 'image')); + + // Ensures user with 'bypass file access' permission can do everything. + $web_user = $this->drupalCreateUser(array('bypass file access')); + $this->assertFileEntityAccess(array('create' => TRUE), NULL, $web_user); + $this->assertFileEntityAccess(array('view' => TRUE, 'download' => TRUE, 'update' => TRUE, 'delete' => TRUE), $file, $web_user); + + // A user with 'administer files' should not access CRUD operations. + $web_user = $this->drupalCreateUser(array('administer files')); + $this->assertFileEntityAccess(array('view' => FALSE, 'download' => FALSE, 'update' => FALSE, 'delete' => FALSE), $file, $web_user); + + // User cannot 'view files'. + $web_user = $this->drupalCreateUser(array('create files')); + $this->assertFileEntityAccess(array('view' => FALSE), $file, $web_user); + // But can upload new ones. + $this->assertFileEntityAccess(array('create' => TRUE), NULL, $web_user); + + // User can view own files but no other files. + $web_user = $this->drupalCreateUser(array('create files', 'view own files')); + $this->assertFileEntityAccess(array('view' => FALSE), $file, $web_user); + $file->uid = $web_user->uid; + $this->assertFileEntityAccess(array('view' => TRUE), $file, $web_user); + + // User can download own files but no other files. + $web_user = $this->drupalCreateUser(array('create files', 'download own image files')); + $this->assertFileEntityAccess(array('download' => FALSE), $file, $web_user); + $file->uid = $web_user->uid; + $this->assertFileEntityAccess(array('download' => TRUE), $file, $web_user); + + // User can update own files but no other files. + $web_user = $this->drupalCreateUser(array('create files', 'view own files', 'edit own image files')); + $this->assertFileEntityAccess(array('update' => FALSE), $file, $web_user); + $file->uid = $web_user->uid; + $this->assertFileEntityAccess(array('update' => TRUE), $file, $web_user); + + // User can delete own files but no other files. + $web_user = $this->drupalCreateUser(array('create files', 'view own files', 'edit own image files', 'delete own image files')); + $this->assertFileEntityAccess(array('delete' => FALSE), $file, $web_user); + $file->uid = $web_user->uid; + $this->assertFileEntityAccess(array('delete' => TRUE), $file, $web_user); + + // User can view any file. + $web_user = $this->drupalCreateUser(array('create files', 'view files')); + $this->assertFileEntityAccess(array('view' => TRUE), $file, $web_user); + + // User can download any file. + $web_user = $this->drupalCreateUser(array('create files', 'download any image files')); + $this->assertFileEntityAccess(array('download' => TRUE), $file, $web_user); + + // User can edit any file. + $web_user = $this->drupalCreateUser(array('create files', 'view files', 'edit any image files')); + $this->assertFileEntityAccess(array('update' => TRUE), $file, $web_user); + + // User can delete any file. + $web_user = $this->drupalCreateUser(array('create files', 'view files', 'edit any image files', 'delete any image files')); + $this->assertFileEntityAccess(array('delete' => TRUE), $file, $web_user); + } + + /** + * Tests page access. + * + * Verifies the privileges required to access the following pages: + * file/add + * file/%/view + * file/%/download + * file/%/edit + * file/%/usage + * file/%/delete + */ + function testFileEntityPageAccess() { + // Test creating files without permission. + $web_user = $this->drupalCreateUser(); + $this->drupalLogin($web_user); + $this->drupalGet('file/add'); + $this->assertResponse(403, 'Users without access can not access the file add page'); + + // Test creating files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'create files' => TRUE, + )); + $this->drupalGet('file/add'); + $this->assertResponse(200, 'Users with access can access the file add page'); + + $file = $this->createFileEntity(array('type' => 'document','uid' => $web_user->uid)); + + // Test viewing own files without permission. + $this->drupalGet("file/{$file->fid}/view"); + $this->assertResponse(403, 'Users without access can not view their own files'); + + // Test viewing own files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'view own files' => TRUE, + )); + $this->drupalGet("file/{$file->fid}/view"); + $this->assertResponse(200, 'Users with access can view their own files'); + + // Test viewing any files without permission. + $file->uid = 1; + file_save($file); + $this->drupalGet("file/{$file->fid}/view"); + $this->assertResponse(403, 'Users with access can not view any file'); + + // Test viewing any files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'view files' => TRUE, + )); + $this->drupalGet("file/{$file->fid}/view"); + $this->assertResponse(200, 'Users with access can view any file'); + + // Test downloading own files without permission. + $file->uid = $web_user->uid; + file_save($file); + $url = "file/{$file->fid}/download"; + $this->drupalGet($url, array('query' => array('token' => file_entity_get_download_token($file)))); + $this->assertResponse(403, 'Users without access can not download their own files'); + + // Test downloading own files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'download own document files' => TRUE, + )); + $this->drupalGet($url, array('query' => array('token' => file_entity_get_download_token($file)))); + $this->assertResponse(200, 'Users with access can download their own files'); + + // Test downloading any files without permission. + $file->uid = 1; + file_save($file); + $url = "file/{$file->fid}/download"; + $this->drupalGet($url, array('query' => array('token' => file_entity_get_download_token($file)))); + $this->assertResponse(403, 'Users without access can not download any file'); + + // Test downloading any files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'download any document files' => TRUE, + )); + $this->drupalGet($url, array('query' => array('token' => file_entity_get_download_token($file)))); + $this->assertResponse(200, 'Users with access can download any file'); + + // Test downloading files with an invalid token. + $this->drupalGet($url, array('query' => array('token' => 'invalid-token'))); + $this->assertResponse(403, 'Cannot download file with an invalid token.'); + + // Test downloading files without a token. + $this->drupalGet($url); + $this->assertResponse(403, 'Cannot download file without a token.'); + variable_set('file_entity_allow_insecure_download', TRUE); + + // Test downloading files with permission but without a token when insecure + // downloads are enabled. + $this->drupalGet($url); + $this->assertResponse(200, 'Users with access can download the file without a token when file_entity_allow_insecure_download is set.'); + + // Tests editing own files without permission. + $file->uid = $web_user->uid; + file_save($file); + $this->drupalGet("file/{$file->fid}/edit"); + $this->assertResponse(403, 'Users without access can not edit own files'); + + // Tests checking the usage of their own files without permission. + $this->drupalGet("file/{$file->fid}/usage"); + $this->assertResponse(403, 'Users without access can not check the usage of their own files'); + + // Tests editing own files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'edit own document files' => TRUE, + )); + $this->drupalGet("file/{$file->fid}/edit"); + $this->assertResponse(200, 'Users with access can edit own files'); + + // Tests checking the usage of their own files without permission. + $this->drupalGet("file/{$file->fid}/usage"); + $this->assertResponse(200, 'Users with access can check the usage of their own files'); + + // Tests editing any files without permission. + $file->uid = 1; + file_save($file); + $this->drupalGet("file/{$file->fid}/edit"); + $this->assertResponse(403, 'Users without access can not edit any file'); + + // Tests checking the usage of any files without permission. + $this->drupalGet("file/{$file->fid}/usage"); + $this->assertResponse(403, 'Users without access can not check the usage of any file'); + + // Tests editing any files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'edit any document files' => TRUE, + )); + $this->drupalGet("file/{$file->fid}/edit"); + $this->assertResponse(200, 'Users with access can edit any file'); + + // Tests checking the usage of any files with permission. + $this->drupalGet("file/{$file->fid}/usage"); + $this->assertResponse(200, 'Users with access can check the usage of any file'); + + // Tests deleting own files without permission. + $file->uid = $web_user->uid; + file_save($file); + $this->drupalGet("file/{$file->fid}/delete"); + $this->assertResponse(403, 'Users without access can not delete their own files'); + + // Tests deleting own files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'delete own document files' => TRUE, + )); + $this->drupalGet("file/{$file->fid}/delete"); + $this->assertResponse(200, 'Users with access can delete their own files'); + + // Tests deleting any files without permission. + $file->uid = 1; + file_save($file); + $this->drupalGet("file/{$file->fid}/delete"); + $this->assertResponse(403, 'Users without access can not delete any file'); + + // Tests deleting any files with permission. + user_role_change_permissions(DRUPAL_AUTHENTICATED_RID, array( + 'delete any document files' => TRUE, + )); + $this->drupalGet("file/{$file->fid}/delete"); + $this->assertResponse(200, 'Users with access can delete any file'); + } + + /** + * Test to see if we have access to download private files when granted the permissions. + */ + function testFileEntityPrivateDownloadAccess() { + foreach ($this->getPrivateDownloadAccessCases() as $case) { + // Create users and login only if non-anonymous. + $authenticated_user = !is_null($case['permissions']); + if ($authenticated_user) { + $account = $this->drupalCreateUser($case['permissions']); + $this->drupalLogin($account); + } + + // Create private, permanent files owned by this user only he's an owner. + if (!empty($case['owner'])) { + $file = $this->createFileEntity(array('type' => 'document', 'uid' => $account->uid, 'scheme' => 'private')); + + // Check if the physical file is there. + $arguments = array('%name' => $file->filename, '%username' => $account->name, '%uri' => $file->uri); + $this->assertTrue(is_file($file->uri), format_string('File %name owned by %username successfully created at %uri.', $arguments)); + $url = file_create_url($file->uri); + $message_file_info = ' ' . format_string('File %uri was checked.', array('%uri' => $file->uri)); + } + + // Try to download the file. + $this->drupalGet($url); + $this->assertResponse($case['expect'], $case['message'] . $message_file_info); + + // Logout authenticated users. + if ($authenticated_user) { + $this->drupalLogout(); + } + } + } + + /** + * Asserts file_entity_access correctly grants or denies access. + */ + function assertFileEntityAccess($ops, $file, $account) { + drupal_static_reset('file_entity_access'); + foreach ($ops as $op => $result) { + $msg = t("file_entity_access returns @result with operation '@op'.", array('@result' => $result ? 'true' : 'false', '@op' => $op)); + $this->assertEqual($result, file_entity_access($op, $file, $account), $msg); + } + } + + /** + * Helper for testFileEntityPrivateDownloadAccess() test. + * + * Defines several cases for accesing private files. + * + * @return array + * Array of associative arrays, each one having the next keys: + * - "message" string with the assertion message. + * - "permissions" array of permissions or NULL for anonymous user. + * - "expect" expected HTTP response code. + * - "owner" Optional boolean indicating if the user is a file owner. + */ + function getPrivateDownloadAccessCases() { + return array( + array( + 'message' => "File owners cannot download their own files unless they are granted the 'view own private files' permission.", + 'permissions' => array(), + 'expect' => 403, + 'owner' => TRUE, + ), + array( + 'message' => "File owners can download their own files as they have been granted the 'view own private files' permission.", + 'permissions' => array('view own private files'), + 'expect' => 200, + 'owner' => TRUE, + ), + array( + 'message' => "Anonymous users cannot download private files.", + 'permissions' => NULL, + 'expect' => 403, + ), + array( + 'message' => "Authenticated users cannot download each other's private files.", + 'permissions' => array(), + 'expect' => 403, + ), + array( + 'message' => "Users who can view public files are not able to download private files.", + 'permissions' => array('view files'), + 'expect' => 403, + ), + array( + 'message' => "Users who bypass file access can download any file.", + 'permissions' => array('bypass file access'), + 'expect' => 200, + ), + ); + } +} + +/** + * Tests overriding file attributes. + */ +class FileEntityAttributeOverrideTestCase extends FileEntityTestHelper { + + public static function getInfo() { + return array( + 'name' => 'File entity attribute override', + 'description' => 'Test overriding file entity attributes.', + 'group' => 'File entity', + ); + } + + /** + * Test to see if file attributes can be overridden. + */ + function testFileEntityFileAttributeOverrides() { + $overrides = array( + 'width' => 40, + 'height' => 20, + 'alt' => $this->randomName(), + 'title' => $this->randomName(), + + ); + + // Create an image file entity for testing. + $file = $this->createFileEntity(array('type' => 'image')); + + // Override a variety of attributes. + foreach ($overrides as $override => $value) { + $file->override['attributes'][$override] = $value; + } + + // Build just the file portion of a file entity. + $build = file_view_file($file, 'full'); + + // Verify that all of the overrides replaced the attributes. + foreach ($overrides as $attribute => $expected_value) { + $this->assertEqual($build['#file']->$attribute, $expected_value, format_string('The %attribute was overridden correctly.', array('%attribute' => $attribute))); + } + } +} |