diff --git a/config/schema/google_vision.schema.yml b/config/schema/google_vision.schema.yml index 76f0c1a..07850ab 100644 --- a/config/schema/google_vision.schema.yml +++ b/config/schema/google_vision.schema.yml @@ -24,3 +24,6 @@ field.field.*.*.*.third_party.google_vision: emotion_detect: type: boolean label: 'Emotion Detection' + google_vision: + type: boolean + label: 'Google Vision' diff --git a/google_vision.module b/google_vision.module index 859a724..551e03b 100644 --- a/google_vision.module +++ b/google_vision.module @@ -10,7 +10,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldDefinitionInterface; - +use Drupal\taxonomy\Entity\Vocabulary; /** * Implements hook_entity_presave(). */ @@ -35,7 +35,7 @@ function google_vision_entity_presave(EntityInterface $entity) { //Use a new taxonomy vocabulary to group the objects by their dominant color. $new_vid = "dominant_color"; $name = "Dominant Color"; - $vocabularies = \Drupal\taxonomy\Entity\Vocabulary::loadMultiple(); + $vocabularies = Vocabulary::loadMultiple(); // Create a vocabulary if not already present. if (!isset($vocabularies[$new_vid])) { $vocabulary = \Drupal\taxonomy\Entity\Vocabulary::create(array( @@ -45,7 +45,9 @@ function google_vision_entity_presave(EntityInterface $entity) { )); $vocabulary->save(); } - google_vision_file_entity_dominant_color($entity, $field, $new_vid); + if (!empty($field->getSettings()['handler_settings']['target_bundles']['dominant_color'])) { + google_vision_file_entity_dominant_color($entity, $field, $new_vid); + } } } } diff --git a/google_vision.services.yml b/google_vision.services.yml index be39f19..91ddb31 100644 --- a/google_vision.services.yml +++ b/google_vision.services.yml @@ -1,4 +1,4 @@ services: google_vision.api: class: Drupal\google_vision\GoogleVisionAPI - arguments: ['@config.factory', '@http_client'] \ No newline at end of file + arguments: ['@config.factory', '@http_client'] diff --git a/src/Controller/SimilarContentController.php b/src/Controller/SimilarContentController.php index 9006afe..e16df87 100644 --- a/src/Controller/SimilarContentController.php +++ b/src/Controller/SimilarContentController.php @@ -9,21 +9,38 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\Core\Url; use Drupal\Core\Entity\EntityInterface; +use Drupal\taxonomy\Entity\Term; +use Drupal\Core\Database\Connection; /** -* Returns the related content based on dominant color of the images. -*/ + * Returns the related content based on dominant color of the images. + */ class SimilarContentController extends ControllerBase { /** + * Database Service Object. + * + * @var \Drupal\Core\Database\Connection + */ + protected $connection; + + /** + * Constructs a SimilarContentController object. + */ + public function __construct(Connection $connection) { + $this->connection = $connection; + } + + /** * Get the file title. */ public function getFileTitle($fid) { - $query = \Drupal::database()->select('file_managed', 'fm'); + $query = $this->connection->select('file_managed', 'fm'); $query->fields('fm', ['filename']); $query->condition('fm.fid', $fid); $title = $query->execute()->fetchField(); return $title; } + /** * Returns the list of image links which share the same dominant color. */ @@ -33,41 +50,56 @@ class SimilarContentController extends ControllerBase { //Get an array of just term ids. $query = \Drupal::entityQuery('taxonomy_term'); - $query->condition('vid', "dominant_color"); + $query->condition('vid', 'dominant_color'); $tids = $query->execute(); - $terms = \Drupal\taxonomy\Entity\Term::loadMultiple($tids); + $terms = Term::loadMultiple($tids); $term_id = array_keys($terms); // Get the list of dominant colors per file. $dominant_color = []; foreach ($term_id as $key => $value) { - $query = \Drupal::database()->select('file__field_labels', 'ffl'); + $query = $this->connection->select('file__field_labels', 'ffl'); $query->fields('ffl', ['field_labels_target_id']); $query->condition('ffl.entity_id', $file_id); $query->condition('ffl.field_labels_target_id', $value); $dominant_color[] = $query->execute()->fetchField(); } + $build = array(); + + if (!empty($dominant_color)) { //Get all the file ids which have one or more dominant colors in common with $dominant_color. - $query = \Drupal::database()->select('file__field_labels', 'ffl'); - $query->fields('ffl', ['entity_id', 'field_labels_target_id']); - $query->condition('field_labels_target_id', $dominant_color, 'IN'); - $files = $query->execute()->fetchAllKeyed(); + $query = $this->connection->select('file__field_labels', 'ffl'); + $query->fields('ffl', ['entity_id', 'field_labels_target_id']); + $query->condition('field_labels_target_id', $dominant_color, 'IN'); + $files = $query->execute()->fetchAllKeyed(); - $build = array(); - $build['#prefix'] = ''; + $build['#prefix'] = ''; - foreach ($files as $key => $value) { - $build[$key] = [ + foreach ($files as $key => $value) { + $build[$key] = [ '#prefix' => '
  • ', '#type' => 'link', '#title' => $this->getFileTitle($key), '#url' => Url::fromRoute('entity.file.canonical', ['file' => $key]), '#suffix' => '
  • ', + ]; + } + } + else { + $build = [ + '#markup' => t('No items found.'), ]; } return $build; } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('database')); + } } diff --git a/src/Tests/SimilarContentsTest.php b/src/Tests/SimilarContentsTest.php new file mode 100644 index 0000000..1e6070d --- /dev/null +++ b/src/Tests/SimilarContentsTest.php @@ -0,0 +1,205 @@ +adminUser = $this->drupalCreateUser([ + 'administer google vision', + 'administer files', + 'edit any image files', + 'create files', + 'administer file types', + 'administer file fields', + 'administer taxonomy', + ]); + $this->drupalLogin($this->adminUser); + //Check whether the API key is set. + $this->drupalGet(Url::fromRoute('google_vision.settings')); + $this->assertNotNull('api_key', 'The api key is set'); + } + + /** + * Uploads an image file and saves it. + * + * @param integer $count + * The index for the image file. + * + * @return integer + * The file id of the newly created image file. + */ + public function uploadImageFile($count) { + $images = $this->drupalGetTestFiles('image'); + $edit = [ + 'files[upload]' => \Drupal::service('file_system')->realpath($images[$count]->uri), + ]; + $this->drupalPostForm('file/add', $edit, t('Next')); + $this->drupalPostForm(NULL, array(), t('Next')); + $this->drupalPostForm(NULL, array(), t('Save')); + return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField(); + } + + /** + * Creates a new field for referencing taxonomy vocabulary. + * + * @param \Drupal\taxonomy\Entity\Vocabulary $vocabulary. + * The vocabulary. + */ + public function createEntityReferenceField($vocabulary) { + $entity_type = 'taxonomy_term'; + $field_name = 'field_labels'; + $field_storage = FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => 'file', + 'translatable' => FALSE, + 'settings' => array( + 'target_type' => $entity_type, + ), + 'type' => 'entity_reference', + 'cardinality' => 1, + )); + $field_storage->save(); + $field = FieldConfig::create(array( + 'field_storage' => $field_storage, + 'entity_type' => 'file', + 'bundle' => 'image', + 'settings' => array( + 'handler' => 'default', + 'handler_settings' => array( + // Restrict selection of terms to a single vocabulary. + 'target_bundles' => array( + $vocabulary->id() => $vocabulary->id(), + ), + ), + ), + )); + $field->save(); + } + + /** + * Creates and returns a new vocabulary. + * + * @param string $name. + * The name for the created vocabulary. + * + * @param string $vid. + * The vocabulary id. + * + * @return \Drupal\taxonomy\Entity\Vocabulary $vocabulary. + * The vocabulary. + */ + function createTaxonomyVocabulary($name, $vid) { + $vocabulary = Vocabulary::create([ + 'name' => $name, + 'description' => t('Stores the dominant color of the images.'), + 'vid' => $vid, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ]); + $vocabulary->save(); + return $vocabulary; + } + + /** + * Test to ensure that the Similar Contents contains the related images. + */ + public function testSimilarContents() { + $name = 'Dominant Color'; + $vid = 'dominant_color'; + $count = 0; + $vocabulary = $this->createTaxonomyVocabulary($name, $vid); + //Check whether the vocabulary is created. + $this->drupalGet(Url::fromRoute('entity.taxonomy_vocabulary.collection')); + $this->assertResponse(200); + //Create a taxonomy reference field. + $this->createEntityReferenceField($vocabulary); + $edit = [ + 'google_vision' => 1, + 'settings[handler_settings][auto_create]' => 1, + ]; + $this->drupalPostForm('admin/structure/file-types/manage/image/edit/fields/file.image.field_labels', $edit, t('Save settings')); + //Ensure that the Dominant Color option is selected. + $this->drupalGet('admin/structure/file-types/manage/image/edit/fields/file.image.field_labels'); + // Upload an image file. + $file_id = $this->uploadImageFile($count); + + //Create multiple images to be displayed in the similar contents link. + for ($count=1; $count < 3; $count++) { + $id[$count] = $this->uploadImageFile($count); + } + $this->drupalGet('file/' . $file_id . '/similarcontent'); + $this->assertResponse(200); + $this->assertNoText('No items found.', 'Similar Contents are displayed.'); + } + + /** + * Test to ensure that no similar images are present. + */ + public function testNoSimilarContents() { + $name = 'Labels'; + $vid = 'other_labels'; + $count = 0; + $vocabulary = $this->createTaxonomyVocabulary($name, $vid); + //Check whether the vocabulary is created. + $this->drupalGet(Url::fromRoute('entity.taxonomy_vocabulary.collection')); + $this->assertResponse(200); + //Create a taxonomy reference field. + $this->createEntityReferenceField($vocabulary); + $edit = [ + 'google_vision' => 1, + 'settings[handler_settings][auto_create]' => 1, + ]; + $this->drupalPostForm('admin/structure/file-types/manage/image/edit/fields/file.image.field_labels', $edit, t('Save settings')); + //Ensure that the Dominant Color option is not selected. + $this->drupalGet('admin/structure/file-types/manage/image/edit/fields/file.image.field_labels'); + // Upload an image file. + $file_id = $this->uploadImageFile($count); + + //Create multiple images to be displayed in the similar contents link. + for ($count=1; $count < 3; $count++) { + $id[$count] = $this->uploadImageFile($count); + } + $this->drupalGet('file/' . $file_id . '/similarcontent'); + $this->assertResponse(200); + $this->assertText('No items found.', 'No items found message is displayed.'); + } +} diff --git a/tests/modules/google_vision_test/src/GoogleVisionAPIFake.php b/tests/modules/google_vision_test/src/GoogleVisionAPIFake.php index c35f123..e426967 100644 --- a/tests/modules/google_vision_test/src/GoogleVisionAPIFake.php +++ b/tests/modules/google_vision_test/src/GoogleVisionAPIFake.php @@ -49,6 +49,31 @@ class GoogleVisionAPIFake extends GoogleVisionAPI { } /** + * Function to retrieve labels for given image. + * + * @param string $filepath . + * + * @return Array|bool. + */ + public function labelDetection($filepath) { + if (!$this->apiKey) { + return FALSE; + } + $response = [ + 'responses' => [ + '0' => [ + 'labelAnnotations' => [ + '0' => [ + 'description' => 'Sample Image', + ], + ], + ], + ], + ]; + return $response; + } + + /** * Function to return the response showing the image contains explicit content. * * @param string $filepath . @@ -73,4 +98,37 @@ class GoogleVisionAPIFake extends GoogleVisionAPI { ); return $response; } + + /** + * Function to retrieve image attributes for given image. + * + * @param string $filepath . + * + * @return Array|bool. + */ + public function imageAttributesDetection($filepath) { + if (!$this->apiKey) { + return FALSE; + } + $response = [ + 'responses' => [ + '0' => [ + 'imagePropertiesAnnotation' => [ + 'dominantColors' => [ + 'colors' => [ + '0' => [ + 'color' => [ + 'red' => 124, + 'blue' => 159, + 'green' => 20, + ], + ], + ], + ], + ], + ], + ], + ]; + return $response; + } }