@ -0,0 +1,202 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
APPENDIX: How to apply the Apache License to your work. | |||
To apply the Apache License to your work, attach the following | |||
boilerplate notice, with the fields enclosed by brackets "{}" | |||
replaced with your own identifying information. (Don't include | |||
the brackets!) The text should be enclosed in the appropriate | |||
comment syntax for the file format. We also recommend that a | |||
file or class name and description of purpose be included on the | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
Copyright {yyyy} {name of copyright owner} | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. | |||
@ -0,0 +1,10 @@ | |||
Switchboard | |||
=========== | |||
Switchboard is a PubSubHubbub 4.0 Hub | |||
Credits | |||
------- | |||
@ -0,0 +1,24 @@ | |||
{ | |||
"require": { | |||
"slim/slim": "2.2.*", | |||
"simplepie/simplepie": "*", | |||
"saltybeagle/savant3": "dev-master", | |||
"j4mie/idiorm": "1.4.*", | |||
"mf2/mf2": "0.2.*", | |||
"barnabywalters/mf-cleaner": "0.*", | |||
"indieauth/client": "0.1.*", | |||
"pda/pheanstalk": "dev-master", | |||
"mpratt/relativetime": ">=1.0" | |||
}, | |||
"autoload": { | |||
"files": [ | |||
"lib/Savant.php", | |||
"lib/config.php", | |||
"lib/helpers.php", | |||
"lib/db_helpers.php", | |||
"lib/mf2_feed_parser.php", | |||
"lib/DeferredTask.php", | |||
"lib/request.php" | |||
] | |||
} | |||
} |
@ -0,0 +1,486 @@ | |||
{ | |||
"_readme": [ | |||
"This file locks the dependencies of your project to a known state", | |||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", | |||
"This file is @generated automatically" | |||
], | |||
"hash": "e97be02376b6b07beab7c771b815ca64", | |||
"packages": [ | |||
{ | |||
"name": "barnabywalters/mf-cleaner", | |||
"version": "v0.1.4", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/barnabywalters/php-mf-cleaner.git", | |||
"reference": "ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/barnabywalters/php-mf-cleaner/zipball/ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4", | |||
"reference": "ef6a16628db6e8aee2b4f8bb8093d18c24b74cd4", | |||
"shasum": "" | |||
}, | |||
"require-dev": { | |||
"php": ">=5.3", | |||
"phpunit/phpunit": "*" | |||
}, | |||
"suggest": { | |||
"mf2/mf2": "To parse microformats2 structures from (X)HTML" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"files": [ | |||
"src/BarnabyWalters/Mf2/Functions.php" | |||
] | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"MIT" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Barnaby Walters", | |||
"email": "barnaby@waterpigs.co.uk" | |||
} | |||
], | |||
"description": "Cleans up microformats2 array structures", | |||
"time": "2014-10-06 23:11:15" | |||
}, | |||
{ | |||
"name": "indieauth/client", | |||
"version": "0.1.8", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/indieweb/indieauth-client-php.git", | |||
"reference": "f979f8ee0fc6daaa6a393e7afdac894000d09544" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/indieweb/indieauth-client-php/zipball/f979f8ee0fc6daaa6a393e7afdac894000d09544", | |||
"reference": "f979f8ee0fc6daaa6a393e7afdac894000d09544", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"barnabywalters/mf-cleaner": "0.*", | |||
"indieweb/link-rel-parser": "0.1.1", | |||
"mf2/mf2": "0.2.*", | |||
"php": ">5.3.0" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"psr-0": { | |||
"IndieAuth": "src/" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"Apache 2.0" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Aaron Parecki", | |||
"homepage": "http://aaronparecki.com" | |||
} | |||
], | |||
"description": "IndieAuth Client Library", | |||
"time": "2014-10-06 07:37:10" | |||
}, | |||
{ | |||
"name": "indieweb/link-rel-parser", | |||
"version": "0.1.1", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/indieweb/link-rel-parser-php.git", | |||
"reference": "9e0e635fd301a8b1da7bc181f651f029c531dbb6" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/indieweb/link-rel-parser-php/zipball/9e0e635fd301a8b1da7bc181f651f029c531dbb6", | |||
"reference": "9e0e635fd301a8b1da7bc181f651f029c531dbb6", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.3.0" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"files": [ | |||
"src/IndieWeb/link_rel_parser.php" | |||
] | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"Apache-2.0" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Aaron Parecki", | |||
"homepage": "http://aaronparecki.com" | |||
}, | |||
{ | |||
"name": "Tantek รelik", | |||
"homepage": "http://tantek.com" | |||
} | |||
], | |||
"description": "Parse rel values from HTTP headers", | |||
"homepage": "https://github.com/indieweb/link-rel-parser-php", | |||
"keywords": [ | |||
"http", | |||
"indieweb", | |||
"microformats2" | |||
], | |||
"time": "2013-12-23 00:14:58" | |||
}, | |||
{ | |||
"name": "j4mie/idiorm", | |||
"version": "v1.4.1", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/j4mie/idiorm.git", | |||
"reference": "11e964157a6a2c6128a0546673ad5e99ac1a62cd" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/j4mie/idiorm/zipball/11e964157a6a2c6128a0546673ad5e99ac1a62cd", | |||
"reference": "11e964157a6a2c6128a0546673ad5e99ac1a62cd", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.2.0" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"classmap": [ | |||
"idiorm.php" | |||
] | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"BSD-2-Clause", | |||
"BSD-3-Clause", | |||
"BSD-4-Clause" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Simon Holywell", | |||
"email": "treffynnon@php.net", | |||
"homepage": "http://simonholywell.com", | |||
"role": "Maintainer" | |||
}, | |||
{ | |||
"name": "Jamie Matthews", | |||
"email": "jamie.matthews@gmail.com", | |||
"homepage": "http://j4mie.org", | |||
"role": "Developer" | |||
}, | |||
{ | |||
"name": "Durham Hale", | |||
"email": "me@durhamhale.com", | |||
"homepage": "http://durhamhale.com", | |||
"role": "Maintainer" | |||
} | |||
], | |||
"description": "A lightweight nearly-zero-configuration object-relational mapper and fluent query builder for PHP5", | |||
"homepage": "http://j4mie.github.com/idiormandparis", | |||
"keywords": [ | |||
"idiorm", | |||
"orm", | |||
"query builder" | |||
], | |||
"time": "2013-12-12 10:25:27" | |||
}, | |||
{ | |||
"name": "mf2/mf2", | |||
"version": "v0.2.9", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/indieweb/php-mf2.git", | |||
"reference": "ad1ee037555be7f3b2ea1d99e063e56207cf2a1d" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/indieweb/php-mf2/zipball/ad1ee037555be7f3b2ea1d99e063e56207cf2a1d", | |||
"reference": "ad1ee037555be7f3b2ea1d99e063e56207cf2a1d", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.3.0" | |||
}, | |||
"require-dev": { | |||
"phpunit/phpunit": "3.7.*" | |||
}, | |||
"suggest": { | |||
"barnabywalters/mf-cleaner": "To more easily handle the canonical data php-mf2 gives you" | |||
}, | |||
"bin": [ | |||
"bin/fetch-mf2", | |||
"bin/parse-mf2" | |||
], | |||
"type": "library", | |||
"autoload": { | |||
"files": [ | |||
"Mf2/Parser.php" | |||
] | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"MIT" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Barnaby Walters", | |||
"homepage": "http://waterpigs.co.uk" | |||
} | |||
], | |||
"description": "A pure, generic microformats2 parser โ makes HTML as easy to consume as a JSON API", | |||
"keywords": [ | |||
"html", | |||
"microformats", | |||
"microformats 2", | |||
"parser", | |||
"semantic" | |||
], | |||
"time": "2014-07-23 09:37:36" | |||
}, | |||
{ | |||
"name": "mpratt/relativetime", | |||
"version": "1.5", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/mpratt/RelativeTime.git", | |||
"reference": "01f5699947f21080bec439bbab63dfe39e2d3f3f" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/mpratt/RelativeTime/zipball/01f5699947f21080bec439bbab63dfe39e2d3f3f", | |||
"reference": "01f5699947f21080bec439bbab63dfe39e2d3f3f", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.3" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"psr-0": { | |||
"RelativeTime": "Lib/" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"MIT" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Michael Pratt", | |||
"email": "pratt@hablarmierda.net", | |||
"homepage": "http://www.michael-pratt.com", | |||
"role": "Author/Developer" | |||
} | |||
], | |||
"description": "A library that calculates the time difference between two dates and returns the result in words (Example: 5 minutes ago or 5 Minutes left). The library supports other languages aswell like Spanish and German.", | |||
"homepage": "https://github.com/mpratt/RelativeTime", | |||
"keywords": [ | |||
"ago", | |||
"date", | |||
"future", | |||
"interval", | |||
"relative", | |||
"time", | |||
"time-ago" | |||
], | |||
"time": "2015-01-17 18:39:31" | |||
}, | |||
{ | |||
"name": "pda/pheanstalk", | |||
"version": "dev-master", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/pda/pheanstalk.git", | |||
"reference": "eb6407431e09ddc1fe99ce40f61ee8d0e622cd22" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/pda/pheanstalk/zipball/eb6407431e09ddc1fe99ce40f61ee8d0e622cd22", | |||
"reference": "eb6407431e09ddc1fe99ce40f61ee8d0e622cd22", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.3.0" | |||
}, | |||
"require-dev": { | |||
"phpunit/phpunit": "~4.0" | |||
}, | |||
"type": "library", | |||
"extra": { | |||
"branch-alias": { | |||
"dev-master": "3.0-dev" | |||
} | |||
}, | |||
"autoload": { | |||
"psr-4": { | |||
"Pheanstalk\\": "src/" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"MIT" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Paul Annesley", | |||
"email": "paul@annesley.cc", | |||
"homepage": "http://paul.annesley.cc/", | |||
"role": "Developer" | |||
} | |||
], | |||
"description": "PHP client for beanstalkd queue", | |||
"homepage": "https://github.com/pda/pheanstalk", | |||
"keywords": [ | |||
"beanstalkd" | |||
], | |||
"time": "2015-03-08 22:40:31" | |||
}, | |||
{ | |||
"name": "saltybeagle/savant3", | |||
"version": "dev-master", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/saltybeagle/Savant3.git", | |||
"reference": "ebf4385bf44bec8c7a169571ac178f626017c466" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/saltybeagle/Savant3/zipball/ebf4385bf44bec8c7a169571ac178f626017c466", | |||
"reference": "ebf4385bf44bec8c7a169571ac178f626017c466", | |||
"shasum": "" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"classmap": [ | |||
"Savant3.php", | |||
"Savant3", | |||
"Savant3/resources" | |||
] | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"LGPL" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Brett Bieber", | |||
"email": "brett.bieber@gmail.com" | |||
} | |||
], | |||
"description": "Savant3 template engine", | |||
"time": "2014-01-07 17:10:32" | |||
}, | |||
{ | |||
"name": "simplepie/simplepie", | |||
"version": "1.3.1", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/simplepie/simplepie.git", | |||
"reference": "ce53709778bc1e2e4deda1651b66e5081398d5cc" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/simplepie/simplepie/zipball/ce53709778bc1e2e4deda1651b66e5081398d5cc", | |||
"reference": "ce53709778bc1e2e4deda1651b66e5081398d5cc", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.2.0" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"psr-0": { | |||
"SimplePie": "library" | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"BSD-3-Clause" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Ryan Parman", | |||
"homepage": "http://ryanparman.com/", | |||
"role": "Creator, alumnus developer" | |||
}, | |||
{ | |||
"name": "Geoffrey Sneddon", | |||
"homepage": "http://gsnedders.com/", | |||
"role": "Alumnus developer" | |||
}, | |||
{ | |||
"name": "Ryan McCue", | |||
"email": "me@ryanmccue.info", | |||
"homepage": "http://ryanmccue.info/", | |||
"role": "Developer" | |||
} | |||
], | |||
"description": "A simple Atom/RSS parsing library for PHP", | |||
"homepage": "http://simplepie.org/", | |||
"keywords": [ | |||
"atom", | |||
"feeds", | |||
"rss" | |||
], | |||
"time": "2012-10-30 17:54:03" | |||
}, | |||
{ | |||
"name": "slim/slim", | |||
"version": "2.2.0", | |||
"source": { | |||
"type": "git", | |||
"url": "https://github.com/slimphp/Slim.git", | |||
"reference": "b8181de1112a1e2f565b40158b621c34ded38053" | |||
}, | |||
"dist": { | |||
"type": "zip", | |||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/b8181de1112a1e2f565b40158b621c34ded38053", | |||
"reference": "b8181de1112a1e2f565b40158b621c34ded38053", | |||
"shasum": "" | |||
}, | |||
"require": { | |||
"php": ">=5.3.0" | |||
}, | |||
"type": "library", | |||
"autoload": { | |||
"psr-0": { | |||
"Slim": "." | |||
} | |||
}, | |||
"notification-url": "https://packagist.org/downloads/", | |||
"license": [ | |||
"MIT" | |||
], | |||
"authors": [ | |||
{ | |||
"name": "Josh Lockhart", | |||
"email": "info@joshlockhart.com", | |||
"homepage": "http://www.joshlockhart.com/" | |||
} | |||
], | |||
"description": "Slim Framework, a PHP micro framework", | |||
"homepage": "http://github.com/codeguy/Slim", | |||
"keywords": [ | |||
"microframework", | |||
"rest", | |||
"router" | |||
], | |||
"time": "2012-12-13 02:15:50" | |||
} | |||
], | |||
"packages-dev": [], | |||
"aliases": [], | |||
"minimum-stability": "stable", | |||
"stability-flags": { | |||
"saltybeagle/savant3": 20, | |||
"pda/pheanstalk": 20 | |||
}, | |||
"prefer-stable": false, | |||
"platform": [], | |||
"platform-dev": [] | |||
} |
@ -0,0 +1,6 @@ | |||
<?php | |||
/* | |||
session_set_cookie_params(86400*30); | |||
session_start(); | |||
*/ |
@ -0,0 +1,70 @@ | |||
<?php | |||
function push_error(&$app, $msg) { | |||
$app->response()->status(400); | |||
echo $msg . "\n"; | |||
die(); | |||
} | |||
$app->post('/', function() use($app) { | |||
$params = $app->request()->params(); | |||
switch(k($params, 'hub_mode')) { | |||
case 'subscribe': | |||
// Sanity check the request params | |||
$topic = k($params, 'hub_topic'); | |||
$callback = k($params, 'hub_callback'); | |||
if(!is_valid_push_url($topic)) { | |||
push_error($app, 'Topic URL was invalid'); | |||
} | |||
if(!is_valid_push_url($callback)) { | |||
push_error($app, 'Callback URL was invalid'); | |||
} | |||
// Make a HEAD request to the topic URL to check if it exists | |||
$topic_head = request\get_head($topic); | |||
if($topic_head) { | |||
if(request\response_is($topic_head['status'], 2)) { | |||
$app->response()->status(202); | |||
// Find or create the feed given the topic URL | |||
$feed = db\find_or_create('feeds', ['feed_url'=>$topic], [ | |||
'hash' => db\random_hash(), | |||
], true); | |||
print_r($feed); | |||
// Find or create the subscription for this callback URL and feed | |||
$subscription = db\find_or_create('subscriptions', ['feed_id'=>$feed->id, 'callback_url'=>$callback], [ | |||
'hash' => db\random_hash(), | |||
]); | |||
$subscription->date_requested = db\now(); | |||
$subscription->challenge = db\random_hash(); | |||
$subscription->save(); | |||
echo "The subscription request is being validated. Check the status here:\n"; | |||
echo Config::$base_url . '/subscription/' . $subscription->hash . "\n"; | |||
} else { | |||
$app->response()->status(400); | |||
echo "The topic URL returned a " . $topic_head['status'] . " status code\n"; | |||
} | |||
} else { | |||
push_error($app, 'There was a problem trying to verify the topic URL'); | |||
} | |||
break; | |||
case 'unsubscribe': | |||
break; | |||
} | |||
}); |
@ -0,0 +1,74 @@ | |||
<?php | |||
class DeferredTask { | |||
public static function run() { | |||
global $pcntl_continue; | |||
$tube = 'monocle-worker'; | |||
echo "PID " . posix_getpid() . " watching tube: " . $tube . "\n"; | |||
bs()->watch($tube)->ignore('default'); | |||
if(isset($pcntl_continue)) { | |||
while($pcntl_continue) | |||
{ | |||
if(($job=bs()->reserve(2)) == FALSE) | |||
continue; | |||
self::process($job); | |||
} // while true | |||
echo "\nBye from pid " . posix_getpid() . "!\n"; | |||
} else { | |||
if(($job=bs()->reserve())) { | |||
self::process($job); | |||
} | |||
} | |||
} | |||
public static function run_once() { | |||
$tube = 'monocle-worker'; | |||
echo "PID " . posix_getpid() . " watching tube: " . $tube . "\n"; | |||
bs()->watch($tube)->ignore('default'); | |||
if(($job=bs()->reserve())) { | |||
self::process($job); | |||
} | |||
echo "\nBye from pid " . posix_getpid() . "!\n"; | |||
} | |||
public static function queue($class, $method, $args=array(), $delay=0) { | |||
if(!is_array($args)) | |||
$args = array($args); | |||
bs()->putInTube('monocle-worker', | |||
json_encode(array('class'=>$class, 'method'=>$method, 'args'=>$args)), | |||
1024, // priority | |||
$delay, // delay | |||
300); // time to run | |||
} | |||
private static function process(&$jobData) { | |||
$data = json_decode($jobData->getData()); | |||
if(!is_object($data) || !property_exists($data, 'class')) { | |||
echo "Found bad job:\n"; | |||
print_r($data); | |||
echo "\n"; | |||
bs()->delete($jobData); | |||
return; | |||
} | |||
echo "===============================================\n"; | |||
echo "# Beginning job: " . $data->class . '::' . $data->method . "\n"; | |||
call_user_func_array(array($data->class, $data->method), $data->args); | |||
echo "\n# Job Complete\n-----------------------------------------------\n\n"; | |||
bs()->delete($jobData); | |||
} | |||
} |
@ -0,0 +1,95 @@ | |||
<?php | |||
/** | |||
* Slim - a micro PHP 5 framework | |||
* | |||
* @author Josh Lockhart | |||
* @link http://www.slimframework.com | |||
* @copyright 2011 Josh Lockhart | |||
* | |||
* MIT LICENSE | |||
* | |||
* Permission is hereby granted, free of charge, to any person obtaining | |||
* a copy of this software and associated documentation files (the | |||
* "Software"), to deal in the Software without restriction, including | |||
* without limitation the rights to use, copy, modify, merge, publish, | |||
* distribute, sublicense, and/or sell copies of the Software, and to | |||
* permit persons to whom the Software is furnished to do so, subject to | |||
* the following conditions: | |||
* | |||
* The above copyright notice and this permission notice shall be | |||
* included in all copies or substantial portions of the Software. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
*/ | |||
namespace Slim\Extras\Views; | |||
/** | |||
* SavantView | |||
* | |||
* The SavantView is a Custom View class that renders templates using the | |||
* Savant3 template language (http://phpsavant.com/). | |||
* | |||
* There are two fields that you, the developer, will need to change: | |||
* - savantDirectory | |||
* - savantOptions | |||
* | |||
* @package Slim | |||
* @author Matthew Callis <http://superfamicom.org/> | |||
*/ | |||
class Savant extends \Slim\View | |||
{ | |||
/** | |||
* @var string The path to the directory containing Savant3.php and the Savant3 folder without trailing slash. | |||
*/ | |||
public static $savantDirectory = null; | |||
/** | |||
* @var array The options for the Savant3 environment, see http://phpsavant.com/api/Savant3/ | |||
*/ | |||
public static $savantOptions = array('template_path' => 'templates'); | |||
/** | |||
* @var persistent instance of the Savant object | |||
*/ | |||
private static $savantInstance = null; | |||
/** | |||
* Renders a template using Savant3.php. | |||
* | |||
* @see View::render() | |||
* @param string $template The template name specified in Slim::render() | |||
* @return string | |||
*/ | |||
public function render($template) | |||
{ | |||
$savant = $this->getInstance(); | |||
$savant->assign($this->data); | |||
return $savant->fetch($template); | |||
} | |||
/** | |||
* Creates new Savant instance if it doesn't already exist, and returns it. | |||
* | |||
* @throws RuntimeException If Savant3 lib directory does not exist. | |||
* @return SavantInstance | |||
*/ | |||
private function getInstance() | |||
{ | |||
if (!self::$savantInstance) { | |||
if (!is_dir(self::$savantDirectory)) { | |||
throw new \RuntimeException('Cannot set the Savant lib directory : ' . self::$savantDirectory . '. Directory does not exist.'); | |||
} | |||
require_once self::$savantDirectory . '/Savant3.php'; | |||
self::$savantInstance = new \Savant3(self::$savantOptions); | |||
} | |||
return self::$savantInstance; | |||
} | |||
} |
@ -0,0 +1,16 @@ | |||
<?php | |||
class Config { | |||
public static $base_url = 'https://example.com'; | |||
public static $hostname = 'example.com'; | |||
public static $ssl = false; | |||
public static $dbHost = '127.0.0.1'; | |||
public static $dbName = 'switchboard'; | |||
public static $dbUsername = 'switchboard'; | |||
public static $dbPassword = ''; | |||
public static $beanstalkServer = '127.0.0.1'; | |||
public static $beanstalkPort = 11300; | |||
public static $defaultAuthorizationEndpoint = 'https://indieauth.com/auth'; | |||
} |
@ -0,0 +1,43 @@ | |||
<?php | |||
namespace db; | |||
use \ORM; | |||
function random_hash() { | |||
$len = 32; | |||
$alpha_numeric = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; | |||
return substr(str_shuffle(str_repeat($alpha_numeric, $len)), 0, $len); | |||
} | |||
function now() { | |||
return date('Y-m-d H:i:s'); | |||
} | |||
function set_updated(&$record) { | |||
$record->date_updated = now(); | |||
} | |||
function find_or_create($table, $where, $defaults, $autosave=false) { | |||
$item = ORM::for_table($table); | |||
// Where is an associative array of key/val combos | |||
foreach($where as $c=>$v) { | |||
$item = $item->where($c, $v); | |||
} | |||
$item = $item->find_one(); | |||
if(!$item) { | |||
$item = ORM::for_table($table)->create(); | |||
$item->date_created = now(); | |||
foreach($defaults as $k=>$v) { | |||
$item->{$k} = $v; | |||
} | |||
foreach($where as $k=>$v) { | |||
$item->{$k} = $v; | |||
} | |||
if($autosave) | |||
$item->save(); | |||
} | |||
return $item; | |||
} | |||
@ -0,0 +1,200 @@ | |||
<?php | |||
// Default timezone | |||
date_default_timezone_set('UTC'); | |||
ORM::configure('mysql:host=' . Config::$dbHost . ';dbname=' . Config::$dbName); | |||
ORM::configure('username', Config::$dbUsername); | |||
ORM::configure('password', Config::$dbPassword); | |||
function friendly_url($url) { | |||
return preg_replace(['/https?:\/\//','/\/$/'],'',$url); | |||
} | |||
function friendly_date($date_string, $tz_offset) { | |||
$date = new DateTime($date_string); | |||
if($tz_offset > 0) | |||
$date->add(new DateInterval('PT'.$tz_offset.'S')); | |||
elseif($tz_offset < 0) | |||
$date->sub(new DateInterval('PT'.abs($tz_offset).'S')); | |||
$tz = ($tz_offset < 0 ? '-' : '+') . sprintf('%02d:%02d', abs($tz_offset/60/60), ($tz_offset/60)%60); | |||
return $date->format('F j, Y g:ia') . ' ' . $tz; | |||
} | |||
function build_url($parsed_url) { | |||
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; | |||
$host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; | |||
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; | |||
$user = isset($parsed_url['user']) ? $parsed_url['user'] : ''; | |||
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : ''; | |||
$pass = ($user || $pass) ? "$pass@" : ''; | |||
$path = isset($parsed_url['path']) ? $parsed_url['path'] : ''; | |||
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; | |||
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; | |||
return "$scheme$user$pass$host$port$path$query$fragment"; | |||
} | |||
// Input: Any URL or string like "aaronparecki.com" | |||
// Output: Normlized URL (default to http if no scheme, force "/" path) | |||
// or return false if not a valid URL | |||
function normalize_url($url) { | |||
$me = parse_url($url); | |||
if(array_key_exists('path', $me) && $me['path'] == '') | |||
return false; | |||
// parse_url returns just "path" for naked domains | |||
if(count($me) == 1 && array_key_exists('path', $me)) { | |||
$me['host'] = $me['path']; | |||
unset($me['path']); | |||
} | |||
if(!array_key_exists('scheme', $me)) | |||
$me['scheme'] = 'http'; | |||
if(!array_key_exists('path', $me)) | |||
$me['path'] = '/'; | |||
// Invalid scheme | |||
if(!in_array($me['scheme'], array('http','https'))) | |||
return false; | |||
return build_url($me); | |||
} | |||
// Checks if a string is a valid URL that we recognize as a PuSH topic or callback. | |||
// Must be http or https | |||
// If there is no path, '/' is assumed | |||
function is_valid_push_url($url) { | |||
$url = parse_url($url); | |||
if(!array_key_exists('scheme', $url)) | |||
return false; | |||
if(!in_array($url['scheme'], ['http','https'])) | |||
return false; | |||
if(!array_key_exists('path', $url)) | |||
$url['path'] = '/'; | |||
return build_url($url); | |||
} | |||
function render($page, $data) { | |||
global $app; | |||
return $app->render('layout.php', array_merge($data, array('page' => $page))); | |||
}; | |||
function partial($template, $data=array(), $debug=false) { | |||
global $app; | |||
if($debug) { | |||
$tpl = new Savant3(\Slim\Extras\Views\Savant::$savantOptions); | |||
echo '<pre>' . $tpl->fetch($template . '.php') . '</pre>'; | |||
return ''; | |||
} | |||
ob_start(); | |||
$tpl = new Savant3(\Slim\Extras\Views\Savant::$savantOptions); | |||
foreach($data as $k=>$v) { | |||
$tpl->{$k} = $v; | |||
} | |||
$tpl->display($template . '.php'); | |||
return ob_get_clean(); | |||
} | |||
function json_response(&$app, $response) { | |||
$app->response()['Content-Type'] = 'application/json'; | |||
$app->response()->body(json_encode($response)); | |||
} | |||
function session($key) { | |||
if(array_key_exists($key, $_SESSION)) | |||
return $_SESSION[$key]; | |||
else | |||
return null; | |||
} | |||
function k($a, $k, $default=null) { | |||
if(is_array($k)) { | |||
$result = true; | |||
foreach($k as $key) { | |||
$result = $result && array_key_exists($key, $a); | |||
} | |||
return $result; | |||
} else { | |||
if(is_array($a) && array_key_exists($k, $a) && $a[$k]) | |||
return $a[$k]; | |||
elseif(is_object($a) && property_exists($a, $k) && $a->$k) | |||
return $a->$k; | |||
else | |||
return $default; | |||
} | |||
} | |||
function get_timezone($lat, $lng) { | |||
try { | |||
$ch = curl_init(); | |||
curl_setopt($ch, CURLOPT_URL, 'http://timezone-api.geoloqi.com/timezone/'.$lat.'/'.$lng); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
$response = curl_exec($ch); | |||
$tz = @json_decode($response); | |||
if($tz) | |||
return new DateTimeZone($tz->timezone); | |||
} catch(Exception $e) { | |||
return null; | |||
} | |||
return null; | |||
} | |||
function micropub_post($endpoint, $params, $access_token, $h='entry') { | |||
$ch = curl_init(); | |||
curl_setopt($ch, CURLOPT_URL, $endpoint); | |||
curl_setopt($ch, CURLOPT_HTTPHEADER, array( | |||
'Authorization: Bearer ' . $access_token | |||
)); | |||
curl_setopt($ch, CURLOPT_POST, true); | |||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array_merge(array( | |||
'h' => $h | |||
), $params))); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
curl_setopt($ch, CURLOPT_HEADER, true); | |||
$response = curl_exec($ch); | |||
$error = curl_error($ch); | |||
return array( | |||
'response' => $response, | |||
'error' => $error, | |||
'curlinfo' => curl_getinfo($ch), | |||
'status' => curl_getinfo($ch, CURLINFO_HTTP_CODE) | |||
); | |||
} | |||
function relative_time($date) { | |||
static $rel; | |||
if(!isset($rel)) { | |||
$config = array( | |||
'language' => '\RelativeTime\Languages\English', | |||
'separator' => ', ', | |||
'suffix' => true, | |||
'truncate' => 1, | |||
); | |||
$rel = new \RelativeTime\RelativeTime($config); | |||
} | |||
return $rel->timeAgo($date); | |||
} | |||
function bs() | |||
{ | |||
static $pheanstalk; | |||
if(!isset($pheanstalk)) { | |||
$pheanstalk = new Pheanstalk\Pheanstalk(Config::$beanstalkServer, Config::$beanstalkPort); | |||
} | |||
return $pheanstalk; | |||
} | |||
function pluralize($word, $num) { | |||
if($num == 1) { | |||
return $word; | |||
} else { | |||
return $word . 's'; | |||
} | |||
} |
@ -0,0 +1,86 @@ | |||
<?php | |||
namespace feeds; | |||
use BarnabyWalters\Mf2; | |||
function parse_mf2(&$html, $base) { | |||
$parser = new \mf2\Parser($html, $base); | |||
return $parser->parse(); | |||
} | |||
function get_rels(&$data) { | |||
if($data && array_key_exists('rels', $data)) { | |||
return $data['rels']; | |||
} else { | |||
return []; | |||
} | |||
} | |||
function get_alternates(&$data) { | |||
if($data && array_key_exists('alternates', $data)) { | |||
return $data['alternates']; | |||
} else { | |||
return []; | |||
} | |||
} | |||
// Compares name, summary and content values to determine if they are equal | |||
function content_is_equal($a, $b) { | |||
// remove html tags | |||
$a = strip_tags($a); | |||
$b = strip_tags($b); | |||
// remove encoded entities | |||
$a = preg_replace('/&#?[a-z0-9]{2,8};/i', '', $a); | |||
$b = preg_replace('/&#?[a-z0-9]{2,8};/i', '', $b); | |||
// remove all non-alphanumeric chars | |||
$a = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($a)); | |||
$b = preg_replace('/[^a-zA-Z0-9]/', '', strtolower($b)); | |||
return $a == $b; | |||
} | |||
// Given a parsed microformat data structure, find the feed on the page. | |||
// This is meant to follow | |||
// * http://indiewebcamp.com/feed#How_To_Consume | |||
// * http://microformats.org/wiki/h-feed#Parsing | |||
// Returns an array: | |||
// [ | |||
// 'properties' => [ list of mf2 properties of the h-feed ], | |||
// 'entries' => [ list of h-entry items of the feed ] | |||
// ] | |||
function find_feed_info(&$data) { | |||
// tantek.com : h-card => h-feed => h-entry | |||
// snarfed.org : h-feed => h-entry | |||
// aaronparecki.com : h-entry | |||
$properties = []; | |||
$entries = []; | |||
// Find the first h-feed | |||
$feeds = Mf2\findMicroformatsByType($data, 'h-feed'); | |||
if(count($feeds)) { | |||
$feed = $feeds[0]; | |||
$properties = $feed['properties']; | |||
$entries = Mf2\findMicroformatsByType($feed['children'], 'h-entry', false); | |||
return [ | |||
'properties' => $properties, | |||
'entries' => $entries | |||
]; | |||
} else { | |||
// This is an implied feed if there are h-entry posts found at the top level | |||
$entries = Mf2\findMicroformatsByType($data['items'], 'h-entry', false); | |||
if(count($entries)) { | |||
return [ | |||
'properties' => [], | |||
'entries' => $entries | |||
]; | |||
} | |||
} | |||
return false; | |||
} |
@ -0,0 +1,72 @@ | |||
<?php | |||
namespace request; | |||
function get_url($url, $include_headers=false) { | |||
$ch = curl_init($url); | |||
set_user_agent($ch); | |||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
if($include_headers) { | |||
curl_setopt($ch, CURLOPT_HEADER, true); | |||
$response = curl_exec($ch); | |||
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); | |||
return [ | |||
'status' => curl_getinfo($ch, CURLINFO_HTTP_CODE), | |||
'headers' => trim(substr($response, 0, $header_size)), | |||
'body' => substr($response, $header_size) | |||
]; | |||
} else { | |||
return curl_exec($ch); | |||
} | |||
} | |||
function get_head($url) { | |||
$ch = curl_init($url); | |||
set_user_agent($ch); | |||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
curl_setopt($ch, CURLOPT_HEADER, true); | |||
curl_setopt($ch, CURLOPT_NOBODY, true); | |||
$headers = curl_exec($ch); | |||
return [ | |||
'status' => curl_getinfo($ch, CURLINFO_HTTP_CODE), | |||
'headers' => trim($headers) | |||
]; | |||
} | |||
function post($url, $params, $format='form') { | |||
$ch = curl_init($url); | |||
set_user_agent($ch); | |||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); | |||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | |||
if($format == 'json') { | |||
$body = json_encode($params); | |||
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json')); | |||
} else { | |||
$body = http_build_query($params); | |||
} | |||
curl_setopt($ch, CURLOPT_POST, true); | |||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body); | |||
curl_setopt($ch, CURLOPT_HEADER, true); | |||
$response = curl_exec($ch); | |||
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); | |||
return [ | |||
'status' => curl_getinfo($ch, CURLINFO_HTTP_CODE), | |||
'headers' => trim(substr($response, 0, $header_size)), | |||
'body' => substr($response, $header_size) | |||
]; | |||
} | |||
function set_user_agent(&$ch) { | |||
// Unfortunately I've seen a bunch of websites return different content when the user agent is set to something like curl or other server-side libraries, so we have to pretend to be a browser to successfully get the real HTML | |||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) p3k/Monocle/0.1.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36'); | |||
} | |||
function response_is($status, $prefix) { | |||
if($status) { | |||
$status_str = (string)$status; | |||
return $status_str[0] == (string)$prefix; | |||
} else { | |||
return false; | |||
} | |||
} |
@ -0,0 +1,347 @@ | |||
/*! | |||
* Bootstrap v3.1.1 (http://getbootstrap.com) | |||
* Copyright 2011-2014 Twitter, Inc. | |||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |||
*/ | |||
.btn-default, | |||
.btn-primary, | |||
.btn-success, | |||
.btn-info, | |||
.btn-warning, | |||
.btn-danger { | |||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); | |||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); | |||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); | |||
} | |||
.btn-default:active, | |||
.btn-primary:active, | |||
.btn-success:active, | |||
.btn-info:active, | |||
.btn-warning:active, | |||
.btn-danger:active, | |||
.btn-default.active, | |||
.btn-primary.active, | |||
.btn-success.active, | |||
.btn-info.active, | |||
.btn-warning.active, | |||
.btn-danger.active { | |||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); | |||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); | |||
} | |||
.btn:active, | |||
.btn.active { | |||
background-image: none; | |||
} | |||
.btn-default { | |||
text-shadow: 0 1px 0 #fff; | |||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); | |||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #dbdbdb; | |||
border-color: #ccc; | |||
} | |||
.btn-default:hover, | |||
.btn-default:focus { | |||
background-color: #e0e0e0; | |||
background-position: 0 -15px; | |||
} | |||
.btn-default:active, | |||
.btn-default.active { | |||
background-color: #e0e0e0; | |||
border-color: #dbdbdb; | |||
} | |||
.btn-primary { | |||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); | |||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #2b669a; | |||
} | |||
.btn-primary:hover, | |||
.btn-primary:focus { | |||
background-color: #2d6ca2; | |||
background-position: 0 -15px; | |||
} | |||
.btn-primary:active, | |||
.btn-primary.active { | |||
background-color: #2d6ca2; | |||
border-color: #2b669a; | |||
} | |||
.btn-success { | |||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); | |||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #3e8f3e; | |||
} | |||
.btn-success:hover, | |||
.btn-success:focus { | |||
background-color: #419641; | |||
background-position: 0 -15px; | |||
} | |||
.btn-success:active, | |||
.btn-success.active { | |||
background-color: #419641; | |||
border-color: #3e8f3e; | |||
} | |||
.btn-info { | |||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); | |||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #28a4c9; | |||
} | |||
.btn-info:hover, | |||
.btn-info:focus { | |||
background-color: #2aabd2; | |||
background-position: 0 -15px; | |||
} | |||
.btn-info:active, | |||
.btn-info.active { | |||
background-color: #2aabd2; | |||
border-color: #28a4c9; | |||
} | |||
.btn-warning { | |||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); | |||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #e38d13; | |||
} | |||
.btn-warning:hover, | |||
.btn-warning:focus { | |||
background-color: #eb9316; | |||
background-position: 0 -15px; | |||
} | |||
.btn-warning:active, | |||
.btn-warning.active { | |||
background-color: #eb9316; | |||
border-color: #e38d13; | |||
} | |||
.btn-danger { | |||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); | |||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-color: #b92c28; | |||
} | |||
.btn-danger:hover, | |||
.btn-danger:focus { | |||
background-color: #c12e2a; | |||
background-position: 0 -15px; | |||
} | |||
.btn-danger:active, | |||
.btn-danger.active { | |||
background-color: #c12e2a; | |||
border-color: #b92c28; | |||
} | |||
.thumbnail, | |||
.img-thumbnail { | |||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); | |||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075); | |||
} | |||
.dropdown-menu > li > a:hover, | |||
.dropdown-menu > li > a:focus { | |||
background-color: #e8e8e8; | |||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); | |||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.dropdown-menu > .active > a, | |||
.dropdown-menu > .active > a:hover, | |||
.dropdown-menu > .active > a:focus { | |||
background-color: #357ebd; | |||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); | |||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.navbar-default { | |||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); | |||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
border-radius: 4px; | |||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); | |||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); | |||
} | |||
.navbar-default .navbar-nav > .active > a { | |||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); | |||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); | |||
background-repeat: repeat-x; | |||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); | |||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); | |||
} | |||
.navbar-brand, | |||
.navbar-nav > li > a { | |||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25); | |||
} | |||
.navbar-inverse { | |||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); | |||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); | |||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); | |||
background-repeat: repeat-x; | |||
} | |||
.navbar-inverse .navbar-nav > .active > a { | |||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); | |||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); | |||
background-repeat: repeat-x; | |||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); | |||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); | |||
} | |||
.navbar-inverse .navbar-brand, | |||
.navbar-inverse .navbar-nav > li > a { | |||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); | |||
} | |||
.navbar-static-top, | |||
.navbar-fixed-top, | |||
.navbar-fixed-bottom { | |||
border-radius: 0; | |||
} | |||
.alert { | |||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2); | |||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); | |||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); | |||
} | |||
.alert-success { | |||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); | |||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); | |||
background-repeat: repeat-x; | |||
border-color: #b2dba1; | |||
} | |||
.alert-info { | |||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); | |||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); | |||
background-repeat: repeat-x; | |||
border-color: #9acfea; | |||
} | |||
.alert-warning { | |||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); | |||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); | |||
background-repeat: repeat-x; | |||
border-color: #f5e79e; | |||
} | |||
.alert-danger { | |||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); | |||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); | |||
background-repeat: repeat-x; | |||
border-color: #dca7a7; | |||
} | |||
.progress { | |||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); | |||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.progress-bar { | |||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); | |||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.progress-bar-success { | |||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); | |||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.progress-bar-info { | |||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); | |||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.progress-bar-warning { | |||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); | |||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.progress-bar-danger { | |||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); | |||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.list-group { | |||
border-radius: 4px; | |||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); | |||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075); | |||
} | |||
.list-group-item.active, | |||
.list-group-item.active:hover, | |||
.list-group-item.active:focus { | |||
text-shadow: 0 -1px 0 #3071a9; | |||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); | |||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); | |||
background-repeat: repeat-x; | |||
border-color: #3278b3; | |||
} | |||
.panel { | |||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); | |||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05); | |||
} | |||
.panel-default > .panel-heading { | |||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); | |||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||
.panel-primary > .panel-heading { | |||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); | |||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); | |||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); | |||
background-repeat: repeat-x; | |||
} | |||