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.

377 lines
11 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. <?php
  2. use Symfony\Component\HttpFoundation\Request;
  3. use Symfony\Component\HttpFoundation\Response;
  4. class Controller {
  5. public $http;
  6. public function __construct() {
  7. $this->http = new Telegraph\HTTP();
  8. }
  9. private function _is_logged_in(Request $request, Response $response) {
  10. session_start();
  11. if(!session('user_id')) {
  12. session_destroy();
  13. $response->setStatusCode(302);
  14. $response->headers->set('Location', '/login?return_to='.$request->getRequestURI());
  15. return false;
  16. } else {
  17. return true;
  18. }
  19. }
  20. private function _get_role(Request $request, Response $response) {
  21. // Default to load their first site, but let the query string override it
  22. $role = ORM::for_table('roles')->join('sites', 'roles.site_id = sites.id')
  23. ->where('user_id', session('user_id'))->order_by_asc('sites.created_at')->find_one();
  24. if($request->get('account')) {
  25. $role = ORM::for_table('roles')->where('user_id', session('user_id'))->where('site_id', $request->get('account'))->find_one();
  26. // Check that the user has permission to access this account
  27. if(!$role) {
  28. $response->setStatusCode(302);
  29. $response->headers->set('Location', '/dashboard');
  30. return false;
  31. }
  32. }
  33. return $role;
  34. }
  35. public function index(Request $request, Response $response) {
  36. p3k\session_setup();
  37. $response->setContent(view('index', [
  38. 'title' => 'Telegraph',
  39. 'user' => $this->_user(),
  40. ]));
  41. return $response;
  42. }
  43. public function api(Request $request, Response $response) {
  44. session_start();
  45. if(session('user_id')) {
  46. $role = $this->_get_role($request, $response);
  47. $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one();
  48. } else {
  49. $role = false;
  50. $site = false;
  51. }
  52. $response->setContent(view('api', [
  53. 'title' => 'Telegraph API Documentation',
  54. 'user' => $this->_user(),
  55. 'accounts' => $this->_accounts(),
  56. 'site' => $site,
  57. 'role' => $role,
  58. 'return_to' => $request->getRequestURI()
  59. ]));
  60. return $response;
  61. }
  62. public function superfeedr(Request $request, Response $response) {
  63. session_start();
  64. if(session('user_id')) {
  65. $role = $this->_get_role($request, $response);
  66. $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one();
  67. } else {
  68. $role = false;
  69. $site = false;
  70. }
  71. $response->setContent(view('superfeedr', [
  72. 'title' => 'Telegraph Superfeedr Documentation',
  73. 'user' => $this->_user(),
  74. 'accounts' => $this->_accounts(),
  75. 'site' => $site,
  76. 'role' => $role,
  77. 'return_to' => $request->getRequestURI()
  78. ]));
  79. return $response;
  80. }
  81. private static function _icon_for_status($status) {
  82. switch($status) {
  83. case 'success':
  84. case 'accepted':
  85. return 'green checkmark';
  86. case 'not_supported':
  87. return 'yellow x';
  88. case 'error':
  89. return 'red x';
  90. case 'pending':
  91. return 'orange wait';
  92. default:
  93. return '';
  94. }
  95. }
  96. public function dashboard(Request $request, Response $response) {
  97. if(!$this->_is_logged_in($request, $response)) {
  98. return $response;
  99. }
  100. if(!$role=$this->_get_role($request, $response)) {
  101. return $response;
  102. }
  103. $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one();
  104. $query = ORM::for_table('webmentions')->where('site_id', $site->id)
  105. ->order_by_desc('created_at')
  106. ->limit(20)
  107. ->find_many();
  108. $webmentions = [];
  109. foreach($query as $m) {
  110. $statuses = ORM::for_table('webmention_status')->where('webmention_id', $m->id)->order_by_desc('created_at')->find_many();
  111. if(count($statuses) == 0) {
  112. $status = 'pending';
  113. } else {
  114. $status = $statuses[0]->status;
  115. }
  116. $icon = self::_icon_for_status($status);
  117. $webmentions[] = [
  118. 'webmention' => $m,
  119. 'statuses' => $statuses,
  120. 'status' => $status,
  121. 'icon' => $icon
  122. ];
  123. }
  124. $response->setContent(view('dashboard', [
  125. 'title' => 'Telegraph Dashboard',
  126. 'user' => $this->_user(),
  127. 'accounts' => $this->_accounts(),
  128. 'site' => $site,
  129. 'role' => $role,
  130. 'webmentions' => $webmentions
  131. ]));
  132. return $response;
  133. }
  134. public function send_a_webmention(Request $request, Response $response) {
  135. session_start();
  136. $_SESSION['_csrf'] = random_string(16);
  137. $response->setContent(view('send-a-webmention', [
  138. 'title' => 'Send a Webmention with Telegraph',
  139. 'user' => $this->_user(),
  140. 'accounts' => $this->_accounts(),
  141. 'csrf' => $_SESSION['_csrf'],
  142. ]));
  143. return $response;
  144. }
  145. public function new_site(Request $request, Response $response) {
  146. if(!$this->_is_logged_in($request, $response)) {
  147. return $response;
  148. }
  149. if(!$role=$this->_get_role($request, $response)) {
  150. return $response;
  151. }
  152. if($request->get('account')) {
  153. // permissions are checked already by _get_role
  154. $site = ORM::for_table('sites')->where_id_is($request->get('account'))->find_one();
  155. } else {
  156. $site = null;
  157. }
  158. $response->setContent(view('new-site', [
  159. 'title' => 'Create New Site',
  160. 'user' => $this->_user(),
  161. 'accounts' => $this->_accounts(),
  162. 'role' => $role,
  163. 'site' => $site
  164. ]));
  165. return $response;
  166. }
  167. public function save_site(Request $request, Response $response) {
  168. if(!$this->_is_logged_in($request, $response)) {
  169. return $response;
  170. }
  171. if(!$role=$this->_get_role($request, $response)) {
  172. return $response;
  173. }
  174. if($request->get('account')) {
  175. // permissions are checked already by _get_role
  176. $site = ORM::for_table('sites')->where_id_is($request->get('account'))->find_one();
  177. $site->name = $request->get('name');
  178. $site->url = $request->get('url');
  179. $site->save();
  180. } else {
  181. $site = ORM::for_table('sites')->create();
  182. $site->created_by = session('user_id');
  183. $site->created_at = date('Y-m-d H:i:s');
  184. $site->name = $request->get('name');
  185. $site->url = $request->get('url');
  186. $site->save();
  187. $role = ORM::for_table('roles')->create();
  188. $role->site_id = $site->id;
  189. $role->user_id = session('user_id');
  190. $role->role = 'owner';
  191. $role->token = random_string(32);
  192. $role->save();
  193. }
  194. $response->setStatusCode(302);
  195. $response->headers->set('Location', '/dashboard?account='.$site->id);
  196. return $response;
  197. }
  198. public function webmention_details(Request $request, Response $response, $args) {
  199. session_start();
  200. // Look up the webmention by its token
  201. $webmention = ORM::for_table('webmentions')->where('token', $args['code'])->find_one();
  202. if(!$webmention) {
  203. $response->setContent(view('not-found'));
  204. return $response;
  205. }
  206. $site = ORM::for_table('sites')->where_id_is($webmention->site_id)->find_one();
  207. // Find the user's role for this site
  208. if($site && $this->_user()) {
  209. $role = ORM::for_table('roles')
  210. ->where('site_id', $site['id'])
  211. ->where('user_id', $this->_user()['id'])
  212. ->find_one();
  213. }
  214. $statuses = ORM::for_table('webmention_status')->where('webmention_id', $webmention->id)->order_by_desc('created_at')->find_many();
  215. if(count($statuses) == 0) {
  216. $status = 'pending';
  217. } else {
  218. $status = $statuses[0]->status;
  219. }
  220. $icon = self::_icon_for_status($status);
  221. $response->setContent(view('webmention-details', [
  222. 'title' => 'Webmention Details',
  223. 'user' => $this->_user(),
  224. 'accounts' => $this->_accounts(),
  225. 'site' => $site,
  226. 'role' => isset($role) ? $role : false,
  227. 'webmention' => $webmention,
  228. 'statuses' => $statuses,
  229. 'icon' => $icon,
  230. 'status' => $status
  231. ]));
  232. return $response;
  233. }
  234. public function dashboard_send(Request $request, Response $response) {
  235. if(!$this->_is_logged_in($request, $response)) {
  236. return $response;
  237. }
  238. if(!$role=$this->_get_role($request, $response)) {
  239. return $response;
  240. }
  241. $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one();
  242. $response->setContent(view('webmention-send', [
  243. 'title' => 'Webmention Details',
  244. 'user' => $this->_user(),
  245. 'accounts' => $this->_accounts(),
  246. 'site' => $site,
  247. 'role' => $role,
  248. 'url' => $request->get('url')
  249. ]));
  250. return $response;
  251. }
  252. public function get_outgoing_links(Request $request, Response $response) {
  253. if(!$this->_is_logged_in($request, $response)) {
  254. return $response;
  255. }
  256. $sourceURL = $request->get('url');
  257. $client = new IndieWeb\MentionClient();
  258. $source = $this->http->get($sourceURL, ['Accept: text/html, */*']);
  259. $parsed = \Mf2\parse($source['body'], $sourceURL);
  260. $links = array_values($client->findOutgoingLinks($parsed));
  261. // Remove the source URL from the list if present
  262. $links = array_filter($links, function($link) use($sourceURL) {
  263. // Remove URL fragment when comparing to ignore more self-links
  264. $link = preg_replace('/#.+$/', '', $link);
  265. return $link != $sourceURL;
  266. });
  267. $response->headers->set('Content-Type', 'application/json');
  268. $response->setContent(json_encode([
  269. 'links' => $links
  270. ]));
  271. return $response;
  272. }
  273. public function discover_endpoint(Request $request, Response $response) {
  274. if(!$this->_is_logged_in($request, $response)) {
  275. return $response;
  276. }
  277. $targetURL = $request->get('target');
  278. if(!Telegraph\Webmention::isProbablySupported($targetURL)) {
  279. $status = 'none';
  280. $cached = -1;
  281. } else {
  282. // Cache the discovered result
  283. $cacheKey = 'telegraph:discover_endpoint:'.$targetURL;
  284. if($request->get('ignore_cache') == 'true' || (!$status = redis()->get($cacheKey))) {
  285. $client = new IndieWeb\MentionClient();
  286. $endpoint = $client->discoverWebmentionEndpoint($targetURL);
  287. if($endpoint) {
  288. $status = 'webmention';
  289. } else {
  290. $endpoint = $client->discoverPingbackEndpoint($targetURL);
  291. if($endpoint) {
  292. $status = 'pingback';
  293. } else {
  294. $status = 'none';
  295. }
  296. }
  297. $cached = false;
  298. redis()->setex($cacheKey, 600, $status);
  299. } else {
  300. $cached = true;
  301. }
  302. }
  303. $response->headers->set('Content-Type', 'application/json');
  304. $response->setContent(json_encode([
  305. 'status' => $status,
  306. 'cached' => $cached
  307. ]));
  308. return $response;
  309. }
  310. private function _user() {
  311. if(!session('user_id')) return null;
  312. return ORM::for_table('users')->where_id_is(session('user_id'))->find_one();
  313. }
  314. private function _accounts() {
  315. if(!session('user_id')) return [];
  316. return ORM::for_table('sites')->join('roles', 'roles.site_id = sites.id')
  317. ->where('roles.user_id', session('user_id'))
  318. ->find_many();
  319. }
  320. }