PageRenderTime 64ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/SitesManager/API.php

https://github.com/CodeYellowBV/piwik
PHP | 1555 lines | 904 code | 155 blank | 496 comment | 78 complexity | 1ea2e502aa0ba3213488429bfa4a87c9 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\SitesManager;
  10. use Exception;
  11. use Piwik\Access;
  12. use Piwik\Common;
  13. use Piwik\Date;
  14. use Piwik\Db;
  15. use Piwik\IP;
  16. use Piwik\MetricsFormatter;
  17. use Piwik\Option;
  18. use Piwik\Piwik;
  19. use Piwik\ProxyHttp;
  20. use Piwik\SettingsPiwik;
  21. use Piwik\SettingsServer;
  22. use Piwik\Site;
  23. use Piwik\TaskScheduler;
  24. use Piwik\Tracker\Cache;
  25. use Piwik\Url;
  26. use Piwik\UrlHelper;
  27. /**
  28. * The SitesManager API gives you full control on Websites in Piwik (create, update and delete), and many methods to retrieve websites based on various attributes.
  29. *
  30. * This API lets you create websites via "addSite", update existing websites via "updateSite" and delete websites via "deleteSite".
  31. * When creating websites, it can be useful to access internal codes used by Piwik for currencies via "getCurrencyList", or timezones via "getTimezonesList".
  32. *
  33. * There are also many ways to request a list of websites: from the website ID via "getSiteFromId" or the site URL via "getSitesIdFromSiteUrl".
  34. * Often, the most useful technique is to list all websites that are known to a current user, based on the token_auth, via
  35. * "getSitesWithAdminAccess", "getSitesWithViewAccess" or "getSitesWithAtLeastViewAccess" (which returns both).
  36. *
  37. * Some methods will affect all websites globally: "setGlobalExcludedIps" will set the list of IPs to be excluded on all websites,
  38. * "setGlobalExcludedQueryParameters" will set the list of URL parameters to remove from URLs for all websites.
  39. * The existing values can be fetched via "getExcludedIpsGlobal" and "getExcludedQueryParametersGlobal".
  40. * See also the documentation about <a href='http://piwik.org/docs/manage-websites/' target='_blank'>Managing Websites</a> in Piwik.
  41. * @method static \Piwik\Plugins\SitesManager\API getInstance()
  42. */
  43. class API extends \Piwik\Plugin\API
  44. {
  45. const DEFAULT_SEARCH_KEYWORD_PARAMETERS = 'q,query,s,search,searchword,k,keyword';
  46. const OPTION_EXCLUDED_IPS_GLOBAL = 'SitesManager_ExcludedIpsGlobal';
  47. const OPTION_DEFAULT_TIMEZONE = 'SitesManager_DefaultTimezone';
  48. const OPTION_DEFAULT_CURRENCY = 'SitesManager_DefaultCurrency';
  49. const OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL = 'SitesManager_ExcludedQueryParameters';
  50. const OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL = 'SitesManager_SearchKeywordParameters';
  51. const OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL = 'SitesManager_SearchCategoryParameters';
  52. const OPTION_EXCLUDED_USER_AGENTS_GLOBAL = 'SitesManager_ExcludedUserAgentsGlobal';
  53. const OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE = 'SitesManager_EnableSiteSpecificUserAgentExclude';
  54. const OPTION_KEEP_URL_FRAGMENTS_GLOBAL = 'SitesManager_KeepURLFragmentsGlobal';
  55. /**
  56. * Returns the javascript tag for the given idSite.
  57. * This tag must be included on every page to be tracked by Piwik
  58. *
  59. * @param int $idSite
  60. * @param string $piwikUrl
  61. * @param bool $mergeSubdomains
  62. * @param bool $groupPageTitlesByDomain
  63. * @param bool $mergeAliasUrls
  64. * @param bool $visitorCustomVariables
  65. * @param bool $pageCustomVariables
  66. * @param bool $customCampaignNameQueryParam
  67. * @param bool $customCampaignKeywordParam
  68. * @param bool $doNotTrack
  69. * @internal param $
  70. * @return string The Javascript tag ready to be included on the HTML pages
  71. */
  72. public function getJavascriptTag($idSite, $piwikUrl = '', $mergeSubdomains = false, $groupPageTitlesByDomain = false,
  73. $mergeAliasUrls = false, $visitorCustomVariables = false, $pageCustomVariables = false,
  74. $customCampaignNameQueryParam = false, $customCampaignKeywordParam = false,
  75. $doNotTrack = false)
  76. {
  77. Piwik::checkUserHasViewAccess($idSite);
  78. if (empty($piwikUrl)) {
  79. $piwikUrl = SettingsPiwik::getPiwikUrl();
  80. }
  81. $piwikUrl = Common::sanitizeInputValues($piwikUrl);
  82. $htmlEncoded = Piwik::getJavascriptCode($idSite, $piwikUrl, $mergeSubdomains, $groupPageTitlesByDomain,
  83. $mergeAliasUrls, $visitorCustomVariables, $pageCustomVariables,
  84. $customCampaignNameQueryParam, $customCampaignKeywordParam,
  85. $doNotTrack);
  86. $htmlEncoded = str_replace(array('<br>', '<br />', '<br/>'), '', $htmlEncoded);
  87. return $htmlEncoded;
  88. }
  89. /**
  90. * Returns image link tracking code for a given site with specified options.
  91. *
  92. * @param int $idSite The ID to generate tracking code for.
  93. * @param string $piwikUrl The domain and URL path to the Piwik installation.
  94. * @param int $idGoal An ID for a goal to trigger a conversion for.
  95. * @param int $revenue The revenue of the goal conversion. Only used if $idGoal is supplied.
  96. * @return string The HTML tracking code.
  97. */
  98. public function getImageTrackingCode($idSite, $piwikUrl = '', $actionName = false, $idGoal = false, $revenue = false)
  99. {
  100. $urlParams = array('idsite' => $idSite, 'rec' => 1);
  101. if ($actionName !== false) {
  102. $urlParams['action_name'] = urlencode(Common::unsanitizeInputValue($actionName));
  103. }
  104. if ($idGoal !== false) {
  105. $urlParams['idGoal'] = $idGoal;
  106. if ($revenue !== false) {
  107. $urlParams['revenue'] = $revenue;
  108. }
  109. }
  110. /**
  111. * Triggered when generating image link tracking code server side. Plugins can use
  112. * this event to customise the image tracking code that is displayed to the
  113. * user.
  114. *
  115. * @param string &$piwikHost The domain and URL path to the Piwik installation, eg,
  116. * `'examplepiwik.com/path/to/piwik'`.
  117. * @param array &$urlParams The query parameters used in the <img> element's src
  118. * URL. See Piwik's image tracking docs for more info.
  119. */
  120. Piwik::postEvent('SitesManager.getImageTrackingCode', array(&$piwikUrl, &$urlParams));
  121. $piwikUrl = (ProxyHttp::isHttps() ? "https://" : "http://") . $piwikUrl . '/piwik.php';
  122. return "<!-- Piwik Image Tracker-->
  123. <img src=\"$piwikUrl?" . Url::getQueryStringFromParameters($urlParams) . "\" style=\"border:0\" alt=\"\" />
  124. <!-- End Piwik -->";
  125. }
  126. /**
  127. * Returns all websites belonging to the specified group
  128. * @param string $group Group name
  129. * @return array of sites
  130. */
  131. public function getSitesFromGroup($group)
  132. {
  133. Piwik::checkUserHasSuperUserAccess();
  134. $group = trim($group);
  135. $sites = Db::get()->fetchAll("SELECT *
  136. FROM " . Common::prefixTable("site") . "
  137. WHERE `group` = ?", $group);
  138. Site::setSitesFromArray($sites);
  139. return $sites;
  140. }
  141. /**
  142. * Returns the list of website groups, including the empty group
  143. * if no group were specified for some websites
  144. *
  145. * @return array of group names strings
  146. */
  147. public function getSitesGroups()
  148. {
  149. Piwik::checkUserHasSuperUserAccess();
  150. $groups = Db::get()->fetchAll("SELECT DISTINCT `group` FROM " . Common::prefixTable("site"));
  151. $cleanedGroups = array();
  152. foreach ($groups as $group) {
  153. $cleanedGroups[] = $group['group'];
  154. }
  155. $cleanedGroups = array_map('trim', $cleanedGroups);
  156. return $cleanedGroups;
  157. }
  158. /**
  159. * Returns the website information : name, main_url
  160. *
  161. * @throws Exception if the site ID doesn't exist or the user doesn't have access to it
  162. * @param int $idSite
  163. * @return array
  164. */
  165. public function getSiteFromId($idSite)
  166. {
  167. Piwik::checkUserHasViewAccess($idSite);
  168. $site = Db::get()->fetchRow("SELECT *
  169. FROM " . Common::prefixTable("site") . "
  170. WHERE idsite = ?", $idSite);
  171. Site::setSitesFromArray(array($site));
  172. return $site;
  173. }
  174. /**
  175. * Returns the list of alias URLs registered for the given idSite.
  176. * The website ID must be valid when calling this method!
  177. *
  178. * @param int $idSite
  179. * @return array list of alias URLs
  180. */
  181. private function getAliasSiteUrlsFromId($idSite)
  182. {
  183. $db = Db::get();
  184. $result = $db->fetchAll("SELECT url
  185. FROM " . Common::prefixTable("site_url") . "
  186. WHERE idsite = ?", $idSite);
  187. $urls = array();
  188. foreach ($result as $url) {
  189. $urls[] = $url['url'];
  190. }
  191. return $urls;
  192. }
  193. /**
  194. * Returns the list of all URLs registered for the given idSite (main_url + alias URLs).
  195. *
  196. * @throws Exception if the website ID doesn't exist or the user doesn't have access to it
  197. * @param int $idSite
  198. * @return array list of URLs
  199. */
  200. public function getSiteUrlsFromId($idSite)
  201. {
  202. Piwik::checkUserHasViewAccess($idSite);
  203. $site = new Site($idSite);
  204. $urls = $this->getAliasSiteUrlsFromId($idSite);
  205. return array_merge(array($site->getMainUrl()), $urls);
  206. }
  207. /**
  208. * Returns the list of all the website IDs registered.
  209. * Caller must check access.
  210. *
  211. * @return array The list of website IDs
  212. */
  213. private function getSitesId()
  214. {
  215. $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site'));
  216. $idSites = array();
  217. foreach ($result as $idSite) {
  218. $idSites[] = $idSite['idsite'];
  219. }
  220. return $idSites;
  221. }
  222. /**
  223. * Returns all websites, requires Super User access
  224. *
  225. * @return array The list of websites, indexed by idsite
  226. */
  227. public function getAllSites()
  228. {
  229. Piwik::checkUserHasSuperUserAccess();
  230. $sites = Db::get()->fetchAll("SELECT * FROM " . Common::prefixTable("site") . " ORDER BY idsite ASC");
  231. $return = array();
  232. foreach ($sites as $site) {
  233. $return[$site['idsite']] = $site;
  234. }
  235. Site::setSitesFromArray($return);
  236. return $return;
  237. }
  238. /**
  239. * Returns the list of all the website IDs registered.
  240. * Requires Super User access.
  241. *
  242. * @return array The list of website IDs
  243. */
  244. public function getAllSitesId()
  245. {
  246. Piwik::checkUserHasSuperUserAccess();
  247. try {
  248. return API::getInstance()->getSitesId();
  249. } catch (Exception $e) {
  250. // can be called before Piwik tables are created so return empty
  251. return array();
  252. }
  253. }
  254. /**
  255. * Returns the list of the website IDs that received some visits since the specified timestamp.
  256. * Requires Super User access.
  257. *
  258. * @param bool|int $timestamp
  259. * @return array The list of website IDs
  260. */
  261. public function getSitesIdWithVisits($timestamp = false)
  262. {
  263. Piwik::checkUserHasSuperUserAccess();
  264. if (empty($timestamp)) $timestamp = time();
  265. $time = Date::factory((int)$timestamp)->getDatetime();
  266. $result = Db::fetchAll("
  267. SELECT
  268. idsite
  269. FROM
  270. " . Common::prefixTable('site') . " s
  271. WHERE EXISTS (
  272. SELECT 1
  273. FROM " . Common::prefixTable('log_visit') . " v
  274. WHERE v.idsite = s.idsite
  275. AND visit_last_action_time > ?
  276. AND visit_last_action_time <= ?
  277. LIMIT 1)
  278. ", array($time, $now = Date::now()->addHour(1)->getDatetime()));
  279. $idSites = array();
  280. foreach ($result as $idSite) {
  281. $idSites[] = $idSite['idsite'];
  282. }
  283. return $idSites;
  284. }
  285. /**
  286. * Returns the list of websites with the 'admin' access for the current user.
  287. * For the superUser it returns all the websites in the database.
  288. *
  289. * @return array for each site, an array of information (idsite, name, main_url, etc.)
  290. */
  291. public function getSitesWithAdminAccess()
  292. {
  293. $sitesId = $this->getSitesIdWithAdminAccess();
  294. return $this->getSitesFromIds($sitesId);
  295. }
  296. /**
  297. * Returns the list of websites with the 'view' access for the current user.
  298. * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesWithAtLeastViewAccess() instead).
  299. *
  300. * @return array for each site, an array of information (idsite, name, main_url, etc.)
  301. */
  302. public function getSitesWithViewAccess()
  303. {
  304. $sitesId = $this->getSitesIdWithViewAccess();
  305. return $this->getSitesFromIds($sitesId);
  306. }
  307. /**
  308. * Returns the list of websites with the 'view' or 'admin' access for the current user.
  309. * For the superUser it returns all the websites in the database.
  310. *
  311. * @param bool|int $limit Specify max number of sites to return
  312. * @param bool $_restrictSitesToLogin Hack necessary when runnning scheduled tasks, where "Super User" is forced, but sometimes not desired, see #3017
  313. * @return array array for each site, an array of information (idsite, name, main_url, etc.)
  314. */
  315. public function getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin = false)
  316. {
  317. $sitesId = $this->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin);
  318. return $this->getSitesFromIds($sitesId, $limit);
  319. }
  320. /**
  321. * Returns the list of websites ID with the 'admin' access for the current user.
  322. * For the superUser it returns all the websites in the database.
  323. *
  324. * @return array list of websites ID
  325. */
  326. public function getSitesIdWithAdminAccess()
  327. {
  328. $sitesId = Access::getInstance()->getSitesIdWithAdminAccess();
  329. return $sitesId;
  330. }
  331. /**
  332. * Returns the list of websites ID with the 'view' access for the current user.
  333. * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesIdWithAtLeastViewAccess() instead).
  334. *
  335. * @return array list of websites ID
  336. */
  337. public function getSitesIdWithViewAccess()
  338. {
  339. return Access::getInstance()->getSitesIdWithViewAccess();
  340. }
  341. /**
  342. * Returns the list of websites ID with the 'view' or 'admin' access for the current user.
  343. * For the superUser it returns all the websites in the database.
  344. *
  345. * @param bool $_restrictSitesToLogin
  346. * @return array list of websites ID
  347. */
  348. public function getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin = false)
  349. {
  350. if (Piwik::hasUserSuperUserAccess() && !TaskScheduler::isTaskBeingExecuted()) {
  351. return Access::getInstance()->getSitesIdWithAtLeastViewAccess();
  352. }
  353. if (!empty($_restrictSitesToLogin)
  354. // Only Super User or logged in user can see viewable sites for a specific login,
  355. // but during scheduled task execution, we sometimes want to restrict sites to
  356. // a different login than the superuser.
  357. && (Piwik::hasUserSuperUserAccessOrIsTheUser($_restrictSitesToLogin)
  358. || TaskScheduler::isTaskBeingExecuted())
  359. ) {
  360. if (Piwik::hasTheUserSuperUserAccess($_restrictSitesToLogin)) {
  361. return Access::getInstance()->getSitesIdWithAtLeastViewAccess();
  362. }
  363. $accessRaw = Access::getInstance()->getRawSitesWithSomeViewAccess($_restrictSitesToLogin);
  364. $sitesId = array();
  365. foreach ($accessRaw as $access) {
  366. $sitesId[] = $access['idsite'];
  367. }
  368. return $sitesId;
  369. } else {
  370. return Access::getInstance()->getSitesIdWithAtLeastViewAccess();
  371. }
  372. }
  373. /**
  374. * Returns the list of websites from the ID array in parameters.
  375. * The user access is not checked in this method so the ID have to be accessible by the user!
  376. *
  377. * @param array $idSites list of website ID
  378. * @param bool $limit
  379. * @return array
  380. */
  381. private function getSitesFromIds($idSites, $limit = false)
  382. {
  383. if (count($idSites) === 0) {
  384. return array();
  385. }
  386. if ($limit) {
  387. $limit = "LIMIT " . (int)$limit;
  388. }
  389. $db = Db::get();
  390. $sites = $db->fetchAll("SELECT *
  391. FROM " . Common::prefixTable("site") . "
  392. WHERE idsite IN (" . implode(", ", $idSites) . ")
  393. ORDER BY idsite ASC $limit");
  394. Site::setSitesFromArray($sites);
  395. return $sites;
  396. }
  397. protected function getNormalizedUrls($url)
  398. {
  399. if (strpos($url, 'www.') !== false) {
  400. $urlBis = str_replace('www.', '', $url);
  401. } else {
  402. $urlBis = str_replace('://', '://www.', $url);
  403. }
  404. return array($url, $urlBis);
  405. }
  406. /**
  407. * Returns the list of websites ID associated with a URL.
  408. *
  409. * @param string $url
  410. * @return array list of websites ID
  411. */
  412. public function getSitesIdFromSiteUrl($url)
  413. {
  414. $url = $this->removeTrailingSlash($url);
  415. list($url, $urlBis) = $this->getNormalizedUrls($url);
  416. if (Piwik::hasUserSuperUserAccess()) {
  417. $ids = Db::get()->fetchAll(
  418. 'SELECT idsite
  419. FROM ' . Common::prefixTable('site') . '
  420. WHERE (main_url = ? OR main_url = ?) ' .
  421. 'UNION
  422. SELECT idsite
  423. FROM ' . Common::prefixTable('site_url') . '
  424. WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis));
  425. } else {
  426. $login = Piwik::getCurrentUserLogin();
  427. $ids = Db::get()->fetchAll(
  428. 'SELECT idsite
  429. FROM ' . Common::prefixTable('site') . '
  430. WHERE (main_url = ? OR main_url = ?)' .
  431. 'AND idsite IN (' . Access::getSqlAccessSite('idsite') . ') ' .
  432. 'UNION
  433. SELECT idsite
  434. FROM ' . Common::prefixTable('site_url') . '
  435. WHERE (url = ? OR url = ?)' .
  436. 'AND idsite IN (' . Access::getSqlAccessSite('idsite') . ')',
  437. array($url, $urlBis, $login, $url, $urlBis, $login));
  438. }
  439. return $ids;
  440. }
  441. /**
  442. * Returns all websites with a timezone matching one the specified timezones
  443. *
  444. * @param array $timezones
  445. * @return array
  446. * @ignore
  447. */
  448. public function getSitesIdFromTimezones($timezones)
  449. {
  450. Piwik::checkUserHasSuperUserAccess();
  451. $timezones = Piwik::getArrayFromApiParameter($timezones);
  452. $timezones = array_unique($timezones);
  453. $ids = Db::get()->fetchAll(
  454. 'SELECT idsite
  455. FROM ' . Common::prefixTable('site') . '
  456. WHERE timezone IN (' . Common::getSqlStringFieldsArray($timezones) . ')
  457. ORDER BY idsite ASC',
  458. $timezones);
  459. $return = array();
  460. foreach ($ids as $id) {
  461. $return[] = $id['idsite'];
  462. }
  463. return $return;
  464. }
  465. /**
  466. * Add a website.
  467. * Requires Super User access.
  468. *
  469. * The website is defined by a name and an array of URLs.
  470. * @param string $siteName Site name
  471. * @param array|string $urls The URLs array must contain at least one URL called the 'main_url' ;
  472. * if several URLs are provided in the array, they will be recorded
  473. * as Alias URLs for this website.
  474. * @param int $ecommerce Is Ecommerce Reporting enabled for this website?
  475. * @param null $siteSearch
  476. * @param string $searchKeywordParameters Comma separated list of search keyword parameter names
  477. * @param string $searchCategoryParameters Comma separated list of search category parameter names
  478. * @param string $excludedIps Comma separated list of IPs to exclude from the reports (allows wildcards)
  479. * @param null $excludedQueryParameters
  480. * @param string $timezone Timezone string, eg. 'Europe/London'
  481. * @param string $currency Currency, eg. 'EUR'
  482. * @param string $group Website group identifier
  483. * @param string $startDate Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
  484. * @param null|string $excludedUserAgents
  485. * @param int $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they
  486. * will be removed. If 0, the default global behavior will be used.
  487. * @see getKeepURLFragmentsGlobal.
  488. * @param string $type The website type, defaults to "website" if not set.
  489. *
  490. * @return int the website ID created
  491. */
  492. public function addSite($siteName,
  493. $urls,
  494. $ecommerce = null,
  495. $siteSearch = null,
  496. $searchKeywordParameters = null,
  497. $searchCategoryParameters = null,
  498. $excludedIps = null,
  499. $excludedQueryParameters = null,
  500. $timezone = null,
  501. $currency = null,
  502. $group = null,
  503. $startDate = null,
  504. $excludedUserAgents = null,
  505. $keepURLFragments = null,
  506. $type = null)
  507. {
  508. Piwik::checkUserHasSuperUserAccess();
  509. $this->checkName($siteName);
  510. $urls = $this->cleanParameterUrls($urls);
  511. $this->checkUrls($urls);
  512. $this->checkAtLeastOneUrl($urls);
  513. $siteSearch = $this->checkSiteSearch($siteSearch);
  514. list($searchKeywordParameters, $searchCategoryParameters) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters);
  515. $keepURLFragments = (int)$keepURLFragments;
  516. self::checkKeepURLFragmentsValue($keepURLFragments);
  517. $timezone = trim($timezone);
  518. if (empty($timezone)) {
  519. $timezone = $this->getDefaultTimezone();
  520. }
  521. $this->checkValidTimezone($timezone);
  522. if (empty($currency)) {
  523. $currency = $this->getDefaultCurrency();
  524. }
  525. $this->checkValidCurrency($currency);
  526. $db = Db::get();
  527. $url = $urls[0];
  528. $urls = array_slice($urls, 1);
  529. $bind = array('name' => $siteName,
  530. 'main_url' => $url,
  531. );
  532. $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
  533. $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
  534. $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
  535. $bind['keep_url_fragment'] = $keepURLFragments;
  536. $bind['timezone'] = $timezone;
  537. $bind['currency'] = $currency;
  538. $bind['ecommerce'] = (int)$ecommerce;
  539. $bind['sitesearch'] = $siteSearch;
  540. $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
  541. $bind['sitesearch_category_parameters'] = $searchCategoryParameters;
  542. $bind['ts_created'] = !is_null($startDate)
  543. ? Date::factory($startDate)->getDatetime()
  544. : Date::now()->getDatetime();
  545. $bind['type'] = $this->checkAndReturnType($type);
  546. if (!empty($group)
  547. && Piwik::hasUserSuperUserAccess()
  548. ) {
  549. $bind['group'] = trim($group);
  550. } else {
  551. $bind['group'] = "";
  552. }
  553. $db->insert(Common::prefixTable("site"), $bind);
  554. $idSite = $db->lastInsertId();
  555. $this->insertSiteUrls($idSite, $urls);
  556. // we reload the access list which doesn't yet take in consideration this new website
  557. Access::getInstance()->reloadAccess();
  558. $this->postUpdateWebsite($idSite);
  559. /**
  560. * Triggered after a site has been added.
  561. *
  562. * @param int $idSite The ID of the site that was added.
  563. */
  564. Piwik::postEvent('SitesManager.addSite.end', array($idSite));
  565. return (int)$idSite;
  566. }
  567. private function postUpdateWebsite($idSite)
  568. {
  569. Site::clearCache();
  570. Cache::regenerateCacheWebsiteAttributes($idSite);
  571. }
  572. /**
  573. * Delete a website from the database, given its Id.
  574. *
  575. * Requires Super User access.
  576. *
  577. * @param int $idSite
  578. * @throws Exception
  579. */
  580. public function deleteSite($idSite)
  581. {
  582. Piwik::checkUserHasSuperUserAccess();
  583. $idSites = API::getInstance()->getSitesId();
  584. if (!in_array($idSite, $idSites)) {
  585. throw new Exception("website id = $idSite not found");
  586. }
  587. $nbSites = count($idSites);
  588. if ($nbSites == 1) {
  589. throw new Exception(Piwik::translate("SitesManager_ExceptionDeleteSite"));
  590. }
  591. $db = Db::get();
  592. $db->query("DELETE FROM " . Common::prefixTable("site") . "
  593. WHERE idsite = ?", $idSite);
  594. $db->query("DELETE FROM " . Common::prefixTable("site_url") . "
  595. WHERE idsite = ?", $idSite);
  596. $db->query("DELETE FROM " . Common::prefixTable("access") . "
  597. WHERE idsite = ?", $idSite);
  598. // we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data)
  599. Cache::deleteCacheWebsiteAttributes($idSite);
  600. /**
  601. * Triggered after a site has been deleted.
  602. *
  603. * Plugins can use this event to remove site specific values or settings, such as removing all
  604. * goals that belong to a specific website. If you store any data related to a website you
  605. * should clean up that information here.
  606. *
  607. * @param int $idSite The ID of the site being deleted.
  608. */
  609. Piwik::postEvent('SitesManager.deleteSite.end', array($idSite));
  610. }
  611. /**
  612. * Checks that the array has at least one element
  613. *
  614. * @param array $urls
  615. * @throws Exception
  616. */
  617. private function checkAtLeastOneUrl($urls)
  618. {
  619. if (!is_array($urls)
  620. || count($urls) == 0
  621. ) {
  622. throw new Exception(Piwik::translate("SitesManager_ExceptionNoUrl"));
  623. }
  624. }
  625. private function checkValidTimezone($timezone)
  626. {
  627. $timezones = $this->getTimezonesList();
  628. foreach (array_values($timezones) as $cities) {
  629. foreach ($cities as $timezoneId => $city) {
  630. if ($timezoneId == $timezone) {
  631. return true;
  632. }
  633. }
  634. }
  635. throw new Exception(Piwik::translate('SitesManager_ExceptionInvalidTimezone', array($timezone)));
  636. }
  637. private function checkValidCurrency($currency)
  638. {
  639. if (!in_array($currency, array_keys($this->getCurrencyList()))) {
  640. throw new Exception(Piwik::translate('SitesManager_ExceptionInvalidCurrency', array($currency, "USD, EUR, etc.")));
  641. }
  642. }
  643. private function checkAndReturnType($type)
  644. {
  645. if(empty($type)) {
  646. $type = Site::DEFAULT_SITE_TYPE;
  647. }
  648. if(!is_string($type)) {
  649. throw new Exception("Invalid website type $type");
  650. }
  651. return $type;
  652. }
  653. /**
  654. * Checks that the submitted IPs (comma separated list) are valid
  655. * Returns the cleaned up IPs
  656. *
  657. * @param string $excludedIps Comma separated list of IP addresses
  658. * @throws Exception
  659. * @return array of IPs
  660. */
  661. private function checkAndReturnExcludedIps($excludedIps)
  662. {
  663. if (empty($excludedIps)) {
  664. return '';
  665. }
  666. $ips = explode(',', $excludedIps);
  667. $ips = array_map('trim', $ips);
  668. $ips = array_filter($ips, 'strlen');
  669. foreach ($ips as $ip) {
  670. if (!$this->isValidIp($ip)) {
  671. throw new Exception(Piwik::translate('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5")));
  672. }
  673. }
  674. $ips = implode(',', $ips);
  675. return $ips;
  676. }
  677. /**
  678. * Add a list of alias Urls to the given idSite
  679. *
  680. * If some URLs given in parameter are already recorded as alias URLs for this website,
  681. * they won't be duplicated. The 'main_url' of the website won't be affected by this method.
  682. *
  683. * @param int $idSite
  684. * @param array|string $urls
  685. * @return int the number of inserted URLs
  686. */
  687. public function addSiteAliasUrls($idSite, $urls)
  688. {
  689. Piwik::checkUserHasAdminAccess($idSite);
  690. $urls = $this->cleanParameterUrls($urls);
  691. $this->checkUrls($urls);
  692. $urlsInit = $this->getSiteUrlsFromId($idSite);
  693. $toInsert = array_diff($urls, $urlsInit);
  694. $this->insertSiteUrls($idSite, $toInsert);
  695. $this->postUpdateWebsite($idSite);
  696. return count($toInsert);
  697. }
  698. /**
  699. * Set the list of alias Urls for the given idSite
  700. *
  701. * Completely overwrites the current list of URLs with the provided list.
  702. * The 'main_url' of the website won't be affected by this method.
  703. *
  704. * @return int the number of inserted URLs
  705. */
  706. public function setSiteAliasUrls($idSite, $urls = array())
  707. {
  708. Piwik::checkUserHasAdminAccess($idSite);
  709. $urls = $this->cleanParameterUrls($urls);
  710. $this->checkUrls($urls);
  711. $this->deleteSiteAliasUrls($idSite);
  712. $this->insertSiteUrls($idSite, $urls);
  713. $this->postUpdateWebsite($idSite);
  714. return count($urls);
  715. }
  716. /**
  717. * Get the start and end IP addresses for an IP address range
  718. *
  719. * @param string $ipRange IP address range in presentation format
  720. * @return array|false Array( low, high ) IP addresses in presentation format; or false if error
  721. */
  722. public function getIpsForRange($ipRange)
  723. {
  724. $range = IP::getIpsForRange($ipRange);
  725. if ($range === false) {
  726. return false;
  727. }
  728. return array(IP::N2P($range[0]), IP::N2P($range[1]));
  729. }
  730. /**
  731. * Sets IPs to be excluded from all websites. IPs can contain wildcards.
  732. * Will also apply to websites created in the future.
  733. *
  734. * @param string $excludedIps Comma separated list of IPs to exclude from being tracked (allows wildcards)
  735. * @return bool
  736. */
  737. public function setGlobalExcludedIps($excludedIps)
  738. {
  739. Piwik::checkUserHasSuperUserAccess();
  740. $excludedIps = $this->checkAndReturnExcludedIps($excludedIps);
  741. Option::set(self::OPTION_EXCLUDED_IPS_GLOBAL, $excludedIps);
  742. Cache::deleteTrackerCache();
  743. return true;
  744. }
  745. /**
  746. * Sets Site Search keyword/category parameter names, to be used on websites which have not specified these values
  747. * Expects Comma separated list of query params names
  748. *
  749. * @param string
  750. * @param string
  751. * @return bool
  752. */
  753. public function setGlobalSearchParameters($searchKeywordParameters, $searchCategoryParameters)
  754. {
  755. Piwik::checkUserHasSuperUserAccess();
  756. Option::set(self::OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL, $searchKeywordParameters);
  757. Option::set(self::OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL, $searchCategoryParameters);
  758. Cache::deleteTrackerCache();
  759. return true;
  760. }
  761. /**
  762. * @return string Comma separated list of URL parameters
  763. */
  764. public function getSearchKeywordParametersGlobal()
  765. {
  766. Piwik::checkUserHasSomeAdminAccess();
  767. $names = Option::get(self::OPTION_SEARCH_KEYWORD_QUERY_PARAMETERS_GLOBAL);
  768. if ($names === false) {
  769. $names = self::DEFAULT_SEARCH_KEYWORD_PARAMETERS;
  770. }
  771. if (empty($names)) {
  772. $names = '';
  773. }
  774. return $names;
  775. }
  776. /**
  777. * @return string Comma separated list of URL parameters
  778. */
  779. public function getSearchCategoryParametersGlobal()
  780. {
  781. Piwik::checkUserHasSomeAdminAccess();
  782. return Option::get(self::OPTION_SEARCH_CATEGORY_QUERY_PARAMETERS_GLOBAL);
  783. }
  784. /**
  785. * Returns the list of URL query parameters that are excluded from all websites
  786. *
  787. * @return string Comma separated list of URL parameters
  788. */
  789. public function getExcludedQueryParametersGlobal()
  790. {
  791. Piwik::checkUserHasSomeViewAccess();
  792. return Option::get(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL);
  793. }
  794. /**
  795. * Returns the list of user agent substrings to look for when excluding visits for
  796. * all websites. If a visitor's user agent string contains one of these substrings,
  797. * their visits will not be included.
  798. *
  799. * @return string Comma separated list of strings.
  800. */
  801. public function getExcludedUserAgentsGlobal()
  802. {
  803. Piwik::checkUserHasSomeAdminAccess();
  804. return Option::get(self::OPTION_EXCLUDED_USER_AGENTS_GLOBAL);
  805. }
  806. /**
  807. * Sets list of user agent substrings to look for when excluding visits. For more info,
  808. * @see getExcludedUserAgentsGlobal.
  809. *
  810. * @param string $excludedUserAgents Comma separated list of strings. Each element is trimmed,
  811. * and empty strings are removed.
  812. */
  813. public function setGlobalExcludedUserAgents($excludedUserAgents)
  814. {
  815. Piwik::checkUserHasSuperUserAccess();
  816. // update option
  817. $excludedUserAgents = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
  818. Option::set(self::OPTION_EXCLUDED_USER_AGENTS_GLOBAL, $excludedUserAgents);
  819. // make sure tracker cache will reflect change
  820. Cache::deleteTrackerCache();
  821. }
  822. /**
  823. * Returns true if site-specific user agent exclusion has been enabled. If it hasn't,
  824. * only the global user agent substrings (see @setGlobalExcludedUserAgents) will be used.
  825. *
  826. * @return bool
  827. */
  828. public function isSiteSpecificUserAgentExcludeEnabled()
  829. {
  830. Piwik::checkUserHasSomeAdminAccess();
  831. return (bool)Option::get(self::OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE);
  832. }
  833. /**
  834. * Sets whether it should be allowed to exclude different user agents for different
  835. * websites.
  836. *
  837. * @param bool $enabled
  838. */
  839. public function setSiteSpecificUserAgentExcludeEnabled($enabled)
  840. {
  841. Piwik::checkUserHasSuperUserAccess();
  842. // update option
  843. Option::set(self::OPTION_SITE_SPECIFIC_USER_AGENT_EXCLUDE_ENABLE, $enabled);
  844. // make sure tracker cache will reflect change
  845. Cache::deleteTrackerCache();
  846. }
  847. /**
  848. * Returns true if the default behavior is to keep URL fragments when tracking,
  849. * false if otherwise.
  850. *
  851. * @return bool
  852. */
  853. public function getKeepURLFragmentsGlobal()
  854. {
  855. Piwik::checkUserHasSomeViewAccess();
  856. return (bool)Option::get(self::OPTION_KEEP_URL_FRAGMENTS_GLOBAL);
  857. }
  858. /**
  859. * Sets whether the default behavior should be to keep URL fragments when
  860. * tracking or not.
  861. *
  862. * @param $enabled bool If true, the default behavior will be to keep URL
  863. * fragments when tracking. If false, the default
  864. * behavior will be to remove them.
  865. */
  866. public function setKeepURLFragmentsGlobal($enabled)
  867. {
  868. Piwik::checkUserHasSuperUserAccess();
  869. // update option
  870. Option::set(self::OPTION_KEEP_URL_FRAGMENTS_GLOBAL, $enabled);
  871. // make sure tracker cache will reflect change
  872. Cache::deleteTrackerCache();
  873. }
  874. /**
  875. * Sets list of URL query parameters to be excluded on all websites.
  876. * Will also apply to websites created in the future.
  877. *
  878. * @param string $excludedQueryParameters Comma separated list of URL query parameters to exclude from URLs
  879. * @return bool
  880. */
  881. public function setGlobalExcludedQueryParameters($excludedQueryParameters)
  882. {
  883. Piwik::checkUserHasSuperUserAccess();
  884. $excludedQueryParameters = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
  885. Option::set(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL, $excludedQueryParameters);
  886. Cache::deleteTrackerCache();
  887. return true;
  888. }
  889. /**
  890. * Returns the list of IPs that are excluded from all websites
  891. *
  892. * @return string Comma separated list of IPs
  893. */
  894. public function getExcludedIpsGlobal()
  895. {
  896. Piwik::checkUserHasSomeAdminAccess();
  897. return Option::get(self::OPTION_EXCLUDED_IPS_GLOBAL);
  898. }
  899. /**
  900. * Returns the default currency that will be set when creating a website through the API.
  901. *
  902. * @return string Currency ID eg. 'USD'
  903. */
  904. public function getDefaultCurrency()
  905. {
  906. Piwik::checkUserHasSomeAdminAccess();
  907. $defaultCurrency = Option::get(self::OPTION_DEFAULT_CURRENCY);
  908. if ($defaultCurrency) {
  909. return $defaultCurrency;
  910. }
  911. return 'USD';
  912. }
  913. /**
  914. * Sets the default currency that will be used when creating websites
  915. *
  916. * @param string $defaultCurrency Currency code, eg. 'USD'
  917. * @return bool
  918. */
  919. public function setDefaultCurrency($defaultCurrency)
  920. {
  921. Piwik::checkUserHasSuperUserAccess();
  922. $this->checkValidCurrency($defaultCurrency);
  923. Option::set(self::OPTION_DEFAULT_CURRENCY, $defaultCurrency);
  924. return true;
  925. }
  926. /**
  927. * Returns the default timezone that will be set when creating a website through the API.
  928. * Via the UI, if the default timezone is not UTC, it will be pre-selected in the drop down
  929. *
  930. * @return string Timezone eg. UTC+7 or Europe/Paris
  931. */
  932. public function getDefaultTimezone()
  933. {
  934. $defaultTimezone = Option::get(self::OPTION_DEFAULT_TIMEZONE);
  935. if ($defaultTimezone) {
  936. return $defaultTimezone;
  937. }
  938. return 'UTC';
  939. }
  940. /**
  941. * Sets the default timezone that will be used when creating websites
  942. *
  943. * @param string $defaultTimezone Timezone string eg. Europe/Paris or UTC+8
  944. * @return bool
  945. */
  946. public function setDefaultTimezone($defaultTimezone)
  947. {
  948. Piwik::checkUserHasSuperUserAccess();
  949. $this->checkValidTimezone($defaultTimezone);
  950. Option::set(self::OPTION_DEFAULT_TIMEZONE, $defaultTimezone);
  951. return true;
  952. }
  953. /**
  954. * Update an existing website.
  955. * If only one URL is specified then only the main url will be updated.
  956. * If several URLs are specified, both the main URL and the alias URLs will be updated.
  957. *
  958. * @param int $idSite website ID defining the website to edit
  959. * @param string $siteName website name
  960. * @param string|array $urls the website URLs
  961. * @param int $ecommerce Whether Ecommerce is enabled, 0 or 1
  962. * @param null|int $siteSearch Whether site search is enabled, 0 or 1
  963. * @param string $searchKeywordParameters Comma separated list of search keyword parameter names
  964. * @param string $searchCategoryParameters Comma separated list of search category parameter names
  965. * @param string $excludedIps Comma separated list of IPs to exclude from being tracked (allows wildcards)
  966. * @param null|string $excludedQueryParameters
  967. * @param string $timezone Timezone
  968. * @param string $currency Currency code
  969. * @param string $group Group name where this website belongs
  970. * @param string $startDate Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
  971. * @param null|string $excludedUserAgents
  972. * @param int|null $keepURLFragments If 1, URL fragments will be kept when tracking. If 2, they
  973. * will be removed. If 0, the default global behavior will be used.
  974. * @param string $type The Website type, default value is "website"
  975. * @throws Exception
  976. * @see getKeepURLFragmentsGlobal. If null, the existing value will
  977. * not be modified.
  978. *
  979. * @return bool true on success
  980. */
  981. public function updateSite($idSite,
  982. $siteName = null,
  983. $urls = null,
  984. $ecommerce = null,
  985. $siteSearch = null,
  986. $searchKeywordParameters = null,
  987. $searchCategoryParameters = null,
  988. $excludedIps = null,
  989. $excludedQueryParameters = null,
  990. $timezone = null,
  991. $currency = null,
  992. $group = null,
  993. $startDate = null,
  994. $excludedUserAgents = null,
  995. $keepURLFragments = null,
  996. $type = null)
  997. {
  998. Piwik::checkUserHasAdminAccess($idSite);
  999. $idSites = API::getInstance()->getSitesId();
  1000. if (!in_array($idSite, $idSites)) {
  1001. throw new Exception("website id = $idSite not found");
  1002. }
  1003. // Build the SQL UPDATE based on specified updates to perform
  1004. $bind = array();
  1005. if (!is_null($siteName)) {
  1006. $this->checkName($siteName);
  1007. $bind['name'] = $siteName;
  1008. }
  1009. if (!is_null($urls)) {
  1010. $urls = $this->cleanParameterUrls($urls);
  1011. $this->checkUrls($urls);
  1012. $this->checkAtLeastOneUrl($urls);
  1013. $url = $urls[0];
  1014. $bind['main_url'] = $url;
  1015. }
  1016. if (!is_null($currency)) {
  1017. $currency = trim($currency);
  1018. $this->checkValidCurrency($currency);
  1019. $bind['currency'] = $currency;
  1020. }
  1021. if (!is_null($timezone)) {
  1022. $timezone = trim($timezone);
  1023. $this->checkValidTimezone($timezone);
  1024. $bind['timezone'] = $timezone;
  1025. }
  1026. if (!is_null($group)
  1027. && Piwik::hasUserSuperUserAccess()
  1028. ) {
  1029. $bind['group'] = trim($group);
  1030. }
  1031. if (!is_null($ecommerce)) {
  1032. $bind['ecommerce'] = (int)(bool)$ecommerce;
  1033. }
  1034. if (!is_null($startDate)) {
  1035. $bind['ts_created'] = Date::factory($startDate)->getDatetime();
  1036. }
  1037. $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
  1038. $bind['excluded_parameters'] = $this->checkAndReturnCommaSeparatedStringList($excludedQueryParameters);
  1039. $bind['excluded_user_agents'] = $this->checkAndReturnCommaSeparatedStringList($excludedUserAgents);
  1040. if (!is_null($keepURLFragments)) {
  1041. $keepURLFragments = (int)$keepURLFragments;
  1042. self::checkKeepURLFragmentsValue($keepURLFragments);
  1043. $bind['keep_url_fragment'] = $keepURLFragments;
  1044. }
  1045. $bind['sitesearch'] = $this->checkSiteSearch($siteSearch);
  1046. list($searchKeywordParameters, $searchCategoryParameters) = $this->checkSiteSearchParameters($searchKeywordParameters, $searchCategoryParameters);
  1047. $bind['sitesearch_keyword_parameters'] = $searchKeywordParameters;
  1048. $bind['sitesearch_category_parameters'] = $searchCategoryParameters;
  1049. $bind['type'] = $this->checkAndReturnType($type);
  1050. $db = Db::get();
  1051. $db->update(Common::prefixTable("site"),
  1052. $bind,
  1053. "idsite = $idSite"
  1054. );
  1055. // we now update the main + alias URLs
  1056. $this->deleteSiteAliasUrls($idSite);
  1057. if (count($urls) > 1) {
  1058. $this->addSiteAliasUrls($idSite, array_slice($urls, 1));
  1059. }
  1060. $this->postUpdateWebsite($idSite);
  1061. }
  1062. /**
  1063. * Updates the field ts_created for the specified websites.
  1064. *
  1065. * @param $idSites int Id Site to update ts_created
  1066. * @param $minDate Date to set as creation date. To play it safe it will substract one more day.
  1067. *
  1068. * @ignore
  1069. */
  1070. public function updateSiteCreatedTime($idSites, $minDate)
  1071. {
  1072. $idSites = Site::getIdSitesFromIdSitesString($idSites);
  1073. Piwik::checkUserHasAdminAccess($idSites);
  1074. // Update piwik_site.ts_created
  1075. $query = "UPDATE " . Common::prefixTable("site") .
  1076. " SET ts_created = ?" .
  1077. " WHERE idsite IN ( " . implode(",", $idSites) . " )
  1078. AND ts_created > ?";
  1079. $minDateSql = $minDate->subDay(1)->getDatetime();
  1080. $bind = array($minDateSql, $minDateSql);
  1081. Db::query($query, $bind);
  1082. }
  1083. private function checkAndReturnCommaSeparatedStringList($parameters)
  1084. {
  1085. $parameters = trim($parameters);
  1086. if (empty($parameters)) {
  1087. return '';
  1088. }
  1089. $parameters = explode(',', $parameters);
  1090. $parameters = array_map('trim', $parameters);
  1091. $parameters = array_filter($parameters, 'strlen');
  1092. $parameters = array_unique($parameters);
  1093. return implode(',', $parameters);
  1094. }
  1095. /**
  1096. * Returns the list of supported currencies
  1097. * @see getCurrencySymbols()
  1098. * @return array ( currencyId => currencyName)
  1099. */
  1100. public function getCurrencyList()
  1101. {
  1102. $currencies = MetricsFormatter::getCurrencyList();
  1103. return array_map(function ($a) {
  1104. return $a[1] . " (" . $a[0] . ")";
  1105. }, $currencies);
  1106. }
  1107. /**
  1108. * Returns the list of currency symbols
  1109. * @see getCurrencyList()
  1110. * @return array( currencyId => currencySymbol )
  1111. */
  1112. public function getCurrencySymbols()
  1113. {
  1114. $currencies = MetricsFormatter::getCurrencyList();
  1115. return array_map(function ($a) {
  1116. return $a[0];
  1117. }, $currencies);
  1118. }
  1119. /**
  1120. * Returns the list of timezones supported.
  1121. * Used for addSite and updateSite
  1122. *
  1123. * @return array of timezone strings
  1124. */
  1125. public function getTimezonesList()
  1126. {
  1127. if (!SettingsServer::isTimezoneSupportEnabled()) {
  1128. return array('UTC' => $this->getTimezonesListUTCOffsets());
  1129. }
  1130. $continents = array('Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
  1131. $timezones = timezone_identifiers_list();
  1132. $return = array();
  1133. foreach ($timezones as $timezone) {
  1134. // filter out timezones not recognized by strtotime()
  1135. // @see http://bugs.php.net/46111
  1136. $testDate = '2008-09-18 13:00:00 ' . $timezone;
  1137. if (!strtotime($testDate)) {
  1138. continue;
  1139. }
  1140. $timezoneExploded = explode('/', $timezone);
  1141. $continent = $timezoneExploded[0];
  1142. // only display timezones that are grouped by continent
  1143. if (!in_array($continent, $continents)) {
  1144. continue;
  1145. }
  1146. $city = $timezoneExploded[1];
  1147. if (!empty($timezoneExploded[2])) {
  1148. $city .= ' - ' . $timezoneExploded[2];
  1149. }
  1150. $city = str_replace('_', ' ', $city);
  1151. $return[$continent][$timezone] = $city;
  1152. }
  1153. foreach ($continents as $continent) {
  1154. if (!empty($return[$continent])) {
  1155. ksort($return[$continent]);
  1156. }
  1157. }
  1158. $return['UTC'] = $this->getTimezonesListUTCOffsets();
  1159. return $return;
  1160. }
  1161. private function getTimezonesListUTCOffsets()
  1162. {
  1163. // manually add the UTC offsets
  1164. $GmtOffsets = array(-12, -11.5, -11, -10.5, -10, -9.5, -9, -8.5, -8, -7.5, -7, -6.5, -6, -5.5, -5, -4.5, -4, -3.5, -3, -2.5, -2, -1.5, -1, -0.5,
  1165. 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 5.75, 6, 6.5, 7, 7.5, 8, 8.5, 8.75, 9, 9.5, 10, 10.5, 11, 11.5, 12, 12.75, 13, 13.75, 14);
  1166. $return = array();
  1167. foreach ($GmtOffsets as $offset) {
  1168. if ($offset > 0) {
  1169. $offset = '+' . $offset;
  1170. } elseif ($offset == 0) {
  1171. $offset = '';
  1172. }
  1173. $offset = 'UTC' . $offset;
  1174. $offsetName = str_replace(array('.25', '.5', '.75'), array(':15', ':30', ':45'), $offset);
  1175. $return[$offset] = $offsetName;
  1176. }
  1177. return $return;
  1178. }
  1179. /**
  1180. * Returns the list of unique timezones from all configured sites.
  1181. *
  1182. * @return array ( string )
  1183. */
  1184. public function getUniqueSiteTimezones()
  1185. {
  1186. Piwik::checkUserHasSuperUserAccess();
  1187. $results = Db::fetchAll("SELECT distinct timezone FROM " . Common::prefixTable('site'));
  1188. $timezones = array();
  1189. foreach ($results as $result) {
  1190. $timezones[] = $result['timezone'];
  1191. }
  1192. return $timezones;
  1193. }
  1194. /**
  1195. * Insert the list of alias URLs for the website.
  1196. * The URLs must not exist already for this website!
  1197. */
  1198. private function insertSiteUrls($idSite, $urls)
  1199. {
  1200. if (count($urls) != 0) {
  1201. $db = Db::get();
  1202. foreach ($urls as $url) {
  1203. try {
  1204. $db->insert(Common::prefixTable("site_url"), array(
  1205. 'idsite' => $idSite,
  1206. 'url' => $url
  1207. )
  1208. );
  1209. } catch(Exception $e) {
  1210. // See bug #4149
  1211. }
  1212. }
  1213. }
  1214. }
  1215. /**
  1216. * Delete all the alias URLs for the given idSite.
  1217. */
  1218. private function deleteSiteAliasUrls($idsite)
  1219. {
  1220. $db = Db::get();
  1221. $db->query("DELETE FROM " . Common::prefixTable("site_url") . "
  1222. WHERE idsite = ?", $idsite);
  1223. }
  1224. /**
  1225. * Remove the final slash in the URLs if found
  1226. *
  1227. * @param string $url
  1228. * @return string the URL without the trailing slash
  1229. */
  1230. private function removeTrailingSlash($url)
  1231. {
  1232. // if there is a final slash, we take the URL w…

Large files files are truncated, but you can click here to view the full file