diff --git a/modules/contacts_demo/contacts_demo.info.yml b/modules/contacts_demo/contacts_demo.info.yml
index 07d1a2c..1fcaabd 100644
--- a/modules/contacts_demo/contacts_demo.info.yml
+++ b/modules/contacts_demo/contacts_demo.info.yml
@@ -6,14 +6,3 @@ core: '8.x'
dependencies:
- contacts
- - default_content
-
-default_content:
- user:
- - 16673f6a-3196-4072-845d-5751e8be98e6
- - 096d7ac0-1358-4677-ad71-c05193ccf279
- profile:
- - 852ed573-0163-4c2f-9264-eb54f57e0c4d
- - c9e213d2-4a7a-40f4-be4b-fe03a06141b7
- - 03d148c4-8cde-4ea4-a924-bccd35a68376
- - 66ac6619-17e5-41c8-9c25-0bcb57889646
diff --git a/modules/contacts_demo/content/profile/03d148c4-8cde-4ea4-a924-bccd35a68376.json b/modules/contacts_demo/content/profile/03d148c4-8cde-4ea4-a924-bccd35a68376.json
deleted file mode 100644
index fd560fa..0000000
--- a/modules/contacts_demo/content/profile/03d148c4-8cde-4ea4-a924-bccd35a68376.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "http:\/\/default\/profile\/3?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/profile\/crm_notes"
- },
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_notes\/uid": [
- {
- "href": "http:\/\/default\/user\/3?_format=hal_json"
- }
- ]
- },
- "profile_id": [
- {
- "value": "3"
- }
- ],
- "uuid": [
- {
- "value": "03d148c4-8cde-4ea4-a924-bccd35a68376"
- }
- ],
- "revision_id": [
- {
- "value": "3"
- }
- ],
- "langcode": [
- {
- "value": "en"
- }
- ],
- "type": [
- {
- "target_id": "crm_notes"
- }
- ],
- "_embedded": {
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_notes\/uid": [
- {
- "_links": {
- "self": {
- "href": "http:\/\/default\/user\/3?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/user\/user"
- }
- },
- "uuid": [
- {
- "value": "16673f6a-3196-4072-845d-5751e8be98e6"
- }
- ]
- }
- ]
- },
- "status": [
- {
- "value": "1"
- }
- ],
- "is_default": [
- {
- "value": "1"
- }
- ],
- "created": [
- {
- "value": "1482155816"
- }
- ],
- "changed": [
- {
- "value": "1482155816"
- }
- ]
-}
\ No newline at end of file
diff --git a/modules/contacts_demo/content/profile/66ac6619-17e5-41c8-9c25-0bcb57889646.json b/modules/contacts_demo/content/profile/66ac6619-17e5-41c8-9c25-0bcb57889646.json
deleted file mode 100644
index 2b611de..0000000
--- a/modules/contacts_demo/content/profile/66ac6619-17e5-41c8-9c25-0bcb57889646.json
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "http:\/\/default\/profile\/4?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/profile\/crm_notes"
- },
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_notes\/uid": [
- {
- "href": "http:\/\/default\/user\/2?_format=hal_json"
- }
- ]
- },
- "profile_id": [
- {
- "value": "4"
- }
- ],
- "uuid": [
- {
- "value": "66ac6619-17e5-41c8-9c25-0bcb57889646"
- }
- ],
- "revision_id": [
- {
- "value": "4"
- }
- ],
- "langcode": [
- {
- "value": "en"
- }
- ],
- "type": [
- {
- "target_id": "crm_notes"
- }
- ],
- "_embedded": {
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_notes\/uid": [
- {
- "_links": {
- "self": {
- "href": "http:\/\/default\/user\/2?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/user\/user"
- }
- },
- "uuid": [
- {
- "value": "096d7ac0-1358-4677-ad71-c05193ccf279"
- }
- ]
- }
- ]
- },
- "status": [
- {
- "value": "1"
- }
- ],
- "is_default": [
- {
- "value": "1"
- }
- ],
- "created": [
- {
- "value": "1482155824"
- }
- ],
- "changed": [
- {
- "value": "1482155824"
- }
- ]
-}
\ No newline at end of file
diff --git a/modules/contacts_demo/content/profile/852ed573-0163-4c2f-9264-eb54f57e0c4d.json b/modules/contacts_demo/content/profile/852ed573-0163-4c2f-9264-eb54f57e0c4d.json
deleted file mode 100644
index 19a1fc0..0000000
--- a/modules/contacts_demo/content/profile/852ed573-0163-4c2f-9264-eb54f57e0c4d.json
+++ /dev/null
@@ -1,119 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "http:\/\/default\/profile\/1?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/profile\/crm_indiv"
- },
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_indiv\/uid": [
- {
- "href": "http:\/\/default\/user\/2?_format=hal_json"
- }
- ]
- },
- "profile_id": [
- {
- "value": "1"
- }
- ],
- "uuid": [
- {
- "value": "852ed573-0163-4c2f-9264-eb54f57e0c4d"
- }
- ],
- "revision_id": [
- {
- "value": "1"
- }
- ],
- "langcode": [
- {
- "value": "en"
- }
- ],
- "type": [
- {
- "target_id": "crm_indiv"
- }
- ],
- "_embedded": {
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_indiv\/uid": [
- {
- "_links": {
- "self": {
- "href": "http:\/\/default\/user\/2?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/user\/user"
- }
- },
- "uuid": [
- {
- "value": "096d7ac0-1358-4677-ad71-c05193ccf279"
- }
- ]
- }
- ]
- },
- "status": [
- {
- "value": "1"
- }
- ],
- "is_default": [
- {
- "value": "1"
- }
- ],
- "created": [
- {
- "value": "1482153603"
- }
- ],
- "changed": [
- {
- "value": "1482153603"
- }
- ],
- "crm_address": [
- {
- "langcode": null,
- "country_code": "GB",
- "administrative_area": null,
- "locality": "London",
- "dependent_locality": null,
- "postal_code": "W12 7JT",
- "sorting_code": null,
- "address_line1": "1 Oxford Street",
- "address_line2": "",
- "organization": "",
- "given_name": "",
- "additional_name": null,
- "family_name": "",
- "lang": "en"
- }
- ],
- "crm_dob": [
- {
- "value": "1985-01-01"
- }
- ],
- "crm_email": [
- {
- "value": "indiv@test.com",
- "lang": "en"
- }
- ],
- "crm_gender": [
- {
- "value": "male"
- }
- ],
- "crm_name": [
- {
- "value": "Individual Contact",
- "lang": "en"
- }
- ]
-}
\ No newline at end of file
diff --git a/modules/contacts_demo/content/profile/c9e213d2-4a7a-40f4-be4b-fe03a06141b7.json b/modules/contacts_demo/content/profile/c9e213d2-4a7a-40f4-be4b-fe03a06141b7.json
deleted file mode 100644
index b0bbf2b..0000000
--- a/modules/contacts_demo/content/profile/c9e213d2-4a7a-40f4-be4b-fe03a06141b7.json
+++ /dev/null
@@ -1,106 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "http:\/\/default\/profile\/2?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/profile\/crm_org"
- },
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_org\/uid": [
- {
- "href": "http:\/\/default\/user\/3?_format=hal_json"
- }
- ]
- },
- "profile_id": [
- {
- "value": "2"
- }
- ],
- "uuid": [
- {
- "value": "c9e213d2-4a7a-40f4-be4b-fe03a06141b7"
- }
- ],
- "revision_id": [
- {
- "value": "2"
- }
- ],
- "langcode": [
- {
- "value": "en"
- }
- ],
- "type": [
- {
- "target_id": "crm_org"
- }
- ],
- "_embedded": {
- "http:\/\/drupal.org\/rest\/relation\/profile\/crm_org\/uid": [
- {
- "_links": {
- "self": {
- "href": "http:\/\/default\/user\/3?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/user\/user"
- }
- },
- "uuid": [
- {
- "value": "16673f6a-3196-4072-845d-5751e8be98e6"
- }
- ]
- }
- ]
- },
- "status": [
- {
- "value": "1"
- }
- ],
- "is_default": [
- {
- "value": "1"
- }
- ],
- "created": [
- {
- "value": "1482153768"
- }
- ],
- "changed": [
- {
- "value": "1482153768"
- }
- ],
- "crm_org_address": [
- {
- "langcode": null,
- "country_code": "GB",
- "administrative_area": null,
- "locality": "Manchester",
- "dependent_locality": null,
- "postal_code": "M14 4WP",
- "sorting_code": null,
- "address_line1": "1442 Brooke Street",
- "address_line2": "",
- "organization": "",
- "given_name": "",
- "additional_name": null,
- "family_name": ""
- }
- ],
- "crm_org_email": [
- {
- "value": "org@test.com"
- }
- ],
- "crm_org_name": [
- {
- "value": "Organisation"
- }
- ]
-}
\ No newline at end of file
diff --git a/modules/contacts_demo/content/user/096d7ac0-1358-4677-ad71-c05193ccf279.json b/modules/contacts_demo/content/user/096d7ac0-1358-4677-ad71-c05193ccf279.json
deleted file mode 100644
index 237bae1..0000000
--- a/modules/contacts_demo/content/user/096d7ac0-1358-4677-ad71-c05193ccf279.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "http:\/\/default\/user\/2?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/user\/user"
- }
- },
- "uid": [
- {
- "value": "2"
- }
- ],
- "uuid": [
- {
- "value": "096d7ac0-1358-4677-ad71-c05193ccf279"
- }
- ],
- "langcode": [
- {
- "value": "en",
- "lang": "en"
- }
- ],
- "created": [
- {
- "value": "1482153603"
- }
- ],
- "changed": [
- {
- "value": "1482153659",
- "lang": "en"
- }
- ],
- "roles": [
- {
- "target_id": "crm_indiv"
- }
- ],
- "default_langcode": [
- {
- "value": "1",
- "lang": "en"
- }
- ]
-}
\ No newline at end of file
diff --git a/modules/contacts_demo/content/user/16673f6a-3196-4072-845d-5751e8be98e6.json b/modules/contacts_demo/content/user/16673f6a-3196-4072-845d-5751e8be98e6.json
deleted file mode 100644
index fa55c1f..0000000
--- a/modules/contacts_demo/content/user/16673f6a-3196-4072-845d-5751e8be98e6.json
+++ /dev/null
@@ -1,48 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "http:\/\/default\/user\/3?_format=hal_json"
- },
- "type": {
- "href": "http:\/\/drupal.org\/rest\/type\/user\/user"
- }
- },
- "uid": [
- {
- "value": "3"
- }
- ],
- "uuid": [
- {
- "value": "16673f6a-3196-4072-845d-5751e8be98e6"
- }
- ],
- "langcode": [
- {
- "value": "en",
- "lang": "en"
- }
- ],
- "created": [
- {
- "value": "1482153698"
- }
- ],
- "changed": [
- {
- "value": "1482153768",
- "lang": "en"
- }
- ],
- "roles": [
- {
- "target_id": "crm_org"
- }
- ],
- "default_langcode": [
- {
- "value": "1",
- "lang": "en"
- }
- ]
-}
\ No newline at end of file
diff --git a/modules/contacts_demo/src/CrmGeneratorBase.php b/modules/contacts_demo/src/CrmGeneratorBase.php
new file mode 100644
index 0000000..fbf99d5
--- /dev/null
+++ b/modules/contacts_demo/src/CrmGeneratorBase.php
@@ -0,0 +1,295 @@
+httpClientFactory = $http_client_factory;
+ $this->userStorage = $user_storage;
+ $this->profileStorage = $profile_storage;
+
+ if (empty(static::ROLE_NAME)) {
+ throw new \Exception(strtr('Role name not defined for @class', [
+ '@class' => __CLASS__,
+ ]));
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ $entity_type_manager = $container->get('entity_type.manager');
+ return new static(
+ $configuration, $plugin_id, $plugin_definition,
+ $container->get('http_client_factory'),
+ $entity_type_manager->getStorage('user'),
+ $entity_type_manager->getStorage('profile')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $roles = user_role_names(TRUE);
+
+ $form['num'] = array(
+ '#type' => 'number',
+ '#title' => $this->t('How many contacts would you like to generate?'),
+ '#default_value' => $this->getSetting('num'),
+ '#required' => TRUE,
+ '#min' => 0,
+ );
+
+ $form['delete'] = array(
+ '#type' => 'checkbox',
+ '#title' => $this->t('Delete all @type contacts (except user id 1) before generating new users.', [
+ '@type' => $roles[static::ROLE_NAME],
+ ]),
+ '#default_value' => $this->getSetting('delete'),
+ );
+
+ $options = $roles;
+ unset($options[AccountInterface::AUTHENTICATED_ROLE]);
+ unset($options['crm_indiv']);
+ unset($options['crm_org']);
+ $form['roles'] = array(
+ '#type' => 'checkboxes',
+ '#title' => $this->t('Which roles should the users receive?'),
+ '#description' => $this->t('Users always receive the authenticated user roles.'),
+ '#options' => $options,
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function generateElements(array $values) {
+ $num = $values['num'];
+ $delete = $values['delete'];
+ $options = [
+ 'roles' => array_filter($values['roles']),
+ ];
+ $options['roles'][] = static::ROLE_NAME;
+
+ if ($delete) {
+ $this->deleteContacts(static::ROLE_NAME);
+ }
+
+ if ($num > 0) {
+ $this->generateContacts($num, $options);
+ }
+
+ $this->setMessage($this->t('@num created.', array('@num' => $this->formatPlural($num, '1 contact', '@count contacts'))));
+ }
+
+ /**
+ * Delete contacts of a given role.
+ *
+ * @param string $role
+ * The role to delete users for.
+ */
+ public function deleteContacts($role) {
+ $uids = $this->userStorage->getQuery()
+ ->condition('uid', 1, '>')
+ ->condition('roles', $role)
+ ->execute();
+ $users = $this->userStorage->loadMultiple($uids);
+ $this->userStorage->delete($users);
+
+ $this->setMessage($this->formatPlural(count($uids), '1 contact deleted', '@count contacts deleted.'));
+ }
+
+ /**
+ * Generate a specified number of individuals based on the given options.
+ *
+ * @param int $number
+ * The number of contacts to create.
+ * @param array $options
+ * Options for the generation.
+ */
+ abstract public function generateContacts($number = 1, array $options = array());
+
+ /**
+ * Create the user for the result row.
+ *
+ * @param \stdClass $result
+ * The result row to generate for.
+ * @param array $options
+ * The options for the generation.
+ *
+ * @return \Drupal\user\UserInterface
+ * The generated user.
+ */
+ protected function createUser(\stdClass $result, array $options = array()) {
+ $options += [
+ 'coupled' => 2,
+ 'roles' => [],
+ ];
+ $options['roles'][] = static::ROLE_NAME;
+ $options['roles'] = array_unique($options['roles']);
+
+ // Generate some reasonable timestamps.
+ $created = strtotime($result->registered);
+ $login = $created + rand(0, time() - $created);
+ $access = $login + rand(0, 86400);
+ $changed = $access + rand(0, time() - $access);
+
+ $user_values = [
+ 'mail' => $result->email,
+ 'status' => 1,
+ 'created' => $created,
+ 'changed' => $changed,
+ 'roles' => $options['roles'],
+ ];
+
+ // If we're doing a coupled user, add the additional info.
+ if ($this->getRandomDecision($options['coupled'])) {
+ $user_values += [
+ 'name' => $result->login->username,
+ 'pass' => $result->login->password,
+ 'access' => $access,
+ 'login' => $login,
+ ];
+ }
+
+ /* @var \Drupal\user\UserInterface $user */
+ $user = $this->userStorage->create($user_values);
+ $user->save();
+ return $user;
+ }
+
+ /**
+ * Create and save a profile of the given type, connecting it to the user.
+ *
+ * @param $type
+ * The profile type.
+ * @param array $values
+ * The values to store.
+ * @param \Drupal\user\UserInterface $user
+ * The user to connect it with.
+ *
+ * @return \Drupal\profile\Entity\ProfileInterface
+ * The generated profile.
+ */
+ protected function createProfile($type, array $values, UserInterface $user) {
+ $values['type'] = $type;
+ $values['uid'] = $user->id();
+ $values += [
+ 'status' => 1,
+ 'is_default' => 1,
+ 'created' => $user->getCreatedTime(),
+ 'changed' => $user->getChangedTime(),
+ ];
+
+ /* @var \Drupal\profile\Entity\ProfileInterface $profile */
+ $profile = $this->profileStorage->create($values);
+ $profile->save();
+ return $profile;
+ }
+
+ /**
+ * Make a randomized decision with a given likelihood.
+ *
+ * @param bool|int $option
+ * Either a boolean indicating we should always return the same answer, or
+ * in integer indicating the likelihood (e.g. 1 in $option will be TRUE).
+ * Negative numbers reverse the result (e.g. 1 in $option will be FALSE).
+ *
+ * @return bool
+ * The random answer.
+ *
+ * @throws \Exception
+ * Thrown if the option is invalid.
+ */
+ protected function getRandomDecision($option) {
+ // Check option is valid.
+ if (!is_bool($option) && (!is_int($option) || $option === 0)) {
+ throw new \Exception(strtr('Invalid argument "@option" for @method.', [
+ '@option' => $option,
+ '@method' => __METHOD__,
+ ]));
+ }
+
+ // If it's a boolean, we simply return it.
+ if (is_bool($option)) {
+ return $option;
+ }
+
+ // Otherwise, generate a random number between 0 and it - 1.
+ $rand = rand(0, abs($option - 1));
+
+ // If our original option was positive, we want to return whether it's zero.
+ // Otherwise return if it's non zero.
+ return $rand === 0 xor $option < 0;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateDrushParams($args) {
+ $values = array(
+ 'num' => array_shift($args),
+ 'roles' => drush_get_option('roles') ? explode(',', drush_get_option('roles')) : array(),
+ 'delete' => drush_get_option('delete'),
+ );
+ return $values;
+ }
+
+}
diff --git a/modules/contacts_demo/src/Plugin/DevelGenerate/CrmIndivGenerate.php b/modules/contacts_demo/src/Plugin/DevelGenerate/CrmIndivGenerate.php
new file mode 100644
index 0000000..9fb03b3
--- /dev/null
+++ b/modules/contacts_demo/src/Plugin/DevelGenerate/CrmIndivGenerate.php
@@ -0,0 +1,149 @@
+ $this->t('Individuals are generated using data provided by randomuser.me.', [
+ ':url' => 'https://randomuser.me',
+ ]),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generateContacts($number = 1, array $options = array()) {
+ // Set some default options.
+ $options += [
+ 'api' => [
+ 'nat' => 'gb',
+ ]
+ ];
+
+
+ // Enforce a couple API options.
+ $options['api']['results'] = $number;
+ $options['api'][] = 'noinfo';
+
+ // Retrieve our results.
+ $client = $this->httpClientFactory->fromOptions();
+ $results = $client->request('GET', 'https://randomuser.me/api', [
+ 'query' => $options['api'],
+ ]);
+ $results = json_decode($results->getBody());
+
+ // Loop over and create the users and associated entities.
+ foreach ($results->results as $result) {
+ $user = $this->createUser($result, $options);
+ $this->createProfileIndiv($result, $user, $options);
+ $this->createProfileNotes($result, $user, $options);
+ }
+ }
+
+ /**
+ * Create the individual profile for the result row.
+ *
+ * @param \stdClass $result
+ * The result row to generate for.
+ * @param \Drupal\user\UserInterface $user
+ * The user account to attach the profile to.
+ * @param array $options
+ * The options for the generation.
+ *
+ * @return \Drupal\profile\Entity\ProfileInterface
+ * The generated profile.
+ */
+ protected function createProfileIndiv(\stdClass $result, UserInterface $user, $options = array()) {
+ $options += [
+ 'photo' => -10,
+ ];
+
+ $values = [
+ 'crm_name' => ucwords("{$result->name->title} {$result->name->first} {$result->name->last}"),
+ 'crm_gender' => $result->gender,
+ 'crm_email' => $result->email,
+ 'crm_dob' => substr($result->dob, 0, 10),
+ 'crm_address' => [
+ 'country_code' => 'GB',
+ 'address_line1' => ucwords($result->location->street),
+ 'locality' => ucwords($result->location->city),
+ 'postal_code' => strtoupper($result->location->postcode),
+ ],
+ 'crm_phone' => '07000 ' . str_pad(rand(0, 999999), 6, '0', STR_PAD_LEFT),
+ ];
+
+ // Generate our photo if required.
+ if ($this->getRandomDecision($options['photo'])) {
+ $photo = file_save_data(file_get_contents($result->picture->large));
+ $values['crm_photo'] = $photo->id();
+ }
+
+ return $this->createProfile('crm_indiv', $values, $user);
+ }
+
+ /**
+ * Create the notes profile for the result row.
+ *
+ * @param \stdClass $result
+ * The result row to generate for.
+ * @param \Drupal\user\UserInterface $user
+ * The user account to attach the profile to.
+ * @param array $options
+ * The options for the generation.
+ *
+ * @return \Drupal\profile\Entity\ProfileInterface
+ * The generated profile.
+ */
+ protected function createProfileNotes(\stdClass $result, UserInterface $user, $options = array()) {
+ $options += [
+ 'notes' => 3,
+ 'notes_content' => 2,
+ ];
+
+ if (!$this->getRandomDecision($options['notes'])) {
+ return NULL;
+ }
+
+ $values = [
+ 'crm_notes' => $this->getRandomDecision($options['notes_content']) ? "{$result->id->name}: {$result->id->value}" : '',
+ ];
+
+ return $this->createProfile('crm_notes', $values, $user);
+ }
+
+}
diff --git a/modules/contacts_demo/src/Plugin/DevelGenerate/CrmOrgGenerate.php b/modules/contacts_demo/src/Plugin/DevelGenerate/CrmOrgGenerate.php
new file mode 100644
index 0000000..4999d7b
--- /dev/null
+++ b/modules/contacts_demo/src/Plugin/DevelGenerate/CrmOrgGenerate.php
@@ -0,0 +1,153 @@
+ $this->t('Organisation are generated using data provided by www.mockaroo.com with images from www.flamingtext.co.uk.', [
+ ':data-url' => 'http://www.mockaroo.com/',
+ ':image-url' => 'http://www.flamingtext.co.uk/',
+ ]),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function generateContacts($number = 1, array $options = array()) {
+ // Organisations never have accounts.
+ $options['coupled'] = FALSE;
+
+ // Retrieve our results.
+ $client = $this->httpClientFactory->fromOptions();
+ $results = $client->request('GET', 'https://www.mockaroo.com/dddbdc80/download?count=10&key=ee1f5f90', [
+ 'query' => [
+ 'count' => $number,
+ 'key' => 'ee1f5f90',
+ ],
+ ]);
+ $results = json_decode($results->getBody());
+
+ // Loop over and create the users and associated entities.
+ foreach ($results as $result) {
+ $user = $this->createUser($result, $options);
+ $this->createProfileOrg($result, $user, $options);
+ $this->createProfileNotes($result, $user, $options);
+ }
+ }
+
+ /**
+ * Create the individual profile for the result row.
+ *
+ * @param \stdClass $result
+ * The result row to generate for.
+ * @param \Drupal\user\UserInterface $user
+ * The user account to attach the profile to.
+ * @param array $options
+ * The options for the generation.
+ *
+ * @return \Drupal\profile\Entity\ProfileInterface
+ * The generated profile.
+ */
+ protected function createProfileOrg(\stdClass $result, UserInterface $user, $options = array()) {
+ $options += [
+ 'logo' => -10,
+ ];
+
+ $values = [
+ 'crm_org_name' => $result->name,
+ 'crm_org_email' => $result->email,
+ 'crm_org_address' => [
+ 'country_code' => 'GB',
+ 'address_line1' => ucwords($result->location->street),
+ 'locality' => ucwords($result->location->city),
+ 'postal_code' => strtoupper($result->location->postcode),
+ ],
+ 'crm_phone' => '07000 ' . str_pad(rand(0, 999999), 6, '0', STR_PAD_LEFT),
+ ];
+
+ // Generate our photo if required.
+ if ($this->getRandomDecision($options['logo'])) {
+ $designs = ['minions', 'comics', 'blackbird'];
+ $query = [
+ 'script' => $designs[rand(0, count($designs) - 1)] . '-logo',
+ '_loc' => 'generate',
+ 'imageoutput' => 'true',
+ 'fillTextType' => 0,
+ 'fillTextColor' => '#' . dechex(rand(0, 15)) . dechex(rand(0, 15)) . dechex(rand(0, 15)) . dechex(rand(0, 15)) . dechex(rand(0, 15)) . dechex(rand(0, 15)),
+ 'text' => $result->name,
+ ];
+ $url = 'http://www.flamingtext.co.uk/net-fu/proxy_form.cgi?' . http_build_query($query);
+
+ $photo = file_save_data(file_get_contents($url));
+ $values['crm_logo'] = $photo->id();
+ }
+
+ return $this->createProfile('crm_org', $values, $user);
+ }
+
+ /**
+ * Create the notes profile for the result row.
+ *
+ * @param \stdClass $result
+ * The result row to generate for.
+ * @param \Drupal\user\UserInterface $user
+ * The user account to attach the profile to.
+ * @param array $options
+ * The options for the generation.
+ *
+ * @return \Drupal\profile\Entity\ProfileInterface
+ * The generated profile.
+ */
+ protected function createProfileNotes(\stdClass $result, UserInterface $user, $options = array()) {
+ $options += [
+ 'notes' => 3,
+ 'notes_content' => 2,
+ ];
+
+ if (!$this->getRandomDecision($options['notes'])) {
+ return NULL;
+ }
+
+ $values = [
+ 'crm_notes' => $this->getRandomDecision($options['notes_content']) ? $result->slogan : '',
+ ];
+
+ return $this->createProfile('crm_notes', $values, $user);
+ }
+
+}
diff --git a/modules/contacts_demo/src/RandomUser.php b/modules/contacts_demo/src/RandomUser.php
new file mode 100644
index 0000000..74a955f
--- /dev/null
+++ b/modules/contacts_demo/src/RandomUser.php
@@ -0,0 +1,127 @@
+httpClientFactory = $http_client_factory;
+ $this->entityTypeManager = $entity_type_manager;
+ }
+
+ public function generateUsers($number = 1, $options = array()) {
+ // Set some default options.
+ $options += [
+ 'photos' => 10,
+ 'api' => [
+ 'nat' => 'gb',
+ ]
+ ];
+
+ // Enforce a couple API options.
+ $options['api']['results'] = $number;
+ $options['api'][] = 'noinfo';
+
+ // Retrieve our results.
+ $results = $this->httpClientFactory->fromOptions()->request('https://randomuser.me/api', [
+ 'query' => $options['api'],
+ ]);
+ $results = json_decode($results);
+
+ // Loop over and create the users and associated entities.
+ foreach ($results->results as $result) {
+ $user = $this->createUser($result, $options);
+ $this->createProfileIndiv($result, $user, $options);
+ $this->createProfileNotes($result, $user, $options);
+ }
+ }
+
+ protected function createUser(\stdClass $result, $options = array()) {
+ // Generate some reasonable timestamps.
+ $created = strtotime($result->registered);
+ $login = $created + rand(0, time() - $created);
+ $access = $login + rand(0, 86400);
+ $changed = $access + rand(0, time() - $access);
+
+ $user_values = [
+ 'mail' => $result->email,
+ 'status' => 1,
+ 'created' => $created,
+ 'changed' => $changed,
+ 'roles' => array('crm_indiv'),
+ ];
+
+ // If we're doing a coupled user, add the additional info.
+ if ($this->getRandomDecision($options['coupled']) ?? 2) {
+ $user_values += [
+ 'name' => $result->login->username,
+ 'pass' => $result->login->password,
+ 'access' => $access,
+ 'login' => $login,
+ ];
+ }
+
+ $user = $this->entityTypeManager->getStorage('user')->create($user_values);
+ $user->save();
+ return $user;
+ }
+
+ /**
+ * Make a randomized decision with a given likelihood.
+ *
+ * @param bool|int $option
+ * Either a boolean indicating we should always return the same answer, or
+ * in integer indicating the likelihood (e.g. 1 in $option will be TRUE).
+ * Negative numbers reverse the result (e.g. 1 in $option will be FALSE).
+ *
+ * @return bool
+ * The random answer.
+ *
+ * @throws \Exception
+ * Thrown if the option is invalid.
+ */
+ protected function getRandomDecision($option) {
+ // Check option is valid.
+ if (!is_bool($option) && (!is_int($option) || $option === 0)) {
+ throw new \Exception(strtr('Invalid argument "@option" for @method.', [
+ '@option' => $option,
+ '@method' => __METHOD__,
+ ]));
+ }
+
+ // If it's a boolean, we simply return it.
+ if (is_bool($option)) {
+ return $option;
+ }
+
+ // Otherwise, generate a random number between 0 and it - 1.
+ $rand = rand(0, abs($option - 1));
+
+ // If our original option was positive, we want to return whether it's zero.
+ // Otherwise return if it's non zero.
+ return $rand === 0 xor $option < 0;
+ }
+}