PageRenderTime 49ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/SitesManager/API.php

https://github.com/quarkness/piwik
PHP | 1115 lines | 679 code | 108 blank | 328 comment | 54 complexity | 1f257bdf0e5949ced7b7663956f1d9f3 MD5 | raw file
  1. <?php
  2. /**
  3. * Piwik - Open source web analytics
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. * @version $Id$
  8. *
  9. * @category Piwik_Plugins
  10. * @package Piwik_SitesManager
  11. */
  12. /**
  13. * 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.
  14. *
  15. * This API lets you create websites via "addSite", update existing websites via "updateSite" and delete websites via "deleteSite".
  16. * When creating websites, it can be useful to access internal codes used by Piwik for currencies via "getCurrencyList", or timezones via "getTimezonesList".
  17. *
  18. * There are also many ways to request a list of websites: from the website ID via "getSiteFromId" or the site URL via "getSitesIdFromSiteUrl".
  19. * Often, the most useful technique is to list all websites that are known to a current user, based on the token_auth, via
  20. * "getSitesWithAdminAccess", "getSitesWithViewAccess" or "getSitesWithAtLeastViewAccess" (which returns both).
  21. *
  22. * Some methods will affect all websites globally: "setGlobalExcludedIps" will set the list of IPs to be excluded on all websites,
  23. * "setGlobalExcludedQueryParameters" will set the list of URL parameters to remove from URLs for all websites.
  24. * The existing values can be fetched via "getExcludedIpsGlobal" and "getExcludedQueryParametersGlobal".
  25. * See also the documentation about <a href='http://piwik.org/docs/manage-websites/' target='_blank'>Managing Websites</a> in Piwik.
  26. * @package Piwik_SitesManager
  27. */
  28. class Piwik_SitesManager_API
  29. {
  30. static private $instance = null;
  31. /**
  32. * @return Piwik_SitesManager_API
  33. */
  34. static public function getInstance()
  35. {
  36. if (self::$instance == null)
  37. {
  38. self::$instance = new self;
  39. }
  40. return self::$instance;
  41. }
  42. const OPTION_EXCLUDED_IPS_GLOBAL = 'SitesManager_ExcludedIpsGlobal';
  43. const OPTION_DEFAULT_TIMEZONE = 'SitesManager_DefaultTimezone';
  44. const OPTION_DEFAULT_CURRENCY = 'SitesManager_DefaultCurrency';
  45. const OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL = 'SitesManager_ExcludedQueryParameters';
  46. /**
  47. * Returns the javascript tag for the given idSite.
  48. * This tag must be included on every page to be tracked by Piwik
  49. *
  50. * @param int $idSite
  51. * @param string $customTitle Custom title given to the pageview
  52. * @return string The Javascript tag ready to be included on the HTML pages
  53. */
  54. public function getJavascriptTag( $idSite, $piwikUrl = '')
  55. {
  56. Piwik::checkUserHasViewAccess($idSite);
  57. if(empty($piwikUrl))
  58. {
  59. $piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
  60. }
  61. $piwikUrl = Piwik_Common::sanitizeInputValues($piwikUrl);
  62. $htmlEncoded = Piwik::getJavascriptCode($idSite, $piwikUrl);
  63. $htmlEncoded = str_replace(array('<br>','<br />','<br/>'), '', $htmlEncoded);
  64. return $htmlEncoded;
  65. }
  66. /**
  67. * Returns all websites belonging to the specified group
  68. * @param string $group Group name
  69. */
  70. public function getSitesFromGroup($group)
  71. {
  72. Piwik::checkUserIsSuperUser();
  73. $group = trim($group);
  74. $sites = Zend_Registry::get('db')->fetchAll("SELECT *
  75. FROM ".Piwik_Common::prefixTable("site")."
  76. WHERE `group` = ?", $group);
  77. return $sites;
  78. }
  79. /**
  80. * Returns the list of website groups, including the empty group
  81. * if no group were specified for some websites
  82. *
  83. * @return array of group names strings
  84. */
  85. public function getSitesGroups()
  86. {
  87. Piwik::checkUserIsSuperUser();
  88. $groups = Zend_Registry::get('db')->fetchAll("SELECT DISTINCT `group` FROM ".Piwik_Common::prefixTable("site"));
  89. $cleanedGroups = array();
  90. foreach($groups as $group)
  91. {
  92. $cleanedGroups[] = $group['group'];
  93. }
  94. $cleanedGroups = array_map('trim', $cleanedGroups);
  95. return $cleanedGroups;
  96. }
  97. /**
  98. * Returns the website information : name, main_url
  99. *
  100. * @exception if the site ID doesn't exist or the user doesn't have access to it
  101. * @return array
  102. */
  103. public function getSiteFromId( $idSite )
  104. {
  105. Piwik::checkUserHasViewAccess( $idSite );
  106. $site = Zend_Registry::get('db')->fetchRow("SELECT *
  107. FROM ".Piwik_Common::prefixTable("site")."
  108. WHERE idsite = ?", $idSite);
  109. return $site;
  110. }
  111. /**
  112. * Returns the list of alias URLs registered for the given idSite.
  113. * The website ID must be valid when calling this method!
  114. *
  115. * @return array list of alias URLs
  116. */
  117. private function getAliasSiteUrlsFromId( $idsite )
  118. {
  119. $db = Zend_Registry::get('db');
  120. $result = $db->fetchAll("SELECT url
  121. FROM ".Piwik_Common::prefixTable("site_url"). "
  122. WHERE idsite = ?", $idsite);
  123. $urls = array();
  124. foreach($result as $url)
  125. {
  126. $urls[] = $url['url'];
  127. }
  128. return $urls;
  129. }
  130. /**
  131. * Returns the list of all URLs registered for the given idSite (main_url + alias URLs).
  132. *
  133. * @exception if the website ID doesn't exist or the user doesn't have access to it
  134. * @return array list of URLs
  135. */
  136. public function getSiteUrlsFromId( $idSite )
  137. {
  138. Piwik::checkUserHasViewAccess($idSite);
  139. $site = new Piwik_Site($idSite);
  140. $urls = $this->getAliasSiteUrlsFromId($idSite);
  141. return array_merge(array($site->getMainUrl()), $urls);
  142. }
  143. /**
  144. * Returns the list of all the website IDs registered.
  145. * Caller must check access.
  146. *
  147. * @return array The list of website IDs
  148. */
  149. private function getSitesId()
  150. {
  151. $result = Piwik_FetchAll("SELECT idsite FROM ".Piwik_Common::prefixTable('site'));
  152. $idSites = array();
  153. foreach($result as $idSite)
  154. {
  155. $idSites[] = $idSite['idsite'];
  156. }
  157. return $idSites;
  158. }
  159. /**
  160. * Returns all websites, requires Super User access
  161. *
  162. * @return array The list of websites, indexed by idsite
  163. */
  164. public function getAllSites()
  165. {
  166. Piwik::checkUserIsSuperUser();
  167. $sites = Zend_Registry::get('db')->fetchAll("SELECT * FROM ".Piwik_Common::prefixTable("site"));
  168. $return = array();
  169. foreach($sites as $site)
  170. {
  171. $return[$site['idsite']] = $site;
  172. }
  173. return $return;
  174. }
  175. /**
  176. * Returns the list of all the website IDs registered.
  177. * Requires super user access.
  178. *
  179. * @return array The list of website IDs
  180. */
  181. public function getAllSitesId()
  182. {
  183. Piwik::checkUserIsSuperUser();
  184. return Piwik_SitesManager_API::getInstance()->getSitesId();
  185. }
  186. /**
  187. * Returns the list of the website IDs that received some visits since the specified timestamp.
  188. * Requires super user access.
  189. *
  190. * @return array The list of website IDs
  191. */
  192. public function getSitesIdWithVisits($timestamp = false)
  193. {
  194. Piwik::checkUserIsSuperUser();
  195. if(empty($timestamp)) $timestamp = time();
  196. $time = Piwik_Date::factory((int)$timestamp)->getDatetime();
  197. $result = Piwik_FetchAll("
  198. SELECT
  199. idsite
  200. FROM
  201. ".Piwik_Common::prefixTable('site')." s
  202. WHERE EXISTS (
  203. SELECT 1
  204. FROM ".Piwik_Common::prefixTable('log_visit'). " v
  205. WHERE v.idsite = s.idsite
  206. AND visit_last_action_time > ?
  207. AND visit_last_action_time <= ?)
  208. ", array($time, $now = Piwik_Date::now()->addHour(1)->getDatetime()));
  209. $idSites = array();
  210. foreach($result as $idSite)
  211. {
  212. $idSites[] = $idSite['idsite'];
  213. }
  214. return $idSites;
  215. }
  216. /**
  217. * Returns the list of websites with the 'admin' access for the current user.
  218. * For the superUser it returns all the websites in the database.
  219. *
  220. * @return array for each site, an array of information (idsite, name, main_url, etc.)
  221. */
  222. public function getSitesWithAdminAccess()
  223. {
  224. $sitesId = $this->getSitesIdWithAdminAccess();
  225. return $this->getSitesFromIds($sitesId);
  226. }
  227. /**
  228. * Returns the list of websites with the 'view' access for the current user.
  229. * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesWithAtLeastViewAccess() instead).
  230. *
  231. * @return array for each site, an array of information (idsite, name, main_url, etc.)
  232. */
  233. public function getSitesWithViewAccess()
  234. {
  235. $sitesId = $this->getSitesIdWithViewAccess();
  236. return $this->getSitesFromIds($sitesId);
  237. }
  238. /**
  239. * Returns the list of websites with the 'view' or 'admin' access for the current user.
  240. * For the superUser it returns all the websites in the database.
  241. *
  242. * @return array array for each site, an array of information (idsite, name, main_url, etc.)
  243. */
  244. public function getSitesWithAtLeastViewAccess($limit = false)
  245. {
  246. $sitesId = $this->getSitesIdWithAtLeastViewAccess();
  247. return $this->getSitesFromIds($sitesId, $limit);
  248. }
  249. /**
  250. * Returns the list of websites ID with the 'admin' access for the current user.
  251. * For the superUser it returns all the websites in the database.
  252. *
  253. * @return array list of websites ID
  254. */
  255. public function getSitesIdWithAdminAccess()
  256. {
  257. $sitesId = Zend_Registry::get('access')->getSitesIdWithAdminAccess();
  258. return $sitesId;
  259. }
  260. /**
  261. * Returns the list of websites ID with the 'view' access for the current user.
  262. * For the superUser it doesn't return any result because the superUser has admin access on all the websites (use getSitesIdWithAtLeastViewAccess() instead).
  263. *
  264. * @return array list of websites ID
  265. */
  266. public function getSitesIdWithViewAccess()
  267. {
  268. return Zend_Registry::get('access')->getSitesIdWithViewAccess();
  269. }
  270. /**
  271. * Returns the list of websites ID with the 'view' or 'admin' access for the current user.
  272. * For the superUser it returns all the websites in the database.
  273. *
  274. * @return array list of websites ID
  275. */
  276. public function getSitesIdWithAtLeastViewAccess()
  277. {
  278. return Zend_Registry::get('access')->getSitesIdWithAtLeastViewAccess();
  279. }
  280. /**
  281. * Returns the list of websites from the ID array in parameters.
  282. * The user access is not checked in this method so the ID have to be accessible by the user!
  283. *
  284. * @param array list of website ID
  285. */
  286. private function getSitesFromIds( $idSites, $limit = false )
  287. {
  288. if(count($idSites) === 0)
  289. {
  290. return array();
  291. }
  292. if($limit)
  293. {
  294. $limit = "LIMIT " . (int)$limit;
  295. }
  296. $db = Zend_Registry::get('db');
  297. $sites = $db->fetchAll("SELECT *
  298. FROM ".Piwik_Common::prefixTable("site")."
  299. WHERE idsite IN (".implode(", ", $idSites).")
  300. ORDER BY idsite ASC $limit");
  301. return $sites;
  302. }
  303. protected function getNormalizedUrls($url)
  304. {
  305. if(strpos($url, 'www.') !== false)
  306. {
  307. $urlBis = str_replace('www.', '', $url);
  308. }
  309. else
  310. {
  311. $urlBis = str_replace('://', '://www.', $url);
  312. }
  313. return array($url, $urlBis);
  314. }
  315. /**
  316. * Returns the list of websites ID associated with a URL.
  317. *
  318. * @param string $url
  319. * @return array list of websites ID
  320. */
  321. public function getSitesIdFromSiteUrl( $url )
  322. {
  323. $url = $this->removeTrailingSlash($url);
  324. list($url, $urlBis) = $this->getNormalizedUrls($url);
  325. if(Piwik::isUserIsSuperUser())
  326. {
  327. $ids = Zend_Registry::get('db')->fetchAll(
  328. 'SELECT idsite
  329. FROM ' . Piwik_Common::prefixTable('site') . '
  330. WHERE (main_url = ? OR main_url = ?) ' .
  331. 'UNION
  332. SELECT idsite
  333. FROM ' . Piwik_Common::prefixTable('site_url') . '
  334. WHERE (url = ? OR url = ?) ', array($url, $urlBis, $url, $urlBis));
  335. }
  336. else
  337. {
  338. $login = Piwik::getCurrentUserLogin();
  339. $ids = Zend_Registry::get('db')->fetchAll(
  340. 'SELECT idsite
  341. FROM ' . Piwik_Common::prefixTable('site') . '
  342. WHERE (main_url = ? OR main_url = ?)' .
  343. 'AND idsite IN (' . Piwik_Access::getSqlAccessSite('idsite') . ') ' .
  344. 'UNION
  345. SELECT idsite
  346. FROM ' . Piwik_Common::prefixTable('site_url') . '
  347. WHERE (url = ? OR url = ?)' .
  348. 'AND idsite IN (' . Piwik_Access::getSqlAccessSite('idsite') . ')',
  349. array($url, $urlBis, $login, $url, $urlBis, $login));
  350. }
  351. return $ids;
  352. }
  353. /**
  354. * Add a website.
  355. * Requires Super User access.
  356. *
  357. * The website is defined by a name and an array of URLs.
  358. * @param string Site name
  359. * @param array|string The URLs array must contain at least one URL called the 'main_url' ;
  360. * if several URLs are provided in the array, they will be recorded
  361. * as Alias URLs for this website.
  362. * @param int Is Ecommerce Reporting enabled for this website?
  363. * @param string Comma separated list of IPs to exclude from the reports (allows wildcards)
  364. * @param string Timezone string, eg. 'Europe/London'
  365. * @param string Currency, eg. 'EUR'
  366. * @param string Website group identifier
  367. * @param string Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
  368. *
  369. *
  370. * @return int the website ID created
  371. */
  372. public function addSite( $siteName, $urls, $ecommerce = null, $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null, $startDate = null )
  373. {
  374. Piwik::checkUserIsSuperUser();
  375. $this->checkName($siteName);
  376. $urls = $this->cleanParameterUrls($urls);
  377. $this->checkUrls($urls);
  378. $this->checkAtLeastOneUrl($urls);
  379. $timezone = trim($timezone);
  380. if(empty($timezone))
  381. {
  382. $timezone = $this->getDefaultTimezone();
  383. }
  384. $this->checkValidTimezone($timezone);
  385. if(empty($currency))
  386. {
  387. $currency = $this->getDefaultCurrency();
  388. }
  389. $this->checkValidCurrency($currency);
  390. $db = Zend_Registry::get('db');
  391. $url = $urls[0];
  392. $urls = array_slice($urls, 1);
  393. $bind = array( 'name' => $siteName,
  394. 'main_url' => $url,
  395. );
  396. $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
  397. $bind['excluded_parameters'] = $this->checkAndReturnExcludedQueryParameters($excludedQueryParameters);
  398. $bind['timezone'] = $timezone;
  399. $bind['currency'] = $currency;
  400. $bind['ecommerce'] = (int)$ecommerce;
  401. $bind['ts_created'] = !is_null($startDate)
  402. ? Piwik_Date::factory($startDate)->getDatetime()
  403. : Piwik_Date::now()->getDatetime();
  404. if(!empty($group)
  405. && Piwik::isUserIsSuperUser())
  406. {
  407. $bind['group'] = trim($group);
  408. }
  409. else
  410. {
  411. $bind['group'] = "";
  412. }
  413. $db->insert(Piwik_Common::prefixTable("site"), $bind);
  414. $idSite = $db->lastInsertId();
  415. $this->insertSiteUrls($idSite, $urls);
  416. // we reload the access list which doesn't yet take in consideration this new website
  417. Zend_Registry::get('access')->reloadAccess();
  418. $this->postUpdateWebsite($idSite);
  419. return (int)$idSite;
  420. }
  421. private function postUpdateWebsite($idSite)
  422. {
  423. Piwik_Site::clearCache();
  424. Piwik_Common::regenerateCacheWebsiteAttributes($idSite);
  425. }
  426. /**
  427. * Delete a website from the database, given its Id.
  428. *
  429. * Requires Super User access.
  430. *
  431. * @param int $idSite
  432. */
  433. public function deleteSite( $idSite )
  434. {
  435. Piwik::checkUserIsSuperUser();
  436. $idSites = Piwik_SitesManager_API::getInstance()->getSitesId();
  437. if(!in_array($idSite, $idSites))
  438. {
  439. throw new Exception("website id = $idSite not found");
  440. }
  441. $nbSites = count($idSites);
  442. if($nbSites == 1)
  443. {
  444. throw new Exception(Piwik_TranslateException("SitesManager_ExceptionDeleteSite"));
  445. }
  446. $db = Zend_Registry::get('db');
  447. $db->query("DELETE FROM ".Piwik_Common::prefixTable("site")."
  448. WHERE idsite = ?", $idSite);
  449. $db->query("DELETE FROM ".Piwik_Common::prefixTable("site_url")."
  450. WHERE idsite = ?", $idSite);
  451. $db->query("DELETE FROM ".Piwik_Common::prefixTable("access")."
  452. WHERE idsite = ?", $idSite);
  453. Piwik_Common::deleteCacheWebsiteAttributes($idSite);
  454. Piwik_PostEvent('SitesManager.deleteSite', $idSite);
  455. }
  456. /**
  457. * Checks that the array has at least one element
  458. *
  459. * @exception if the parameter is not an array or if array empty
  460. */
  461. private function checkAtLeastOneUrl( $urls )
  462. {
  463. if(!is_array($urls)
  464. || count($urls) == 0)
  465. {
  466. throw new Exception(Piwik_TranslateException("SitesManager_ExceptionNoUrl"));
  467. }
  468. }
  469. private function checkValidTimezone($timezone)
  470. {
  471. $timezones = $this->getTimezonesList();
  472. foreach($timezones as $continent => $cities)
  473. {
  474. foreach($cities as $timezoneId => $city)
  475. {
  476. if($timezoneId == $timezone)
  477. {
  478. return true;
  479. }
  480. }
  481. }
  482. throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidTimezone', array($timezone)));
  483. }
  484. private function checkValidCurrency($currency)
  485. {
  486. if(!in_array($currency, array_keys($this->getCurrencyList())))
  487. {
  488. throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidCurrency', array($currency, "USD, EUR, etc.")));
  489. }
  490. }
  491. /**
  492. * Checks that the submitted IPs (comma separated list) are valid
  493. * Returns the cleaned up IPs
  494. *
  495. * @param string $excludedIps Comma separated list of IP addresses
  496. * @return array of IPs
  497. */
  498. private function checkAndReturnExcludedIps($excludedIps)
  499. {
  500. if(empty($excludedIps))
  501. {
  502. return '';
  503. }
  504. $ips = explode(',', $excludedIps);
  505. $ips = array_map('trim', $ips);
  506. $ips = array_filter($ips, 'strlen');
  507. foreach($ips as $ip)
  508. {
  509. if(!$this->isValidIp($ip))
  510. {
  511. throw new Exception(Piwik_TranslateException('SitesManager_ExceptionInvalidIPFormat', array($ip, "1.2.3.4, 1.2.3.*, or 1.2.3.4/5")));
  512. }
  513. }
  514. $ips = implode(',', $ips);
  515. return $ips;
  516. }
  517. /**
  518. * Add a list of alias Urls to the given idSite
  519. *
  520. * If some URLs given in parameter are already recorded as alias URLs for this website,
  521. * they won't be duplicated. The 'main_url' of the website won't be affected by this method.
  522. *
  523. * @return int the number of inserted URLs
  524. */
  525. public function addSiteAliasUrls( $idSite, $urls)
  526. {
  527. Piwik::checkUserHasAdminAccess( $idSite );
  528. $urls = $this->cleanParameterUrls($urls);
  529. $this->checkUrls($urls);
  530. $urlsInit = $this->getSiteUrlsFromId($idSite);
  531. $toInsert = array_diff($urls, $urlsInit);
  532. $this->insertSiteUrls($idSite, $toInsert);
  533. $this->postUpdateWebsite($idSite);
  534. return count($toInsert);
  535. }
  536. /**
  537. * Get the start and end IP addresses for an IP address range
  538. *
  539. * @param string $ipRange IP address range in presentation format
  540. * @return array|false Array( low, high ) IP addresses in presentation format; or false if error
  541. */
  542. public function getIpsForRange($ipRange)
  543. {
  544. $range = Piwik_IP::getIpsForRange($ipRange);
  545. if($range === false)
  546. {
  547. return false;
  548. }
  549. return array( Piwik_IP::N2P($range[0]), Piwik_IP::N2P($range[1]) );
  550. }
  551. /**
  552. * Sets IPs to be excluded from all websites. IPs can contain wildcards.
  553. * Will also apply to websites created in the future.
  554. *
  555. * @param string Comma separated list of IPs to exclude from being tracked (allows wildcards)
  556. * @return bool
  557. */
  558. public function setGlobalExcludedIps($excludedIps)
  559. {
  560. Piwik::checkUserIsSuperUser();
  561. $excludedIps = $this->checkAndReturnExcludedIps($excludedIps);
  562. Piwik_SetOption(self::OPTION_EXCLUDED_IPS_GLOBAL, $excludedIps);
  563. Piwik_Common::deleteTrackerCache();
  564. return true;
  565. }
  566. /**
  567. * Returns the list of URL query parameters that are excluded from all websites
  568. *
  569. * @return string Comma separated list of URL parameters
  570. */
  571. public function getExcludedQueryParametersGlobal()
  572. {
  573. Piwik::checkUserHasSomeAdminAccess();
  574. return Piwik_GetOption(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL);
  575. }
  576. /**
  577. * Sets list of URL query parameters to be excluded on all websites.
  578. * Will also apply to websites created in the future.
  579. *
  580. * @param string Comma separated list of URL query parameters to exclude from URLs
  581. * @return bool
  582. */
  583. public function setGlobalExcludedQueryParameters($excludedQueryParameters)
  584. {
  585. Piwik::checkUserIsSuperUser();
  586. $excludedQueryParameters = $this->checkAndReturnExcludedQueryParameters($excludedQueryParameters);
  587. Piwik_SetOption(self::OPTION_EXCLUDED_QUERY_PARAMETERS_GLOBAL, $excludedQueryParameters);
  588. Piwik_Common::deleteTrackerCache();
  589. return true;
  590. }
  591. /**
  592. * Returns the list of IPs that are excluded from all websites
  593. *
  594. * @return string Comma separated list of IPs
  595. */
  596. public function getExcludedIpsGlobal()
  597. {
  598. Piwik::checkUserHasSomeAdminAccess();
  599. return Piwik_GetOption(self::OPTION_EXCLUDED_IPS_GLOBAL);
  600. }
  601. /**
  602. * Returns the default currency that will be set when creating a website through the API.
  603. *
  604. * @return string Currency ID eg. 'USD'
  605. */
  606. public function getDefaultCurrency()
  607. {
  608. Piwik::checkUserHasSomeAdminAccess();
  609. $defaultCurrency = Piwik_GetOption(self::OPTION_DEFAULT_CURRENCY);
  610. if($defaultCurrency)
  611. {
  612. return $defaultCurrency;
  613. }
  614. return 'USD';
  615. }
  616. /**
  617. * Sets the default currency that will be used when creating websites
  618. *
  619. * @param string $defaultCurrency Currency code, eg. 'USD'
  620. * @return bool
  621. */
  622. public function setDefaultCurrency($defaultCurrency)
  623. {
  624. Piwik::checkUserIsSuperUser();
  625. $this->checkValidCurrency($defaultCurrency);
  626. Piwik_SetOption(self::OPTION_DEFAULT_CURRENCY, $defaultCurrency);
  627. return true;
  628. }
  629. /**
  630. * Returns the default timezone that will be set when creating a website through the API.
  631. * Via the UI, if the default timezone is not UTC, it will be pre-selected in the drop down
  632. *
  633. * @return string Timezone eg. UTC+7 or Europe/Paris
  634. */
  635. public function getDefaultTimezone()
  636. {
  637. $defaultTimezone = Piwik_GetOption(self::OPTION_DEFAULT_TIMEZONE);
  638. if($defaultTimezone)
  639. {
  640. return $defaultTimezone;
  641. }
  642. return 'UTC';
  643. }
  644. /**
  645. * Sets the default timezone that will be used when creating websites
  646. *
  647. * @param string $defaultTimezone Timezone string eg. Europe/Paris or UTC+8
  648. * @return bool
  649. */
  650. public function setDefaultTimezone($defaultTimezone)
  651. {
  652. Piwik::checkUserIsSuperUser();
  653. $this->checkValidTimezone($defaultTimezone);
  654. Piwik_SetOption(self::OPTION_DEFAULT_TIMEZONE, $defaultTimezone);
  655. return true;
  656. }
  657. /**
  658. * Update an existing website.
  659. * If only one URL is specified then only the main url will be updated.
  660. * If several URLs are specified, both the main URL and the alias URLs will be updated.
  661. *
  662. * @param int website ID defining the website to edit
  663. * @param string website name
  664. * @param string|array the website URLs
  665. * @param string Comma separated list of IPs to exclude from being tracked (allows wildcards)
  666. * @param string Timezone
  667. * @param string Currency code
  668. * @param string Group name where this website belongs
  669. * @param string Date at which the statistics for this website will start. Defaults to today's date in YYYY-MM-DD format
  670. *
  671. * @exception if any of the parameter is not correct
  672. *
  673. * @return bool true on success
  674. */
  675. public function updateSite( $idSite, $siteName, $urls = null, $ecommerce = null, $excludedIps = null, $excludedQueryParameters = null, $timezone = null, $currency = null, $group = null, $startDate = null)
  676. {
  677. Piwik::checkUserHasAdminAccess($idSite);
  678. $idSites = Piwik_SitesManager_API::getInstance()->getSitesId();
  679. if(!in_array($idSite, $idSites))
  680. {
  681. throw new Exception("website id = $idSite not found");
  682. }
  683. $this->checkName($siteName);
  684. // SQL fields to update
  685. $bind = array();
  686. if(!is_null($urls))
  687. {
  688. $urls = $this->cleanParameterUrls($urls);
  689. $this->checkUrls($urls);
  690. $this->checkAtLeastOneUrl($urls);
  691. $url = $urls[0];
  692. $bind['main_url'] = $url;
  693. }
  694. if(!is_null($currency))
  695. {
  696. $currency = trim($currency);
  697. $this->checkValidCurrency($currency);
  698. $bind['currency'] = $currency;
  699. }
  700. if(!is_null($timezone))
  701. {
  702. $timezone = trim($timezone);
  703. $this->checkValidTimezone($timezone);
  704. $bind['timezone'] = $timezone;
  705. }
  706. if(!is_null($group)
  707. && Piwik::isUserIsSuperUser())
  708. {
  709. $bind['group'] = trim($group);
  710. }
  711. if(!is_null($ecommerce))
  712. {
  713. $bind['ecommerce'] = (int)(bool)$ecommerce;
  714. }
  715. if(!is_null($startDate))
  716. {
  717. $bind['ts_created'] = Piwik_Date::factory($startDate)->getDatetime();
  718. }
  719. $bind['excluded_ips'] = $this->checkAndReturnExcludedIps($excludedIps);
  720. $bind['excluded_parameters'] = $this->checkAndReturnExcludedQueryParameters($excludedQueryParameters);
  721. $bind['name'] = $siteName;
  722. $db = Zend_Registry::get('db');
  723. $db->update(Piwik_Common::prefixTable("site"),
  724. $bind,
  725. "idsite = $idSite"
  726. );
  727. // we now update the main + alias URLs
  728. $this->deleteSiteAliasUrls($idSite);
  729. if(count($urls) > 1)
  730. {
  731. $insertedUrls = $this->addSiteAliasUrls($idSite, array_slice($urls,1));
  732. }
  733. $this->postUpdateWebsite($idSite);
  734. }
  735. private function checkAndReturnExcludedQueryParameters($parameters)
  736. {
  737. $parameters = trim($parameters);
  738. if(empty($parameters))
  739. {
  740. return '';
  741. }
  742. $parameters = explode(',', $parameters);
  743. $parameters = array_map('trim', $parameters);
  744. $parameters = array_filter($parameters, 'strlen');
  745. $parameters = array_unique($parameters);
  746. return implode(',', $parameters);
  747. }
  748. /**
  749. * Returns the list of supported currencies
  750. * @see getCurrencySymbols()
  751. * @return array ( currencyId => currencyName)
  752. */
  753. public function getCurrencyList()
  754. {
  755. $currencies = Piwik::getCurrencyList();
  756. return array_map(create_function('$a', 'return $a[1]." (".$a[0].")";'), $currencies);
  757. }
  758. /**
  759. * Returns the list of currency symbols
  760. * @see getCurrencyList()
  761. * @return array( currencyId => currencySymbol )
  762. */
  763. public function getCurrencySymbols()
  764. {
  765. $currencies = Piwik::getCurrencyList();
  766. return array_map(create_function('$a', 'return $a[0];'), $currencies);
  767. }
  768. /**
  769. * Returns the list of timezones supported.
  770. * Used for addSite and updateSite
  771. *
  772. * @TODO NOT COMPATIBLE WITH API RESPONSE AUTO BUILDER
  773. *
  774. * @return array of timezone strings
  775. */
  776. public function getTimezonesList()
  777. {
  778. if(!Piwik::isTimezoneSupportEnabled())
  779. {
  780. return array('UTC' => $this->getTimezonesListUTCOffsets());
  781. }
  782. $continents = array( 'Africa', 'America', 'Antarctica', 'Arctic', 'Asia', 'Atlantic', 'Australia', 'Europe', 'Indian', 'Pacific');
  783. $timezones = timezone_identifiers_list();
  784. $return = array();
  785. foreach($timezones as $timezone)
  786. {
  787. // filter out timezones not recognized by strtotime()
  788. // @see http://bugs.php.net/46111
  789. $testDate = '2008-09-18 13:00:00 ' . $timezone;
  790. if(!strtotime($testDate))
  791. {
  792. continue;
  793. }
  794. $timezoneExploded = explode('/', $timezone);
  795. $continent = $timezoneExploded[0];
  796. // only display timezones that are grouped by continent
  797. if(!in_array($continent, $continents))
  798. {
  799. continue;
  800. }
  801. $city = $timezoneExploded[1];
  802. if(!empty($timezoneExploded[2]))
  803. {
  804. $city .= ' - '.$timezoneExploded[2];
  805. }
  806. $city = str_replace('_', ' ', $city);
  807. $return[$continent][$timezone] = $city;
  808. }
  809. foreach($continents as $continent)
  810. {
  811. if(!empty($return[$continent]))
  812. {
  813. ksort($return[$continent]);
  814. }
  815. }
  816. $return['UTC'] = $this->getTimezonesListUTCOffsets();
  817. return $return;
  818. }
  819. private function getTimezonesListUTCOffsets()
  820. {
  821. // manually add the UTC offsets
  822. $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,
  823. 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);
  824. $return = array();
  825. foreach($GmtOffsets as $offset)
  826. {
  827. if($offset > 0)
  828. {
  829. $offset = '+'.$offset;
  830. }
  831. elseif($offset == 0)
  832. {
  833. $offset = '';
  834. }
  835. $offset = 'UTC' . $offset;
  836. $offsetName = str_replace(array('.25','.5','.75'), array(':15',':30',':45'), $offset);
  837. $return[$offset] = $offsetName;
  838. }
  839. return $return;
  840. }
  841. /**
  842. * Returns the list of unique timezones from all configured sites.
  843. *
  844. * @return array ( string )
  845. */
  846. public function getUniqueSiteTimezones()
  847. {
  848. Piwik::checkUserIsSuperUser();
  849. $results = Piwik_FetchAll("SELECT distinct timezone FROM ".Piwik_Common::prefixTable('site'));
  850. $timezones = array();
  851. foreach($results as $result)
  852. {
  853. $timezones[] = $result['timezone'];
  854. }
  855. return $timezones;
  856. }
  857. /**
  858. * Insert the list of alias URLs for the website.
  859. * The URLs must not exist already for this website!
  860. */
  861. private function insertSiteUrls($idSite, $urls)
  862. {
  863. if(count($urls) != 0)
  864. {
  865. $db = Zend_Registry::get('db');
  866. foreach($urls as $url)
  867. {
  868. $db->insert(Piwik_Common::prefixTable("site_url"), array(
  869. 'idsite' => $idSite,
  870. 'url' => $url
  871. )
  872. );
  873. }
  874. }
  875. }
  876. /**
  877. * Delete all the alias URLs for the given idSite.
  878. */
  879. private function deleteSiteAliasUrls($idsite)
  880. {
  881. $db = Zend_Registry::get('db');
  882. $db->query("DELETE FROM ".Piwik_Common::prefixTable("site_url") ."
  883. WHERE idsite = ?", $idsite);
  884. }
  885. /**
  886. * Remove the final slash in the URLs if found
  887. *
  888. * @return string the URL without the trailing slash
  889. */
  890. private function removeTrailingSlash($url)
  891. {
  892. // if there is a final slash, we take the URL without this slash (expected URL format)
  893. if(strlen($url) > 5
  894. && $url[strlen($url)-1] == '/')
  895. {
  896. $url = substr($url,0,strlen($url)-1);
  897. }
  898. return $url;
  899. }
  900. /**
  901. * Tests if the URL is a valid URL
  902. *
  903. * @return bool
  904. */
  905. private function isValidUrl( $url )
  906. {
  907. return Piwik_Common::isLookLikeUrl($url);
  908. }
  909. /**
  910. * Tests if the IP is a valid IP, allowing wildcards, except in the first octet.
  911. * Wildcards can only be used from right to left, ie. 1.1.*.* is allowed, but 1.1.*.1 is not.
  912. *
  913. * @param string $ip IP address
  914. * @return bool
  915. */
  916. private function isValidIp( $ip )
  917. {
  918. return Piwik_IP::getIpsForRange($ip) !== false;
  919. }
  920. /**
  921. * Check that the website name has a correct format.
  922. *
  923. * @exception if the website name is empty
  924. */
  925. private function checkName($siteName)
  926. {
  927. if(empty($siteName))
  928. {
  929. throw new Exception(Piwik_TranslateException("SitesManager_ExceptionEmptyName"));
  930. }
  931. }
  932. /**
  933. * Check that the array of URLs are valid URLs
  934. *
  935. * @exception if any of the urls is not valid
  936. * @param array
  937. */
  938. private function checkUrls($urls)
  939. {
  940. foreach($urls as $url)
  941. {
  942. if(!$this->isValidUrl($url))
  943. {
  944. throw new Exception(sprintf(Piwik_TranslateException("SitesManager_ExceptionInvalidUrl"),$url));
  945. }
  946. }
  947. }
  948. /**
  949. * Clean the parameter URLs:
  950. * - if the parameter is a string make it an array
  951. * - remove the trailing slashes if found
  952. *
  953. * @param string|array urls
  954. * @return array the array of cleaned URLs
  955. */
  956. private function cleanParameterUrls( $urls )
  957. {
  958. if(!is_array($urls))
  959. {
  960. $urls = array($urls);
  961. }
  962. $urls = array_filter($urls);
  963. $urls = array_map('urldecode', $urls);
  964. foreach($urls as &$url)
  965. {
  966. $url = $this->removeTrailingSlash($url);
  967. if(strpos($url, 'http') !== 0)
  968. {
  969. $url = 'http://'.$url;
  970. }
  971. }
  972. $urls = array_unique($urls);
  973. return $urls;
  974. }
  975. public function getPatternMatchSites($pattern)
  976. {
  977. $ids = $this->getSitesIdWithAtLeastViewAccess();
  978. if(empty($ids))
  979. {
  980. return array();
  981. }
  982. $ids_str = '';
  983. foreach($ids as $id_num => $id_val)
  984. {
  985. $ids_str .= $id_val.' , ';
  986. }
  987. $ids_str .= $id_val;
  988. $db = Zend_Registry::get('db');
  989. $bind = array('%'.$pattern.'%', 'http%'.$pattern.'%');
  990. // Also match the idsite
  991. $where = '';
  992. if(is_numeric($pattern))
  993. {
  994. $bind[] = $pattern;
  995. $where = 'OR s.idsite = ?';
  996. }
  997. $sites = $db->fetchAll("SELECT idsite, name, main_url
  998. FROM ".Piwik_Common::prefixTable('site')." s
  999. WHERE ( s.name like ?
  1000. OR s.main_url like ?
  1001. $where )
  1002. AND idsite in ($ids_str)
  1003. LIMIT ".Piwik::getWebsitesCountToDisplay(),
  1004. $bind) ;
  1005. return $sites;
  1006. }
  1007. }