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 .= '
'; $out .= "\n"; $out .= self::createHtmlIndentation($indentation + 1); $out .= ''; $out .= "\n"; $out .= $this->term->toHtmlWithIndentation($indentation + 2); $out .= "\n"; $out .= self::createHtmlIndentation($indentation + 1); $out .= ''; $out .= "\n"; $out .= self::createHtmlIndentation($indentation); $out .= '
'; if (!empty($this->definitions)) { foreach ($this->definitions as $definition) { $out .= "\n"; $out .= self::createHtmlIndentation($indentation); $out .= '
'; $out .= "\n"; $out .= $definition->toHtmlWithIndentation($indentation + 1); $out .= "\n"; $out .= self::createHtmlIndentation($indentation); $out .= '
'; } } else { $out .= "\n"; $out .= self::createHtmlIndentation($indentation); $out .= '
'; } return $out; } public function toPlainTextWithIndentation($indentation) { $out = $this->term->toPlainTextWithIndentation($indentation); foreach ($this->definitions as $definition) { $out .= "\n"; $out .= $definition->toPlainTextWithIndentation($indentation + 1); } return $out; } public function toMarkdownWithIndentation($indentation) { $out = self::createMarkdownIndentation($indentation); $out .= ' * **'; $out .= $this->term->toMarkdownWithIndentation(0); $out .= '**'; foreach ($this->definitions as $definition) { $out .= "\n"; if ($definition instanceof DefinitionList) { $out .= $definition->toMarkdownWithIndentation($indentation + 1); } else { $out .= self::createMarkdownIndentation($indentation + 1); $out .= ' * '; $out .= $definition->toMarkdownWithIndentation(0); } } return $out; } /** * Creates a new group within a definition list * * The group has one term to be defined and any number of definitions for the term * * @param string|Markup $term the term to be defined * @param callable|null $init (optional) a callback that receives the new instance and may initialize it */ public function __construct($term, callable $init = null) { if (!($term instanceof Markup)) { $term = new TextMarkup((string) $term); } $this->term = $term; $this->definitions = []; if (\is_callable($init)) { $init($this); } } } ================================================ FILE: src/Markup/DefinitionList/DefinitionList.php ================================================ definitionGroups[] = new DefinitionGroup($term, $init); } public function toHtmlWithIndentation($indentation) { $out = self::createHtmlIndentation($indentation); $out .= '
'; $out .= "\n"; foreach ($this->definitionGroups as $definitionGroup) { $out .= $definitionGroup->toHtmlWithIndentation($indentation + 1); $out .= "\n"; } $out .= self::createHtmlIndentation($indentation); $out .= '
'; return $out; } public function toPlainTextWithIndentation($indentation) { $out = ''; foreach ($this->definitionGroups as $definitionGroup) { if ($out !== '') { $out .= "\n"; } $out .= $definitionGroup->toPlainTextWithIndentation($indentation); } return $out; } public function toMarkdownWithIndentation($indentation) { $out = ''; foreach ($this->definitionGroups as $definitionGroup) { if ($out !== '') { $out .= "\n"; } $out .= $definitionGroup->toMarkdownWithIndentation($indentation); } return $out; } /** * Creates a new definition list * * The list can have any number of definition groups * * @param callable|null $init (optional) a callback that receives the new instance and may initialize it */ public function __construct(callable $init = null) { $this->definitionGroups = []; if (\is_callable($init)) { $init($this); } } } ================================================ FILE: src/Markup/ImageMarkup.php ================================================ source = (string) $source; $this->alternativeText = ($alternativeText !== null) ? ((string) $alternativeText) : null; $this->width = ($width !== null) ? ((int) $width) : null; $this->height = ($height !== null) ? ((int) $height) : null; } public function toHtmlWithIndentation($indentation) { $out = self::createHtmlIndentation($indentation); $out .= 'source); $out .= '"'; if ($this->alternativeText !== null) { $out .= ' alt="'; $out .= self::escapeForHtml($this->alternativeText); $out .= '"'; } if ($this->width !== null) { $out .= ' width="'; $out .= self::escapeForHtml($this->width); $out .= '"'; } if ($this->height !== null) { $out .= ' height="'; $out .= self::escapeForHtml($this->height); $out .= '"'; } $out .= '>'; return $out; } public function toPlainTextWithIndentation($indentation) { $out = self::createPlainTextIndentation($indentation); $out .= $this->source; if ($this->alternativeText !== null) { $out .= ' ('; $out .= $this->alternativeText; $out .= ')'; } return $out; } public function toMarkdownWithIndentation($indentation) { $out = self::createMarkdownIndentation($indentation); $out .= '!['; if ($this->alternativeText !== null) { $out .= $this->alternativeText; } $out .= ']('; $out .= $this->source; $out .= ')'; return $out; } } ================================================ FILE: src/Markup/LinkMarkup.php ================================================ target = (string) $target; if ($label === null) { $label = $target; } if (!($label instanceof Markup)) { $label = new TextMarkup((string) $label); } $this->label = $label; $this->description = ($description !== null) ? ((string) $description) : null; } public function toHtmlWithIndentation($indentation) { $out = self::createHtmlIndentation($indentation); $out .= 'target); $out .= '"'; if ($this->description !== null) { $out .= ' title="'; $out .= self::escapeForHtml($this->description); $out .= '"'; } $out .= '>'; $out .= "\n"; if ($this->label !== null) { $out .= $this->label->toHtmlWithIndentation($indentation + 1); } $out .= "\n"; $out .= self::createHtmlIndentation($indentation); $out .= ''; return $out; } public function toPlainTextWithIndentation($indentation) { $out = self::createPlainTextIndentation($indentation); if ($this->label !== null) { $out .= $this->label->toPlainTextWithIndentation(0); $out .= Markup::SPACE; } if ($this->description !== null) { $out .= Markup::EN_DASH; $out .= Markup::SPACE; $out .= $this->description; $out .= Markup::SPACE; } $out .= '('; $out .= $this->target; $out .= ')'; return $out; } public function toMarkdownWithIndentation($indentation) { $out = self::createMarkdownIndentation($indentation); $out .= '['; if ($this->label !== null) { $out .= $this->label->toMarkdownWithIndentation(0); if ($this->description !== null) { $out .= Markup::SPACE; $out .= Markup::EN_DASH; $out .= Markup::SPACE; $out .= $this->description; } } $out .= ']('; $out .= $this->target; $out .= ')'; return $out; } } ================================================ FILE: src/Markup/Markup.php ================================================ toHtmlWithIndentation($indentation !== null ? $indentation : 0); } /** * Converts the markup to HTML * * @internal * * @param int $indentation the level of indentation * @return string */ abstract public function toHtmlWithIndentation($indentation); /** * Converts the markup to plain text * * @param int|null $indentation (optional) the level of indentation * @return string */ public function toPlainText($indentation = null) { return $this->toPlainTextWithIndentation($indentation !== null ? $indentation : 0); } /** * Converts the markup to plain text * * @internal * * @param int $indentation the level of indentation * @return string */ abstract public function toPlainTextWithIndentation($indentation); /** * Converts the markup to Markdown * * @param int|null $indentation (optional) the level of indentation * @return string */ public function toMarkdown($indentation = null) { return $this->toMarkdownWithIndentation($indentation !== null ? $indentation : 0); } /** * Converts the markup to Markdown * * @internal * * @param int $indentation the level of indentation * @return string */ abstract public function toMarkdownWithIndentation($indentation); /** * Creates the HTML sequence used for indentation of the specified level * * @param int $indentation the level of indentation * @return string */ protected static function createHtmlIndentation($indentation) { return \str_repeat(self::INDENTATION_UNIT_HTML, $indentation); } /** * Creates the plain text sequence used for indentation of the specified level * * @param int $indentation the level of indentation * @return string */ protected static function createPlainTextIndentation($indentation) { return \str_repeat(self::INDENTATION_UNIT_PLAIN_TEXT, $indentation); } /** * Creates the Markdown sequence used for indentation of the specified level * * @param int $indentation the level of indentation * @return string */ protected static function createMarkdownIndentation($indentation) { return \str_repeat(self::INDENTATION_UNIT_MARKDOWN, $indentation); } /** * Escapes the supplied text for use in HTML * * @param string $text * @return string */ protected static function escapeForHtml($text) { return \htmlspecialchars($text, \ENT_QUOTES, self::CHARSET_DEFAULT); } } ================================================ FILE: src/Markup/SimpleMarkup.php ================================================ toMarkup()->toHtmlWithIndentation($indentation); } public function toPlainTextWithIndentation($indentation) { return $this->toMarkup()->toPlainTextWithIndentation($indentation); } public function toMarkdownWithIndentation($indentation) { return $this->toMarkup()->toMarkdownWithIndentation($indentation); } /** * Returns the generic markup of the instance * * @internal * * @return Markup */ abstract protected function toMarkup(); } ================================================ FILE: src/Markup/TextMarkup.php ================================================ text = (string) $text; } public function toHtmlWithIndentation($indentation) { $out = self::createHtmlIndentation($indentation); $out .= self::escapeForHtml($this->text); return $out; } public function toPlainTextWithIndentation($indentation) { $out = self::createPlainTextIndentation($indentation); $out .= $this->text; return $out; } public function toMarkdownWithIndentation($indentation) { $out = self::createMarkdownIndentation($indentation); $out .= $this->text; return $out; } public function __toString() { return $this->text; } } ================================================ FILE: src/PrivacyPolicy.php ================================================ publishedAt = $publishedAt !== null ? ((int) $publishedAt) : null; } /** * Sets the time when the policy takes effect * * @param int|null $takesEffectAt the time as a UNIX timestamp in seconds, or `null` to unset */ public function setTakesEffectAt($takesEffectAt) { $this->takesEffectAt = $takesEffectAt !== null ? ((int) $takesEffectAt) : null; } /** * Sets the time when the policy will expire * * @param int|null $expiresAt the time as a UNIX timestamp in seconds, or `null` to unset */ public function setExpiresAt($expiresAt) { $this->expiresAt = $expiresAt !== null ? ((int) $expiresAt) : null; } /** * Returns whether the name of the current version has been defined * * @return bool */ public function hasVersionName() { return $this->versionName !== null; } /** * Sets the name of the current version * * @param string|null $versionName the name in arbitrary format, or `null` to unset */ public function setVersionName($versionName) { $this->versionName = $versionName !== null ? ((string) $versionName) : null; } /** * Returns whether the URL of the official policy in its latest version has been defined * * @return bool */ public function hasCanonicalUrl() { return $this->canonicalUrl !== null; } /** * Sets the URL of the official policy in its latest version * * @param string|null $canonicalUrl the URL, or `null` to unset */ public function setCanonicalUrl($canonicalUrl) { $this->canonicalUrl = $canonicalUrl !== null ? ((string) $canonicalUrl) : null; } /** * Returns whether scopes have been defined that describe which services the policy applies to * * @return bool */ public function hasScopes() { return !empty($this->scopes); } /** * Adds a scope that describes which services the policy applies to * * @param Scope $scope */ public function addScope(Scope $scope) { $this->scopes[] = $scope; } /** * Returns whether data groups have been defined that describe which data is collected and why * * @return bool */ public function hasDataGroups() { return !empty($this->dataGroups); } /** * Adds a data group that describes which data is collected and why * * @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 addDataGroup($title, $description = null, array $bases = null, array $specialConditions = null, array $purposes = null, $requirement = null, callable $init = null) { $this->dataGroups[] = new DataGroup($title, $description, $bases, $specialConditions, $purposes, $requirement, $init); } /** * Returns whether user data is sold, rented or traded with third parties * * @return bool */ public function isUserDataTraded() { return $this->userDataTraded; } /** * Sets whether user data is sold, rented or traded with third parties * * @param bool $userDataTraded */ public function setUserDataTraded($userDataTraded) { $this->userDataTraded = (bool) $userDataTraded; } /** * Returns whether the principle of Data Economy, Data Avoidance or Data Minimization is being followed * * The principle means that, in all situations and use cases, the service provider aims at collecting * the minimum amount of user data possible, and, afterwards, user data is deleted again as soon as possible * * @return bool */ public function hasDataMinimizationGoal() { return $this->dataMinimizationGoal; } /** * Sets whether the principle of Data Economy, Data Avoidance or Data Minimization is being followed * * The principle means that, in all situations and use cases, the service provider aims at collecting * the minimum amount of user data possible, and, afterwards, user data is deleted again as soon as possible * * @param bool $dataMinimizationGoal */ public function setDataMinimizationGoal($dataMinimizationGoal) { $this->dataMinimizationGoal = (bool) $dataMinimizationGoal; } /** * Returns whether a minimum age for children has been defined * * See "COPPA" (USA), "GDPR" (EU), etc. * * @return bool */ public function hasChildrenMinimumAge() { return !empty($this->childrenMinimumAge); } /** * Sets the minimum age for children * * See "COPPA" (USA), "GDPR" (EU), etc. * * @param int|null $childrenMinimumAge the minimum age in years, or `null` to unset */ public function setChildrenMinimumAge($childrenMinimumAge) { $this->childrenMinimumAge = $childrenMinimumAge !== null ? ((int) $childrenMinimumAge) : null; } /** * Returns whether the user may opt-out from all emails that are not essential to the operation of the services * * In particular, this includes emails sent for marketing or promotional purposes * * @return bool */ public function hasPromotionalEmailOptOut() { return $this->promotionalEmailOptOut; } /** * Sets whether the user may opt-out from all emails that are not essential to the operation of the services * * In particular, this includes emails sent for marketing or promotional purposes * * @param bool $promotionalEmailOptOut */ public function setPromotionalEmailOptOut($promotionalEmailOptOut) { $this->promotionalEmailOptOut = (bool) $promotionalEmailOptOut; } /** * Returns whether cookies are used and sent to the user's device * * @return bool */ public function hasCookies() { return $this->hasFirstPartyCookies() || $this->hasThirdPartyCookies(); } /** * Returns whether first-party cookies are used and sent to the user's device * * @return bool */ public function hasFirstPartyCookies() { return $this->firstPartyCookies; } /** * Sets whether first-party cookies are used and sent to the user's device * * @param bool $firstPartyCookies */ public function setFirstPartyCookies($firstPartyCookies) { $this->firstPartyCookies = (bool) $firstPartyCookies; } /** * Returns whether third-party cookies are used and sent to the user's device * * @return bool */ public function hasThirdPartyCookies() { return $this->thirdPartyCookies; } /** * Sets whether third-party cookies are used and sent to the user's device * * @param bool $thirdPartyCookies */ public function setThirdPartyCookies($thirdPartyCookies) { $this->thirdPartyCookies = (bool) $thirdPartyCookies; } /** * Returns whether users may delete their entire accounts or similar collections of personal data * * @return bool */ public function isAccountDeletable() { return $this->accountDeletable; } /** * Sets whether users may delete their entire accounts or similar collections of personal data * * @param bool $accountDeletable */ public function setAccountDeletable($accountDeletable) { $this->accountDeletable = (bool) $accountDeletable; } /** * Returns whether there are additional copies of user data for backup purposes * * @return bool */ public function hasPreservationInBackups() { return $this->preservationInBackups; } /** * Sets whether there are additional copies of user data for backup purposes * * @param bool $preservationInBackups */ public function setPreservationInBackups($preservationInBackups) { $this->preservationInBackups = (bool) $preservationInBackups; } /** * Returns whether user data may be shared with third-party service providers, vendors, contractors or agents * * @return bool */ public function hasThirdPartyServiceProviders() { return $this->thirdPartyServiceProviders; } /** * Sets whether user data may be shared with third-party service providers, vendors, contractors or agents * * @param bool $thirdPartyServiceProviders */ public function setThirdPartyServiceProviders($thirdPartyServiceProviders) { $this->thirdPartyServiceProviders = (bool) $thirdPartyServiceProviders; } /** * Returns whether user data may be transferred internationally to third countries with proper safeguards for protection * * @return bool */ public function hasInternationalTransfers() { return $this->internationalTransfers; } /** * Sets whether user data may be transferred internationally to third countries with proper safeguards for protection * * @param bool $internationalTransfers */ public function setInternationalTransfers($internationalTransfers) { $this->internationalTransfers = (bool) $internationalTransfers; } /** * Returns whether user data may be part of the assets transferred during mergers, acquisitions or * other changes of ownership * * @return bool */ public function hasTransferUponMergerOrAcquisition() { return $this->transferUponMergerOrAcquisition; } /** * Sets whether user data may be part of the assets transferred during mergers, acquisitions or * other changes of ownership * * @param bool $transferUponMergerOrAcquisition */ public function setTransferUponMergerOrAcquisition($transferUponMergerOrAcquisition) { $this->transferUponMergerOrAcquisition = (bool) $transferUponMergerOrAcquisition; } /** * Returns whether all connections to the server are, without exceptions, secured using SSL/TLS * * @return bool */ public function hasTlsEverywhere() { return $this->tlsEverywhere; } /** * Sets whether all connections to the server are, without exceptions, secured using SSL/TLS * * @param bool $tlsEverywhere */ public function setTlsEverywhere($tlsEverywhere) { $this->tlsEverywhere = (bool) $tlsEverywhere; } /** * Returns the name of the data protection authority that is responsible * * @return string|null */ public function getCompetentSupervisoryAuthorityName() { return $this->competentSupervisoryAuthorityName; } /** * Returns the URL for more information on the data protection authority that is responsible * * @return string|null */ public function getCompetentSupervisoryAuthorityUrl() { return $this->competentSupervisoryAuthorityUrl; } /** * Sets the data protection authority that is responsible * * @param string|null $name the name, or `null` to unset * @param string|null $url (optional) the URL for more information, or `null` to unset */ public function setCompetentSupervisoryAuthority($name = null, $url = null) { $this->competentSupervisoryAuthorityName = ($name !== null) ? ((string) $name) : null; $this->competentSupervisoryAuthorityUrl = ($url !== null) ? ((string) $url) : null; } /** * Returns the declared period for notifications to the user about major changes * * @return int */ public function getNotificationPeriod() { return $this->notificationPeriod; } /** * Sets the declared period for notifications to the user about major changes * * @param int $notificationPeriod */ public function setNotificationPeriod($notificationPeriod) { $this->notificationPeriod = (int) $notificationPeriod; } /** * Returns whether the user has the right of access * * @return bool */ public function hasRightOfAccess() { return $this->rightOfAccess; } /** * Sets whether the user has the right of access * * @param bool $rightOfAccess */ public function setRightOfAccess($rightOfAccess) { $this->rightOfAccess = (bool) $rightOfAccess; } /** * Returns whether the user has the right to rectification * * @return bool */ public function hasRightToRectification() { return $this->rightToRectification; } /** * Sets whether the user has the right to rectification * * @param bool $rightToRectification */ public function setRightToRectification($rightToRectification) { $this->rightToRectification = (bool) $rightToRectification; } /** * Returns whether the user has the right to erasure * * @return bool */ public function hasRightToErasure() { return $this->rightToErasure; } /** * Sets whether the user has the right to erasure * * @param bool $rightToErasure */ public function setRightToErasure($rightToErasure) { $this->rightToErasure = (bool) $rightToErasure; } /** * Returns whether the user has the right to restriction of processing * * @return bool */ public function hasRightToRestrictProcessing() { return $this->rightToRestrictProcessing; } /** * Sets whether the user has the right to restriction of processing * * @param bool $rightToRestrictProcessing */ public function setRightToRestrictProcessing($rightToRestrictProcessing) { $this->rightToRestrictProcessing = (bool) $rightToRestrictProcessing; } /** * Returns whether the user has the right to data portability * * @return bool */ public function hasRightToDataPortability() { return $this->rightToDataPortability; } /** * Sets whether the user has the right to data portability * * @param bool $rightToDataPortability */ public function setRightToDataPortability($rightToDataPortability) { $this->rightToDataPortability = (bool) $rightToDataPortability; } /** * Returns whether the user has the right to object * * @return bool */ public function hasRightToObject() { return $this->rightToObject; } /** * Sets whether the user has the right to object * * @param bool $rightToObject */ public function setRightToObject($rightToObject) { $this->rightToObject = (bool) $rightToObject; } /** * Returns whether the user has rights related to automated individual decision-making, including profiling * * @return bool */ public function hasRightsRelatedToAutomatedDecisions() { return $this->rightsRelatedToAutomatedDecisions; } /** * Sets whether the user has rights related to automated individual decision-making, including profiling * * @param bool $rightsRelatedToAutomatedDecisions */ public function setRightsRelatedToAutomatedDecisions($rightsRelatedToAutomatedDecisions) { $this->rightsRelatedToAutomatedDecisions = (bool) $rightsRelatedToAutomatedDecisions; } /** * Returns whether any contact information has been set * * @return bool */ public function hasContactInformation() { return $this->hasContactEmail() || $this->hasContactUrl() || $this->hasContactImage(); } /** * Returns whether the email address for contact has been defined * * @return bool */ public function hasContactEmail() { return $this->contactEmail !== null; } /** * Sets the email address for contact * * @param string|null $contactEmail the email address, or `null` to unset */ public function setContactEmail($contactEmail) { $this->contactEmail = $contactEmail !== null ? ((string) $contactEmail) : null; } /** * Returns whether the URL of a page with (detailed) contact information has been defined * * @return bool */ public function hasContactUrl() { return $this->contactUrl !== null; } /** * Sets the URL of a page with (detailed) contact information * * @param string|null $contactUrl the URL, or `null` to unset */ public function setContactUrl($contactUrl) { $this->contactUrl = $contactUrl !== null ? ((string) $contactUrl) : null; } /** * Returns whether an image showing (detailed) contact information has been defined * * @return bool */ public function hasContactImage() { return !empty($this->contactImage) && !empty($this->contactImage[0]); } /** * Sets an image showing (detailed) contact information * * @param string $source the URL of the image to display * @param string|null $alternativeText (optional) the alternative text to show if the image cannot be displayed * @param int|null $width (optional) the suggested width of the image in pixels * @param int|null $height (optional) the suggested height of the image in pixels */ public function setContactImage($source, $alternativeText = null, $width = null, $height = null) { $this->contactImage = [ ($source !== null) ? ((string) $source) : null, ($source !== null && $alternativeText !== null) ? ((string) $alternativeText) : null, ($source !== null && $width !== null) ? ((int) $width) : null, ($source !== null && $height !== null) ? ((int) $height) : null, ]; } public function __construct() { $this->publishedAt = null; $this->takesEffectAt = null; $this->expiresAt = null; $this->versionName = null; $this->canonicalUrl = null; $this->scopes = []; $this->dataGroups = []; $this->userDataTraded = false; $this->dataMinimizationGoal = true; $this->childrenMinimumAge = self::CHILDREN_MINIMUM_AGE_DEFAULT; $this->promotionalEmailOptOut = true; $this->firstPartyCookies = true; $this->thirdPartyCookies = true; $this->accountDeletable = true; $this->preservationInBackups = true; $this->thirdPartyServiceProviders = true; $this->internationalTransfers = true; $this->transferUponMergerOrAcquisition = true; $this->tlsEverywhere = false; $this->competentSupervisoryAuthorityName = null; $this->competentSupervisoryAuthorityUrl = null; $this->notificationPeriod = self::NOTIFICATION_PERIOD_DEFAULT; $this->rightOfAccess = true; $this->rightToRectification = true; $this->rightToErasure = true; $this->rightToRestrictProcessing = true; $this->rightToDataPortability = true; $this->rightToObject = true; $this->rightsRelatedToAutomatedDecisions = true; $this->contactEmail = null; $this->contactUrl = null; $this->contactImage = null; } } ================================================ FILE: src/Scope/AndroidAppScope.php ================================================ trackId; } /** * @param string $trackId the track ID of the application, e.g. `54614917093` * @param string $name the name of the application, e.g. `My iOS App` */ public function __construct($trackId, $name) { $this->trackId = (string) $trackId; $this->name = (string) $name; } protected function toMarkup() { $target = \sprintf(self::URL_FORMAT, $this->trackId); return new LinkMarkup($target, $this->name); } } ================================================ FILE: src/Scope/IosAppScope.php ================================================ packageName; } /** * @param string $packageName the package name of the application, e.g. `com.example.app` * @param string $name the name of the application, e.g. `My Android App` */ public function __construct($packageName, $name) { $this->packageName = (string) $packageName; $this->name = (string) $name; } protected function toMarkup() { $target = \sprintf(self::URL_FORMAT, $this->packageName); return new LinkMarkup($target, $this->name); } } ================================================ FILE: src/Scope/Scope.php ================================================ url; } /** * @param string $url the URL of the website, e.g. `https://www.example.com/` * @param string $name the name of the website, e.g. `My Website` */ public function __construct($url, $name) { $this->url = (string) $url; $this->name = (string) $name; } protected function toMarkup() { return new LinkMarkup($this->url, $this->name); } } ================================================ FILE: src/Throwable/Error.php ================================================ setPublishedAt(1393372800); $policy->setTakesEffectAt(1394582400); $policy->setExpiresAt(1395792000); $policy->setVersionName('v3.1.4'); $policy->setCanonicalUrl('https://www.example.com/privacy.html'); $policy->addScope( new \Delight\PrivacyPolicy\Scope\WebsiteScope('https://www.example.com/', 'example.com') ); $policy->addScope( new \Delight\PrivacyPolicy\Scope\PlayStoreAndroidAppScope('com.example.app', 'Example for Android') ); $policy->addScope( new \Delight\PrivacyPolicy\Scope\AppStoreIosAppScope('54614917093', 'Example for iOS') ); $policy->addDataGroup( 'Server logs', 'Whenever you access our services, including your access of any individual part or section of our services, we record certain information about the nature of your access. That information is never combined with information from other data sources and will not be associated with the identity of any account. However, we reserve the right to review the data retrospectively if there is specific evidence supporting the suspicion of a case of fraud or any other illegal activity or illegal use of our services.', [ \Delight\PrivacyPolicy\Data\DataBasis::LEGITIMATE_INTERESTS ], null, [ \Delight\PrivacyPolicy\Data\DataPurpose::ADMINISTRATION ], \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, function (\Delight\PrivacyPolicy\Data\DataGroup $group) { $retentionTimeHours = 24 * 14; $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_HTTP_METHOD, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_HTTP_STATUS, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_IP_ADDRESS, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_REFERER, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_SIZE, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_DATETIME, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_URL, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::ACCESS_USERAGENT_STRING, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, $retentionTimeHours ); } ); $policy->addDataGroup( 'Account information', 'When you create an account by signing up, and whenever you use that account by signing in afterwards, we collect the data that you provide to us voluntarily in the course of that process.', [ \Delight\PrivacyPolicy\Data\DataBasis::CONTRACT, \Delight\PrivacyPolicy\Data\DataBasis::CONSENT ], [ \Delight\PrivacyPolicy\Data\DataSpecialCondition::EXPLICIT_CONSENT ], [ \Delight\PrivacyPolicy\Data\DataPurpose::ADMINISTRATION, \Delight\PrivacyPolicy\Data\DataPurpose::FULFILLMENT, \Delight\PrivacyPolicy\Data\DataPurpose::PERSONALIZATION ], \Delight\PrivacyPolicy\Data\DataRequirement::OPT_IN, function (\Delight\PrivacyPolicy\Data\DataGroup $group) { $group->addElement( \Delight\PrivacyPolicy\Data\DataType::USER_EMAIL, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, null ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::USER_PASSWORD_HASHED_STRONG, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, null ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::USER_NAME_ALIAS, \Delight\PrivacyPolicy\Data\DataRequirement::OPT_IN, null ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::USER_REGISTRATION_DATETIME, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, null ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::USER_LOGIN_DATETIME, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, null ); $group->addElement( \Delight\PrivacyPolicy\Data\DataType::USER_SIGNATURE, \Delight\PrivacyPolicy\Data\DataRequirement::ALWAYS, null ); } ); $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); $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); echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo 'PHP-PrivacyPolicy'; echo ''; echo ''; echo '

'; echo 'English'; echo ' · '; echo 'German (formal)'; echo ' · '; echo 'German (informal)'; echo ' · '; echo 'JSON'; echo '

'; echo '
'; if ($policy instanceof \Delight\PrivacyPolicy\HumanPrivacyPolicy) { echo '

' . $policy->getShortTitle() . '

'; echo '

' . $policy->getLongTitle() . '

'; echo '

'; echo 'HTML'; echo ' · '; echo 'Plain text'; echo ' · '; echo 'Markdown'; echo '

'; echo '
'; } if ($policy instanceof \Delight\PrivacyPolicy\Language\JsonPrivacyPolicy) { echo '

JSON

'; echo '
' . $policy->toJson() . '
'; } if ($policy instanceof \Delight\PrivacyPolicy\HumanPrivacyPolicy) { echo '

HTML

'; echo $policy->toHtml(); echo '
'; echo '

Plain text

'; echo '
' . $policy->toPlainText() . '
'; echo '
'; echo '

Markdown

'; echo '
' . $policy->toMarkdown() . '
'; } echo ''; echo '';