├── .gitignore ├── composer.json ├── VK.php ├── README.md ├── VKBase.php └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | 4 | # composer vendor dir 5 | /vendor 6 | 7 | # composer itself is not needed 8 | composer.phar 9 | composer.lock -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jumper423/yii2-vk", 3 | "description": "Расширенная работа с API VK", 4 | "keywords": ["yii2", "vk", "api", "вконтакте", "апи", "vkontakte"], 5 | "homepage": "http://infoblog1.ru/learn/cms/yii/rabota-s-api-vk-v-yii2", 6 | "type": "project", 7 | "license": "Apache License 2.0", 8 | "support": { 9 | "issues": "https://github.com/jumper423/yii2-vk/issues?state=open", 10 | "source": "https://github.com/jumper423/yii2-vk" 11 | }, 12 | "minimum-stability": "stable", 13 | "require": { 14 | "php": ">=5.4.0", 15 | "yiisoft/yii2": "*", 16 | "yiisoft/yii2-authclient": "~2.0", 17 | "jumper423/yii2-captcha": "*", 18 | "jumper423/yii2-behaviors": "*" 19 | }, 20 | "config": { 21 | "process-timeout": 1800 22 | }, 23 | "autoload": { 24 | "psr-4": {"jumper423\\": ""} 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /VK.php: -------------------------------------------------------------------------------- 1 | trigger(self::EVENT_INIT); 36 | } 37 | 38 | public function behaviors() 39 | { 40 | return [ 41 | [ 42 | 'class' => СallableBehavior::className(), 43 | 'attributes' => [ 44 | self::EVENT_INIT => ['clientId','clientSecret'], 45 | ], 46 | ], 47 | ]; 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getOauthUri() 54 | { 55 | return "https://oauth.vk.com/authorize?client_id={$this->clientId}&display=popup&redirect_uri={$this->redirectUri}&scope={$this->scope}&response_type=token"; 56 | } 57 | 58 | public function setToken($token) 59 | { 60 | $this->token = $token; 61 | } 62 | 63 | public function isToken() 64 | { 65 | if (!$this->token) { 66 | return false; 67 | } 68 | try { 69 | $this->api('messages.getDialogs', ['count' => 0]); 70 | return true; 71 | } catch (Exception $e){ 72 | return false; 73 | } 74 | } 75 | 76 | /** 77 | * @param string $apiSubUrl 78 | * @param array $params 79 | * @param array $headers 80 | * @param bool $delay 81 | * @param bool $error 82 | * @return array 83 | * @throws \Exception 84 | */ 85 | public function api($apiSubUrl, $params = [], $headers = [], $delay = false, $error = false) 86 | { 87 | $params['lang'] = 'ru'; 88 | $countError = 0; 89 | $e = new \Exception(); 90 | while ($countError < 5) { 91 | try { 92 | return parent::api($apiSubUrl, 'POST', $params, $headers, $delay)['response']; 93 | } catch (\Exception $e) { 94 | if ($error) { 95 | throw $e; 96 | } 97 | if ($e->getCode()) { 98 | if ($e->getCode() == 6) { 99 | sleep(2); 100 | return $this->api($apiSubUrl, $params, $headers, $delay, true); 101 | } else { 102 | throw $e; 103 | } 104 | } elseif ($countError > 0) { 105 | $this->big = true; 106 | } 107 | ++$countError; 108 | } 109 | } 110 | throw $e; 111 | } 112 | 113 | /** Позволять выполнять долгие запросы */ 114 | public function long(){ 115 | $this->speed = self::SPEED_LONG; 116 | } 117 | 118 | /** Стараться по возможности выполнять быстрые запросы, если в вк происходят сбои */ 119 | public function fast(){ 120 | $this->speed = self::SPEED_FAST; 121 | } 122 | 123 | /** 124 | * Returns default cURL options. 125 | * @return array cURL options. 126 | */ 127 | protected function defaultCurlOptions() 128 | { 129 | $result = parent::defaultCurlOptions(); 130 | $result[CURLOPT_NOSIGNAL] = 1; 131 | if ($this->speed == self::SPEED_LONG || $this->big) { 132 | $result[CURLOPT_CONNECTTIMEOUT_MS] = 60000; 133 | $result[CURLOPT_TIMEOUT_MS] = 60000; 134 | $this->big = false; 135 | } else { 136 | $result[CURLOPT_CONNECTTIMEOUT_MS] = 500; 137 | $result[CURLOPT_TIMEOUT_MS] = 500; 138 | } 139 | return $result; 140 | } 141 | 142 | public function setAccessToken($token) 143 | { 144 | if (is_string($token)) { 145 | $token = Json::decode($token); 146 | } 147 | if (is_array($token)) { 148 | $token = new OAuthToken($token); 149 | } 150 | parent::setAccessToken($token); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2 VK 2 | ================ 3 | [![PHP version](https://badge.fury.io/ph/jumper423%2Fyii2-vk.svg)](https://badge.fury.io/ph/jumper423%2Fyii2-vk) 4 | 5 | Времнно используйте только вторую версию 6 | ================ 7 | 8 | Компонент для расширенной работы с ВК API в YII2. Загрузка изображений, распознавание капчи, постановка очередей и многое другое. 9 | 10 | Сайт с подробным описанием [yii2 api vk](http://infoblog1.ru/learn/cms/yii/rabota-s-api-vk-v-yii2) 11 | 12 | Особенности 13 | ------------ 14 | * Задержка выполнения 15 | * Разделины post и get 16 | * Загрузка изображений 17 | * Добавление в очереди 18 | * Запись задач и выполнению их по cron-у 19 | * Интеграция с распознованием капчи 20 | * Запись в атрибуты token-а 21 | 22 | Установка 23 | ------------ 24 | Предпочтительный способ установить это расширение через [composer](http://getcomposer.org/download/). 25 | 26 | Либо запустить 27 | 28 | ``` 29 | php composer.phar require --prefer-dist jumper423/yii2-vk "2.*" 30 | ``` 31 | 32 | или добавить 33 | 34 | ``` 35 | "jumper423/yii2-vk": "2.*" 36 | ``` 37 | 38 | в файл `composer.json`. 39 | 40 | Конфигурация 41 | ------------ 42 | ```php 43 | 'components' => [ 44 | 'vk' => [ 45 | 'class' => 'jumper423\VK', 46 | 'clientId' => '11111', 47 | 'clientSecret' => 'n9wsv98svSD867SA7dsda87', 48 | 'delay' => 0.7, // Минимальная задержка между запросами 49 | 'delayExecute' => 120, // Задержка между группами инструкций в очереди 50 | 'limitExecute' => 1, // Количество инструкций на одно выполнении в очереди 51 | 'captcha' => 'captcha', // Компонент по распознованию капчи 52 | ], 53 | ], 54 | 'aliases' => [ 55 | '@actions' => '@backend/runtime/cron', // Папка куда будут складироваться очереди для cron-а 56 | ], 57 | ``` 58 | 59 | "Расширенная" конфигурация 60 | 61 | ```php 62 | 'components' => [ 63 | 'captcha' => [ 64 | 'class' => 'jumper423\Captcha', 65 | 'pathTmp' => '@imagescache/captcha', 66 | 'apiKey' => '42eab4119020dbc729f657', 67 | ], 68 | 'authClientCollection' => [ 69 | 'class' => 'yii\authclient\Collection', 70 | 'clients' => [ 71 | 'vkontakte' => [ 72 | 'class' => 'jumper423\VK', 73 | 'clientId' => '11111', 74 | 'clientSecret' => 'n9wsv98svSD867SA7dsda87', 75 | 'delay' => 0.7, 76 | 'delayExecute' => 120, 77 | 'limitExecute' => 1, 78 | 'captcha' => 'captcha', 79 | 'scope' => 'friends,photos,pages,wall,groups,email,stats,ads,offline,notifications', //,messages,nohttps 80 | 'title' => 'ВКонтакте' 81 | ], 82 | ], 83 | ], 84 | ], 85 | 'aliases' => [ 86 | '@actions' => '@backend/runtime/cron', // Папка куда будут складироваться очереди для cron-а 87 | ], 88 | ``` 89 | 90 | 91 | Использование 92 | ------------ 93 | 94 | Вызов следовательно 95 | 96 | ```php 97 | /** 98 | * @var jumper423\VK $vk 99 | */ 100 | 101 | $vk = Yii::$app->vk; 102 | 103 | или 104 | 105 | $vk = Yii::$app->authClientCollection->getClient('vkontakte'); 106 | ``` 107 | 108 | Создание альбома 109 | 110 | ```php 111 | $response = $vk->post('photos.createAlbum', ['group_id' => $groupId, 'title' => $title, 'upload_by_admins_only' => 1]); 112 | ``` 113 | 114 | Добавление инструкции в очередь 115 | 116 | ```php 117 | foreach ($images as $image) { 118 | $vk->addAction('photos.edit', ['caption' => $caption, 'owner_id' => $ownerId, 'photo_id' => $image,]); 119 | } 120 | // Добавление в cron 121 | $vk->addActionsInCron('photos.edit'); 122 | // Или начать выполнение очереди командой 123 | // $vk->performAnAction(); 124 | ``` 125 | 126 | Выполнение cron-а 127 | 128 | ```php 129 | $vk->performAnActionFromCron('photos.edit'); 130 | ``` 131 | 132 | Загрузка изображения в альбом пользователя или группы 133 | 134 | ```php 135 | $imageId = $vk->loadImage($imagePath, $albumId, $groupId); 136 | ``` 137 | 138 | Авторизация со всеми правами с помощью selenium 139 | ------------ 140 | 141 | ```php 142 | $data = $webDriver->getData($this->api->getOauthUri()); 143 | if (!count($data)) { 144 | throw new Exception('Ошибка при авторизации'); 145 | } 146 | $token = [ 147 | 'tokenParamKey' => 'access_token', 148 | 'tokenSecretParamKey' => 'oauth_token_secret', 149 | 'createTimestamp' => time(), 150 | 'params' => $data, 151 | ]; 152 | $this->vk->vk_id = $data['user_id']; 153 | $this->vk->token = $token; 154 | $this->vk->save(); 155 | $this->api->setToken($this->vk->token); 156 | ``` 157 | 158 | ```php 159 | /** 160 | * @param $url string 161 | * @param $recursia bool 162 | */ 163 | public function getData($url, $recursia = true) 164 | { 165 | $this->driver->get($url); 166 | $this->driver->findElement(WebDriverBy::name('email'))->sendKeys($this->vkTable->login); 167 | $this->driver->findElement(WebDriverBy::name('pass'))->sendKeys($this->vkTable->password); 168 | $this->driver->findElement(WebDriverBy::id('install_allow'))->click(); 169 | sleep(3); 170 | while ($this->driver->findElements(WebDriverBy::xpath('//input[@name=\'captcha_key\']'))) { 171 | $this->captcha(); 172 | $this->driver->findElement(WebDriverBy::name('pass'))->sendKeys($this->vkTable->password); 173 | $this->driver->findElement(WebDriverBy::id('install_allow'))->click(); 174 | sleep(3); 175 | } 176 | $this->driver->wait(60, 1000)->until( 177 | WebDriverExpectedCondition::titleContains('VK | Request Access') 178 | ); 179 | $this->driver->findElement(WebDriverBy::id('install_allow'))->click(); 180 | $this->driver->wait(60, 1000)->until( 181 | WebDriverExpectedCondition::titleContains('OAuth Blank') 182 | ); 183 | $urlCurrent = $this->driver->getCurrentURL(); 184 | $parseUrl = parse_url($urlCurrent); 185 | if (!isset($parseUrl['fragment']) && $recursia == true) { 186 | return $this->getData($url, false); 187 | } 188 | $query = $parseUrl['fragment']; 189 | parse_str($query, $data); 190 | return $data; 191 | } 192 | ``` 193 | -------------------------------------------------------------------------------- /VKBase.php: -------------------------------------------------------------------------------- 1 | captcha)) { 35 | if (Yii::$app->has($this->captcha) && Yii::$app->get($this->captcha) instanceof CaptchaInterface) { 36 | $this->captcha = Yii::$app->get($this->captcha); 37 | } else { 38 | $this->captcha = null; 39 | } 40 | } elseif (is_object($this->captcha)) { 41 | if (!($this->captcha instanceof CaptchaInterface)) { 42 | $this->captcha = null; 43 | } 44 | } else { 45 | $this->captcha = null; 46 | } 47 | } 48 | 49 | /** 50 | * @param bool|false $delay 51 | */ 52 | private function sleep($delay = false) 53 | { 54 | if ($delay === false) { 55 | $delay = $this->delay; 56 | } 57 | if ($this->lastRequest === null) { 58 | $this->lastRequest = microtime(true); 59 | } else { 60 | $microtime = microtime(true); 61 | if ($this->lastRequest + $delay > $microtime) { 62 | usleep(($this->lastRequest + $delay - $microtime) * self::MILLISECOND); 63 | $this->lastRequest = microtime(true); 64 | } 65 | } 66 | usleep(rand($this->sleepRandMin * self::MILLISECOND, $this->sleepRandMax * self::MILLISECOND)); 67 | } 68 | 69 | /** 70 | * @inheritdoc 71 | */ 72 | public function api($apiSubUrl, $method = 'GET', $params = [], $headers = [], $delay = false) 73 | { 74 | $this->sleep($delay); 75 | if (preg_match('/^https?:\\/\\//is', $apiSubUrl)) { 76 | $url = $apiSubUrl; 77 | } else { 78 | $url = $this->apiBaseUrl . '/' . $apiSubUrl; 79 | } 80 | $accessToken = $this->getAccessToken(); 81 | /*if (!is_object($accessToken) || !$accessToken->getIsValid()) { 82 | throw new Exception('Invalid access token.'); 83 | }*/ 84 | $response = $this->apiInternal($accessToken, $url, $method, $params, $headers); 85 | if (ArrayHelper::getValue($response, 'error.error_code') == 14 && $this->captcha) { 86 | if ($this->captcha->run(ArrayHelper::getValue($response, 'error.captcha_img'))) { 87 | $response = self::api($apiSubUrl, $method, ArrayHelper::merge($params, 88 | [ 89 | 'captcha_sid' => ArrayHelper::getValue($response, 'error.captcha_sid'), 90 | 'captcha_key' => $this->captcha->result() 91 | ]) 92 | , $headers); 93 | } else { 94 | throw new Exception($this->captcha->error()); 95 | } 96 | } elseif (ArrayHelper::getValue($response, 'error')) { 97 | throw new Exception(ArrayHelper::getValue($response, 'error.error_msg'), ArrayHelper::getValue($response, 'error.error_code')); 98 | } 99 | return $response; 100 | } 101 | 102 | /** 103 | * @inheritdoc 104 | */ 105 | protected function apiInternal($accessToken, $url, $method, array $params, array $headers) 106 | { 107 | if (is_object($accessToken)) { 108 | $params['uids'] = $accessToken->getParam('user_id'); 109 | $params['access_token'] = $accessToken->getToken(); 110 | } 111 | return $this->sendRequest($method, $url, $params, $headers); 112 | } 113 | 114 | /** 115 | * @param $apiSubUrl 116 | * @param array $params 117 | */ 118 | public function addAction($apiSubUrl, array $params = []) 119 | { 120 | $this->execute[] = "API.{$apiSubUrl}(" . Json::encode($params) . ")"; 121 | } 122 | 123 | /** 124 | * @param $method 125 | */ 126 | public function addActionsInCron($method) 127 | { 128 | $json = Json::decode(file_get_contents(Yii::getAlias("@actions/{$method}.json"))); 129 | $json = ArrayHelper::merge($json, $this->execute); 130 | file_put_contents(Yii::getAlias("@actions/{$method}.json"), Json::encode($json)); 131 | } 132 | 133 | /** 134 | * @param $method 135 | * @return array 136 | */ 137 | public function performAnActionFromCron($method) 138 | { 139 | $response = []; 140 | do { 141 | $execute = Json::decode(file_get_contents(Yii::getAlias("@actions/{$method}.json"))); 142 | if (count($execute)) { 143 | $executeRow = array_shift($execute); 144 | $response = ArrayHelper::merge($response, $this->post('execute', ['code' => "return \n [" . $executeRow . "];"])); 145 | $execute = Json::decode(file_get_contents(Yii::getAlias("@actions/{$method}.json"))); 146 | $r = array_search($executeRow, $execute); 147 | if ($r !== false) { 148 | unset($execute[$r]); 149 | file_put_contents(Yii::getAlias("@actions/{$method}.json"), Json::encode($execute)); 150 | } 151 | sleep($this->delayExecute2); 152 | } else { 153 | break; 154 | } 155 | } while (true); 156 | return $response; 157 | } 158 | 159 | /** 160 | * @return array 161 | */ 162 | public function performAnAction() 163 | { 164 | $executeCount = count($this->execute); 165 | $response = []; 166 | for ($i = 0; $executeCount > $i; $i += $this->limitExecute) { 167 | $response = ArrayHelper::merge($response, $this->post('execute', ['code' => "return \n [" . implode(",\n", array_slice($this->execute, $i, $this->limitExecute)) . "];"])); 168 | sleep($this->delayExecute2); 169 | } 170 | $this->execute = []; 171 | return $response; 172 | } 173 | 174 | /** 175 | * @param $imagePath 176 | * @param $albumId 177 | * @param null|int $groupId 178 | * @return int 179 | * @throws Exception 180 | */ 181 | public function loadImage($imagePath, $albumId, $groupId = null) 182 | { 183 | $params = ['album_id' => $albumId]; 184 | if ($groupId) { 185 | $params['group_id'] = $groupId; 186 | } 187 | $request = $this->post('photos.getUploadServer', $params); 188 | $uploadUrl = ArrayHelper::getValue($request, 'response.upload_url'); 189 | if ($uploadUrl) { 190 | $ch = curl_init($uploadUrl); 191 | curl_setopt($ch, CURLOPT_POST, 1); 192 | if (version_compare(PHP_VERSION, '5.5.0') >= 0) { 193 | curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false); 194 | } 195 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 196 | curl_setopt($ch, CURLOPT_POSTFIELDS, [ 197 | 'file1' => "@" . $imagePath 198 | ]); 199 | $paramsSave = Json::decode(curl_exec($ch)); 200 | curl_close($ch); 201 | if (count($paramsSave)) { 202 | return ArrayHelper::getValue($this->post('photos.save', $paramsSave), 'response.0.pid'); 203 | } else { 204 | throw new Exception('Empty params save'); 205 | } 206 | } else { 207 | throw new Exception('Not upload_url'); 208 | } 209 | } 210 | 211 | /** 212 | * @inheritdoc 213 | */ 214 | protected function initUserAttributes() 215 | { 216 | $response = $this->api('users.get.json', 'GET', [ 217 | 'fields' => implode(',', $this->attributeNames), 218 | ]); 219 | $attributes = array_shift($response['response']); 220 | 221 | $accessToken = $this->getAccessToken(); 222 | if (is_object($accessToken)) { 223 | $accessTokenParams = $accessToken->getParams(); 224 | $attributes = array_merge($accessTokenParams, $attributes); 225 | } 226 | 227 | return $attributes; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | --------------------------------------------------------------------------------