├── .gitignore ├── LICENSE.txt ├── README.md ├── composer.json ├── composer.lock ├── controllers ├── controllers.php ├── static.php └── webmention.php ├── lib ├── config.template.php ├── helpers.php └── mf2.php ├── public ├── .htaccess ├── bootstrap │ ├── css │ │ ├── bootstrap-responsive.css │ │ ├── bootstrap-responsive.min.css │ │ ├── bootstrap.css │ │ └── bootstrap.min.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ ├── bootstrap.js │ │ └── bootstrap.min.js ├── css │ └── style.css ├── index.php └── js │ ├── jquery-1.7.1.min.js │ ├── jquery.timeago.js │ └── timeago │ ├── README.md │ ├── jquery.timeago.ar.js │ ├── jquery.timeago.bg.js │ ├── jquery.timeago.bs.js │ ├── jquery.timeago.ca.js │ ├── jquery.timeago.cs.js │ ├── jquery.timeago.cy.js │ ├── jquery.timeago.da.js │ ├── jquery.timeago.de.js │ ├── jquery.timeago.dv.js │ ├── jquery.timeago.el.js │ ├── jquery.timeago.en-short.js │ ├── jquery.timeago.en.js │ ├── jquery.timeago.es-short.js │ ├── jquery.timeago.es.js │ ├── jquery.timeago.et.js │ ├── jquery.timeago.fa.js │ ├── jquery.timeago.fi.js │ ├── jquery.timeago.fr-short.js │ ├── jquery.timeago.fr.js │ ├── jquery.timeago.gl.js │ ├── jquery.timeago.he.js │ ├── jquery.timeago.hr.js │ ├── jquery.timeago.hu.js │ ├── jquery.timeago.hy.js │ ├── jquery.timeago.id.js │ ├── jquery.timeago.is.js │ ├── jquery.timeago.it.js │ ├── jquery.timeago.ja.js │ ├── jquery.timeago.jv.js │ ├── jquery.timeago.ko.js │ ├── jquery.timeago.ky.js │ ├── jquery.timeago.lt.js │ ├── jquery.timeago.mk.js │ ├── jquery.timeago.nl.js │ ├── jquery.timeago.no.js │ ├── jquery.timeago.pl.js │ ├── jquery.timeago.pt-br-short.js │ ├── jquery.timeago.pt-br.js │ ├── jquery.timeago.pt.js │ ├── jquery.timeago.ro.js │ ├── jquery.timeago.rs.js │ ├── jquery.timeago.ru.js │ ├── jquery.timeago.rw.js │ ├── jquery.timeago.si.js │ ├── jquery.timeago.sk.js │ ├── jquery.timeago.sl.js │ ├── jquery.timeago.sv.js │ ├── jquery.timeago.th.js │ ├── jquery.timeago.tr.js │ ├── jquery.timeago.uk.js │ ├── jquery.timeago.uz.js │ ├── jquery.timeago.vi.js │ ├── jquery.timeago.zh-CN.js │ └── jquery.timeago.zh-TW.js ├── resources └── locales │ ├── de │ └── default.po │ ├── default.pot │ ├── en │ └── default.po │ ├── es │ └── default.po │ ├── fr │ └── default.po │ ├── nl │ └── default.po │ ├── ru │ └── default.po │ └── sv │ └── default.po ├── sql ├── 0001.sql ├── 0002.sql ├── 0003.sql └── schema.sql ├── test.php └── views ├── _post-row.php ├── calendar.php ├── comment-full.php ├── index.php ├── layout.php ├── members.php ├── post.php ├── posts.php ├── submit-full.php ├── submit.php ├── webmention-error.php └── webmention.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.phar 3 | lib/config.php 4 | .DS_Store 5 | .idea/ 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD License 2 | ----------- 3 | 4 | Copyright (c) 2013-2017 by Aaron Parecki. All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of Django nor the names of its contributors may be used 17 | to endorse or promote products derived from this software without 18 | specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IndieNews 2 | 3 | This site is live on https://news.indieweb.org. It is a news aggregator for [IndieWeb](https://indieweb.org)-related posts. 4 | 5 | To submit one of your posts, link to https://news.indieweb.org/en from one the post and send a [Webmention](https://indieweb.org/webmention) to https://news.indieweb.org/webmention. 6 | 7 | [Full instructions](https://news.indieweb.org/how-to-submit-a-post) 8 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "slim/slim": "^4", 4 | "j4mie/idiorm": "1.3.*", 5 | "cakephp/i18n": "^5.0", 6 | "tantek/cassis": "dev-main", 7 | "p3k/utils": "^1.0", 8 | "p3k/xray": "^1.15", 9 | "mf2/mf2": "^0.4.6", 10 | "p3k/http": "^0.1.8", 11 | "slim/php-view": "^3.2", 12 | "slim/psr7": "^1.6" 13 | }, 14 | "autoload": { 15 | "files": [ 16 | "lib/helpers.php", 17 | "lib/mf2.php" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /controllers/controllers.php: -------------------------------------------------------------------------------- 1 | redirect('/home.json', '/en.json', 301); 7 | $app->redirect('/newest.json', '/en.json', 301); 8 | 9 | // Redirect post IDs to the post URL version 10 | $app->get('/post/{id:[0-9]+}{format:|\.json}', function($request, $response, $args) { 11 | $post = ORM::for_table('posts')->where('id', $args['id'])->find_one(); 12 | 13 | if(!$post) 14 | return $response->withStatus(404); 15 | 16 | $url = Config::$baseURL . '/post/'. slugForURL($post->href) . ($args['format']); 17 | 18 | return $response 19 | ->withHeader('Location', $url) 20 | ->withStatus(302); 21 | }); 22 | 23 | // Language-specific feeds 24 | $app->get('/{lang:'.LANG_REGEX.'}{format:|\.json|\.jf2}', function($request, $response, $args) { 25 | 26 | $params = $request->getQueryParams(); 27 | 28 | $format = formatFromRouteArgs($args); 29 | 30 | I18n::setLocale($args['lang']); 31 | 32 | // Get posts ordered by date submitted 33 | $posts = ORM::for_table('posts') 34 | ->where('lang', $args['lang']) 35 | ->order_by_desc('date_submitted'); 36 | 37 | if(array_key_exists('before', $params)) { 38 | $before = date('Y-m-d H:i:s', b60to10($params['before'])); 39 | $posts = $posts->where_lt('date_submitted', $before); 40 | } 41 | 42 | $posts = $posts->limit(20)->find_many(); 43 | 44 | $atomFeed = ''; 45 | $webSubTags = '' . "\n" . ''; 46 | 47 | $renderer = new PhpRenderer(__DIR__.'/../views/'); 48 | $renderer->setLayout('layout.php'); 49 | 50 | $temp = $renderer->render(new \Slim\Psr7\Response(), "posts.php", [ 51 | 'title' => 'IndieNews ' . $args['lang'], 52 | 'posts' => $posts, 53 | 'lang' => $args['lang'], 54 | 'meta' => $atomFeed.$webSubTags, 55 | ]); 56 | 57 | $html = $temp->getBody()->__toString(); 58 | 59 | $response = $response->withAddedHeader('Link', '<' . Config::$baseURL . '/'.$args['lang'].'/webmention>; rel="webmention"'); 60 | $response = $response->withAddedHeader('Link', '<' . Config::$baseURL . '/'.$args['lang'].'>; rel="self"'); 61 | $response = $response->withAddedHeader('Link', '<' . Config::$hubURL . '>; rel="hub"'); 62 | 63 | return respondWithFormat($response, $html, $format); 64 | }); 65 | 66 | 67 | $app->get('/{lang:'.LANG_REGEX.'}/{year:\d{4}}/{month:\d{2}}', function($request, $response, $args) { 68 | 69 | $year = $args['year']; 70 | $month = $args['month']; 71 | $lang = $args['lang']; 72 | 73 | I18n::setLocale($args['lang']); 74 | setlocale(LC_ALL, localeFromLangCode($args['lang'])); 75 | 76 | $date = new DateTime($year.'-'.$month.'-01'); 77 | 78 | $posts = ORM::for_table('posts') 79 | ->where('lang', $lang) 80 | ->where_lte('date_submitted', $date->format('Y-m-t').' 23:59:59') 81 | ->where_gte('date_submitted', $date->format('Y-m-01')) 82 | ->order_by_asc('date_submitted') 83 | ->find_many(); 84 | 85 | $prev = false; 86 | $next = false; 87 | 88 | $prevPost = ORM::for_table('posts') 89 | ->where('lang', $lang) 90 | ->where_lt('date_submitted', $date->format('Y-m-01')) 91 | ->order_by_desc('date_submitted') 92 | ->find_one(); 93 | if($prevPost) { 94 | $prev = new DateTime($prevPost->date_submitted); 95 | } 96 | 97 | $nextPost = ORM::for_table('posts') 98 | ->where('lang', $lang) 99 | ->where_gt('date_submitted', $date->format('Y-m-t').' 23:59:59') 100 | ->order_by_asc('date_submitted') 101 | ->find_one(); 102 | if($nextPost) { 103 | $next = new DateTime($nextPost->date_submitted); 104 | } 105 | 106 | $calendar = []; 107 | foreach($posts as $post) { 108 | $postDate = $post->post_date ?: $post->date_submitted; 109 | $day = printLocalDate('j', $postDate, $post->tzoffset); 110 | if(!array_key_exists($day, $calendar)) 111 | $calendar[(int)$day] = []; 112 | $calendar[(int)$day][] = $post; 113 | } 114 | ksort($calendar); 115 | 116 | return render($response, 'calendar', [ 117 | 'title' => 'IndieNews ' . $lang, 118 | 'date' => $date, 119 | 'year' => $year, 120 | 'month' => $month, 121 | 'calendar' => $calendar, 122 | 'meta' => '', 123 | 'lang' => $lang, 124 | 'next' => $next, 125 | 'prev' => $prev 126 | ]); 127 | }); 128 | 129 | 130 | 131 | 132 | // Language-specific submit instructions 133 | $app->get('/{lang:'.LANG_REGEX.'}/submit', function($request, $response, $args) { 134 | I18n::setLocale($args['lang']); 135 | 136 | return render($response, 'submit', array( 137 | 'title' => __('About IndieNews'), 138 | 'meta' => '', 139 | 'lang' => $args['lang'] 140 | )); 141 | }); 142 | 143 | 144 | $app->get('/{lang:'.LANG_REGEX.'}/members', function($request, $response, $args) { 145 | I18n::setLocale($args['lang']); 146 | 147 | $users = ORM::for_table('users') 148 | ->select('users.*') 149 | ->select_expr('COUNT(posts.id) AS num_posts') 150 | ->join('posts', ['posts.user_id', '=', 'users.id']) 151 | ->where('posts.lang', $args['lang']) 152 | ->where_gt('posts.date_submitted', date('Y-m-d H:i:s', strtotime('1 year ago'))) 153 | ->group_by('users.id') 154 | ->order_by_desc('num_posts') 155 | ->find_many(); 156 | 157 | return render($response, 'members', array( 158 | 'title' => __('IndieNews Members'), 159 | 'meta' => '', 160 | 'lang' => $args['lang'], 161 | 'users' => $users 162 | )); 163 | }); 164 | 165 | 166 | // Language-specific permalinks 167 | $app->get('/{lang:'.LANG_REGEX.'}/{slug:.*?}{format:|\.json|\.jf2}', function($request, $response, $args) { 168 | $format = formatFromRouteArgs($args); 169 | $slug = $args['slug']; 170 | 171 | I18n::setLocale($args['lang']); 172 | 173 | $post = ORM::for_table('posts')->where_in('href', array('http://'.$slug,'https://'.$slug))->find_one(); 174 | $posts = array($post); 175 | 176 | $response = $response->withAddedHeader('Link', '<' . Config::$baseURL . '/'.$args['lang'].'/webmention>; rel="webmention"'); 177 | 178 | if(!$post) { 179 | return $response->withStatus(404); 180 | } 181 | 182 | $renderer = new PhpRenderer(__DIR__.'/../views/'); 183 | $renderer->setLayout('layout.php'); 184 | 185 | $temp = $renderer->render(new \Slim\Psr7\Response(), "post.php", [ 186 | 'title' => $post->title, 187 | 'post' => $post, 188 | 'view' => 'single', 189 | 'meta' => '', 190 | 'lang' => $args['lang'] 191 | ]); 192 | 193 | $html = $temp->getBody()->__toString(); 194 | 195 | return respondWithFormat($response, $html, $format); 196 | }); 197 | -------------------------------------------------------------------------------- /controllers/static.php: -------------------------------------------------------------------------------- 1 | get('/', function($request, $response) { 3 | return render($response, 'index', array( 4 | 'title' => 'IndieNews homepage', 5 | 'meta' => '', 6 | 'lang' => 'en' 7 | )); 8 | }); 9 | 10 | $app->get('/how-to-submit-a-post', function($request, $response) use($app) { 11 | return render($response, 'submit-full', array( 12 | 'title' => 'IndieNews - How to submit a post', 13 | 'meta' => '', 14 | 'lang' => 'en' 15 | )); 16 | }); 17 | 18 | $app->redirect('/how', '/how-to-submit-a-post', 301); 19 | $app->redirect('/how-to-comment', '/how-to-submit-a-post', 301); 20 | $app->redirect('/constructing-post-urls', '/how-to-submit-a-post', 301); 21 | -------------------------------------------------------------------------------- /controllers/webmention.php: -------------------------------------------------------------------------------- 1 | get('/{lang:'.LANG_REGEX.'}/webmention', function($request, $response, $args) { 5 | I18n::setLocale($args['lang']); 6 | 7 | return render($response, 'webmention', array( 8 | 'title' => 'IndieNews Webmention Endpoint', 9 | 'meta' => '', 10 | 'lang' => $args['lang'] 11 | )); 12 | }); 13 | 14 | $app->post('/{lang:'.LANG_REGEX.'}/webmention', function($request, $response, $args) { 15 | 16 | $params = (array)$request->getParsedBody(); 17 | $is_html = $params['html'] ?? false; 18 | 19 | $sourceURL = $params['source'] ?? null; 20 | $targetURL = $params['target'] ?? null; 21 | 22 | $error = function($err, $description=false) use($is_html, $response, $args) { 23 | if($is_html) { 24 | $response = $response->withStatus(400); 25 | return render($response, 'webmention-error', [ 26 | 'title' => 'Webmention Error', 27 | 'error' => $err, 28 | 'description' => $description, 29 | 'meta' => '', 30 | 'lang' => $args['lang'] 31 | ]); 32 | } else { 33 | $error = array( 34 | 'error' => $err 35 | ); 36 | if($description) 37 | $error['error_description'] = $description; 38 | 39 | $response->getBody()->write(json_encode($error)); 40 | return $response->withHeader('Content-Type', 'application/json')->withStatus(400); 41 | } 42 | }; 43 | 44 | if($sourceURL == null) { 45 | return $error('missing_source_url', 'No source URL was provided in the request.'); 46 | } 47 | 48 | $source = parse_url($sourceURL); 49 | 50 | # Verify $source is valid 51 | if($source == null 52 | || !array_key_exists('scheme', $source) 53 | || !in_array($source['scheme'], array('http','https')) 54 | || !array_key_exists('host', $source) 55 | ) { 56 | return $error('invalid_source_url', 'The source URL was not valid. Ensure the URL is an http or https URL.'); 57 | } 58 | 59 | if($targetURL == null) { 60 | return $error('missing_target_url', 'No target URL was provided in the request.'); 61 | } 62 | 63 | # Verify $target is actually a resource under our control (home page, individual post) 64 | $target = parse_url($targetURL); 65 | 66 | # Verify $source is valid 67 | if($target == null 68 | || !array_key_exists('scheme', $target) 69 | || !in_array($target['scheme'], array('http','https')) 70 | || !array_key_exists('host', $target) 71 | || $target['host'] != parse_url(Config::$baseURL, PHP_URL_HOST) 72 | ) { 73 | return $error('target_not_supported', 'The target URL provided is not supported. Only '.Config::$baseURL.' URLs are accepted.'); 74 | } 75 | 76 | if(!preg_match('/^' . str_replace('/', '\/', Config::$baseURL) . '(?:\/('.LANG_REGEX.'))\/?$/', $targetURL, $match)) { 77 | return $error('target_not_supported', 'The target you specified does not match a supported URL on this site.'); 78 | } 79 | 80 | // Parse the language from the target URL, so that the story ends up on the specified 81 | // feed regardless of which endpoint it was sent to. 82 | // If no lang was sent in the target param (like if they just linked to the indienews home page), 83 | // then use the language specified by the webmention endpoint. 84 | if(array_key_exists(1, $match)) 85 | $lang = $match[1]; 86 | 87 | $record = array( 88 | 'post_author' => $source['scheme'].'://'.$source['host'], 89 | 'title' => false, 90 | 'body' => false, 91 | 'date' => false 92 | ); 93 | $notices = array(); 94 | 95 | # Now fetch and parse the page looking for Microformats 96 | $xray = new p3k\XRay(); 97 | $xray->http = new p3k\HTTP('IndieNews/1.0.0 (https://news.indieweb.org/)'); 98 | $xrayresponse = $xray->parse($sourceURL, ['ignore-as2' => true]); 99 | 100 | if(isset($xrayresponse['error'])) { 101 | return $error($xrayresponse['error'], 'An error occurred while attempting to fetch the source URL: ' . $xrayresponse['error_description']); 102 | } 103 | 104 | $post = $xrayresponse['data']; 105 | if(isset($xrayresponse['refs'])) 106 | $refs = $xrayresponse['refs']; 107 | else 108 | $refs = []; 109 | 110 | if(!isset($post['type']) || !in_array($post['type'], ['entry','event'])) { 111 | return $error('no_link_found', 'No h-entry or h-event was found on the page, so we were unable to find a u-syndication or u-category URL. If you have multiple top-level h-* objects, ensure that one of them has a u-url property set to the URL of the page.'); 112 | } 113 | 114 | $authorURL = false; 115 | 116 | if($post['type'] == 'entry') { 117 | 118 | if(isset($post['name'])) { 119 | $record['title'] = ellipsize_to_word($post['name'], 200, '...', 10); 120 | } else { 121 | $notices[] = 'No "name" property found on the h-entry.'; 122 | } 123 | 124 | if(isset($post['content'])) { 125 | $record['body'] = ellipsize_to_word($post['content']['text'], 500, '...', 10); 126 | } 127 | 128 | if(array_key_exists('published', $post)) { 129 | try { 130 | $published = new DateTime($post['published']); 131 | } catch(Exception $e) { 132 | $notices[] = 'Failed to parse published date'; 133 | } 134 | if(isset($published)) { 135 | $record['date'] = $published; 136 | $utcdate = clone $published; 137 | $utcdate->setTimeZone(new DateTimeZone('UTC')); 138 | } 139 | } else { 140 | $notices[] = 'No published date found'; 141 | } 142 | 143 | 144 | } elseif($post['type'] == 'event') { 145 | if(isset($post['name'])) { 146 | $record['title'] = ellipsize_to_word($post['name'], 200, '...', 10); 147 | } else { 148 | $notices[] = 'No "name" was found for this h-event'; 149 | } 150 | 151 | if(isset($post['start'])) { 152 | try { 153 | $start = new DateTime($post['start']); 154 | } catch(Exception $e) { 155 | $notices[] = 'Failed to parse start date'; 156 | } 157 | if(isset($start) && $start) { 158 | $record['date'] = $start; 159 | $utcdate = clone $start; 160 | $utcdate->setTimeZone(new DateTimeZone('UTC')); 161 | } 162 | } 163 | 164 | if($locations=$post['location']) { 165 | $locationURL = $locations[0]; 166 | if(array_key_exists($locationURL, $refs)) { 167 | $location = $refs[$locationURL]; 168 | if(array_key_exists('name', $location)) { 169 | $record['title'] .= ' at ' . ellipsize_to_word($location['name'], 200, '...', 10); 170 | } 171 | } 172 | } 173 | } 174 | 175 | if(isset($post['author']) && $post['author']['url']) { 176 | $authorURL = parse_url($post['author']['url']); 177 | if($authorURL && array_key_exists('host', $authorURL)) { 178 | $record['post_author'] = $post['author']['url']; 179 | } else { 180 | $notices[] = 'No host was found on the author URL (' . $post['author']['url'] . ')'; 181 | } 182 | } else { 183 | $notices[] = 'No author URL was found for the h-entry. Using the domain name instead.'; 184 | } 185 | 186 | $synURL = false; 187 | if(array_key_exists('syndication', $post)) { 188 | foreach($post['syndication'] as $syn) { 189 | if(strpos($syn, Config::$baseURL) === 0) { 190 | $synURL = $syn; 191 | } 192 | } 193 | } 194 | if(array_key_exists('category', $post)) { 195 | foreach($post['category'] as $cat) { 196 | if(strpos($cat, Config::$baseURL) === 0) { 197 | $synURL = $cat; 198 | } 199 | } 200 | } 201 | if(!$synURL) { 202 | return $error('no_link_found', 'Could not find a syndication or category link for this entry to news.indieweb.org. Please see https://news.indieweb.org/how for more information.'); 203 | } 204 | if($synURL != $targetURL) { 205 | return $error('target_mismatch', 'The URL on the page did not match the target URL of the Webmention. Make sure your post links to ' . $targetURL); 206 | } 207 | 208 | if(array_key_exists('in-reply-to', $post)) { 209 | // We can only use the first in-reply-to. Not sure what the correct behavior would be for multiple. 210 | $inReplyTo = $post['in-reply-to'][0]; 211 | } else { 212 | $inReplyTo = false; 213 | } 214 | 215 | # Get the domain of $source and find or create a user account 216 | $user = ORM::for_table('users')->where('url', $record['post_author'])->find_one(); 217 | 218 | if($user == FALSE) { 219 | $user = ORM::for_table('users')->create(); 220 | $user->url = $record['post_author']; 221 | $user->date_created = date('Y-m-d H:i:s'); 222 | $user->save(); 223 | } 224 | 225 | $href = $sourceURL; 226 | 227 | if(array_key_exists('bookmark-of', $post)) { 228 | // Strip utm tracking params 229 | $href = p3k\url\strip_tracking_params($post['bookmark-of'][0]); 230 | if(array_key_exists($href, $refs)) { 231 | if(array_key_exists('name', $refs[$href])) { 232 | $record['title'] = $refs[$href]['name']; 233 | } else { 234 | // TODO: Parse the bookmark URL and find the canonical post title 235 | } 236 | } 237 | // If this is a submission of a bookmark, set the post author to the bookmark website. 238 | // For now, just set it to the domain of the bookmark. Later we could parse the bookmark for an h-card. 239 | if($href != $sourceURL) { 240 | $record['post_author'] = parse_url($href, PHP_URL_SCHEME) . '://' . parse_url($href, PHP_URL_HOST); 241 | } 242 | } 243 | 244 | $indieNewsPermalink = permalinkForURL($lang, $href); 245 | 246 | # If there is no existing post for $sourceURL, update the properties 247 | $post = ORM::for_table('posts')->where('lang', $lang)->where('source_url', $sourceURL)->find_one(); 248 | if($post != FALSE) { 249 | if($record['date']) { 250 | $post->post_date = $utcdate->format('Y-m-d H:i:s'); 251 | $post->tzoffset = $record['date']->format('Z'); 252 | } 253 | $post->post_author = $record['post_author']; 254 | $post->title = $record['title']; 255 | if($inReplyTo) 256 | $post->in_reply_to = $inReplyTo; 257 | if($record['body']) 258 | $post->body = $record['body']; 259 | $post->save(); 260 | $notices[] = 'Already registered, updating properties of the post.'; 261 | $update = true; 262 | } else { 263 | # Record a new post 264 | $post = ORM::for_table('posts')->create(); 265 | $post->lang = $lang; 266 | $post->user_id = $user->id; 267 | $post->date_submitted = date('Y-m-d H:i:s'); 268 | if($record['date']) { 269 | $post->post_date = $utcdate->format('Y-m-d H:i:s'); 270 | $post->tzoffset = $record['date']->format('Z'); 271 | } 272 | $post->post_author = $record['post_author']; 273 | $post->title = $record['title']; 274 | if($inReplyTo) 275 | $post->in_reply_to = $inReplyTo; 276 | if($record['body']) 277 | $post->body = $record['body']; 278 | $post->href = $href; 279 | $post->source_url = $sourceURL; 280 | $post->save(); 281 | $update = false; 282 | 283 | irc_notice('[indienews' . ($lang == 'en' ? '' : '/'.strtolower($lang)) . '] New post: ' . ($post->title ? '"'.$post->title.'" ' : '') . $post->href . ($sourceURL == $href ? '' : ' (from ' . $sourceURL . ')')); 284 | 285 | # Ping the hub 286 | if(Config::$hubURL) { 287 | $ch = curl_init(Config::$hubURL); 288 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 289 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 290 | 'hub.mode' => 'publish', 291 | 'hub.topic' => Config::$baseURL . '/' . $lang 292 | ])); 293 | curl_setopt($ch, CURLOPT_TIMEOUT, 4); 294 | curl_exec($ch); 295 | } 296 | } 297 | 298 | if($is_html) { 299 | return $response 300 | ->withHeader('Location', $indieNewsPermalink) 301 | ->withStatus(302); 302 | } else { 303 | $response = $response 304 | ->withHeader('Content-Type', 'application/json') 305 | ->withStatus(201); 306 | 307 | $responseData = array( 308 | 'title' => $record['title'], 309 | 'body' => $record['body'] ? true : false, 310 | 'author' => $record['post_author'], 311 | 'date' => ($record['date'] ? $record['date']->format('c') : false) 312 | ); 313 | if($inReplyTo) 314 | $responseData['in-reply-to'] = $inReplyTo; 315 | 316 | $data = array( 317 | 'result' => 'success', 318 | 'notices' => $notices, 319 | 'data' => $responseData, 320 | 'source' => $sourceURL, 321 | 'url' => $indieNewsPermalink 322 | ); 323 | 324 | $response->getBody()->write(json_encode($data, JSON_PRETTY_PRINT)); 325 | 326 | return $response->withHeader('Location', $indieNewsPermalink); 327 | } 328 | }); 329 | 330 | -------------------------------------------------------------------------------- /lib/config.template.php: -------------------------------------------------------------------------------- 1 | 'English', 15 | 'sv' => 'Svenska', 16 | 'de' => 'Deutsch', 17 | 'fr' => 'Français', 18 | 'nl' => 'Nederlands', 19 | 'ru' => 'русский', 20 | 'es' => 'Español', 21 | ]; 22 | } 23 | 24 | function localeFromLangCode($code) { 25 | switch($code) { 26 | case 'en': 27 | return 'en_US'; 28 | case 'sv': 29 | return 'sv_SE.UTF-8'; 30 | case 'de': 31 | return 'de_DE.UTF-8'; 32 | case 'fr': 33 | return 'fr_FR.UTF-8'; 34 | case 'nl': 35 | return 'nl_NL.UTF-8'; 36 | case 'ru': 37 | return 'ru_RU.UTF-8'; 38 | case 'es': 39 | return 'es_MX.UTF-8'; 40 | } 41 | } 42 | 43 | function getPostsForParentID($parentID) { 44 | return ORM::for_table('posts')->raw_query(' 45 | SELECT *, GREATEST(1, TIMESTAMPDIFF(HOUR, date_submitted, NOW())) AS age 46 | FROM posts 47 | ORDER BY date_submitted DESC 48 | ')->find_many(); 49 | } 50 | 51 | function respondWithFormat($response, $html, $format) { 52 | if($format == 'json') { 53 | $parser = new mf2\Parser($html); 54 | $output = $parser->parse(); 55 | $output['note'][] = "This JSON is automatically generated by parsing the microformats from the HTML representation of this page using the php-mf2 library."; 56 | $output['note'][] = "You can find the php-mf2 library at github.com/microformats/php-mf2"; 57 | $output['note'][] = "If you see a problem with the output, please let me know! (github.com/aaronpk/IndieNews/issues)"; 58 | $response->getBody()->write(json_encode($output)); 59 | return $response->withHeader('Content-Type', 'application/json'); 60 | } elseif($format == 'jf2') { 61 | $xray = new p3k\XRay(); 62 | $parsed = $xray->parse(Config::$baseURL, $html); 63 | $response->getBody()->write(json_encode($parsed)); 64 | return $response->withHeader('Content-Type', 'application/json'); 65 | } else { 66 | $response->getBody()->write($html); 67 | return $response; 68 | } 69 | } 70 | 71 | // $format - one of the php.net/date format strings 72 | // $date - a string that will be passed to DateTime() 73 | // $offset - numeric timezone offset 74 | function printLocalDate($format, $date, $offset) { 75 | if($offset != 0) 76 | $tz = new DateTimeZone(($offset < 0 ? '-' : '+') . sprintf('%02d:%02d', abs(floor($offset / 60 / 60)), (($offset / 60) % 60))); 77 | else 78 | $tz = new DateTimeZone('UTC'); 79 | $d = new DateTime($date); 80 | $d->setTimeZone($tz); 81 | return $d->format($format); 82 | } 83 | 84 | function formatFromRouteArgs($args) { 85 | return empty($args['format']) ? 'html' : trim($args['format'], '.'); 86 | } 87 | 88 | // Strip the http:// prefix 89 | function slugForURL($url) { 90 | return preg_replace('/https?:\/\//', '', $url); 91 | } 92 | 93 | function permalinkForURL($lang, $url) { 94 | return Config::$baseURL . '/' . $lang . '/' . slugForURL($url); 95 | } 96 | 97 | function shouldDisplayPostName($name) { 98 | if(!$name) return false; 99 | $name = str_replace('http://','https://',$name); 100 | return strlen($name) < 200 # must be less than 200 chars 101 | && substr_count($name, "\n") <= 1 # must be less than 2 lines 102 | && substr_count($name, "https://") <= 1; # must have at most 1 URL 103 | } 104 | 105 | function render($response, $page, $data) { 106 | $renderer = new PhpRenderer(__DIR__.'/../views/'); 107 | $renderer->setLayout('layout.php'); 108 | return $renderer->render($response, "$page.php", $data); 109 | }; 110 | 111 | function session($key) { 112 | if(array_key_exists($key, $_SESSION)) 113 | return $_SESSION[$key]; 114 | else 115 | return null; 116 | } 117 | 118 | function getLoggedInUser() { 119 | if(session('user')) { 120 | return ORM::for_table('users')->where('url', session('user'))->find_one(); 121 | } else { 122 | return false; 123 | } 124 | } 125 | 126 | function display_url($url) { 127 | return preg_replace(['/https?:\/\//','/\/$/'],'',$url); 128 | } 129 | 130 | function pa($a) { 131 | echo '
'; print_r($a); echo ''; 132 | } 133 | 134 | function irc_notice($msg) { 135 | if(isset(Config::$ircURL)) { 136 | $ch = curl_init(Config::$ircURL); 137 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 138 | curl_setopt($ch, CURLOPT_POST, true); 139 | curl_setopt($ch, CURLOPT_HTTPHEADER, [ 140 | 'Authorization: Bearer ' . Config::$ircToken 141 | ]); 142 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( 143 | 'content' => $msg, 144 | 'channel' => Config::$ircChannel 145 | ))); 146 | curl_exec($ch); 147 | } 148 | } 149 | 150 | /** 151 | * Converts base 10 to base 60. 152 | * http://tantek.pbworks.com/NewBase60 153 | * @param int $n 154 | * @return string 155 | */ 156 | function b10to60($n) 157 | { 158 | $s = ""; 159 | $m = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ_abcdefghijkmnopqrstuvwxyz"; 160 | if ($n==0) 161 | return 0; 162 | 163 | while ($n>0) 164 | { 165 | $d = $n % 60; 166 | $s = $m[$d] . $s; 167 | $n = ($n-$d)/60; 168 | } 169 | return $s; 170 | } 171 | 172 | /** 173 | * Converts base 60 to base 10, with error checking 174 | * http://tantek.pbworks.com/NewBase60 175 | * @param string $s 176 | * @return int 177 | */ 178 | function b60to10($s) 179 | { 180 | $n = 0; 181 | for($i = 0; $i < strlen($s); $i++) // iterate from first to last char of $s 182 | { 183 | $c = ord($s[$i]); // put current ASCII of char into $c 184 | if ($c>=48 && $c<=57) { $c=$c-48; } 185 | else if ($c>=65 && $c<=72) { $c-=55; } 186 | else if ($c==73 || $c==108) { $c=1; } // typo capital I, lowercase l to 1 187 | else if ($c>=74 && $c<=78) { $c-=56; } 188 | else if ($c==79) { $c=0; } // error correct typo capital O to 0 189 | else if ($c>=80 && $c<=90) { $c-=57; } 190 | else if ($c==95) { $c=34; } // underscore 191 | else if ($c>=97 && $c<=107) { $c-=62; } 192 | else if ($c>=109 && $c<=122) { $c-=63; } 193 | else { $c = 0; } // treat all other noise as 0 194 | $n = (60 * $n) + $c; 195 | } 196 | return $n; 197 | } 198 | -------------------------------------------------------------------------------- /lib/mf2.php: -------------------------------------------------------------------------------- 1 | _data = json_decode(json_encode($data)); 9 | else 10 | $this->_data = $data; 11 | $this->_parent = $parent; 12 | } 13 | 14 | public function __get($key) { 15 | $cacheKey = '_'.$key; 16 | 17 | if(method_exists($this, $key) && property_exists($this, $cacheKey)) { 18 | if($this->{$cacheKey} !== null) 19 | return $this->{$cacheKey}; 20 | $val = $this->$key(); 21 | $this->{$cacheKey} = $val; 22 | return $val; 23 | } 24 | 25 | return null; 26 | } 27 | 28 | protected function _is($obj, $type) { 29 | return in_array($type, $obj->type); 30 | } 31 | 32 | protected function _findFirstItem($type) { 33 | foreach($this->_data->items as $i) { 34 | if($this->_is($i, $type)) 35 | return $i; 36 | } 37 | return null; 38 | } 39 | 40 | // Find the property in the 'properties' object and return the values 41 | // If $first is true, returns only the first value, otherwise returns an array 42 | public function property($key, $first=false) { 43 | if($this->_data && property_exists($this->_data, 'properties')) { 44 | if(property_exists($this->_data->properties, $key)) { 45 | if($first) 46 | return $this->_data->properties->{$key}[0]; 47 | else 48 | return $this->_data->properties->{$key}; 49 | } 50 | } 51 | if($first) 52 | return null; 53 | else 54 | return array(); 55 | } 56 | 57 | protected function _stringForProperty($key, $single=false) { 58 | if($content=$this->property($key)) { 59 | if($single) { 60 | return $content[0]; 61 | } else{ 62 | return implode(' ', $content); 63 | } 64 | } 65 | return ''; 66 | } 67 | } 68 | 69 | class MF2Page extends MF2Object { 70 | protected $_hentry = null; 71 | protected $_hevent = null; 72 | protected $_author = null; 73 | 74 | protected function hentry() { 75 | $item = $this->_findFirstItem('h-entry'); 76 | if($item) 77 | return new HEntry($item, $this); 78 | else 79 | return null; 80 | } 81 | 82 | protected function hevent() { 83 | $item = $this->_findFirstItem('h-event'); 84 | if($item) 85 | return new HEvent($item, $this); 86 | else 87 | return null; 88 | } 89 | 90 | protected function author() { 91 | $author = null; 92 | $entry = $this->_findFirstItem('h-entry'); 93 | if($entry) { 94 | if(property_exists($entry->properties, 'author')) { 95 | $author = $entry->properties->author; 96 | } else { 97 | 98 | } 99 | } 100 | } 101 | 102 | } 103 | 104 | class HEntry extends MF2Object { 105 | protected $_content = null; 106 | protected $_published = null; 107 | protected $_author = null; 108 | 109 | protected function content() { 110 | if($content=$this->property('name')) 111 | return implode(' ', $content); 112 | if($content=$this->property('content')) 113 | return implode(' ', $content); 114 | return ''; 115 | } 116 | 117 | protected function published() { 118 | if($time=$this->property('published', true)) 119 | return new DateTime($time); 120 | return null; 121 | } 122 | 123 | // Search the h-entry for an author. If none is found, fall back to the parent's h-card. 124 | protected function author() { 125 | if($author=$this->property('author', true)) 126 | return new HCard($author); 127 | if($author=$this->_parent->_findFirstItem('h-card')) 128 | return new HCard($author); 129 | return null; 130 | } 131 | } 132 | 133 | class HEvent extends MF2Object { 134 | protected $_location = null; 135 | protected $_published = null; 136 | protected $_author = null; 137 | 138 | protected function location() { 139 | $locations = array(); 140 | if($location=$this->property('location')) { 141 | foreach($location as $l) 142 | $locations[] = new HCard($l); 143 | return $locations; 144 | } else { 145 | return null; 146 | } 147 | } 148 | 149 | protected function published() { 150 | if($time=$this->property('published', true)) 151 | return new DateTime($time); 152 | return null; 153 | } 154 | 155 | // Search the h-entry for an author. If none is found, fall back to the parent's h-card. 156 | protected function author() { 157 | if($author=$this->property('author', true)) 158 | return new HCard($author); 159 | if($author=$this->_parent->_findFirstItem('h-card')) 160 | return new HCard($author); 161 | return null; 162 | } 163 | } 164 | 165 | class HCard extends MF2Object { 166 | protected $_name; 167 | protected $_nickname; 168 | protected $_given_name; 169 | protected $_family_name; 170 | protected $_adr; 171 | protected $_note; 172 | protected $_photo; 173 | protected $_url; 174 | 175 | protected function name() { 176 | return $this->_stringForProperty('name', true); 177 | } 178 | 179 | protected function nickname() { 180 | return $this->_stringForProperty('nickname', true); 181 | } 182 | 183 | protected function given_name() { 184 | return $this->_stringForProperty('given-name', true); 185 | } 186 | 187 | protected function family_name() { 188 | return $this->_stringForProperty('family-name', true); 189 | } 190 | 191 | protected function adr() { 192 | return $this->_stringForProperty('adr'. true); 193 | } 194 | 195 | protected function note() { 196 | return $this->_stringForProperty('note', true); 197 | } 198 | 199 | protected function photo() { 200 | return $this->_stringForProperty('photo', true); 201 | } 202 | 203 | protected function url() { 204 | return $this->_stringForProperty('url', true); 205 | } 206 | 207 | } 208 | 209 | class ParserPlus extends mf2\Parser { 210 | public function xpath($query) { 211 | return $this->xpath->query($query); 212 | } 213 | } 214 | 215 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteCond %{REQUEST_FILENAME} !-f 3 | RewriteRule ^ index.php [QSA,L] 4 | -------------------------------------------------------------------------------- /public/bootstrap/css/bootstrap-responsive.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.2.2 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | @-ms-viewport { 12 | width: device-width; 13 | } 14 | 15 | .clearfix { 16 | *zoom: 1; 17 | } 18 | 19 | .clearfix:before, 20 | .clearfix:after { 21 | display: table; 22 | line-height: 0; 23 | content: ""; 24 | } 25 | 26 | .clearfix:after { 27 | clear: both; 28 | } 29 | 30 | .hide-text { 31 | font: 0/0 a; 32 | color: transparent; 33 | text-shadow: none; 34 | background-color: transparent; 35 | border: 0; 36 | } 37 | 38 | .input-block-level { 39 | display: block; 40 | width: 100%; 41 | min-height: 30px; 42 | -webkit-box-sizing: border-box; 43 | -moz-box-sizing: border-box; 44 | box-sizing: border-box; 45 | } 46 | 47 | .hidden { 48 | display: none; 49 | visibility: hidden; 50 | } 51 | 52 | .visible-phone { 53 | display: none !important; 54 | } 55 | 56 | .visible-tablet { 57 | display: none !important; 58 | } 59 | 60 | .hidden-desktop { 61 | display: none !important; 62 | } 63 | 64 | .visible-desktop { 65 | display: inherit !important; 66 | } 67 | 68 | @media (min-width: 768px) and (max-width: 979px) { 69 | .hidden-desktop { 70 | display: inherit !important; 71 | } 72 | .visible-desktop { 73 | display: none !important ; 74 | } 75 | .visible-tablet { 76 | display: inherit !important; 77 | } 78 | .hidden-tablet { 79 | display: none !important; 80 | } 81 | } 82 | 83 | @media (max-width: 767px) { 84 | .hidden-desktop { 85 | display: inherit !important; 86 | } 87 | .visible-desktop { 88 | display: none !important; 89 | } 90 | .visible-phone { 91 | display: inherit !important; 92 | } 93 | .hidden-phone { 94 | display: none !important; 95 | } 96 | } 97 | 98 | @media (min-width: 1200px) { 99 | .row { 100 | margin-left: -30px; 101 | *zoom: 1; 102 | } 103 | .row:before, 104 | .row:after { 105 | display: table; 106 | line-height: 0; 107 | content: ""; 108 | } 109 | .row:after { 110 | clear: both; 111 | } 112 | [class*="span"] { 113 | float: left; 114 | min-height: 1px; 115 | margin-left: 30px; 116 | } 117 | .container, 118 | .navbar-static-top .container, 119 | .navbar-fixed-top .container, 120 | .navbar-fixed-bottom .container { 121 | width: 1170px; 122 | } 123 | .span12 { 124 | width: 1170px; 125 | } 126 | .span11 { 127 | width: 1070px; 128 | } 129 | .span10 { 130 | width: 970px; 131 | } 132 | .span9 { 133 | width: 870px; 134 | } 135 | .span8 { 136 | width: 770px; 137 | } 138 | .span7 { 139 | width: 670px; 140 | } 141 | .span6 { 142 | width: 570px; 143 | } 144 | .span5 { 145 | width: 470px; 146 | } 147 | .span4 { 148 | width: 370px; 149 | } 150 | .span3 { 151 | width: 270px; 152 | } 153 | .span2 { 154 | width: 170px; 155 | } 156 | .span1 { 157 | width: 70px; 158 | } 159 | .offset12 { 160 | margin-left: 1230px; 161 | } 162 | .offset11 { 163 | margin-left: 1130px; 164 | } 165 | .offset10 { 166 | margin-left: 1030px; 167 | } 168 | .offset9 { 169 | margin-left: 930px; 170 | } 171 | .offset8 { 172 | margin-left: 830px; 173 | } 174 | .offset7 { 175 | margin-left: 730px; 176 | } 177 | .offset6 { 178 | margin-left: 630px; 179 | } 180 | .offset5 { 181 | margin-left: 530px; 182 | } 183 | .offset4 { 184 | margin-left: 430px; 185 | } 186 | .offset3 { 187 | margin-left: 330px; 188 | } 189 | .offset2 { 190 | margin-left: 230px; 191 | } 192 | .offset1 { 193 | margin-left: 130px; 194 | } 195 | .row-fluid { 196 | width: 100%; 197 | *zoom: 1; 198 | } 199 | .row-fluid:before, 200 | .row-fluid:after { 201 | display: table; 202 | line-height: 0; 203 | content: ""; 204 | } 205 | .row-fluid:after { 206 | clear: both; 207 | } 208 | .row-fluid [class*="span"] { 209 | display: block; 210 | float: left; 211 | width: 100%; 212 | min-height: 30px; 213 | margin-left: 2.564102564102564%; 214 | *margin-left: 2.5109110747408616%; 215 | -webkit-box-sizing: border-box; 216 | -moz-box-sizing: border-box; 217 | box-sizing: border-box; 218 | } 219 | .row-fluid [class*="span"]:first-child { 220 | margin-left: 0; 221 | } 222 | .row-fluid .controls-row [class*="span"] + [class*="span"] { 223 | margin-left: 2.564102564102564%; 224 | } 225 | .row-fluid .span12 { 226 | width: 100%; 227 | *width: 99.94680851063829%; 228 | } 229 | .row-fluid .span11 { 230 | width: 91.45299145299145%; 231 | *width: 91.39979996362975%; 232 | } 233 | .row-fluid .span10 { 234 | width: 82.90598290598291%; 235 | *width: 82.8527914166212%; 236 | } 237 | .row-fluid .span9 { 238 | width: 74.35897435897436%; 239 | *width: 74.30578286961266%; 240 | } 241 | .row-fluid .span8 { 242 | width: 65.81196581196582%; 243 | *width: 65.75877432260411%; 244 | } 245 | .row-fluid .span7 { 246 | width: 57.26495726495726%; 247 | *width: 57.21176577559556%; 248 | } 249 | .row-fluid .span6 { 250 | width: 48.717948717948715%; 251 | *width: 48.664757228587014%; 252 | } 253 | .row-fluid .span5 { 254 | width: 40.17094017094017%; 255 | *width: 40.11774868157847%; 256 | } 257 | .row-fluid .span4 { 258 | width: 31.623931623931625%; 259 | *width: 31.570740134569924%; 260 | } 261 | .row-fluid .span3 { 262 | width: 23.076923076923077%; 263 | *width: 23.023731587561375%; 264 | } 265 | .row-fluid .span2 { 266 | width: 14.52991452991453%; 267 | *width: 14.476723040552828%; 268 | } 269 | .row-fluid .span1 { 270 | width: 5.982905982905983%; 271 | *width: 5.929714493544281%; 272 | } 273 | .row-fluid .offset12 { 274 | margin-left: 105.12820512820512%; 275 | *margin-left: 105.02182214948171%; 276 | } 277 | .row-fluid .offset12:first-child { 278 | margin-left: 102.56410256410257%; 279 | *margin-left: 102.45771958537915%; 280 | } 281 | .row-fluid .offset11 { 282 | margin-left: 96.58119658119658%; 283 | *margin-left: 96.47481360247316%; 284 | } 285 | .row-fluid .offset11:first-child { 286 | margin-left: 94.01709401709402%; 287 | *margin-left: 93.91071103837061%; 288 | } 289 | .row-fluid .offset10 { 290 | margin-left: 88.03418803418803%; 291 | *margin-left: 87.92780505546462%; 292 | } 293 | .row-fluid .offset10:first-child { 294 | margin-left: 85.47008547008548%; 295 | *margin-left: 85.36370249136206%; 296 | } 297 | .row-fluid .offset9 { 298 | margin-left: 79.48717948717949%; 299 | *margin-left: 79.38079650845607%; 300 | } 301 | .row-fluid .offset9:first-child { 302 | margin-left: 76.92307692307693%; 303 | *margin-left: 76.81669394435352%; 304 | } 305 | .row-fluid .offset8 { 306 | margin-left: 70.94017094017094%; 307 | *margin-left: 70.83378796144753%; 308 | } 309 | .row-fluid .offset8:first-child { 310 | margin-left: 68.37606837606839%; 311 | *margin-left: 68.26968539734497%; 312 | } 313 | .row-fluid .offset7 { 314 | margin-left: 62.393162393162385%; 315 | *margin-left: 62.28677941443899%; 316 | } 317 | .row-fluid .offset7:first-child { 318 | margin-left: 59.82905982905982%; 319 | *margin-left: 59.72267685033642%; 320 | } 321 | .row-fluid .offset6 { 322 | margin-left: 53.84615384615384%; 323 | *margin-left: 53.739770867430444%; 324 | } 325 | .row-fluid .offset6:first-child { 326 | margin-left: 51.28205128205128%; 327 | *margin-left: 51.175668303327875%; 328 | } 329 | .row-fluid .offset5 { 330 | margin-left: 45.299145299145295%; 331 | *margin-left: 45.1927623204219%; 332 | } 333 | .row-fluid .offset5:first-child { 334 | margin-left: 42.73504273504273%; 335 | *margin-left: 42.62865975631933%; 336 | } 337 | .row-fluid .offset4 { 338 | margin-left: 36.75213675213675%; 339 | *margin-left: 36.645753773413354%; 340 | } 341 | .row-fluid .offset4:first-child { 342 | margin-left: 34.18803418803419%; 343 | *margin-left: 34.081651209310785%; 344 | } 345 | .row-fluid .offset3 { 346 | margin-left: 28.205128205128204%; 347 | *margin-left: 28.0987452264048%; 348 | } 349 | .row-fluid .offset3:first-child { 350 | margin-left: 25.641025641025642%; 351 | *margin-left: 25.53464266230224%; 352 | } 353 | .row-fluid .offset2 { 354 | margin-left: 19.65811965811966%; 355 | *margin-left: 19.551736679396257%; 356 | } 357 | .row-fluid .offset2:first-child { 358 | margin-left: 17.094017094017094%; 359 | *margin-left: 16.98763411529369%; 360 | } 361 | .row-fluid .offset1 { 362 | margin-left: 11.11111111111111%; 363 | *margin-left: 11.004728132387708%; 364 | } 365 | .row-fluid .offset1:first-child { 366 | margin-left: 8.547008547008547%; 367 | *margin-left: 8.440625568285142%; 368 | } 369 | input, 370 | textarea, 371 | .uneditable-input { 372 | margin-left: 0; 373 | } 374 | .controls-row [class*="span"] + [class*="span"] { 375 | margin-left: 30px; 376 | } 377 | input.span12, 378 | textarea.span12, 379 | .uneditable-input.span12 { 380 | width: 1156px; 381 | } 382 | input.span11, 383 | textarea.span11, 384 | .uneditable-input.span11 { 385 | width: 1056px; 386 | } 387 | input.span10, 388 | textarea.span10, 389 | .uneditable-input.span10 { 390 | width: 956px; 391 | } 392 | input.span9, 393 | textarea.span9, 394 | .uneditable-input.span9 { 395 | width: 856px; 396 | } 397 | input.span8, 398 | textarea.span8, 399 | .uneditable-input.span8 { 400 | width: 756px; 401 | } 402 | input.span7, 403 | textarea.span7, 404 | .uneditable-input.span7 { 405 | width: 656px; 406 | } 407 | input.span6, 408 | textarea.span6, 409 | .uneditable-input.span6 { 410 | width: 556px; 411 | } 412 | input.span5, 413 | textarea.span5, 414 | .uneditable-input.span5 { 415 | width: 456px; 416 | } 417 | input.span4, 418 | textarea.span4, 419 | .uneditable-input.span4 { 420 | width: 356px; 421 | } 422 | input.span3, 423 | textarea.span3, 424 | .uneditable-input.span3 { 425 | width: 256px; 426 | } 427 | input.span2, 428 | textarea.span2, 429 | .uneditable-input.span2 { 430 | width: 156px; 431 | } 432 | input.span1, 433 | textarea.span1, 434 | .uneditable-input.span1 { 435 | width: 56px; 436 | } 437 | .thumbnails { 438 | margin-left: -30px; 439 | } 440 | .thumbnails > li { 441 | margin-left: 30px; 442 | } 443 | .row-fluid .thumbnails { 444 | margin-left: 0; 445 | } 446 | } 447 | 448 | @media (min-width: 768px) and (max-width: 979px) { 449 | .row { 450 | margin-left: -20px; 451 | *zoom: 1; 452 | } 453 | .row:before, 454 | .row:after { 455 | display: table; 456 | line-height: 0; 457 | content: ""; 458 | } 459 | .row:after { 460 | clear: both; 461 | } 462 | [class*="span"] { 463 | float: left; 464 | min-height: 1px; 465 | margin-left: 20px; 466 | } 467 | .container, 468 | .navbar-static-top .container, 469 | .navbar-fixed-top .container, 470 | .navbar-fixed-bottom .container { 471 | width: 724px; 472 | } 473 | .span12 { 474 | width: 724px; 475 | } 476 | .span11 { 477 | width: 662px; 478 | } 479 | .span10 { 480 | width: 600px; 481 | } 482 | .span9 { 483 | width: 538px; 484 | } 485 | .span8 { 486 | width: 476px; 487 | } 488 | .span7 { 489 | width: 414px; 490 | } 491 | .span6 { 492 | width: 352px; 493 | } 494 | .span5 { 495 | width: 290px; 496 | } 497 | .span4 { 498 | width: 228px; 499 | } 500 | .span3 { 501 | width: 166px; 502 | } 503 | .span2 { 504 | width: 104px; 505 | } 506 | .span1 { 507 | width: 42px; 508 | } 509 | .offset12 { 510 | margin-left: 764px; 511 | } 512 | .offset11 { 513 | margin-left: 702px; 514 | } 515 | .offset10 { 516 | margin-left: 640px; 517 | } 518 | .offset9 { 519 | margin-left: 578px; 520 | } 521 | .offset8 { 522 | margin-left: 516px; 523 | } 524 | .offset7 { 525 | margin-left: 454px; 526 | } 527 | .offset6 { 528 | margin-left: 392px; 529 | } 530 | .offset5 { 531 | margin-left: 330px; 532 | } 533 | .offset4 { 534 | margin-left: 268px; 535 | } 536 | .offset3 { 537 | margin-left: 206px; 538 | } 539 | .offset2 { 540 | margin-left: 144px; 541 | } 542 | .offset1 { 543 | margin-left: 82px; 544 | } 545 | .row-fluid { 546 | width: 100%; 547 | *zoom: 1; 548 | } 549 | .row-fluid:before, 550 | .row-fluid:after { 551 | display: table; 552 | line-height: 0; 553 | content: ""; 554 | } 555 | .row-fluid:after { 556 | clear: both; 557 | } 558 | .row-fluid [class*="span"] { 559 | display: block; 560 | float: left; 561 | width: 100%; 562 | min-height: 30px; 563 | margin-left: 2.7624309392265194%; 564 | *margin-left: 2.709239449864817%; 565 | -webkit-box-sizing: border-box; 566 | -moz-box-sizing: border-box; 567 | box-sizing: border-box; 568 | } 569 | .row-fluid [class*="span"]:first-child { 570 | margin-left: 0; 571 | } 572 | .row-fluid .controls-row [class*="span"] + [class*="span"] { 573 | margin-left: 2.7624309392265194%; 574 | } 575 | .row-fluid .span12 { 576 | width: 100%; 577 | *width: 99.94680851063829%; 578 | } 579 | .row-fluid .span11 { 580 | width: 91.43646408839778%; 581 | *width: 91.38327259903608%; 582 | } 583 | .row-fluid .span10 { 584 | width: 82.87292817679558%; 585 | *width: 82.81973668743387%; 586 | } 587 | .row-fluid .span9 { 588 | width: 74.30939226519337%; 589 | *width: 74.25620077583166%; 590 | } 591 | .row-fluid .span8 { 592 | width: 65.74585635359117%; 593 | *width: 65.69266486422946%; 594 | } 595 | .row-fluid .span7 { 596 | width: 57.18232044198895%; 597 | *width: 57.12912895262725%; 598 | } 599 | .row-fluid .span6 { 600 | width: 48.61878453038674%; 601 | *width: 48.56559304102504%; 602 | } 603 | .row-fluid .span5 { 604 | width: 40.05524861878453%; 605 | *width: 40.00205712942283%; 606 | } 607 | .row-fluid .span4 { 608 | width: 31.491712707182323%; 609 | *width: 31.43852121782062%; 610 | } 611 | .row-fluid .span3 { 612 | width: 22.92817679558011%; 613 | *width: 22.87498530621841%; 614 | } 615 | .row-fluid .span2 { 616 | width: 14.3646408839779%; 617 | *width: 14.311449394616199%; 618 | } 619 | .row-fluid .span1 { 620 | width: 5.801104972375691%; 621 | *width: 5.747913483013988%; 622 | } 623 | .row-fluid .offset12 { 624 | margin-left: 105.52486187845304%; 625 | *margin-left: 105.41847889972962%; 626 | } 627 | .row-fluid .offset12:first-child { 628 | margin-left: 102.76243093922652%; 629 | *margin-left: 102.6560479605031%; 630 | } 631 | .row-fluid .offset11 { 632 | margin-left: 96.96132596685082%; 633 | *margin-left: 96.8549429881274%; 634 | } 635 | .row-fluid .offset11:first-child { 636 | margin-left: 94.1988950276243%; 637 | *margin-left: 94.09251204890089%; 638 | } 639 | .row-fluid .offset10 { 640 | margin-left: 88.39779005524862%; 641 | *margin-left: 88.2914070765252%; 642 | } 643 | .row-fluid .offset10:first-child { 644 | margin-left: 85.6353591160221%; 645 | *margin-left: 85.52897613729868%; 646 | } 647 | .row-fluid .offset9 { 648 | margin-left: 79.8342541436464%; 649 | *margin-left: 79.72787116492299%; 650 | } 651 | .row-fluid .offset9:first-child { 652 | margin-left: 77.07182320441989%; 653 | *margin-left: 76.96544022569647%; 654 | } 655 | .row-fluid .offset8 { 656 | margin-left: 71.2707182320442%; 657 | *margin-left: 71.16433525332079%; 658 | } 659 | .row-fluid .offset8:first-child { 660 | margin-left: 68.50828729281768%; 661 | *margin-left: 68.40190431409427%; 662 | } 663 | .row-fluid .offset7 { 664 | margin-left: 62.70718232044199%; 665 | *margin-left: 62.600799341718584%; 666 | } 667 | .row-fluid .offset7:first-child { 668 | margin-left: 59.94475138121547%; 669 | *margin-left: 59.838368402492065%; 670 | } 671 | .row-fluid .offset6 { 672 | margin-left: 54.14364640883978%; 673 | *margin-left: 54.037263430116376%; 674 | } 675 | .row-fluid .offset6:first-child { 676 | margin-left: 51.38121546961326%; 677 | *margin-left: 51.27483249088986%; 678 | } 679 | .row-fluid .offset5 { 680 | margin-left: 45.58011049723757%; 681 | *margin-left: 45.47372751851417%; 682 | } 683 | .row-fluid .offset5:first-child { 684 | margin-left: 42.81767955801105%; 685 | *margin-left: 42.71129657928765%; 686 | } 687 | .row-fluid .offset4 { 688 | margin-left: 37.01657458563536%; 689 | *margin-left: 36.91019160691196%; 690 | } 691 | .row-fluid .offset4:first-child { 692 | margin-left: 34.25414364640884%; 693 | *margin-left: 34.14776066768544%; 694 | } 695 | .row-fluid .offset3 { 696 | margin-left: 28.45303867403315%; 697 | *margin-left: 28.346655695309746%; 698 | } 699 | .row-fluid .offset3:first-child { 700 | margin-left: 25.69060773480663%; 701 | *margin-left: 25.584224756083227%; 702 | } 703 | .row-fluid .offset2 { 704 | margin-left: 19.88950276243094%; 705 | *margin-left: 19.783119783707537%; 706 | } 707 | .row-fluid .offset2:first-child { 708 | margin-left: 17.12707182320442%; 709 | *margin-left: 17.02068884448102%; 710 | } 711 | .row-fluid .offset1 { 712 | margin-left: 11.32596685082873%; 713 | *margin-left: 11.219583872105325%; 714 | } 715 | .row-fluid .offset1:first-child { 716 | margin-left: 8.56353591160221%; 717 | *margin-left: 8.457152932878806%; 718 | } 719 | input, 720 | textarea, 721 | .uneditable-input { 722 | margin-left: 0; 723 | } 724 | .controls-row [class*="span"] + [class*="span"] { 725 | margin-left: 20px; 726 | } 727 | input.span12, 728 | textarea.span12, 729 | .uneditable-input.span12 { 730 | width: 710px; 731 | } 732 | input.span11, 733 | textarea.span11, 734 | .uneditable-input.span11 { 735 | width: 648px; 736 | } 737 | input.span10, 738 | textarea.span10, 739 | .uneditable-input.span10 { 740 | width: 586px; 741 | } 742 | input.span9, 743 | textarea.span9, 744 | .uneditable-input.span9 { 745 | width: 524px; 746 | } 747 | input.span8, 748 | textarea.span8, 749 | .uneditable-input.span8 { 750 | width: 462px; 751 | } 752 | input.span7, 753 | textarea.span7, 754 | .uneditable-input.span7 { 755 | width: 400px; 756 | } 757 | input.span6, 758 | textarea.span6, 759 | .uneditable-input.span6 { 760 | width: 338px; 761 | } 762 | input.span5, 763 | textarea.span5, 764 | .uneditable-input.span5 { 765 | width: 276px; 766 | } 767 | input.span4, 768 | textarea.span4, 769 | .uneditable-input.span4 { 770 | width: 214px; 771 | } 772 | input.span3, 773 | textarea.span3, 774 | .uneditable-input.span3 { 775 | width: 152px; 776 | } 777 | input.span2, 778 | textarea.span2, 779 | .uneditable-input.span2 { 780 | width: 90px; 781 | } 782 | input.span1, 783 | textarea.span1, 784 | .uneditable-input.span1 { 785 | width: 28px; 786 | } 787 | } 788 | 789 | @media (max-width: 767px) { 790 | body { 791 | padding-right: 20px; 792 | padding-left: 20px; 793 | } 794 | .navbar-fixed-top, 795 | .navbar-fixed-bottom, 796 | .navbar-static-top { 797 | margin-right: -20px; 798 | margin-left: -20px; 799 | } 800 | .container-fluid { 801 | padding: 0; 802 | } 803 | .dl-horizontal dt { 804 | float: none; 805 | width: auto; 806 | clear: none; 807 | text-align: left; 808 | } 809 | .dl-horizontal dd { 810 | margin-left: 0; 811 | } 812 | .container { 813 | width: auto; 814 | } 815 | .row-fluid { 816 | width: 100%; 817 | } 818 | .row, 819 | .thumbnails { 820 | margin-left: 0; 821 | } 822 | .thumbnails > li { 823 | float: none; 824 | margin-left: 0; 825 | } 826 | [class*="span"], 827 | .uneditable-input[class*="span"], 828 | .row-fluid [class*="span"] { 829 | display: block; 830 | float: none; 831 | width: 100%; 832 | margin-left: 0; 833 | -webkit-box-sizing: border-box; 834 | -moz-box-sizing: border-box; 835 | box-sizing: border-box; 836 | } 837 | .span12, 838 | .row-fluid .span12 { 839 | width: 100%; 840 | -webkit-box-sizing: border-box; 841 | -moz-box-sizing: border-box; 842 | box-sizing: border-box; 843 | } 844 | .row-fluid [class*="offset"]:first-child { 845 | margin-left: 0; 846 | } 847 | .input-large, 848 | .input-xlarge, 849 | .input-xxlarge, 850 | input[class*="span"], 851 | select[class*="span"], 852 | textarea[class*="span"], 853 | .uneditable-input { 854 | display: block; 855 | width: 100%; 856 | min-height: 30px; 857 | -webkit-box-sizing: border-box; 858 | -moz-box-sizing: border-box; 859 | box-sizing: border-box; 860 | } 861 | .input-prepend input, 862 | .input-append input, 863 | .input-prepend input[class*="span"], 864 | .input-append input[class*="span"] { 865 | display: inline-block; 866 | width: auto; 867 | } 868 | .controls-row [class*="span"] + [class*="span"] { 869 | margin-left: 0; 870 | } 871 | .modal { 872 | position: fixed; 873 | top: 20px; 874 | right: 20px; 875 | left: 20px; 876 | width: auto; 877 | margin: 0; 878 | } 879 | .modal.fade { 880 | top: -100px; 881 | } 882 | .modal.fade.in { 883 | top: 20px; 884 | } 885 | } 886 | 887 | @media (max-width: 480px) { 888 | .nav-collapse { 889 | -webkit-transform: translate3d(0, 0, 0); 890 | } 891 | .page-header h1 small { 892 | display: block; 893 | line-height: 20px; 894 | } 895 | input[type="checkbox"], 896 | input[type="radio"] { 897 | border: 1px solid #ccc; 898 | } 899 | .form-horizontal .control-label { 900 | float: none; 901 | width: auto; 902 | padding-top: 0; 903 | text-align: left; 904 | } 905 | .form-horizontal .controls { 906 | margin-left: 0; 907 | } 908 | .form-horizontal .control-list { 909 | padding-top: 0; 910 | } 911 | .form-horizontal .form-actions { 912 | padding-right: 10px; 913 | padding-left: 10px; 914 | } 915 | .media .pull-left, 916 | .media .pull-right { 917 | display: block; 918 | float: none; 919 | margin-bottom: 10px; 920 | } 921 | .media-object { 922 | margin-right: 0; 923 | margin-left: 0; 924 | } 925 | .modal { 926 | top: 10px; 927 | right: 10px; 928 | left: 10px; 929 | } 930 | .modal-header .close { 931 | padding: 10px; 932 | margin: -10px; 933 | } 934 | .carousel-caption { 935 | position: static; 936 | } 937 | } 938 | 939 | @media (max-width: 979px) { 940 | body { 941 | padding-top: 0; 942 | } 943 | .navbar-fixed-top, 944 | .navbar-fixed-bottom { 945 | position: static; 946 | } 947 | .navbar-fixed-top { 948 | margin-bottom: 20px; 949 | } 950 | .navbar-fixed-bottom { 951 | margin-top: 20px; 952 | } 953 | .navbar-fixed-top .navbar-inner, 954 | .navbar-fixed-bottom .navbar-inner { 955 | padding: 5px; 956 | } 957 | .navbar .container { 958 | width: auto; 959 | padding: 0; 960 | } 961 | .navbar .brand { 962 | padding-right: 10px; 963 | padding-left: 10px; 964 | margin: 0 0 0 -5px; 965 | } 966 | .nav-collapse { 967 | clear: both; 968 | } 969 | .nav-collapse .nav { 970 | float: none; 971 | margin: 0 0 10px; 972 | } 973 | .nav-collapse .nav > li { 974 | float: none; 975 | } 976 | .nav-collapse .nav > li > a { 977 | margin-bottom: 2px; 978 | } 979 | .nav-collapse .nav > .divider-vertical { 980 | display: none; 981 | } 982 | .nav-collapse .nav .nav-header { 983 | color: #777777; 984 | text-shadow: none; 985 | } 986 | .nav-collapse .nav > li > a, 987 | .nav-collapse .dropdown-menu a { 988 | padding: 9px 15px; 989 | font-weight: bold; 990 | color: #777777; 991 | -webkit-border-radius: 3px; 992 | -moz-border-radius: 3px; 993 | border-radius: 3px; 994 | } 995 | .nav-collapse .btn { 996 | padding: 4px 10px 4px; 997 | font-weight: normal; 998 | -webkit-border-radius: 4px; 999 | -moz-border-radius: 4px; 1000 | border-radius: 4px; 1001 | } 1002 | .nav-collapse .dropdown-menu li + li a { 1003 | margin-bottom: 2px; 1004 | } 1005 | .nav-collapse .nav > li > a:hover, 1006 | .nav-collapse .dropdown-menu a:hover { 1007 | background-color: #f2f2f2; 1008 | } 1009 | .navbar-inverse .nav-collapse .nav > li > a, 1010 | .navbar-inverse .nav-collapse .dropdown-menu a { 1011 | color: #999999; 1012 | } 1013 | .navbar-inverse .nav-collapse .nav > li > a:hover, 1014 | .navbar-inverse .nav-collapse .dropdown-menu a:hover { 1015 | background-color: #111111; 1016 | } 1017 | .nav-collapse.in .btn-group { 1018 | padding: 0; 1019 | margin-top: 5px; 1020 | } 1021 | .nav-collapse .dropdown-menu { 1022 | position: static; 1023 | top: auto; 1024 | left: auto; 1025 | display: none; 1026 | float: none; 1027 | max-width: none; 1028 | padding: 0; 1029 | margin: 0 15px; 1030 | background-color: transparent; 1031 | border: none; 1032 | -webkit-border-radius: 0; 1033 | -moz-border-radius: 0; 1034 | border-radius: 0; 1035 | -webkit-box-shadow: none; 1036 | -moz-box-shadow: none; 1037 | box-shadow: none; 1038 | } 1039 | .nav-collapse .open > .dropdown-menu { 1040 | display: block; 1041 | } 1042 | .nav-collapse .dropdown-menu:before, 1043 | .nav-collapse .dropdown-menu:after { 1044 | display: none; 1045 | } 1046 | .nav-collapse .dropdown-menu .divider { 1047 | display: none; 1048 | } 1049 | .nav-collapse .nav > li > .dropdown-menu:before, 1050 | .nav-collapse .nav > li > .dropdown-menu:after { 1051 | display: none; 1052 | } 1053 | .nav-collapse .navbar-form, 1054 | .nav-collapse .navbar-search { 1055 | float: none; 1056 | padding: 10px 15px; 1057 | margin: 10px 0; 1058 | border-top: 1px solid #f2f2f2; 1059 | border-bottom: 1px solid #f2f2f2; 1060 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1061 | -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1062 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); 1063 | } 1064 | .navbar-inverse .nav-collapse .navbar-form, 1065 | .navbar-inverse .nav-collapse .navbar-search { 1066 | border-top-color: #111111; 1067 | border-bottom-color: #111111; 1068 | } 1069 | .navbar .nav-collapse .nav.pull-right { 1070 | float: none; 1071 | margin-left: 0; 1072 | } 1073 | .nav-collapse, 1074 | .nav-collapse.collapse { 1075 | height: 0; 1076 | overflow: hidden; 1077 | } 1078 | .navbar .btn-navbar { 1079 | display: block; 1080 | } 1081 | .navbar-static .navbar-inner { 1082 | padding-right: 10px; 1083 | padding-left: 10px; 1084 | } 1085 | } 1086 | 1087 | @media (min-width: 980px) { 1088 | .nav-collapse.collapse { 1089 | height: auto !important; 1090 | overflow: visible !important; 1091 | } 1092 | } 1093 | -------------------------------------------------------------------------------- /public/bootstrap/css/bootstrap-responsive.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.2.2 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */@-ms-viewport{width:device-width}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .dropdown-menu a:hover{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:hover{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}} 10 | -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronpk/IndieNews/be5543af847ef6974d8ccc1a7d533536e3e80023/public/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaronpk/IndieNews/be5543af847ef6974d8ccc1a7d533536e3e80023/public/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap.js by @fat & @mdo 3 | * Copyright 2012 Twitter, Inc. 4 | * http://www.apache.org/licenses/LICENSE-2.0.txt 5 | */ 6 | !function($){"use strict";$(function(){$.support.transition=function(){var transitionEnd=function(){var name,el=document.createElement("bootstrap"),transEndEventNames={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(name in transEndEventNames)if(void 0!==el.style[name])return transEndEventNames[name]}();return transitionEnd&&{end:transitionEnd}}()})}(window.jQuery),!function($){"use strict";var dismiss='[data-dismiss="alert"]',Alert=function(el){$(el).on("click",dismiss,this.close)};Alert.prototype.close=function(e){function removeElement(){$parent.trigger("closed").remove()}var $parent,$this=$(this),selector=$this.attr("data-target");selector||(selector=$this.attr("href"),selector=selector&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),e&&e.preventDefault(),$parent.length||($parent=$this.hasClass("alert")?$this:$this.parent()),$parent.trigger(e=$.Event("close")),e.isDefaultPrevented()||($parent.removeClass("in"),$.support.transition&&$parent.hasClass("fade")?$parent.on($.support.transition.end,removeElement):removeElement())};var old=$.fn.alert;$.fn.alert=function(option){return this.each(function(){var $this=$(this),data=$this.data("alert");data||$this.data("alert",data=new Alert(this)),"string"==typeof option&&data[option].call($this)})},$.fn.alert.Constructor=Alert,$.fn.alert.noConflict=function(){return $.fn.alert=old,this},$(document).on("click.alert.data-api",dismiss,Alert.prototype.close)}(window.jQuery),!function($){"use strict";var Button=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.button.defaults,options)};Button.prototype.setState=function(state){var d="disabled",$el=this.$element,data=$el.data(),val=$el.is("input")?"val":"html";state+="Text",data.resetText||$el.data("resetText",$el[val]()),$el[val](data[state]||this.options[state]),setTimeout(function(){"loadingText"==state?$el.addClass(d).attr(d,d):$el.removeClass(d).removeAttr(d)},0)},Button.prototype.toggle=function(){var $parent=this.$element.closest('[data-toggle="buttons-radio"]');$parent&&$parent.find(".active").removeClass("active"),this.$element.toggleClass("active")};var old=$.fn.button;$.fn.button=function(option){return this.each(function(){var $this=$(this),data=$this.data("button"),options="object"==typeof option&&option;data||$this.data("button",data=new Button(this,options)),"toggle"==option?data.toggle():option&&data.setState(option)})},$.fn.button.defaults={loadingText:"loading..."},$.fn.button.Constructor=Button,$.fn.button.noConflict=function(){return $.fn.button=old,this},$(document).on("click.button.data-api","[data-toggle^=button]",function(e){var $btn=$(e.target);$btn.hasClass("btn")||($btn=$btn.closest(".btn")),$btn.button("toggle")})}(window.jQuery),!function($){"use strict";var Carousel=function(element,options){this.$element=$(element),this.options=options,"hover"==this.options.pause&&this.$element.on("mouseenter",$.proxy(this.pause,this)).on("mouseleave",$.proxy(this.cycle,this))};Carousel.prototype={cycle:function(e){return e||(this.paused=!1),this.options.interval&&!this.paused&&(this.interval=setInterval($.proxy(this.next,this),this.options.interval)),this},to:function(pos){var $active=this.$element.find(".item.active"),children=$active.parent().children(),activePos=children.index($active),that=this;if(!(pos>children.length-1||0>pos))return this.sliding?this.$element.one("slid",function(){that.to(pos)}):activePos==pos?this.pause().cycle():this.slide(pos>activePos?"next":"prev",$(children[pos]))},pause:function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&$.support.transition.end&&(this.$element.trigger($.support.transition.end),this.cycle()),clearInterval(this.interval),this.interval=null,this},next:function(){return this.sliding?void 0:this.slide("next")},prev:function(){return this.sliding?void 0:this.slide("prev")},slide:function(type,next){var e,$active=this.$element.find(".item.active"),$next=next||$active[type](),isCycling=this.interval,direction="next"==type?"left":"right",fallback="next"==type?"first":"last",that=this;if(this.sliding=!0,isCycling&&this.pause(),$next=$next.length?$next:this.$element.find(".item")[fallback](),e=$.Event("slide",{relatedTarget:$next[0]}),!$next.hasClass("active")){if($.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(e),e.isDefaultPrevented())return;$next.addClass(type),$next[0].offsetWidth,$active.addClass(direction),$next.addClass(direction),this.$element.one($.support.transition.end,function(){$next.removeClass([type,direction].join(" ")).addClass("active"),$active.removeClass(["active",direction].join(" ")),that.sliding=!1,setTimeout(function(){that.$element.trigger("slid")},0)})}else{if(this.$element.trigger(e),e.isDefaultPrevented())return;$active.removeClass("active"),$next.addClass("active"),this.sliding=!1,this.$element.trigger("slid")}return isCycling&&this.cycle(),this}}};var old=$.fn.carousel;$.fn.carousel=function(option){return this.each(function(){var $this=$(this),data=$this.data("carousel"),options=$.extend({},$.fn.carousel.defaults,"object"==typeof option&&option),action="string"==typeof option?option:options.slide;data||$this.data("carousel",data=new Carousel(this,options)),"number"==typeof option?data.to(option):action?data[action]():options.interval&&data.cycle()})},$.fn.carousel.defaults={interval:5e3,pause:"hover"},$.fn.carousel.Constructor=Carousel,$.fn.carousel.noConflict=function(){return $.fn.carousel=old,this},$(document).on("click.carousel.data-api","[data-slide]",function(e){var href,$this=$(this),$target=$($this.attr("data-target")||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,"")),options=$.extend({},$target.data(),$this.data());$target.carousel(options),e.preventDefault()})}(window.jQuery),!function($){"use strict";var Collapse=function(element,options){this.$element=$(element),this.options=$.extend({},$.fn.collapse.defaults,options),this.options.parent&&(this.$parent=$(this.options.parent)),this.options.toggle&&this.toggle()};Collapse.prototype={constructor:Collapse,dimension:function(){var hasWidth=this.$element.hasClass("width");return hasWidth?"width":"height"},show:function(){var dimension,scroll,actives,hasData;if(!this.transitioning){if(dimension=this.dimension(),scroll=$.camelCase(["scroll",dimension].join("-")),actives=this.$parent&&this.$parent.find("> .accordion-group > .in"),actives&&actives.length){if(hasData=actives.data("collapse"),hasData&&hasData.transitioning)return;actives.collapse("hide"),hasData||actives.data("collapse",null)}this.$element[dimension](0),this.transition("addClass",$.Event("show"),"shown"),$.support.transition&&this.$element[dimension](this.$element[0][scroll])}},hide:function(){var dimension;this.transitioning||(dimension=this.dimension(),this.reset(this.$element[dimension]()),this.transition("removeClass",$.Event("hide"),"hidden"),this.$element[dimension](0))},reset:function(size){var dimension=this.dimension();return this.$element.removeClass("collapse")[dimension](size||"auto")[0].offsetWidth,this.$element[null!==size?"addClass":"removeClass"]("collapse"),this},transition:function(method,startEvent,completeEvent){var that=this,complete=function(){"show"==startEvent.type&&that.reset(),that.transitioning=0,that.$element.trigger(completeEvent)};this.$element.trigger(startEvent),startEvent.isDefaultPrevented()||(this.transitioning=1,this.$element[method]("in"),$.support.transition&&this.$element.hasClass("collapse")?this.$element.one($.support.transition.end,complete):complete())},toggle:function(){this[this.$element.hasClass("in")?"hide":"show"]()}};var old=$.fn.collapse;$.fn.collapse=function(option){return this.each(function(){var $this=$(this),data=$this.data("collapse"),options="object"==typeof option&&option;data||$this.data("collapse",data=new Collapse(this,options)),"string"==typeof option&&data[option]()})},$.fn.collapse.defaults={toggle:!0},$.fn.collapse.Constructor=Collapse,$.fn.collapse.noConflict=function(){return $.fn.collapse=old,this},$(document).on("click.collapse.data-api","[data-toggle=collapse]",function(e){var href,$this=$(this),target=$this.attr("data-target")||e.preventDefault()||(href=$this.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),option=$(target).data("collapse")?"toggle":$this.data();$this[$(target).hasClass("in")?"addClass":"removeClass"]("collapsed"),$(target).collapse(option)})}(window.jQuery),!function($){"use strict";function clearMenus(){$(toggle).each(function(){getParent($(this)).removeClass("open")})}function getParent($this){var $parent,selector=$this.attr("data-target");return selector||(selector=$this.attr("href"),selector=selector&&/#/.test(selector)&&selector.replace(/.*(?=#[^\s]*$)/,"")),$parent=$(selector),$parent.length||($parent=$this.parent()),$parent}var toggle="[data-toggle=dropdown]",Dropdown=function(element){var $el=$(element).on("click.dropdown.data-api",this.toggle);$("html").on("click.dropdown.data-api",function(){$el.parent().removeClass("open")})};Dropdown.prototype={constructor:Dropdown,toggle:function(){var $parent,isActive,$this=$(this);if(!$this.is(".disabled, :disabled"))return $parent=getParent($this),isActive=$parent.hasClass("open"),clearMenus(),isActive||$parent.toggleClass("open"),$this.focus(),!1},keydown:function(e){var $this,$items,$parent,isActive,index;if(/(38|40|27)/.test(e.keyCode)&&($this=$(this),e.preventDefault(),e.stopPropagation(),!$this.is(".disabled, :disabled"))){if($parent=getParent($this),isActive=$parent.hasClass("open"),!isActive||isActive&&27==e.keyCode)return $this.click();$items=$("[role=menu] li:not(.divider):visible a",$parent),$items.length&&(index=$items.index($items.filter(":focus")),38==e.keyCode&&index>0&&index--,40==e.keyCode&&$items.length-1>index&&index++,~index||(index=0),$items.eq(index).focus())}}};var old=$.fn.dropdown;$.fn.dropdown=function(option){return this.each(function(){var $this=$(this),data=$this.data("dropdown");data||$this.data("dropdown",data=new Dropdown(this)),"string"==typeof option&&data[option].call($this)})},$.fn.dropdown.Constructor=Dropdown,$.fn.dropdown.noConflict=function(){return $.fn.dropdown=old,this},$(document).on("click.dropdown.data-api touchstart.dropdown.data-api",clearMenus).on("click.dropdown touchstart.dropdown.data-api",".dropdown form",function(e){e.stopPropagation()}).on("touchstart.dropdown.data-api",".dropdown-menu",function(e){e.stopPropagation()}).on("click.dropdown.data-api touchstart.dropdown.data-api",toggle,Dropdown.prototype.toggle).on("keydown.dropdown.data-api touchstart.dropdown.data-api",toggle+", [role=menu]",Dropdown.prototype.keydown)}(window.jQuery),!function($){"use strict";var Modal=function(element,options){this.options=options,this.$element=$(element).delegate('[data-dismiss="modal"]',"click.dismiss.modal",$.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};Modal.prototype={constructor:Modal,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var that=this,e=$.Event("show");this.$element.trigger(e),this.isShown||e.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.backdrop(function(){var transition=$.support.transition&&that.$element.hasClass("fade");that.$element.parent().length||that.$element.appendTo(document.body),that.$element.show(),transition&&that.$element[0].offsetWidth,that.$element.addClass("in").attr("aria-hidden",!1),that.enforceFocus(),transition?that.$element.one($.support.transition.end,function(){that.$element.focus().trigger("shown")}):that.$element.focus().trigger("shown")}))},hide:function(e){e&&e.preventDefault(),e=$.Event("hide"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),$(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),$.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal())},enforceFocus:function(){var that=this;$(document).on("focusin.modal",function(e){that.$element[0]===e.target||that.$element.has(e.target).length||that.$element.focus()})},escape:function(){var that=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(e){27==e.which&&that.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var that=this,timeout=setTimeout(function(){that.$element.off($.support.transition.end),that.hideModal()},500);this.$element.one($.support.transition.end,function(){clearTimeout(timeout),that.hideModal()})},hideModal:function(){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(callback){var animate=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var doAnimate=$.support.transition&&animate;this.$backdrop=$('').appendTo(document.body),this.$backdrop.click("static"==this.options.backdrop?$.proxy(this.$element[0].focus,this.$element[0]):$.proxy(this.hide,this)),doAnimate&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),doAnimate?this.$backdrop.one($.support.transition.end,callback):callback()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),$.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one($.support.transition.end,$.proxy(this.removeBackdrop,this)):this.removeBackdrop()):callback&&callback()}};var old=$.fn.modal;$.fn.modal=function(option){return this.each(function(){var $this=$(this),data=$this.data("modal"),options=$.extend({},$.fn.modal.defaults,$this.data(),"object"==typeof option&&option);data||$this.data("modal",data=new Modal(this,options)),"string"==typeof option?data[option]():options.show&&data.show()})},$.fn.modal.defaults={backdrop:!0,keyboard:!0,show:!0},$.fn.modal.Constructor=Modal,$.fn.modal.noConflict=function(){return $.fn.modal=old,this},$(document).on("click.modal.data-api",'[data-toggle="modal"]',function(e){var $this=$(this),href=$this.attr("href"),$target=$($this.attr("data-target")||href&&href.replace(/.*(?=#[^\s]+$)/,"")),option=$target.data("modal")?"toggle":$.extend({remote:!/#/.test(href)&&href},$target.data(),$this.data());e.preventDefault(),$target.modal(option).one("hide",function(){$this.focus()})})}(window.jQuery),!function($){"use strict";var Tooltip=function(element,options){this.init("tooltip",element,options)};Tooltip.prototype={constructor:Tooltip,init:function(type,element,options){var eventIn,eventOut;this.type=type,this.$element=$(element),this.options=this.getOptions(options),this.enabled=!0,"click"==this.options.trigger?this.$element.on("click."+this.type,this.options.selector,$.proxy(this.toggle,this)):"manual"!=this.options.trigger&&(eventIn="hover"==this.options.trigger?"mouseenter":"focus",eventOut="hover"==this.options.trigger?"mouseleave":"blur",this.$element.on(eventIn+"."+this.type,this.options.selector,$.proxy(this.enter,this)),this.$element.on(eventOut+"."+this.type,this.options.selector,$.proxy(this.leave,this))),this.options.selector?this._options=$.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},getOptions:function(options){return options=$.extend({},$.fn[this.type].defaults,options,this.$element.data()),options.delay&&"number"==typeof options.delay&&(options.delay={show:options.delay,hide:options.delay}),options},enter:function(e){var self=$(e.currentTarget)[this.type](this._options).data(this.type);return self.options.delay&&self.options.delay.show?(clearTimeout(this.timeout),self.hoverState="in",this.timeout=setTimeout(function(){"in"==self.hoverState&&self.show()},self.options.delay.show),void 0):self.show()},leave:function(e){var self=$(e.currentTarget)[this.type](this._options).data(this.type);return this.timeout&&clearTimeout(this.timeout),self.options.delay&&self.options.delay.hide?(self.hoverState="out",this.timeout=setTimeout(function(){"out"==self.hoverState&&self.hide()},self.options.delay.hide),void 0):self.hide()},show:function(){var $tip,inside,pos,actualWidth,actualHeight,placement,tp;if(this.hasContent()&&this.enabled){switch($tip=this.tip(),this.setContent(),this.options.animation&&$tip.addClass("fade"),placement="function"==typeof this.options.placement?this.options.placement.call(this,$tip[0],this.$element[0]):this.options.placement,inside=/in/.test(placement),$tip.detach().css({top:0,left:0,display:"block"}).insertAfter(this.$element),pos=this.getPosition(inside),actualWidth=$tip[0].offsetWidth,actualHeight=$tip[0].offsetHeight,inside?placement.split(" ")[1]:placement){case"bottom":tp={top:pos.top+pos.height,left:pos.left+pos.width/2-actualWidth/2};break;case"top":tp={top:pos.top-actualHeight,left:pos.left+pos.width/2-actualWidth/2};break;case"left":tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left-actualWidth};break;case"right":tp={top:pos.top+pos.height/2-actualHeight/2,left:pos.left+pos.width}}$tip.offset(tp).addClass(placement).addClass("in")}},setContent:function(){var $tip=this.tip(),title=this.getTitle();$tip.find(".tooltip-inner")[this.options.html?"html":"text"](title),$tip.removeClass("fade in top bottom left right")},hide:function(){function removeWithAnimation(){var timeout=setTimeout(function(){$tip.off($.support.transition.end).detach()},500);$tip.one($.support.transition.end,function(){clearTimeout(timeout),$tip.detach()})}var $tip=this.tip();return $tip.removeClass("in"),$.support.transition&&this.$tip.hasClass("fade")?removeWithAnimation():$tip.detach(),this},fixTitle:function(){var $e=this.$element;($e.attr("title")||"string"!=typeof $e.attr("data-original-title"))&&$e.attr("data-original-title",$e.attr("title")||"").removeAttr("title")},hasContent:function(){return this.getTitle()},getPosition:function(inside){return $.extend({},inside?{top:0,left:0}:this.$element.offset(),{width:this.$element[0].offsetWidth,height:this.$element[0].offsetHeight})},getTitle:function(){var title,$e=this.$element,o=this.options;return title=$e.attr("data-original-title")||("function"==typeof o.title?o.title.call($e[0]):o.title)},tip:function(){return this.$tip=this.$tip||$(this.options.template)},validate:function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled=!this.enabled},toggle:function(e){var self=$(e.currentTarget)[this.type](this._options).data(this.type);self[self.tip().hasClass("in")?"hide":"show"]()},destroy:function(){this.hide().$element.off("."+this.type).removeData(this.type)}};var old=$.fn.tooltip;$.fn.tooltip=function(option){return this.each(function(){var $this=$(this),data=$this.data("tooltip"),options="object"==typeof option&&option;data||$this.data("tooltip",data=new Tooltip(this,options)),"string"==typeof option&&data[option]()})},$.fn.tooltip.Constructor=Tooltip,$.fn.tooltip.defaults={animation:!0,placement:"top",selector:!1,template:'
= strftime('%A',strtotime(sprintf(($start==1?'2007-10':'2008-06').'-%02d',$i+1))) ?> | 9 | 10 |
---|
'."\n"; 20 | } 21 | } 22 | for($i = 1; $i <= $date->format('t'); $i++) { 23 | $thisDay = mktime(0,0,0,$month,$i,$year); 24 | if(strftime('%w',$thisDay) == $start) 25 | echo ' | '."\n";
31 |
32 | echo ' ' . $i . ' ' . "\n";
33 |
34 | if(array_key_exists($i, $calendar)) {
35 | foreach($calendar[$i] as $post) {
36 | echo '';
37 | echo '';
38 | if(shouldDisplayPostName($post->title)) {
39 | echo htmlspecialchars($post->title);
40 | } else {
41 | echo display_url($post->href);
42 | }
43 | echo ''."\n";
44 | if(shouldDisplayPostName($post->title))
45 | echo ' ';
46 | echo ' ';
47 | }
48 | }
49 |
50 | echo ' | '."\n";
51 | if( strftime(($start==1?'%u':'%w'),$thisDay) == ($start==1?7:6) )
52 | echo ''."\n";
53 | }
54 | if( $lastDayOfWeek < ($start==1?7:6) )
55 | {
56 | for( $i=$lastDayOfWeek; $i<($start==1?7:6); $i++ )
57 | {
58 | echo ''."\n"; 59 | } 60 | echo ''."\n"; 61 | } 62 | ?> 63 | |
In order to comment on a post, you do not need an IndieNews account. Instead, you 7 | can only submit comments as posts from your own site, by linking to the IndieNews page 8 | you wish to comment on and sending a notification using the 9 | webmention protocol!
10 | 11 | 12 |Create a new post on your site, and mark it up with the Microformats markup for 15 | an h-entry.
16 | 17 | 18 | 19 |Somewhere in the h-entry, add a link to the original post you are commenting on 22 | (not the IndieNews URL) with the class "u-in-reply-to". 23 | This usually looks something like the following:
24 | 25 |<a href="http://aaronparecki.com/notes/2013/04/25/1" class="u-in-reply-to" rel="in-reply-to">
26 | In Reply To @aaronpk
27 | </a>
28 |
29 |
30 |
31 | Inside the h-entry, add a link to the IndieNews URL for the post with the class 34 | u-syndication. This usually 35 | looks something like the following:
36 | 37 |<a href="https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1" class="u-syndication" rel="syndication">Also posted on IndieNews</a>
38 |
39 | You can construct the IndieNews URL before it's posted to IndieNews by following the 40 | convention IndieNews uses for building its permalinks. Follow the example above, or 41 | read the full instructions on constructing post URLs. 42 | 43 | 44 | 45 |
Make a POST request to news.indieweb.org/webmention
with two parameters,
50 | source
and target
, where target is
51 | https://news.indieweb.org/post/example.com/100
and source is
52 | http://example.com/100
assuming you are submitting a page on your site with
53 | the url http://example.com/100
.
POST /webmention HTTP/1.1
56 | Host: news.indieweb.org
57 |
58 | target=https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1/original-post-discovery
59 | &source=http://aaronparecki.com/notes/2013/04/25/1/original-post-discovery
60 |
61 |
62 | {
65 | "result": "success",
66 | "notices": [
67 | ],
68 | "data": {
69 | "title": "A demonstration of Original Post Discovery #indieweb",
70 | "author": "aaronparecki.com",
71 | "date": "2013-04-26T03:22:39+00:00"
72 | },
73 | "source": "http://aaronparecki.com/notes/2013/04/25/1/original-post-discovery",
74 | "target": "https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1/original-post-discovery",
75 | "href": "https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1/original-post-discovery"
76 | }
77 |
78 |
79 | This webmention endpoint returns more data than is technically required for a WebMention to succeed. It will 80 | return data that is useful for debugging purposes while you're initially trying it out.
81 | 82 |result
- Will be equal to "success" if the submission was acceptednotices
- An array of string messages if there was anything that needs attention in your submission. These are not errors, but will indicate if microformat markup was not present or invalid.data
- This object shows the values extracted from the page, including title, author and date.source
- The source URL sent in the initial request.target
- The target URL sent in the initial request.href
- The permalink to this submission on news.indieweb.org.canonical
- If you accidentally linked your "in-reply-to" to the IndieNews URL, this field will tell you the canonical URL of the post you were actually replying to.curl https://news.indieweb.org/webmention -i \
96 | -d target=https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1/original-post-discovery \
97 | -d source=http://aaronparecki.com/notes/2013/04/25/1/original-post-discovery
98 |
99 |
100 | <?php
102 | $ch = curl_init("https://news.indieweb.org/webmention");
103 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
104 | curl_setopt($ch, CURLOPT_POST, true);
105 | curl_setopt($ch, CURLOPT_POSTFIELDS, array(
106 | 'target' => 'https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1/original-post-discovery',
107 | 'source' => 'http://aaronparecki.com/notes/2013/04/25/1/original-post-discovery'
108 | ));
109 | echo curl_exec($ch);
110 | ?>
111 |
112 | require 'rest-client'
114 | require 'json'
115 |
116 | data = JSON.parse RestClient.post "https://news.indieweb.org/webmention", {
117 | 'target' => 'https://news.indieweb.org/post/aaronparecki.com/notes/2013/04/25/1/original-post-discovery',
118 | 'source' => 'http://aaronparecki.com/notes/2013/04/25/1/original-post-discovery'
119 | }
120 | jj data
121 |
122 |
123 | If you update the post (for example trying to debug the microformats markup, or changing the post 126 | title), you can re-send the webmention. The existing post will be updated with the new information found.
127 | 128 | 129 |Your page must be marked up with an h-entry 132 | or an h-event, IndieNews will 133 | use the name in the entry as the title of the submission.
134 | 135 |If an h-card is present, 136 | author information will be pulled from there, otherwise it will fall back to using the domain name as the author.
137 | 138 | 139 |If you use a client which automatically sends pingbacks to 142 | any links found in the post, then you can use the same flow as the WebMention flow but send a Pingback instead! 143 | You can find the pingback endpoint using the normal pingback discovery mechanism.
144 | 145 |Note that the rich debugging response will not be present in the pingback reply.
146 | 147 |= __('IndieNews is a community-curated list of articles relevant to the {0}Indie Web{1}.', ['', '']) ?>
4 |Want to see a new language? You can add a translation as a pull request! Copy the Locale file to a new folder, fill it out for your language, and submit a pull request!
17 |= __('People who have submitted posts in the last year') ?>
7 | 8 |= __('There are no posts submitted to the English feed yet! As soon as you {0}submit a post{1} it will show up here.', ['','']) ?>
22 |IndieNews is a community-curated list of articles relevant to the Indie Web.
4 |In order to submit a post, you do not need an IndieNews account. Instead, you can only submit 12 | posts from your own site by linking to the IndieNews site and sending a notification 13 | using the webmention protocol!
14 | 15 | 16 |Create a new post on your site, and mark it up with the Microformats markup for 19 | an h-entry.
20 | 21 |If you are submitting your own post, that's all you have to do.
22 | 23 |To submit someone else's post, you can post a bookmark on your site and submit that 24 | URL to IndieNews! Post an h-entry as normal, then include a 25 | u-bookmark-of property 26 | linking to the actual URL you want to submit.
27 | 28 | 29 |Inside the h-entry, add a link to the IndieNews home page for your language with the class 32 | u-syndication or 33 | u-category. This usually 34 | looks something like the following:
35 | 36 |<a href="https://news.indieweb.org/en" class="u-syndication">
37 | Also posted on IndieNews
38 | </a>
39 |
40 | <a href="https://news.indieweb.org/en" class="u-category">#indienews</a>
41 |
42 |
43 | If your website sends Webmentions automatically, then you don't need to worry about this step. Otherwise, follow the instructions below to send a Webmention, or just use the Webmention form available at the IndieNews Webmention endpoint.
46 | 47 |Make a POST request to https://news.indieweb.org/en/webmention
with two parameters,
50 | source
and target
, where target is
51 | https://news.indieweb.org/en
and source is
52 | http://example.com/100
assuming you are submitting a page on your site with
53 | the url http://example.com/100
.
Note that each language's home page has a unique Webmention endpoint, so you should 56 | do the Webmention endpoint discovery as normal to find it.
57 | 58 |POST /en/webmention HTTP/1.1
59 | Host: news.indieweb.org
60 |
61 | target=https://news.indieweb.org/en
62 | &source=https://aaronparecki.com/2013/04/25/4/original-post-discovery
63 |
64 |
65 |
66 | HTTP/1.1 201 Created
69 | Location: https://news.indieweb.org/en/aaronparecki.com/2013/04/25/4/original-post-discovery
70 |
71 | {
72 | "result": "success",
73 | "notices": [
74 | ],
75 | "data": {
76 | "title": "A demonstration of Original Post Discovery #indieweb",
77 | "author": "aaronparecki.com",
78 | "date": "2013-04-26T03:22:39+00:00"
79 | },
80 | "source": "https://aaronparecki.com/2013/04/25/4/original-post-discovery",
81 | "url": "https://news.indieweb.org/en/aaronparecki.com/2013/04/25/4/original-post-discovery"
82 | }
83 |
84 |
85 | You can find the permalink of your syndication by looking for the Location
header in the response. You can then update your post with that URL so that your post always links to the IndieNews permalink instead of the IndieNews home page.
This webmention endpoint also returns more data that is useful for debugging purposes while you're initially trying it out.
88 | 89 |result
- Will be equal to "success" if the submission was acceptednotices
- An array of string messages if there was anything that needs attention in your submission. These are not errors, but will indicate if microformat markup was not present or invalid.data
- This object shows the values extracted from the page, including title, author and date.source
- The source URL sent in the initial requesturl
- The permalink to this submission on news.indieweb.org.curl https://news.indieweb.org/en/webmention -i \
101 | -d target=https://news.indieweb.org/en \
102 | -d source=https://aaronparecki.com/2013/04/25/4/original-post-discovery
103 |
104 |
105 | <?php
107 | $ch = curl_init("https://news.indieweb.org/en/webmention");
108 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
109 | curl_setopt($ch, CURLOPT_POST, true);
110 | curl_setopt($ch, CURLOPT_POSTFIELDS, array(
111 | 'target' => 'https://news.indieweb.org/en',
112 | 'source' => 'https://aaronparecki.com/2013/04/25/4/original-post-discovery'
113 | ));
114 | echo curl_exec($ch);
115 | ?>
116 |
117 | require 'rest-client'
119 | require 'json'
120 |
121 | data = JSON.parse RestClient.post "https://news.indieweb.org/en/webmention", {
122 | 'target' => 'https://news.indieweb.org/en',
123 | 'source' => 'https://aaronparecki.com/2013/04/25/4/original-post-discovery'
124 | }
125 | jj data
126 |
127 |
128 | If you update the post (for example trying to debug the microformats markup, or changing the post 131 | title), you can re-send the webmention. The existing IndieNews post will be updated with the new information found.
132 | 133 | 134 |Your page must be marked up with an h-entry 137 | or an h-event, IndieNews will 138 | use the name in the entry as the title of the submission.
139 | 140 |If an h-card is present, 141 | author information will be pulled from there, otherwise it will fall back to using the domain name as the author.
142 | 143 | 144 |If you use a client which automatically sends pingbacks to 147 | any links found in the post, then you can use the same flow as the WebMention flow but send a Pingback instead! 148 | You can find the pingback endpoint using the normal pingback discovery mechanism.
149 | 150 |Note that the rich debugging response will not be present in the pingback reply.
151 | 152 |= __('Write a post on your own site, and mark it up with the Microformats markup for an {0}.', 'h-entry') ?>
8 | 9 |= __('To submit someone else\'s post, create a {0} post on your site linking to it.', 'bookmark') ?>
10 | 11 |= __('Somewhere on the page you are submitting, add a {0} or {1} link to {2}the IndieNews home page{3} for your language.', ['u-syndication', 'u-category', '', '']) ?>
14 | 15 |= __('If your website sends {0} automatically, then your post will be submitted as soon as you post it to your website! Otherwise, you can enter your post\'s URL below.', ['Webmentions']) ?> 18 | 19 |
= __('IndieNews will fetch the page from your site and look for the {1} markup to find the post title and author.', ['Webmention', 'h-entry']) ?>
20 | 21 |34 | = __('Detailed instructions on how to submit a post') ?> 35 |
36 | 37 |= __('Error') ?>: = $error ?>
= htmlspecialchars($description) ?>
10 | 11 |= __('Once you\'ve written a post with the proper markup, you will need to send a Webmention to submit it to IndieNews. Your software should do that automatically if it supports {0}, but you can also use the form below.', ['Webmention']) ?>
8 |