diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..6e3139d --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,3 @@ +service_name: travis-ci +coverage_clover: coverage.clover +json_path: coveralls-upload.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e51cd25 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# This is the top-most .editorconfig file; do not search in parent directories. +root = true + +# All files. +[*] +end_of_line = LF +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8153b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/composer.lock +/vendor/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0af6a08 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: php +php: 7.1 +install: + - composer install +script: + - composer test-coverage +after_script: + - wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar + - travis_retry php coveralls.phar -v +branches: + only: + - master + - develop +notifications: + irc: "chat.freenode.net#geosocio" + email: false diff --git a/ArrayUtils.php b/ArrayUtils.php deleted file mode 100644 index a4e645d..0000000 --- a/ArrayUtils.php +++ /dev/null @@ -1,34 +0,0 @@ -toArray() : $collection; - $item = reset($collection); - while ($item !== false) { - if ($callback($item)) { - return $item; - }; - $item = next($collection); - } - - return null; - } -} diff --git a/Dispatcher/DispatcherInterface.php b/Dispatcher/DispatcherInterface.php deleted file mode 100644 index d51ea75..0000000 --- a/Dispatcher/DispatcherInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -sendGrid = $sendGrid; - } - - /** - * {@inheritdoc} - */ - public function send(MessageInterface $message) : bool - { - - $mail = $this->convertMessage($message); - - $response = $this->sendGrid->client->mail()->send()->post($mail); - - if ($response->statusCode() !== 202) { - $message = 'SendGrid Error'; - - $body = json_decode($response->body()); - - if (!empty($body->errors) && !empty($body->errors[0]->message)) { - $message = $body->errors[0]->message; - } - - throw new \Exception($message); - } - - return true; - } - - /** - * Convert Message Entity to SendGrid Mail. - * - * @param EmailMessage $message - */ - protected function convertMessage(EmailMessage $message) : Mail - { - // @TODO Make this configurable! - $from = new Email(null, "geosocio@geosoc.io"); - $to = new Email(null, $message->getTo()); - $content = new Content("text/plain", $message->getTextString()); - - return new Mail($from, $message->getSubject(), $to, $content); - } -} diff --git a/EntityAttacher.php b/EntityAttacher.php deleted file mode 100644 index 0ab03f1..0000000 --- a/EntityAttacher.php +++ /dev/null @@ -1,101 +0,0 @@ -em = $em; - $this->reader = $reader; - } - - public function attach($object) - { - if (!is_object($object)) { - throw new \InvalidArgumentException('Can only attach objects'); - } - - $object = clone $object; - - $class = get_class($object); - $metadata = $this->em->getClassMetadata($class); - - // Return the item if it's already in the database. - if ($item = $this->em->find($class, $metadata->getIdentifierValues($object))) { - return $item; - } - - $mappings = $metadata->getAssociationMappings(); - - $associations = array_filter($mappings, function ($meta) use ($object, $metadata) { - - // Ensure the property is explicitly set to cascade the attach with - // the GeoSocio\Core\Annotation\Attach annotation. It would be - // better to use the cascade option, but an unknown cascade option - // throws an exception. - $annotations = $this->reader->getPropertyAnnotations($metadata->getReflectionProperty($meta['fieldName'])); - $annotations = array_filter($annotations, function ($annotation) { - return $annotation instanceof Attach; - }); - - if (!count($annotations)) { - return false; - } - - $value = $metadata->getFieldValue($object, $meta['fieldName']); - - if (!$value) { - return false; - } - - if ($value instanceof Collection) { - return !$value->isEmpty(); - } - - return true; - }); - - - foreach ($associations as $data) { - $meta = $this->em->getClassMetadata($data['targetEntity']); - $value = $metadata->getFieldValue($object, $data['fieldName']); - - if ($value instanceof Collection) { - $item = $value->map(function ($stub) use ($data, $meta) { - $item = $this->em->find($data['targetEntity'], $meta->getIdentifierValues($stub)); - - // If the item was not found in the database, recursively call this - // method. - if (!$item) { - return $this->attach($stub); - } - - return $item; - }); - } else { - $item = $this->em->find($data['targetEntity'], $meta->getIdentifierValues($value)); - - // If the item was not found in the database, recursively call this - // method. - if (!$item) { - $item = $this->attach($value); - } - } - - $metadata->setFieldValue($object, $data['fieldName'], $item); - } - - return $object; - } -} diff --git a/EntityAttacherInterface.php b/EntityAttacherInterface.php deleted file mode 100644 index 6b18da3..0000000 --- a/EntityAttacherInterface.php +++ /dev/null @@ -1,8 +0,0 @@ - +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + +This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + +As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + +"The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + +An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + +A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + +The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + +The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + +You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + +If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + +a) under this License, provided that you make a good faith effort to +ensure that, in the event an Application does not supply the +function or data, the facility still operates, and performs +whatever part of its purpose remains meaningful, or + +b) under the GNU GPL, with none of the additional permissions of +this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. + +The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + +a) Give prominent notice with each copy of the object code that the +Library is used in it and that the Library and its use are +covered by this License. + +b) Accompany the object code with a copy of the GNU GPL and this license +document. + +4. Combined Works. + +You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + +a) Give prominent notice with each copy of the Combined Work that +the Library is used in it and that the Library and its use are +covered by this License. + +b) Accompany the Combined Work with a copy of the GNU GPL and this license +document. + +c) For a Combined Work that displays copyright notices during +execution, include the copyright notice for the Library among +these notices, as well as a reference directing the user to the +copies of the GNU GPL and this license document. + +d) Do one of the following: + +0) Convey the Minimal Corresponding Source under the terms of this +License, and the Corresponding Application Code in a form +suitable for, and under terms that permit, the user to +recombine or relink the Application with a modified version of +the Linked Version to produce a modified Combined Work, in the +manner specified by section 6 of the GNU GPL for conveying +Corresponding Source. + +1) Use a suitable shared library mechanism for linking with the +Library. A suitable mechanism is one that (a) uses at run time +a copy of the Library already present on the user's computer +system, and (b) will operate properly with a modified version +of the Library that is interface-compatible with the Linked +Version. + +e) Provide Installation Information, but only if you would otherwise +be required to provide such information under section 6 of the +GNU GPL, and only to the extent that such information is +necessary to install and execute a modified version of the +Combined Work produced by recombining or relinking the +Application with a modified version of the Linked Version. (If +you use option 4d0, the Installation Information must accompany +the Minimal Corresponding Source and Corresponding Application +Code. If you use option 4d1, you must provide the Installation +Information in the manner specified by section 6 of the GNU GPL +for conveying Corresponding Source.) + +5. Combined Libraries. + +You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + +a) Accompany the combined library with a copy of the same work based +on the Library, uncombined with any other library facilities, +conveyed under the terms of this License. + +b) Give prominent notice with the combined library that part of it +is a work based on the Library, and explaining where to find the +accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/PlaceFinder.php b/PlaceFinder.php deleted file mode 100644 index 9ae9e09..0000000 --- a/PlaceFinder.php +++ /dev/null @@ -1,190 +0,0 @@ -doctrine = $doctrine; - $this->search = $search; - $this->whosonfirst = $whosonfirst; - } - - /** - * {@inheritdoc} - */ - public function find(Location $input) : Location - { - $em = $this->doctrine->getEntityManager(); - $repository = $em->getRepository(Location::class); - - // Get all of the details from Mapzen. - $input = $this->search->get($input->getId()); - - if (!$input->getPlace()) { - throw new \Exception('Place is missing from input'); - } - - // Check to see if a location already exists. - $location = $repository->find($input->getId()); - if (!$location) { - $location = new Location([ - 'id' => $input->getId(), - 'latitude' => $input->getLatitude(), - 'longitude' => $input->getLongitude(), - ]); - $em->persist($location); - $em->flush(); - } - - // Loop through the ancestors to find a place since it's possible for a - // place to not exist. - $place = null; - try { - $place = $this->getPlace($input->getPlace()); - } catch (ClientException $e) { - if ($e->getResponse()->getStatusCode() !== 404) { - throw $e; - } - $ancestor = $input->getPlace()->getAncestors()->first(); - while ($ancestor) { - try { - $place = $this->getPlace($ancestor->getAncestor()); - break; - } catch (ClientException $e) { - if ($e->getResponse()->getStatusCode() !== 404) { - throw $e; - } - $ancestor = $input->getPlace()->getAncestors()->next(); - continue; - } - } - } - - if (!$place) { - throw new \Exception("No Place Found."); - } - - $location->setPlace($place); - $em->flush(); - - return $location; - } - - /** - * Gets a Place - * - * @param Place $input - */ - protected function getPlace(Place $input) : Place - { - $em = $this->doctrine->getEntityManager(); - $repository = $em->getRepository(Place::class); - $input = $this->whosonfirst->get($input->getId()); - - $place = $repository->find($input->getId()); - - $parent = null; - if ($input->getParent()) { - $parent = $this->getPlace($input->getParent()); - } - - if ($parent && $place) { - $place->setParent($parent); - $em->flush(); - } - - if (!$place) { - $place = new Place([ - 'id' => $input->getId(), - 'slug' => Slug::create($input->getName()), - 'parent' => $parent, - 'name' => $input->getName(), - ]); - - foreach ($this->getSlugs($place) as $slug) { - try { - $place->setSlug($slug); - $em->persist($place); - $em->flush(); - break; - } catch (UniqueConstraintViolationException $e) { - $em = $this->doctrine->resetManager(); - if ($parent = $place->getParent()) { - $parent = $em->merge($parent); - $place->setParent($parent); - } - // Try again. - } - } - - if (!$repository->find($place->getId())) { - throw new \Exception("Place was not created"); - } - } - - return $place; - } - - /** - * Get slug appends. - * - * @param Place $place - * @param string $previous - * @param array $slugs - */ - protected function getSlugs(Place $place, string $previous = '', array $slugs = []) : array - { - if (!$previous) { - $previous = $place->getSlug(); - $slugs[] = $previous; - } - - if ($parent = $place->getParent()) { - $slug = $previous . '-' . $parent->getSlug(); - $slugs[] = $slug; - $slugs = $this->getSlugs($parent, $slug, $slugs); - } - - return $slugs; - } -} diff --git a/PlaceFinderInterface.php b/PlaceFinderInterface.php deleted file mode 100644 index 78ef227..0000000 --- a/PlaceFinderInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -slug('St. Petersburg'); +// st-petersburg +``` +See more examples in `tests/SluggerTest.php` diff --git a/User/EmailVerification.php b/User/EmailVerification.php deleted file mode 100644 index 8b42f75..0000000 --- a/User/EmailVerification.php +++ /dev/null @@ -1,145 +0,0 @@ -doctrine = $doctrine; - $this->random = $random; - $this->dispatcher = $dispatcher; - } - - /** - * Create a Verification from an email address. - * - * @param string $email_address Valid email address. - * - * @return EmailVerify Newly created verify object. - */ - public function create(string $email_address) : VerifyInterface - { - $em = $this->doctrine->getManager(); - - // Get the existig email from the database. - $email = $this->findExisting($email_address); - - // If there is ane email, then there's also a user. - if (!$email) { - $email = new Email([ - 'email' => $email_address, - ]); - - $user = $em->getRepository(User::class)->createFromEmail($email); - } - - $saved = false; - while (!$saved) { - try { - $verify = new EmailVerify([ - 'email' => $email, - 'token' => $this->random->generateString(6, $this->random::CHAR_LOWER | $this->random::CHAR_DIGITS), - 'code' => $this->random->generateString(6, $this->random::CHAR_DIGITS), - ]); - - $email->setVerify($verify); - $em->persist($verify); - $em->flush(); - $saved = true; - } catch (UniqueConstraintViolationException $e) { - // Try again. - } - } - - return $verify; - } - - /** - * {@inheritdoc} - */ - public function send(VerifyInterface $verify) : bool - { - $message = new EmailMessage([ - 'to' => $verify->getEmail()->getEmail(), - 'subject' => 'Confirm Your Email (' . $verify->getCode() . ')', - 'text' => [ - 'Please visit the following location to verify your email:', - 'https://thechur.ch/v/e/' . $verify->getToken() . '/' . $verify->getCode(), - ], - ]); - - // Send the Message using Async. - return $this->dispatcher->send($message); - } - - - /** - * Finds an Existing Email. - * - * @param string $email_address Valid email_addressr. - * - * @return mixed Existing Email object or NULL. - */ - protected function findExisting(string $email_address) :? Email - { - - $em = $this->doctrine->getManager(); - - // Get the existig email from the database. - $repository = $this->doctrine->getRepository(Email::class); - - // If there is ane email, then there's also a user. - if ($email = $repository->findOneByEmail($email_address)) { - $repository = $this->doctrine->getRepository(EmailVerify::class); - - // If one is found, destroy it so a new one can be issued. - if ($verify = $repository->findOneByEmail($email_address)) { - $em->remove($verify); - $em->flush(); - } - } - - return $email; - } -} diff --git a/User/VerificationInterface.php b/User/VerificationInterface.php deleted file mode 100644 index ace0802..0000000 --- a/User/VerificationInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -verifications); - } - - /** - * {@inheritdoc} - */ - public function getVerification(string $type) : VerificationInterface - { - if (!array_key_exists($type, $this->verifications)) { - throw new \LogicException('Verification does not exist.'); - } - - return $this->verifications[$type]; - } - - /** - * {@inheritdoc} - */ - public function addVerification(VerificationInterface $verification, string $type) : self - { - $this->verifications[$type] = $verification; - - return $this; - } -} diff --git a/User/VerificationManagerInterface.php b/User/VerificationManagerInterface.php deleted file mode 100644 index 4a8ba2d..0000000 --- a/User/VerificationManagerInterface.php +++ /dev/null @@ -1,39 +0,0 @@ - + + The coding standard for GeoSoc.io + + vendor/ + diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..d35cbc7 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + ./tests + + + + + + ./src + + + diff --git a/Slug.php b/src/Slugger.php similarity index 77% rename from Slug.php rename to src/Slugger.php index 7c0bf36..2c7246f 100644 --- a/Slug.php +++ b/src/Slugger.php @@ -1,19 +1,21 @@ assertEquals($slug, $slugger->slug($text)); + } + + /** + * Data provider for slug test. + */ + public function slugs() : array + { + return [ + [ + 'Orlando', + 'orlando', + ], + [ + 'Orlando-', + 'orlando', + ], + [ + 'Saint Petersburg', + 'saint-petersburg', + ], + [ + 'Saint Petersburg', + 'saint-petersburg' + ], + [ + 'St. Petersburg', + 'st-petersburg', + ], + [ + 'Orléans', + 'orléans', + ], + [ + 'Āhualoa', + 'āhualoa', + ], + [ + 'Hōnaunau-Napoʻopoʻo', + 'hōnaunau-napoʻopoʻo', + ], + [ + 'Béal Feirste', + 'béal-feirste', + ], + [ + 'Llandygái', + 'llandygái', + ], + [ + 'Caersŵs', + 'caersŵs', + ], + [ + 'Aberdâr', + 'aberdâr', + ], + [ + 'Pentredŵr', + 'pentredŵr', + ], + [ + 'Llannerch-y-môr', + 'llannerch-y-môr', + ], + [ + '香港', + '香港', + ], + [ + '東京', + '東京', + ], + [ + 'Sydney (C)', + 'sydney-c', + ], + ]; + } +}