You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

396 lines
14 KiB

9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
12 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bart Visscher <bartv@thisnet.nl>
  6. * @author Brice Maron <brice@bmaron.net>
  7. * @author Christoph Wurst <christoph@owncloud.com>
  8. * @author Frank Karlitschek <frank@karlitschek.de>
  9. * @author Individual IT Services <info@individual-it.net>
  10. * @author Jakob Sack <mail@jakobsack.de>
  11. * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
  12. * @author Joas Schilling <coding@schilljs.com>
  13. * @author John Molakvoæ <skjnldsv@users.noreply.github.com>
  14. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  15. * @author Lukas Reschke <lukas@statuscode.ch>
  16. * @author Marin Treselj <marin@pixelipo.com>
  17. * @author Michael Letzgus <www@chronos.michael-letzgus.de>
  18. * @author Morris Jobke <hey@morrisjobke.de>
  19. * @author Robin Appelman <robin@icewind.nl>
  20. * @author Roeland Jago Douma <roeland@famdouma.nl>
  21. * @author Thomas Müller <thomas.mueller@tmit.eu>
  22. * @author Vincent Petry <pvince81@owncloud.com>
  23. *
  24. * @license AGPL-3.0
  25. *
  26. * This code is free software: you can redistribute it and/or modify
  27. * it under the terms of the GNU Affero General Public License, version 3,
  28. * as published by the Free Software Foundation.
  29. *
  30. * This program is distributed in the hope that it will be useful,
  31. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  32. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  33. * GNU Affero General Public License for more details.
  34. *
  35. * You should have received a copy of the GNU Affero General Public License, version 3,
  36. * along with this program. If not, see <http://www.gnu.org/licenses/>
  37. *
  38. */
  39. use OC\TemplateLayout;
  40. require_once __DIR__.'/template/functions.php';
  41. /**
  42. * This class provides the templates for ownCloud.
  43. */
  44. class OC_Template extends \OC\Template\Base {
  45. /** @var string */
  46. private $renderAs; // Create a full page?
  47. /** @var string */
  48. private $path; // The path to the template
  49. /** @var array */
  50. private $headers = array(); //custom headers
  51. /** @var string */
  52. protected $app; // app id
  53. protected static $initTemplateEngineFirstRun = true;
  54. /**
  55. * Constructor
  56. *
  57. * @param string $app app providing the template
  58. * @param string $name of the template file (without suffix)
  59. * @param string $renderAs If $renderAs is set, OC_Template will try to
  60. * produce a full page in the according layout. For
  61. * now, $renderAs can be set to "guest", "user" or
  62. * "admin".
  63. * @param bool $registerCall = true
  64. */
  65. public function __construct( $app, $name, $renderAs = "", $registerCall = true ) {
  66. // Read the selected theme from the config file
  67. self::initTemplateEngine($renderAs);
  68. $theme = OC_Util::getTheme();
  69. $requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : '';
  70. $parts = explode('/', $app); // fix translation when app is something like core/lostpassword
  71. $l10n = \OC::$server->getL10N($parts[0]);
  72. /** @var \OCP\Defaults $themeDefaults */
  73. $themeDefaults = \OC::$server->query(\OCP\Defaults::class);
  74. list($path, $template) = $this->findTemplate($theme, $app, $name);
  75. // Set the private data
  76. $this->renderAs = $renderAs;
  77. $this->path = $path;
  78. $this->app = $app;
  79. parent::__construct($template, $requestToken, $l10n, $themeDefaults);
  80. }
  81. /**
  82. * @param string $renderAs
  83. */
  84. public static function initTemplateEngine($renderAs) {
  85. if (self::$initTemplateEngineFirstRun){
  86. //apps that started before the template initialization can load their own scripts/styles
  87. //so to make sure this scripts/styles here are loaded first we use OC_Util::addScript() with $prepend=true
  88. //meaning the last script/style in this list will be loaded first
  89. if (\OC::$server->getSystemConfig()->getValue ('installed', false) && $renderAs !== 'error' && !\OCP\Util::needUpgrade()) {
  90. if (\OC::$server->getConfig ()->getAppValue ( 'core', 'backgroundjobs_mode', 'ajax' ) == 'ajax') {
  91. OC_Util::addScript ( 'backgroundjobs', null, true );
  92. }
  93. }
  94. OC_Util::addStyle('server', null, true);
  95. OC_Util::addStyle('jquery-ui-fixes',null,true);
  96. OC_Util::addVendorStyle('jquery-ui/themes/base/jquery-ui',null,true);
  97. OC_Util::addVendorStyle('select2/select2', null, true);
  98. OC_Util::addStyle('jquery.ocdialog');
  99. OC_Util::addTranslations("core", null, true);
  100. OC_Util::addStyle('search', 'results');
  101. OC_Util::addScript('search', 'search', true);
  102. OC_Util::addScript('search', 'searchprovider');
  103. OC_Util::addScript('merged-template-prepend', null, true);
  104. OC_Util::addScript('jquery-ui-fixes');
  105. OC_Util::addScript('files/fileinfo');
  106. OC_Util::addScript('files/client');
  107. OC_Util::addScript('contactsmenu');
  108. if (\OC::$server->getConfig()->getSystemValue('debug')) {
  109. // Add the stuff we need always
  110. // following logic will import all vendor libraries that are
  111. // specified in core/js/core.json
  112. $fileContent = file_get_contents(OC::$SERVERROOT . '/core/js/core.json');
  113. if($fileContent !== false) {
  114. $coreDependencies = json_decode($fileContent, true);
  115. foreach(array_reverse($coreDependencies['vendor']) as $vendorLibrary) {
  116. //remove trailing ".js" as addVendorScript will append it
  117. OC_Util::addVendorScript(
  118. substr($vendorLibrary, 0, -3),null,true);
  119. }
  120. } else {
  121. throw new \Exception('Cannot read core/js/core.json');
  122. }
  123. } else {
  124. // Import all (combined) default vendor libraries
  125. OC_Util::addVendorScript('core', null, true);
  126. }
  127. if (\OC::$server->getRequest()->isUserAgent([\OC\AppFramework\Http\Request::USER_AGENT_IE])) {
  128. // polyfill for btoa/atob for IE friends
  129. OC_Util::addVendorScript('base64/base64');
  130. // shim for the davclient.js library
  131. \OCP\Util::addScript('files/iedavclient');
  132. }
  133. self::$initTemplateEngineFirstRun = false;
  134. }
  135. }
  136. /**
  137. * find the template with the given name
  138. * @param string $name of the template file (without suffix)
  139. *
  140. * Will select the template file for the selected theme.
  141. * Checking all the possible locations.
  142. * @param string $theme
  143. * @param string $app
  144. * @return string[]
  145. */
  146. protected function findTemplate($theme, $app, $name) {
  147. // Check if it is a app template or not.
  148. if( $app !== '' ) {
  149. $dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app));
  150. } else {
  151. $dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT);
  152. }
  153. $locator = new \OC\Template\TemplateFileLocator( $dirs );
  154. $template = $locator->find($name);
  155. $path = $locator->getPath();
  156. return array($path, $template);
  157. }
  158. /**
  159. * Add a custom element to the header
  160. * @param string $tag tag name of the element
  161. * @param array $attributes array of attributes for the element
  162. * @param string $text the text content for the element. If $text is null then the
  163. * element will be written as empty element. So use "" to get a closing tag.
  164. */
  165. public function addHeader($tag, $attributes, $text=null) {
  166. $this->headers[]= array(
  167. 'tag' => $tag,
  168. 'attributes' => $attributes,
  169. 'text' => $text
  170. );
  171. }
  172. /**
  173. * Process the template
  174. * @return boolean|string
  175. *
  176. * This function process the template. If $this->renderAs is set, it
  177. * will produce a full page.
  178. */
  179. public function fetchPage($additionalParams = null) {
  180. $data = parent::fetchPage($additionalParams);
  181. if( $this->renderAs ) {
  182. $page = new TemplateLayout($this->renderAs, $this->app);
  183. if(is_array($additionalParams)) {
  184. foreach ($additionalParams as $key => $value) {
  185. $page->assign($key, $value);
  186. }
  187. }
  188. // Add custom headers
  189. $headers = '';
  190. foreach(OC_Util::$headers as $header) {
  191. $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']);
  192. if ( strcasecmp($header['tag'], 'script') === 0 && in_array('src', array_map('strtolower', array_keys($header['attributes']))) ) {
  193. $headers .= ' defer';
  194. }
  195. foreach($header['attributes'] as $name=>$value) {
  196. $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"';
  197. }
  198. if ($header['text'] !== null) {
  199. $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>';
  200. } else {
  201. $headers .= '/>';
  202. }
  203. }
  204. $page->assign('headers', $headers);
  205. $page->assign('content', $data);
  206. return $page->fetchPage($additionalParams);
  207. }
  208. return $data;
  209. }
  210. /**
  211. * Include template
  212. *
  213. * @param string $file
  214. * @param array|null $additionalParams
  215. * @return string returns content of included template
  216. *
  217. * Includes another template. use <?php echo $this->inc('template'); ?> to
  218. * do this.
  219. */
  220. public function inc( $file, $additionalParams = null ) {
  221. return $this->load($this->path.$file.'.php', $additionalParams);
  222. }
  223. /**
  224. * Shortcut to print a simple page for users
  225. * @param string $application The application we render the template for
  226. * @param string $name Name of the template
  227. * @param array $parameters Parameters for the template
  228. * @return boolean|null
  229. */
  230. public static function printUserPage( $application, $name, $parameters = array() ) {
  231. $content = new OC_Template( $application, $name, "user" );
  232. foreach( $parameters as $key => $value ) {
  233. $content->assign( $key, $value );
  234. }
  235. print $content->printPage();
  236. }
  237. /**
  238. * Shortcut to print a simple page for admins
  239. * @param string $application The application we render the template for
  240. * @param string $name Name of the template
  241. * @param array $parameters Parameters for the template
  242. * @return bool
  243. */
  244. public static function printAdminPage( $application, $name, $parameters = array() ) {
  245. $content = new OC_Template( $application, $name, "admin" );
  246. foreach( $parameters as $key => $value ) {
  247. $content->assign( $key, $value );
  248. }
  249. return $content->printPage();
  250. }
  251. /**
  252. * Shortcut to print a simple page for guests
  253. * @param string $application The application we render the template for
  254. * @param string $name Name of the template
  255. * @param array|string $parameters Parameters for the template
  256. * @return bool
  257. */
  258. public static function printGuestPage( $application, $name, $parameters = array() ) {
  259. $content = new OC_Template( $application, $name, "guest" );
  260. foreach( $parameters as $key => $value ) {
  261. $content->assign( $key, $value );
  262. }
  263. return $content->printPage();
  264. }
  265. /**
  266. * Print a fatal error page and terminates the script
  267. * @param string $error_msg The error message to show
  268. * @param string $hint An optional hint message - needs to be properly escape
  269. * @suppress PhanAccessMethodInternal
  270. */
  271. public static function printErrorPage( $error_msg, $hint = '' ) {
  272. if (\OC::$server->getAppManager()->isEnabledForUser('theming') && !\OC_App::isAppLoaded('theming')) {
  273. \OC_App::loadApp('theming');
  274. }
  275. if ($error_msg === $hint) {
  276. // If the hint is the same as the message there is no need to display it twice.
  277. $hint = '';
  278. }
  279. try {
  280. $content = new \OC_Template( '', 'error', 'error', false );
  281. $errors = array(array('error' => $error_msg, 'hint' => $hint));
  282. $content->assign( 'errors', $errors );
  283. $content->printPage();
  284. } catch (\Exception $e) {
  285. $logger = \OC::$server->getLogger();
  286. $logger->error("$error_msg $hint", ['app' => 'core']);
  287. $logger->logException($e, ['app' => 'core']);
  288. header(self::getHttpProtocol() . ' 500 Internal Server Error');
  289. header('Content-Type: text/plain; charset=utf-8');
  290. print("$error_msg $hint");
  291. }
  292. die();
  293. }
  294. /**
  295. * print error page using Exception details
  296. * @param Exception|Throwable $exception
  297. * @param bool $fetchPage
  298. * @return bool|string
  299. * @suppress PhanAccessMethodInternal
  300. */
  301. public static function printExceptionErrorPage($exception, $fetchPage = false) {
  302. try {
  303. $request = \OC::$server->getRequest();
  304. $content = new \OC_Template('', 'exception', 'error', false);
  305. $content->assign('errorClass', get_class($exception));
  306. $content->assign('errorMsg', $exception->getMessage());
  307. $content->assign('errorCode', $exception->getCode());
  308. $content->assign('file', $exception->getFile());
  309. $content->assign('line', $exception->getLine());
  310. $content->assign('trace', $exception->getTraceAsString());
  311. $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false));
  312. $content->assign('remoteAddr', $request->getRemoteAddress());
  313. $content->assign('requestID', $request->getId());
  314. if ($fetchPage) {
  315. return $content->fetchPage();
  316. }
  317. $content->printPage();
  318. } catch (\Exception $e) {
  319. $logger = \OC::$server->getLogger();
  320. $logger->logException($exception, ['app' => 'core']);
  321. $logger->logException($e, ['app' => 'core']);
  322. header(self::getHttpProtocol() . ' 500 Internal Server Error');
  323. header('Content-Type: text/plain; charset=utf-8');
  324. print("Internal Server Error\n\n");
  325. print("The server encountered an internal error and was unable to complete your request.\n");
  326. print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n");
  327. print("More details can be found in the server log.\n");
  328. }
  329. die();
  330. }
  331. /**
  332. * This is only here to reduce the dependencies in case of an exception to
  333. * still be able to print a plain error message.
  334. *
  335. * Returns the used HTTP protocol.
  336. *
  337. * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
  338. * @internal Don't use this - use AppFramework\Http\Request->getHttpProtocol instead
  339. */
  340. protected static function getHttpProtocol() {
  341. $claimedProtocol = strtoupper($_SERVER['SERVER_PROTOCOL']);
  342. $validProtocols = [
  343. 'HTTP/1.0',
  344. 'HTTP/1.1',
  345. 'HTTP/2',
  346. ];
  347. if(in_array($claimedProtocol, $validProtocols, true)) {
  348. return $claimedProtocol;
  349. }
  350. return 'HTTP/1.1';
  351. }
  352. }