http = new Telegraph\HTTP(); } private function _is_logged_in(Request $request, Response $response) { session_start(); if(!session('user_id')) { session_destroy(); $response->setStatusCode(302); $response->headers->set('Location', '/login?return_to='.$request->getRequestURI()); return false; } else { return true; } } private function _get_role(Request $request, Response $response) { // Default to load their first site, but let the query string override it $role = ORM::for_table('roles')->join('sites', 'roles.site_id = sites.id') ->where('user_id', session('user_id'))->order_by_asc('sites.created_at')->find_one(); if($request->get('account')) { $role = ORM::for_table('roles')->where('user_id', session('user_id'))->where('site_id', $request->get('account'))->find_one(); // Check that the user has permission to access this account if(!$role) { $response->setStatusCode(302); $response->headers->set('Location', '/dashboard'); return false; } } return $role; } public function index(Request $request, Response $response) { p3k\session_setup(); $response->setContent(view('index', [ 'title' => 'Telegraph', 'user' => $this->_user(), ])); return $response; } public function api(Request $request, Response $response) { session_start(); if(session('user_id')) { $role = $this->_get_role($request, $response); $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one(); } else { $role = false; $site = false; } $response->setContent(view('api', [ 'title' => 'Telegraph API Documentation', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'site' => $site, 'role' => $role, 'return_to' => $request->getRequestURI() ])); return $response; } public function superfeedr(Request $request, Response $response) { session_start(); if(session('user_id')) { $role = $this->_get_role($request, $response); $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one(); } else { $role = false; $site = false; } $response->setContent(view('superfeedr', [ 'title' => 'Telegraph Superfeedr Documentation', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'site' => $site, 'role' => $role, 'return_to' => $request->getRequestURI() ])); return $response; } private static function _icon_for_status($status) { switch($status) { case 'success': case 'accepted': return 'green checkmark'; case 'not_supported': return 'yellow x'; case 'error': return 'red x'; case 'pending': return 'orange wait'; default: return ''; } } public function dashboard(Request $request, Response $response) { if(!$this->_is_logged_in($request, $response)) { return $response; } if(!$role=$this->_get_role($request, $response)) { return $response; } $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one(); $query = ORM::for_table('webmentions')->where('site_id', $site->id) ->order_by_desc('created_at') ->limit(20) ->find_many(); $webmentions = []; foreach($query as $m) { $statuses = ORM::for_table('webmention_status')->where('webmention_id', $m->id)->order_by_desc('created_at')->find_many(); if(count($statuses) == 0) { $status = 'pending'; } else { $status = $statuses[0]->status; } $icon = self::_icon_for_status($status); $webmentions[] = [ 'webmention' => $m, 'statuses' => $statuses, 'status' => $status, 'icon' => $icon ]; } $response->setContent(view('dashboard', [ 'title' => 'Telegraph Dashboard', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'site' => $site, 'role' => $role, 'webmentions' => $webmentions ])); return $response; } public function send_a_webmention(Request $request, Response $response) { session_start(); $_SESSION['_csrf'] = random_string(16); $response->setContent(view('send-a-webmention', [ 'title' => 'Send a Webmention with Telegraph', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'csrf' => $_SESSION['_csrf'], ])); return $response; } public function new_site(Request $request, Response $response) { if(!$this->_is_logged_in($request, $response)) { return $response; } if(!$role=$this->_get_role($request, $response)) { return $response; } if($request->get('account')) { // permissions are checked already by _get_role $site = ORM::for_table('sites')->where_id_is($request->get('account'))->find_one(); } else { $site = null; } $response->setContent(view('new-site', [ 'title' => 'Create New Site', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'role' => $role, 'site' => $site ])); return $response; } public function save_site(Request $request, Response $response) { if(!$this->_is_logged_in($request, $response)) { return $response; } if(!$role=$this->_get_role($request, $response)) { return $response; } if($request->get('account')) { // permissions are checked already by _get_role $site = ORM::for_table('sites')->where_id_is($request->get('account'))->find_one(); $site->name = $request->get('name'); $site->url = $request->get('url'); $site->save(); } else { $site = ORM::for_table('sites')->create(); $site->created_by = session('user_id'); $site->created_at = date('Y-m-d H:i:s'); $site->name = $request->get('name'); $site->url = $request->get('url'); $site->save(); $role = ORM::for_table('roles')->create(); $role->site_id = $site->id; $role->user_id = session('user_id'); $role->role = 'owner'; $role->token = random_string(32); $role->save(); } $response->setStatusCode(302); $response->headers->set('Location', '/dashboard?account='.$site->id); return $response; } public function webmention_details(Request $request, Response $response, $args) { session_start(); // Look up the webmention by its token $webmention = ORM::for_table('webmentions')->where('token', $args['code'])->find_one(); if(!$webmention) { $response->setContent(view('not-found')); return $response; } $site = ORM::for_table('sites')->where_id_is($webmention->site_id)->find_one(); // Find the user's role for this site if($site && $this->_user()) { $role = ORM::for_table('roles') ->where('site_id', $site['id']) ->where('user_id', $this->_user()['id']) ->find_one(); } $statuses = ORM::for_table('webmention_status')->where('webmention_id', $webmention->id)->order_by_desc('created_at')->find_many(); if(count($statuses) == 0) { $status = 'pending'; } else { $status = $statuses[0]->status; } $icon = self::_icon_for_status($status); $response->setContent(view('webmention-details', [ 'title' => 'Webmention Details', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'site' => $site, 'role' => isset($role) ? $role : false, 'webmention' => $webmention, 'statuses' => $statuses, 'icon' => $icon, 'status' => $status ])); return $response; } public function dashboard_send(Request $request, Response $response) { if(!$this->_is_logged_in($request, $response)) { return $response; } if(!$role=$this->_get_role($request, $response)) { return $response; } $site = ORM::for_table('sites')->where_id_is($role->site_id)->find_one(); $response->setContent(view('webmention-send', [ 'title' => 'Webmention Details', 'user' => $this->_user(), 'accounts' => $this->_accounts(), 'site' => $site, 'role' => $role, 'url' => $request->get('url') ])); return $response; } public function get_outgoing_links(Request $request, Response $response) { if(!$this->_is_logged_in($request, $response)) { return $response; } $sourceURL = $request->get('url'); $client = new IndieWeb\MentionClient(); $source = $this->http->get($sourceURL, ['Accept: text/html, */*']); $parsed = \Mf2\parse($source['body'], $sourceURL); $links = array_values($client->findOutgoingLinks($parsed)); // Remove the source URL from the list if present $links = array_filter($links, function($link) use($sourceURL) { // Remove URL fragment when comparing to ignore more self-links $link = preg_replace('/#.+$/', '', $link); return $link != $sourceURL; }); $response->headers->set('Content-Type', 'application/json'); $response->setContent(json_encode([ 'links' => $links ])); return $response; } public function discover_endpoint(Request $request, Response $response) { if(!$this->_is_logged_in($request, $response)) { return $response; } $targetURL = $request->get('target'); if(!Telegraph\Webmention::isProbablySupported($targetURL)) { $status = 'none'; $cached = -1; } else { // Cache the discovered result $cacheKey = 'telegraph:discover_endpoint:'.$targetURL; if($request->get('ignore_cache') == 'true' || (!$status = redis()->get($cacheKey))) { $client = new IndieWeb\MentionClient(); $endpoint = $client->discoverWebmentionEndpoint($targetURL); if($endpoint) { $status = 'webmention'; } else { $endpoint = $client->discoverPingbackEndpoint($targetURL); if($endpoint) { $status = 'pingback'; } else { $status = 'none'; } } $cached = false; redis()->setex($cacheKey, 600, $status); } else { $cached = true; } } $response->headers->set('Content-Type', 'application/json'); $response->setContent(json_encode([ 'status' => $status, 'cached' => $cached ])); return $response; } private function _user() { if(!session('user_id')) return null; return ORM::for_table('users')->where_id_is(session('user_id'))->find_one(); } private function _accounts() { if(!session('user_id')) return []; return ORM::for_table('sites')->join('roles', 'roles.site_id = sites.id') ->where('roles.user_id', session('user_id')) ->find_many(); } }