Repository: delight-im/PHP-PrivacyPolicy Branch: master Commit: 4fb4bfc70522 Files: 49 Total size: 313.1 KB Directory structure: gitextract_ui_n7_0g/ ├── .editorconfig ├── .gitignore ├── LICENSE ├── Migration.md ├── README.md ├── Sorting.md ├── composer.json ├── src/ │ ├── Data/ │ │ ├── DataBasis.php │ │ ├── DataElement.php │ │ ├── DataGroup.php │ │ ├── DataPurpose.php │ │ ├── DataRequirement.php │ │ ├── DataSpecialCondition.php │ │ └── DataType.php │ ├── HumanPrivacyPolicy.php │ ├── Language/ │ │ ├── EnglishPrivacyPolicy.php │ │ ├── GermanFormalPrivacyPolicy.php │ │ ├── GermanInformalPrivacyPolicy.php │ │ ├── GermanPrivacyPolicy.php │ │ └── JsonPrivacyPolicy.php │ ├── MachinePrivacyPolicy.php │ ├── Markup/ │ │ ├── AbbreviationMarkup.php │ │ ├── ConcatenationMarkup.php │ │ ├── DefinitionList/ │ │ │ ├── DefinitionGroup.php │ │ │ └── DefinitionList.php │ │ ├── ImageMarkup.php │ │ ├── LinkMarkup.php │ │ ├── Markup.php │ │ ├── SimpleMarkup.php │ │ └── TextMarkup.php │ ├── PrivacyPolicy.php │ ├── Scope/ │ │ ├── AndroidAppScope.php │ │ ├── AppStoreIosAppScope.php │ │ ├── IosAppScope.php │ │ ├── MobileAppScope.php │ │ ├── PlayStoreAndroidAppScope.php │ │ ├── Scope.php │ │ └── WebsiteScope.php │ └── Throwable/ │ ├── Error.php │ ├── Exception.php │ ├── InvalidFormatArgumentsError.php │ ├── TranslationNotFoundError.php │ ├── UnexpectedDataBasisError.php │ ├── UnexpectedDataPurposeError.php │ ├── UnexpectedDataRequirementError.php │ ├── UnexpectedDataSpecialConditionError.php │ ├── UnexpectedDataTypeError.php │ └── UnexpectedScopeError.php └── tests/ └── index.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] charset = utf-8 indent_style = tab trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true [*.md] indent_style = space indent_size = 4 ================================================ FILE: .gitignore ================================================ # IntelliJ .idea/ # Composer vendor/ composer.phar ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) delight.im (https://www.delight.im/) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Migration.md ================================================ # Migration * [General](#general) * [From `v1.x.x` to `v2.x.x`](#from-v1xx-to-v2xx) ## General Update your version of this library using Composer and its `composer update` or `composer require` commands [[?]](https://github.com/delight-im/Knowledge/blob/master/Composer%20(PHP).md#how-do-i-update-libraries-or-modules-within-my-application). ## From `v1.x.x` to `v2.x.x` * The Internationalization extension (`intl`) for PHP is now required. * The method `setLastUpdated` from class `PrivacyPolicy` has been renamed to `setPublishedAt`. * The method `hasLastUpdated` from class `PrivacyPolicy` has been removed. * The method `setExpiration` from class `PrivacyPolicy` has been renamed to `setExpiresAt`. * The method `hasExpiration` from class `PrivacyPolicy` has been removed. * For method `addDataGroup` from class `PrivacyPolicy`, between the second parameter (named “description”) and the former third parameter (named “purposes”), two new parameters (named “bases” and “specialConditions”) have been added. * For the constructor of class `DataGroup`, between the second parameter (named “description”) and the former third parameter (named “purposes”), two new parameters (named “bases” and “specialConditions”) have been added. * For method `addElement` from class `DataGroup`, the fourth parameter (named “viewable”) and the fifth parameter (named “deletable”) have been removed. * For the constructor of class `DataElement`, the fourth parameter (named “viewable”) and the fifth parameter (named “deletable”) have been removed. * The methods `isViewable` and `isDeletable` from class `DataElement` have been removed. * The method `setRightToInformation` from class `PrivacyPolicy` has been replaced by the five separate methods `setRightOfAccess`, `setRightToRectification`, `setRightToErasure`, `setRightToRestrictProcessing` and `setRightToObject`. * The method `hasRightToInformation` from class `PrivacyPolicy` has been removed. * The key `meta.updated` in the JSON output has been renamed to `meta.published`. * The attributes `viewable` and `deletable` in `data[*].elements[*]` in the JSON output have been removed. * For `data[*].requirement` and `data[*].elements[*].requirement` in the JSON output, all occurrences of the value `opt_in` have been replaced with `optIn`, and all occurrences of `opt_out` have been replaced with `optOut`. * The key `choices.information.request` in the JSON output has been renamed to `choices.data.access`. * The key `choices.information.update` in the JSON output has been renamed to `choices.data.rectification`. * The key `choices.information.delete` in the JSON output has been renamed to `choices.data.erasure`. ================================================ FILE: README.md ================================================ # PHP-PrivacyPolicy Programmatically composable privacy policies for [humans](../../tree/examples/Humans) and [machines](../../tree/examples/Machines) ## Requirements * PHP 5.6.0+ * Internationalization extension (`intl`) ## Installation 1. Include the library via Composer [[?]](https://github.com/delight-im/Knowledge/blob/master/Composer%20(PHP).md): ``` $ composer require delight-im/privacy-policy ``` 1. Include the Composer autoloader: ```php require __DIR__ . '/vendor/autoload.php'; ``` ## Upgrading Migrating from an earlier version of this project? See our [upgrade guide](Migration.md) for help. ## Usage * [Creating an instance](#creating-an-instance) * [Privacy policies for humans in natural language](#privacy-policies-for-humans-in-natural-language) * [Privacy policies for machines in formal language](#privacy-policies-for-machines-in-formal-language) * [Displaying or printing an instance](#displaying-or-printing-an-instance) * [Privacy policies for humans in natural language](#privacy-policies-for-humans-in-natural-language-1) * [Privacy policies for machines in formal language](#privacy-policies-for-machines-in-formal-language-1) * [Defining metadata](#defining-metadata) * [Describing your privacy practices](#describing-your-privacy-practices) * [Explaining the amount, type and purpose of the data you collect](#explaining-the-amount-type-and-purpose-of-the-data-you-collect) * [Lawful bases](#lawful-bases) * [Special conditions](#special-conditions) * [Data purposes](#data-purposes) * [Data requirements](#data-requirements) * [Data types](#data-types) * [Primary](#primary) * [Secondary](#secondary) * [Tertiary](#tertiary) * [Specifying the scope of your policy](#specifying-the-scope-of-your-policy) * [Configuring an instance](#configuring-an-instance) * [Privacy policies for machines in formal language](#privacy-policies-for-machines-in-formal-language-2) ### Creating an instance #### Privacy policies for humans in natural language ```php $policy = new \Delight\PrivacyPolicy\Language\EnglishPrivacyPolicy(); // or $policy = new \Delight\PrivacyPolicy\Language\GermanFormalPrivacyPolicy(); // or $policy = new \Delight\PrivacyPolicy\Language\GermanInformalPrivacyPolicy(); ``` #### Privacy policies for machines in formal language ```php $policy = new \Delight\PrivacyPolicy\Language\JsonPrivacyPolicy(); ``` ### Displaying or printing an instance #### Privacy policies for humans in natural language ```php $policy->toHtml(); // or $policy->toPlainText(); // or $policy->toMarkdown(); ``` #### Privacy policies for machines in formal language ```php $policy->toJson(); ``` ### Defining metadata ```php $policy->setPublishedAt(1393372800); $policy->setTakesEffectAt(1394582400); $policy->setExpiresAt(1395792000); $policy->setVersionName('v3.1.4'); $policy->setCanonicalUrl('https://www.example.com/privacy.html'); $policy->setContactEmail('privacy@example.com'); $policy->setContactUrl('https://www.example.com/contact.html'); // $policy->setContactImage('https://www.example.com/images/contact.png', 'Jane Doe, 123 Main Street, Anytown, USA', 420, 360); ``` ### Describing your privacy practices ```php $policy->setUserDataTraded(false); $policy->setDataMinimizationGoal(true); $policy->setChildrenMinimumAge(16); $policy->setPromotionalEmailOptOut(true); $policy->setFirstPartyCookies(true); $policy->setThirdPartyCookies(true); $policy->setAccountDeletable(true); $policy->setPreservationInBackups(true); $policy->setThirdPartyServiceProviders(true); $policy->setInternationalTransfers(true); $policy->setTransferUponMergerOrAcquisition(true); $policy->setTlsEverywhere(true); $policy->setCompetentSupervisoryAuthority('Estonian Data Protection Inspectorate', 'http://www.aki.ee/en'); $policy->setNotificationPeriod(30); $policy->setRightOfAccess(true); $policy->setRightToRectification(true); $policy->setRightToErasure(true); $policy->setRightToRestrictProcessing(true); $policy->setRightToDataPortability(true); $policy->setRightToObject(true); $policy->setRightsRelatedToAutomatedDecisions(true); ``` ### Explaining the amount, type and purpose of the data you collect ```php $policy->addDataGroup( 'Server logs', 'Whenever you access our services, ...', [ \Delight\PrivacyPolicy\Data\DataBasis::LEGITIMATE_INTERESTS ], null, // [ \Delight\PrivacyPolicy\Data\DataSpecialCondition::EXPLICIT_CONSENT ] [ \Delight\PrivacyPolicy\Data\DataPurpose::ADMINISTRATION ], \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, function (\Delight\PrivacyPolicy\Data\DataGroup $group) { $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_IP_ADDRESS, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, 24 * 7 ); } ); ``` #### Lawful bases ```php \Delight\PrivacyPolicy\Data\DataBasis::CONSENT; \Delight\PrivacyPolicy\Data\DataBasis::CONTRACT; \Delight\PrivacyPolicy\Data\DataBasis::LEGAL_OBLIGATION; \Delight\PrivacyPolicy\Data\DataBasis::LEGITIMATE_INTERESTS; \Delight\PrivacyPolicy\Data\DataBasis::PUBLIC_INTEREST; \Delight\PrivacyPolicy\Data\DataBasis::VITAL_INTERESTS; ``` #### Special conditions ```php \Delight\PrivacyPolicy\Data\DataSpecialCondition::ARCHIVING_OR_RESEARCH; \Delight\PrivacyPolicy\Data\DataSpecialCondition::EMPLOYMENT_AND_SOCIAL_SECURITY; \Delight\PrivacyPolicy\Data\DataSpecialCondition::EXPLICIT_CONSENT; \Delight\PrivacyPolicy\Data\DataSpecialCondition::FOUNDATION_ASSOCIATION_OR_NON_PROFIT; \Delight\PrivacyPolicy\Data\DataSpecialCondition::HEALTH_AND_SOCIAL_CARE; \Delight\PrivacyPolicy\Data\DataSpecialCondition::LEGAL_CLAIMS_OR_JUDICIAL_CAPACITY; \Delight\PrivacyPolicy\Data\DataSpecialCondition::PUBLIC_DATA; \Delight\PrivacyPolicy\Data\DataSpecialCondition::PUBLIC_HEALTH; \Delight\PrivacyPolicy\Data\DataSpecialCondition::SUBSTANTIAL_PUBLIC_INTEREST; \Delight\PrivacyPolicy\Data\DataSpecialCondition::VITAL_INTERESTS; ``` #### Data purposes ```php \Delight\PrivacyPolicy\Data\DataPurpose::ADMINISTRATION; \Delight\PrivacyPolicy\Data\DataPurpose::ADVERTISING; \Delight\PrivacyPolicy\Data\DataPurpose::CUSTOMER_SUPPORT; \Delight\PrivacyPolicy\Data\DataPurpose::FULFILLMENT; \Delight\PrivacyPolicy\Data\DataPurpose::MARKETING; \Delight\PrivacyPolicy\Data\DataPurpose::PERSONALIZATION; \Delight\PrivacyPolicy\Data\DataPurpose::RESEARCH; ``` #### Data requirements ```php \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS; \Delight\PrivacyPolicy\Data\DataRequirement::OPT_IN; \Delight\PrivacyPolicy\Data\DataRequirement::OPT_OUT; ``` #### Data types ##### Primary ```php \Delight\PrivacyPolicy\Data\DataType::ACCESS_DATETIME; \Delight\PrivacyPolicy\Data\DataType::ACCESS_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::ACCESS_DATETIME_TIME; \Delight\PrivacyPolicy\Data\DataType::ACCESS_HTTP_METHOD; \Delight\PrivacyPolicy\Data\DataType::ACCESS_HTTP_STATUS; \Delight\PrivacyPolicy\Data\DataType::ACCESS_IP_ADDRESS; \Delight\PrivacyPolicy\Data\DataType::ACCESS_IP_ADDRESS_25_PERCENT; \Delight\PrivacyPolicy\Data\DataType::ACCESS_IP_ADDRESS_50_PERCENT; \Delight\PrivacyPolicy\Data\DataType::ACCESS_IP_ADDRESS_75_PERCENT; \Delight\PrivacyPolicy\Data\DataType::ACCESS_REFERER; \Delight\PrivacyPolicy\Data\DataType::ACCESS_SIZE; \Delight\PrivacyPolicy\Data\DataType::ACCESS_URL; \Delight\PrivacyPolicy\Data\DataType::ACCESS_USERAGENT_STRING; \Delight\PrivacyPolicy\Data\DataType::BILLING_CANCELLATION_TIME; \Delight\PrivacyPolicy\Data\DataType::BILLING_END_TIME; \Delight\PrivacyPolicy\Data\DataType::BILLING_FREE_TRIAL; \Delight\PrivacyPolicy\Data\DataType::BILLING_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::BILLING_NEXT_PAYMENT_TIME; \Delight\PrivacyPolicy\Data\DataType::BILLING_PAST_DUE; \Delight\PrivacyPolicy\Data\DataType::BILLING_PLAN; \Delight\PrivacyPolicy\Data\DataType::BILLING_START_TIME; \Delight\PrivacyPolicy\Data\DataType::CUSTOMER_NUMBER; \Delight\PrivacyPolicy\Data\DataType::DEVICE_BROWSER; \Delight\PrivacyPolicy\Data\DataType::DEVICE_BROWSER_BRAND; \Delight\PrivacyPolicy\Data\DataType::DEVICE_BROWSER_VERSION; \Delight\PrivacyPolicy\Data\DataType::DEVICE_DATETIME_TIME_ZONE; \Delight\PrivacyPolicy\Data\DataType::DEVICE_ID; \Delight\PrivacyPolicy\Data\DataType::DEVICE_ID_IMEI; \Delight\PrivacyPolicy\Data\DataType::DEVICE_ID_MAC_ADDRESS; \Delight\PrivacyPolicy\Data\DataType::DEVICE_ID_PERMANENT; \Delight\PrivacyPolicy\Data\DataType::DEVICE_ID_RESETTABLE; \Delight\PrivacyPolicy\Data\DataType::DEVICE_LANGUAGE; \Delight\PrivacyPolicy\Data\DataType::DEVICE_LOCATION_APPROXIMATE; \Delight\PrivacyPolicy\Data\DataType::DEVICE_LOCATION_PRECISE; \Delight\PrivacyPolicy\Data\DataType::DEVICE_MANUFACTURER; \Delight\PrivacyPolicy\Data\DataType::DEVICE_MODEL; \Delight\PrivacyPolicy\Data\DataType::DEVICE_OS; \Delight\PrivacyPolicy\Data\DataType::DEVICE_OS_BRAND; \Delight\PrivacyPolicy\Data\DataType::DEVICE_OS_VERSION; \Delight\PrivacyPolicy\Data\DataType::USER_ACCESS_PRIVILEGES; \Delight\PrivacyPolicy\Data\DataType::USER_ADDRESS; \Delight\PrivacyPolicy\Data\DataType::USER_ADDRESS_COUNTRY; \Delight\PrivacyPolicy\Data\DataType::USER_ADDRESS_LOCALITY; \Delight\PrivacyPolicy\Data\DataType::USER_ADDRESS_PLACE; \Delight\PrivacyPolicy\Data\DataType::USER_ADDRESS_POSTAL_CODE; \Delight\PrivacyPolicy\Data\DataType::USER_ADDRESS_REGION; \Delight\PrivacyPolicy\Data\DataType::USER_BIRTH_DATE; \Delight\PrivacyPolicy\Data\DataType::USER_BIRTH_DATE_MONTH_DAY; \Delight\PrivacyPolicy\Data\DataType::USER_BIRTH_DATE_YEAR; \Delight\PrivacyPolicy\Data\DataType::USER_BIRTH_DATE_YEAR_MONTH; \Delight\PrivacyPolicy\Data\DataType::USER_BIRTH_PLACE; \Delight\PrivacyPolicy\Data\DataType::USER_BLOOD_GROUP; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_DEPARTMENT; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_LOGO; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_NAME; \Delight\PrivacyPolicy\Data\DataType::USER_COUNTRY; \Delight\PrivacyPolicy\Data\DataType::USER_EMAIL; \Delight\PrivacyPolicy\Data\DataType::USER_EMAIL_VERIFIED; \Delight\PrivacyPolicy\Data\DataType::USER_FAX; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_BANK_ACCOUNT_ID; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_BANK_ID; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_BANK_NAME; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_CREDIT_CARD_BRAND; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_CREDIT_CARD_CVC; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_CREDIT_CARD_EXPIRATION; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_CREDIT_CARD_NUMBER; \Delight\PrivacyPolicy\Data\DataType::USER_GENDER; \Delight\PrivacyPolicy\Data\DataType::USER_GEO_COORDINATES; \Delight\PrivacyPolicy\Data\DataType::USER_HEIGHT; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_DEU_ST_IDNR; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_DEU_ST_NR; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_EU_VAT_IN; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_USA_SSN; \Delight\PrivacyPolicy\Data\DataType::USER_IP_ADDRESS; \Delight\PrivacyPolicy\Data\DataType::USER_IP_ADDRESS_25_PERCENT; \Delight\PrivacyPolicy\Data\DataType::USER_IP_ADDRESS_50_PERCENT; \Delight\PrivacyPolicy\Data\DataType::USER_IP_ADDRESS_75_PERCENT; \Delight\PrivacyPolicy\Data\DataType::USER_LOGIN_DATETIME; \Delight\PrivacyPolicy\Data\DataType::USER_LOGIN_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::USER_LOGIN_DATETIME_TIME; \Delight\PrivacyPolicy\Data\DataType::USER_NAME; \Delight\PrivacyPolicy\Data\DataType::USER_NAME_ALIAS; \Delight\PrivacyPolicy\Data\DataType::USER_NAME_FAMILY; \Delight\PrivacyPolicy\Data\DataType::USER_NAME_GIVEN; \Delight\PrivacyPolicy\Data\DataType::USER_NOTES; \Delight\PrivacyPolicy\Data\DataType::USER_OCCUPATION; \Delight\PrivacyPolicy\Data\DataType::USER_OCCUPATION_CURRENT; \Delight\PrivacyPolicy\Data\DataType::USER_OCCUPATION_PREFERRED; \Delight\PrivacyPolicy\Data\DataType::USER_PASSWORD_CLEARTEXT; \Delight\PrivacyPolicy\Data\DataType::USER_PASSWORD_HASHED; \Delight\PrivacyPolicy\Data\DataType::USER_PASSWORD_HASHED_STRONG; \Delight\PrivacyPolicy\Data\DataType::USER_PASSWORD_RESETTABLE; \Delight\PrivacyPolicy\Data\DataType::USER_PHONE; \Delight\PrivacyPolicy\Data\DataType::USER_PHONE_HOME; \Delight\PrivacyPolicy\Data\DataType::USER_PHONE_MOBILE; \Delight\PrivacyPolicy\Data\DataType::USER_PICTURE; \Delight\PrivacyPolicy\Data\DataType::USER_REGISTRATION_DATETIME; \Delight\PrivacyPolicy\Data\DataType::USER_REGISTRATION_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::USER_REGISTRATION_DATETIME_TIME; \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE; \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE_DRAWN; \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE_HANDWRITTEN; \Delight\PrivacyPolicy\Data\DataType::USER_WEBSITE_URL; \Delight\PrivacyPolicy\Data\DataType::USER_WEIGHT; ``` ##### Secondary ```php \Delight\PrivacyPolicy\Data\DataType::ACCESS_APP_VERSION; \Delight\PrivacyPolicy\Data\DataType::ACCESS_FIRST_TIME; \Delight\PrivacyPolicy\Data\DataType::BILLING_ID_PAYMENT_SERVICE_PROVIDER; \Delight\PrivacyPolicy\Data\DataType::CALENDAR_EVENT_DATETIME; \Delight\PrivacyPolicy\Data\DataType::CALENDAR_EVENT_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::CALENDAR_EVENT_DATETIME_TIME; \Delight\PrivacyPolicy\Data\DataType::CALENDAR_EVENT_TITLE; \Delight\PrivacyPolicy\Data\DataType::CALENDAR_EVENT_TYPE; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS_ENERGY_USAGE; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS_ERRORS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS_ERRORS_LOGS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS_ERRORS_RATE; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS_FRAME_RATE; \Delight\PrivacyPolicy\Data\DataType::CLIENT_DIAGNOSTICS_TIMING; \Delight\PrivacyPolicy\Data\DataType::CLIENT_INTERACTIONS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_INTERACTIONS_CLICKS_OR_TAPS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_INTERACTIONS_SESSIONS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_INTERACTIONS_VIEWS; \Delight\PrivacyPolicy\Data\DataType::CLIENT_INTERACTIONS_VISITS; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ADDRESS; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ADDRESS_COUNTRY; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ADDRESS_LOCALITY; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ADDRESS_PLACE; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ADDRESS_POSTAL_CODE; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ADDRESS_REGION; \Delight\PrivacyPolicy\Data\DataType::CONTACT_BIRTH_DATE; \Delight\PrivacyPolicy\Data\DataType::CONTACT_BIRTH_DATE_MONTH_DAY; \Delight\PrivacyPolicy\Data\DataType::CONTACT_BIRTH_DATE_YEAR; \Delight\PrivacyPolicy\Data\DataType::CONTACT_BIRTH_DATE_YEAR_MONTH; \Delight\PrivacyPolicy\Data\DataType::CONTACT_COMPANY_DEPARTMENT; \Delight\PrivacyPolicy\Data\DataType::CONTACT_COMPANY_NAME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_CREATION_TIME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_EMAIL; \Delight\PrivacyPolicy\Data\DataType::CONTACT_FAX; \Delight\PrivacyPolicy\Data\DataType::CONTACT_FINANCIAL_BANK_ACCOUNT_ID; \Delight\PrivacyPolicy\Data\DataType::CONTACT_FINANCIAL_BANK_ID; \Delight\PrivacyPolicy\Data\DataType::CONTACT_FINANCIAL_BANK_NAME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_GENDER; \Delight\PrivacyPolicy\Data\DataType::CONTACT_IDENTIFIERS_EU_VAT_IN; \Delight\PrivacyPolicy\Data\DataType::CONTACT_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_NAME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_NAME_ALIAS; \Delight\PrivacyPolicy\Data\DataType::CONTACT_NAME_FAMILY; \Delight\PrivacyPolicy\Data\DataType::CONTACT_NAME_GIVEN; \Delight\PrivacyPolicy\Data\DataType::CONTACT_ORIGINAL_MESSAGE_TIME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_PHONE; \Delight\PrivacyPolicy\Data\DataType::CONTACT_PHONE_HOME; \Delight\PrivacyPolicy\Data\DataType::CONTACT_PHONE_MOBILE; \Delight\PrivacyPolicy\Data\DataType::CONTACT_REFERENCE; \Delight\PrivacyPolicy\Data\DataType::CONTACT_WEBSITE_URL; \Delight\PrivacyPolicy\Data\DataType::EMAIL_BCC; \Delight\PrivacyPolicy\Data\DataType::EMAIL_BODY; \Delight\PrivacyPolicy\Data\DataType::EMAIL_CC; \Delight\PrivacyPolicy\Data\DataType::EMAIL_DATETIME; \Delight\PrivacyPolicy\Data\DataType::EMAIL_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::EMAIL_DATETIME_TIME; \Delight\PrivacyPolicy\Data\DataType::EMAIL_FROM; \Delight\PrivacyPolicy\Data\DataType::EMAIL_REPLY_TO; \Delight\PrivacyPolicy\Data\DataType::EMAIL_RETURN_PATH; \Delight\PrivacyPolicy\Data\DataType::EMAIL_SUBJECT; \Delight\PrivacyPolicy\Data\DataType::EMAIL_TO; \Delight\PrivacyPolicy\Data\DataType::FILE_CONTENTS; \Delight\PrivacyPolicy\Data\DataType::FILE_NAME; \Delight\PrivacyPolicy\Data\DataType::FILE_SIZE; \Delight\PrivacyPolicy\Data\DataType::INVOICE_AMOUNT_GROSS; \Delight\PrivacyPolicy\Data\DataType::INVOICE_AMOUNT_NET; \Delight\PrivacyPolicy\Data\DataType::INVOICE_CUSTOMER_NUMBER; \Delight\PrivacyPolicy\Data\DataType::INVOICE_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::INVOICE_FULFILLMENT_PERIOD; \Delight\PrivacyPolicy\Data\DataType::INVOICE_NUMBER; \Delight\PrivacyPolicy\Data\DataType::INVOICE_RECIPIENT; \Delight\PrivacyPolicy\Data\DataType::INVOICE_TAXES; \Delight\PrivacyPolicy\Data\DataType::USER_REFERENCE; ``` ##### Tertiary ```php \Delight\PrivacyPolicy\Data\DataType::ACCESS_DEVICE_FEATURES_FILE_UPLOAD; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_BILLING_AMOUNT; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_BILLING_CYCLE; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_CANCELLATION_PERIOD; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_CANCELLATION_TIME; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_CREATION_TIME; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_NOTES; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_PARTNER; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_PERIOD_END; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_PERIOD_EXTENSION; \Delight\PrivacyPolicy\Data\DataType::CONTRACT_PERIOD_START; \Delight\PrivacyPolicy\Data\DataType::DEVICE_DIAGNOSTICS; \Delight\PrivacyPolicy\Data\DataType::DEVICE_DIAGNOSTICS_ENERGY; \Delight\PrivacyPolicy\Data\DataType::DEVICE_DIAGNOSTICS_ENERGY_LEVEL; \Delight\PrivacyPolicy\Data\DataType::DEVICE_DIAGNOSTICS_ENERGY_SOURCE; \Delight\PrivacyPolicy\Data\DataType::INVOICE_CREATION_TIME; \Delight\PrivacyPolicy\Data\DataType::INVOICE_DISCOUNT; \Delight\PrivacyPolicy\Data\DataType::INVOICE_ITEM_DESCRIPTION; \Delight\PrivacyPolicy\Data\DataType::INVOICE_ITEM_DISCOUNT; \Delight\PrivacyPolicy\Data\DataType::INVOICE_ITEM_PRICE_GROSS; \Delight\PrivacyPolicy\Data\DataType::INVOICE_ITEM_PRICE_NET; \Delight\PrivacyPolicy\Data\DataType::INVOICE_ITEM_QUANTITY; \Delight\PrivacyPolicy\Data\DataType::INVOICE_ITEM_TAXES; \Delight\PrivacyPolicy\Data\DataType::INVOICE_MESSAGE; \Delight\PrivacyPolicy\Data\DataType::INVOICE_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::INVOICE_NOTES; \Delight\PrivacyPolicy\Data\DataType::INVOICE_PAYMENT_DATETIME; \Delight\PrivacyPolicy\Data\DataType::INVOICE_PAYMENT_TERMS; \Delight\PrivacyPolicy\Data\DataType::INVOICE_REFUND_DATETIME; \Delight\PrivacyPolicy\Data\DataType::INVOICE_REMINDER_DATETIME; \Delight\PrivacyPolicy\Data\DataType::LETTER_BODY; \Delight\PrivacyPolicy\Data\DataType::LETTER_CC; \Delight\PrivacyPolicy\Data\DataType::LETTER_CREATION_TIME; \Delight\PrivacyPolicy\Data\DataType::LETTER_DATETIME; \Delight\PrivacyPolicy\Data\DataType::LETTER_DATETIME_DATE; \Delight\PrivacyPolicy\Data\DataType::LETTER_DATETIME_TIME; \Delight\PrivacyPolicy\Data\DataType::LETTER_ENCLOSURES; \Delight\PrivacyPolicy\Data\DataType::LETTER_HEADLINE; \Delight\PrivacyPolicy\Data\DataType::LETTER_IS_FIRST; \Delight\PrivacyPolicy\Data\DataType::LETTER_MATTER_PERSONAL_OR_BUSINESS; \Delight\PrivacyPolicy\Data\DataType::LETTER_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::LETTER_PS; \Delight\PrivacyPolicy\Data\DataType::LETTER_SALUTATION; \Delight\PrivacyPolicy\Data\DataType::LETTER_SUBJECT; \Delight\PrivacyPolicy\Data\DataType::LETTER_VALEDICTION; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_COMMERCIAL_REGISTER_ENTRY; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_EXECUTIVE_BOARD_MEMBERS; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_LOGO_CREATION_TIME; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_LOGO_LABEL; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_LOGO_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_MANAGEMENT_MEMBERS; \Delight\PrivacyPolicy\Data\DataType::USER_COMPANY_SUPERVISORY_BOARD_MEMBERS; \Delight\PrivacyPolicy\Data\DataType::USER_FINANCIAL_PURCHASE_HISTORY; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_ACCOUNT_ID; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_ACCOUNT_NAME; \Delight\PrivacyPolicy\Data\DataType::USER_IDENTIFIERS_ACCOUNT_TYPE; \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE_CREATION_TIME; \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE_LABEL; \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE_MODIFICATION_TIME; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_COLOR; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_CONSTRUCTION_PLACE; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_CONSTRUCTION_YEAR; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_MAKE; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_MODEL; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_NOTES; \Delight\PrivacyPolicy\Data\DataType::VEHICLE_REGISTRATION_PLATE_NUMBER; ``` ### Specifying the scope of your policy ```php $policy->addScope( new \Delight\PrivacyPolicy\Scope\WebsiteScope('https://www.example.com/', 'example.com') ); // and/or $policy->addScope( new \Delight\PrivacyPolicy\Scope\PlayStoreAndroidAppScope('com.example.app', 'Example for Android') ); // and/or $policy->addScope( new \Delight\PrivacyPolicy\Scope\AppStoreIosAppScope('54614917093', 'Example for iOS') ); ``` ### Configuring an instance #### Privacy policies for machines in formal language ```php $policy->setMinified(true); ``` ## Frequently asked questions ### How can I help translate the policy to my language? If there's a class for your language in the [`src/Language/`](src/Language/) directory already, you can just start working on that file. If there's no such class yet, simply create a new file for your language: 1. Copy the [template](../../blob/templates/src/Language/MyLanguagePrivacyPolicy.php) class that extends `HumanPrivacyPolicy` 1. Rename the class and file to specify the name of your language (following the common naming scheme) 1. Update the documentation comment for the class 1. Replace all occurrences of ```php throw new TranslationNotFoundError(); ``` (except for that in the `default` case of the `switch` block) with a `return` statement specifying the translated string ### Why is the HTML output not displayed correctly when using the “Bootstrap” framework? The “Bootstrap” front-end framework overwrites some of the default CSS properties for definition/description lists. In order to fix how the policy is displayed, you have to reset those properties to their default values or improve them further. Usually, you may want to restrict these resets to the container element of your policy. #### Bootstrap 3 ```css dl { margin-top: 0; margin-bottom: 16px; } dl dl { margin-left: 24px; } dd { margin-bottom: 8px; margin-left: 0; } dl dl dl dd { margin-bottom: 0; } ``` #### Bootstrap 4 ```css dl { margin-top: 0; margin-bottom: 1rem; } dl dl { margin-left: 1.5rem; } dd { margin-bottom: 0.5rem; margin-left: 0; } dl dl dl dd { margin-bottom: 0; } ``` ## Disclaimer This project does not constitute legal advice and is not to be relied upon or acted on as such. Any material presented here is for general information purposes only and may be out of date, incomplete or not suitable for your jurisdiction. You should seek independent legal advice from a qualified professional to guide your decisions around a valid and complete privacy policy. ## Contributing All contributions are welcome! If you wish to contribute, please create an issue first so that your feature, problem or question can be discussed. ## License This project is licensed under the terms of the [MIT License](https://opensource.org/licenses/MIT). ================================================ FILE: Sorting.md ================================================ # Sorting ## Blocks of text ### Single-line blocks ```bash $ cat unsorted.txt | sed 's/^[ \t]*//' | LC_ALL=C sort -f > sorted.txt ``` ## Constants ### Single-line constants with preceding single-line documentation comments ```bash $ cat unsorted.txt | sed 's/^[ \t]*//' | awk '{getline x;print x;}1' | paste -d "\t" - - | LC_ALL=C sort -f | tr '\t' '\n' | awk '{getline x;print x;}1' > sorted.txt ``` ## Cases in switch statements ### Keywords `case` and `return` on two separate single lines ```bash $ cat unsorted.txt | sed 's/^[ \t]*//' | paste -d "\t" - - | LC_ALL=C sort -f | tr '\t' '\n' > sorted.txt ``` ### Keywords `case` and `return` on same single line ```bash $ cat unsorted.txt | sed 's/^[ \t]*//' | LC_ALL=C sort -f > sorted.txt ``` ================================================ FILE: composer.json ================================================ { "name": "delight-im/privacy-policy", "description": "Programmatically composable privacy policies for humans and machines", "require": { "php": ">=5.6.0", "ext-intl": "*" }, "type": "library", "keywords": [ "privacy-policy", "privacy", "policy", "legal", "data-protection", "user-data" ], "homepage": "https://github.com/delight-im/PHP-PrivacyPolicy", "license": "MIT", "autoload": { "psr-4": { "Delight\\PrivacyPolicy\\": "src/" } } } ================================================ FILE: src/Data/DataBasis.php ================================================ type; } /** * Returns one of the constants from the {@see DataRequirement} class * * @return string */ public function getRequirement() { return $this->requirement; } /** * Returns whether a maximum retention time of the information has been defined * * @return bool */ public function hasMaxRetention() { return $this->maxRetention !== null; } /** * Returns the maximum retention time of the information in hours * * @return int|null */ public function getMaxRetention() { return $this->maxRetention; } /** * Creates a new individual data element * * @param string $type one of the constants from the {@see DataType} class * @param string|null $requirement (optional) one of the constants from the {@see DataRequirement} class * @param int|null $maxRetention (optional) the maximum retention time of the information in hours */ public function __construct($type, $requirement = null, $maxRetention = null) { $this->type = (string) $type; $this->requirement = $requirement !== null ? ((string) $requirement) : DataRequirement::ALWAYS; $this->maxRetention = $maxRetention !== null ? ((int) $maxRetention) : null; } } ================================================ FILE: src/Data/DataGroup.php ================================================ title; } /** * Returns whether a description of the group and of the circumstances of collection in natural language has been defined * * @return bool */ public function hasDescription() { return $this->description !== null; } /** * Returns the description of the group and of the circumstances of collection in natural language * * @return string|null */ public function getDescription() { return $this->description; } /** * Returns whether any constants from the {@see DataBasis} class have been added * * @return bool */ public function hasBases() { return !empty($this->bases); } /** * Returns any number of constants from the {@see DataBasis} class * * @return string[] */ public function getBases() { return $this->bases; } /** * Returns whether any constants from the {@see DataSpecialCondition} class have been added * * @return bool */ public function hasSpecialConditions() { return !empty($this->specialConditions); } /** * Returns any number of constants from the {@see DataSpecialCondition} class * * @return string[] */ public function getSpecialConditions() { return $this->specialConditions; } /** * Returns whether any constants from the {@see DataPurpose} class have been added * * @return bool */ public function hasPurposes() { return !empty($this->purposes); } /** * Returns any number of constants from the {@see DataPurpose} class * * @return string[] */ public function getPurposes() { return $this->purposes; } /** * Returns one of the constants from the {@see DataRequirement} class * * @return string */ public function getRequirement() { return $this->requirement; } /** * Returns whether any {@see DataElement} instances have been added * * @return bool */ public function hasElements() { return !empty($this->dataElements); } /** * Returns any number of {@see DataElement} instances * * @return DataElement[] */ public function getElements() { return $this->dataElements; } /** * Adds a new data element to the group * * @param string $type one of the constants from the {@see DataType} class * @param string|null $requirement (optional) one of the constants from the {@see DataRequirement} class * @param int|null $maxRetention (optional) the maximum retention time of the information in hours */ public function addElement($type, $requirement = null, $maxRetention = null) { $this->dataElements[] = new DataElement($type, $requirement, $maxRetention); } /** * Creates a new group of data elements * * @param string $title the title of the group in natural language, e.g. `Registration data` or `Access logs` * @param string|null $description (optional) the description of the group and of the circumstances of collection in natural language * @param string[]|null $bases (optional) any number of constants from the {@see DataBasis} class * @param string[]|null $specialConditions (optional) any number of constants from the {@see DataSpecialCondition} class * @param string[]|null $purposes (optional) any number of constants from the {@see DataPurpose} class * @param string|null $requirement (optional) one of the constants from the {@see DataRequirement} class * @param callable|null $init (optional) a callback that receives the new instance and may initialize it */ public function __construct($title, $description = null, array $bases = null, array $specialConditions = null, array $purposes = null, $requirement = null, callable $init = null) { $this->title = (string) $title; $this->description = ($description !== null) ? ((string) $description) : null; $this->bases = ($bases !== null) ? ((array) $bases) : []; $this->specialConditions = ($specialConditions !== null) ? ((array) $specialConditions) : []; $this->purposes = ($purposes !== null) ? ((array) $purposes) : []; $this->requirement = ($requirement !== null) ? ((string) $requirement) : DataRequirement::ALWAYS; $this->dataElements = []; if (\is_callable($init)) { $init($this); } } } ================================================ FILE: src/Data/DataPurpose.php ================================================ toMarkup()->toHtml(0); } /** * Returns the policy as plain text * * @return string */ public function toPlainText() { return $this->toMarkup()->toPlainText(0); } /** * Returns the policy as Markdown * * @return string */ public function toMarkdown() { return $this->toMarkup()->toMarkdown(0); } /** * Returns a translation of the supplied text * * This method should only ever be called by the {@see lang} method * * @param string $text the English text to translate * @return string the translated text * @throws TranslationNotFoundError */ abstract protected function translateUnformatted($text); /** * Formats the specified UNIX timestamp as a date * * @param int $unixTimestamp the UNIX timestamp in seconds to format * @return string the formatted date */ abstract protected function formatDate($unixTimestamp); /** * Formats the specified number of hours * * @param int $n * @return string the formatted number as a string */ abstract protected function formatHours($n); /** * Formats the specified number of days * * @param int $n * @return string the formatted number as a string */ abstract protected function formatDays($n); /** * Formats the specified number of weeks * * @param int $n * @return string the formatted number as a string */ abstract protected function formatWeeks($n); /** * Formats the specified number of months * * @param int $n * @return string the formatted number as a string */ abstract protected function formatMonths($n); /** * Formats the specified number of years * * @param int $n * @return string the formatted number as a string */ abstract protected function formatYears($n); /** * Returns the policy as generic markup * * @return Markup */ private function toMarkup() { return new DefinitionList(function (DefinitionList $list) { if ($this->publishedAt !== null) { $list->addDefinitionGroup($this->lang('Date of publication'), function (DefinitionGroup $group) { $group->addDefinition($this->formatDate($this->publishedAt)); }); } if ($this->takesEffectAt !== null) { $list->addDefinitionGroup($this->lang('Effective date'), function (DefinitionGroup $group) { $group->addDefinition($this->formatDate($this->takesEffectAt)); }); } if ($this->expiresAt !== null) { $list->addDefinitionGroup($this->lang('Date of expiration'), function (DefinitionGroup $group) { $group->addDefinition($this->formatDate($this->expiresAt)); }); } if ($this->hasVersionName()) { $list->addDefinitionGroup($this->lang('Version'), function (DefinitionGroup $group) { $group->addDefinition($this->versionName); }); } if ($this->hasCanonicalUrl()) { $list->addDefinitionGroup($this->lang('Latest version'), function (DefinitionGroup $group) { $group->addDefinition(new LinkMarkup($this->canonicalUrl)); }); } $list->addDefinitionGroup($this->lang('General'), function (DefinitionGroup $group) { $group->addDefinition( $this->lang('Protecting your privacy and keeping your personal information safe is our highest priority.') . Markup::SPACE . $this->lang('This privacy statement (“privacy policy” or “policy”) is designed to help you better understand how and to what extent we collect, use, disclose, transfer and store your information.') ); $group->addDefinitionInteractively(function () { $definition = $this->lang('The policy applies to our websites, mobile apps, software applications, products and services, collectively referred to as “services”.'); if ($this->hasScopes()) { $definition .= Markup::SPACE; $definition .= $this->lang('These services include:'); } return $definition; }); if ($this->hasScopes()) { $group->addDefinition(new DefinitionList(function (DefinitionList $list) { foreach ($this->scopes as $scope) { $list->addDefinitionGroup($scope, function (DefinitionGroup $group) use ($scope) { if ($scope instanceof WebsiteScope) { $group->addDefinition($this->lang('Website')); } elseif ($scope instanceof PlayStoreAndroidAppScope) { $group->addDefinition($this->lang('Android app (available from “Google Play”, a digital distribution platform operated by Google Inc.)')); } elseif ($scope instanceof AppStoreIosAppScope) { $group->addDefinition($this->lang('iOS app (available from the “App Store”, a digital distribution platform operated by Apple Inc.)')); } else { throw new UnexpectedScopeError(); } }); } })); } $group->addDefinition($this->lang('This privacy policy governs your use of our services regardless of the domain names, operating systems, platforms or devices that are used to access the services, and regardless of whether such access is in connection with an account or not.')); $group->addDefinition($this->lang('As a condition for your use of our services as a customer, user or visitor (collectively referred to as a “user” or as your “use”), you consent to the terms of this policy and you agree that your personal information will be handled as outlined below.')); }); $list->addDefinitionGroup($this->lang('Our principles'), function (DefinitionGroup $group) { if (!$this->isUserDataTraded()) { $group->addDefinition($this->lang('We never sell, rent out or trade any of our user’s personal information with third parties for commercial purposes.')); } if ($this->hasDataMinimizationGoal()) { $group->addDefinition($this->lang('We always collect only the minimum amount of personal information necessary to provide our services to you, unless you choose to provide more such information voluntarily.')); } $group->addDefinition( $this->lang('We encourage you to give us, and, more generally, any provider of digital services, only the amount of data you are comfortable sharing.') . Markup::SPACE . $this->lang('If in doubt, rather do not share sensitive information.') ); if ($this->rightOfAccess && $this->rightToRectification && $this->rightToErasure) { $group->addDefinition($this->lang('We offer you simple ways to view, update or delete the data we have collected about you.')); } }); if ($this->hasChildrenMinimumAge()) { $list->addDefinitionGroup($this->lang('Children’s Online Privacy Protection'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('None of our services are designed for, intended to attract, or directed towards children under the age of %d.', $this->childrenMinimumAge)); $group->addDefinition( $this->lang('We never knowingly collect any information from children under %d.', $this->childrenMinimumAge) . Markup::SPACE . $this->lang('If you are a child below that age, you may not use any of our services.', $this->childrenMinimumAge) ); $group->addDefinition($this->lang('If we have any plausible reason to believe that you are a user who is under the age of %d, we will have to prohibit you from continuing your use of our services.', $this->childrenMinimumAge)); $group->addDefinitionInteractively(function () { $definition = $this->lang('Should you believe that we might have any personal information of a child under the age of %d, in particular a child of your own, please contact us so that the data in question can be deleted, if appropriate.', $this->childrenMinimumAge); if ($this->hasContactInformation()) { $definition .= Markup::SPACE; $definition .= $this->lang('For our contact information, please see further below.'); } return $definition; }); }); } if ($this->hasPromotionalEmailOptOut()) { $list->addDefinitionGroup($this->lang('Email communication'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You may opt out of receiving any newsletters or promotional messages from us at any time.')); $group->addDefinition($this->lang('This is possible either by using the “Unsubscribe” feature at the bottom of such emails that we may send, or by adjusting the settings in your account within our services, where applicable.')); $group->addDefinition($this->lang('You will continue to receive essential, non-promotional messages regarding your account, such as technical notices, order confirmations, or other service-related messages, which are required for us to be able to provide our services to you.')); }); } if ($this->hasCookies()) { $list->addDefinitionGroup($this->lang('Cookies'), function (DefinitionGroup $group) { if ($this->hasFirstPartyCookies()) { $group->addDefinition( $this->lang('Cookies are minimal text files that contain small amounts of data.') . Markup::SPACE . $this->lang('They are transferred from our servers to your device through your web browser or app.') . Markup::SPACE . $this->lang('Your web browser or app then sends these small text files back to us whenever you access our services.') ); $group->addDefinition( $this->lang('These cookies do not necessarily contain any personal or identifying information.') . Markup::SPACE . $this->lang('They are, however, commonly used to store a unique identifier for every individual user, so that our servers do not lose information on who you are while you are moving through the individual parts of our services.') ); $group->addDefinition( $this->lang('We may use cookies and similar technologies, such as “Web Storage” (specifically “localStorage”) and “Internal Storage”, to make interactions with our services more convenient, efficient and secure.') . Markup::SPACE . $this->lang('For example, we may use these technologies to keep you signed in and to remember your preferences with regard to our services.') ); $group->addDefinition( $this->lang('As such, cookies and the related technologies are essential for the operation of our services.') . Markup::SPACE . $this->lang('You therefore consent to our use of cookies and related technologies when using our services.') . Markup::SPACE . $this->lang('Without these technologies, use of our services would not be reasonably possible.') ); $group->addDefinition( $this->lang('Your web browser or operating system usually provides means to delete such data currently stored on your device.') . Markup::SPACE . $this->lang('They may even provide methods to disable the use of such technologies completely.') . Markup::SPACE . $this->lang('You are welcome to make use of these settings and features of your web browser or operating system, but that may prevent our services from working correctly for you.') . Markup::SPACE . $this->lang('For more information on how to delete such data currently stored on your device, please refer to the manual or help section of your web browser or operating system.') ); } if ($this->hasThirdPartyCookies()) { $group->addDefinition(new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup($this->lang('Third-party cookies'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('Some contents of our services are provided by third parties that are not directly affiliated with us.')); $group->addDefinition($this->lang('These external contents, which are displayed, rendered, played back or otherwise conveyed directly within our services, may include advertising, analytics and components from social media.')); $group->addDefinition($this->lang('The third parties that provide these contents may store cookies on your device for their own purposes and interests, which we cannot control.')); $group->addDefinition($this->lang('The settings and features of your web browser or operating system may allow you to control how third parties can store cookies on your device.')); }); })); } }); } if ($this->hasDataGroups()) { $list->addDefinitionGroup($this->lang('Information we collect and why we collect it'), function (DefinitionGroup $group) { foreach ($this->dataGroups as $dataGroup) { $group->addDefinition(new DefinitionList(function (DefinitionList $list) use ($dataGroup) { $list->addDefinitionGroup($dataGroup->getTitle(), function (DefinitionGroup $group) use ($dataGroup) { $group->addDefinition($dataGroup->getDescription()); $group->addDefinition( new ConcatenationMarkup( new TextMarkup( $this->lang('Required:') ), new AbbreviationMarkup( DataRequirement::toBool($dataGroup->getRequirement()) ? $this->lang('yes') : $this->lang('no'), $this->lang( DataRequirement::toNaturalLanguage($dataGroup->getRequirement()) ) ) ) ); if ($dataGroup->hasBases()) { $basesMarkup = []; $basesMarkup[] = new TextMarkup( $this->lang('Lawful basis:') ); foreach ($dataGroup->getBases() as $base) { $basesMarkup[] = new AbbreviationMarkup( $this->lang( DataBasis::toNaturalLanguage($base) ), $this->lang( DataBasis::toLegalReference($base) ) ); $basesMarkup[] = new TextMarkup(Markup::MIDDLE_DOT); } \array_pop($basesMarkup); $group->addDefinition( new ConcatenationMarkup(...$basesMarkup) ); } if ($dataGroup->hasSpecialConditions()) { $specialConditionsMarkup = []; $specialConditionsMarkup[] = new TextMarkup( $this->lang('Condition for the processing of special categories of personal data:') ); foreach ($dataGroup->getSpecialConditions() as $specialCondition) { $specialConditionsMarkup[] = new AbbreviationMarkup( $this->lang( DataSpecialCondition::toNaturalLanguage($specialCondition) ), $this->lang( DataSpecialCondition::toLegalReference($specialCondition) ) ); $specialConditionsMarkup[] = new TextMarkup(Markup::MIDDLE_DOT); } \array_pop($specialConditionsMarkup); $group->addDefinition( new ConcatenationMarkup(...$specialConditionsMarkup) ); } if ($dataGroup->hasPurposes()) { foreach ($dataGroup->getPurposes() as $purpose) { $group->addDefinition($this->lang( DataPurpose::toNaturalLanguage($purpose) )); } } if ($dataGroup->hasElements()) { foreach ($dataGroup->getElements() as $dataElement) { $group->addDefinition(new DefinitionList(function (DefinitionList $list) use ($dataElement) { $dataTypeName = DataType::toNaturalLanguage($dataElement->getType()); $list->addDefinitionGroup($this->lang($dataTypeName), function (DefinitionGroup $group) use ($dataElement) { $group->addDefinition( new ConcatenationMarkup( new TextMarkup( $this->lang('Required:') ), new AbbreviationMarkup( DataRequirement::toBool($dataElement->getRequirement()) ? $this->lang('yes') : $this->lang('no'), $this->lang( DataRequirement::toNaturalLanguage($dataElement->getRequirement()) ) ) ) ); if ($dataElement->hasMaxRetention()) { if ($dataElement->getMaxRetention() <= 72) { // format number of 0 to 72 hours $group->addDefinition($this->lang( 'Maximum retention time: %s', $this->formatHours($dataElement->getMaxRetention()) )); } elseif ($dataElement->getMaxRetention() <= 504) { // format number of 4 to 21 days $group->addDefinition($this->lang( 'Maximum retention time: %s', $this->formatDays(\ceil($dataElement->getMaxRetention() / 24)) )); } elseif ($dataElement->getMaxRetention() <= 1008) { // format number of 4 to 6 weeks $group->addDefinition($this->lang( 'Maximum retention time: %s', $this->formatWeeks(\ceil($dataElement->getMaxRetention() / 24 / 7)) )); } elseif ($dataElement->getMaxRetention() <= 13148) { // format number of 2 to 18 months $group->addDefinition($this->lang( 'Maximum retention time: %s', $this->formatMonths(\ceil($dataElement->getMaxRetention() / 24 / 30.436875)) )); } else { // format number of 2 or more years $group->addDefinition($this->lang( 'Maximum retention time: %s', $this->formatYears(\ceil($dataElement->getMaxRetention() / 24 / 365.2425)) )); } } }); })); } } }); })); } }); } $list->addDefinitionGroup($this->lang('Mandatory disclosure'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('Laws in the jurisdictions that we operate in may obligate us to disclose certain personal information or other information that we collect about our users to local law enforcement authorities.')); $group->addDefinition($this->lang('We may be compelled to such disclosure in response to a court order, a warrant or a similar request by a judicial body or a government agency, or when we believe in good faith that the disclosure is reasonably necessary to protect our rights or property, that of any third party, or the safety of the general public.')); $group->addDefinition($this->lang('In any case, we will provide data only to the extent necessary to satisfy the request, and, whenever possible and legally permitted, we will make a reasonable effort to notify affected users of any such disclosure.')); }); $list->addDefinitionGroup($this->lang('Retention and deletion of data'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('We will retain certain pieces of personal information for as long as you use our services, as long as your account exists, or as long as needed for us to be able to provide our services to you.')); if ($this->isAccountDeletable()) { $group->addDefinitionInteractively(function () { $definition = $this->lang('If you would like to cancel your use of our services, delete your account, or delete your personal information, you may do so in the respective sections of our services.'); $definition .= Markup::SPACE; $definition .= $this->lang('If you need help, please contact us.'); if ($this->hasContactInformation()) { $definition .= Markup::SPACE; $definition .= $this->lang('See further below for our contact information.'); } return $definition; }); } if ($this->hasPreservationInBackups()) { $group->addDefinition( $this->lang('In order to prevent loss of data due to human errors or system failures, we keep additional backup copies of data, as most companies and service providers do, which may include some of your personal information.') . Markup::SPACE . $this->lang('This means that parts of your personal information may temporarily remain on our servers even after deletion or termination of your use of our services.') ); } $group->addDefinition($this->lang('We may retain and use your personal information and data as necessary to comply with our legal obligations, to resolve disputes, and to enforce our rights and agreements.')); }); if ($this->hasThirdPartyServiceProviders()) { $list->addDefinitionGroup($this->lang('Service providers, contractors and agents'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('From time to time, we may share some information we have collected from you, including personal information, with a limited number of third-party vendors, service providers, contractors, resellers, agents or business partners, solely for the purpose of performing certain functions on our behalf.')); $group->addDefinition( $this->lang('Such third parties help us provide and improve our services.') . Markup::SPACE . $this->lang('Functions performed by them on our behalf may include payment processing, network data transmission, fraud prevention, customer support management and similar services.') ); $group->addDefinition($this->lang('These third parties do not have any right to use the information that we share about you beyond what is necessary to assist us with the specific task at hand.')); }); } if ($this->hasInternationalTransfers()) { $list->addDefinitionGroup($this->lang('International data transfers'), function (DefinitionGroup $group) { $group->addDefinition( $this->lang('Some of our external service providers and recipients of personal data are based outside your country and outside our country.') . Markup::SPACE . $this->lang('This means that the processing of your personal data by those third parties involves an international transfer of your data to a third country.') . Markup::SPACE . $this->lang('Such third countries may have data protection rules that are different from those in your or our country.') ); $group->addDefinition($this->lang('Thus, for any international transfer of data, we ensure that appropriate technical, organizational and contractual measures are in place to guarantee that a similar degree of protection is afforded to your data internationally and that your personal data remains protected to the standards described in this policy.')); $group->addDefinition(new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup($this->lang('We verify that at least one of the following safeguards is implemented:'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('The country of the recipient has been deemed to provide an adequate level of protection for personal data by the European Commission.')); $group->addDefinition($this->lang('The recipient is certified as part of the EU-US and Swiss-US Privacy Shield Frameworks.')); $group->addDefinition($this->lang('Standard Contractual Clauses, as adopted by or approved by the European Commission, are in place.')); $group->addDefinition($this->lang('The recipient has Binding Corporate Rules that guarantee the protection of personal data.')); }); })); $group->addDefinition($this->lang('Please contact us if you want further information on the specific mechanisms used by us when transferring your personal data internationally.')); }); } if ($this->hasTransferUponMergerOrAcquisition()) { $list->addDefinitionGroup($this->lang('Mergers and acquisitions'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('If we are involved in a merger, an acquisition by another company, or a sale of all or a portion of our business or assets, your information will likely be among the assets transferred.')); $group->addDefinition($this->lang('If any such change of ownership happens, the organization receiving your personal information will have to respect the promises that we have made in any pre-existing privacy policy such as this one.')); $group->addDefinition( $this->lang('You will be notified via a prominent notice within our services or by email to the primary email address specified in your account at least %d days before any such transfer of your personal information.', $this->getNotificationPeriod()) . Markup::SPACE . $this->lang('This notification will include help on choices you may have regarding the transfer and treatment of your personal information.') ); }); } $list->addDefinitionGroup($this->lang('How we secure your information'), function (DefinitionGroup $group) { $group->addDefinition( $this->lang('We take the trust that you place in us very seriously.') . Markup::SPACE . $this->lang('We have therefore implemented all measures reasonably necessary to protect the personal information of our users from unauthorized access, modification, deletion, disclosure or other misuse.') ); $group->addDefinition($this->lang('We follow generally accepted industry standards to protect the data submitted to us, both during transmission and after we have received it, and continue to expand our protections as becomes necessary with changing technology.')); if ($this->hasTlsEverywhere()) { $group->addDefinition($this->lang('In particular, all connections to and from our services are encrypted using Secure Sockets Layer (SSL) and Transport Layer Security (TLS) technologies.')); } $group->addDefinition( $this->lang('However, please be aware that, despite our best efforts, no method of electronic transmission or storage is perfectly secure and no measures can guarantee absolute security.') . Markup::SPACE . $this->lang('Hardware or software failure as well as other factors may compromise the security of user information, as is the case with all other providers of digital services.') ); $group->addDefinition( $this->lang('Apart from that, please recognize that protecting your personal information is, in other parts, also your own responsibility.') . Markup::SPACE . $this->lang('Especially, you are responsible for safeguarding any passwords and other authentication information that you use to access our services, as well as limiting physical access to the devices used.') ); $group->addDefinition($this->lang('You should never disclose your authentication information to any third party and you should notify us immediately of any unauthorized use of your account.')); }); $list->addDefinitionGroup($this->lang('Links to external websites, applications and products'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('Some parts and sections of our services may contain external links to websites, applications or products owned by and operated by third parties.')); $group->addDefinition( $this->lang('We advise you to verify the privacy practices of those third parties individually.') . Markup::SPACE . $this->lang('We have no knowledge about and are not responsible for the way that those third parties handle any personal information which you provide to them yourself.') ); $group->addDefinition($this->lang('We encourage you not to provide any personal information to those third parties before assuring yourself of proper privacy practices on their part.')); }); if ($this->rightOfAccess || $this->rightToRectification || $this->rightToErasure || $this->rightToRestrictProcessing || $this->rightToDataPortability || $this->rightToObject || $this->rightsRelatedToAutomatedDecisions) { $list->addDefinitionGroup($this->lang('Your rights'), function (DefinitionGroup $group) { $group->addDefinition($this->lang('Except as limited under applicable law, you have the following rights with regard to your personal data:')); if ($this->rightOfAccess) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Access your personal data'), $this->lang('Right of access (EU, General Data Protection Regulation (GDPR), Article 15)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to be informed of and request access to the personal data we process about you.')); } ); }) ); } if ($this->rightToRectification) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Update your personal data'), $this->lang('Right to rectification (EU, General Data Protection Regulation (GDPR), Article 16)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to request that we amend or update your personal data where it is inaccurate or incomplete.')); } ); }) ); } if ($this->rightToErasure) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Erase your personal data'), $this->lang('Right to erasure (EU, General Data Protection Regulation (GDPR), Article 17)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to request that we delete your personal data.')); } ); }) ); } if ($this->rightToRestrictProcessing) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Restrict the use of your personal data'), $this->lang('Right to restriction of processing (EU, General Data Protection Regulation (GDPR), Article 18)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to request that we temporarily or permanently stop processing your personal data.')); } ); }) ); } if ($this->rightToDataPortability) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Export your personal data'), $this->lang('Right to data portability (EU, General Data Protection Regulation (GDPR), Article 20)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to request a copy of your personal data in electronic and machine-readable form, and the right to transmit that personal data to another service provider.')); } ); }) ); } if ($this->rightToObject) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Object to direct marketing'), $this->lang('Right to object (EU, General Data Protection Regulation (GDPR), Article 21)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to object to your personal data being processed for direct marketing purposes.')); } ); }) ); $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Object to the use of your personal data'), $this->lang('Right to object (EU, General Data Protection Regulation (GDPR), Article 21)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right to object to us processing your personal data on grounds relating to your particular situation where we no longer have a legitimate reason or legal need to process it.')); } ); }) ); } if ($this->rightsRelatedToAutomatedDecisions) { $group->addDefinition( new DefinitionList(function (DefinitionList $list) { $list->addDefinitionGroup( new AbbreviationMarkup( $this->lang('Object to automated decision-making'), $this->lang('Rights related to automated individual decision-making, including profiling (EU, General Data Protection Regulation (GDPR), Article 22)') ), function (DefinitionGroup $group) { $group->addDefinition($this->lang('You have the right not to be subject to a decision based solely on automated decision-making, including profiling, where the decision would have a legal effect on you or produce a similarly significant effect.')); } ); }) ); } $group->addDefinitionInteractively(function () { $definition = $this->lang('If you have any questions regarding the protection of your data, your rights, or how to exercise them, please contact us.'); if ($this->hasContactInformation()) { $definition .= Markup::SPACE; $definition .= $this->lang('For our contact information, please see further below.'); } $definition .= Markup::SPACE; $definition .= $this->lang('Upon verification of your identity, we will respond to your request within a reasonable period of time.'); return $definition; }); $group->addDefinitionInteractively(function () { $definition = $this->lang('If you are unsatisfied with our response or with the way we are processing your personal data, you may contact your local data protection authority.'); if ($this->competentSupervisoryAuthorityName !== null || $this->competentSupervisoryAuthorityUrl !== null) { $definition .= Markup::SPACE; $definition .= $this->lang('You may also contact the data protection authority that is responsible for us:'); } return $definition; }); if ($this->competentSupervisoryAuthorityName !== null || $this->competentSupervisoryAuthorityUrl !== null) { if ($this->competentSupervisoryAuthorityUrl !== null) { $group->addDefinition( new LinkMarkup($this->competentSupervisoryAuthorityUrl, $this->competentSupervisoryAuthorityName) ); } else { $group->addDefinition($this->competentSupervisoryAuthorityName); } } $group->addDefinition($this->lang('You are free to file a complaint with the data protection authority.')); }); } $list->addDefinitionGroup($this->lang('Changes to this privacy policy'), function (DefinitionGroup $group) { $group->addDefinition( $this->lang('We may change this privacy policy from time to time.') . Markup::SPACE . $this->lang('Most changes will presumably be minor only and will therefore not affect your rights.') ); $group->addDefinition( $this->lang('Should there be any substantial changes to this policy with material effects on any of your rights or choices, you will be notified via a prominent notice within our services or by email to the primary email address specified in your account at least %d days prior to such changes taking effect.', $this->getNotificationPeriod()) . Markup::SPACE . $this->lang('This notification will include help on choices you may have regarding the treatment of your personal information.') ); $group->addDefinition($this->lang('For any less significant changes to this privacy policy that do not affect your rights or choices in a material way, we encourage all users to check this policy for updated versions periodically.')); $group->addDefinition($this->lang('In general, the applicable version of this policy is the one that is current at the time of your access of our services.')); }); if ($this->hasContactInformation()) { $list->addDefinitionGroup($this->lang('Contact information of the controller'), function (DefinitionGroup $group) { $group->addDefinition( $this->lang('If you have any questions or concerns regarding this policy, our privacy practices or certain aspects of our services, please contact us at any time.') . Markup::SPACE . $this->lang('We want to help and will be happy to address your concerns.') . Markup::SPACE . $this->lang('We are responsible for the processing of personal data under this policy.') ); if ($this->hasContactEmail()) { $group->addDefinition($this->lang('You can reach us via email at:')); $group->addDefinition( new LinkMarkup('mailto:' . $this->contactEmail, $this->contactEmail) ); } if ($this->hasContactUrl() && !$this->hasContactImage()) { $group->addDefinition($this->lang('Our full contact information can be found at:')); $group->addDefinition( new LinkMarkup($this->contactUrl) ); } if ($this->hasContactImage()) { $imageMarkup = new ImageMarkup( $this->contactImage[0], $this->contactImage[1], $this->contactImage[2], $this->contactImage[3] ); if ($this->hasContactUrl()) { $group->addDefinition( new LinkMarkup($this->contactUrl, $imageMarkup, $this->contactImage[1]) ); } else { $group->addDefinition($imageMarkup); } } }); } }); } /** * Returns a translation of the supplied text, optionally inserting any specified format arguments * * This uses the {@see translateUnformatted} method implemented by subclasses * * @param string $text the English text to translate * @param array ...$arguments (optional) the format arguments to insert * @return string the translated text with format arguments inserted as needed * @throws InvalidFormatArgumentsError * @throws TranslationNotFoundError */ private function lang($text, ...$arguments) { try { $translated = $this->translateUnformatted($text); } catch (TranslationNotFoundError $e) { if (self::FAIL_ON_MISSING_TRANSLATIONS) { throw new TranslationNotFoundError($text); } else { $translated = $text; } } $formatted = @\sprintf($translated, ...$arguments); if ($formatted === false) { throw new InvalidFormatArgumentsError(); } return $formatted; } } ================================================ FILE: src/Language/EnglishPrivacyPolicy.php ================================================ format($unixTimestamp); } protected function formatHours($n) { return \sprintf(($n === 1 ? '%d hour' : '%d hours'), $n); } protected function formatDays($n) { return \sprintf(($n === 1 ? '%d day' : '%d days'), $n); } protected function formatWeeks($n) { return \sprintf(($n === 1 ? '%d week' : '%d weeks'), $n); } protected function formatMonths($n) { return \sprintf(($n === 1 ? '%d month' : '%d months'), $n); } protected function formatYears($n) { return \sprintf(($n === 1 ? '%d year' : '%d years'), $n); } } ================================================ FILE: src/Language/GermanFormalPrivacyPolicy.php ================================================ format($unixTimestamp); } protected function formatHours($n) { return \sprintf(($n === 1 ? '%d Stunde' : '%d Stunden'), $n); } protected function formatDays($n) { return \sprintf(($n === 1 ? '%d Tag' : '%d Tage'), $n); } protected function formatWeeks($n) { return \sprintf(($n === 1 ? '%d Woche' : '%d Wochen'), $n); } protected function formatMonths($n) { return \sprintf(($n === 1 ? '%d Monat' : '%d Monate'), $n); } protected function formatYears($n) { return \sprintf(($n === 1 ? '%d Jahr' : '%d Jahre'), $n); } } ================================================ FILE: src/Language/JsonPrivacyPolicy.php ================================================ hasVersionName()) { $out['meta']['version'] = (string) $this->versionName; } if ($this->publishedAt !== null) { $out['meta']['published'] = (int) $this->publishedAt; } if ($this->takesEffectAt !== null) { $out['meta']['effective'] = (int) $this->takesEffectAt; } if ($this->expiresAt !== null) { $out['meta']['expires'] = (int) $this->expiresAt; } if ($this->hasCanonicalUrl()) { $out['meta']['canonical'] = (string) $this->canonicalUrl; } if ($this->hasScopes()) { $out['meta']['scopes'] = []; foreach ($this->scopes as $scope) { if ($scope instanceof WebsiteScope) { $out['meta']['scopes'][] = [ 'type' => 'web', 'url' => (string) $scope->getUrl() ]; } elseif ($scope instanceof PlayStoreAndroidAppScope) { $out['meta']['scopes'][] = [ 'type' => 'android', 'packageName' => (string) $scope->getPackageName() ]; } elseif ($scope instanceof AppStoreIosAppScope) { $out['meta']['scopes'][] = [ 'type' => 'ios', 'trackId' => (string) $scope->getTrackId() ]; } else { throw new UnexpectedScopeError(); } } } if ($this->hasDataGroups()) { $out['data'] = []; foreach ($this->dataGroups as $dataGroup) { $groupRecord = []; if ($dataGroup->hasBases()) { $groupRecord['bases'] = []; foreach ($dataGroup->getBases() as $base) { $groupRecord['bases'][] = (string) $base; } } if ($dataGroup->hasSpecialConditions()) { $groupRecord['specialConditions'] = []; foreach ($dataGroup->getSpecialConditions() as $specialCondition) { $groupRecord['specialConditions'][] = (string) $specialCondition; } } if ($dataGroup->hasPurposes()) { $groupRecord['purposes'] = []; foreach ($dataGroup->getPurposes() as $purpose) { $groupRecord['purposes'][] = (string) $purpose; } } $groupRecord['requirement'] = (string) $dataGroup->getRequirement(); if ($dataGroup->hasElements()) { $groupRecord['elements'] = []; foreach ($dataGroup->getElements() as $element) { $record = [ 'type' => (string) $element->getType(), 'requirement' => (string) $element->getRequirement() ]; if ($element->hasMaxRetention()) { $record['retention'] = []; $record['retention']['max'] = (int) $element->getMaxRetention(); } $groupRecord['elements'][] = $record; } } $out['data'][] = $groupRecord; } } $out['principles'] = []; $out['principles']['data'] = []; $out['principles']['data']['trade'] = (bool) $this->isUserDataTraded(); $out['principles']['data']['avoidance'] = (bool) $this->hasDataMinimizationGoal(); if ($this->hasChildrenMinimumAge()) { $out['children'] = []; $out['children']['age'] = []; $out['children']['age']['min'] = (int) $this->childrenMinimumAge; } $out['email'] = []; $out['email']['marketing'] = []; $out['email']['marketing']['optOut'] = (bool) $this->hasPromotionalEmailOptOut(); $out['cookies'] = []; $out['cookies']['firstParty'] = (bool) $this->hasFirstPartyCookies(); $out['cookies']['thirdParty'] = (bool) $this->hasThirdPartyCookies(); $out['choices'] = []; if ($this->rightOfAccess || $this->rightToRectification || $this->rightToErasure || $this->rightToRestrictProcessing || $this->rightToDataPortability || $this->rightToObject || $this->rightsRelatedToAutomatedDecisions) { $out['choices']['data'] = []; $out['choices']['data']['access'] = (bool) $this->rightOfAccess; $out['choices']['data']['rectification'] = (bool) $this->rightToRectification; $out['choices']['data']['erasure'] = (bool) $this->rightToErasure; $out['choices']['data']['restrictProcessing'] = (bool) $this->rightToRestrictProcessing; $out['choices']['data']['portability'] = (bool) $this->rightToDataPortability; $out['choices']['data']['object'] = (bool) $this->rightToObject; $out['choices']['data']['automatedDecisions'] = (bool) $this->rightsRelatedToAutomatedDecisions; } $out['choices']['account'] = []; $out['choices']['account']['deletion'] = (bool) $this->isAccountDeletable(); $out['backups'] = (bool) $this->hasPreservationInBackups(); $out['serviceProviders'] = []; $out['serviceProviders']['thirdParties'] = (bool) $this->hasThirdPartyServiceProviders(); $out['transfers'] = []; $out['transfers']['international'] = (bool) $this->hasInternationalTransfers(); $out['mergersAndAcquisitions'] = []; $out['mergersAndAcquisitions']['transfer'] = (bool) $this->hasTransferUponMergerOrAcquisition(); $out['security'] = []; $out['security']['tls'] = (bool) $this->hasTlsEverywhere(); if ($this->competentSupervisoryAuthorityName !== null || $this->competentSupervisoryAuthorityUrl !== null) { $out['supervisoryAuthority'] = []; if ($this->competentSupervisoryAuthorityName !== null) { $out['supervisoryAuthority']['name'] = $this->competentSupervisoryAuthorityName; } if ($this->competentSupervisoryAuthorityUrl !== null) { $out['supervisoryAuthority']['url'] = $this->competentSupervisoryAuthorityUrl; } } $out['changes'] = []; $out['changes']['notificationPeriod'] = (int) $this->getNotificationPeriod(); if ($this->hasContactInformation()) { $out['contact'] = []; if ($this->hasContactEmail()) { $out['contact']['email'] = (string) $this->contactEmail; } if ($this->hasContactUrl()) { $out['contact']['url'] = (string) $this->contactUrl; } if ($this->hasContactImage()) { $out['contact']['image'] = []; if (!empty($this->contactImage[0])) { $out['contact']['image']['source'] = (string) $this->contactImage[0]; } if (!empty($this->contactImage[1])) { $out['contact']['image']['alternativeText'] = (string) $this->contactImage[1]; } if (!empty($this->contactImage[2])) { $out['contact']['image']['width'] = (int) $this->contactImage[2]; } if (!empty($this->contactImage[3])) { $out['contact']['image']['height'] = (int) $this->contactImage[3]; } } } return $this->encodeAsJson($out); } /** * Encodes the supplied data as JSON * * @param array $data * @return string */ private function encodeAsJson(array $data) { $flags = self::FLAGS_ENCODE; if (!$this->isMinified()) { $flags |= \JSON_PRETTY_PRINT; } return \json_encode($data, $flags); } } ================================================ FILE: src/MachinePrivacyPolicy.php ================================================ minified; } /** * Sets whether any output should be minified * * @param bool $minified */ public function setMinified($minified) { $this->minified = (bool) $minified; } public function __construct() { parent::__construct(); $this->minified = false; } } ================================================ FILE: src/Markup/AbbreviationMarkup.php ================================================ abridgement = (string) $abridgement; $this->expansion = ($expansion !== null) ? ((string) $expansion) : null; } public function toHtmlWithIndentation($indentation) { $out = self::createHtmlIndentation($indentation); $out .= 'expansion !== null) { $out .= ' title="'; $out .= self::escapeForHtml($this->expansion); $out .= '"'; } $out .= '>'; $out .= "\n"; $out .= self::createHtmlIndentation($indentation + 1); $out .= self::escapeForHtml($this->abridgement); $out .= "\n"; $out .= self::createHtmlIndentation($indentation); $out .= ''; return $out; } public function toPlainTextWithIndentation($indentation) { $out = self::createPlainTextIndentation($indentation); $out .= $this->abridgement; if ($this->expansion !== null) { $out .= ' ('; $out .= $this->expansion; $out .= ')'; } return $out; } public function toMarkdownWithIndentation($indentation) { $out = self::createMarkdownIndentation($indentation); $out .= $this->abridgement; if ($this->expansion !== null) { $out .= ' ('; $out .= $this->expansion; $out .= ')'; } return $out; } } ================================================ FILE: src/Markup/ConcatenationMarkup.php ================================================ elements = $elements; } public function toHtmlWithIndentation($indentation) { $out = []; foreach ($this->elements as $element) { $out[] = $element->toHtmlWithIndentation($indentation); } return \implode("\n", $out); } public function toPlainTextWithIndentation($indentation) { $out = []; foreach ($this->elements as $element) { $out[] = $element->toPlainTextWithIndentation(0); } return self::createPlainTextIndentation($indentation) . \implode(Markup::SPACE, $out); } public function toMarkdownWithIndentation($indentation) { $out = []; foreach ($this->elements as $element) { $out[] = $element->toMarkdownWithIndentation(0); } return self::createMarkdownIndentation($indentation) . \implode(Markup::SPACE, $out); } } ================================================ FILE: src/Markup/DefinitionList/DefinitionGroup.php ================================================ definitions[] = $definition; } /** * Adds a new definition for the term of the group by executing the specified callback * * The callback must return a definition * * @param callable $build */ public function addDefinitionInteractively(callable $build) { $this->addDefinition($build()); } public function toHtmlWithIndentation($indentation) { $out = self::createHtmlIndentation($indentation); $out .= '
'; echo 'English'; echo ' · '; echo 'German (formal)'; echo ' · '; echo 'German (informal)'; echo ' · '; echo 'JSON'; echo '
'; echo ''; echo 'HTML'; echo ' · '; echo 'Plain text'; echo ' · '; echo 'Markdown'; echo '
'; echo '' . $policy->toJson() . ''; } if ($policy instanceof \Delight\PrivacyPolicy\HumanPrivacyPolicy) { echo '
' . $policy->toPlainText() . ''; echo '
' . $policy->toMarkdown() . ''; } echo ''; echo '';