Check-in [df89a65d10]
Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | Remove uneeded pages, cron-jobs, doc/*, templates for FC-mirrors. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | mirror |
Files: | files | file ages | folders |
SHA1: | df89a65d102136b21b24e130025dd3307cfc729d |
User & Date: | mario 2014-11-16 15:53:45 |
2014-11-16
| ||
16:02 | Allow direct invocations, remove [END] and API mapping. check-in: 72d74c8cca user: mario tags: mirror | |
15:53 | Remove uneeded pages, cron-jobs, doc/*, templates for FC-mirrors. check-in: df89a65d10 user: mario tags: mirror | |
15:50 | Empty default SQlite store (for mirroring) check-in: 80739648ae user: mario tags: mirror | |
Deleted cron.daily/autoupdate.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php /** * api: freshcode * title: Autoupdate runner * description: Cron job for invoking autoupdates on per-project basis * version: 0.6.0 * depends: curl * author: mario * license: AGPL * x-cron: 15 03 * * * * * * Each project listing can contain: * `autoupdate_module` = none | regex | github | sourceforge | releases.json * `autoupdate_url` = http://... * `autoupdate_regex` = "version=/.../ \n changes=/.../" * * This module tries to load the mentioned reference URLs, extract version * and changelog, scope/state and download link; then updates the database. * */ // run in cron context chdir(dirname(__DIR__)); include("config.php"); // go $_SESSION["submitter"] = ""; $run = new Autoupdate(); $run->debug = 1; $run->msg_html = 0; $run->all(); #print_r($run->test("regex", "linux")); #print_r($run->test("regex", "php")); |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted cron.daily/news_github.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
<?php /** * title: github releases news feed * description: fetch from cache database, build feed and releases page * version: 0.3 * category: template * api: cli * type: cron * x-cron: 15 * * * * * * Short summary * → ./template/feed.github.htm * Long table * → ./template/github-releases.htm * */ chdir(dirname(__DIR__)); include("./config.php"); db(new PDO("sqlite:github.db")); // query $recent = db(" SELECT t_published, created_at, repo_name, author_login, author_avatar, release_url, release_tag, release_title, release_body, repo_url, repo_description, repo_homepage, COALESCE(NULLIF(repo_language, ?), ?) AS repo_language FROM releases WHERE LENGTH(repo_description) > 0 GROUP BY repo_name ORDER BY t_published DESC LIMIT 500 ", "", "no-lang"); // prepare output $lang = ""; $out = []; $full = ""; // printout foreach ($recent as $r) { $r = array_map("htmlspecialchars", $r); #-- filter some if (preg_match("~/(test|main)$~", $r["repo_name"]) or preg_match("~Contribute to .+? by creating an account on GitHub~", $r["repo_description"])) { continue; } #-- sidebar feed if (count($out) < 25) { $name = trim(strstr($r["repo_name"], "/"), "/"); $out[] = " <a href=\"$r[release_url]\" title=\"$r[repo_description]\">$name " . "<em class=version title=\"$r[release_title]\">$r[release_tag]</em></a>"; } #-- complete list $verdesc = input::spaces(substr($r["release_body"], 0, 200)); $name = explode("/", $r["repo_name"]); // project blob $full .= <<<HTML <tr class="github release"> <td class=author-avatar><img src="$r[author_avatar]&s=40" alt="$r[author_login]" height=40 width=40></td> <td class=repo-name> <a href="$r[repo_url]" title="$r[repo_name]"> <small class=repo-org>$name[0] /</small> <strong class=repo-localname>$name[1]</strong> </a> <span class=repo-language>$r[repo_language]<span> </td> <td class=repo-description> $r[repo_description] <a class=repo-homepage href="$r[repo_homepage]">$r[repo_homepage]</a> </td> <td class=release> <a class=release-tag href="$r[release_url]"><em class=version title="$r[release_title]">$r[release_tag]</em></a> <span class=release-body>$verdesc</span> </td> </tr> HTML; } // write file_put_contents("./template/feed.github.htm", implode("\n", $out)); file_put_contents("./template/github-releases.htm", $full); |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted cron.daily/poll_githubarchive.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
<?php /** * title: GitHubArchive releases poll * description: Fetch GitHub releases via BigQuery githubarchive:github.timeline * version: 0.4 * category: rpc * api: cli * depends: config.local * doc: http://www.githubarchive.org/ * github-url: https://github.com/igrigorik/githubarchive.org * type: cron * x-cron: 05 * * * * * * Queries githubarchive.org event blobs. * (Fetching via Google BigQuery too easily exceeded the quotas.) * * JSON blobs are stored under: * http://data.githubarchive.org/2014-10-30-{0..23}.json.gz * which contain newline-separated JSON objects. * → fetched via SplFileObject and gzip-decoding stream prefix * → pre-filtered for "type":"ReleaseEvent" by RegexIterator * → merged with repo meta data (desc, urls, lang) per GitHub API * * Stores everything into github.db cache table. * * CREATE TABLE releases ( * t_published INT, * created_at VARCHAR, * repo_name VARCHAR, * author_login VARCHAR, * author_avatar VARCHAR, * release_url VARCHAR UNIQUE, * release_tag VARCHAR, * release_title VARCHAR, * release_body TEXT, * repo_url VARCHAR, * repo_description TEXT, * repo_homepage VARCHAR, * repo_language VARCHAR, * UNIQUE ( repo_name, release_tag ) ON CONFLICT FAIL * ); * CREATE INDEX idx_releases ON releases ( * t_published ASC * ); * CREATE INDEX unique_releases ON releases ( * repo_name, * release_tag * ); * * Which allows easier display/feed generation for news_github.php. * */ // Common settings chdir(dirname(__DIR__)); include("./config.php"); // Separate github.releases database db(new PDO("sqlite:github.db")); /** * GitHubArchive via Google BigQuery * (unused now) * */ class GHA_BQ { /** * Google API OAuth connection * */ function Google_API_Client() { $client = new Google_Client(); $client->setApplicationName("freshcode-github"); $client->setDeveloperKey(GOOGLEAPI_DEV_KEY); $client->setClientId(GOOGLEAPI_CLIENT_ID); $cred = new Google_Auth_AssertionCredentials( GOOGLEAPI_EMAIL, array('https://www.googleapis.com/auth/bigquery'), file_get_contents(GOOGLEAPI_KEYFILE) ); $client->setAssertionCredentials($cred); $client->getAuth()->isAccessTokenExpired() and $client->getAuth()->refreshTokenWithAssertion($cred); return $client; } /** * Populates BigQuery configuration and Job, * executes it right away, and waits for response. * */ function BigQuery_execute($sql) { // new BigQuery + Google_Client instances $client = new Google_Service_Bigquery( self::Google_API_Client() ); // create query job $job = new Google_Service_Bigquery_Job(); $config = new Google_Service_Bigquery_JobConfiguration(); $queryConfig = new Google_Service_Bigquery_JobConfigurationQuery(); $queryConfig->setQuery($sql); $queryConfig->setPriority("INTERACTIVE"); // speedier results $config->setQuery($queryConfig); $job->setId(md5(microtime())); $job->setConfiguration($config); // run job and pack results $res = $client->jobs->getQueryResults( GOOGLEAPI_PROFILE, $client->jobs->insert(GOOGLEAPI_PROFILE, $job)->getJobReference()["jobId"] ); return $res; } // run query for recent project releases function githubarchive_releases() { return self::BigQuery_execute(" SELECT TIMESTAMP(created_at) as t_published, created_at, url, type, repository_url, repository_owner, repository_name, repository_description, repository_language, repository_homepage -- , payload_name, payload_url, payload_desc, payload_commit, payload_member_avatar_url -- , payload_release_tag_name, payload_release_name, payload_release_body FROM [githubarchive:github.timeline] WHERE type='ReleaseEvent' -- AND LENGTH(repository_description) > 0 ORDER BY created_at DESC LIMIT 500 "); } } /** * Query GitHub projects (max 5000/hour) * */ class GitHub_API { /** * Generic wrapper for simple GitHub API * */ function call($api = "events", $data = []) { return json_decode( curl("https://api.github.com/$api") ->userpwd(GITHUB_API_PW) //->writeheader(fopen("gh-curl-header.txt", "a+")) ->timeout(5) ->exec() ); } /** * Retrieve Github global /events (push, pull, commit, comment, release, ...) * */ function events() { return self::call("events"); } /** * Fetch meta data for repository * */ function repo_meta($fullname) { return self::call("repos/$fullname"); } /** * HTML extract repo title * @obsolete, see `repo_meta` * */ function repo_title($gh_url) { preg_match('~<meta\s+content="([^<>"]+?)"\s+property="og:description"\s+/?>~s', curl($gh_url)->exec(), $m); $gh_title = htmlentities(html_entity_decode($m[1])); if (preg_match("/Contribute to .+? development by creating an account on GitHub./", $gh_title)) { continue; } } /** * Normalize Github.com URL, split full, owner and repo name * */ function repo_name($url) { if (preg_match("~^https?://github\.com/([\w.-]+)/([\w.-]+)(?:/|$)~", $url, $m) or preg_match("~^https?://(?:api|uploads)\.github\.com/(?:repos|users)/([\w.-]+)/([\w.-]+)(?:/|$)~", $url, $m)) { return array("https://github.com/$m[1]/$m[2]/", "$m[1]/$m[2]", $m[1], $m[2]); } } } /** * Retrieve event archives from //data.githubarchive.org/YYYY-MM-DD-HH.json.gz * */ class GitHubArchive { /** * date/timespec for GHA * */ function last_hour($hour=1) { // return 2014-05-31-hH (minus 8 hours, because GHA uses filenames according to its own timezone) return gmdate("Y-m-d-G", time() - 5*60 - (7 + $hour) * 3600); } /** * Fetch GHA day+hour .json.gz resource line-wise filtered by ReleaseEvent type * */ function json_lines($day_hour) { // open remote resource and decompress implicitly $gz = new SplFileObject("compress.zlib://http://data.githubarchive.org/$day_hour.json.gz"); // filter JSON blob lines on type=ReleaseEvents return new RegexIterator($gz, '/ "type" : "ReleaseEvent" /smux'); } /** * Iterator over last GitHubArchive data files * */ function fetch_json_last_hours($count = 2) { $it = new AppendIterator; foreach (range(1, 24) as $prev) { // Keep iterating for successful URL accesses try { $it->append(self::json_lines($day_hour = self::last_hour($prev))); print "fetching .../$day_hour.json.gz\n"; } catch (Exception $http_fail) { // SplFileObject just gives RuntimeException for HTTP 404 results print "not present .../$day_hour.json.gz\n"; continue; } // Retrieve at least two blob files if (--$count <= 0) { break; } } return $it; } } /** * Apply callback over iterator values. * */ class CallbackIterator extends IteratorIterator { public $mapfn; function __construct($iter, $mapfn) { $this->mapfn = $mapfn; parent::__construct($iter); } function current() { return call_user_func($this->mapfn, parent::current()); } } #-- Select input source, # JSON lines/blobs/files, pre-converted to objects // from GHA files $in_json = new CallbackIterator(GitHubArchive::fetch_json_last_hours(), "json_decode"); // direct polling of GitHub API (priorly polled in a loop) #$in_json = GitHub_API::events(); // previously collected event blobs #$in_json = new CallbackIterator(new CallbackIterator(new GlobIterator("./@/github/ev*"), "file_get_contents"), "json_decode"); #-- Load into cache database insert_github_releases($in_json); /** * Traverse github /events JSON objects and update DB. * */ function insert_github_releases($in_json) { foreach ($in_json as $event) { // Properties from events payload $p = array( "t_published" => strtotime($event->payload->release->published_at), "created_at" => $event->payload->release->created_at, "repo_name" => GitHub_API::repo_name($event->payload->release->html_url)[1], "author_login" => $event->payload->release->author->login, "author_avatar" => $event->payload->release->author->avatar_url, "release_url" => $event->payload->release->html_url, "release_tag" => $event->payload->release->tag_name, "release_title" => $event->payload->release->name, "release_body" => $event->payload->release->body, ); // Skip existing entries $exists = db(" SELECT t_published FROM releases WHERE release_url = ? OR (repo_name = ? AND release_tag = ?)", $p["release_url"], $p["repo_name"], $p["release_tag"] ); if ($exists->t_published) { print "already have $p[release_url]\n"; continue; } // Add additional repository infos if ($meta = GitHub_API::repo_meta($p["repo_name"])) { $p = $p + array( "repo_url" => $meta->html_url, "repo_description" => $meta->description, "repo_homepage" => $meta->homepage, "repo_language" => $meta->language, ); } else { continue; } // Store print_r($p); db("INSERT INTO releases (:?) VALUES (::)", $p, $p); } } ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted cron.daily/social_links.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?php /** * api: cli * title: Social links count * description: Queries api.i-o/links for project homepages * version: 0.1 * category: rpc * type: cron * x-cron: 20 03 * * * * * Retrieve social media sharing site links for project homepages. * Stores them in `release`.`social_links` * * Only updates latest DB entry, so a versioned history of link counts * will be retained. (Not that it's needed, ...) * */ // deps chdir(dirname(__DIR__)); include("config.php"); // use "remotely" for implicit caching define("IO_LINKS", "http://api.include-once.org/links/social.ajax.php"); // traverse projects foreach (db("SELECT *, MAX(t_changed) FROM release_versions GROUP BY name ORDER BY t_published DESC")->fetchAll() as $project) { // homepage $url = $project["homepage"]; // request counts $counts = curl() ->url(IO_LINKS . "?url=$url") ->UserAgent("cron.daily/social_links (0.1; http://freshcode.club/)") ->exec(); // summarize $counts = json_decode($counts, TRUE); $counts = array_sum($counts); print "$url = $counts\n"; // store $project["social_links"] = $counts; #$project->store("REPLACE"); db("UPDATE release SET social_links=? WHERE :&", $counts, array( "name" => $project["name"], "t_changed" => $project["t_changed"], "t_published" => $project["t_published"], ) ); } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted cron.daily/twitter.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<?php /** * api: cli * title: Twitter bridge * description: Posts recent releases on twitter * version: 0.1 * category: rpc * type: cron * x-cron: 50 * * * * * * Summarize new releases for Twitter feed. * Currently using `twidge` (pre-configured in $HOME), * which doesn't support //t.co/ inline images yet. * */ chdir(dirname(__DIR__)); include("config.php"); /** * Releases within the last hour * */ $rel = db(" SELECT * FROM release_versions WHERE t_published >= ? ", time()-7300 )->fetchAll(); // query recent/previous status messages $prev = twit("lsarchive"); // condense individual tweets foreach ($rel as $row) { // skip existing if (is_int(strpos($prev, $row["title"]))) { print "skip($row[name]) "; continue; } // homepage if (empty($row["homepage"]) or strlen($row["homepage"] > 80)) { $row["homepage"] = "http://freshcode.club/projects/$row[name]"; } // prepare post $msg = "$row[title] $row[version] released. $row[homepage]"; $msg = preg_replace("/\s+/", " ", $msg); // add tags $tags = p_csv($row["tags"]); shuffle($tags); foreach ($tags as $tag) { $tag = preg_replace("/^(\w)\+\+/", "\\1pp", $tag); $tag = preg_replace("/-/", "_", $tag); $msg .= " #$tag"; } // cut to limit while (strlen($msg) > 140) { $msg = preg_replace("/\s\S+$/s", "", $msg); } // send print_r("$msg\n"); twit("update", $msg); } /** * Invoke cmdline twitter client * */ function twit() { $cmd = "twidge"; foreach (func_get_args() as $param) { $cmd .= " " . escapeshellarg($param); } return ` $cmd `; } ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to db.sql.
1 2 3 4 5 6 7 8 9 10 11 |
# # title: freshcode database schema # version: 0.7 # CREATE TABLE [release] ( name VARCHAR( 100 ) NOT NULL, title TEXT NOT NULL, homepage TEXT, description TEXT NOT NULL, license VARCHAR( 100 ), |
< > | | < > |
1 2 3 4 5 6 7 8 9 10 11 |
--# --# title: freshcode database schema --# version: 0.7 --# CREATE TABLE [release] ( name VARCHAR( 100 ) NOT NULL, title TEXT NOT NULL, homepage TEXT, description TEXT NOT NULL, license VARCHAR( 100 ), |
Deleted doc/cacert.pem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
-----BEGIN CERTIFICATE----- MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ 8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg 18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD -----END CERTIFICATE----- |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted doc/fc-submit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
#!/usr/bin/env python3 """ freshcode-submit -- script transactions with the Freshcode.club server """ import sys, re, requests, json, netrc, email.parser, optparse version = "3.0" class Update: "Encapsulate dictionaries describing a project metadata update." def __init__(self): self.name = None self.per_project = {} self.urlassoc = [] self.per_release = {} def __repr__(self): return "Update(" + repr(self.__dict__) + ")" # The Freecode API implementation is sensitive to the kind of HTTP request # method you use. The general rule is: # # Reading records: GET # Adding new records: POST # Updating existing records: PUT # Deleting existing records: DELETE # # From http://help.freecode.com/faqs/api-7/data-api-intro: # 200 OK - Request was successful, the requested content is included # 201 Created - Creation of a new resource was successful # 401 Unauthorized - You need to provide an authentication code with this # request # 403 Forbidden - You don't have permission to access this URI # 404 Not Found - The requested resource was not found # 409 Conflict - The validation of your submitted data failed, please check # the response body for error pointers # 500 Server Error - The request hit a problem and was aborted, please report # as a bug if it persists # 503 Service Unavailable - You hit your API credit limit def RequestWithMethod(method, url, **kwargs): """requests.request is really a drop-in replacement here; with TLS-SNI support""" # Here verify="cacert.pem" would better solve the certificate issue return requests.request(method, url, verify=False, **kwargs) class FreecodeSessionException(Exception): "Carry exception state when a session blows up." def __init__(self, msg): Exception.__init__(self) self.msg = msg class FreecodeSession: "Encapsulate the state of a Freecode API session." server = "https://%s.freshcode.club/" % "api" def __init__(self, auth=None, verbose=0, emit_enable=True): "Initialize Freecode session credentials." self.auth = auth self.verbose = verbose self.emit_enable = emit_enable self.project = None self.permalink = None self.id = None self.project_data = None # If user didn't supply credentials, fetch from ~/.netrc if not self.auth: try: credentials = netrc.netrc() except netrc.NetrcParseError as e: raise FreecodeSessionException("ill-formed .netrc: %s:%s %s" \ % (e.filename, e.lineno, e.msg)) except IOError as e: raise FreecodeSessionException(("missing .netrc file %s" % \ str(e).split()[-1])) ret = credentials.authenticators("freshcode") if not ret: raise FreecodeSessionException("no credentials for Freshcode") _login, self.auth, _password = ret def on_project(self, name): "Select project by Freecode shortname." if self.verbose: print(("Selecting project: %s" % name)) self.project = name pquery = FreecodeSession.server + "projects/%s.json?auth_code=%s" \ % (self.project, self.auth) handle = RequestWithMethod("GET", url=pquery) content = json.loads(handle.text) self.project_data = content['project'] #if self.verbose: # print "Project data: %s" % self.project_data self.permalink = self.project_data['permalink'] self.id = self.project_data['id'] def edit_request(self, url, method="GET", request=None, force=False): "Wrap a JSON object with the auth code and ship it as a request" if request is None: request = {} url = FreecodeSession.server + url data = {"auth_code" : self.auth} data.update(request) data = json.dumps(data) headers = {"Content-Type" : "application/json"} if self.verbose: print(("Request URL:", method, url)) #if self.verbose: # print "Request headers:", headers if self.verbose: print(("Request data:", data)) if self.emit_enable or force: req = RequestWithMethod(method=method, url=url, data=data, headers=headers) if self.verbose: print(req.status_code, req.url, req.headers) content = req.text if self.verbose: print(("Response:", content)) return content def publish_release(self, data): "Add a new release to the current project." if self.verbose: print(("Publishing %s release: %s" % (self.project, repr(data)))) self.edit_request("projects/" + self.permalink + "/releases.json", "POST", {"release": data}) def withdraw_release(self, dversion): "Withdraw a specified release from the current project." if self.verbose: print("Withdrawing %s release: %s" % (self.project, dversion)) releases = self.edit_request("projects/%s/releases/pending.json" \ % self.permalink, force=True) releases = json.loads(releases) for release in releases: properties = release["release"] if properties.get("version") == dversion: vid = properties["id"] break else: raise FreecodeSessionException("couldn't find release %s"%dversion) deletelink = "projects/%s/releases/%s.json" % (self.permalink, vid) self.edit_request(deletelink, "DELETE", {}) def update_core(self, coredata): "Update the core data for a project." if self.verbose: print("Core data update for %s is: %s" % (self.project, coredata)) self.edit_request("projects/" + self.permalink + ".json", "PUT", {"project": coredata}) def update_urls(self, urlassoc): "Update URL list for a project." if self.verbose: print("URL list update for %s is: %s" % (self.project, urlassoc)) # First, get permalinks for all existing URLs uquery = FreecodeSession.server + "projects/%s/urls.json?auth_code=%s" \ % (self.permalink, self.auth) handle = RequestWithMethod("GET", url=uquery) content = json.loads(handle.text) permadict = content['urls'] # Just send the new dict over self.edit_request("projects/%s/urls.json" % (self.permalink), "PUSH", {"urls" : dict(urlassoc)}) class FreecodeMetadataFactory: "Factory class for producing Freecode records in JSON." freecode_field_map = ( ("Project", "P", "name"), # Project ("Summary", "S", "oneliner"), # Project ("Description", "D", "description"), # Project ("License-List", "L", "license_list"), # Project ("Project-Tag-List", "T", "project_tag_list"), # Project ("Version", "v", "version"), # Release ("Changes", "c", "changelog"), # Release ("Hide", "x", "hidden_from_frontpage"), # Release ("Release-Tag-List", "t", "tag_list"), # Release ) # Which attributes have project scope, all others have release scupe projectwide = ('name', 'description', 'oneliner', 'license_list', 'project_tag_list') def __init__(self): self.message_parser = email.parser.Parser() self.argument_parser = optparse.OptionParser( \ usage="usage: %prog [options]") for (msg_field, shortopt, rpc_field) in FreecodeMetadataFactory.freecode_field_map: self.argument_parser.add_option("-" + shortopt, "--" + msg_field.lower(), dest=rpc_field, help="Set the %s field"%msg_field) self.argument_parser.add_option('-q', '--query', dest='query', help='Query metadata for PROJECT',metavar="PROJECT") self.argument_parser.add_option('-d', '--delete', dest='delete', default=False, action='store_true', help='Suppress reading fields from stdin.') self.argument_parser.add_option('-n', '--no-stdin', dest='read', default=True, action='store_false', help='Suppress reading fields from stdin.') self.argument_parser.add_option('-N', '--dryrun', dest='dryrun', default=False, action='store_true', help='Suppress reading fields from stdin.') self.argument_parser.add_option('-V', '--verbose', dest='verbose', default=False, action='store_true', help='Enable verbose debugging.') self.argument_parser.add_option('-?', '--showversion', dest='showversion', default=False, action='store_true', help='Show version and quit.') @staticmethod def header_to_field(hdr): "Map a header name from the job card format to a field." lhdr = hdr.lower().replace("-", "_") for (alias, _shortopt, field) in FreecodeMetadataFactory.freecode_field_map: if lhdr == alias.lower().replace("-", "_").replace("/", "_"): return field raise FreecodeSessionException("Illegal field name %s" % hdr) def getMetadata(self, stream): "Return an Update object describing project and release attributes." data = {} urls = {} (options, _args) = self.argument_parser.parse_args() # Stuff from stdin if present prior_version = data.get("version") if not (options.query or options.showversion) and options.read: message = self.message_parser.parse(stream) for (key, value) in list(message.items()): value = re.sub("\n +", " ", value).strip() if key.endswith("-URL"): key = key.replace("-", " ") urls.update({key[:-4] : value}) else: if key.endswith("List"): value = [x.strip() for x in value.split()] data.update({FreecodeMetadataFactory.header_to_field(key) : value}) if not 'changelog' in data: payload = message.get_payload().strip() if payload: data['changelog'] = payload + "\n" if prior_version and data.get("version") != prior_version: raise FreecodeSessionException("Version conflict on stdin.") # Merge in options from the command line; # they override what's on stdin. controls = ('query', 'delete', 'read', 'dryrun', 'verbose', 'showversion') prior_version = data.get("version") for (key, value) in list(options.__dict__.items()): if key not in controls and value != None: data[key] = value del options.__dict__[key] if prior_version and data.get("version") != prior_version and not options.delete: raise FreecodeSessionException("Version conflict in options.") # Hidden flag special handling if "hidden_from_frontpage" in data: data["hidden_from_frontpage"] = data["hidden_from_frontpage"] in ("Y", "y") # Now merge in the URLs, doing symbol substitution urllist = [] for (label, furl) in list(urls.items()): for (k, v) in list(data.items()): if type(v) == type(""): furl = furl.replace('${' + k + '}', v) urllist.append((label, furl)) # Sort out what things go where update = Update() if options.showversion: pass elif options.query: update.name = options.query else: update.name = data.pop('name') update.urlassoc = urllist for (k, v) in list(data.items()): if k in FreecodeMetadataFactory.projectwide: # Hack to get around a namespace collision if k == "project_release_tag": k = "release_tag" update.per_project[k] = v else: update.per_release[k] = v # Return this return (options, update) if __name__ == "__main__": try: # First, gather update data from stdin and command-line switches factory = FreecodeMetadataFactory() (options, update) = factory.getMetadata(sys.stdin) # Some switches shouldn't be passed to the server query = 'query' in options.__dict__ and options.query verbose = 'verbose' in options.__dict__ and options.verbose delete = 'delete' in options.__dict__ and options.delete dryrun = 'dryrun' in options.__dict__ and options.dryrun showversion = 'showversion' in options.__dict__ and options.showversion if showversion: print("freshcode-submit", version) raise SystemExit(0) # Time to ship the update. # Establish session session = FreecodeSession(verbose=int(verbose), emit_enable=not dryrun) try: session.on_project(update.name) except ValueError as e: print(e) print("freshcode-submit: looks like a server-side problem at freshcode.club; bailing out.", file=sys.stderr) raise SystemExit(1) if options.query: print("Project: %s" % session.project_data["name"]) print("Summary: %s" % session.project_data["oneliner"]) print("Description: %s" % session.project_data["description"].replace("\n", "\n ").rstrip()) print("License-List: %s" % ",".join(session.project_data["license_list"])) print("Project-Tag-List: %s" % ",".join(session.project_data["tag_list"])) for assoc in session.project_data['approved_urls']: #print "Querying", assoc["redirector"] #req = RequestWithMethod(method="HEAD", # url=assoc["redirector"], # data={}, # headers={}) #handle = urllib2.urlopen(req) #print "===" #print handle.info() #print "===" print("%s-URL: %s" % (assoc["label"].replace(" ","-"), assoc["redirector"])) if 'recent_releases' in session.project_data and session.project_data['recent_releases']: most_recent = session.project_data['recent_releases'][0] print("Version: %s" % most_recent['version']) print("Tag-List: %s" % ",".join(most_recent['tag_list'])) if most_recent.get('hidden_from_frontpage'): print("Hide: Y") else: print("Hide: N") print("") print(most_recent["changelog"]) else: # OK, now actually add or delete the release. if update.per_project: session.update_core(update.per_project) if update.urlassoc: session.update_urls(update.urlassoc) if delete: session.withdraw_release(update.per_release['version']) elif update.per_release and list(update.per_release.keys())!=["version"]: session.publish_release(update.per_release) except FreecodeSessionException as e: print("freshcode-submit:", e.msg, file=sys.stderr) sys.exit(1) except requests.exceptions.HTTPError as f: print("freshcode-submit: HTTPError %s" % (f.code), file=sys.stderr) print(f.read(), file=sys.stderr) sys.exit(1) except requests.exceptions.RequestException as f: print("freshcode-submit: URLError %s" % (f.reason,), file=sys.stderr) sys.exit(1) # end |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted doc/freecode2releases.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
#!/usr/bin/env python # encoding: utf-8 # api: cli # type: main # title: freecode-to-releases # description: Extracts project descriptions+history from Freecode.com into releases.JSON # category: scraping # version: 0.7 # config: # <env-unused name=XDG_CONFIG_HOME value=~/.config description="user config base dir"> # license: MITL # doc: http://fossil.include-once.org/freshcode/wiki/freecode2releases # # # Fetches prior freshmeat/freecode.com project listings, and extracts # the version history. Packages data up as `releases.json` format, which # makes a nice backup format and exchange format. And also suits importing # into freshcode.club listings. # # Notably this should only be done by package maintainers to retain their # original authorship and thus reusability. # import sys import errno import collections from datetime import datetime import re import requests try: from bs4 import BeautifulSoup as bs except: print("f2r: BeatifulSoup missing,\nuse `apt-get install python-bs4` || `pip install beautifulsoup4`\n") exit(errno.ENOPKG) try: import simplejson as json except: import json # scrape from freecode.com def freecode_fetch(name): try: url = "http://freecode.com/projects/%s" % name html = bs(requests.get(url).text) except: print("project not found, %s" % url) return None # le basics r = collections.OrderedDict([ ("$feed-license", "author/editor"), ("$feed-origin", url), ("name", name), ("title", html.find("meta", {"property": "og:title"})["content"]), ("oneliner", html.find("meta", {"property": "og:description"})["content"]), #("image", "http://freshcode.com" + html.find("meta", {"property": "og:image"})["content"]), ("keywords", html.find("meta", {"name": "keywords"})["content"]), ("description", html.select("div.project-detail p")[0].string), ("tags", freecode_tags(html.select("#project-tag-cloud a"))), ("submitter", html.select("li.avatar a.avatar")[0]["title"]), ("urls", freecode_urls(html.select(".sub-navigation li.url a"))), ("releases", freecode_releases(name)), ]) return r # extract tag basename from <a> link list def freecode_tags(li): return ", ".join([ a["href"][6:] for a in li ]) # convert url list <li> <a> into dict def freecode_urls(li): r = [ (a.string, "http://freecode.com" + a["href"]) for a in li ] return collections.OrderedDict(r) # fetch releases pages def freecode_releases(name): last_page = 1 page = 1 r = [] while page <= last_page: # iterate through /releases pages url = "http://freecode.com/projects/%s/releases%s" % (name, ("?page=%s" % page if page else "")) html = bs(requests.get(url).text) for ver in html.select("div.release.clearfix"): # remove changelog gimmicks for rm in ("strong", ".truncate_ellipsis", ".truncate_more_link"): for e in ver.select(rm): e.replace_with(""); # collect r.append({ "version": ver.select("li.release a")[0].string, "changes": "".join(ver.select("p.truncate.changes")[0].contents).strip(), "scope": "incremental", "state": "historic", "published": strftime(ver.select("li.rdate")[0].string.strip()).isoformat(), "submitter": ver.select(".author a")[0]["title"] }) # next page try: last_page = int(html.select(".pagination a")[-2].string) except: last_page = 1 page = page + 1 return r # try to deduce time from different formats def strftime(s): for fmt in [ "%d %b %Y %H:%M", "%Y-%m-%d %H:%M" ]: try: return datetime.strptime(s, fmt) except: None pass # process CLI arguments, invoke retrieval methods def freecode_cli(argv0="f2r", name="", output=""): if name: output = output or "%s-releases.json" % name json.dump(freecode_fetch(name), open(output, "wt"), indent=4) else: print("synopsis: freecode2releases.py [projectname [output.json]]"); print("[31mPlease only download and resubmit project data you initially wrote yourself.[0m"); return # argv to main if __name__ == "__main__": freecode_cli(*sys.argv) |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted doc/logo.svgz.
cannot compute difference between binary files
Deleted doc/mktrove.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
#!/usr/bin/php -qC <?php /** * api: cli * title: convert trove.csv * description: generates PHP array of trove categories and tag leaves * * `soffice --headleass --convert-to csv trove.ods` * does not work, so we need it preconverted in trove.csv * * only first 4 columns contain the tree and leaves * */ #-- read in $csv = array_map("str_getcsv", file(__DIR__."/trove.csv")); unset($csv[0]); // remove head // target $TREE = array(); $TMAP = array(); // last tree $last_path = array(); # loop over lines foreach ($csv as $row) { // Cut out only 5 columns (rest of spreadsheet is documentation) $py_trove = $row[5]; $row = array_map("trim", array_slice($row, 0, 5)); // merge current leave parts with last path if ($row = array_filter($row)) { $row = array_slice($last_path, 0, key($row)) + $row; $last_path = $row; } // skip empty else { continue; } $TMAP[] = array($py_trove, implode(" :: ", $row)); // append to what we have $path = array_filter($row); $leaf = array_pop($path); $var = enclose("['", $path, "']"); eval(" \$TREE{$var}[] = '$leaf'; "); # if ($up_var) unset(\$TREE{$up_var}[array_search('$up_leaf', \$TREE{$up_var})]); } // reorder, strip Status and License, then output print var_export54(array( "Topic" => $TREE["Topic"], "Programming Language" => $TREE["Programming Language"], "Environment" => $TREE["Environment"], "Framework" => $TREE["Framework"], "Operating System" => $TREE["Operating System"], "Audience" => $TREE["Audience"], # "Natural" => $TREE["Natural"], )); // save mapping onto py-trove file_put_contents(__DIR__."/trove.pypi-map.csv", json_encode($TMAP, JSON_PRETTY_PRINT)); #-- le functions function enclose($pre, $values, $post) { return $values ? $pre . implode("$post$pre", $values) . $post : ""; } function var_export54($var, $indent="") { switch (gettype($var)) { case "string": return '"' . addcslashes($var, "\\\$\"\r\n\t\v\f") . '"'; case "array": // $indexed = array_keys($var) === range(0, count($var) - 1); $r = []; foreach ($var as $key => $value) { $indexed = is_numeric($key); $r[] = "$indent " . ($indexed ? "" : var_export54($key) . " => ") . var_export54($value, "$indent "); } return "[\n" . implode(",\n", $r) . "\n" . $indent . "]"; case "boolean": return $var ? "TRUE" : "FALSE"; default: return var_export($var, TRUE); } } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted doc/trove.ods.
cannot compute difference between binary files
Deleted forum.css.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
/** * api: css * type: stylesheet * title: meta forum layout * description: Minimalistic forum style * version: 0.1 * * Posts are just presented in a nested list, * and left-side title banner sticks out. * */ /** * Google fonts * */ @import url(http://fonts.googleapis.com/css?family=Hind:400,500,700,300); /** * General * */ html, body { padding: 0; margin: 0; height: 100%; } body { /* font: 400 12pt/16pt Kreon;/ font: 500 12pt/16pt Raleway; font: 500 12pt/16pt Numans; font: 500 12pt/16pt Inder; font: 400 12pt/16pt Voces; font: 400 12pt/16pt Magra;*/ font: 400 12pt/16pt Hind; } /** * Title border * */ #title { display: block; position: fixed; left: 0; top: 0; background: #222; width: 150pt; float: left; height: 100%; min-height: 5000px; padding: 0; margin: 0; } h1 { transform: rotate(270deg); font-size: 92pt; font-size: 12.5vh; position: relative; top: 500pt; top: 67vh; white-space: nowrap; letter-spacing: -0.025em; } h1 * { color: #fff; text-decoration: none; } h1 { font-weight: 300; } h1 b { font-weight: 700; } h1 .red { color: #744; } h1 .grey { color: #444; } /** * Forum tree * */ .forum, .forum ul { padding: 5pt 0 10pt 30pt; list-style: none; } ul.forum > li { padding-bottom: 10pt; } ul.forum { padding: 5pt; padding-left: 180pt; list-style: none; padding-bottom: 90pt; } /** * One post block (wrapped in <li>) * */ .forum .entry { display: block; width: 600pt; /* background: #eee; border: 1px solid #ddd; */ min-height: 30pt; padding-left: 5pt; margin-top: 15pt; } /** * Tag / author / time - left-rotated meta info. * */ .forum .entry .meta { } .forum .entry .meta div { /* border: 2px dashed #faa; */ } .forum .entry .meta div > * { font-size: 90%; line-height: 90%; color: #666; padding-right: 5pt; } .forum .entry .meta .datetime { font-size: 5pt; color: #ccc; } .forum .entry .meta .category { color: #85879f; background: #fcf9f1; border-radius: 3pt; font-size: 105%; letter-spacing: 0.1em; } /** * Post content. * */ .forum .entry .summary { display: block; margin: 0; font-weight: bold; font-size: 108%; color: #449; } .forum .entry .excerpt { color: #777; z-index: 7; width: 600pt; } .forum .entry .excerpt:hover { color: #5a5a5a !important; cursor: nw-resize; } .forum .entry .trimmed { /* hidden divs */ display: none; } .forum .entry { color: #444; } .forum .entry .content p { padding-top: 0; margin-top: 0; } .forum .entry .content ul { margin: 10pt !important; padding: 0 !important; } .forum .entry .content ul li:before { content: "→"; margin-right: 5pt; color: #522; } .forum .entry .content code { background: #eee; font-size: 90%; padding: 1pt; border-radius: 3pt; } .action { margin: 1pt; padding: 2pt 10pt; color: #33c; background: #e7e7fc; border: #f0f0f9; border-radius: 5pt; font-size: 10pt; opacity: 0.85; cursor: pointer; } .action.forum-edit { opacity: 0.5; } .action:hover { opacity: 1.0; } .action.forum-edit:hover { cursor: s-resize; } .action.forum-reply:hover { cursor: copy; } /** * Submit form * */ label { display: block; padding: 5pt; } form.forum-submit label b { display: inline-block; width: 80pt; text-align: right; } form.forum-submit label b select { font-weight: 900; font-size: 105%; text-align: right; } form.forum-submit .markup-buttons { width: 80pt; padding: 5pt; text-align: right; position: relative; } .markup-buttons .action { display: inline-block; margin: 0 10pt 7pt 0; cursor: text; padding: 3pt 15pt 1pt 15pt; line-height: 1em; background: #eef0f7; opacity: 0.3; } .markup-buttons .action:hover { opacity: 1.0; } input, textarea, select { font-size: 105%; padding: 1.5pt; border: 1px solid #bbb; border-left: 5pt solid #ccc; border-radius: 5pt; } .error, .warning { background: #eeddaa; border: 2px solid #ddcc99; border-radius: 5pt; padding: 5pt; } .error { background: #eebbaa; } |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted handler_api.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 |
<?php /** * api: freshcode * title: Submit API * description: Implements the Freecode JSON Rest API for release updates * version: 0.3 * type: handler * category: API * doc: http://fossil.include-once.org/freshcode/wiki/Freecode+JSON+API * author: mario * license: AGPL * * This utility code emulates the freecode.com API, to support release * submissions via freecode-submit and similar tools. The base features * fit well with the freshcode.club database scheme. * * Our RewriteRules map following Freecode API request paths: * * GET projects/<name>.json query * PUT projects/<name>.json update_core * POST projects/<name>/releases.json publish * GET projects/<name>/releases/<w>.json version_GET, id= * DELETE projects/<name>/releases/<i>.json version_DELETE * * From the ridiculous amount of URL manipulation calls, we just keep: * * GET/PUT projects/<name>/urls.json urls (assoc array) * * * Retrieval requests usually come with an ?auth_code= token. For POST * or PUT access it's part of the JSON request body. Which comes with * varying payloads depending on request type: * * { "auth_code": "pw123", * "project": { * "license_list": "GNU GPL", * "project_tag_list": "kernel,operating-system", * "oneliner": "Linux kernel desc", * "description": "Does this and that.." * } } * * Any crypt(3) password hash in a projects `lock` field will be checked * against the plain auth_code. * * At this point everything went through index.php already; runtime env * thus initialized. Therefore API methods can be invoked directly, which * either retrieve or store project data, and prepare a JSON response. * */ /* @Test @sh @t query wget http://freshcode/projects/linux.json?auth_code=unused -O- ./fc-submit -q linux @t change_core ./fc-submit -P linux -D "new proj" -S "oneliner" -L "GNU GPL" -T "kernel,linux" -n -V @t publish ./fc-submit -P linux -v "3.55.1" -c "Change all the things" -t "major,bugfix" -n -V @t delete ./fc-submit -P linux -v "3.55.1" -d -n -V @t urls wget http://freshcode/projects/linux/urls.json?auth_code=0 -O- */ // Wraps API methods and utility code class FreeCode_API { // HTTP method var $method; // API function var $api; // Project name var $name; // Optional revision ID (just used for releases/; either "pending" or t_published timestamp) var $rev; // inner @array from JSON request body var $body; // Optional auth_code (from URL or JSON body) var $auth_code; // Logging var $log = TRUE; var $timestamp = 0; /** * Initialize params from RewriteRule args. * */ function __construct() { // URL params $this->name = $_GET->proj_name["name"]; $this->api = $_GET->id->strtolower->in_array("api", "query,update_core,publish,urls,version_get,version_delete"); $this->method = $_SERVER->id->strtoupper["REQUEST_METHOD"]; $this->auth_code = $_REQUEST->text["auth_code"]; $this->rev = $_REQUEST->text["id"]; // optional param // Request body is only copied, because it comes with varying payloads (release, project, urls) if ($_SERVER->int["CONTENT_LENGTH"] && $_SERVER->stripos…json->is_int["CONTENT_TYPE"]) { $this->body = json_decode(file_get_contents("php://input"), TRUE); $this->auth_code = $this->body["auth_code"]; } // Logging $this->timestamp = sprintf("%s (%s)", time(), gmdate(DATE_ISO8601, time())); $this->log($this, "\n\n\n/* << REQUEST */"); } /** * Log incoming request, outgoing response, or data set as prepared prior updating DB. * */ function log($what, $prefix) { if ($this->log) { file_put_contents("api-log.txt", "$prefix\n" . json_encode($what, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n", FILE_APPEND); } return $what; } /** * Invoke API target function. * After retrieving current project data. * */ function dispatch() { // Fetch latest revision $project = new release($this->name); if (!$project["name"]) { $this->error_exit(NULL, "404 No such project", "Invalid Project ID"); } // Run dialed method, then output JSON response. $this->json_exit( $this->{ $this->api ?: "error_exit" }($project) ); } /** * GET project description * ----------------------- * * @unauthorized * * Just returns the current releases project description * and basic fields. * Freecode API clients expect: * → id (something numeric, which we don't have, so just a CRC32 here) * → permalink (=name) * → oneliner (we just extract the first line) * → license_list * → tag_list * → approved_urls * * Also appends extraneous freshcode fields. * */ function query($project) { // Everyone can access this, but only the owner will see private fields $data = $this->auth_filter($project); // Alias some fields for fc-submit, but append our data scheme intact return array( "project" => array( "id" => crc32($data["name"]), "permalink" => $data["name"], "oneliner" => substr($data["description"], 0, 100), "license_list" => p_csv($data["license"]), "tag_list" => p_csv($data["tags"]), "approved_urls" => $this->array_uncolumn(p_key_value($data["urls"], NULL)) ) + $data->getArrayCopy() ); } // Expand associative URLs into [{label:,redirector:},..] list function array_uncolumn($kv, $ind="label", $dat="redirector", $r=array()) { foreach ($kv as $key=>$value) { $r[] = array($ind=>$key, $dat=>$value); } return $r; } /** * PUT project base fields * ----------------------- * * @auth-required * * Whereas the project ->body contains: * → license_list * → project_tag_list * → oneliner (IGNORED) * → description * Additionally we'd accept: * → state * → download (URL) * → homepage * */ function update_core($project) { $core = new input($this->body["project"], "core"); // extract fields $new = array( // standard FC API fields "license" => tags::map_license(p_csv($core->words["license_list"])[0]), "tags" => f_tags(join(" ", $core->text["project_tag_list"])), "description" => $core->text["description"], // additional overrides "homepage" => $core->url->http["homepage"], "download" => $core->url->http["download"], "state" => tags::state_tag($core->name["state"]), ); return $this->insert($project, $new); } /** * POST release/version * -------------------- * * @auth-required * * Here the release body contains: * → version * → changelog * → tag_list * → hidden_from_frontpage * We'd also accept: * → state * → download * */ function publish($project) { $rel = new input($this->body["release"], "rel"); // extract fields $new = array( "version" => $rel->text["version"], "changes" => $rel->text["changelog"], "scope" => tags::scope_tags($rel->text["tag_list"]), "state" => tags::state_tag($rel->text["state"] . $rel->text["tag_list"]), "download" => $rel->url->http["download"], ); $flags = array( "hidden" => $rel->int["hidden_from_frontpage"], "deleted" => 0, ); return $this->insert($project, $new, $flags); } /** * Check for "pending" releases * ---------------------------- * * @unauthorized * * We don't have a pre-approval scheme on Freshcode currently, * so this just returns a history of released versions. * * For the `id` we're just using the `t_published` timestamp. * Thus a "withdraw" request could reference it. * */ function version_GET($project) { assert($this->rev === "pending"); // Query release revisions $list = db(" SELECT name, version, t_published, MAX(t_changed) AS t_changed, scope, hidden, changes FROM release WHERE name=? GROUP BY version LIMIT 10", $this->name ); // Assemble oddly nested result array $r = []; foreach ($list as $row) { $r[] = array("release" => array( "id" => $row["t_published"], "version" => $row["version"], "tag_list" => explode(" ", $row["scope"]), "hidden_from_frontpage" => (bool)$row["hidden"], "approved_at" => gmdate(DateTime::ISO8601, $row["t_changed"]), "created_at" => gmdate(DateTime::ISO8601, $row["t_published"]), "changelog" => $row["changes"], )); } return $r; } /** * "Withdraw" a "pending" release * ------------------------------ * * @auth-required * * We're faking two things here. Firstly that the review process * was enabled by default. Secondly that you could delete things. * (The database is designed to be somewhat "immutable", we just * pile up revisions normally.) * * So withdrawing a release just means it gets marked "deleted" * (formerly just "hidden" and/or flagged for moderator attention.) * This somewhat still may terminate a project lifeline (due to VIEW * revision grouping), but can be undone by submitting the release * anew. * * The reasoning being that withdrawn releases are really just * authors making last minute fixes; commonly retracted releases * are just resent later, or with a different changelog. * */ function version_DELETE($project) { // Obviously requires a valid `lock` hash $this->requires_permission($project); assert(is_numeric($this->rev)); // Hide all entries for revision $r = db([ " UPDATE release ", " SET :, " => ["hidden" => 1, "deleted" => 1, "flag" => 0], " WHERE :& " => ["name" => $this->name, "t_published" => $this->rev] ]); return $r ? array("success" => TRUE) : $this->error_exit(NULL); } /** * URL editing * ----------- * * @auth-required * * Here we deviate from the overflowing Freecode API with individual * URL manipulation. (PUT,POST,DELETE /projects/<name>/urls/targz.json) * That's just double the work for client and API. * * Instead on freshcode there is but one associative URL blob for * reading and updating all URLs at once. * * GET /projects/name/urls.json { "urls" : { "src": "http://..", * "github": "http://.." } } * PUT /projects/name/urls.json { "urls": { "txz":, "doc":.. } } * * Our labels use the tag-form, so incoming labels will be adapted. * ("Tar/BZ2" becomes "tar-bz2".) * * Internally the urls are stored in an INI-style key=value text blob. * (But the API stays somewhat more RESTy with an associative dict.) * */ function urls($project) { /** * For a GET query just mirror "Other URLs" as dict * * @unauthorized * */ if ($this->method == "GET") { $urls = p_key_value($project["urls"], NULL); $urls["homepage"] = $project["homepage"]; $urls["download"] = $project["download"]; return array("urls" => $urls); } /** * Updates may come as PUT, POST, PUSH request * * @auth-required * */ else { // Filter incoming URLs $urls = new input($this->body["urls"], "urls"); $urls = $urls->list->url[$urls->keys()]; $new["urls"] = ""; foreach ($urls as $label => $url) { // Remove non-alphanumeric characters $label = trim(preg_replace("/\W+/", "-", $label), "-"); $lower = strtolower($label); in_array($lower, ["home-page", "website"]) and $lower = "homepage"; // Split homepage, download URL into separate fields, if ($lower == "homepage" or $lower == "download") { $new[$lower] = $url; } // While remaining go into `urls` key=value block, retain case-sensitivity here else { $new["urls"] .= "$label = $url\r\n"; } } // Update DB return $this->insert($project, $new); } } /** * Perform partial update * * @auth-required * */ function insert($project, $new, $flags=[]) { // Write permissions required obviously. $this->requires_permission($project); // Log data $this->log($new, "/* ++ STORE DATA */"); // Add new fields to $project $flags["via"] = "api"; $project->update(array_filter($new, "strlen"), $flags, [], TRUE); // Store or return JSON API error. return ($project->store() and (header("Status: 201 Created") + 1)) ? array("success" => TRUE) : $this->error_exit(NULL, "500 Internal Issues", "Database mistake"); } /** * Strip down raw project data for absent auth_code * in read/GET requests. * */ function auth_filter($data) { if (!$this->is_authorized($data)) { unset( $data["lock"], $data["submitter_openid"], $data["submitter"], $data["submitter_image"], $data["hidden"], $data["deleted"], $data["flag"], $data["social_links"], $data["autoupdate_regex"], $data["autoupdate_url"], $data["t_changed"] ); } return $data; } /** * Prevent further operations for (write) requests that * actually REQUIRE a valid authorization token. * * @exit if unauthorized * */ function requires_permission($data) { return $this->is_authorized($data) ? $data : $this->error_exit(NULL, "401 Unauthorized", "No matching API auth_token hash. Add a crypt(3) password in your freshcode.club project entries `lock` field, comma-delimited to your OpenID handle. See http://fossil.include-once.org/freshcode/wiki/Freecode+JSON+API"); } /** * The `lock` field usually contains one or more OpenID urls. It's * a comma-delimited field. * * Using the API additionally requires a password hash, as in crypt(3) * or `openssl passwd -1` or PHPs password_hash(), to be present. * * It will simply be compared against the ?auth_code= parameter. * */ function is_authorized($data) { foreach (preg_grep("/^[^:]+$/", p_csv($data["lock"])) as $hash) { if (password_verify($this->auth_code, $hash)) { return TRUE; } } return FALSE; } /** * JSON encode and finish. * */ function json_exit($data) { header("Content-Type2: json/vnd.freecode.com; version=3; charset=UTF-8"); header("Content-Type: application/json"); $this->log($data, "/* >> RESPONSE */"); exit( json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) ); } /** * Bail with error response. * */ function error_exit($data, $http = "503 Unavailable", $json = "unknown method") { header("Status: $http"); $this->json_exit(["error" => "$json"]); } } ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted page_drchangelog.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
<?php /** * type: page * title: Dr. Changelog * description: Tool to experiment and try out Autoupdate modules * version: 0.1 * license: AfferoLGPL * * Reuses fields from /submit form to start a live check run with * actual Autoupdate modules. * */ $header_add = "<meta name=robots content=noindex>"; include("template/header.php"); ?> <aside id=sidebar> <section> <h5>Know your audience</h5> <small> <p> Whatever source you choose for release announcements, try to keep them <b>user-friendly</b>. </p> <p> End users aren't fond of commit logs. While "merged pull request XY" might be technically highly relevant (for e.g. libraries), it's gibberish to most everyone else.</p> <p> So be careful with the <em>GitHub</em> module in particular. If you're not using githubs /release tool, a commit log may be used still. Only basic filtering is applied.</p> <p> Likewise write <em>Changelogs</em> as <b>summaries</b>. (They're better and more correctly called NEWS or RELEASE-NOTES files actually.)</p> </small> </section> </aside> <section id=main> <?php #-- Output formatted results class TestProject extends ArrayObject { function update($result) { #-- output formatted print "<dl>\n"; foreach ($result as $key=>$value) { print "<dt><b>$key</b></dt>\n<dd>" . input::html($value) . "</dd>\n"; } print "</dl>"; } } // run test if ($_REQUEST->has("test")) { #-- prepare $run = new Autoupdate(); $run->debug = 1; $project = new TestProject(array( "name" => "testproject", "version" => "0.0.0.0.0.0.1", "t_published" => 0, "homepage" => "", "download" => "", "urls" => "", "autoupdate_module" => $_REQUEST->id->in_array("autoupdate_module", "none,release.json,changelog,regex,github,sourceforge"), "autoupdate_url" => $_REQUEST->url["autoupdate_url"], "autoupdate_regex" => $_REQUEST->raw["autoupdate_regex"], )); #-- exec $method = $run->map[$project["autoupdate_module"]]; print "<h3>Results for <em>$method</em> extraction</h3>\n"; $result = $run->$method($project); $result = new TestProject((array)$result); $result->update($result); } // display form else { $data = $_REQUEST->list->html["name,autoupdate_module,autoupdate_url,autoupdate_regex"]; $data["autoupdate_regex"] or $data["autoupdate_regex"] = "\n\nversion = /Version ([\d.]+)/\n\nchanges = http://example.org/news.html\nchanges = $('article pre#release')\nchanges = ~ ((add|fix|change) \V+) ~mix*"; $current_date = strftime("%Y-%m-%d", time()); $select = "form_select_options"; print<<<FORM <style> /** * page-specific layout * */ .autoupdate-alternatives { border-spacing: 5pt; } .autoupdate-alternatives td { padding: 3pt; width: 25%; vertical-align: top; background: #fcfcfc linear-gradient(to bottom, #f7f0e9, #fff); box-shadow: 2px 2px 3px 1px #f9f5f1; border-radius: 10pt; font-size: 95%; } .autoupdate-alternatives td .hidden { position: absolute; display: none; } .autoupdate-alternatives td:hover .hidden { display: block; } .autoupdate-alternatives td .hidden pre { position: relative; top: -30pt; left: -30pt; padding: 7pt; border: 7px solid #111; border-radius: 7pt; background: #f7f7f7; background-image: radial-gradient(circle at 50% 50%, rgb(255,255,255), rgb(244,244,244)); } li { padding: 1.5pt; } </style> <h3>Dr. Changelog</h3> <form action=drchangelog method=POST> <img src=img/drchangelog.png align=right alt="birdy big eyes" title="Don't ask me, I'm just a pictogram."> <p> Freshcode.club can automatically track your software releases. There are <a href="http://fossil.include-once.org/freshcode/wiki/Autoupdate">various alternatives for</a> uncovering them. Try them out. <label> Retrieval method <select name=autoupdate_module> {$select("release.json,changelog,regex,github,sourceforge", $data["autoupdate_module"])} </select> </label> <table class=autoupdate-alternatives><tr> <td> <a href="http://fossil.include-once.org/freshcode/wiki/releases.json"><em>releases.json</em></a> defines a concrete scheme for publishing version and release notes. <span class=hidden><pre> { "version": "1.0.0", "changes": "Fixes and adds lots of new functions ..", "state": "stable", "scope": "major feature", "download": "http://exmpl.org/" } </pre></span> </td> <td>While a <a href="http://fossil.include-once.org/freshcode/wiki/AutoupdateChangelog"><em>Changelog</em></a> text file is likely the easiest, given a coherent format and style. <span class=hidden><pre> 1.0.0 ($current_date) ------------------ * Changes foo and bar. + Adds baz baz. - Some more bugs removed. 0.9.9 (2014-02-27) ------------------ * Now uses Qt5 - Removed all the bugs 0.9.1 (2014-01-20) ------------------ * Initial release with </pre></span> </td> <td><a href="http://fossil.include-once.org/freshcode/wiki/AutoupdateGithub"><em>Github</em></a> extraction prefers <nobr>/releases</nobr> notes. But may as last resort condense a git commit log. <span class=hidden><pre><a href="https://github.com/blog/1547-release-your-software"><img src="https://camo.githubusercontent.com/9f23f54df9e2f69047fb0f9f80b2e33c8339606f/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f32312f3733373136362f62643163623637652d653332392d313165322d393064312d3361656365653930373339662e6a7067" width=400 height=200></a></pre></span> </td> <td>Using <a href="http://fossil.include-once.org/freshcode/wiki/AutoupdateRegex"><em>regex/xpath</em></a> is however the most universal way to extract from project websites. <span class=hidden><pre> <span style=color:gray># load page</span> changes = http://exmpl/news <span style=color:gray># jQuery</span> changes = $("body .release") <span style=color:gray># RegExp</span> version = /Version \d+\.\d+/ </pre></span> </td> </tr></table> </p> <p> <label> Autoupdate URL <input name=autoupdate_url type=url size=80 value="$data[autoupdate_url]" placeholder="https://github.com/user/repo/tags.atom" maxlength=250> </label> Add the URL to your Changelog, releases.json, or GitHub project here. For the regex method this will also be the first page to be extracted from. </p> <p> <h4>Content Scraping</h4> Picking out from your own project website can be surprisingly simple. Define a list for at least <code>version = ...</code> and <code>changes = ...</code> - Add source URLs and apply <a href="http://fossil.include-once.org/freshcode/wiki/AutoupdateRegex"> RegExp, XPath, or jQuery</a> selectors for extraction. <label> Extraction Rules <em>(URLs, Regex, Xpath, jQuery)</em> <textarea cols=67 rows=10 name=autoupdate_regex placeholder="version = /-(\d+\.\d+\.\d+)\.txz/" maxlength=2500>$data[autoupdate_regex]</textarea> <small> <li>Assigning new URLs is only necessary when there's different data to extract from.</li> <li>RegExps like <code>version = /Changes for ([\d.]+)/</code> often match headlines well.</li> <li>A common XPath rule for extracting the first bullet point list is <code>changes = (//ul)[1]/li</code>.</li> <li>While <code>changes = $("section#main article .release")</code> narrows it down for HTML pages.</li> <li>You often can mix extractors, first an XPath/jQuery expression, then a RegExp.</li> <li>Rules for state=, scope= and download= are optional.</li> </small> </label> </p> <p> <input type=submit name=test value=Test-Run> </p> </form> FORM; } include("template/bottom.php"); ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted page_forum.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
<?php /** * api: freshcode * type: main * title: meta/forum * description: Simple threaded discussion / documentation forum. * version: 0.2 * * Distinct layout from main site and harbours its own dispatcher. * Editing/post features. CSS is melted in, as there's no subpaging. * */ #-- custom config include_once("./shared.phar"); // autoloader define("INPUT_QUIET", 1) and include_once("lib/input.php"); // input filter define("HTTP_HOST", $_SERVER->id["HTTP_HOST"]); include_once("lib/deferred_openid_session.php"); // auth+session include_once("aux.php"); // utility functions include_once("config.local.php"); include_once("lib/db.php"); // database API db(new PDO("sqlite:forum.db")); // separate storage #-- set up forum handling $f = new forum(); $f->is_admin = in_array(@$_SESSION["openid"], $moderator_ids); #-- dispatch functions switch ($name = $_GET->id["name"]) { case "submit": exit( $f->submit() ); case "post": exit( $f->submit_form($_REQUEST->int["pid"], 0) ); case "edit": exit( $f->edit_form(0, $_REQUEST->int["id"]) ); case "index": case "": default: // handled below per default } ?> <!DOCTYPE html> <html> <head> <title>freshcode.club forum</title> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="gimmicks.js?019"></script> <meta charset=UTF-8> <?= "<style>\n" . file_get_contents("forum.css") . "</style>"; ?> </head> <body> <div id=title> <h1><a href="/"><b>fresh</b>(code)<b class=red>.</b><span class=grey>club</span></a></h1> </div> <br> <ul class=forum> <li> <div class=entry> <a class="action forum-new" data-id=0>New Thread</a> </div> </li> <?php $f->index(); ?> </ul> </body> </html> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted page_githubreleases.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
<?php /** * api: freshcode * title: github-releases * description: dump releases feed * version: 0.3 * * Shows the summarized GitHub releases from the stored template dump * (updated by cron.daily/news_github.php from GHA and cache DB.) * */ include("template/header.php"); ?> <style> .github-releases { width: 100%; } #githubreleases { table-layout: fixed; width: 100%; } .github.release td { padding: 4pt 1pt; font-size: 95%; overflow: hidden; text-overflow: ellipsis; box-shadow: none; } .github.release .author-avatar img { border-radius: 4pt; } .github.release .repo-name { } .github.release .repo-name small { display: block; font-size: 85%; color: #555; } .github.release .repo-name strong { font-weight: 400; display: block; } .github.release .repo-description { font-size: 90%; } .github.release .repo-homepage { font-size: 70%; display: block; color: #b0b0f0; } .github.release .release-tag { font-weight: 700; } .github.release .release-body { font: 70%/80% normal; max-height: 25pt; color: #999; } .github-releases .repo-language { font-size: 60%; padding: 0.5pt 1pt; border: dotted 1px #eef; background: #f1f3ff; color: #aae; } </style> <section id=main style="width:70%"> <h2>GitHub Releases</h2> <article class=github-releases> <table id=githubreleases> <colgroup> <col width="5%"> <col width="25%"> <col width="35%"> <col width="35%"> </colgroup> <?php include("template/github-releases.htm"); ?> </table> </article> <p style="break: both; clear: all; background: #f3f5f7; padding: 20pt;;"> Project information courtesy of <a href="http://githubarchive.org/">http://githubarchive.org/</a> and the GitHub API. </p> <?php include("template/bottom.php"); |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted page_submit.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
<?php /** * api: freshcode * type: page * title: Submit/edit project or release * description: Single-page edit form for projects and their releases * version: 0.7.0 * category: form * license: AGPLv3 * * Prepares the submission form. On POST checks a few constraints, * but UPDATE itself is handled by release::update() and ::store(). * * Tags: http://aehlke.github.io/tag-it/ * */ // Form field names $form_fields = array( "name", "title", "homepage", "description", "license", "tags", "image", "version", "state", "scope", "changes", "download", "urls", "autoupdate_module", "autoupdate_url", "autoupdate_regex", "submitter", "lock", ); // Get project ID from request $name = $_REQUEST->proj_name->length…3…33["name"]; // Retrieve existing project data in DB. $data = release::latest($name); $is_new = empty($data); // Else create empty form value defaults in $data if ($is_new) { $data = array_fill_keys($form_fields, ""); $data["name"] = $name; $data["submitter"] = $_SESSION["name"]; // Optional: import initial $data from elsewhere if ($_POST->has("import_via")) { $data = array_merge($data, project_import::fetch()); } } // Project entry can be locked for editing by specific OpenIDs. if (!release::permission($data, $_SESSION["openid"])) { $error = "This entry cannot be edited with your current <a href='/login'>login</a>. Its original author registered a different one. If your OpenID provider login fails to work, please flag for for moderator attention."; exit(include("page_error.php")); } // Start page output include("template/header.php"); include("template/submit_sidebar.php"); /** * Fetch form input on submit. * Check some constraints. * Then insert into database. * */ if ($name and $_REQUEST->has("title", "description")) { // Check field lengths if (!$_REQUEST->multi->serialize->length…150…150->strlen["title,description,homepage,changes"]) { print("<h3>Submission too short</h3> <p>You didn't fill out crucial information. Please note that our user base expects an enticing set of data points to find your project.</p>"); } // Terms and conditions elseif (array_sum($_REQUEST->array->int->range…0…1["req"]) < 2) { print "<h3>Terms and Conditions</h3> <p>Please go back and assert that your open source project listing is reusable under the CC-BY-SA license.</p>"; } elseif (!csrf(TRUE)) { print "<h3>CSRF token invalid</h3> <p>This is likely a session timeout (1 hour), etc. Please retry or login again.</p>"; } // Passed else { // Merge new data $release = new release($data); $release->update( $_REQUEST, array( "flag" => 0, // User flags presumably become obsolete when project gets manually edited "submitter_openid" => $_SESSION["openid"], "via" => "form", ) ); // Update project if ($release->store()) { print "<h2>Submitted</h2> <p>Your project and release informations have been saved.</p> <p>See the result in <a href=\"http://freshcode.club/projects/$name\">http://freshcode.club/projects/$name</a>.</p>"; } else { print "Unspecified database error. Please retry later."; } } } #-- Output input form with current $data else { $data = array_map("input::html", $data); include("template/submit_form.php"); } include("template/bottom.php"); ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted submit_import.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
<?php /** * api: freshcode * title: Import project description * description: Allow DOAP/JSON/etc. import prior manual /submit form intake. * version: 0.5 * * * Checks for uploaded $_FILES or ?import_url= * → Deciphers project name, description, license, tags, etc. * → Passes on extra $data to /submit <form> * */ define("UP_IMPORT_TYPE", "import_via"); define("UP_IMPORT_FILE", "import_file"); define("UP_IMPORT_NAME", "import_name"); /** * Invoked by page_submit itself to populate any empty $data set. * */ class project_import { /** * Evaluate request params, and import data if any. * */ static function fetch($data=NULL) { #-- file upload? if (!empty($_FILES[UP_IMPORT_FILE]["tmp_name"])) { $data = file_get_contents($_FILES[UP_IMPORT_FILE]["tmp_name"]); } #-- import scheme, and project name $type = $_REQUEST->id[UP_IMPORT_TYPE]; $name = $_REQUEST->text[UP_IMPORT_NAME]; if ($type and ($data or $name)) { $i = new self; return (array)@($i->convert($type, $data, $name)); } else { return array(); } } /** * Dispatch to submodules. * */ function convert($type, $data, $name) { #-- switch to fetch methods switch (strtoupper($type)) { case "JSON": return $this->JSON($data); case "PKG-INFO": case "PKGINFO": case "LSM": case "DEBIAN": case "RPMSPEC": return $this->PKG_INFO($data); case "DOAP": return $this->DOAP($data); case "FREECODE": return $this->FREECODE($name); case "SOURCEFORGE": return $this->SOURCEFORGE($name); default: return array(); } } /** * Extract from common JSON formats. * * release.json common.js package.json bower.json composer.json pypi.json * ------------- ------------- ------------- ------------- --------------- ------------- * name name name name name name * version version version version version version * title * description description description description description description * homepage homepage homepage homepage homepage home_page * license licenses* license license* license license * image * state classifiers * download repository repository download_url * urls* repositories repositories release_url * tags keywords keywords keywords keywords keywords * trove classifiers * */ function JSON($data) { // check if it is actually json if ($data = json_decode($data, TRUE)) { // rename a few plain fields $map = array( "name" => "title", // title is commonly absent "screenshot" => "image", "home_page" => "homepage", // pypi "download_url" => "download", // pypi "summary" => "description", // pypi "release_url" => "urls", // pypi ); foreach ($map as $old=>$new) { if (empty($data[$new]) and !empty($data[$old])) { $data[$to] = $data[$from]; } } // complex mapping $map = array( "keywords" => "tags", "classifiers" => "tags", "licenses" => "license", "license" => "license", "repository" => "urls", "repositories" => "urls", "urls" => "urls", ); foreach ($map as $old=>$new) { if (!empty($data[$old])) { switch ($old) { // keywords (common.js, composer.json) become tags case "keywords": $data[$new] = strtolower(join(", ", $data[$old])); break; // Trove classifiers (pypi) case "classifiers": $data[$new] = tags::trove_to_tags($data[$old]); break; // license alias // see spdx.org case "licenses": case "license": while (is_array($data[$old])) { $data[$old] = current($data[$old]); } $data[$new] = tags::map_license($data[$old]); break; // URLs case "repository": $data[$new] = $data[$old]["type"] . "=" . $data[$old]["url"] . "\n"; break; case "repositories": $data[$new] = http_build_query(array_column($data[$old], "url", "type"), "", "\n"); break; case "urls": is_array($data[$old]) and $data[$new] = http_build_query(array_column($data[$old], "url", "packagetype"), "", "\n"); break; } } } // common fields from releases.json are just kept asis $asis = array( "name", "title", "homepage", "description", "license", "tags", "image", "version", "state", "scope", "changes", "download", "urls", "autoupdate_module", "autoupdate_url", "autoupdate_regex", "submitter", "lock", ); // done return( array_filter( array_intersect_key($data, array_flip($asis)), "is_string" ) ); } } /** * Extracts from PKG-INFO and other RFC822-style text files. * * used PKG-INFO LSM Debian RPMSpec * ---- ------------- ------------- ------------ ------- * → Name Title Package Name * → Version Version Version Version * → Description Description Description * → Summary Summary * → Home-Page Primary-Site Homepage URL * Author Author Vendor * → License Coding-Policy Copyright * → Keywords Keywords Section Group * Classifiers * → Platform Platforms * * [1] http://legacy.python.org/dev/peps/pep-0345/ * [2] http://lsm.execpc.com/LSM.README * [3] http://www.debian.org/doc/debian-policy/ch-controlfields.html * [4] http://www.rpm.org/max-rpm/s1-rpm-build-creating-spec-file.html * */ function PKG_INFO($data) { // Simple KEY: VALUE format (value may span multiple lines). preg_match_all("/ ^ %? ([\w-]+): \s* (.+ (?:\R[\v].+$)* ) $ /xm", $data, $uu ) and $data = array_change_key_case(array_combine($uu[1], $uu[2]), CASE_LOWER); // Test if it's PKG-INFO if (!empty($data["description"])) { return array( "title" => $data["name"] ?: $data["title"], "version" => $data["version"], "description" => $data["description"] ?: $data["summary"], "tags" => preg_replace("/[\s,;]+/", ", ", "$data[platform], $data[keywords]"), # "trove-tags" => $data["classifiers"], "homepage" => $data["home-page"] ?: $data["url"] ?: $data["homepage"] ?: $data["primary-site"], "download" => $data["download-url"], "license" => tags::map_license($data["license"] ?: $data["coding-policy"] ?: $data["copyright"]), ); } } /** * Import from DOAP description. * * Would actually require a RDF toolkit, * but for the simple use case here, it's just processed namespace-unaware as xml. * */ function DOAP($data) { if ($x = simplexml_load_string($data)->Project) { $x = array( "name" => strval($x->shortname), "title" => strval($x->name), "description" => strval($x->description ?: $x->shortdesc), "homepage" => strval($x->homepage["resource"]), "download" => strval($x->{'download-page'}["resource"]), "tags" => strval($x->{'programming-language'}) .", ". strval($x->category["resource"]), "license" => tags::map_license(basename(strval($x->license["resource"]))), "version" => strval($x->release->Version->revision), ); return $x; } } /** * Freecodes JSON API is gone, so we have to extract from the project * page itself. * */ function FREECODE($name) { // retrieve if ($html = curl("http://freecode.com/projects/$name")->exec()) { // regex extract to reduce false positives preg_match_all('~ <meta \s+ property="og:title" \s+ content="(?<title>[^"]+)" | <meta \s+ name="keywords" \s+ content="(?<tags>[^"]+)" | class="project-detail"> \s+ <p> (?<description>[^<>]+)</p> | >Licenses< .+? rel="tag"> (?<license>[^<>]+)</a> | >Implementation< .+? rel="tag"> (?<lang>[^<>]+)</a> ~smix', $html, $uu, PREG_SET_ORDER); // join fields if (!empty($uu[0][0])) { $uu = call_user_func_array("array_merge", array_map("array_filter", $uu)); return array( "name" => $name, "title" => $uu["title"], "description" => $uu["description"], "tags" => strtolower((!empty($uu["lang"]) ? "$uu[lang], " : "") . $uu["tags"]), "license" => tags::map_license($uu["license"]), ); } } } /** * Sourceforge still provides a JSON export. * */ function SOURCEFORGE($name) { // retrieve if ($data = json_decode(curl("https://sourceforge.net/rest/p/$name")->exec(), TRUE)) { // custom json extraction return array( "name" => $data["shortname"], "title" => $data["name"], "homepage" => $data["external_homepage"] ?: $data["url"], "description" => $data["short_description"], "image" => $data["screenshots"][0]["thumbnail_url"], "license" => tags::map_license($data["categories"]["license"][0]["fullname"]), "tags" => implode(", ", array_merge( array_column($data["categories"]["language"], "shortname"), array_column($data["categories"]["environment"], "fullname"), array_column($data["categories"]["topic"], "shortname") ) ), "state" => $data["categories"]["developmentstatus"][0]["shortname"], "urls" => "SourceForge = https://sourceforge.net/projects/$name\nGitHub = \n", ); } } } #print_r((new project_import)->freecode("firefox")); ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to template/bottom.php.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<footer id=spotlight> <?php include("template/spotlight.htm"); ?> </footer> <footer id=bottom> <a href="http://fossil.include-once.org/freshcode/wiki/About">About</a> | <a href="http://fossil.include-once.org/freshcode/wiki/Privacy">Privacy / Policy</a> | <a href="http://fossil.include-once.org/freshcode/wiki/Contribute">Contribute</a> | <small> <a href="/login"><i>optional</i> Login</a> </small> <small style=float:right> <span style="display:inline-block; vertical-align:middle;">bookmark<br>freshcode</span> on <?php print social_share_links("freshcode", "http://freshcode.club/"); ?> </small> <br> <small style="font-size:90%"> This is a non-commercial project. <br> All project entries are licensed as CC-BY-SA. There will be <a href="/feed">/atom+json feeds</a>.. </small> </footer> </html> |
< < | | < < |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<footer id=spotlight> <?php include("template/spotlight.htm"); ?> </footer> <footer id=bottom> <a href="http://fossil.include-once.org/freshcode/wiki/About">About</a> | <small> <!--a href="/login"><i>optional</i> Login</a--> </small> <small style=float:right> <span style="display:inline-block; vertical-align:middle;">bookmark<br>freshcode</span> on <?php print social_share_links("freshcode", "http://opensourcestore.org/"); ?> </small> <br> <small style="font-size:90%"> All project entries are licensed as CC-BY-SA. There will be <a href="/feed">/atom+json feeds</a>.. </small> </footer> </html> |
Deleted template/forum_entry.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php /** * api: FTD * title: Single forum post * description: * */ $_ = "trim"; print <<<HTML <li> <article class=entry> <h6 class=summary><b>$entry[summary]</b> <span class="funcs trimmed"> <a class="action forum-edit" data-id=$entry[id] title="Edit">Ed</a> <a class="action forum-reply" data-id=$entry[id] title="Reply">Re</a> </span> </h6> <aside class=meta><div> <b class=category>$entry[tag]</b> <i class=author> <img align=top src="$entry[miniature]" width=16 height=16> $entry[author] </i> <var class=datetime>{$_(strftime("%Y-%m-%d - %H:%M",$entry["t_published"]))}</var> </div></aside> <div class="excerpt">$entry[excerpt]…</div> <div class="content trimmed">$entry[html]</div> </article> HTML; |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted template/forum_submit_form.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<?php /** * api: ftt * type: template * title: Post submit/edit form * description: Outputs input form for new / reply / editing forum posts. * * */ ?> <!-- faux --> <form action=spex method=POST style="displaY: None"> <input type="hidden" name="back" value="index" /> <input type="hidden" name="mode" value="posting" /> <input type="hidden" name="id" value="0" /> <input type="hidden" name="posting_mode" value="0" /> <input type="text" size="40" name="name" value="" maxlength="40"> <input type="text" size="40" name="email" value=""> <input type="text" size="40" name="homepage" value=""> <input id="subject" type="text" size="50" name="subject" value=""> <textarea cols="80" rows="21" name="comment"></textarea> <input type="submit" name="save_entry" value="OK - Submit" title="Save entry"> </form> <!-- actual --> <form class=forum-submit action=none style="display: run-in"> <label> <b>Author</b> <input name=author placeholder=your-name size=50 value="<?=$author?>"> </label> <label> <b><select name=img_type><option>gravatar<option>identicon<option>monsterid<option>wavatar<option>retro</select></b> <input name=image type=email placeholder="you@example.com" size=50 value="<?=$image?>"> </label> <label> <b>Category</b> <select name=tag><?=form_select_options($forum_cfg["categories"], $tag)?></select> </label> <label> <b>Summary</b> <input name=summary placeholder="..." size=60 value="<?=$summary?>"> </label> <label> <span style="position: absolute"> <div class="markup-buttons"> <a class="action markup" style="font-style: italic" data-before="*" data-after="*">italic</a> <a class="action markup" style="font-weight: bold" data-before="**" data-after="**">bold</a> <a class="action markup" style="text-decoration: underline" data-before="[" data-after="](http://example.org/)">link</a> <a class="action markup" style="" data-before="`" data-after="`">{code}</a> <a class="action markup" style="" data-before="\n * " data-after="">• list</a> </div> </span> <b>Message</b> <textarea name=source cols=55 rows=12><?=$source?></textarea> </label> <input type=hidden name=id value="<?=$id?>"> <input type=hidden name=pid value="<?=$pid?>"> <button class="action forum-submit">Follow The Thread</button> <br> </form> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to template/header.php.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
* page $title. * */ ?> <!DOCTYPE html> <html> <head> <title><?= isset($title) ? $title : "freshcode.club" ?></title> <meta name=version content=0.7.4> <meta charset=UTF-8> <link rel=stylesheet href="/freshcode.css?0.7.3"> <link rel="shortcut icon" href="/img/changes.png"> <base href="/index"> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <!--[if lt IE 9]><script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js"></script><![endif]--> ................................................................................ <script src="/gimmicks.js?2"></script> <?php if (isset($header_add)) { print $header_add . "\n"; } ?> </head> <body> <nav id=topbar> Open source software release tracking. <?= is_int(strpos(HTTP_HOST, ".")) ? '<small style="color:#9c7" class=version>[0.7.4 alpha]</small>' : '<b style="color:#c54">[local dev]</b>'; ?> <span style=float:right> <a href="//freshmeat.club/">freshmeat.club</a> | <a href="//freecode.club/">freecode.club</a> | <b><a href="//freshcode.club/">freshcode.club</a></b> </span> </nav> <footer id=logo> <a href="/" title="freshcode.club"><img src="img/logo.png" width=200 height=110 alt=freshcode border=0></a> <?=file_get_contents("template/stats.htm");?> </footer> <nav id=tools> <a href="/">Home</a> <a href="/submit" class=submit>Submit</a> <span class=submenu> <a href="/names">Browse</a> <a href="/tags" style="left:-5pt;">Projects by Tag</a> </span> <form id=search_q style="display:inline" action=search><input name=q size=5><a href="/search">Search</a></form> <a href="//fossil.include-once.org/freshcode/wiki/About">About</a> <a href="/links">Links</a> <a href="/meta" class=meta>Meta</a> </nav> |
|
|
|
<
|
>
|
<
<
|
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
* page $title. * */ ?> <!DOCTYPE html> <html> <head> <title><?= isset($title) ? $title : "opensourcestore.org" ?></title> <meta name=version content=0.7.4> <meta charset=UTF-8> <link rel=stylesheet href="/freshcode.css?0.7.3"> <link rel="shortcut icon" href="/img/changes.png"> <base href="/index"> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <!--[if lt IE 9]><script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js"></script><![endif]--> ................................................................................ <script src="/gimmicks.js?2"></script> <?php if (isset($header_add)) { print $header_add . "\n"; } ?> </head> <body> <nav id=topbar> Open source software release tracking. <?= is_int(strpos(HTTP_HOST, ".")) ? '<small style="color:#9c7" class=version>[0.7.4]</small>' : '<b style="color:#c54">[local dev]</b>'; ?> <span style=float:right> <a href="http://fossies.org/">fossies.org</a> | <a href="http://freshcode.club/">freshcode.club</a> | <b><a href="//opensourcestore.org/">opensourcestore.org</a></b> </span> </nav> <footer id=logo> <a href="/" title="freshcode.club"><img src="img/logo.png" width=200 height=110 alt=freshcode border=0></a> <?=file_get_contents("template/stats.htm");?> </footer> <nav id=tools> <a href="/">Home</a> <a href="#" onclick="void(window.location='http://fresh'+'code.club'+'/submit')" class=submit>Submit</a> <span class=submenu> <a href="/names">Browse</a> <a href="/tags" style="left:-5pt;">Projects by Tag</a> </span> <form id=search_q style="display:inline" action=search><input name=q size=5><a href="/search">Search</a></form> <a href="/links">Links</a> </nav> |
Changes to template/projects_sidebar.php.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<section> <h5>Submitted by</h5> <a class=submitter href="/search?user=$entry[submitter]">$entry[submitter_img]$entry[submitter]</a><br> </section> <section style="font-size:90%"> <h5>Manage</h5> You can also help out here by:<br> <a class=long-links href="/submit/$entry[name]" style="display:inline-block; margin: 3pt 1pt;">← Updating infos</a><br> or <a href="/flag/$entry[name]">flagging</a> this entry for moderator attention. </section> <section style="font-size:90%"> <h5>Share project {$_(social_share_count($entry["social_links"]))}</h5> {$_(social_share_links($entry["name"], $entry["homepage"]))} </section> </aside> <section id=main> SIDEBAR; ?> |
| | | < |
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<section> <h5>Submitted by</h5> <a class=submitter href="/search?user=$entry[submitter]">$entry[submitter_img]$entry[submitter]</a><br> </section> <section style="font-size:90%"> <h5>Update</h5> You can update this project information, or new releases<br> <a class=long-links href="#" onclick="void(window.location = 'http://freshcode.club/'+'submit/$entry[name]')" style="display:inline-block; margin: 3pt 1pt;">← Updating infos</a> </section> <section style="font-size:90%"> <h5>Share project {$_(social_share_count($entry["social_links"]))}</h5> {$_(social_share_links($entry["name"], $entry["homepage"]))} </section> </aside> <section id=main> SIDEBAR; ?> |
Deleted template/submit_form.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
<?php /** * api: freshcode * type: template * title: Project submit/edit form * description: Input fields for project description/release editing. * version: 0.5 * x-func-req: form_select_options * x-var-req: tags::$licenses * * * Expects previous or empty field set in $data. * → * * Also prints out a trivial diversion form for crawlbots. * */ // inline placeholders if (!strlen(trim($data["urls"]))) { $data["urls"] = "wiki = \r\ngithub = \r\nrelease-notes = \r\n"; } // editors note $editor_note = $data["editor_note"] ? "<var class=editor-note>$data[editor_note]</var>" : ""; // varexpressions $select = "form_select_options"; $_ = "trim"; // output print <<<HTML <span class="PageRank" style="DisplaY: nOne; VisiBility: HiddEN;"> Please bots, submit your recommended link here: <br /> <form action="/submit/pagerank" method="POST"> Name: <input name="name" value="" /> <br/> Email: <input name="email" value="" /> <br/> Website: <input name="link" value="http://" /> <br/> Comment: <textarea name="comment"></textarea> <br/> <input type="submit" name="submit" value="Send" /> </form><hr/> (Real form follows...) </span> <form action="" method=POST enctype="multipart/form-data" accept-encoding=UTF-8 rel=nofollow> <input type=hidden name=is_new value=$is_new> <h3>General Project Info</h3> {$editor_note} <p> <label> Project ID <input name=name size=20 placeholder=projectname value="$data[name]" maxlength=33 required pattern="^\w[-_\w]+\w(\.\w{2,7})?$"> <small>A short moniker which becomes your http://freshcode.club/projects/<var>name</var>.<br> <small>May contain letters, numbers, hyphen or underscore.</small></small> </label> <label> Title <input name=title size=50 placeholder="Awesome Software" value="$data[title]" maxlength=100 required> </label> <label> Homepage <input name=homepage size=50 type=url placeholder="http://project.example.org/" value="$data[homepage]" maxlength=250> </label> <label> Description <textarea cols=55 rows=9 name=description maxlength=1500 required>$data[description]</textarea> <small>Please give a concise roundup of what this software does, what specific features it provides, the intended target audience, or how it compares to similar apps.</small> </label> <label> License <select name=license> {$select(tags::$licenses, $data["license"])} </select> <small>Again note that FLOSS is preferred.</small> </label> <label> Tags<br> <input id=tags name=tags size=50 placeholder="game, desktop, gtk, python" value="$data[tags]" maxlength=150 pattern="^\s*((c\+\+|\w+([-.]\w+)*(\[,;\s]+)?){0,10}\s*$" style="display:inline-block"> <span style="inline-block; height: 0px; overflow: visible; position: absolute;"> <img src=img/addtrove.png with=100 height=150 style="position:relative;top:-150px;"> <span id=trove_tags class=add-tags>{$_(tags::trove_select(tags::$tree))}</span> </span> <small style="width:60%">Categorize your project. Tags can be made up of letters, numbers and dashes. This can include usage context, application type, programming languages, related projects, etc.</small> </label> <label> Image <input type=url name=image size=50 placeholder="http://i.imgur.com/xyzbar.png" value="$data[image]" maxlength=250> <small>Previews will be 120x90 px large. Alternatively a homepage screenshot will appear later.</small> </label> </p> <h3>Release Submission</h3> <p> <label> Version <input name=version size=20 placeholder=2.0.1 value="$data[version]" maxlength=32> <small>Prefer <a href="http://semver.org/">semantic versioning</a> for releases.</small> </label> <label> State <select name=state> {$select("initial,alpha,beta,development,prerelease,stable,mature,historic", $data["state"])} </select> <small>Tells about the stability or target audience of the current release.</small> </label> <label> Scope <br> <select name=scope> {$select("minor feature,minor bugfix,major feature,major bugfix,security,documentation,cleanup,hidden", $data["scope"])} </select> <small>Indicate the significance and primary scope of this release.</small> </label> <label> Changes <textarea cols=60 rows=8 name=changes maxlength=2000>$data[changes]</textarea> <small>Summarize the changes in this release. Documentation additions are as crucial as new features or fixed issues.</small> </label> <label> Download URL <input name=download size=50 type=url placeholder="http://project.example.org/" value="$data[download]" maxlength=250> <small>In particular for the download link one could apply the <a class="action version-placeholder"><b><kbd>\$version</kbd></b> placeholder</a>.</small> </label> <label> Other URLs <textarea cols=60 rows=5 name=urls maxlength=2000>$data[urls]</textarea> <small>An ini-style list of URLs like <code>src = http://foo, deb = http://bar</code>. Use customized label tags, common link names include src / rpm / deb / txz / dvcs / release-notes / forum, etc. Either may contain a <a class="action version-placeholder">\$version placeholder</a> again.</small> </label> </p> <h3>Automatic Release Tracking</h3> <p> <em>You can skip this section.</em> But future release submissions can be automated, with a normalized Changelog, or <var>releases.json</var>, or an extraction ruleset <a href=/drchangelog class="action drchangelog"><img src=img/drchangelog.png width=37 height=37 align=right style="padding:5pt"></a> for your version control system or project homepage. See the <a href="http://fossil.include-once.org/freshcode/wiki/Autoupdate">Autoupdate Howto</a> or <a href=/drchangelog class="action drchangelog">Dr.Changelog</a>. </p> <p> <label> Via <select name=autoupdate_module> {$select("none,release.json,changelog,regex,github,sourceforge", $data["autoupdate_module"])} </select> </label> <label> Autoupdate URL <input name=autoupdate_url type=url size=50 value="$data[autoupdate_url]" placeholder="https://github.com/user/repo/Changelog.md" maxlength=250> <small>This is the primary source for <b>releases.json</b> or a <b>Changelog</b>. It's also initially used for <b>Regex</b> rules in absence of override URLs. GitHub and SourceForge links are usually autodiscovered.</small> </label> <label> Rules <span style="font-weight: 100">(URLs, Regex, XPath, jQuery)</span> <textarea cols=50 rows=3 name=autoupdate_regex placeholder="version = /foo-(\d+\.\d+\.\d+)\.txz/" maxlength=2500>$data[autoupdate_regex]</textarea> <small> <a href="http://fossil.include-once.org/freshcode/wiki/AutoupdateRegex">Regex/Xpath automated updates</a> expect a list of <code>field = ..</code> rules. Define an URL and then associated RegExp, XPath or jQuery selectors for the version= and changes= fields, and optionally for state=, scope= and download=.</small> </label> </p> <h3>Publish</h3> <p> Please proofread again before saving. <label> Submitter <input name=submitter size=50 placeholder="Your Name, optional@example.com" value="$data[submitter]" maxlength=100> <small>List your name or nick name here. Optionally add a gravatar email.</small> </label> <label> Lock Entry <input name=lock size=50 placeholder="$_SESSION[openid]" value="$data[lock]" maxlength=250> <small>Normally all projects can be edited by everyone (WikiStyle). If you commit to yours, you can however <a class="action lock-entry"><b>lock</b> this project</a> against one or multiple OpenID handles (comma-separated, take care to use exact URLs; or <a href="/login">log in</a> beforehand). Or add a password hash for using the submit API. </label> </p> <p> <b>Terms and Conditions</b> <label class=inline><input type=checkbox name="req[os]" value=1 required> It's open source / libre / Free software or pertains BSD/Linux.</label> <label class=inline><input type=checkbox name="req[cc]" value=1 required> Your entry is shareable under the <a href="http://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA</a> license.</label> </p> <p> <input type=submit value="Submit Project/Release"> {$_(csrf())} </p> <p style=margin-bottom:75pt> Thanks for your time and effort! </p> </form> HTML; ?> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted template/submit_import.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?php /** * type: template * title: Submit form import sidebar * description: Import function sidebar section. * * Local stylesheet addition for making it slightly less prominent * until hovered over. * */ ?> <style> .submit-import.trimmed { display: none; } </style> <form action="/submit" method=POST enctype="multipart/form-data" class="submit-import trimmed"> <section> <a> <h5>Import</h5> <p> Automatically fill in basic project description <label> From <select name=import_via style="font-size: 125%"><option title="releases.json, common.js, package.json, bower.json, composer.json">JSON<option title="Description of a Project XML">DOAP<option title="Python Package Info">PKG-INFO<option title="Freecode.com project listing">freecode<option title="Sourceforge.net project homepage">sourceforge</select> <small>Which file format or service to use for importing.</small> </label> <label> with Name <input type=text name=import_name placeholder=project-id maxlength=33 pattern="^[\w-_.]+$"> <small>Prior project name on freecode or sourceforge.</small> </label> <label> or File Upload <input type=file name=import_file size=5 placeholder="releases.json"> <small>Upload a project.json or .doap or PKG-INFO summary.</small> </label> <input type=submit value="Import and Edit"> </p> <p><small> But please don't perform mass-imports. When copying from freecode/sourceforge try to bring the description up to date. Edits ensure their reusability under the CC-BY-SA license. </small></p> </a> </section> </form> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Deleted template/submit_sidebar.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php /** * type: template * title: Project submit #sidebar * description: Generic advises for project submissions * * */ ?> <aside id=sidebar> <section> <h5>Submit project<br>and/or release</h5> <p> You can submit <em title="Free, Libre, and Open Source Software">FLOSS</em> or <em title="or Solaris/Darwin/Hurd">BSD/Linux</em> software here. It's not required that you're a developer of said project. </p> <p><small> You can always edit the common project information together with a current release. It will show up on the frontpage whenever you update a new version number and a changelog summary. </small></p> <?php if ($is_new) { print "<p>Or <a class='action submit-import' style='color:blue'>import</a> a project..</p>"; } ?> </section> <?php include("template/submit_import.php"); ?> </aside> <section id=main> |
< < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |