Compare commits
1566 Commits
1.0-beta4
...
2.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
| a45f644148 | |||
| 6c32aaae95 | |||
| 23afdf3a70 | |||
| a3cac44c78 | |||
| 0f0e8eb82a | |||
| a0d6ccc5ca | |||
| e56983af1f | |||
| 27ea492cf7 | |||
| 7a0e6970b4 | |||
| 9aa66d6244 | |||
| abd454c456 | |||
| 5f736213af | |||
| d1f1333f48 | |||
| 7ce895bf5e | |||
| e72a943ad2 | |||
| 3ee1582e60 | |||
| eff6a406f3 | |||
| ff1c7d16be | |||
| 173629a400 | |||
| 7e80861588 | |||
| d481f42b7d | |||
| acc0160c29 | |||
| 3a59d13fd1 | |||
| e1101bee83 | |||
| 3080a4afa4 | |||
| e678c4752a | |||
| 3bcc4d4cb2 | |||
| 160e2d8f0b | |||
| f902d78153 | |||
| 86719c63bf | |||
| b91465c067 | |||
| 1930c19d82 | |||
| 7883367246 | |||
| 790573d458 | |||
| 5c072d2b57 | |||
| 131eaa3e94 | |||
| c7f622d369 | |||
| 1fc8ed8794 | |||
| d8dbe76bf5 | |||
| ccf50a9fd8 | |||
| 31e33fc42e | |||
| 73cd160bfc | |||
| 1d405d0e62 | |||
| c38d27d421 | |||
| 27e475a941 | |||
| 6108b0f26c | |||
| 1d76102a24 | |||
| 8ba854c068 | |||
| b4faefc04d | |||
| 5c895a7fd1 | |||
| 619cc45359 | |||
| 516022d60e | |||
| bd2c8b4677 | |||
| 30a40b0298 | |||
| e23edfd287 | |||
| 9ee44a109a | |||
| 6663d49329 | |||
| 0aafb8dfcb | |||
| 8b909e7ea7 | |||
| 1d41effebc | |||
| 540ef5e084 | |||
| 4aa2997106 | |||
| c60b549313 | |||
| 6c9bad13e1 | |||
| da82839bf8 | |||
| 5ad863c63a | |||
| 1caf557de7 | |||
| e1b9f4fb37 | |||
| a42938f42c | |||
| 292c1324e9 | |||
| 39643c6b76 | |||
| b88cf91fc8 | |||
| 488a468e3e | |||
| 10b2411c10 | |||
| d1af8ad4db | |||
| 8eedc8cfac | |||
| c5c7f90a81 | |||
| 5b2b5858fe | |||
| 6d7c7a6e3f | |||
| 7019c7cf6c | |||
| b1d05721cf | |||
| 252ebd6071 | |||
| b4b592a0c0 | |||
| 77a7752a59 | |||
| 7ec2897ee0 | |||
| 0aa344dc24 | |||
| 5a4bbcc9a7 | |||
| 27a8708b67 | |||
| 303768dfe9 | |||
| dda57bb944 | |||
| 87f23b005c | |||
| d51b38ed30 | |||
| 557e549db7 | |||
| 9c9c23cf08 | |||
| ff7b031d57 | |||
| 1f4408de9e | |||
| 10b40f85d6 | |||
| d275bdf4d3 | |||
| 8c3c77c1bd | |||
| a1bb1b3c2a | |||
| 56ea1de99b | |||
| dad1c546a5 | |||
| 5432f61509 | |||
| 00a051add4 | |||
| e82160e5e9 | |||
| 0cf434c00f | |||
| 831b02aaf2 | |||
| 6be9750155 | |||
| e6a228c43b | |||
| 4059a061c0 | |||
| 1bb1939ab7 | |||
| 01fddd0cb2 | |||
| fc73222723 | |||
| ab64c3d9ac | |||
| 23ff8d3619 | |||
| c997cfcc9c | |||
| 2863bf2ab5 | |||
| 71eff67f8b | |||
| d25b828821 | |||
| e9fa8c40aa | |||
| fc031e5706 | |||
| 82899c0402 | |||
| 3c65dfb735 | |||
| 22c1d29b89 | |||
| d4aa174367 | |||
| da2240f9d4 | |||
| 347fa6beb0 | |||
| c5d0db8b71 | |||
| a7f1921f7d | |||
| 2e15e30bf0 | |||
| a24c1ee30a | |||
| f4b617f2ff | |||
| 752b90d1f2 | |||
| 59507c5b24 | |||
| c13eda461f | |||
| 69edb774eb | |||
| 958671a7ae | |||
| aeff8aa765 | |||
| f27aca26f6 | |||
| a6e27f7466 | |||
| 5c514b0be3 | |||
| b7b2005494 | |||
| 6cbbf1481a | |||
| 0c5bcd82ba | |||
| 7b1648961d | |||
| 8a99c7a86b | |||
| 9b88658c04 | |||
| 52e423f307 | |||
| 625acf3352 | |||
| cad8cda7af | |||
| c23fc05df8 | |||
| 5a166c5c1a | |||
| 1c9cd2a7f0 | |||
| 1dc4e5da2e | |||
| 3447d1ee07 | |||
| 71ef0ed254 | |||
| 003fa77438 | |||
| f530f7f5e1 | |||
| 1d7b350b25 | |||
| e9fbd2d12e | |||
| 981cacf7b9 | |||
| 9cbb404b4a | |||
| f19f9f62d1 | |||
| ac9fec610a | |||
| c3510620ad | |||
| 0a0c600887 | |||
| 16bbb4aa41 | |||
| fba3f536a5 | |||
| cceca9ea1d | |||
| 268e9e7277 | |||
| 5b7da07620 | |||
| 8ac95cbfcc | |||
| b3cc1a14e7 | |||
| 33c36f6b48 | |||
| add597bad9 | |||
| 03690d1387 | |||
| f1eccfd63f | |||
| 53cf510689 | |||
| 970e0e994f | |||
| 75c48e3ae0 | |||
| d502762598 | |||
| 8a493541fa | |||
| 735068d181 | |||
| bd0f3d32c9 | |||
| 54a2241e13 | |||
| 4529d0f4b6 | |||
| 164d260c49 | |||
| b2a903feef | |||
| 33fe61f92f | |||
| 18f8f32f70 | |||
| 5ead137fe6 | |||
| 78507d2835 | |||
| 872384b0c1 | |||
| 3d9ccf9390 | |||
| 386cd24663 | |||
| c929c88faf | |||
| e686b5e6fe | |||
| 71200c4155 | |||
| 18d5f4541e | |||
| 59e91bc893 | |||
| a4e1ddf2d6 | |||
| 57cf6fd76c | |||
| 37a564a868 | |||
| fcc6949d4a | |||
| 2aac2f278f | |||
| c89d35e851 | |||
| 3d3ed955f1 | |||
| e9b395ec4b | |||
| 18cf594f8a | |||
| 0d6a7929e1 | |||
| f6af634aec | |||
| 2db616b586 | |||
| c26a3edc0a | |||
| ba873ae667 | |||
| cf0ea8f113 | |||
| 9dbcf9d418 | |||
| fa853bb603 | |||
| 880a0e1c0b | |||
| 3bfbd22f13 | |||
| ce782c84b8 | |||
| 3123b3c0c8 | |||
| 1ce8f30342 | |||
| 16dabc3263 | |||
| fdef5f4605 | |||
| 794dc4ee8a | |||
| 784bb4c38d | |||
| 8263e71192 | |||
| 68e9dcf615 | |||
| e0d188809c | |||
| d30262154a | |||
| ec3ce598f6 | |||
| 9c8f7af196 | |||
| 4c5e544183 | |||
| 0a878469d4 | |||
| 1210dae105 | |||
| 772d8c4b93 | |||
| 2c13918acc | |||
| 359b3f43cc | |||
| 772732531e | |||
| cd1298d6df | |||
| fcb1fba5c2 | |||
| 8a60bc4cc2 | |||
| 4b55e704ab | |||
| 0e8f778199 | |||
| 4e9f656ecb | |||
| cfb28c9da0 | |||
| 8e417206d5 | |||
| 4aafa7f0df | |||
| 4f0dfac6a6 | |||
| 7c99da0c95 | |||
| 24429857d8 | |||
| d2755b1c30 | |||
| 2afdea1ccc | |||
| 72fcaf8a6c | |||
| e75b215a25 | |||
| b0b352fc8e | |||
| 917040d4a0 | |||
| fef4124130 | |||
| 02d17813a1 | |||
| 159986c4fb | |||
| da3d4998c0 | |||
| 4180fddac1 | |||
| 34c2cc7a1a | |||
| 0f30f48b93 | |||
| 609594fa5e | |||
| dc22b46bf3 | |||
| 03fb6fde5f | |||
| 5716249455 | |||
| d4ebe5c5dc | |||
| 0d3bafdfdf | |||
| 98f0929f16 | |||
| db96045a0a | |||
| 1db9d411c5 | |||
| 451bad02f0 | |||
| b004a236ba | |||
| 1a5f7e2d88 | |||
| 5e98404dfb | |||
| 463573bf69 | |||
| 5def3f5862 | |||
| 71e51207ce | |||
| 1137fae94d | |||
| d2fcbf5d84 | |||
| 19c283140e | |||
| 89ee994f77 | |||
| a78d6afeaa | |||
| 4d5fd9be81 | |||
| 40f59b219b | |||
| 497e0cad7c | |||
| b026d3b115 | |||
| 616f9fea26 | |||
| a3bcd60a37 | |||
| fb96ea8845 | |||
| e610143f51 | |||
| af43bd3767 | |||
| 3f7a62908c | |||
| a1691859ca | |||
| 9c08a891f9 | |||
| f1e29e69cb | |||
| 558d9aabab | |||
| 75c3478a0c | |||
| 49e564ec15 | |||
| e643992350 | |||
| b125ed0394 | |||
| c6da9bea71 | |||
| 50243f0e34 | |||
| d13de40db6 | |||
| a1413a3da9 | |||
| fad316151c | |||
| 8c55a9e6c9 | |||
| 230413bdd1 | |||
| dc1c2debfb | |||
| a754db33c9 | |||
| 7083d183b9 | |||
| 8bb1f3d69a | |||
| f90af145ca | |||
| bccb5bba75 | |||
| 7d6c3edcdd | |||
| ec00964de2 | |||
| 7244d6cb61 | |||
| 83aaf84195 | |||
| 16a3d04cbd | |||
| f9d5155abf | |||
| b45c188516 | |||
| f506da40e2 | |||
| 3b84dc08fc | |||
| 78f66dcc52 | |||
| c937de3443 | |||
| 2b7a488917 | |||
| e177976099 | |||
| 89659c9eae | |||
| 109d67dbb1 | |||
| 6682139ec5 | |||
| e6f55346fd | |||
| eccf5eb2e0 | |||
| 1dbcd63b59 | |||
| 4793ee6509 | |||
| 6eebd8c909 | |||
| 0ab7404f93 | |||
| 4fcb7eaf13 | |||
| 8ce32af612 | |||
| 34437f408c | |||
| ab4aeb8bd8 | |||
| fdab81e910 | |||
| c3fdbcc60f | |||
| e62d27ff9b | |||
| 8b8cdabc89 | |||
| bdd23b076a | |||
| 7b2d336893 | |||
| 443cecd2d8 | |||
| b71ebd9af9 | |||
| 22ed64522a | |||
| 2e02b50409 | |||
| a3b4e8a2c0 | |||
| d5e9a99ce6 | |||
| 68568bf0b7 | |||
| 02947111c0 | |||
| e68d9179a1 | |||
| c713273619 | |||
| 3f357ee241 | |||
| c3cb46809b | |||
| 72ea6dd1a7 | |||
| 54f2b4a254 | |||
| 3162601c59 | |||
| 6819604185 | |||
| f967422fae | |||
| 428af5a8c3 | |||
| fd72e99d53 | |||
| 2a2903ceaa | |||
| fac3f8abfa | |||
| e40ff14d7d | |||
| a2cdaa8cdd | |||
| 4a230f9844 | |||
| 5f246a5543 | |||
| 2ba06b1ee2 | |||
| 3c5b025ac3 | |||
| ceb79aa016 | |||
| 925d8ab316 | |||
| d5b28518e9 | |||
| 0a3a5f6cd7 | |||
| ab2c93c7eb | |||
| 727b39a90e | |||
| c14a7c4251 | |||
| c37381b946 | |||
| 790d18a319 | |||
| 6fd3d82c01 | |||
| fcb3faf109 | |||
| d75a9fa38b | |||
| 0e7cf1fc50 | |||
| 9cc6bd87fe | |||
| 80127e4fb1 | |||
| 4ca0c9139c | |||
| a64f1d9f2d | |||
| bac5a34552 | |||
| a737d2a0ee | |||
| 545b852f46 | |||
| 392f4a2681 | |||
| 120544cccd | |||
| 82b07eb272 | |||
| c2257428b2 | |||
| 8ba913d87a | |||
| 06fdfd025e | |||
| d990dc6f05 | |||
| bdf39ff10d | |||
| 2ac2e0bc2b | |||
| 1a0ea1f35a | |||
| 43e6f47d5b | |||
| 9f1d650ae8 | |||
| b69fc0628a | |||
| a8596c35c8 | |||
| a15692b1b7 | |||
| 6622cf9968 | |||
| 7fc14130c7 | |||
| 579764b264 | |||
| 3e1f25e22f | |||
| 5a58461491 | |||
| 773ed2e7a6 | |||
| 496f21e6fa | |||
| 053b9568b2 | |||
| 930334cd6d | |||
| 2686457448 | |||
| fedaf00537 | |||
| ea8da8c6d5 | |||
| 95135988f9 | |||
| d85454fbf8 | |||
| 728a432850 | |||
| f59601fced | |||
| 0e654f6273 | |||
| f48a018929 | |||
| b958d9e59b | |||
| 4919584b87 | |||
| 1473e8c9fb | |||
| 47cadf36c8 | |||
| 20a69dffe7 | |||
| 009696d0a8 | |||
| e9d5c2bf02 | |||
| 946d6a5124 | |||
| a6523cfe86 | |||
| c54ea989a3 | |||
| 342f0cc55a | |||
| 0e7650683d | |||
| 768303a573 | |||
| 0bc2baa65c | |||
| b7dd5f824c | |||
| 54139268f8 | |||
| 44a16e82d8 | |||
| d0b90fbe18 | |||
| 55333dfd21 | |||
| 47e12c3677 | |||
| 3f3fbef11f | |||
| 48ffc5a4b6 | |||
| 6ecdd48a3f | |||
| b17874a7d5 | |||
| 308db01684 | |||
| b1a65df9df | |||
| 8d9b8912af | |||
| 4ab58dcf6c | |||
| 9e07dc982c | |||
| 9948d899d3 | |||
| 53e121881b | |||
| 7e63b892f9 | |||
| 9fb6ac830f | |||
| 9b9b05008a | |||
| ac9d58211e | |||
| 3cf22a0541 | |||
| 82d6d9cb06 | |||
| 75e9d1df03 | |||
| 170746f99d | |||
| 51d9699fa1 | |||
| 2878416f8b | |||
| 4346a86068 | |||
| 399bd777d7 | |||
| 2f3c816579 | |||
| 9a014e48d6 | |||
| 98510a4189 | |||
| c7d3bf1671 | |||
| 4d6e818e40 | |||
| 1cbef2d3b5 | |||
| 04aaa199b7 | |||
| 132f614dee | |||
| 9744e97131 | |||
| 769e19dc4a | |||
| e3c34bfc06 | |||
| d6fa2f70ac | |||
| 276a1e9d3f | |||
| 164bd80118 | |||
| c844dc0c50 | |||
| 1a93ee423b | |||
| 7d74a2f32b | |||
| 14d7a69b8c | |||
| 34c06cabef | |||
| 0ee043f745 | |||
| 371ac69a6b | |||
| 2385f891e5 | |||
| 0c83fd5994 | |||
| f98a2a0fc3 | |||
| bdf2add2e8 | |||
| 3e6b7ea0e9 | |||
| 6141388969 | |||
| 2d733277e6 | |||
| a05be8abec | |||
| d0c2243b10 | |||
| 6894d48e03 | |||
| f37d1427a1 | |||
| 6e22bd737b | |||
| bcf53ab75b | |||
| a24f2c8808 | |||
| 48b67328e2 | |||
| efad7e53a1 | |||
| 2ab8cb6816 | |||
| db2b4bf678 | |||
| b0cce9e636 | |||
| 1df1204d94 | |||
| 6ee416a069 | |||
| 0ca374e6a1 | |||
| d8f9f37ab2 | |||
| 092ca70725 | |||
| a36737f485 | |||
| 46bbd8d321 | |||
| 6c87418ff0 | |||
| 1bd12b6229 | |||
| 0a018fe039 | |||
| 6d37a7e6c1 | |||
| b3dc0749d3 | |||
| 2691cf0438 | |||
| 1d14779154 | |||
| aa4d6562c1 | |||
| 0ed6302212 | |||
| 0f00688096 | |||
| 73b7744383 | |||
| 8378485e33 | |||
| ba7b9d48d4 | |||
| 3e30422cda | |||
| 495aecfe74 | |||
| 71798e4ec4 | |||
| 32da2a70ef | |||
| fca3c75723 | |||
| dcae2fc25d | |||
| 0e7971d835 | |||
| c641baad0e | |||
| 732c2ad897 | |||
| 0bf99bb144 | |||
| 0bd2cb1ecd | |||
| e4977b8a86 | |||
| c0d9eba07f | |||
| d9085c63e3 | |||
| 7781faa0b0 | |||
| 78cedc2262 | |||
| 4d85d7e9ba | |||
| 7a577c519f | |||
| 55f58c9c5e | |||
| 2f6a596760 | |||
| 8c7e0f95b9 | |||
| d9b7175593 | |||
| 59f18f9a85 | |||
| 9ca5fd43f9 | |||
| f170f31594 | |||
| 874e3e10a4 | |||
| f8c2736a10 | |||
| 70b54da2b1 | |||
| c9fa9677c1 | |||
| fa8d563934 | |||
| 19aee7cd54 | |||
| 2725de8efb | |||
| 91f78f26f2 | |||
| 2734044aca | |||
| 0536b809b0 | |||
| e1dd7f70c5 | |||
| f5deb024a2 | |||
| 68c6f1bd7f | |||
| da93261a7d | |||
| 3d2b2d62be | |||
| eb3bd7efb7 | |||
| f59f45d740 | |||
| 017e20895f | |||
| 2c0ffcf397 | |||
| 3d3368cfd5 | |||
| 92504e0dd4 | |||
| 11204db45a | |||
| d4c029f46e | |||
| c5e8ba25bb | |||
| eaf95758dc | |||
| 7ffb1e80bf | |||
| 5078e8360a | |||
| d29bfaf139 | |||
| 2a94b1d1b7 | |||
| cbce162b40 | |||
| fb8389f463 | |||
| 970c40bb93 | |||
| 0ac38198ab | |||
| 653e8be4c1 | |||
| 89c03230c3 | |||
| 3b815d2de5 | |||
| d91691573f | |||
| 7812f508bc | |||
| 5f09650eef | |||
| 8af35ad932 | |||
| 9c0c882006 | |||
| 94f2364cd8 | |||
| 8125b415d8 | |||
| d01db0c71d | |||
| ed4d5cf2d7 | |||
| 427b61a35b | |||
| 7f2d9f9613 | |||
| c64a14787d | |||
| 02b225a82e | |||
| 15d33c24dc | |||
| 2f69eb4afa | |||
| 29c4517f7a | |||
| be463487cc | |||
| 905ae369bd | |||
| c8dee95396 | |||
| c0284f6182 | |||
| 93e28e4d2e | |||
| 5644c2d88e | |||
| de00c9208d | |||
| 2c093b03de | |||
| 9e0fff7cb0 | |||
| 77bb7b92a2 | |||
| 3bb7c5ffec | |||
| c2e2906c8d | |||
| bc782eaa72 | |||
| 34d15eb4d0 | |||
| 1b0e6e9ae6 | |||
| 61b9fdd5e4 | |||
| eacaf7f864 | |||
| 6079aaa33d | |||
| 6e334aba68 | |||
| 42a9064620 | |||
| 889249804f | |||
| c5772d118f | |||
| 2e45e7bebc | |||
| 8394ab4619 | |||
| 4cfbd5d893 | |||
| 7df80cb32c | |||
| daacffefa6 | |||
| 1990517b22 | |||
| c3235553dd | |||
| 71691fe44a | |||
| aa6e27cf4f | |||
| 4ffc77d9f5 | |||
| fdcbdda1ad | |||
| 367664ee87 | |||
| e11e03cb32 | |||
| 3ba208b205 | |||
| 127915f4ea | |||
| 03493be075 | |||
| 843dbe5195 | |||
| a8c90c5c1b | |||
| a65f5d5563 | |||
| 27f15aa4ca | |||
| f8bf895254 | |||
| 589dce52c6 | |||
| 38ba7ed972 | |||
| 3e5a342f65 | |||
| e4788de51e | |||
| 569f8d6851 | |||
| 19f2f11ee8 | |||
| 9e11bfa4a6 | |||
| 7dfc3c2b58 | |||
| 80709502c7 | |||
| 2cdb0b8f40 | |||
| b2d9357c78 | |||
| c4b1e79018 | |||
| dc61832a9e | |||
| 33767049a5 | |||
| 0c678cf24a | |||
| b9ec99e25b | |||
| d692b3b08d | |||
| 6b767d1cc0 | |||
| ad4d1caa9e | |||
| b84a80559a | |||
| 163eae0bb1 | |||
| bd9f08157c | |||
| 9d50517cea | |||
| 2b9fe72b39 | |||
| 93fd4692f6 | |||
| 0440249631 | |||
| 3eb951572d | |||
| 9de6a0a7cc | |||
| 19875ef0da | |||
| 00fcfd299b | |||
| 79e051a1f2 | |||
| 97a2dd74c8 | |||
| 8d6ff10e8e | |||
| 9a5c1bc62a | |||
| 6ad93dff69 | |||
| c78c1a3f08 | |||
| 9e7f6caf03 | |||
| 820d81aa61 | |||
| 3329f1bf3d | |||
| 90c67dbd12 | |||
| 96b2c59c04 | |||
| 3d99ce9dad | |||
| 99410a21eb | |||
| 1345a10788 | |||
| f3052b4542 | |||
| e342acf7ba | |||
| 2b17e0aa77 | |||
| dda7884ace | |||
| 10939766de | |||
| a20f96b76d | |||
| adf17b677e | |||
| 894cd087f4 | |||
| 44f1fef018 | |||
| 170a1407fe | |||
| b68f0a81e5 | |||
| 7fe8a9adc4 | |||
| af5c371e95 | |||
| 13c7f9a462 | |||
| 512e5e5bd1 | |||
| 7f782e4496 | |||
| c86b40f014 | |||
| 8ae45e7fe2 | |||
| 166ff0a093 | |||
| 82978fbd57 | |||
| 1186b3b67a | |||
| 30b948e68b | |||
| 1aa1461a2e | |||
| 92ae99bd29 | |||
| d4d33a4130 | |||
| 7a21c308be | |||
| 37cad52229 | |||
| 81315897f0 | |||
| 9254b6cf46 | |||
| 0e65fa85d3 | |||
| 4eb71ab555 | |||
| e9a64ef8a9 | |||
| a50d7f0f20 | |||
| 2903ffc54f | |||
| 5ea5310ab4 | |||
| 1256e4c645 | |||
| dc69d3e8d8 | |||
| 9c55ed0923 | |||
| 7c2c49d9b1 | |||
| 41bd2be68a | |||
| 66d7a4dcd8 | |||
| b9c026ce32 | |||
| c4457fba85 | |||
| d4b42995f7 | |||
| 485d57972e | |||
| 85c5a1ff8d | |||
| 7b8bb75228 | |||
| fe16457efc | |||
| 46533cac7d | |||
| 747a15841d | |||
| fc01f94387 | |||
| e9d4d17693 | |||
| 25f9c66834 | |||
| d25a3f13c2 | |||
| b13376e918 | |||
| c8b4ef7fed | |||
| b0f9f5ac21 | |||
| 6062f74c6b | |||
| eb365a01fb | |||
| eb0c88a9d4 | |||
| db3bffa284 | |||
| ca6c0de380 | |||
| d91ff81ca6 | |||
| 9d2140c9a1 | |||
| 7a0f454d39 | |||
| d5b717dc77 | |||
| 7cb517ce54 | |||
| a460404252 | |||
| d0287608b6 | |||
| 1532376710 | |||
| d3122db7b2 | |||
| b46b8933ab | |||
| 62f3e6db75 | |||
| a0c57b35a3 | |||
| 217f3ca0b4 | |||
| 3eba7538a4 | |||
| fa6f5db97f | |||
| ebea829d80 | |||
| e319c49891 | |||
| efd0a9f5f1 | |||
| 94888d5fd4 | |||
| ac8b064f47 | |||
| 3c133bff49 | |||
| 20bb3f7f2a | |||
| cc1f78a83d | |||
| ff02fd8aca | |||
| 063a2fadaa | |||
| 266b7328ef | |||
| 893b8e4cef | |||
| 1772de2531 | |||
| 75dc3a71b7 | |||
| 0be82dedb6 | |||
| 8a76674568 | |||
| 40800c97b2 | |||
| 6926f6dcc7 | |||
| a63cd1b06f | |||
| 9cf370cfb6 | |||
| ccaefcf69a | |||
| 15eb5ca4b8 | |||
| 224528f1de | |||
| ad2b61db80 | |||
| 344c8f6b5c | |||
| 4bc70ed401 | |||
| b95a6f57bf | |||
| 87e37e82fd | |||
| 8519cc796f | |||
| 827bd1f899 | |||
| ed0436d21e | |||
| 242746fd17 | |||
| f23fd0ee5e | |||
| 1087b3cb4e | |||
| f60c9b00ab | |||
| 6fe9b616aa | |||
| 655550e23a | |||
| 4bada2b954 | |||
| a87a1b7d3b | |||
| 4fae3b0a85 | |||
| 052bdfc17e | |||
| 476b8902bb | |||
| 6f0b92138f | |||
| cd271fc485 | |||
| 0bf65303ca | |||
| c4800fc6da | |||
| d51c2e05d3 | |||
| ce096afed7 | |||
| 06e7e7ff7b | |||
| bbbda080bf | |||
| 574f3faf06 | |||
| b56c86457c | |||
| 7212386e98 | |||
| b73a175386 | |||
| c9e6fec4bf | |||
| fcd37d0c7b | |||
| b40cd4e73f | |||
| 1b6e21d7a6 | |||
| 7ee1972599 | |||
| 24479b479d | |||
| 90a1a78b1e | |||
| 4a50075784 | |||
| 606bea72e1 | |||
| 4eb603430d | |||
| 76b1e0babe | |||
| f2248e604d | |||
| f56791e6c4 | |||
| 750d904a16 | |||
| 691a03f176 | |||
| 48fb171d7a | |||
| 8fd0512a3c | |||
| 5b16d508b5 | |||
| 05e313ad28 | |||
| b9fa7d2c9c | |||
| 8ce508cab0 | |||
| dffbec1c44 | |||
| ad0eccb4cd | |||
| 44d35257e8 | |||
| cf8a5e1eed | |||
| 6b0894c66a | |||
| a7058a5a13 | |||
| 1403af5be3 | |||
| 20b4d7d621 | |||
| 7331ed3e80 | |||
| 79dd109e37 | |||
| a305326973 | |||
| 3dca040a0b | |||
| 8327f1c371 | |||
| 73c833780c | |||
| f2cc1db1a8 | |||
| 34c2d1bdd1 | |||
| 29e95769b5 | |||
| e3c44f9c0f | |||
| 40d2042228 | |||
| ab494e4ede | |||
| 1cd02d55fb | |||
| f183f72bf4 | |||
| 8b6c710b09 | |||
| 04b589420e | |||
| e38e46ecdb | |||
| ace428669b | |||
| b37110cc82 | |||
| cde2fc3842 | |||
| ffcd442989 | |||
| 76dd27e7f7 | |||
| 9f86454b48 | |||
| b852df020c | |||
| fa926fb47c | |||
| 6fc2c29daa | |||
| a1b31d93b6 | |||
| 824f8c45ed | |||
| a0822259e7 | |||
| 9b8283d0fc | |||
| 04a7674bdd | |||
| 2d4cfc58ec | |||
| 0dc4797a4c | |||
| b668db242d | |||
| bbfe6fa50b | |||
| a15108e65b | |||
| aa1083bdac | |||
| b3c720b1c3 | |||
| 657245dcbd | |||
| 5af2555f59 | |||
| 49882dc151 | |||
| 19438d3021 | |||
| d5c481c2f4 | |||
| 8763e4efde | |||
| ecb8c1389c | |||
| d4690a8fa1 | |||
| d05f5eeb1d | |||
| 4362417495 | |||
| a9bbe11169 | |||
| 45e60cb52a | |||
| 211068ce50 | |||
| 051f7fb28c | |||
| 79666a3046 | |||
| 78abff6a52 | |||
| 1daa8e4a0f | |||
| dc76489221 | |||
| 7c503c4438 | |||
| b83690ebd8 | |||
| a34d920847 | |||
| 358c689cec | |||
| 2e8625c25f | |||
| ab86a5124a | |||
| 280972a66c | |||
| 200c758ff4 | |||
| 8492f37323 | |||
| f8c3798522 | |||
| 9f3477a279 | |||
| 046b931624 | |||
| 70549136ba | |||
| 6c0c750000 | |||
| 2f3c05651e | |||
| fa9a7bbb3c | |||
| 830612f555 | |||
| d49446ff98 | |||
| dc59f164a9 | |||
| deab6280d3 | |||
| d07abb5c42 | |||
| fb9df0c269 | |||
| af8292c1de | |||
| 38cf3413df | |||
| 800868e27e | |||
| c70bfefc68 | |||
| 15317991f3 | |||
| 4188f38ad5 | |||
| 7dd8b5026d | |||
| 6da20812ce | |||
| 887b015def | |||
| 505a74ad1d | |||
| 83cac9ac05 | |||
| a818ff2000 | |||
| 0ce85e0a7f | |||
| 86edff4447 | |||
| ebd6bf6007 | |||
| 1f78bd8471 | |||
| f83ffc3ac3 | |||
| 392f9a1b9c | |||
| 9f8541ef2a | |||
| cca9284b6a | |||
| 3e87066506 | |||
| 9cf6bac1a5 | |||
| b738bea9ca | |||
| 9c67b1b829 | |||
| 955fc67438 | |||
| 91b6be3186 | |||
| 17065e613f | |||
| cec19bd866 | |||
| 5594d7d054 | |||
| 2b58426b2d | |||
| 6a4bbf0fe5 | |||
| 8e68391a57 | |||
| 93edcab52e | |||
| ccd0b381b6 | |||
| d259f73665 | |||
| 0f6273cdb8 | |||
| 4e067ceabd | |||
| 58dbe10388 | |||
| d423113b00 | |||
| 26452f891f | |||
| 2f26729c84 | |||
| b6a3c8866a | |||
| d610968932 | |||
| 26b77483ee | |||
| d14e3f1e22 | |||
| b3cda72e93 | |||
| 3602405ec0 | |||
| d59536deea | |||
| 6400371ff9 | |||
| c1aad6d574 | |||
| cc1ec61b85 | |||
| c710f977b2 | |||
| 5425b0dd82 | |||
| 4247b37551 | |||
| 82980a148b | |||
| c13aac1bc3 | |||
| da87848cee | |||
| 25052a76ca | |||
| a13ff95777 | |||
| cdda041a90 | |||
| 6924253423 | |||
| 69213014d1 | |||
| aa126ba458 | |||
| c9563378ea | |||
| ba22fb1cef | |||
| 29cd317aff | |||
| 0bf95d865a | |||
| ae43ec99d9 | |||
| 7f186e21e0 | |||
| bca2853ade | |||
| 97d54f2ac8 | |||
| 8142d4b1e6 | |||
| 35d4e27588 | |||
| ec15d0a784 | |||
| c93a5c137f | |||
| 752cd4a8ef | |||
| 5d198e2b98 | |||
| 1d14e65315 | |||
| 67a8848aed | |||
| 30bd273580 | |||
| cbc75befb5 | |||
| a9f5e572dd | |||
| 8038b38802 | |||
| 79024eb004 | |||
| 0c3db64585 | |||
| 3dc8d84229 | |||
| 87f01ea2e9 | |||
| 0b9bb8cb78 | |||
| 009669360d | |||
| a342945b61 | |||
| 1fce49fac7 | |||
| a50583fb97 | |||
| d18ff7d956 | |||
| 3ec62cf95a | |||
| ab157bbb75 | |||
| f61ffec352 | |||
| 88f0e31622 | |||
| 38eecef26b | |||
| 99408dfcf3 | |||
| 0bf0dfe10d | |||
| e3b00bcaf5 | |||
| 6caba976ec | |||
| 1d6a9ac25a | |||
| 03e501dedd | |||
| 18209292a4 | |||
| 007f26e582 | |||
| 04b43dc097 | |||
| becc5bfbf2 | |||
| 230fa05eb7 | |||
| 96834a47b0 | |||
| e212e6b12a | |||
| 404adf970d | |||
| 7d5d9ea449 | |||
| 74e09e562b | |||
| 60c3a4d3e1 | |||
| f3f0b11393 | |||
| f2b6b4e230 | |||
| 6a3c510157 | |||
| 4555c38d3b | |||
| 24696800e5 | |||
| 818b186f8a | |||
| 4910af33ff | |||
| 0626e52f3c | |||
| f034640ca3 | |||
| 1829b362fc | |||
| 34acb02cbb | |||
| 4877836b12 | |||
| 07ed2b0231 | |||
| 9c743ab965 | |||
| 78bddb22be | |||
| decc23aaf2 | |||
| 2395a3802a | |||
| 7ec445b06e | |||
| 21f29fe492 | |||
| 5b5e47c3ae | |||
| b6413975c3 | |||
| d151b51c67 | |||
| 827f5b42a6 | |||
| ef17914960 | |||
| 72a857158c | |||
| 87090d8ae7 | |||
| 8af31ae0f7 | |||
| feecea2806 | |||
| 07da861126 | |||
| c97d23c533 | |||
| 4d99bae893 | |||
| 2a6440c134 | |||
| 4dbba60439 | |||
| 29cf52b677 | |||
| fd86559a5b | |||
| d70dd7ac69 | |||
| 43c7b978c3 | |||
| dfff18f81b | |||
| a4a870e1ec | |||
| 03303cd71b | |||
| c2cf7075c2 | |||
| bfe1ad6dbc | |||
| 6212acfc81 | |||
| 847f57686e | |||
| 44fd0faa23 | |||
| df6c8b3be9 | |||
| a192c21c6a | |||
| f3312ce58d | |||
| 603ecb0052 | |||
| 4a74d9857c | |||
| 7256e9e139 | |||
| f09d76b0ea | |||
| 0f859c6f32 | |||
| 389d751e92 | |||
| 5ce3978472 | |||
| d5f36a8d9e | |||
| 08f539f738 | |||
| 6e8030a0db | |||
| b96b075b55 | |||
| f8e9d8bdbc | |||
| bf20b541ae | |||
| 3945335f39 | |||
| 2c534c184d | |||
| a8ef1f3f43 | |||
| 86da39886d | |||
| a7f39918bf | |||
| 69c57493e7 | |||
| 5fe1948097 | |||
| d9b51a21fa | |||
| 9cb9ab552b | |||
| 7a873ef1d7 | |||
| 292cd0dbd5 | |||
| f86784c22d | |||
| 2dd5c1e4a3 | |||
| 29d9c0ffe1 | |||
| d7ee9f986b | |||
| 1bcbe8bebf | |||
| db117db3c5 | |||
| 4a16f33dcc | |||
| 0d67b00d5d | |||
| 7d2f1aa279 | |||
| bfa32856bc | |||
| 06e1a9a98a | |||
| d9bb0cdeb8 | |||
| 4d2bd6e507 | |||
| a297fb1e38 | |||
| a4585f7eaa | |||
| 182faf2696 | |||
| d967a1fa14 | |||
| 22db488d21 | |||
| 1be13ba1fc | |||
| d6d8a045e6 | |||
| 1d0995bb8e | |||
| 3345c9dc39 | |||
| fecb62a396 | |||
| ad697686c0 | |||
| cdada41505 | |||
| 08718c01e4 | |||
| b152f2b6ba | |||
| 04fbe8f5ef | |||
| e68348f627 | |||
| 9591ee2603 | |||
| 5814ef0d25 | |||
| d607330557 | |||
| ad03eb6286 | |||
| cc60cbbbab | |||
| d619120fc4 | |||
| 2c4e7a1cea | |||
| a33a3d2afb | |||
| 03832b45e1 | |||
| 028e34b6c4 | |||
| ad53faf25c | |||
| 0c51bfea6f | |||
| 6fa3f70bc2 | |||
| db41c907aa | |||
| 2a97194253 | |||
| a7048bc45d | |||
| eb5b677250 | |||
| 897b2b5302 | |||
| 5805ac4574 | |||
| 07e028fe5c | |||
| 08dde123b1 | |||
| 82cff5af70 | |||
| 8754bd88a5 | |||
| 6a915551ab | |||
| ed02e38e1d | |||
| 8d7cd2ccd5 | |||
| c0586a906c | |||
| 181d16fe22 | |||
| 3ee27ee6ba | |||
| 6775da70a8 | |||
| f7382cd8c3 | |||
| 7339b0b08d | |||
| 1acd18510a | |||
| fb26cc9375 | |||
| d47a05a9a5 | |||
| 17b2afefad | |||
| 4744cb0e1d | |||
| 7c6aa8d826 | |||
| b3f7b7d200 | |||
| 1ab567f6e3 | |||
| f0d584503f | |||
| 042486c511 | |||
| ded2c63312 | |||
| bf79463070 | |||
| f98373cc34 | |||
| 25114854b3 | |||
| 4ca17924a1 | |||
| 9a010227d7 | |||
| e1a625ad35 | |||
| eace9f914d | |||
| d3b52886f5 | |||
| 71b0d53c5e | |||
| 11c680f97a | |||
| 223268c2fa | |||
| 53e3158dfe | |||
| 31a10069a5 | |||
| 4c14936353 | |||
| 2bb207d005 | |||
| d429305836 | |||
| 49c803425c | |||
| cf75bb31e9 | |||
| fc52df0677 | |||
| 6065553c13 | |||
| 346380e131 | |||
| affbd83b48 | |||
| 381d182726 | |||
| 2048661b0c | |||
| 6a0329f756 | |||
| 35c7e0a69c | |||
| decb9a5814 | |||
| 61e79d9344 | |||
| 36a733af8d | |||
| a8464c9719 | |||
| e145f767f3 | |||
| 56532c4e72 | |||
| fa37042b32 | |||
| 0e7f04b04e | |||
| cbcae4037c | |||
| 72f7ff0589 | |||
| 2c83741171 | |||
| 78dd437928 | |||
| f5b5622a89 | |||
| bb75d2b01a | |||
| 2cf87a4da1 | |||
| 9fad46bd0e | |||
| dcc73856a9 | |||
| 4b842b20ce | |||
| 49b56f19d8 | |||
| 25e1213d1b | |||
| 92fc97eeb3 | |||
| 48e063904d | |||
| 99679d0688 | |||
| d3b47e9470 | |||
| 1570a65381 | |||
| d4949327ef | |||
| c9bd17a100 | |||
| 0a022f9a39 | |||
| 565bb72d99 | |||
| e5382002b4 | |||
| 3628b24d12 | |||
| cd425599ce | |||
| e7345a2c4f | |||
| 032e0ca13a | |||
| 3ade95a3d7 | |||
| fddf4fbacc | |||
| 926acd7bba | |||
| 8975653d4c | |||
| 689de3dbcc | |||
| ab5bb94b12 | |||
| 6203ef8e51 | |||
| e83cf5a787 | |||
| d09a5674e9 | |||
| d0a599bbae | |||
| 30c12d3927 | |||
| 860473f33c | |||
| e1cfef7bf1 | |||
| b4fd2154fe | |||
| f37891fdb6 | |||
| aad8fbab09 | |||
| 7785f0c75f | |||
| a71dc5d7d0 | |||
| 655214ab30 | |||
| fb5a9666ed | |||
| 60ca369cd3 | |||
| b89d5a2bf4 | |||
| 53ae58e1a1 | |||
| 792097fb6a | |||
| 970cfb1166 | |||
| 01cd443441 | |||
| 488fc63b67 | |||
| 6285e57c49 | |||
| 243e13ab59 | |||
| 5e98c2183a | |||
| 9f3148fec7 | |||
| c9357429fd | |||
| 41265e07d4 | |||
| 2e4440c3f8 | |||
| 3141347214 | |||
| 943ac3c77e | |||
| 83b47311ba | |||
| 16fd1cce61 | |||
| f14807de06 | |||
| ed2853564e | |||
| 26170f4613 | |||
| 68268c0199 | |||
| 5966d2c2d3 | |||
| 26929c08d3 | |||
| 044bf638a8 | |||
| 58f6269f36 | |||
| 3a68883ae9 | |||
| 211ed48361 | |||
| c515ffec9c | |||
| 4e09039d2c | |||
| 1e1e4e4eca | |||
| c8265d95b0 | |||
| 2e384abab6 | |||
| 736a4fb77e | |||
| 38dafee05d | |||
| fa0bfb775a | |||
| 445a1a1c8d | |||
| 1e1fd6f24d | |||
| a678f9df38 | |||
| f85bfdf186 | |||
| cae70cdbdb | |||
| ebae8c8315 | |||
| 6af66b1106 | |||
| f355d2c87f | |||
| f4fbfaa7cb | |||
| 21f50d5a08 | |||
| 1b539ba1ec | |||
| 3e0e7e1208 | |||
| b8fdd2d85f | |||
| c95b78a8ce | |||
| 3ae345b3d7 | |||
| d1d3498b62 | |||
| f878daeb8b | |||
| 9ba98a0abe | |||
| 1cecaa7926 | |||
| 2744d07d71 | |||
| 5ed8050791 | |||
| c3b261e321 | |||
| 9ffce01e0c | |||
| 8905191413 | |||
| b4b22940df | |||
| e1cf0fda27 | |||
| f41d00ed8a | |||
| d866e8be91 | |||
| be4c8197eb | |||
| fcb5fd27e2 | |||
| 9c9b226589 | |||
| a562e3905a | |||
| 607e12b4f2 | |||
| 7f66783976 | |||
| 2abcccb371 | |||
| 9bc32632af | |||
| 52e3f58c72 | |||
| b5c1ed1227 | |||
| 4d058d4824 | |||
| cb4fba5a33 | |||
| 4a84d94e91 | |||
| 0b57c6825a | |||
| 529db4861d | |||
| 9de34d4e84 | |||
| d7ad5d6560 | |||
| aeea7c6af0 | |||
| b1bfd4cb0c | |||
| 2eb111a300 | |||
| 76e487cd7e | |||
| 60fc4f4b1a | |||
| da5fc42f61 | |||
| 1151e9cc8f | |||
| dfde415198 | |||
| e2b83a8298 | |||
| 5cfafc6110 | |||
| 1810c13b55 | |||
| a0aa150418 | |||
| 5c8d438c08 | |||
| 17bd2cc94c | |||
| cbfd5a1019 | |||
| 04fcbad8c5 | |||
| 5df72bb4a4 | |||
| 41acd466ce | |||
| 43c1115eeb | |||
| db75a4425c | |||
| 0f9d3ef173 | |||
| 05d6dd487c | |||
| d460914f65 | |||
| 6bf4702608 | |||
| f014856424 | |||
| c432fa1674 | |||
| f778e47283 | |||
| 4886ed6d36 | |||
| 74ec445a66 | |||
| 6cab59c340 | |||
| 2e2ebe5ec7 | |||
| 68e2061666 | |||
| 7b171c7340 | |||
| 5bea1a4d31 | |||
| 9e7c840b18 | |||
| ac4d114214 | |||
| d5501950e2 | |||
| 42c80841c8 | |||
| 0b5c6ff319 | |||
| 05b6401817 | |||
| 59cc585271 | |||
| f0133fe5f4 | |||
| b8bb2b4ab6 | |||
| 72c20a5297 | |||
| 5846b0f1b3 | |||
| 39cc09dfc3 | |||
| 678f2cb6ee | |||
| 13991e5288 | |||
| 5c0ad7376a | |||
| af1daea191 | |||
| 9772578ee2 | |||
| 16ac4e3dbe | |||
| 2ab37d6205 | |||
| e070b9b511 | |||
| e232d5532a | |||
| 74dc587452 | |||
| 87a44f4704 | |||
| c2b7a11c77 | |||
| 7a4482b8a4 | |||
| defa7754a4 | |||
| 1bf152a551 | |||
| 99c8761b75 | |||
| 0c2f453750 | |||
| d7aec74403 | |||
| 760d44d194 | |||
| f2d3ee98a6 | |||
| 705f04937f | |||
| 5862c872c6 | |||
| b7066c0333 | |||
| e4178da65c | |||
| c7abf20bbe | |||
| b3a2586ecf | |||
| 45e9e0f565 | |||
| 352523640d | |||
| 05824223a9 | |||
| f616ab60ef | |||
| 363bc4eb86 | |||
| 2b191d8c37 | |||
| 7d7ed6a0ee | |||
| 83020b993c | |||
| 985ce3ec53 | |||
| 33fe6a46a4 | |||
| cd8a344156 | |||
| 6ae804853f | |||
| b6d859fa92 | |||
| 1a0d776394 | |||
| 1ba1628ed6 | |||
| 027b4e1568 | |||
| df6afaf090 | |||
| c1e24b0461 | |||
| 36b9219842 | |||
| 8e76eaad19 | |||
| fad925644f | |||
| a84f77d6ba | |||
| 0c994bd934 | |||
| 18889e230a | |||
| e557b7bb44 | |||
| 3ae9190e78 | |||
| e231f3f023 | |||
| 2502e1359c | |||
| 20f00edd15 | |||
| 2f3425dff6 | |||
| 272600ff80 | |||
| 1745ebc832 | |||
| 5188585864 | |||
| 1e98ee1de0 | |||
| 5011388f39 | |||
| 5e07dc8b51 | |||
| 894c36ea32 | |||
| 031df528b6 | |||
| 9d3b88b379 | |||
| 5eebe4e50d | |||
| 2916d24b20 | |||
| 3352332f3f | |||
| 2621569200 | |||
| 93eed12505 | |||
| 125f9ee838 | |||
| 8df8eb6c95 | |||
| bf7516112f | |||
| 5a26af476e | |||
| 2287bf06f5 | |||
| 89812ec81c | |||
| 06fef43180 | |||
| 01e671f4d1 | |||
| d47d2533ac | |||
| 27a74816da | |||
| 47baa1077e | |||
| f0f7b94362 | |||
| 747c6698b6 | |||
| 8f91e10faa | |||
| 6cd8af85da | |||
| 34d67c835e | |||
| 7f17a38d35 | |||
| 07ae20eeed | |||
| 4cc3c2ac17 | |||
| 66e074b43d | |||
| eb44ca4213 | |||
| dfbbc14b33 | |||
| 34bf601a56 | |||
| 969a91a1e3 | |||
| fbe8e27568 | |||
| 4ee705a79c | |||
| 8623a53200 | |||
| 4e5b04113d | |||
| 3cc22aab82 | |||
| be2b9055b9 | |||
| 92cd6e9af8 | |||
| 58ace4941e | |||
| 37527034ab | |||
| 88e1108f11 | |||
| 3408ed48ba | |||
| b8c67f8068 | |||
| 79026b73a8 | |||
| 6a6c1c1172 | |||
| 18bd1cc3b1 | |||
| 74c0733c1e | |||
| f55f734fda | |||
| d477db2cfb | |||
| 6fe07ab815 | |||
| 1a1142893f | |||
| 7bda34c66b | |||
| 2af5015668 | |||
| 5801355cbc | |||
| d6b28d43c8 | |||
| 6d7e6f5e36 | |||
| 469590fde5 | |||
| 00dbaf90bc | |||
| 705250b93d | |||
| 0d64be15de | |||
| f6597c7cb9 | |||
| a8778dc23e | |||
| d62dfd88d2 | |||
| d081f272b0 | |||
| 1b2abab6dd | |||
| 10ab20d8e2 | |||
| 48207b6814 | |||
| 1a08e7b6f6 | |||
| b9523a0ba0 | |||
| 084ec2a63d | |||
| b9e0514783 | |||
| 3e05742568 | |||
| 660b998eb7 | |||
| b084cde854 | |||
| 8ca368e7e3 | |||
| a0fd7c5b44 | |||
| 7eb64927cc | |||
| 9074534c93 | |||
| 12d9cfbcaa | |||
| f3a6080fce | |||
| f16b0747a4 | |||
| 964481d023 | |||
| d143cae25a | |||
| d11e2bcf48 | |||
| 3c33e40b61 | |||
| eaf2c769be | |||
| ce4a1dcc19 | |||
| 2230a38cd6 | |||
| ef6051b95e | |||
| af1d279226 | |||
| d5ce28df67 | |||
| 693b3f8677 | |||
| a322312740 | |||
| 2edde7fe33 | |||
| 08a12b6dbb | |||
| 876bb3af42 | |||
| 7f9f5281e5 | |||
| 3eb049036e | |||
| 093f1efb21 | |||
| 7d1778bbd2 | |||
| 5ebf6eeca1 | |||
| 6fb4600334 | |||
| b6b36e1b5a | |||
| c51be6b697 |
10
.editorconfig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
; top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
; Unix-style newlines
|
||||||
|
[*]
|
||||||
|
end_of_line = LF
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
48
.gitignore
vendored
@ -1,8 +1,40 @@
|
|||||||
assets/*
|
# Cache, logs & sessions
|
||||||
cache/*
|
/var/*
|
||||||
vendor
|
!/var/cache
|
||||||
composer.phar
|
/var/cache/*
|
||||||
db/poche.sqlite
|
!var/cache/.gitkeep
|
||||||
output
|
!/var/logs
|
||||||
phpdoc*
|
/var/logs/*
|
||||||
inc/poche/myconfig.inc.php
|
!var/logs/.gitkeep
|
||||||
|
!/var/sessions
|
||||||
|
/var/sessions/*
|
||||||
|
!var/sessions/.gitkeep
|
||||||
|
!var/SymfonyRequirements.php
|
||||||
|
|
||||||
|
# Parameters
|
||||||
|
/app/config/parameters.yml
|
||||||
|
|
||||||
|
# Managed by Composer
|
||||||
|
/vendor/
|
||||||
|
|
||||||
|
# Assets and user uploads
|
||||||
|
/web/bundles/
|
||||||
|
/web/uploads/
|
||||||
|
|
||||||
|
# Build
|
||||||
|
/app/build
|
||||||
|
/build
|
||||||
|
|
||||||
|
# Composer PHAR
|
||||||
|
/composer.phar
|
||||||
|
|
||||||
|
# Data for wallabag
|
||||||
|
data/assets/*
|
||||||
|
data/db/wallabag*.sqlite
|
||||||
|
|
||||||
|
# Docker container logs and data
|
||||||
|
docker/logs/
|
||||||
|
docker/data/
|
||||||
|
|
||||||
|
# To avoid crazy stuff on some PR, we must manually FORCE ADD IT on each new release
|
||||||
|
composer.lock
|
||||||
|
|||||||
28
.scrutinizer.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
filter:
|
||||||
|
paths:
|
||||||
|
- src/*
|
||||||
|
excluded_paths:
|
||||||
|
- 'vendor/*'
|
||||||
|
- 'app/*'
|
||||||
|
- 'var/*'
|
||||||
|
- 'web/*'
|
||||||
|
- 'src/Wallabag/*Bundle/Tests/*'
|
||||||
|
- '*Test.php'
|
||||||
|
|
||||||
|
tools:
|
||||||
|
php_cs_fixer: true
|
||||||
|
php_analyzer: true
|
||||||
|
php_mess_detector: true
|
||||||
|
php_changetracking: true
|
||||||
|
php_code_sniffer: true
|
||||||
|
php_pdepend: true
|
||||||
|
sensiolabs_security_checker: true
|
||||||
|
#external_code_coverage:
|
||||||
|
# timeout: 3600
|
||||||
|
php_code_coverage: true
|
||||||
|
php_sim: false
|
||||||
|
php_cpd: false
|
||||||
|
|
||||||
|
checks:
|
||||||
|
php:
|
||||||
|
code_rating: true
|
||||||
51
.travis.yml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
language: php
|
||||||
|
|
||||||
|
# faster builds on docker-container setup
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
# used for HHVM
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- tidy
|
||||||
|
|
||||||
|
# cache vendor dirs
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- vendor
|
||||||
|
- $HOME/.composer/cache
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.5
|
||||||
|
- 5.6
|
||||||
|
- 7.0
|
||||||
|
- hhvm
|
||||||
|
|
||||||
|
env:
|
||||||
|
- DB=mysql
|
||||||
|
- DB=pgsql
|
||||||
|
- DB=sqlite
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
exclude:
|
||||||
|
- php: hhvm
|
||||||
|
env: DB=pgsql # driver for PostgreSQL currently unsupported by HHVM, requires 3rd party dependency
|
||||||
|
allow_failures:
|
||||||
|
- php: hhvm
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- v2
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
|
||||||
|
- if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi;
|
||||||
|
- composer self-update --no-progress
|
||||||
|
- if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
|
||||||
|
- if [[ "$DB" = "pgsql" ]]; then psql -c 'create database wallabag;' -U postgres; fi;
|
||||||
|
|
||||||
|
script:
|
||||||
|
- travis_wait composer update --no-interaction --no-progress
|
||||||
|
- ant prepare-$DB
|
||||||
|
- bin/phpunit -v
|
||||||
@ -1,11 +1,30 @@
|
|||||||
# How contributing
|
# How to contribute
|
||||||
|
|
||||||
## You found a bug
|
## You found a bug
|
||||||
Please [open a new issue](https://github.com/inthepoche/poche/issues/new).
|
Please [open a new issue](https://github.com/wallabag/wallabag/issues/new).
|
||||||
|
|
||||||
To fix the bug quickly, we need some infos:
|
To fix the bug quickly, we need some infos:
|
||||||
* your poche version (in ./inc/poche/myconfig.inc.php)
|
* your wallabag version (on top of the ./index.php file, and also on config page)
|
||||||
* the link you want to poche and which causes problem
|
* your webserver installation :
|
||||||
|
* type of hosting (shared or dedicated)
|
||||||
|
* in case of a dedicated server, the server and OS used
|
||||||
|
* the php version used, eventually `phpinfo()`
|
||||||
|
* which storage system you choose at install (SQLite, MySQL/MariaDB or PostgreSQL)
|
||||||
|
* any problem on the `wallabag_compatibility_test.php` page
|
||||||
|
* any particular details which could be related
|
||||||
|
|
||||||
|
|
||||||
|
If relevant :
|
||||||
|
* the link you want to save and which causes problem
|
||||||
|
* the file you want to import into wallabag, or just an extract
|
||||||
|
|
||||||
|
If you have the skills :
|
||||||
|
* enable DEBUG mode and look the output at cache/log.txt
|
||||||
|
* look for errors into php and server logs
|
||||||
|
|
||||||
|
Note : If you have large portions of text, use [Github's Gist service](https://gist.github.com/) or other pastebin-like.
|
||||||
|
|
||||||
## You want to fix a bug or to add a feature
|
## You want to fix a bug or to add a feature
|
||||||
Please fork poche and work with the dev branch. Do not work on master branch.
|
Please fork wallabag and work with **the dev branch** only. **Do not work on master branch**.
|
||||||
|
|
||||||
|
[Don't forget to read our guidelines](https://github.com/wallabag/wallabag/blob/dev/GUIDELINES.md).
|
||||||
|
|||||||
27
COPYING.md
@ -1,14 +1,19 @@
|
|||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
Copyright (c) 2013-2016 Nicolas Lœuillet
|
||||||
Version 2, December 2004
|
|
||||||
|
|
||||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
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:
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies of this license document, and changing it is allowed as long
|
copies or substantial portions of the Software.
|
||||||
as the name is changed.
|
|
||||||
|
|
||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
16
CREDITS.md
@ -1,15 +1,3 @@
|
|||||||
poche is based on :
|
wallabag is mainly developed by [Nicolas Lœuillet](https://github.com/nicosomb), [@j0k3r](https://github.com/j0k3r) and [@tcitworld](https://github.com/tcitworld) under the MIT License.
|
||||||
* PHP Readability https://bitbucket.org/fivefilters/php-readability
|
|
||||||
* Full Text RSS http://code.fivefilters.org/full-text-rss/src
|
|
||||||
* Encoding https://github.com/neitanod/forceutf8
|
|
||||||
* logo by Brightmix http://www.iconfinder.com/icondetails/43256/128/jeans_monotone_pocket_icon
|
|
||||||
* icons http://icomoon.io
|
|
||||||
* PHP Simple HTML DOM Parser (for Pocket import) http://simplehtmldom.sourceforge.net/
|
|
||||||
* Session https://github.com/tontof/kriss_feed/blob/master/src/class/Session.php
|
|
||||||
* Twig http://twig.sensiolabs.org
|
|
||||||
* Flash messages https://github.com/plasticbrain/PHP-Flash-Messages
|
|
||||||
* Pagination https://github.com/daveismyname/pagination
|
|
||||||
|
|
||||||
poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
|
Thank you [to others contributors](https://github.com/wallabag/wallabag/graphs/contributors).
|
||||||
|
|
||||||
Contributors : https://github.com/inthepoche/poche/graphs/contributors
|
|
||||||
|
|||||||
13
Capfile
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
set :deploy_config_path, 'app/config/capistrano/deploy.rb'
|
||||||
|
set :stage_config_path, 'app/config/capistrano/deploy'
|
||||||
|
|
||||||
|
# Load DSL and set up stages
|
||||||
|
require 'capistrano/setup'
|
||||||
|
|
||||||
|
# Include default deployment tasks
|
||||||
|
require 'capistrano/deploy'
|
||||||
|
|
||||||
|
require 'capistrano/symfony'
|
||||||
|
|
||||||
|
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
|
||||||
|
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
|
||||||
5
Gemfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
gem 'capistrano', '~> 3.1'
|
||||||
|
gem 'capistrano-symfony', '~> 0.1', :github => 'capistrano/symfony'
|
||||||
|
gem 'capistrano-composer', '~> 0.0.3'
|
||||||
41
Gemfile.lock
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
GIT
|
||||||
|
remote: git://github.com/capistrano/symfony.git
|
||||||
|
revision: ca56a01b817097d2831400ef9b1867fc8e07dcf8
|
||||||
|
specs:
|
||||||
|
capistrano-symfony (0.4.0)
|
||||||
|
capistrano (~> 3.1)
|
||||||
|
capistrano-composer (~> 0.0.3)
|
||||||
|
capistrano-file-permissions (~> 0.1.0)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
capistrano (3.4.0)
|
||||||
|
i18n
|
||||||
|
rake (>= 10.0.0)
|
||||||
|
sshkit (~> 1.3)
|
||||||
|
capistrano-composer (0.0.6)
|
||||||
|
capistrano (>= 3.0.0.pre)
|
||||||
|
capistrano-file-permissions (0.1.1)
|
||||||
|
capistrano (~> 3.1)
|
||||||
|
colorize (0.7.7)
|
||||||
|
i18n (0.7.0)
|
||||||
|
net-scp (1.2.1)
|
||||||
|
net-ssh (>= 2.6.5)
|
||||||
|
net-ssh (2.9.2)
|
||||||
|
rake (10.4.2)
|
||||||
|
sshkit (1.7.1)
|
||||||
|
colorize (>= 0.7.0)
|
||||||
|
net-scp (>= 1.1.2)
|
||||||
|
net-ssh (>= 2.8.0)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
capistrano (~> 3.1)
|
||||||
|
capistrano-composer (~> 0.0.3)
|
||||||
|
capistrano-symfony (~> 0.1)!
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
1.10.6
|
||||||
72
INSTALL.md
@ -1,72 +0,0 @@
|
|||||||
# Installing poche
|
|
||||||
|
|
||||||
## requirements
|
|
||||||
* PHP 5.2.0 or higher
|
|
||||||
* XML ([?](http://php.net/xml))
|
|
||||||
* PCRE ([?](http://php.net/pcre))
|
|
||||||
* Data filtering ([?](http://uk.php.net/manual/en/book.filter.php))
|
|
||||||
* Tidy ([?](http://php.net/tidy))
|
|
||||||
* cURL ([?](http://php.net/curl))
|
|
||||||
* Parallel URL fetching
|
|
||||||
* allow_url_fopen ([?](http://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen))
|
|
||||||
|
|
||||||
To see if your server is ok to run poche, execute http://yourpoche/poche_compatibility_test.php.
|
|
||||||
|
|
||||||
## you don't want to install twig (the template engine) by yourself
|
|
||||||
|
|
||||||
Download this file http://static.inthepoche.com/files/poche-1.0-latest-with-twig.zip
|
|
||||||
|
|
||||||
Extract this file on your server.
|
|
||||||
|
|
||||||
## you want to install twig by yourself
|
|
||||||
|
|
||||||
Download the latest version here : http://www.inthepoche.com/?pages/T%C3%A9l%C3%A9charger-poche
|
|
||||||
|
|
||||||
Extract this file on your server.
|
|
||||||
|
|
||||||
```php
|
|
||||||
curl -s http://getcomposer.org/installer | php
|
|
||||||
php composer.phar install
|
|
||||||
```
|
|
||||||
|
|
||||||
### using sqlite
|
|
||||||
|
|
||||||
Copy / paste install/poche.sqlite in db folder.
|
|
||||||
|
|
||||||
### using mysql or postgresql
|
|
||||||
|
|
||||||
Execute the sql file in /install (mysql.sql or postgres.sql)
|
|
||||||
|
|
||||||
Then, go to step 3.
|
|
||||||
|
|
||||||
# Upgrading poche
|
|
||||||
|
|
||||||
Replace all the files except **db/poche.sqlite**. Also remember to edit the file /inc/poche/config.inc.php.
|
|
||||||
|
|
||||||
## Upgrading from poche <= 0.3
|
|
||||||
|
|
||||||
You have to execute http://yourpoche/install/update_sqlite_from_0_to_1.php
|
|
||||||
|
|
||||||
Then, go to step 3.
|
|
||||||
|
|
||||||
## Upgrading from poche >= 1.0 beta1
|
|
||||||
|
|
||||||
Nothing to do here.
|
|
||||||
|
|
||||||
Then, go to step 3.
|
|
||||||
|
|
||||||
# Here is the step 3
|
|
||||||
|
|
||||||
You must have write access on assets, cache and db directories. These directories may not exist, you'll have to create them.
|
|
||||||
|
|
||||||
You can use poche ! Enjoy.
|
|
||||||
|
|
||||||
# Some problems you may encounter
|
|
||||||
|
|
||||||
## Blank page
|
|
||||||
|
|
||||||
Be sure to have write access on assets, cache and db directories.
|
|
||||||
|
|
||||||
## PHP Fatal error: Call to a member function fetchAll() on a non-object in /var/www/poche/inc/poche/Database.class.php on line 42
|
|
||||||
|
|
||||||
If you want to install poche, delete the db/poche.sqlite file and copy / paste the install/poche.sqlite in /db. Be sure to have write access.
|
|
||||||
41
README.md
@ -1,29 +1,26 @@
|
|||||||
# what is poche ?
|
[](https://travis-ci.org/wallabag/wallabag)
|
||||||
Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is free (like in freedom) and open source.
|
[](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
|
||||||
|
[](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
|
||||||
|
|
||||||
## Some features
|
# What is wallabag?
|
||||||
* adding, deleting, archiving and setting as favorite a link
|
wallabag is a self hostable application allowing you to not miss any content anymore.
|
||||||
* import from pocket / readability / instapaper
|
Click, save and read it when you can. It extracts content so that you can read it when you have time.
|
||||||
* share links by email and on twitter
|
|
||||||
* a design adapted to tablets and smartphones
|
|
||||||
* extensions for Chrome and Firefox
|
|
||||||
* Android application
|
|
||||||
* multi languages: french, english, spanish, german.
|
|
||||||
* multi users (very soon!)
|
|
||||||
* update notification in configuration screen
|
|
||||||
* many storage modes (sqlite, mysql, postgresql)
|
|
||||||
* many templates: [have a look here](https://github.com/inthepoche/poche-themes).
|
|
||||||
* ...
|
|
||||||
|
|
||||||
To test poche, a demo website is online : [demo.inthepoche.com](http://demo.inthepoche.com) (login poche, password poche).
|
More informations on our website: [wallabag.org](http://wallabag.org)
|
||||||
|
|
||||||
To use poche hosting, [you can create an account here](http://app.inthepoche.com/).
|
# Want to test the v2?
|
||||||
|
|
||||||
## Installation
|
Keep in mind it's an **unstable** branch, everything can be broken :)
|
||||||
Read the [INSTALL.md file](https://github.com/inthepoche/poche/blob/master/INSTALL.md).
|
|
||||||
|
If you don't have it yet, please [install composer](https://getcomposer.org/download/). Then you can install wallabag by executing the following commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
composer create-project wallabag/wallabag wallabag 2.0.*@alpha
|
||||||
|
php bin/console wallabag:install
|
||||||
|
php bin/console server:run
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Copyright © 2010-2013 Nicolas Lœuillet <nicolas.loeuillet@gmail.com>
|
Copyright © 2013-2016 Nicolas Lœuillet <nicolas@loeuillet.org>
|
||||||
This work is free. You can redistribute it and/or modify it under the
|
This work is free. You can redistribute it and/or modify it under the
|
||||||
terms of the Do What The Fuck You Want To Public License, Version 2,
|
terms of the MIT License. See the COPYING file for more details.
|
||||||
as published by Sam Hocevar. See the COPYING file for more details.
|
|
||||||
|
|||||||
9
TODO.md
@ -1,9 +0,0 @@
|
|||||||
# TODO
|
|
||||||
|
|
||||||
* pouvoir annuler la suppression
|
|
||||||
* conventions codage ? phing ? vérifier error_log qui trainent
|
|
||||||
* phpDocumentor
|
|
||||||
* minifier css
|
|
||||||
* barre fixe d'admin sur la page d'un billet ?
|
|
||||||
* revoir export (export vers pocket &cie ? )
|
|
||||||
* raccourcis clavier
|
|
||||||
7
app/.htaccess
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<IfModule mod_authz_core.c>
|
||||||
|
Require all denied
|
||||||
|
</IfModule>
|
||||||
|
<IfModule !mod_authz_core.c>
|
||||||
|
Order deny,allow
|
||||||
|
Deny from all
|
||||||
|
</IfModule>
|
||||||
7
app/AppCache.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
|
||||||
|
|
||||||
|
class AppCache extends HttpCache
|
||||||
|
{
|
||||||
|
}
|
||||||
68
app/AppKernel.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Component\HttpKernel\Kernel;
|
||||||
|
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||||
|
|
||||||
|
class AppKernel extends Kernel
|
||||||
|
{
|
||||||
|
public function registerBundles()
|
||||||
|
{
|
||||||
|
$bundles = [
|
||||||
|
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
|
||||||
|
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
|
||||||
|
new Symfony\Bundle\TwigBundle\TwigBundle(),
|
||||||
|
new Symfony\Bundle\MonologBundle\MonologBundle(),
|
||||||
|
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
|
||||||
|
new Symfony\Bundle\AsseticBundle\AsseticBundle(),
|
||||||
|
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
|
||||||
|
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
|
||||||
|
new FOS\RestBundle\FOSRestBundle(),
|
||||||
|
new FOS\UserBundle\FOSUserBundle(),
|
||||||
|
new JMS\SerializerBundle\JMSSerializerBundle(),
|
||||||
|
new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
|
||||||
|
new Nelmio\CorsBundle\NelmioCorsBundle(),
|
||||||
|
new Liip\ThemeBundle\LiipThemeBundle(),
|
||||||
|
new Wallabag\CoreBundle\WallabagCoreBundle(),
|
||||||
|
new Wallabag\ApiBundle\WallabagApiBundle(),
|
||||||
|
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
|
||||||
|
new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(),
|
||||||
|
new FOS\OAuthServerBundle\FOSOAuthServerBundle(),
|
||||||
|
new Wallabag\UserBundle\WallabagUserBundle(),
|
||||||
|
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
|
||||||
|
new Scheb\TwoFactorBundle\SchebTwoFactorBundle(),
|
||||||
|
new KPhoen\RulerZBundle\KPhoenRulerZBundle(),
|
||||||
|
new Wallabag\ImportBundle\WallabagImportBundle(),
|
||||||
|
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
|
||||||
|
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
|
||||||
|
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
|
||||||
|
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
|
||||||
|
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
|
||||||
|
$bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bundles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRootDir()
|
||||||
|
{
|
||||||
|
return __DIR__;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCacheDir()
|
||||||
|
{
|
||||||
|
return dirname(__DIR__).'/var/cache/'.$this->getEnvironment();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogDir()
|
||||||
|
{
|
||||||
|
return dirname(__DIR__).'/var/logs';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerContainerConfiguration(LoaderInterface $loader)
|
||||||
|
{
|
||||||
|
$loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml');
|
||||||
|
}
|
||||||
|
}
|
||||||
0
assets/.gitignore → app/Resources/views/.gitkeep
Executable file → Normal file
13
app/autoload.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||||
|
use Composer\Autoload\ClassLoader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassLoader $loader
|
||||||
|
*/
|
||||||
|
$loader = require __DIR__.'/../vendor/autoload.php';
|
||||||
|
|
||||||
|
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
|
||||||
|
|
||||||
|
return $loader;
|
||||||
23
app/config/capistrano/deploy.rb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# config valid only for current version of Capistrano
|
||||||
|
lock '3.4.0'
|
||||||
|
|
||||||
|
set :application, 'wallabag'
|
||||||
|
set :repo_url, 'git@github.com:wallabag/wallabag.git'
|
||||||
|
|
||||||
|
set :ssh_user, 'framasoft_bag'
|
||||||
|
server '78.46.248.87', user: fetch(:ssh_user), roles: %w{web app db}
|
||||||
|
|
||||||
|
set :scm, :git
|
||||||
|
|
||||||
|
set :format, :pretty
|
||||||
|
set :log_level, :info
|
||||||
|
# set :log_level, :debug
|
||||||
|
|
||||||
|
set :composer_install_flags, '--no-dev --prefer-dist --no-interaction --optimize-autoloader'
|
||||||
|
|
||||||
|
set :linked_files, %w{app/config/parameters.yml}
|
||||||
|
set :linked_dirs, %w{app/logs web/uploads data}
|
||||||
|
|
||||||
|
set :keep_releases, 3
|
||||||
|
|
||||||
|
after 'deploy:finishing', 'deploy:cleanup'
|
||||||
2
app/config/capistrano/deploy/staging.rb
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
set :branch, 'v2'
|
||||||
|
set :deploy_to, '/var/www/v2.wallabag.org/web/'
|
||||||
209
app/config/config.yml
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: parameters.yml }
|
||||||
|
- { resource: security.yml }
|
||||||
|
- { resource: services.yml }
|
||||||
|
|
||||||
|
framework:
|
||||||
|
#esi: ~
|
||||||
|
translator: { fallback: "%locale%" }
|
||||||
|
secret: "%secret%"
|
||||||
|
router:
|
||||||
|
resource: "%kernel.root_dir%/config/routing.yml"
|
||||||
|
strict_requirements: ~
|
||||||
|
form: ~
|
||||||
|
csrf_protection: ~
|
||||||
|
validation: { enable_annotations: true }
|
||||||
|
templating:
|
||||||
|
engines: ['twig']
|
||||||
|
#assets_version: SomeVersionScheme
|
||||||
|
default_locale: "%locale%"
|
||||||
|
trusted_hosts: ~
|
||||||
|
trusted_proxies: ~
|
||||||
|
session:
|
||||||
|
# handler_id set to null will use default session handler from php.ini
|
||||||
|
handler_id: session.handler.native_file
|
||||||
|
save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
|
||||||
|
fragments: ~
|
||||||
|
http_method_override: true
|
||||||
|
assets: ~
|
||||||
|
|
||||||
|
wallabag_core:
|
||||||
|
languages:
|
||||||
|
en: 'English'
|
||||||
|
fr: 'Français'
|
||||||
|
de: 'Deutsch'
|
||||||
|
|
||||||
|
wallabag_import:
|
||||||
|
allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain']
|
||||||
|
resource_dir: "%kernel.root_dir%/../web/uploads/import"
|
||||||
|
|
||||||
|
# Twig Configuration
|
||||||
|
twig:
|
||||||
|
debug: "%kernel.debug%"
|
||||||
|
strict_variables: "%kernel.debug%"
|
||||||
|
globals:
|
||||||
|
share_twitter: %share_twitter%
|
||||||
|
share_mail: %share_mail%
|
||||||
|
share_shaarli: %share_shaarli%
|
||||||
|
shaarli_url: %shaarli_url%
|
||||||
|
share_diaspora: %share_diaspora%
|
||||||
|
diaspora_url: %diaspora_url%
|
||||||
|
flattr: %flattr%
|
||||||
|
flattrable: 1
|
||||||
|
flattred: 2
|
||||||
|
carrot: %carrot%
|
||||||
|
show_printlink: %show_printlink%
|
||||||
|
export_epub: %export_epub%
|
||||||
|
export_mobi: %export_mobi%
|
||||||
|
export_pdf: %export_pdf%
|
||||||
|
version: %app.version%
|
||||||
|
twofactor_auth: %twofactor_auth%
|
||||||
|
warning_message: %warning_message%
|
||||||
|
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
|
||||||
|
form_themes:
|
||||||
|
- "LexikFormFilterBundle:Form:form_div_layout.html.twig"
|
||||||
|
|
||||||
|
# Assetic Configuration
|
||||||
|
assetic:
|
||||||
|
debug: "%kernel.debug%"
|
||||||
|
use_controller: false
|
||||||
|
bundles: [ ]
|
||||||
|
#java: /usr/bin/java
|
||||||
|
filters:
|
||||||
|
cssrewrite: ~
|
||||||
|
#closure:
|
||||||
|
# jar: "%kernel.root_dir%/Resources/java/compiler.jar"
|
||||||
|
#yui_css:
|
||||||
|
# jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar"
|
||||||
|
|
||||||
|
# Doctrine Configuration
|
||||||
|
doctrine:
|
||||||
|
dbal:
|
||||||
|
driver: "%database_driver%"
|
||||||
|
host: "%database_host%"
|
||||||
|
port: "%database_port%"
|
||||||
|
dbname: "%database_name%"
|
||||||
|
user: "%database_user%"
|
||||||
|
password: "%database_password%"
|
||||||
|
charset: UTF8
|
||||||
|
path: "%database_path%"
|
||||||
|
|
||||||
|
orm:
|
||||||
|
auto_generate_proxy_classes: "%kernel.debug%"
|
||||||
|
entity_managers:
|
||||||
|
default:
|
||||||
|
auto_mapping: true
|
||||||
|
|
||||||
|
stof_doctrine_extensions:
|
||||||
|
default_locale: "%locale%"
|
||||||
|
translation_fallback: true
|
||||||
|
orm:
|
||||||
|
default:
|
||||||
|
tree: true
|
||||||
|
sluggable: true
|
||||||
|
|
||||||
|
doctrine_migrations:
|
||||||
|
dir_name: "%kernel.root_dir%/DoctrineMigrations"
|
||||||
|
namespace: Application\Migrations
|
||||||
|
table_name: migration_versions
|
||||||
|
name: Application Migrations
|
||||||
|
|
||||||
|
# Swiftmailer Configuration
|
||||||
|
swiftmailer:
|
||||||
|
transport: "%mailer_transport%"
|
||||||
|
host: "%mailer_host%"
|
||||||
|
username: "%mailer_user%"
|
||||||
|
password: "%mailer_password%"
|
||||||
|
spool: { type: memory }
|
||||||
|
|
||||||
|
fos_rest:
|
||||||
|
param_fetcher_listener: true
|
||||||
|
body_listener: true
|
||||||
|
format_listener: true
|
||||||
|
view:
|
||||||
|
view_response_listener: 'force'
|
||||||
|
formats:
|
||||||
|
xml: true
|
||||||
|
json : true
|
||||||
|
templating_formats:
|
||||||
|
html: true
|
||||||
|
force_redirects:
|
||||||
|
html: true
|
||||||
|
failed_validation: HTTP_BAD_REQUEST
|
||||||
|
default_engine: twig
|
||||||
|
routing_loader:
|
||||||
|
default_format: json
|
||||||
|
|
||||||
|
nelmio_api_doc:
|
||||||
|
sandbox:
|
||||||
|
enabled: false
|
||||||
|
name: wallabag API documentation
|
||||||
|
|
||||||
|
nelmio_cors:
|
||||||
|
defaults:
|
||||||
|
allow_credentials: false
|
||||||
|
allow_origin: []
|
||||||
|
allow_headers: []
|
||||||
|
allow_methods: []
|
||||||
|
expose_headers: []
|
||||||
|
max_age: 0
|
||||||
|
hosts: []
|
||||||
|
#origin_regex: false
|
||||||
|
paths:
|
||||||
|
'^/api/':
|
||||||
|
allow_origin: ['*']
|
||||||
|
allow_headers: ['X-Custom-Auth']
|
||||||
|
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||||
|
max_age: 3600
|
||||||
|
'^/':
|
||||||
|
#origin_regex: true
|
||||||
|
allow_origin: ['^http://localhost:[0-9]+']
|
||||||
|
allow_headers: ['X-Custom-Auth']
|
||||||
|
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||||
|
max_age: 3600
|
||||||
|
hosts: ['^api\.']
|
||||||
|
|
||||||
|
liip_theme:
|
||||||
|
load_controllers: false
|
||||||
|
themes:
|
||||||
|
- baggy
|
||||||
|
- material
|
||||||
|
autodetect_theme: wallabag_core.helper.detect_active_theme
|
||||||
|
|
||||||
|
path_patterns:
|
||||||
|
bundle_resource:
|
||||||
|
- %%bundle_path%%/Resources/views/themes/%%current_theme%%/%%template%%
|
||||||
|
|
||||||
|
fos_user:
|
||||||
|
db_driver: orm
|
||||||
|
firewall_name: main
|
||||||
|
user_class: Wallabag\UserBundle\Entity\User
|
||||||
|
registration:
|
||||||
|
confirmation:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
fos_oauth_server:
|
||||||
|
db_driver: orm
|
||||||
|
client_class: Wallabag\ApiBundle\Entity\Client
|
||||||
|
access_token_class: Wallabag\ApiBundle\Entity\AccessToken
|
||||||
|
refresh_token_class: Wallabag\ApiBundle\Entity\RefreshToken
|
||||||
|
auth_code_class: Wallabag\ApiBundle\Entity\AuthCode
|
||||||
|
service:
|
||||||
|
user_provider: fos_user.user_manager
|
||||||
|
|
||||||
|
scheb_two_factor:
|
||||||
|
trusted_computer:
|
||||||
|
enabled: true
|
||||||
|
cookie_name: wllbg_trusted_computer
|
||||||
|
cookie_lifetime: 2592000
|
||||||
|
|
||||||
|
email:
|
||||||
|
enabled: %twofactor_auth%
|
||||||
|
sender_email: %twofactor_sender%
|
||||||
|
digits: 6
|
||||||
|
template: WallabagUserBundle:Authentication:form.html.twig
|
||||||
|
mailer: wallabag_user.auth_code_mailer
|
||||||
|
|
||||||
|
kphoen_rulerz:
|
||||||
|
executors:
|
||||||
|
doctrine: true
|
||||||
44
app/config/config_dev.yml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: config.yml }
|
||||||
|
|
||||||
|
framework:
|
||||||
|
router:
|
||||||
|
resource: "%kernel.root_dir%/config/routing_dev.yml"
|
||||||
|
strict_requirements: true
|
||||||
|
profiler: { only_exceptions: false }
|
||||||
|
|
||||||
|
web_profiler:
|
||||||
|
toolbar: true
|
||||||
|
intercept_redirects: false
|
||||||
|
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: stream
|
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
|
level: debug
|
||||||
|
channels: [!event]
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
|
bubble: false
|
||||||
|
verbosity_levels:
|
||||||
|
VERBOSITY_VERBOSE: INFO
|
||||||
|
VERBOSITY_VERY_VERBOSE: DEBUG
|
||||||
|
channels: [!event, !doctrine]
|
||||||
|
console_very_verbose:
|
||||||
|
type: console
|
||||||
|
bubble: false
|
||||||
|
verbosity_levels:
|
||||||
|
VERBOSITY_VERBOSE: NOTICE
|
||||||
|
VERBOSITY_VERY_VERBOSE: NOTICE
|
||||||
|
VERBOSITY_DEBUG: DEBUG
|
||||||
|
channels: [doctrine]
|
||||||
|
|
||||||
|
assetic:
|
||||||
|
use_controller: true
|
||||||
|
|
||||||
|
swiftmailer:
|
||||||
|
# see http://mailcatcher.me/
|
||||||
|
transport: smtp
|
||||||
|
host: 'localhost'
|
||||||
|
port: 1025
|
||||||
25
app/config/config_prod.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: config.yml }
|
||||||
|
|
||||||
|
#framework:
|
||||||
|
# validation:
|
||||||
|
# cache: apc
|
||||||
|
|
||||||
|
#doctrine:
|
||||||
|
# orm:
|
||||||
|
# metadata_cache_driver: apc
|
||||||
|
# result_cache_driver: apc
|
||||||
|
# query_cache_driver: apc
|
||||||
|
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: fingers_crossed
|
||||||
|
action_level: error
|
||||||
|
handler: nested
|
||||||
|
nested:
|
||||||
|
type: stream
|
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
|
level: debug
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
36
app/config/config_test.yml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
imports:
|
||||||
|
- { resource: config_dev.yml }
|
||||||
|
|
||||||
|
framework:
|
||||||
|
test: ~
|
||||||
|
session:
|
||||||
|
storage_id: session.storage.mock_file
|
||||||
|
profiler:
|
||||||
|
collect: false
|
||||||
|
|
||||||
|
web_profiler:
|
||||||
|
toolbar: false
|
||||||
|
intercept_redirects: false
|
||||||
|
|
||||||
|
swiftmailer:
|
||||||
|
# to be able to read emails sent
|
||||||
|
spool:
|
||||||
|
type: file
|
||||||
|
|
||||||
|
doctrine:
|
||||||
|
dbal:
|
||||||
|
driver: "%test_database_driver%"
|
||||||
|
host: "%test_database_host%"
|
||||||
|
port: "%test_database_port%"
|
||||||
|
dbname: "%test_database_name%"
|
||||||
|
user: "%test_database_user%"
|
||||||
|
password: "%test_database_password%"
|
||||||
|
charset: UTF8
|
||||||
|
path: "%test_database_path%"
|
||||||
|
orm:
|
||||||
|
metadata_cache_driver:
|
||||||
|
type: service
|
||||||
|
id: filesystem_cache
|
||||||
|
query_cache_driver:
|
||||||
|
type: service
|
||||||
|
id: filesystem_cache
|
||||||
75
app/config/parameters.yml.dist
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# This file is a "template" of what your parameters.yml file should look like
|
||||||
|
parameters:
|
||||||
|
# Uncomment these settings or manually update your parameters.yml
|
||||||
|
# to use docker-compose
|
||||||
|
#
|
||||||
|
# database_driver: %env.database_driver%
|
||||||
|
# database_host: %env.database_host%
|
||||||
|
# database_port: %env.database_port%
|
||||||
|
# database_name: %env.database_name%
|
||||||
|
# database_user: %env.database_user%
|
||||||
|
# database_password: %env.database_password%
|
||||||
|
|
||||||
|
database_driver: pdo_sqlite
|
||||||
|
database_host: 127.0.0.1
|
||||||
|
database_port: ~
|
||||||
|
database_name: symfony
|
||||||
|
database_user: root
|
||||||
|
database_password: ~
|
||||||
|
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
|
||||||
|
database_table_prefix: wallabag_
|
||||||
|
|
||||||
|
test_database_driver: pdo_sqlite
|
||||||
|
test_database_host: 127.0.0.1
|
||||||
|
test_database_port: ~
|
||||||
|
test_database_name: ~
|
||||||
|
test_database_user: ~
|
||||||
|
test_database_password: ~
|
||||||
|
test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite"
|
||||||
|
|
||||||
|
mailer_transport: smtp
|
||||||
|
mailer_host: 127.0.0.1
|
||||||
|
mailer_user: ~
|
||||||
|
mailer_password: ~
|
||||||
|
|
||||||
|
locale: en
|
||||||
|
|
||||||
|
# A secret key that's used to generate certain security-related tokens
|
||||||
|
secret: ThisTokenIsNotSoSecretChangeIt
|
||||||
|
|
||||||
|
# wallabag misc
|
||||||
|
app.version: 2.0.0-alpha
|
||||||
|
twofactor_auth: true
|
||||||
|
twofactor_sender: no-reply@wallabag.org
|
||||||
|
|
||||||
|
# message to display at the bottom of the page
|
||||||
|
warning_message: >
|
||||||
|
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
|
||||||
|
|
||||||
|
download_pictures: false # if true, pictures will be stored into data/assets for each article
|
||||||
|
|
||||||
|
# Entry view
|
||||||
|
share_twitter: true
|
||||||
|
share_mail: true
|
||||||
|
share_shaarli: true
|
||||||
|
shaarli_url: http://myshaarli.com
|
||||||
|
share_diaspora: true
|
||||||
|
diaspora_url: http://diasporapod.com
|
||||||
|
flattr: true
|
||||||
|
carrot: true
|
||||||
|
show_printlink: true
|
||||||
|
export_epub: true
|
||||||
|
export_mobi: true
|
||||||
|
export_pdf: true
|
||||||
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
|
# default user config
|
||||||
|
items_on_page: 12
|
||||||
|
theme: material
|
||||||
|
language: en
|
||||||
|
from_email: no-reply@wallabag.org
|
||||||
|
rss_limit: 50
|
||||||
|
|
||||||
|
# pocket import
|
||||||
|
pocket_consumer_key: xxxxxxxx
|
||||||
33
app/config/routing.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
wallabag_import:
|
||||||
|
resource: "@WallabagImportBundle/Controller/"
|
||||||
|
type: annotation
|
||||||
|
prefix: /import
|
||||||
|
|
||||||
|
wallabag_api:
|
||||||
|
resource: "@WallabagApiBundle/Resources/config/routing.yml"
|
||||||
|
prefix: /
|
||||||
|
|
||||||
|
app:
|
||||||
|
resource: "@WallabagCoreBundle/Controller/"
|
||||||
|
type: annotation
|
||||||
|
|
||||||
|
doc-api:
|
||||||
|
resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
|
||||||
|
prefix: /api/doc
|
||||||
|
|
||||||
|
rest :
|
||||||
|
type : rest
|
||||||
|
resource : "routing_rest.yml"
|
||||||
|
prefix : /api
|
||||||
|
|
||||||
|
homepage:
|
||||||
|
path: "/{page}"
|
||||||
|
defaults: { _controller: WallabagCoreBundle:Entry:showUnread, page : 1 }
|
||||||
|
requirements:
|
||||||
|
page: \d+
|
||||||
|
|
||||||
|
fos_user:
|
||||||
|
resource: "@FOSUserBundle/Resources/config/routing/all.xml"
|
||||||
|
|
||||||
|
fos_oauth_server_token:
|
||||||
|
resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"
|
||||||
14
app/config/routing_dev.yml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
_wdt:
|
||||||
|
resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml"
|
||||||
|
prefix: /_wdt
|
||||||
|
|
||||||
|
_profiler:
|
||||||
|
resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml"
|
||||||
|
prefix: /_profiler
|
||||||
|
|
||||||
|
_errors:
|
||||||
|
resource: "@TwigBundle/Resources/config/routing/errors.xml"
|
||||||
|
prefix: /_error
|
||||||
|
|
||||||
|
_main:
|
||||||
|
resource: routing.yml
|
||||||
3
app/config/routing_rest.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Rest_Wallabag:
|
||||||
|
type : rest
|
||||||
|
resource: "@WallabagApiBundle/Resources/config/routing_rest.yml"
|
||||||
60
app/config/security.yml
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
security:
|
||||||
|
encoders:
|
||||||
|
FOS\UserBundle\Model\UserInterface: sha512
|
||||||
|
|
||||||
|
role_hierarchy:
|
||||||
|
ROLE_ADMIN: ROLE_USER
|
||||||
|
ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
|
||||||
|
|
||||||
|
providers:
|
||||||
|
administrators:
|
||||||
|
entity: { class: WallabagUserBundle:User, property: username }
|
||||||
|
fos_userbundle:
|
||||||
|
id: fos_user.user_provider.username
|
||||||
|
|
||||||
|
# the main part of the security, where you can set up firewalls
|
||||||
|
# for specific sections of your app
|
||||||
|
firewalls:
|
||||||
|
# disables authentication for assets and the profiler, adapt it according to your needs
|
||||||
|
dev:
|
||||||
|
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||||
|
security: false
|
||||||
|
|
||||||
|
oauth_token:
|
||||||
|
pattern: ^/oauth/v2/token
|
||||||
|
security: false
|
||||||
|
|
||||||
|
api:
|
||||||
|
pattern: /api/.*
|
||||||
|
fos_oauth: true
|
||||||
|
stateless: true
|
||||||
|
anonymous: true
|
||||||
|
|
||||||
|
login_firewall:
|
||||||
|
pattern: ^/login$
|
||||||
|
anonymous: ~
|
||||||
|
|
||||||
|
secured_area:
|
||||||
|
pattern: ^/
|
||||||
|
form_login:
|
||||||
|
provider: fos_userbundle
|
||||||
|
csrf_token_generator: security.csrf.token_manager
|
||||||
|
|
||||||
|
anonymous: true
|
||||||
|
remember_me:
|
||||||
|
secret: "%secret%"
|
||||||
|
lifetime: 31536000
|
||||||
|
path: /
|
||||||
|
domain: ~
|
||||||
|
|
||||||
|
logout:
|
||||||
|
path: /logout
|
||||||
|
target: /
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
- { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||||
|
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||||
|
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||||
|
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||||
|
- { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
|
||||||
|
- { path: ^/, roles: ROLE_USER }
|
||||||
32
app/config/services.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
parameters:
|
||||||
|
lexik_form_filter.get_filter.doctrine_orm.class: Wallabag\CoreBundle\Event\Subscriber\CustomDoctrineORMSubscriber
|
||||||
|
|
||||||
|
services:
|
||||||
|
# used for tests
|
||||||
|
filesystem_cache:
|
||||||
|
class: Doctrine\Common\Cache\FilesystemCache
|
||||||
|
arguments:
|
||||||
|
- %kernel.cache_dir%/doctrine/metadata
|
||||||
|
|
||||||
|
twig.extension.text:
|
||||||
|
class: Twig_Extensions_Extension_Text
|
||||||
|
tags:
|
||||||
|
- { name: twig.extension }
|
||||||
|
|
||||||
|
wallabag.twig_extension:
|
||||||
|
class: Wallabag\CoreBundle\Twig\WallabagExtension
|
||||||
|
public: false
|
||||||
|
tags:
|
||||||
|
- { name: twig.extension }
|
||||||
|
|
||||||
|
wallabag.locale_listener:
|
||||||
|
class: Wallabag\CoreBundle\EventListener\LocaleListener
|
||||||
|
arguments: ["%kernel.default_locale%"]
|
||||||
|
tags:
|
||||||
|
- { name: kernel.event_subscriber }
|
||||||
|
|
||||||
|
wallabag.user_locale_listener:
|
||||||
|
class: Wallabag\CoreBundle\EventListener\UserLocaleListener
|
||||||
|
arguments: ["@session"]
|
||||||
|
tags:
|
||||||
|
- { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }
|
||||||
65
app/config/tests/parameters.yml.dist.mysql
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# This file is a "template" of what your parameters.yml file should look like
|
||||||
|
parameters:
|
||||||
|
database_driver: pdo_sqlite
|
||||||
|
database_host: 127.0.0.1
|
||||||
|
database_port: ~
|
||||||
|
database_name: symfony
|
||||||
|
database_user: root
|
||||||
|
database_password: ~
|
||||||
|
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
|
||||||
|
database_table_prefix: wallabag_
|
||||||
|
|
||||||
|
test_database_driver: pdo_mysql
|
||||||
|
test_database_host: localhost
|
||||||
|
test_database_port: 3306
|
||||||
|
test_database_name: wallabag
|
||||||
|
test_database_user: root
|
||||||
|
test_database_password: ~
|
||||||
|
test_database_path: ~
|
||||||
|
|
||||||
|
mailer_transport: smtp
|
||||||
|
mailer_host: 127.0.0.1
|
||||||
|
mailer_user: ~
|
||||||
|
mailer_password: ~
|
||||||
|
|
||||||
|
locale: en
|
||||||
|
|
||||||
|
# A secret key that's used to generate certain security-related tokens
|
||||||
|
secret: ThisTokenIsNotSoSecretChangeIt
|
||||||
|
|
||||||
|
# wallabag misc
|
||||||
|
app.version: 2.0.0-alpha
|
||||||
|
twofactor_auth: true
|
||||||
|
twofactor_sender: no-reply@wallabag.org
|
||||||
|
|
||||||
|
# message to display at the bottom of the page
|
||||||
|
warning_message: >
|
||||||
|
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
|
||||||
|
|
||||||
|
download_pictures: false # if true, pictures will be stored into data/assets for each article
|
||||||
|
|
||||||
|
# Entry view
|
||||||
|
share_twitter: true
|
||||||
|
share_mail: true
|
||||||
|
share_shaarli: true
|
||||||
|
shaarli_url: http://myshaarli.com
|
||||||
|
share_diaspora: true
|
||||||
|
diaspora_url: http://diasporapod.com
|
||||||
|
flattr: true
|
||||||
|
carrot: true
|
||||||
|
show_printlink: true
|
||||||
|
export_epub: true
|
||||||
|
export_mobi: true
|
||||||
|
export_pdf: true
|
||||||
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
|
# default user config
|
||||||
|
items_on_page: 12
|
||||||
|
theme: material
|
||||||
|
language: en_US
|
||||||
|
from_email: no-reply@wallabag.org
|
||||||
|
rss_limit: 50
|
||||||
|
|
||||||
|
# pocket import
|
||||||
|
pocket_consumer_key: xxxxxxxx
|
||||||
65
app/config/tests/parameters.yml.dist.pgsql
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# This file is a "template" of what your parameters.yml file should look like
|
||||||
|
parameters:
|
||||||
|
database_driver: pdo_sqlite
|
||||||
|
database_host: 127.0.0.1
|
||||||
|
database_port: ~
|
||||||
|
database_name: symfony
|
||||||
|
database_user: root
|
||||||
|
database_password: ~
|
||||||
|
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
|
||||||
|
database_table_prefix: wallabag_
|
||||||
|
|
||||||
|
test_database_driver: pdo_pgsql
|
||||||
|
test_database_host: localhost
|
||||||
|
test_database_port:
|
||||||
|
test_database_name: wallabag
|
||||||
|
test_database_user: travis
|
||||||
|
test_database_password: ~
|
||||||
|
test_database_path: ~
|
||||||
|
|
||||||
|
mailer_transport: smtp
|
||||||
|
mailer_host: 127.0.0.1
|
||||||
|
mailer_user: ~
|
||||||
|
mailer_password: ~
|
||||||
|
|
||||||
|
locale: en
|
||||||
|
|
||||||
|
# A secret key that's used to generate certain security-related tokens
|
||||||
|
secret: ThisTokenIsNotSoSecretChangeIt
|
||||||
|
|
||||||
|
# wallabag misc
|
||||||
|
app.version: 2.0.0-alpha
|
||||||
|
twofactor_auth: true
|
||||||
|
twofactor_sender: no-reply@wallabag.org
|
||||||
|
|
||||||
|
# message to display at the bottom of the page
|
||||||
|
warning_message: >
|
||||||
|
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
|
||||||
|
|
||||||
|
download_pictures: false # if true, pictures will be stored into data/assets for each article
|
||||||
|
|
||||||
|
# Entry view
|
||||||
|
share_twitter: true
|
||||||
|
share_mail: true
|
||||||
|
share_shaarli: true
|
||||||
|
shaarli_url: http://myshaarli.com
|
||||||
|
share_diaspora: true
|
||||||
|
diaspora_url: http://diasporapod.com
|
||||||
|
flattr: true
|
||||||
|
carrot: true
|
||||||
|
show_printlink: true
|
||||||
|
export_epub: true
|
||||||
|
export_mobi: true
|
||||||
|
export_pdf: true
|
||||||
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
|
# default user config
|
||||||
|
items_on_page: 12
|
||||||
|
theme: material
|
||||||
|
language: en_US
|
||||||
|
from_email: no-reply@wallabag.org
|
||||||
|
rss_limit: 50
|
||||||
|
|
||||||
|
# pocket import
|
||||||
|
pocket_consumer_key: xxxxxxxx
|
||||||
65
app/config/tests/parameters.yml.dist.sqlite
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# This file is a "template" of what your parameters.yml file should look like
|
||||||
|
parameters:
|
||||||
|
database_driver: pdo_sqlite
|
||||||
|
database_host: 127.0.0.1
|
||||||
|
database_port: ~
|
||||||
|
database_name: symfony
|
||||||
|
database_user: root
|
||||||
|
database_password: ~
|
||||||
|
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
|
||||||
|
database_table_prefix: wallabag_
|
||||||
|
|
||||||
|
test_database_driver: pdo_sqlite
|
||||||
|
test_database_host: localhost
|
||||||
|
test_database_port:
|
||||||
|
test_database_name: ~
|
||||||
|
test_database_user: ~
|
||||||
|
test_database_password: ~
|
||||||
|
test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite"
|
||||||
|
|
||||||
|
mailer_transport: smtp
|
||||||
|
mailer_host: 127.0.0.1
|
||||||
|
mailer_user: ~
|
||||||
|
mailer_password: ~
|
||||||
|
|
||||||
|
locale: en
|
||||||
|
|
||||||
|
# A secret key that's used to generate certain security-related tokens
|
||||||
|
secret: ThisTokenIsNotSoSecretChangeIt
|
||||||
|
|
||||||
|
# wallabag misc
|
||||||
|
app.version: 2.0.0-alpha
|
||||||
|
twofactor_auth: true
|
||||||
|
twofactor_sender: no-reply@wallabag.org
|
||||||
|
|
||||||
|
# message to display at the bottom of the page
|
||||||
|
warning_message: >
|
||||||
|
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
|
||||||
|
|
||||||
|
download_pictures: false # if true, pictures will be stored into data/assets for each article
|
||||||
|
|
||||||
|
# Entry view
|
||||||
|
share_twitter: true
|
||||||
|
share_mail: true
|
||||||
|
share_shaarli: true
|
||||||
|
shaarli_url: http://myshaarli.com
|
||||||
|
share_diaspora: true
|
||||||
|
diaspora_url: http://diasporapod.com
|
||||||
|
flattr: true
|
||||||
|
carrot: true
|
||||||
|
show_printlink: true
|
||||||
|
export_epub: true
|
||||||
|
export_mobi: true
|
||||||
|
export_pdf: true
|
||||||
|
wallabag_url: http://v2.wallabag.org
|
||||||
|
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
|
||||||
|
|
||||||
|
# default user config
|
||||||
|
items_on_page: 12
|
||||||
|
theme: material
|
||||||
|
language: en_US
|
||||||
|
from_email: no-reply@wallabag.org
|
||||||
|
rss_limit: 50
|
||||||
|
|
||||||
|
# pocket import
|
||||||
|
pocket_consumer_key: xxxxxxxx
|
||||||
29
bin/console
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||||
|
use Symfony\Component\Console\Input\ArgvInput;
|
||||||
|
use Symfony\Component\Debug\Debug;
|
||||||
|
|
||||||
|
// if you don't want to setup permissions the proper way, just uncomment the following PHP line
|
||||||
|
// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
|
||||||
|
//umask(0000);
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Composer\Autoload\ClassLoader $loader
|
||||||
|
*/
|
||||||
|
$loader = require __DIR__.'/../app/autoload.php';
|
||||||
|
|
||||||
|
$input = new ArgvInput();
|
||||||
|
$env = $input->getParameterOption(['--env', '-e'], getenv('SYMFONY_ENV') ?: 'dev');
|
||||||
|
$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['--no-debug', '']) && $env !== 'prod';
|
||||||
|
|
||||||
|
if ($debug) {
|
||||||
|
Debug::enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
$kernel = new AppKernel($env, $debug);
|
||||||
|
$application = new Application($kernel);
|
||||||
|
$application->run($input);
|
||||||
1
bin/doctrine
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../vendor/doctrine/orm/bin/doctrine
|
||||||
1
bin/doctrine-dbal
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../vendor/doctrine/dbal/bin/doctrine-dbal
|
||||||
1
bin/doctrine-migrations
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../vendor/doctrine/migrations/bin/doctrine-migrations
|
||||||
1
bin/doctrine.php
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../vendor/doctrine/orm/bin/doctrine.php
|
||||||
1
bin/phpunit
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../vendor/phpunit/phpunit/phpunit
|
||||||
1
bin/security-checker
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../vendor/sensiolabs/security-checker/security-checker
|
||||||
143
bin/symfony_requirements
Executable file
@ -0,0 +1,143 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once dirname(__FILE__).'/../var/SymfonyRequirements.php';
|
||||||
|
|
||||||
|
$lineSize = 70;
|
||||||
|
$symfonyRequirements = new SymfonyRequirements();
|
||||||
|
$iniPath = $symfonyRequirements->getPhpIniConfigPath();
|
||||||
|
|
||||||
|
echo_title('Symfony2 Requirements Checker');
|
||||||
|
|
||||||
|
echo '> PHP is using the following php.ini file:'.PHP_EOL;
|
||||||
|
if ($iniPath) {
|
||||||
|
echo_style('green', ' '.$iniPath);
|
||||||
|
} else {
|
||||||
|
echo_style('warning', ' WARNING: No configuration file (php.ini) used by PHP!');
|
||||||
|
}
|
||||||
|
|
||||||
|
echo PHP_EOL.PHP_EOL;
|
||||||
|
|
||||||
|
echo '> Checking Symfony requirements:'.PHP_EOL.' ';
|
||||||
|
|
||||||
|
$messages = array();
|
||||||
|
foreach ($symfonyRequirements->getRequirements() as $req) {
|
||||||
|
/** @var $req Requirement */
|
||||||
|
if ($helpText = get_error_message($req, $lineSize)) {
|
||||||
|
echo_style('red', 'E');
|
||||||
|
$messages['error'][] = $helpText;
|
||||||
|
} else {
|
||||||
|
echo_style('green', '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$checkPassed = empty($messages['error']);
|
||||||
|
|
||||||
|
foreach ($symfonyRequirements->getRecommendations() as $req) {
|
||||||
|
if ($helpText = get_error_message($req, $lineSize)) {
|
||||||
|
echo_style('yellow', 'W');
|
||||||
|
$messages['warning'][] = $helpText;
|
||||||
|
} else {
|
||||||
|
echo_style('green', '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($checkPassed) {
|
||||||
|
echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects');
|
||||||
|
} else {
|
||||||
|
echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects');
|
||||||
|
|
||||||
|
echo_title('Fix the following mandatory requirements', 'red');
|
||||||
|
|
||||||
|
foreach ($messages['error'] as $helpText) {
|
||||||
|
echo ' * '.$helpText.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($messages['warning'])) {
|
||||||
|
echo_title('Optional recommendations to improve your setup', 'yellow');
|
||||||
|
|
||||||
|
foreach ($messages['warning'] as $helpText) {
|
||||||
|
echo ' * '.$helpText.PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo PHP_EOL;
|
||||||
|
echo_style('title', 'Note');
|
||||||
|
echo ' The command console could use a different php.ini file'.PHP_EOL;
|
||||||
|
echo_style('title', '~~~~');
|
||||||
|
echo ' than the one used with your web server. To be on the'.PHP_EOL;
|
||||||
|
echo ' safe side, please check the requirements from your web'.PHP_EOL;
|
||||||
|
echo ' server using the ';
|
||||||
|
echo_style('yellow', 'web/config.php');
|
||||||
|
echo ' script.'.PHP_EOL;
|
||||||
|
echo PHP_EOL;
|
||||||
|
|
||||||
|
exit($checkPassed ? 0 : 1);
|
||||||
|
|
||||||
|
function get_error_message(Requirement $requirement, $lineSize)
|
||||||
|
{
|
||||||
|
if ($requirement->isFulfilled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errorMessage = wordwrap($requirement->getTestMessage(), $lineSize - 3, PHP_EOL.' ').PHP_EOL;
|
||||||
|
$errorMessage .= ' > '.wordwrap($requirement->getHelpText(), $lineSize - 5, PHP_EOL.' > ').PHP_EOL;
|
||||||
|
|
||||||
|
return $errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
function echo_title($title, $style = null)
|
||||||
|
{
|
||||||
|
$style = $style ?: 'title';
|
||||||
|
|
||||||
|
echo PHP_EOL;
|
||||||
|
echo_style($style, $title.PHP_EOL);
|
||||||
|
echo_style($style, str_repeat('~', strlen($title)).PHP_EOL);
|
||||||
|
echo PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function echo_style($style, $message)
|
||||||
|
{
|
||||||
|
// ANSI color codes
|
||||||
|
$styles = array(
|
||||||
|
'reset' => "\033[0m",
|
||||||
|
'red' => "\033[31m",
|
||||||
|
'green' => "\033[32m",
|
||||||
|
'yellow' => "\033[33m",
|
||||||
|
'error' => "\033[37;41m",
|
||||||
|
'success' => "\033[37;42m",
|
||||||
|
'title' => "\033[34m",
|
||||||
|
);
|
||||||
|
$supports = has_color_support();
|
||||||
|
|
||||||
|
echo($supports ? $styles[$style] : '').$message.($supports ? $styles['reset'] : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function echo_block($style, $title, $message)
|
||||||
|
{
|
||||||
|
$message = ' '.trim($message).' ';
|
||||||
|
$width = strlen($message);
|
||||||
|
|
||||||
|
echo PHP_EOL.PHP_EOL;
|
||||||
|
|
||||||
|
echo_style($style, str_repeat(' ', $width).PHP_EOL);
|
||||||
|
echo_style($style, str_pad(' ['.$title.']', $width, ' ', STR_PAD_RIGHT).PHP_EOL);
|
||||||
|
echo_style($style, str_pad($message, $width, ' ', STR_PAD_RIGHT).PHP_EOL);
|
||||||
|
echo_style($style, str_repeat(' ', $width).PHP_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
function has_color_support()
|
||||||
|
{
|
||||||
|
static $support;
|
||||||
|
|
||||||
|
if (null === $support) {
|
||||||
|
if (DIRECTORY_SEPARATOR == '\\') {
|
||||||
|
$support = false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI');
|
||||||
|
} else {
|
||||||
|
$support = function_exists('posix_isatty') && @posix_isatty(STDOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $support;
|
||||||
|
}
|
||||||
90
build.xml
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="wallabag" default="build">
|
||||||
|
<target name="build" depends="clean,prepare,phpunit"/>
|
||||||
|
<target name="prepare-mysql" depends="clean,db_mysql,prepare"/>
|
||||||
|
<target name="prepare-sqlite" depends="clean,db_sqlite,prepare"/>
|
||||||
|
<target name="prepare-pgsql" depends="clean,db_pgsql,prepare"/>
|
||||||
|
|
||||||
|
<target name="clean" description="Cleanup build artifacts">
|
||||||
|
<delete dir="${basedir}/var/cache"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="prepare" description="Prepare for build">
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="doctrine:database:drop"/>
|
||||||
|
<arg value="--force"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="doctrine:database:create"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="doctrine:schema:create"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="cache:clear"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="doctrine:fixtures:load"/>
|
||||||
|
<arg value="--no-interaction"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="db_mysql" description="Run test for MySQL">
|
||||||
|
<delete dir="${basedir}/app/config/parameters.yml"/>
|
||||||
|
<exec executable="cp">
|
||||||
|
<arg value="${basedir}/app/config/tests/parameters.yml.dist.mysql"/>
|
||||||
|
<arg value="${basedir}/app/config/parameters.yml"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="cache:clear"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="db_sqlite" description="Run test for SQLite">
|
||||||
|
<delete dir="${basedir}/app/config/parameters.yml"/>
|
||||||
|
<exec executable="cp">
|
||||||
|
<arg value="${basedir}/app/config/tests/parameters.yml.dist.sqlite"/>
|
||||||
|
<arg value="${basedir}/app/config/parameters.yml"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="cache:clear"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="db_pgsql" description="Run test for PostgreSQL">
|
||||||
|
<delete dir="${basedir}/app/config/parameters.yml"/>
|
||||||
|
<exec executable="cp">
|
||||||
|
<arg value="${basedir}/app/config/tests/parameters.yml.dist.pgsql"/>
|
||||||
|
<arg value="${basedir}/app/config/parameters.yml"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="php">
|
||||||
|
<arg value="${basedir}/bin/console"/>
|
||||||
|
<arg value="cache:clear"/>
|
||||||
|
<arg value="--env=test"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="phpunit" description="Run unit tests with PHPUnit + HTML Coverage">
|
||||||
|
<exec executable="phpunit" failonerror="true">
|
||||||
|
<arg value="--coverage-html"/>
|
||||||
|
<arg value="build/coverage"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
</project>
|
||||||
120
composer.json
@ -1,7 +1,119 @@
|
|||||||
{
|
{
|
||||||
"require": {
|
"name": "wallabag/wallabag",
|
||||||
"twig/twig": "1.*",
|
"type": "project",
|
||||||
"twig/extensions": "1.0.*",
|
"description": "open source self hostable read-it-later web application",
|
||||||
"umpirsky/twig-gettext-extractor": "1.1.*"
|
"keywords": ["read-it-later","read it later"],
|
||||||
|
"homepage": "https://github.com/wallabag/wallabag",
|
||||||
|
"license": "MIT",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Nicolas Lœuillet",
|
||||||
|
"email": "nicolas@loeuillet.org",
|
||||||
|
"homepage": "http://www.cdetc.fr",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Thomas Citharel",
|
||||||
|
"homepage": "http://tcit.fr",
|
||||||
|
"role": "Developer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Jérémy Benoist",
|
||||||
|
"homepage": "http://www.j0k3r.net",
|
||||||
|
"role": "Developer"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"email": "hello@wallabag.org",
|
||||||
|
"issues": "https://github.com/wallabag/wallabag/issues"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.5.9",
|
||||||
|
"symfony/symfony": "3.0.*",
|
||||||
|
"doctrine/orm": "^2.5",
|
||||||
|
"doctrine/doctrine-bundle": "^1.6",
|
||||||
|
"doctrine/doctrine-cache-bundle": "^1.2",
|
||||||
|
"twig/extensions": "~1.0",
|
||||||
|
"symfony/assetic-bundle": "~2.3",
|
||||||
|
"symfony/swiftmailer-bundle": "^2.3",
|
||||||
|
"symfony/monolog-bundle": "^2.8",
|
||||||
|
"sensio/distribution-bundle": "^5.0",
|
||||||
|
"sensio/framework-extra-bundle": "^3.0.2",
|
||||||
|
"incenteev/composer-parameter-handler": "^2.0",
|
||||||
|
"nelmio/cors-bundle": "~1.4.0",
|
||||||
|
"friendsofsymfony/rest-bundle": "~1.4",
|
||||||
|
"jms/serializer-bundle": "~1.0",
|
||||||
|
"nelmio/api-doc-bundle": "~2.7",
|
||||||
|
"ezyang/htmlpurifier": "~4.6",
|
||||||
|
"mgargano/simplehtmldom": "~1.5",
|
||||||
|
"tecnickcom/tcpdf": "~6.2",
|
||||||
|
"simplepie/simplepie": "~1.3.1",
|
||||||
|
"willdurand/hateoas-bundle": "~1.0",
|
||||||
|
"htmlawed/htmlawed": "~1.1.19",
|
||||||
|
"liip/theme-bundle": "~1.1",
|
||||||
|
"pagerfanta/pagerfanta": "~1.0.3",
|
||||||
|
"lexik/form-filter-bundle": "~5.0",
|
||||||
|
"j0k3r/graby": "~1.0",
|
||||||
|
"friendsofsymfony/user-bundle": "dev-master",
|
||||||
|
"friendsofsymfony/oauth-server-bundle": "^1.5@dev",
|
||||||
|
"stof/doctrine-extensions-bundle": "^1.2@dev",
|
||||||
|
"scheb/two-factor-bundle": "~2.0",
|
||||||
|
"grandt/phpepub": "~4.0",
|
||||||
|
"wallabag/php-mobi": "~1.0.0",
|
||||||
|
"kphoen/rulerz-bundle": "~0.10",
|
||||||
|
"guzzlehttp/guzzle": "^5.2.0",
|
||||||
|
"doctrine/doctrine-migrations-bundle": "^1.0",
|
||||||
|
"paragonie/random_compat": "~1.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/doctrine-fixtures-bundle": "~2.2",
|
||||||
|
"sensio/generator-bundle": "^3.0",
|
||||||
|
"phpunit/phpunit": "~4.4",
|
||||||
|
"symfony/phpunit-bridge": "^2.7"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build-parameters": [
|
||||||
|
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters"
|
||||||
|
],
|
||||||
|
"post-cmd": [
|
||||||
|
"@build-parameters",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
|
||||||
|
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget"
|
||||||
|
],
|
||||||
|
"post-install-cmd": [
|
||||||
|
"@post-cmd"
|
||||||
|
],
|
||||||
|
"post-update-cmd": [
|
||||||
|
"@post-cmd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"symfony-app-dir": "app",
|
||||||
|
"symfony-bin-dir": "bin",
|
||||||
|
"symfony-var-dir": "var",
|
||||||
|
"symfony-web-dir": "web",
|
||||||
|
"symfony-tests-dir": "tests",
|
||||||
|
"symfony-assets-install": "relative",
|
||||||
|
"incenteev-parameters": {
|
||||||
|
"file": "app/config/parameters.yml",
|
||||||
|
"env-map": {
|
||||||
|
"mailer_host": "WALLABAG_MAILER_HOST",
|
||||||
|
"mailer_user": "WALLABAG_MAILER_USER",
|
||||||
|
"mailer_password": "WALLABAG_MAILER_PASSWORD",
|
||||||
|
"secret": "WALLABAG_SECRET"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "": "src/" },
|
||||||
|
"classmap": [ "app/AppKernel.php", "app/AppCache.php" ]
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"bin-dir": "bin"
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
}
|
}
|
||||||
6982
composer.lock
generated
0
db/.gitignore → data/assets/.gitignore
vendored
0
data/db/.gitignore
vendored
Normal file
42
docker-compose.yml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
nginx:
|
||||||
|
image: nginx
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
volumes:
|
||||||
|
- ./docker/nginx/nginx.conf:/nginx.conf
|
||||||
|
- ./docker/logs/nginx:/var/log/nginx
|
||||||
|
- .:/var/www/html
|
||||||
|
links:
|
||||||
|
- php:php
|
||||||
|
command: nginx -c /nginx.conf
|
||||||
|
php:
|
||||||
|
build: docker/php
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
volumes:
|
||||||
|
- .:/var/www/html
|
||||||
|
#links:
|
||||||
|
# - "postgres:rdbms"
|
||||||
|
# - "mariadb:rdbms"
|
||||||
|
env_file:
|
||||||
|
- ./docker/php/env
|
||||||
|
# Comment non-used DBMS lines
|
||||||
|
# If all DBMS are commented out, sqlite will be used as default
|
||||||
|
# - ./docker/postgres/env
|
||||||
|
# - ./docker/mariadb/env
|
||||||
|
#postgres:
|
||||||
|
# image: postgres:9
|
||||||
|
# ports:
|
||||||
|
# - "5432:5432"
|
||||||
|
# volumes:
|
||||||
|
# - ./docker/data/pgsql:/var/lib/postgresql/data
|
||||||
|
# env_file:
|
||||||
|
# - ./docker/postgres/env
|
||||||
|
#mariadb:
|
||||||
|
# image: mariadb:10
|
||||||
|
# ports:
|
||||||
|
# - "3306:3306"
|
||||||
|
# volumes:
|
||||||
|
# - ./docker/data/mariadb:/var/lib/mysql
|
||||||
|
# env_file:
|
||||||
|
# - ./docker/mariadb/env
|
||||||
10
docker/mariadb/env
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
MYSQL_ROOT_PASSWORD=wallaroot
|
||||||
|
MYSQL_USER=wallabag
|
||||||
|
MYSQL_PASSWORD=wallapass
|
||||||
|
MYSQL_DATABASE=wallabag
|
||||||
|
SYMFONY__ENV__DATABASE_DRIVER=pdo_mysql
|
||||||
|
SYMFONY__ENV__DATABASE_HOST=rdbms
|
||||||
|
SYMFONY__ENV__DATABASE_PORT=3306
|
||||||
|
SYMFONY__ENV__DATABASE_NAME=wallabag
|
||||||
|
SYMFONY__ENV__DATABASE_USER=wallabag
|
||||||
|
SYMFONY__ENV__DATABASE_PASSWORD=wallapass
|
||||||
89
docker/nginx/nginx.conf
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
user nginx;
|
||||||
|
worker_processes 1;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 2048;
|
||||||
|
multi_accept on;
|
||||||
|
use epoll;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
|
||||||
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||||
|
'$status $body_bytes_sent "$http_referer" '
|
||||||
|
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log main;
|
||||||
|
|
||||||
|
server_tokens off;
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
tcp_nodelay on;
|
||||||
|
keepalive_timeout 15;
|
||||||
|
types_hash_max_size 2048;
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
access_log off;
|
||||||
|
error_log off;
|
||||||
|
gzip on;
|
||||||
|
gzip_disable "msie6";
|
||||||
|
open_file_cache max=100;
|
||||||
|
|
||||||
|
|
||||||
|
upstream php-upstream {
|
||||||
|
server php:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
#server_name domain.tld www.domain.tld;
|
||||||
|
root /var/www/html/web;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
# try to serve file directly, fallback to app.php
|
||||||
|
try_files $uri /app.php$is_args$args;
|
||||||
|
}
|
||||||
|
# DEV
|
||||||
|
# This rule should only be placed on your development environment
|
||||||
|
# In production, don't include this and don't deploy app_dev.php or config.php
|
||||||
|
location ~ ^/(app_dev|config)\.php(/|$) {
|
||||||
|
fastcgi_pass php-upstream;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||||
|
include fastcgi_params;
|
||||||
|
# When you are using symlinks to link the document root to the
|
||||||
|
# current version of your application, you should pass the real
|
||||||
|
# application path instead of the path to the symlink to PHP
|
||||||
|
# FPM.
|
||||||
|
# Otherwise, PHP's OPcache may not properly detect changes to
|
||||||
|
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
|
||||||
|
# for more information).
|
||||||
|
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||||
|
fastcgi_param DOCUMENT_ROOT $realpath_root;
|
||||||
|
}
|
||||||
|
# PROD
|
||||||
|
location ~ ^/app\.php(/|$) {
|
||||||
|
fastcgi_pass php-upstream;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.*)$;
|
||||||
|
include fastcgi_params;
|
||||||
|
# When you are using symlinks to link the document root to the
|
||||||
|
# current version of your application, you should pass the real
|
||||||
|
# application path instead of the path to the symlink to PHP
|
||||||
|
# FPM.
|
||||||
|
# Otherwise, PHP's OPcache may not properly detect changes to
|
||||||
|
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
|
||||||
|
# for more information).
|
||||||
|
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||||
|
fastcgi_param DOCUMENT_ROOT $realpath_root;
|
||||||
|
# Prevents URIs that include the front controller. This will 404:
|
||||||
|
# http://domain.tld/app.php/some-path
|
||||||
|
# Remove the internal directive to allow URIs like this
|
||||||
|
internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_log /var/log/nginx/project_error.log;
|
||||||
|
access_log /var/log/nginx/project_access.log;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon off;
|
||||||
10
docker/php/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
FROM php:fpm
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libmcrypt-dev libicu-dev libpq-dev libxml2-dev \
|
||||||
|
&& docker-php-ext-install \
|
||||||
|
iconv mcrypt mbstring intl pdo pdo_mysql pdo_pgsql
|
||||||
|
|
||||||
|
RUN usermod -u 1000 www-data
|
||||||
|
|
||||||
|
CMD ["php-fpm"]
|
||||||
6
docker/php/env
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
SYMFONY__ENV__DATABASE_DRIVER=pdo_sqlite
|
||||||
|
SYMFONY__ENV__DATABASE_HOST=127.0.0.1
|
||||||
|
SYMFONY__ENV__DATABASE_PORT=~
|
||||||
|
SYMFONY__ENV__DATABASE_NAME=symfony
|
||||||
|
SYMFONY__ENV__DATABASE_USER=root
|
||||||
|
SYMFONY__ENV__DATABASE_PASSWORD=~
|
||||||
9
docker/postgres/env
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
POSTGRES_USER=wallabag
|
||||||
|
POSTGRES_PASSWORD=wallapass
|
||||||
|
POSTGRES_DB=wallabag
|
||||||
|
export SYMFONY__ENV__DATABASE_DRIVER=pdo_pgsql
|
||||||
|
export SYMFONY__ENV__DATABASE_HOST=rdbms
|
||||||
|
export SYMFONY__ENV__DATABASE_PORT=5432
|
||||||
|
export SYMFONY__ENV__DATABASE_NAME=wallabag
|
||||||
|
export SYMFONY__ENV__DATABASE_USER=wallabag
|
||||||
|
export SYMFONY__ENV__DATABASE_PASSWORD=wallapass
|
||||||
0
docs/README.rst
Normal file
55
docs/en/conf.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# wallabag documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Fri Oct 16 06:47:23 2015.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
extensions = []
|
||||||
|
templates_path = ['_templates']
|
||||||
|
source_suffix = '.rst'
|
||||||
|
master_doc = 'index'
|
||||||
|
project = u'wallabag'
|
||||||
|
copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence'
|
||||||
|
version = '2.0.0'
|
||||||
|
release = version
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
html_theme = 'default'
|
||||||
|
html_static_path = ['_static']
|
||||||
|
htmlhelp_basename = 'wallabagdoc'
|
||||||
|
latex_elements = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'wallabag.tex', u'wallabag Documentation',
|
||||||
|
u'Nicolas Lœuillet', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
man_pages = [
|
||||||
|
('index', 'wallabag', u'wallabag Documentation',
|
||||||
|
[u'Nicolas Lœuillet'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'wallabag', u'wallabag Documentation',
|
||||||
|
u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
##### Guzzle sphinx theme
|
||||||
|
|
||||||
|
import guzzle_sphinx_theme
|
||||||
|
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
|
||||||
|
html_theme_path = guzzle_sphinx_theme.html_theme_path()
|
||||||
|
html_theme = 'guzzle_sphinx_theme'
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
html_sidebars = {
|
||||||
|
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register the theme as an extension to generate a sitemap.xml
|
||||||
|
extensions.append("guzzle_sphinx_theme")
|
||||||
51
docs/en/developer/docker.rst
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
Run Wallabag in docker-compose
|
||||||
|
==============================
|
||||||
|
|
||||||
|
In order to run your own development instance of wallabag, you may
|
||||||
|
want to use the pre-configured docker compose files.
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
Make sure to have `Docker
|
||||||
|
<https://docs.docker.com/installation/ubuntulinux/>`__ and `Docker
|
||||||
|
Compose <https://docs.docker.com/compose/install/>`__ availables on
|
||||||
|
your system and up to date.
|
||||||
|
|
||||||
|
Switch DBMS
|
||||||
|
-----------
|
||||||
|
|
||||||
|
By default, Wallabag will start with a sqlite database.
|
||||||
|
Since Wallabag provide support for Postgresql and MySQL, docker
|
||||||
|
containers are also available for these ones.
|
||||||
|
|
||||||
|
In ``docker-compose.yml``, for the chosen DBMS uncomment :
|
||||||
|
|
||||||
|
- the container definition (``postgres`` or ``mariadb`` root level
|
||||||
|
block)
|
||||||
|
- the container link in the ``php`` container
|
||||||
|
- the container env file in the ``php`` container
|
||||||
|
|
||||||
|
In order to keep running Symfony commands on your host (such as
|
||||||
|
``wallabag:install``), you also should :
|
||||||
|
|
||||||
|
- source the proper env files on your command line, so variables
|
||||||
|
like ``SYMFONY__ENV__DATABASE_HOST`` will exist.
|
||||||
|
- create a ``127.0.0.1 rdbms`` on your system ``hosts`` file
|
||||||
|
|
||||||
|
Run Wallabag
|
||||||
|
------------
|
||||||
|
|
||||||
|
#. Fork and clone the project
|
||||||
|
#. Edit ``app/config/parameters.yml`` to replace ``database_*``
|
||||||
|
properties with commented ones (with values prefixed by ``env.``)
|
||||||
|
#. ``composer install`` the project dependencies
|
||||||
|
#. ``php app/console wallabag:install`` to create the schema
|
||||||
|
#. ``docker-compose up`` to run the containers
|
||||||
|
#. Finally, browse to http://localhost:8080/ to find your freshly
|
||||||
|
installed wallabag.
|
||||||
|
|
||||||
|
At various step, you'll probably run into UNIX permission problems,
|
||||||
|
bad paths in generated cache, etc…
|
||||||
|
Operations like removing cache files or changing files owners might
|
||||||
|
be frequently required, so don't be afraid !
|
||||||
37
docs/en/index.rst
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
wallabag documentation
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. image:: ../img/wallabag.png
|
||||||
|
:alt: wallabag logo
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
**wallabag** is a read-it-later application: it saves a web page by
|
||||||
|
keeping content only. Elements like navigation or ads are deleted.
|
||||||
|
|
||||||
|
The main documentation for this application is organized into a couple sections:
|
||||||
|
|
||||||
|
* :ref:`user-docs`
|
||||||
|
* :ref:`dev-docs`
|
||||||
|
|
||||||
|
.. _user-docs:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: User documentation
|
||||||
|
|
||||||
|
user/create_account
|
||||||
|
user/login
|
||||||
|
user/configuration
|
||||||
|
user/first_article
|
||||||
|
user/import
|
||||||
|
user/download_articles
|
||||||
|
user/filters
|
||||||
|
user/tags
|
||||||
|
|
||||||
|
.. _dev-docs:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Developer documentation
|
||||||
|
|
||||||
|
developer/docker
|
||||||
2
docs/en/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Sphinx>=1.3.0,<1.4.0
|
||||||
|
guzzle_sphinx_theme>=0.7.0,<0.8.0
|
||||||
99
docs/en/user/configuration.rst
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
Now you're logged in, it's time to configure your account as you want.
|
||||||
|
|
||||||
|
Click on ``Config`` menu. You have five tabs: ``Settings``, ``RSS``, ``User information``, ``Password`` and ``Tagging rules``.
|
||||||
|
|
||||||
|
Settings
|
||||||
|
--------
|
||||||
|
|
||||||
|
Theme
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
wallabag is customizable. You can choose your prefered theme here. You can also create a new one, a chapter is dedicated for this. The default theme is ``Material``, it's the theme used in the documentation screenshots.
|
||||||
|
|
||||||
|
Items per page
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You can change the number of articles displayed on each page.
|
||||||
|
|
||||||
|
Language
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
You can change the language of wallabag interface.
|
||||||
|
|
||||||
|
RSS
|
||||||
|
---
|
||||||
|
|
||||||
|
wallabag provides RSS feeds for each article status: unread, starred and archive.
|
||||||
|
|
||||||
|
Firstly, you need to create a personal token: click on ``Create your token``.
|
||||||
|
It's possible to change your token by clicking on ``Reset your token``.
|
||||||
|
|
||||||
|
Now you have three links, one for each status: add them into your favourite RSS reader.
|
||||||
|
|
||||||
|
You can also define how many articles you want in each RSS feed (default value: 50).
|
||||||
|
|
||||||
|
User information
|
||||||
|
----------------
|
||||||
|
|
||||||
|
You can change your name, your email address and enable ``Two factor authentication``.
|
||||||
|
|
||||||
|
Two factor authentication
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Two-factor authentication (also known as 2FA) is a technology patented in 1984 that provides identification of users by means of the combination of two different components.
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/Two-factor_authentication
|
||||||
|
|
||||||
|
If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email. You have to put this code on the following form.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/2FA_form.png
|
||||||
|
:alt: Two factor authentication
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
If you don't want to receive a code each time you want to login, you can check the ``I'm on a trusted computer`` checkbox: wallabag will remember you for 15 days.
|
||||||
|
|
||||||
|
Password
|
||||||
|
--------
|
||||||
|
|
||||||
|
You can change your password here.
|
||||||
|
|
||||||
|
Tagging rules
|
||||||
|
-------------
|
||||||
|
|
||||||
|
If you want to automatically assign a tag to new articles, this part of the configuration is for you.
|
||||||
|
|
||||||
|
What does « tagging rules » mean?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
They are rules used by wallabag to automatically tag new entries.
|
||||||
|
Each time a new entry is added, all the tagging rules will be used to add the tags you configured, thus saving you the trouble to manually classify your entries.
|
||||||
|
|
||||||
|
How do I use them?
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Let assume you want to tag new entries as *« short reading »* when the reading time is inferior to 3 minutes.
|
||||||
|
In that case, you should put « readingTime <= 3 » in the **Rule** field and *« short reading »* in the **Tags** field.
|
||||||
|
Several tags can added simultaneously by separating them by a comma: *« short reading, must read »*.
|
||||||
|
Complex rules can be written by using predefined operators: if *« readingTime >= 5 AND domainName = "github.com" »* then tag as *« long reading, github »*.
|
||||||
|
|
||||||
|
Which variables and operators can I use to write rules?
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The following variables and operators can be used to create tagging rules:
|
||||||
|
|
||||||
|
=========== ============================================== ======== ==========
|
||||||
|
Variable Meaning Operator Meaning
|
||||||
|
----------- ---------------------------------------------- -------- ----------
|
||||||
|
title Title of the entry <= Less than…
|
||||||
|
url URL of the entry < Strictly less than…
|
||||||
|
isArchived Whether the entry is archived or not => Greater than…
|
||||||
|
isStared Whether the entry is starred or not > Strictly greater than…
|
||||||
|
content The entry's content = Equal to…
|
||||||
|
language The entry's language != Not equal to…
|
||||||
|
mimetype The entry's mime-type OR One rule or another
|
||||||
|
readingTime The estimated entry's reading time, in minutes AND One rule and another
|
||||||
|
domainName The domain name of the entry matches Tests that a subject is matches a search (case-insensitive). Example: title matches "football"
|
||||||
|
=========== ============================================== ======== ==========
|
||||||
43
docs/en/user/create_account.rst
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Create an account
|
||||||
|
=================
|
||||||
|
|
||||||
|
On the login page, click on ``Register`` button.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/registration_form.png
|
||||||
|
:alt: Registration form
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
You have to fill the form. Please sure to type a valid email address, we'll send you an activation email.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/sent_email.png
|
||||||
|
:alt: Email was sent to activate account
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Check your inbox, you now have a new mail with a link like this ``http://wallabag/register/confirm/Ba19wokGovN-DdBQNfg4YgRkUQWRP4-k2g0Bk-hBTX4``. Click on it to activate your account.
|
||||||
|
|
||||||
|
Your account is now activated.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/activated_account.png
|
||||||
|
:alt: Welcome on board!
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Frequently asked questions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
I can't valid the registration form
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Make sure that all fields are well filled:
|
||||||
|
|
||||||
|
* valid email address
|
||||||
|
* same passwords in two fields
|
||||||
|
|
||||||
|
I don't receive my activation email
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Are you sure your email address was correct? Did you check your spams folder?
|
||||||
|
|
||||||
|
When I click on the activation link, I've got this message: ``The user with confirmation token "DtrOPfbQeVkWf6N" does not exist``.
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You already enabled your account or the URL of the activation email is wrong.
|
||||||
16
docs/en/user/download_articles.rst
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Download articles
|
||||||
|
=================
|
||||||
|
|
||||||
|
You can download each article in several formats: ePUB, MOBI, PDF, XML, JSON, CSV.
|
||||||
|
|
||||||
|
On the article view, click on this icon, in the sidebar:
|
||||||
|
|
||||||
|
.. image:: ../../img/user/download_article.png
|
||||||
|
:alt: download article
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
You can also download a full category in these formats. For example, on **Unread** view, click on this icon in the top bar:
|
||||||
|
|
||||||
|
.. image:: ../../img/user/download_articles.png
|
||||||
|
:alt: download articles
|
||||||
|
:align: center
|
||||||
2
docs/en/user/filters.rst
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Filters
|
||||||
|
=======
|
||||||
62
docs/en/user/first_article.rst
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
Save your first article
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The main purpose of wallabag is to save web articles. You have many ways to do it.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
A quickstart will be displayed in the application until you save your first article.
|
||||||
|
|
||||||
|
By using a bookmarklet
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
On the ``Howto`` page, you have a ``Bookmarklet`` tab. Drag and drop the ``bag it!`` link to your bookmarks bar of your browser.
|
||||||
|
|
||||||
|
Now, each time you're reading an article on the web and you want to save it, click on the ``bag it!`` link in your bookmarks bar. The article is saved.
|
||||||
|
|
||||||
|
By using the classic form
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
In the top bar of your screen, you have 3 icons. With the first one, a plus sign, you can easily save a new article.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/topbar.png
|
||||||
|
:alt: Top bar
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Click on it to display a new field, paste the article URL inside and press your ``Return`` key. The article is saved.
|
||||||
|
|
||||||
|
By using a browser add-on
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Firefox
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
*This addon is not yet available for wallabag v2*.
|
||||||
|
|
||||||
|
Chrome
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
*This addon is not yet available for wallabag v2*.
|
||||||
|
|
||||||
|
By using your smarphone application
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
Android
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
*This application is not yet available for wallabag v2*.
|
||||||
|
|
||||||
|
Firefox OS
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
*This application is not yet available for wallabag v2*.
|
||||||
|
|
||||||
|
Windows Phone
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
*This application is not yet available for wallabag v2*.
|
||||||
|
|
||||||
|
iOS
|
||||||
|
~~~
|
||||||
|
|
||||||
|
*This application is not yet available for wallabag v2*.
|
||||||
64
docs/en/user/import.rst
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
Migrate to wallabag
|
||||||
|
===================
|
||||||
|
|
||||||
|
From wallabag 1.x
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Export your data from your wallabag 1.x
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
On your config page, click on ``JSON export`` in the ``Export your wallabag data`` section.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/export_wllbg_1.png
|
||||||
|
:alt: Export from wallabag 1.x
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
You will have a ``wallabag-export-1-1970-01-01.json`` file.
|
||||||
|
|
||||||
|
Import your data into wallabag 2.x
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Click on ``Import`` link in the menu, select your export file on your computer and import it.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/import_wllbg.png
|
||||||
|
:alt: Import from wallabag 1.x
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
All your wallabag 1.x articles will be imported.
|
||||||
|
|
||||||
|
From Pocket
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Create a new applicaton on Pocket
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To import your data from Pocket, we use the Pocket API. You need to create a new application on their developer website to continue.
|
||||||
|
|
||||||
|
* Create a new application `on the developer website <https://getpocket.com/developer/apps/new>`_
|
||||||
|
* Fill in the required fields: application name, application description, permissions (only **retrieve**), platform (**web**), accept the terms of service and submit your new application
|
||||||
|
|
||||||
|
Pocket will give you a **Consumer Key** (for example, `49961-985e4b92fe21fe4c78d682c1`). You need to configure the ``pocket_consumer_key`` into the ``app/config/parameters.yml`` file in wallabag.
|
||||||
|
|
||||||
|
Now, all is fine to migrate from Pocket.
|
||||||
|
|
||||||
|
Import your data into wallabag 2.x
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Click on ``Import`` link in the menu, on ``Import contents`` in Pocket section and then on ``Connect to Pocket and import data``.
|
||||||
|
|
||||||
|
You need to authorize wallabag to interact with your Pocket account. Your data will be imported. Data import can be a demanding process for your server (we need to work on this import to improve it).
|
||||||
|
|
||||||
|
From Instapaper
|
||||||
|
---------------
|
||||||
|
|
||||||
|
*Feature not yet implemented in wallabag v2.*
|
||||||
|
|
||||||
|
From Readability
|
||||||
|
----------------
|
||||||
|
|
||||||
|
*Feature not yet implemented in wallabag v2.*
|
||||||
|
|
||||||
|
From HTML or JSON file
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
*Feature not yet implemented in wallabag v2.*
|
||||||
20
docs/en/user/login.rst
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Login
|
||||||
|
=====
|
||||||
|
|
||||||
|
Your account is now enabled, congratulations!
|
||||||
|
|
||||||
|
To login to wallabag, fill the form on login page.
|
||||||
|
|
||||||
|
If you are on your personal computer and you want to stay connected, you can check the ``Keep me logged in`` checkbox: wallabag will remember you for one year.
|
||||||
|
|
||||||
|
.. image:: ../../img/user/login_form.png
|
||||||
|
:alt: Login form
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
Frequently asked questions
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
I forgot my password
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You can reset your password by clicking on ``Forgot your password?`` link, on the login page. Then, fill the form with your email address
|
||||||
2
docs/en/user/tags.rst
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Tags
|
||||||
|
====
|
||||||
55
docs/fr/conf.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# wallabag documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Fri Oct 16 06:47:23 2015.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
extensions = []
|
||||||
|
templates_path = ['_templates']
|
||||||
|
source_suffix = '.rst'
|
||||||
|
master_doc = 'index'
|
||||||
|
project = u'wallabag-fr'
|
||||||
|
copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence'
|
||||||
|
version = '2.0.0'
|
||||||
|
release = version
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
html_theme = 'default'
|
||||||
|
html_static_path = ['_static']
|
||||||
|
htmlhelp_basename = 'wallabagfrdoc'
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
}
|
||||||
|
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'wallabag-fr.tex', u'wallabag Documentation',
|
||||||
|
u'Nicolas Lœuillet', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
man_pages = [
|
||||||
|
('index', 'wallabagfr', u'wallabag Documentation',
|
||||||
|
[u'Nicolas Lœuillet'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'wallabag', u'wallabag Documentation',
|
||||||
|
u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
##### Guzzle sphinx theme
|
||||||
|
|
||||||
|
import guzzle_sphinx_theme
|
||||||
|
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
|
||||||
|
html_theme_path = guzzle_sphinx_theme.html_theme_path()
|
||||||
|
html_theme = 'guzzle_sphinx_theme'
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
html_sidebars = {
|
||||||
|
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register the theme as an extension to generate a sitemap.xml
|
||||||
|
extensions.append("guzzle_sphinx_theme")
|
||||||
17
docs/fr/index.rst
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
Documentation de wallabag
|
||||||
|
=========================
|
||||||
|
|
||||||
|
.. image:: ../img/wallabag.png
|
||||||
|
:alt: wallabag logo
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
**wallabag** est une application de lecture différée : elle permet
|
||||||
|
simplement d’archiver une page web en ne conservant que le contenu. Les
|
||||||
|
éléments superflus (menu, publicité, etc.) sont supprimés.
|
||||||
|
|
||||||
|
La documentation principale de cette application est découpée en plusieurs sections :
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
index
|
||||||
2
docs/fr/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Sphinx>=1.3.0,<1.4.0
|
||||||
|
guzzle_sphinx_theme>=0.7.0,<0.8.0
|
||||||
BIN
docs/img/user/2FA_form.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/user/activated_account.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/img/user/download_article.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
docs/img/user/download_articles.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
docs/img/user/export_wllbg_1.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
docs/img/user/import_wllbg.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
docs/img/user/login_form.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
docs/img/user/registration_form.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/img/user/sent_email.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/img/user/topbar.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
docs/img/wallabag.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
55
docs/pt-br/conf.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# wallabag documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Fri Oct 16 06:47:23 2015.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
extensions = []
|
||||||
|
templates_path = ['_templates']
|
||||||
|
source_suffix = '.rst'
|
||||||
|
master_doc = 'index'
|
||||||
|
project = u'wallabag-pt'
|
||||||
|
copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence'
|
||||||
|
version = '2.0.0'
|
||||||
|
release = version
|
||||||
|
exclude_patterns = ['_build']
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
html_theme = 'default'
|
||||||
|
html_static_path = ['_static']
|
||||||
|
htmlhelp_basename = 'wallabagfrdoc'
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
}
|
||||||
|
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'wallabag-pt.tex', u'wallabag Documentation',
|
||||||
|
u'Nicolas Lœuillet', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
man_pages = [
|
||||||
|
('index', 'wallabagpt', u'wallabag Documentation',
|
||||||
|
[u'Nicolas Lœuillet'], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
texinfo_documents = [
|
||||||
|
('index', 'wallabag', u'wallabag Documentation',
|
||||||
|
u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
##### Guzzle sphinx theme
|
||||||
|
|
||||||
|
import guzzle_sphinx_theme
|
||||||
|
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
|
||||||
|
html_theme_path = guzzle_sphinx_theme.html_theme_path()
|
||||||
|
html_theme = 'guzzle_sphinx_theme'
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
html_sidebars = {
|
||||||
|
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
|
||||||
|
}
|
||||||
|
|
||||||
|
# Register the theme as an extension to generate a sitemap.xml
|
||||||
|
extensions.append("guzzle_sphinx_theme")
|
||||||
36
docs/pt-br/index.rst
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
wallabag documentation
|
||||||
|
======================
|
||||||
|
|
||||||
|
.. image:: ../img/wallabag.png
|
||||||
|
:alt: wallabag logo
|
||||||
|
:align: center
|
||||||
|
|
||||||
|
**wallabag** É uma aplicação "leia mais tarde": Ele salva páginas da web mantendo apenas o conteúdo. Elementos como ícones de navegação ou propaganda são deletedos.
|
||||||
|
|
||||||
|
A documentação principal desta aplicação é organizada em duas seções:
|
||||||
|
|
||||||
|
* :ref:`doc-usuario`
|
||||||
|
* :ref:`doc-desenvolvedor`
|
||||||
|
|
||||||
|
.. _user-docs:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Documentação do usuário
|
||||||
|
|
||||||
|
user/criar_conta
|
||||||
|
user/login
|
||||||
|
user/configuração
|
||||||
|
user/primeiro_artigo
|
||||||
|
user/importar
|
||||||
|
user/baixar_artigos
|
||||||
|
user/filtros
|
||||||
|
user/tags
|
||||||
|
|
||||||
|
.. _dev-docs:
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Documentação do desenvolvedor
|
||||||
|
|
||||||
|
developer/docker
|
||||||
2
docs/pt-br/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Sphinx>=1.3.0,<1.4.0
|
||||||
|
guzzle_sphinx_theme>=0.7.0,<0.8.0
|
||||||
262
inc/3rdparty/Encoding.php
vendored
@ -1,262 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @author "Sebastián Grignoli" <grignoli@framework2.com.ar>
|
|
||||||
* @package Encoding
|
|
||||||
* @version 1.1
|
|
||||||
* @link http://www.framework2.com.ar/dzone/forceUTF8-es/
|
|
||||||
* @example http://www.framework2.com.ar/dzone/forceUTF8-es/
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Encoding {
|
|
||||||
|
|
||||||
protected static $win1252ToUtf8 = array(
|
|
||||||
128 => "\xe2\x82\xac",
|
|
||||||
|
|
||||||
130 => "\xe2\x80\x9a",
|
|
||||||
131 => "\xc6\x92",
|
|
||||||
132 => "\xe2\x80\x9e",
|
|
||||||
133 => "\xe2\x80\xa6",
|
|
||||||
134 => "\xe2\x80\xa0",
|
|
||||||
135 => "\xe2\x80\xa1",
|
|
||||||
136 => "\xcb\x86",
|
|
||||||
137 => "\xe2\x80\xb0",
|
|
||||||
138 => "\xc5\xa0",
|
|
||||||
139 => "\xe2\x80\xb9",
|
|
||||||
140 => "\xc5\x92",
|
|
||||||
|
|
||||||
142 => "\xc5\xbd",
|
|
||||||
|
|
||||||
|
|
||||||
145 => "\xe2\x80\x98",
|
|
||||||
146 => "\xe2\x80\x99",
|
|
||||||
147 => "\xe2\x80\x9c",
|
|
||||||
148 => "\xe2\x80\x9d",
|
|
||||||
149 => "\xe2\x80\xa2",
|
|
||||||
150 => "\xe2\x80\x93",
|
|
||||||
151 => "\xe2\x80\x94",
|
|
||||||
152 => "\xcb\x9c",
|
|
||||||
153 => "\xe2\x84\xa2",
|
|
||||||
154 => "\xc5\xa1",
|
|
||||||
155 => "\xe2\x80\xba",
|
|
||||||
156 => "\xc5\x93",
|
|
||||||
|
|
||||||
158 => "\xc5\xbe",
|
|
||||||
159 => "\xc5\xb8"
|
|
||||||
);
|
|
||||||
|
|
||||||
protected static $brokenUtf8ToUtf8 = array(
|
|
||||||
"\xc2\x80" => "\xe2\x82\xac",
|
|
||||||
|
|
||||||
"\xc2\x82" => "\xe2\x80\x9a",
|
|
||||||
"\xc2\x83" => "\xc6\x92",
|
|
||||||
"\xc2\x84" => "\xe2\x80\x9e",
|
|
||||||
"\xc2\x85" => "\xe2\x80\xa6",
|
|
||||||
"\xc2\x86" => "\xe2\x80\xa0",
|
|
||||||
"\xc2\x87" => "\xe2\x80\xa1",
|
|
||||||
"\xc2\x88" => "\xcb\x86",
|
|
||||||
"\xc2\x89" => "\xe2\x80\xb0",
|
|
||||||
"\xc2\x8a" => "\xc5\xa0",
|
|
||||||
"\xc2\x8b" => "\xe2\x80\xb9",
|
|
||||||
"\xc2\x8c" => "\xc5\x92",
|
|
||||||
|
|
||||||
"\xc2\x8e" => "\xc5\xbd",
|
|
||||||
|
|
||||||
|
|
||||||
"\xc2\x91" => "\xe2\x80\x98",
|
|
||||||
"\xc2\x92" => "\xe2\x80\x99",
|
|
||||||
"\xc2\x93" => "\xe2\x80\x9c",
|
|
||||||
"\xc2\x94" => "\xe2\x80\x9d",
|
|
||||||
"\xc2\x95" => "\xe2\x80\xa2",
|
|
||||||
"\xc2\x96" => "\xe2\x80\x93",
|
|
||||||
"\xc2\x97" => "\xe2\x80\x94",
|
|
||||||
"\xc2\x98" => "\xcb\x9c",
|
|
||||||
"\xc2\x99" => "\xe2\x84\xa2",
|
|
||||||
"\xc2\x9a" => "\xc5\xa1",
|
|
||||||
"\xc2\x9b" => "\xe2\x80\xba",
|
|
||||||
"\xc2\x9c" => "\xc5\x93",
|
|
||||||
|
|
||||||
"\xc2\x9e" => "\xc5\xbe",
|
|
||||||
"\xc2\x9f" => "\xc5\xb8"
|
|
||||||
);
|
|
||||||
|
|
||||||
protected static $utf8ToWin1252 = array(
|
|
||||||
"\xe2\x82\xac" => "\x80",
|
|
||||||
|
|
||||||
"\xe2\x80\x9a" => "\x82",
|
|
||||||
"\xc6\x92" => "\x83",
|
|
||||||
"\xe2\x80\x9e" => "\x84",
|
|
||||||
"\xe2\x80\xa6" => "\x85",
|
|
||||||
"\xe2\x80\xa0" => "\x86",
|
|
||||||
"\xe2\x80\xa1" => "\x87",
|
|
||||||
"\xcb\x86" => "\x88",
|
|
||||||
"\xe2\x80\xb0" => "\x89",
|
|
||||||
"\xc5\xa0" => "\x8a",
|
|
||||||
"\xe2\x80\xb9" => "\x8b",
|
|
||||||
"\xc5\x92" => "\x8c",
|
|
||||||
|
|
||||||
"\xc5\xbd" => "\x8e",
|
|
||||||
|
|
||||||
|
|
||||||
"\xe2\x80\x98" => "\x91",
|
|
||||||
"\xe2\x80\x99" => "\x92",
|
|
||||||
"\xe2\x80\x9c" => "\x93",
|
|
||||||
"\xe2\x80\x9d" => "\x94",
|
|
||||||
"\xe2\x80\xa2" => "\x95",
|
|
||||||
"\xe2\x80\x93" => "\x96",
|
|
||||||
"\xe2\x80\x94" => "\x97",
|
|
||||||
"\xcb\x9c" => "\x98",
|
|
||||||
"\xe2\x84\xa2" => "\x99",
|
|
||||||
"\xc5\xa1" => "\x9a",
|
|
||||||
"\xe2\x80\xba" => "\x9b",
|
|
||||||
"\xc5\x93" => "\x9c",
|
|
||||||
|
|
||||||
"\xc5\xbe" => "\x9e",
|
|
||||||
"\xc5\xb8" => "\x9f"
|
|
||||||
);
|
|
||||||
|
|
||||||
static function toUTF8($text){
|
|
||||||
/**
|
|
||||||
* Function Encoding::toUTF8
|
|
||||||
*
|
|
||||||
* This function leaves UTF8 characters alone, while converting almost all non-UTF8 to UTF8.
|
|
||||||
*
|
|
||||||
* It assumes that the encoding of the original string is either Windows-1252 or ISO 8859-1.
|
|
||||||
*
|
|
||||||
* It may fail to convert characters to UTF-8 if they fall into one of these scenarios:
|
|
||||||
*
|
|
||||||
* 1) when any of these characters: ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞß
|
|
||||||
* are followed by any of these: ("group B")
|
|
||||||
* ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶•¸¹º»¼½¾¿
|
|
||||||
* For example: %ABREPRESENT%C9%BB. «REPRESENTÉ»
|
|
||||||
* The "«" (%AB) character will be converted, but the "É" followed by "»" (%C9%BB)
|
|
||||||
* is also a valid unicode character, and will be left unchanged.
|
|
||||||
*
|
|
||||||
* 2) when any of these: àáâãäåæçèéêëìíîï are followed by TWO chars from group B,
|
|
||||||
* 3) when any of these: ðñòó are followed by THREE chars from group B.
|
|
||||||
*
|
|
||||||
* @name toUTF8
|
|
||||||
* @param string $text Any string.
|
|
||||||
* @return string The same string, UTF8 encoded
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(is_array($text))
|
|
||||||
{
|
|
||||||
foreach($text as $k => $v)
|
|
||||||
{
|
|
||||||
$text[$k] = self::toUTF8($v);
|
|
||||||
}
|
|
||||||
return $text;
|
|
||||||
} elseif(is_string($text)) {
|
|
||||||
|
|
||||||
$max = strlen($text);
|
|
||||||
$buf = "";
|
|
||||||
for($i = 0; $i < $max; $i++){
|
|
||||||
$c1 = $text{$i};
|
|
||||||
if($c1>="\xc0"){ //Should be converted to UTF8, if it's not UTF8 already
|
|
||||||
$c2 = $i+1 >= $max? "\x00" : $text{$i+1};
|
|
||||||
$c3 = $i+2 >= $max? "\x00" : $text{$i+2};
|
|
||||||
$c4 = $i+3 >= $max? "\x00" : $text{$i+3};
|
|
||||||
if($c1 >= "\xc0" & $c1 <= "\xdf"){ //looks like 2 bytes UTF8
|
|
||||||
if($c2 >= "\x80" && $c2 <= "\xbf"){ //yeah, almost sure it's UTF8 already
|
|
||||||
$buf .= $c1 . $c2;
|
|
||||||
$i++;
|
|
||||||
} else { //not valid UTF8. Convert it.
|
|
||||||
$cc1 = (chr(ord($c1) / 64) | "\xc0");
|
|
||||||
$cc2 = ($c1 & "\x3f") | "\x80";
|
|
||||||
$buf .= $cc1 . $cc2;
|
|
||||||
}
|
|
||||||
} elseif($c1 >= "\xe0" & $c1 <= "\xef"){ //looks like 3 bytes UTF8
|
|
||||||
if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf"){ //yeah, almost sure it's UTF8 already
|
|
||||||
$buf .= $c1 . $c2 . $c3;
|
|
||||||
$i = $i + 2;
|
|
||||||
} else { //not valid UTF8. Convert it.
|
|
||||||
$cc1 = (chr(ord($c1) / 64) | "\xc0");
|
|
||||||
$cc2 = ($c1 & "\x3f") | "\x80";
|
|
||||||
$buf .= $cc1 . $cc2;
|
|
||||||
}
|
|
||||||
} elseif($c1 >= "\xf0" & $c1 <= "\xf7"){ //looks like 4 bytes UTF8
|
|
||||||
if($c2 >= "\x80" && $c2 <= "\xbf" && $c3 >= "\x80" && $c3 <= "\xbf" && $c4 >= "\x80" && $c4 <= "\xbf"){ //yeah, almost sure it's UTF8 already
|
|
||||||
$buf .= $c1 . $c2 . $c3;
|
|
||||||
$i = $i + 2;
|
|
||||||
} else { //not valid UTF8. Convert it.
|
|
||||||
$cc1 = (chr(ord($c1) / 64) | "\xc0");
|
|
||||||
$cc2 = ($c1 & "\x3f") | "\x80";
|
|
||||||
$buf .= $cc1 . $cc2;
|
|
||||||
}
|
|
||||||
} else { //doesn't look like UTF8, but should be converted
|
|
||||||
$cc1 = (chr(ord($c1) / 64) | "\xc0");
|
|
||||||
$cc2 = (($c1 & "\x3f") | "\x80");
|
|
||||||
$buf .= $cc1 . $cc2;
|
|
||||||
}
|
|
||||||
} elseif(($c1 & "\xc0") == "\x80"){ // needs conversion
|
|
||||||
if(isset(self::$win1252ToUtf8[ord($c1)])) { //found in Windows-1252 special cases
|
|
||||||
$buf .= self::$win1252ToUtf8[ord($c1)];
|
|
||||||
} else {
|
|
||||||
$cc1 = (chr(ord($c1) / 64) | "\xc0");
|
|
||||||
$cc2 = (($c1 & "\x3f") | "\x80");
|
|
||||||
$buf .= $cc1 . $cc2;
|
|
||||||
}
|
|
||||||
} else { // it doesn't need convesion
|
|
||||||
$buf .= $c1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $buf;
|
|
||||||
} else {
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static function toWin1252($text) {
|
|
||||||
if(is_array($text)) {
|
|
||||||
foreach($text as $k => $v) {
|
|
||||||
$text[$k] = self::toWin1252($v);
|
|
||||||
}
|
|
||||||
return $text;
|
|
||||||
} elseif(is_string($text)) {
|
|
||||||
return utf8_decode(str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), self::toUTF8($text)));
|
|
||||||
} else {
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static function toISO8859($text) {
|
|
||||||
return self::toWin1252($text);
|
|
||||||
}
|
|
||||||
|
|
||||||
static function toLatin1($text) {
|
|
||||||
return self::toWin1252($text);
|
|
||||||
}
|
|
||||||
|
|
||||||
static function fixUTF8($text){
|
|
||||||
if(is_array($text)) {
|
|
||||||
foreach($text as $k => $v) {
|
|
||||||
$text[$k] = self::fixUTF8($v);
|
|
||||||
}
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
$last = "";
|
|
||||||
while($last <> $text){
|
|
||||||
$last = $text;
|
|
||||||
$text = self::toUTF8(utf8_decode(str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), $text)));
|
|
||||||
}
|
|
||||||
$text = self::toUTF8(utf8_decode(str_replace(array_keys(self::$utf8ToWin1252), array_values(self::$utf8ToWin1252), $text)));
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
static function UTF8FixWin1252Chars($text){
|
|
||||||
// If you received an UTF-8 string that was converted from Windows-1252 as it was ISO8859-1
|
|
||||||
// (ignoring Windows-1252 chars from 80 to 9F) use this function to fix it.
|
|
||||||
// See: http://en.wikipedia.org/wiki/Windows-1252
|
|
||||||
|
|
||||||
return str_replace(array_keys(self::$brokenUtf8ToUtf8), array_values(self::$brokenUtf8ToUtf8), $text);
|
|
||||||
}
|
|
||||||
|
|
||||||
static function removeBOM($str=""){
|
|
||||||
if(substr($str, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
|
|
||||||
$str=substr($str, 3);
|
|
||||||
}
|
|
||||||
return $str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
109
inc/3rdparty/JSLikeHTMLElement.php
vendored
@ -1,109 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* JavaScript-like HTML DOM Element
|
|
||||||
*
|
|
||||||
* This class extends PHP's DOMElement to allow
|
|
||||||
* users to get and set the innerHTML property of
|
|
||||||
* HTML elements in the same way it's done in
|
|
||||||
* JavaScript.
|
|
||||||
*
|
|
||||||
* Example usage:
|
|
||||||
* @code
|
|
||||||
* require_once 'JSLikeHTMLElement.php';
|
|
||||||
* header('Content-Type: text/plain');
|
|
||||||
* $doc = new DOMDocument();
|
|
||||||
* $doc->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
|
|
||||||
* $doc->loadHTML('<div><p>Para 1</p><p>Para 2</p></div>');
|
|
||||||
* $elem = $doc->getElementsByTagName('div')->item(0);
|
|
||||||
*
|
|
||||||
* // print innerHTML
|
|
||||||
* echo $elem->innerHTML; // prints '<p>Para 1</p><p>Para 2</p>'
|
|
||||||
* echo "\n\n";
|
|
||||||
*
|
|
||||||
* // set innerHTML
|
|
||||||
* $elem->innerHTML = '<a href="http://fivefilters.org">FiveFilters.org</a>';
|
|
||||||
* echo $elem->innerHTML; // prints '<a href="http://fivefilters.org">FiveFilters.org</a>'
|
|
||||||
* echo "\n\n";
|
|
||||||
*
|
|
||||||
* // print document (with our changes)
|
|
||||||
* echo $doc->saveXML();
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* @author Keyvan Minoukadeh - http://www.keyvan.net - keyvan@keyvan.net
|
|
||||||
* @see http://fivefilters.org (the project this was written for)
|
|
||||||
*/
|
|
||||||
class JSLikeHTMLElement extends DOMElement
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Used for setting innerHTML like it's done in JavaScript:
|
|
||||||
* @code
|
|
||||||
* $div->innerHTML = '<h2>Chapter 2</h2><p>The story begins...</p>';
|
|
||||||
* @endcode
|
|
||||||
*/
|
|
||||||
public function __set($name, $value) {
|
|
||||||
if ($name == 'innerHTML') {
|
|
||||||
// first, empty the element
|
|
||||||
for ($x=$this->childNodes->length-1; $x>=0; $x--) {
|
|
||||||
$this->removeChild($this->childNodes->item($x));
|
|
||||||
}
|
|
||||||
// $value holds our new inner HTML
|
|
||||||
if ($value != '') {
|
|
||||||
$f = $this->ownerDocument->createDocumentFragment();
|
|
||||||
// appendXML() expects well-formed markup (XHTML)
|
|
||||||
$result = @$f->appendXML($value); // @ to suppress PHP warnings
|
|
||||||
if ($result) {
|
|
||||||
if ($f->hasChildNodes()) $this->appendChild($f);
|
|
||||||
} else {
|
|
||||||
// $value is probably ill-formed
|
|
||||||
$f = new DOMDocument();
|
|
||||||
$value = mb_convert_encoding($value, 'HTML-ENTITIES', 'UTF-8');
|
|
||||||
// Using <htmlfragment> will generate a warning, but so will bad HTML
|
|
||||||
// (and by this point, bad HTML is what we've got).
|
|
||||||
// We use it (and suppress the warning) because an HTML fragment will
|
|
||||||
// be wrapped around <html><body> tags which we don't really want to keep.
|
|
||||||
// Note: despite the warning, if loadHTML succeeds it will return true.
|
|
||||||
$result = @$f->loadHTML('<htmlfragment>'.$value.'</htmlfragment>');
|
|
||||||
if ($result) {
|
|
||||||
$import = $f->getElementsByTagName('htmlfragment')->item(0);
|
|
||||||
foreach ($import->childNodes as $child) {
|
|
||||||
$importedNode = $this->ownerDocument->importNode($child, true);
|
|
||||||
$this->appendChild($importedNode);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// oh well, we tried, we really did. :(
|
|
||||||
// this element is now empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$trace = debug_backtrace();
|
|
||||||
trigger_error('Undefined property via __set(): '.$name.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for getting innerHTML like it's done in JavaScript:
|
|
||||||
* @code
|
|
||||||
* $string = $div->innerHTML;
|
|
||||||
* @endcode
|
|
||||||
*/
|
|
||||||
public function __get($name)
|
|
||||||
{
|
|
||||||
if ($name == 'innerHTML') {
|
|
||||||
$inner = '';
|
|
||||||
foreach ($this->childNodes as $child) {
|
|
||||||
$inner .= $this->ownerDocument->saveXML($child);
|
|
||||||
}
|
|
||||||
return $inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
$trace = debug_backtrace();
|
|
||||||
trigger_error('Undefined property via __get(): '.$name.' in '.$trace[0]['file'].' on line '.$trace[0]['line'], E_USER_NOTICE);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return '['.$this->tagName.']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1138
inc/3rdparty/Readability.php
vendored
136
inc/3rdparty/Session.class.php
vendored
@ -1,136 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Session management class
|
|
||||||
* http://www.developpez.net/forums/d51943/php/langage/sessions/
|
|
||||||
* http://sebsauvage.net/wiki/doku.php?id=php:session
|
|
||||||
* http://sebsauvage.net/wiki/doku.php?id=php:shaarli
|
|
||||||
*
|
|
||||||
* Features:
|
|
||||||
* - Everything is stored on server-side (we do not trust client-side data,
|
|
||||||
* such as cookie expiration)
|
|
||||||
* - IP addresses + user agent are checked on each access to prevent session
|
|
||||||
* cookie hijacking (such as Firesheep)
|
|
||||||
* - Session expires on user inactivity (Session expiration date is
|
|
||||||
* automatically updated everytime the user accesses a page.)
|
|
||||||
* - A unique secret key is generated on server-side for this session
|
|
||||||
* (and never sent over the wire) which can be used
|
|
||||||
* to sign forms (HMAC) (See $_SESSION['uid'] )
|
|
||||||
* - Token management to prevent XSRF attacks.
|
|
||||||
*
|
|
||||||
* TODO:
|
|
||||||
* - log login fail
|
|
||||||
* - prevent brute force (ban IP)
|
|
||||||
*
|
|
||||||
* HOWTOUSE:
|
|
||||||
* - Just call Session::init(); to initialize session and
|
|
||||||
* check if connected with Session::isLogged()
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Session
|
|
||||||
{
|
|
||||||
// If the user does not access any page within this time,
|
|
||||||
// his/her session is considered expired (in seconds).
|
|
||||||
public static $inactivity_timeout = 3600;
|
|
||||||
private static $_instance;
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
private function __construct()
|
|
||||||
{
|
|
||||||
// Use cookies to store session.
|
|
||||||
ini_set('session.use_cookies', 1);
|
|
||||||
// Force cookies for session (phpsessionID forbidden in URL)
|
|
||||||
ini_set('session.use_only_cookies', 1);
|
|
||||||
if (!session_id()){
|
|
||||||
// Prevent php to use sessionID in URL if cookies are disabled.
|
|
||||||
ini_set('session.use_trans_sid', false);
|
|
||||||
session_start('poche');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize session
|
|
||||||
public static function init()
|
|
||||||
{
|
|
||||||
if (!isset(self::$_instance)) {
|
|
||||||
self::$_instance = new Session();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the IP address, user agent and language of the client
|
|
||||||
// (Used to prevent session cookie hijacking.)
|
|
||||||
private static function _allInfos()
|
|
||||||
{
|
|
||||||
$infos = $_SERVER["REMOTE_ADDR"];
|
|
||||||
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
||||||
$infos.=$_SERVER['HTTP_X_FORWARDED_FOR'];
|
|
||||||
}
|
|
||||||
if (isset($_SERVER['HTTP_CLIENT_IP'])) {
|
|
||||||
$infos.='_'.$_SERVER['HTTP_CLIENT_IP'];
|
|
||||||
}
|
|
||||||
$infos.='_'.$_SERVER['HTTP_USER_AGENT'];
|
|
||||||
$infos.='_'.$_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
|
||||||
return sha1($infos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that user/password is correct and init some SESSION variables.
|
|
||||||
public static function login($login,$password,$login_test,$password_test,
|
|
||||||
$pValues = array())
|
|
||||||
{
|
|
||||||
foreach ($pValues as $key => $value) {
|
|
||||||
$_SESSION[$key] = $value;
|
|
||||||
}
|
|
||||||
if ($login==$login_test && $password==$password_test){
|
|
||||||
// generate unique random number to sign forms (HMAC)
|
|
||||||
$_SESSION['uid'] = sha1(uniqid('',true).'_'.mt_rand());
|
|
||||||
$_SESSION['info']=Session::_allInfos();
|
|
||||||
$_SESSION['username']=$login;
|
|
||||||
// Set session expiration.
|
|
||||||
$_SESSION['expires_on']=time()+Session::$inactivity_timeout;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force logout
|
|
||||||
public static function logout()
|
|
||||||
{
|
|
||||||
unset($_SESSION['uid'],$_SESSION['info'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure user is logged in.
|
|
||||||
public static function isLogged()
|
|
||||||
{
|
|
||||||
if (!isset ($_SESSION['uid'])
|
|
||||||
|| $_SESSION['info']!=Session::_allInfos()
|
|
||||||
|| time()>=$_SESSION['expires_on']){
|
|
||||||
Session::logout();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// User accessed a page : Update his/her session expiration date.
|
|
||||||
$_SESSION['expires_on']=time()+Session::$inactivity_timeout;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a token.
|
|
||||||
public static function getToken()
|
|
||||||
{
|
|
||||||
if (!isset($_SESSION['tokens'])){
|
|
||||||
$_SESSION['tokens']=array();
|
|
||||||
}
|
|
||||||
// We generate a random string and store it on the server side.
|
|
||||||
$rnd = sha1(uniqid('',true).'_'.mt_rand());
|
|
||||||
$_SESSION['tokens'][$rnd]=1;
|
|
||||||
return $rnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tells if a token is ok. Using this function will destroy the token.
|
|
||||||
// return true if token is ok.
|
|
||||||
public static function isToken($token)
|
|
||||||
{
|
|
||||||
if (isset($_SESSION['tokens'][$token]))
|
|
||||||
{
|
|
||||||
unset($_SESSION['tokens'][$token]); // Token is used: destroy it.
|
|
||||||
return true; // Token is ok.
|
|
||||||
}
|
|
||||||
return false; // Wrong token, or already used.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
231
inc/3rdparty/class.messages.php
vendored
@ -1,231 +0,0 @@
|
|||||||
<?php
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// Session-Based Flash Messages v1.0
|
|
||||||
// Copyright 2012 Mike Everhart (http://mikeeverhart.net)
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Description:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// Stores messages in Session data to be easily retrieved later on.
|
|
||||||
// This class includes four different types of messages:
|
|
||||||
// - Success
|
|
||||||
// - Error
|
|
||||||
// - Warning
|
|
||||||
// - Information
|
|
||||||
//
|
|
||||||
// See README for basic usage instructions, or see samples/index.php for more advanced samples
|
|
||||||
//
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
// Changelog
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// 2011-05-15 - v1.0 - Initial Version
|
|
||||||
//
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class Messages {
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------------------
|
|
||||||
// Class Variables
|
|
||||||
//-----------------------------------------------------------------------------------------------
|
|
||||||
var $msgId;
|
|
||||||
var $msgTypes = array( 'help', 'info', 'warning', 'success', 'error' );
|
|
||||||
var $msgClass = 'messages';
|
|
||||||
var $msgWrapper = "<div class='%s %s'><a href='#' class='closeMessage'>X</a>\n%s</div>\n";
|
|
||||||
var $msgBefore = '<p>';
|
|
||||||
var $msgAfter = "</p>\n";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @author Mike Everhart
|
|
||||||
*/
|
|
||||||
public function __construct() {
|
|
||||||
|
|
||||||
// Generate a unique ID for this user and session
|
|
||||||
$this->msgId = md5(uniqid());
|
|
||||||
|
|
||||||
// Create the session array if it doesnt already exist
|
|
||||||
if( !array_key_exists('flash_messages', $_SESSION) ) $_SESSION['flash_messages'] = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a message to the queue
|
|
||||||
*
|
|
||||||
* @author Mike Everhart
|
|
||||||
*
|
|
||||||
* @param string $type The type of message to add
|
|
||||||
* @param string $message The message
|
|
||||||
* @param string $redirect_to (optional) If set, the user will be redirected to this URL
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function add($type, $message, $redirect_to=null) {
|
|
||||||
|
|
||||||
if( !isset($_SESSION['flash_messages']) ) return false;
|
|
||||||
|
|
||||||
if( !isset($type) || !isset($message[0]) ) return false;
|
|
||||||
|
|
||||||
// Replace any shorthand codes with their full version
|
|
||||||
if( strlen(trim($type)) == 1 ) {
|
|
||||||
$type = str_replace( array('h', 'i', 'w', 'e', 's'), array('help', 'info', 'warning', 'error', 'success'), $type );
|
|
||||||
|
|
||||||
// Backwards compatibility...
|
|
||||||
} elseif( $type == 'information' ) {
|
|
||||||
$type = 'info';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it's a valid message type
|
|
||||||
if( !in_array($type, $this->msgTypes) ) die('"' . strip_tags($type) . '" is not a valid message type!' );
|
|
||||||
|
|
||||||
// If the session array doesn't exist, create it
|
|
||||||
if( !array_key_exists( $type, $_SESSION['flash_messages'] ) ) $_SESSION['flash_messages'][$type] = array();
|
|
||||||
|
|
||||||
$_SESSION['flash_messages'][$type][] = $message;
|
|
||||||
|
|
||||||
if( !is_null($redirect_to) ) {
|
|
||||||
header("Location: $redirect_to");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------------------
|
|
||||||
// display()
|
|
||||||
// print queued messages to the screen
|
|
||||||
//-----------------------------------------------------------------------------------------------
|
|
||||||
/**
|
|
||||||
* Display the queued messages
|
|
||||||
*
|
|
||||||
* @author Mike Everhart
|
|
||||||
*
|
|
||||||
* @param string $type Which messages to display
|
|
||||||
* @param bool $print True = print the messages on the screen
|
|
||||||
* @return mixed
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function display($type='all', $print=true) {
|
|
||||||
$messages = '';
|
|
||||||
$data = '';
|
|
||||||
|
|
||||||
if( !isset($_SESSION['flash_messages']) ) return false;
|
|
||||||
|
|
||||||
if( $type == 'g' || $type == 'growl' ) {
|
|
||||||
$this->displayGrowlMessages();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print a certain type of message?
|
|
||||||
if( in_array($type, $this->msgTypes) ) {
|
|
||||||
foreach( $_SESSION['flash_messages'][$type] as $msg ) {
|
|
||||||
$messages .= $this->msgBefore . $msg . $this->msgAfter;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
|
|
||||||
|
|
||||||
// Clear the viewed messages
|
|
||||||
$this->clear($type);
|
|
||||||
|
|
||||||
// Print ALL queued messages
|
|
||||||
} elseif( $type == 'all' ) {
|
|
||||||
foreach( $_SESSION['flash_messages'] as $type => $msgArray ) {
|
|
||||||
$messages = '';
|
|
||||||
foreach( $msgArray as $msg ) {
|
|
||||||
$messages .= $this->msgBefore . $msg . $this->msgAfter;
|
|
||||||
}
|
|
||||||
$data .= sprintf($this->msgWrapper, $this->msgClass, $type, $messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear ALL of the messages
|
|
||||||
$this->clear();
|
|
||||||
|
|
||||||
// Invalid Message Type?
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print everything to the screen or return the data
|
|
||||||
if( $print ) {
|
|
||||||
echo $data;
|
|
||||||
} else {
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if there are any queued error messages
|
|
||||||
*
|
|
||||||
* @author Mike Everhart
|
|
||||||
*
|
|
||||||
* @return bool true = There ARE error messages
|
|
||||||
* false = There are NOT any error messages
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function hasErrors() {
|
|
||||||
return empty($_SESSION['flash_messages']['error']) ? false : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if there are any ($type) messages queued
|
|
||||||
*
|
|
||||||
* @author Mike Everhart
|
|
||||||
*
|
|
||||||
* @param string $type The type of messages to check for
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function hasMessages($type=null) {
|
|
||||||
if( !is_null($type) ) {
|
|
||||||
if( !empty($_SESSION['flash_messages'][$type]) ) return $_SESSION['flash_messages'][$type];
|
|
||||||
} else {
|
|
||||||
foreach( $this->msgTypes as $type ) {
|
|
||||||
if( !empty($_SESSION['flash_messages']) ) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear messages from the session data
|
|
||||||
*
|
|
||||||
* @author Mike Everhart
|
|
||||||
*
|
|
||||||
* @param string $type The type of messages to clear
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public function clear($type='all') {
|
|
||||||
if( $type == 'all' ) {
|
|
||||||
unset($_SESSION['flash_messages']);
|
|
||||||
} else {
|
|
||||||
unset($_SESSION['flash_messages'][$type]);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() { return $this->hasMessages(); }
|
|
||||||
|
|
||||||
public function __destruct() {
|
|
||||||
//$this->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // end class
|
|
||||||
?>
|
|
||||||
612
inc/3rdparty/content-extractor/ContentExtractor.php
vendored
@ -1,612 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Content Extractor
|
|
||||||
*
|
|
||||||
* Uses patterns specified in site config files and auto detection (hNews/PHP Readability)
|
|
||||||
* to extract content from HTML files.
|
|
||||||
*
|
|
||||||
* @version 0.8
|
|
||||||
* @date 2012-02-21
|
|
||||||
* @author Keyvan Minoukadeh
|
|
||||||
* @copyright 2011 Keyvan Minoukadeh
|
|
||||||
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ContentExtractor
|
|
||||||
{
|
|
||||||
protected static $tidy_config = array(
|
|
||||||
'clean' => true,
|
|
||||||
'output-xhtml' => true,
|
|
||||||
'logical-emphasis' => true,
|
|
||||||
'show-body-only' => false,
|
|
||||||
'new-blocklevel-tags' => 'article, aside, footer, header, hgroup, menu, nav, section, details, datagrid',
|
|
||||||
'new-inline-tags' => 'mark, time, meter, progress, data',
|
|
||||||
'wrap' => 0,
|
|
||||||
'drop-empty-paras' => true,
|
|
||||||
'drop-proprietary-attributes' => false,
|
|
||||||
'enclose-text' => true,
|
|
||||||
'enclose-block-text' => true,
|
|
||||||
'merge-divs' => true,
|
|
||||||
'merge-spans' => true,
|
|
||||||
'char-encoding' => 'utf8',
|
|
||||||
'hide-comments' => true
|
|
||||||
);
|
|
||||||
protected $html;
|
|
||||||
protected $config;
|
|
||||||
protected $title;
|
|
||||||
protected $author = array();
|
|
||||||
protected $language;
|
|
||||||
protected $date;
|
|
||||||
protected $body;
|
|
||||||
protected $success = false;
|
|
||||||
public $fingerprints = array();
|
|
||||||
public $readability;
|
|
||||||
public $debug = false;
|
|
||||||
|
|
||||||
function __construct($path, $fallback=null) {
|
|
||||||
SiteConfig::set_config_path($path, $fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function debug($msg) {
|
|
||||||
if ($this->debug) {
|
|
||||||
$mem = round(memory_get_usage()/1024, 2);
|
|
||||||
$memPeak = round(memory_get_peak_usage()/1024, 2);
|
|
||||||
echo '* ',$msg;
|
|
||||||
echo ' - mem used: ',$mem," (peak: $memPeak)\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reset() {
|
|
||||||
$this->html = null;
|
|
||||||
$this->readability = null;
|
|
||||||
$this->config = null;
|
|
||||||
$this->title = null;
|
|
||||||
$this->body = null;
|
|
||||||
$this->author = array();
|
|
||||||
$this->language = null;
|
|
||||||
$this->date = null;
|
|
||||||
$this->success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function findHostUsingFingerprints($html) {
|
|
||||||
$this->debug('Checking fingerprints...');
|
|
||||||
$head = substr($html, 0, 8000);
|
|
||||||
foreach ($this->fingerprints as $_fp => $_fphost) {
|
|
||||||
$lookin = 'html';
|
|
||||||
if (is_array($_fphost)) {
|
|
||||||
if (isset($_fphost['head']) && $_fphost['head']) {
|
|
||||||
$lookin = 'head';
|
|
||||||
}
|
|
||||||
$_fphost = $_fphost['hostname'];
|
|
||||||
}
|
|
||||||
if (strpos($$lookin, $_fp) !== false) {
|
|
||||||
$this->debug("Found match: $_fphost");
|
|
||||||
return $_fphost;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns true on success, false on failure
|
|
||||||
// $smart_tidy indicates that if tidy is used and no results are produced, we will
|
|
||||||
// try again without it. Tidy helps us deal with PHP's patchy HTML parsing most of the time
|
|
||||||
// but it has problems of its own which we try to avoid with this option.
|
|
||||||
public function process($html, $url, $smart_tidy=true) {
|
|
||||||
$this->reset();
|
|
||||||
// extract host name
|
|
||||||
$host = @parse_url($url, PHP_URL_HOST);
|
|
||||||
if (!($this->config = SiteConfig::build($host))) {
|
|
||||||
// no match, check HTML for fingerprints
|
|
||||||
if (!empty($this->fingerprints) && ($_fphost = $this->findHostUsingFingerprints($html))) {
|
|
||||||
$this->config = SiteConfig::build($_fphost);
|
|
||||||
}
|
|
||||||
unset($_fphost);
|
|
||||||
if (!$this->config) {
|
|
||||||
// no match, so use defaults
|
|
||||||
$this->config = new SiteConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// store copy of config in our static cache array in case we need to process another URL
|
|
||||||
SiteConfig::add_to_cache($host, $this->config);
|
|
||||||
|
|
||||||
// do string replacements
|
|
||||||
foreach ($this->config->replace_string as $_repl) {
|
|
||||||
$html = str_replace($_repl[0], $_repl[1], $html);
|
|
||||||
}
|
|
||||||
unset($_repl);
|
|
||||||
|
|
||||||
// use tidy (if it exists)?
|
|
||||||
// This fixes problems with some sites which would otherwise
|
|
||||||
// trouble DOMDocument's HTML parsing. (Although sometimes it
|
|
||||||
// makes matters worse, which is why you can override it in site config files.)
|
|
||||||
$tidied = false;
|
|
||||||
if ($this->config->tidy && function_exists('tidy_parse_string') && $smart_tidy) {
|
|
||||||
$this->debug('Using Tidy');
|
|
||||||
$tidy = tidy_parse_string($html, self::$tidy_config, 'UTF8');
|
|
||||||
if (tidy_clean_repair($tidy)) {
|
|
||||||
$original_html = $html;
|
|
||||||
$tidied = true;
|
|
||||||
// $html = $tidy->value;
|
|
||||||
}
|
|
||||||
$body = $tidy->body();
|
|
||||||
if (preg_replace('/\s+/', '', $body->value) !== "<body></body>") {
|
|
||||||
$html = $tidy->value;
|
|
||||||
}
|
|
||||||
unset($tidy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// load and parse html
|
|
||||||
$this->readability = new Readability($html, $url);
|
|
||||||
|
|
||||||
// we use xpath to find elements in the given HTML document
|
|
||||||
// see http://en.wikipedia.org/wiki/XPath_1.0
|
|
||||||
$xpath = new DOMXPath($this->readability->dom);
|
|
||||||
|
|
||||||
// try to get title
|
|
||||||
foreach ($this->config->title as $pattern) {
|
|
||||||
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
|
|
||||||
if (is_string($elems)) {
|
|
||||||
$this->debug('Title expression evaluated as string');
|
|
||||||
$this->title = trim($elems);
|
|
||||||
break;
|
|
||||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
|
||||||
$this->debug('Title matched');
|
|
||||||
$this->title = $elems->item(0)->textContent;
|
|
||||||
// remove title from document
|
|
||||||
try {
|
|
||||||
$elems->item(0)->parentNode->removeChild($elems->item(0));
|
|
||||||
} catch (DOMException $e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to get author (if it hasn't already been set)
|
|
||||||
if (empty($this->author)) {
|
|
||||||
foreach ($this->config->author as $pattern) {
|
|
||||||
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
|
|
||||||
if (is_string($elems)) {
|
|
||||||
$this->debug('Author expression evaluated as string');
|
|
||||||
if (trim($elems) != '') {
|
|
||||||
$this->author[] = trim($elems);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
|
||||||
foreach ($elems as $elem) {
|
|
||||||
if (!isset($elem->parentNode)) continue;
|
|
||||||
$this->author[] = trim($elem->textContent);
|
|
||||||
}
|
|
||||||
if (!empty($this->author)) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to get language
|
|
||||||
$_lang_xpath = array('//html[@lang]/@lang', '//meta[@name="DC.language"]/@content');
|
|
||||||
foreach ($_lang_xpath as $pattern) {
|
|
||||||
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
|
|
||||||
if (is_string($elems)) {
|
|
||||||
if (trim($elems) != '') {
|
|
||||||
$this->language = trim($elems);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
|
||||||
foreach ($elems as $elem) {
|
|
||||||
if (!isset($elem->parentNode)) continue;
|
|
||||||
$this->language = trim($elem->textContent);
|
|
||||||
}
|
|
||||||
if ($this->language) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to get date
|
|
||||||
foreach ($this->config->date as $pattern) {
|
|
||||||
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
|
|
||||||
if (is_string($elems)) {
|
|
||||||
$this->debug('Date expression evaluated as string');
|
|
||||||
$this->date = strtotime(trim($elems, "; \t\n\r\0\x0B"));
|
|
||||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
|
||||||
$this->debug('Date matched');
|
|
||||||
$this->date = $elems->item(0)->textContent;
|
|
||||||
$this->date = strtotime(trim($this->date, "; \t\n\r\0\x0B"));
|
|
||||||
// remove date from document
|
|
||||||
// $elems->item(0)->parentNode->removeChild($elems->item(0));
|
|
||||||
}
|
|
||||||
if (!$this->date) {
|
|
||||||
$this->date = null;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip elements (using xpath expressions)
|
|
||||||
foreach ($this->config->strip as $pattern) {
|
|
||||||
$elems = @$xpath->query($pattern, $this->readability->dom);
|
|
||||||
// check for matches
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('Stripping '.$elems->length.' elements (strip)');
|
|
||||||
for ($i=$elems->length-1; $i >= 0; $i--) {
|
|
||||||
$elems->item($i)->parentNode->removeChild($elems->item($i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip elements (using id and class attribute values)
|
|
||||||
foreach ($this->config->strip_id_or_class as $string) {
|
|
||||||
$string = strtr($string, array("'"=>'', '"'=>''));
|
|
||||||
$elems = @$xpath->query("//*[contains(@class, '$string') or contains(@id, '$string')]", $this->readability->dom);
|
|
||||||
// check for matches
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('Stripping '.$elems->length.' elements (strip_id_or_class)');
|
|
||||||
for ($i=$elems->length-1; $i >= 0; $i--) {
|
|
||||||
$elems->item($i)->parentNode->removeChild($elems->item($i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip images (using src attribute values)
|
|
||||||
foreach ($this->config->strip_image_src as $string) {
|
|
||||||
$string = strtr($string, array("'"=>'', '"'=>''));
|
|
||||||
$elems = @$xpath->query("//img[contains(@src, '$string')]", $this->readability->dom);
|
|
||||||
// check for matches
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('Stripping '.$elems->length.' image elements');
|
|
||||||
for ($i=$elems->length-1; $i >= 0; $i--) {
|
|
||||||
$elems->item($i)->parentNode->removeChild($elems->item($i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// strip elements using Readability.com and Instapaper.com ignore class names
|
|
||||||
// .entry-unrelated and .instapaper_ignore
|
|
||||||
// See https://www.readability.com/publishers/guidelines/#view-plainGuidelines
|
|
||||||
// and http://blog.instapaper.com/post/730281947
|
|
||||||
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' entry-unrelated ') or contains(concat(' ',normalize-space(@class),' '),' instapaper_ignore ')]", $this->readability->dom);
|
|
||||||
// check for matches
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('Stripping '.$elems->length.' .entry-unrelated,.instapaper_ignore elements');
|
|
||||||
for ($i=$elems->length-1; $i >= 0; $i--) {
|
|
||||||
$elems->item($i)->parentNode->removeChild($elems->item($i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip elements that contain style="display: none;"
|
|
||||||
$elems = @$xpath->query("//*[contains(@style,'display:none')]", $this->readability->dom);
|
|
||||||
// check for matches
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('Stripping '.$elems->length.' elements with inline display:none style');
|
|
||||||
for ($i=$elems->length-1; $i >= 0; $i--) {
|
|
||||||
$elems->item($i)->parentNode->removeChild($elems->item($i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to get body
|
|
||||||
foreach ($this->config->body as $pattern) {
|
|
||||||
$elems = @$xpath->query($pattern, $this->readability->dom);
|
|
||||||
// check for matches
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('Body matched');
|
|
||||||
if ($elems->length == 1) {
|
|
||||||
$this->body = $elems->item(0);
|
|
||||||
// prune (clean up elements that may not be content)
|
|
||||||
if ($this->config->prune) {
|
|
||||||
$this->debug('Pruning content');
|
|
||||||
$this->readability->prepArticle($this->body);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
$this->body = $this->readability->dom->createElement('div');
|
|
||||||
$this->debug($elems->length.' body elems found');
|
|
||||||
foreach ($elems as $elem) {
|
|
||||||
if (!isset($elem->parentNode)) continue;
|
|
||||||
$isDescendant = false;
|
|
||||||
foreach ($this->body->childNodes as $parent) {
|
|
||||||
if ($this->isDescendant($parent, $elem)) {
|
|
||||||
$isDescendant = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($isDescendant) {
|
|
||||||
$this->debug('Element is child of another body element, skipping.');
|
|
||||||
} else {
|
|
||||||
// prune (clean up elements that may not be content)
|
|
||||||
if ($this->config->prune) {
|
|
||||||
$this->debug('Pruning content');
|
|
||||||
$this->readability->prepArticle($elem);
|
|
||||||
}
|
|
||||||
$this->debug('Element added to body');
|
|
||||||
$this->body->appendChild($elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// auto detect?
|
|
||||||
$detect_title = $detect_body = $detect_author = $detect_date = false;
|
|
||||||
// detect title?
|
|
||||||
if (!isset($this->title)) {
|
|
||||||
if (empty($this->config->title) || $this->config->autodetect_on_failure) {
|
|
||||||
$detect_title = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect body?
|
|
||||||
if (!isset($this->body)) {
|
|
||||||
if (empty($this->config->body) || $this->config->autodetect_on_failure) {
|
|
||||||
$detect_body = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect author?
|
|
||||||
if (empty($this->author)) {
|
|
||||||
if (empty($this->config->author) || $this->config->autodetect_on_failure) {
|
|
||||||
$detect_author = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// detect date?
|
|
||||||
if (!isset($this->date)) {
|
|
||||||
if (empty($this->config->date) || $this->config->autodetect_on_failure) {
|
|
||||||
$detect_date = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for hNews
|
|
||||||
if ($detect_title || $detect_body) {
|
|
||||||
// check for hentry
|
|
||||||
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' hentry ')]", $this->readability->dom);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('hNews: found hentry');
|
|
||||||
$hentry = $elems->item(0);
|
|
||||||
|
|
||||||
if ($detect_title) {
|
|
||||||
// check for entry-title
|
|
||||||
$elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' entry-title ')]", $hentry);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('hNews: found entry-title');
|
|
||||||
$this->title = $elems->item(0)->textContent;
|
|
||||||
// remove title from document
|
|
||||||
$elems->item(0)->parentNode->removeChild($elems->item(0));
|
|
||||||
$detect_title = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($detect_date) {
|
|
||||||
// check for time element with pubdate attribute
|
|
||||||
$elems = @$xpath->query(".//time[@pubdate] | .//abbr[contains(concat(' ',normalize-space(@class),' '),' published ')]", $hentry);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('hNews: found publication date');
|
|
||||||
$this->date = strtotime(trim($elems->item(0)->textContent));
|
|
||||||
// remove date from document
|
|
||||||
//$elems->item(0)->parentNode->removeChild($elems->item(0));
|
|
||||||
if ($this->date) {
|
|
||||||
$detect_date = false;
|
|
||||||
} else {
|
|
||||||
$this->date = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($detect_author) {
|
|
||||||
// check for time element with pubdate attribute
|
|
||||||
$elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' vcard ') and (contains(concat(' ',normalize-space(@class),' '),' author ') or contains(concat(' ',normalize-space(@class),' '),' byline '))]", $hentry);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('hNews: found author');
|
|
||||||
$author = $elems->item(0);
|
|
||||||
$fn = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' fn ')]", $author);
|
|
||||||
if ($fn && $fn->length > 0) {
|
|
||||||
foreach ($fn as $_fn) {
|
|
||||||
if (trim($_fn->textContent) != '') {
|
|
||||||
$this->author[] = trim($_fn->textContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (trim($author->textContent) != '') {
|
|
||||||
$this->author[] = trim($author->textContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$detect_author = empty($this->author);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for entry-content.
|
|
||||||
// according to hAtom spec, if there are multiple elements marked entry-content,
|
|
||||||
// we include all of these in the order they appear - see http://microformats.org/wiki/hatom#Entry_Content
|
|
||||||
if ($detect_body) {
|
|
||||||
$elems = @$xpath->query(".//*[contains(concat(' ',normalize-space(@class),' '),' entry-content ')]", $hentry);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('hNews: found entry-content');
|
|
||||||
if ($elems->length == 1) {
|
|
||||||
// what if it's empty? (some sites misuse hNews - place their content outside an empty entry-content element)
|
|
||||||
$e = $elems->item(0);
|
|
||||||
if (($e->tagName == 'img') || (trim($e->textContent) != '')) {
|
|
||||||
$this->body = $elems->item(0);
|
|
||||||
// prune (clean up elements that may not be content)
|
|
||||||
if ($this->config->prune) {
|
|
||||||
$this->debug('Pruning content');
|
|
||||||
$this->readability->prepArticle($this->body);
|
|
||||||
}
|
|
||||||
$detect_body = false;
|
|
||||||
} else {
|
|
||||||
$this->debug('hNews: skipping entry-content - appears not to contain content');
|
|
||||||
}
|
|
||||||
unset($e);
|
|
||||||
} else {
|
|
||||||
$this->body = $this->readability->dom->createElement('div');
|
|
||||||
$this->debug($elems->length.' entry-content elems found');
|
|
||||||
foreach ($elems as $elem) {
|
|
||||||
if (!isset($elem->parentNode)) continue;
|
|
||||||
$isDescendant = false;
|
|
||||||
foreach ($this->body->childNodes as $parent) {
|
|
||||||
if ($this->isDescendant($parent, $elem)) {
|
|
||||||
$isDescendant = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($isDescendant) {
|
|
||||||
$this->debug('Element is child of another body element, skipping.');
|
|
||||||
} else {
|
|
||||||
// prune (clean up elements that may not be content)
|
|
||||||
if ($this->config->prune) {
|
|
||||||
$this->debug('Pruning content');
|
|
||||||
$this->readability->prepArticle($elem);
|
|
||||||
}
|
|
||||||
$this->debug('Element added to body');
|
|
||||||
$this->body->appendChild($elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$detect_body = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for elements marked with instapaper_title
|
|
||||||
if ($detect_title) {
|
|
||||||
// check for instapaper_title
|
|
||||||
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' instapaper_title ')]", $this->readability->dom);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('title found (.instapaper_title)');
|
|
||||||
$this->title = $elems->item(0)->textContent;
|
|
||||||
// remove title from document
|
|
||||||
$elems->item(0)->parentNode->removeChild($elems->item(0));
|
|
||||||
$detect_title = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// check for elements marked with instapaper_body
|
|
||||||
if ($detect_body) {
|
|
||||||
$elems = @$xpath->query("//*[contains(concat(' ',normalize-space(@class),' '),' instapaper_body ')]", $this->readability->dom);
|
|
||||||
if ($elems && $elems->length > 0) {
|
|
||||||
$this->debug('body found (.instapaper_body)');
|
|
||||||
$this->body = $elems->item(0);
|
|
||||||
// prune (clean up elements that may not be content)
|
|
||||||
if ($this->config->prune) {
|
|
||||||
$this->debug('Pruning content');
|
|
||||||
$this->readability->prepArticle($this->body);
|
|
||||||
}
|
|
||||||
$detect_body = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find author in rel="author" marked element
|
|
||||||
// We only use this if there's exactly one.
|
|
||||||
// If there's more than one, it could indicate more than
|
|
||||||
// one author, but it could also indicate that we're processing
|
|
||||||
// a page listing different articles with different authors.
|
|
||||||
if ($detect_author) {
|
|
||||||
$elems = @$xpath->query("//a[contains(concat(' ',normalize-space(@rel),' '),' author ')]", $this->readability->dom);
|
|
||||||
if ($elems && $elems->length == 1) {
|
|
||||||
$this->debug('Author found (rel="author")');
|
|
||||||
$author = trim($elems->item(0)->textContent);
|
|
||||||
if ($author != '') {
|
|
||||||
$this->author[] = $author;
|
|
||||||
$detect_author = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find date in pubdate marked time element
|
|
||||||
// For the same reason given above, we only use this
|
|
||||||
// if there's exactly one element.
|
|
||||||
if ($detect_date) {
|
|
||||||
$elems = @$xpath->query("//time[@pubdate]", $this->readability->dom);
|
|
||||||
if ($elems && $elems->length == 1) {
|
|
||||||
$this->debug('Date found (pubdate marked time element)');
|
|
||||||
$this->date = strtotime(trim($elems->item(0)->textContent));
|
|
||||||
// remove date from document
|
|
||||||
//$elems->item(0)->parentNode->removeChild($elems->item(0));
|
|
||||||
if ($this->date) {
|
|
||||||
$detect_date = false;
|
|
||||||
} else {
|
|
||||||
$this->date = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// still missing title or body, so we detect using Readability
|
|
||||||
if ($detect_title || $detect_body) {
|
|
||||||
$this->debug('Using Readability');
|
|
||||||
// clone body if we're only using Readability for title (otherwise it may interfere with body element)
|
|
||||||
if (isset($this->body)) $this->body = $this->body->cloneNode(true);
|
|
||||||
$success = $this->readability->init();
|
|
||||||
}
|
|
||||||
if ($detect_title) {
|
|
||||||
$this->debug('Detecting title');
|
|
||||||
$this->title = $this->readability->getTitle()->textContent;
|
|
||||||
}
|
|
||||||
if ($detect_body && $success) {
|
|
||||||
$this->debug('Detecting body');
|
|
||||||
$this->body = $this->readability->getContent();
|
|
||||||
if ($this->body->childNodes->length == 1 && $this->body->firstChild->nodeType === XML_ELEMENT_NODE) {
|
|
||||||
$this->body = $this->body->firstChild;
|
|
||||||
}
|
|
||||||
// prune (clean up elements that may not be content)
|
|
||||||
if ($this->config->prune) {
|
|
||||||
$this->debug('Pruning content');
|
|
||||||
$this->readability->prepArticle($this->body);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($this->body)) {
|
|
||||||
// remove scripts
|
|
||||||
$this->readability->removeScripts($this->body);
|
|
||||||
// remove any h1-h6 elements that appear as first thing in the body
|
|
||||||
// and which match our title
|
|
||||||
if (isset($this->title) && ($this->title != '')) {
|
|
||||||
$firstChild = $this->body->firstChild;
|
|
||||||
while ($firstChild->nodeType && ($firstChild->nodeType !== XML_ELEMENT_NODE)) {
|
|
||||||
$firstChild = $firstChild->nextSibling;
|
|
||||||
}
|
|
||||||
if (($firstChild->nodeType === XML_ELEMENT_NODE)
|
|
||||||
&& in_array(strtolower($firstChild->tagName), array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))
|
|
||||||
&& (strtolower(trim($firstChild->textContent)) == strtolower(trim($this->title)))) {
|
|
||||||
$this->body->removeChild($firstChild);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->success = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we've had no success and we've used tidy, there's a chance
|
|
||||||
// that tidy has messed up. So let's try again without tidy...
|
|
||||||
if (!$this->success && $tidied && $smart_tidy) {
|
|
||||||
$this->debug('Trying again without tidy');
|
|
||||||
$this->process($original_html, $url, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->success;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function isDescendant(DOMElement $parent, DOMElement $child) {
|
|
||||||
$node = $child->parentNode;
|
|
||||||
while ($node != null) {
|
|
||||||
if ($node->isSameNode($parent)) return true;
|
|
||||||
$node = $node->parentNode;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContent() {
|
|
||||||
return $this->body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle() {
|
|
||||||
return $this->title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAuthors() {
|
|
||||||
return $this->author;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLanguage() {
|
|
||||||
return $this->language;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDate() {
|
|
||||||
return $this->date;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSiteConfig() {
|
|
||||||
return $this->config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
184
inc/3rdparty/content-extractor/SiteConfig.php
vendored
@ -1,184 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Site Config
|
|
||||||
*
|
|
||||||
* Each instance of this class should hold extraction patterns and other directives
|
|
||||||
* for a website. See ContentExtractor class to see how it's used.
|
|
||||||
*
|
|
||||||
* @version 0.6
|
|
||||||
* @date 2011-10-30
|
|
||||||
* @author Keyvan Minoukadeh
|
|
||||||
* @copyright 2011 Keyvan Minoukadeh
|
|
||||||
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
|
|
||||||
*/
|
|
||||||
|
|
||||||
class SiteConfig
|
|
||||||
{
|
|
||||||
// Use first matching element as title (0 or more xpath expressions)
|
|
||||||
public $title = array();
|
|
||||||
|
|
||||||
// Use first matching element as body (0 or more xpath expressions)
|
|
||||||
public $body = array();
|
|
||||||
|
|
||||||
// Use first matching element as author (0 or more xpath expressions)
|
|
||||||
public $author = array();
|
|
||||||
|
|
||||||
// Use first matching element as date (0 or more xpath expressions)
|
|
||||||
public $date = array();
|
|
||||||
|
|
||||||
// Strip elements matching these xpath expressions (0 or more)
|
|
||||||
public $strip = array();
|
|
||||||
|
|
||||||
// Strip elements which contain these strings (0 or more) in the id or class attribute
|
|
||||||
public $strip_id_or_class = array();
|
|
||||||
|
|
||||||
// Strip images which contain these strings (0 or more) in the src attribute
|
|
||||||
public $strip_image_src = array();
|
|
||||||
|
|
||||||
// Additional HTTP headers to send
|
|
||||||
// NOT YET USED
|
|
||||||
public $http_header = array();
|
|
||||||
|
|
||||||
// Process HTML with tidy before creating DOM
|
|
||||||
public $tidy = true;
|
|
||||||
|
|
||||||
// Autodetect title/body if xpath expressions fail to produce results.
|
|
||||||
// Note that this applies to title and body separately, ie.
|
|
||||||
// * if we get a body match but no title match, this option will determine whether we autodetect title
|
|
||||||
// * if neither match, this determines whether we autodetect title and body.
|
|
||||||
// Also note that this only applies when there is at least one xpath expression in title or body, ie.
|
|
||||||
// * if title and body are both empty (no xpath expressions), this option has no effect (both title and body will be auto-detected)
|
|
||||||
// * if there's an xpath expression for title and none for body, body will be auto-detected and this option will determine whether we auto-detect title if the xpath expression for it fails to produce results.
|
|
||||||
// Usage scenario: you want to extract something specific from a set of URLs, e.g. a table, and if the table is not found, you want to ignore the entry completely. Auto-detection is unlikely to succeed here, so you construct your patterns and set this option to false. Another scenario may be a site where auto-detection has proven to fail (or worse, picked up the wrong content).
|
|
||||||
public $autodetect_on_failure = true;
|
|
||||||
|
|
||||||
// Clean up content block - attempt to remove elements that appear to be superfluous
|
|
||||||
public $prune = true;
|
|
||||||
|
|
||||||
// Test URL - if present, can be used to test the config above
|
|
||||||
public $test_url = null;
|
|
||||||
|
|
||||||
// Single-page link - should identify a link element or URL pointing to the page holding the entire article
|
|
||||||
// This is useful for sites which split their articles across multiple pages. Links to such pages tend to
|
|
||||||
// display the first page with links to the other pages at the bottom. Often there is also a link to a page
|
|
||||||
// which displays the entire article on one page (e.g. 'print view').
|
|
||||||
// This should be an XPath expression identifying the link to that page. If present and we find a match,
|
|
||||||
// we will retrieve that page and the rest of the options in this config will be applied to the new page.
|
|
||||||
public $single_page_link = array();
|
|
||||||
|
|
||||||
// Single-page link in feed? - same as above, but patterns applied to item description HTML taken from feed
|
|
||||||
public $single_page_link_in_feed = array();
|
|
||||||
|
|
||||||
// TODO: which parser to use for turning raw HTML into a DOMDocument
|
|
||||||
public $parser = 'libxml';
|
|
||||||
|
|
||||||
// String replacement to be made on HTML before processing begins
|
|
||||||
public $replace_string = array();
|
|
||||||
|
|
||||||
// the options below cannot be set in the config files which this class represents
|
|
||||||
|
|
||||||
public static $debug = false;
|
|
||||||
protected static $config_path;
|
|
||||||
protected static $config_path_fallback;
|
|
||||||
protected static $config_cache = array();
|
|
||||||
const HOSTNAME_REGEX = '/^(([a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9-]*[A-Za-z0-9])$/';
|
|
||||||
|
|
||||||
protected static function debug($msg) {
|
|
||||||
if (self::$debug) {
|
|
||||||
$mem = round(memory_get_usage()/1024, 2);
|
|
||||||
$memPeak = round(memory_get_peak_usage()/1024, 2);
|
|
||||||
echo '* ',$msg;
|
|
||||||
echo ' - mem used: ',$mem," (peak: $memPeak)\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function set_config_path($path, $fallback=null) {
|
|
||||||
self::$config_path = $path;
|
|
||||||
self::$config_path_fallback = $fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function add_to_cache($host, SiteConfig $config) {
|
|
||||||
$host = strtolower($host);
|
|
||||||
self::$config_cache[$host] = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns SiteConfig instance if an appropriate one is found, false otherwise
|
|
||||||
public static function build($host) {
|
|
||||||
$host = strtolower($host);
|
|
||||||
if (substr($host, 0, 4) == 'www.') $host = substr($host, 4);
|
|
||||||
if (!$host || (strlen($host) > 200) || !preg_match(self::HOSTNAME_REGEX, $host)) return false;
|
|
||||||
// check for site configuration
|
|
||||||
$try = array($host);
|
|
||||||
$split = explode('.', $host);
|
|
||||||
if (count($split) > 1) {
|
|
||||||
array_shift($split);
|
|
||||||
$try[] = '.'.implode('.', $split);
|
|
||||||
}
|
|
||||||
foreach ($try as $h) {
|
|
||||||
if (array_key_exists($h, self::$config_cache)) {
|
|
||||||
self::debug("... cached ($h)");
|
|
||||||
return self::$config_cache[$h];
|
|
||||||
} elseif (file_exists(self::$config_path."/$h.txt")) {
|
|
||||||
self::debug("... from file ($h)");
|
|
||||||
$file = self::$config_path."/$h.txt";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isset($file)) {
|
|
||||||
if (isset(self::$config_path_fallback)) {
|
|
||||||
self::debug("... trying fallback ($host)");
|
|
||||||
foreach ($try as $h) {
|
|
||||||
if (file_exists(self::$config_path_fallback."/$h.txt")) {
|
|
||||||
self::debug("... from fallback file ($h)");
|
|
||||||
$file = self::$config_path_fallback."/$h.txt";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isset($file)) {
|
|
||||||
self::debug("... no match in fallback directory");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self::debug("... no match ($host)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$config_file = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
||||||
if (!$config_file || !is_array($config_file)) return false;
|
|
||||||
$config = new SiteConfig();
|
|
||||||
foreach ($config_file as $line) {
|
|
||||||
$line = trim($line);
|
|
||||||
|
|
||||||
// skip comments, empty lines
|
|
||||||
if ($line == '' || $line[0] == '#') continue;
|
|
||||||
|
|
||||||
// get command
|
|
||||||
$command = explode(':', $line, 2);
|
|
||||||
// if there's no colon ':', skip this line
|
|
||||||
if (count($command) != 2) continue;
|
|
||||||
$val = trim($command[1]);
|
|
||||||
$command = trim($command[0]);
|
|
||||||
if ($command == '' || $val == '') continue;
|
|
||||||
|
|
||||||
// check for commands where we accept multiple statements
|
|
||||||
if (in_array($command, array('title', 'body', 'author', 'date', 'strip', 'strip_id_or_class', 'strip_image_src', 'single_page_link', 'single_page_link_in_feed', 'http_header'))) {
|
|
||||||
array_push($config->$command, $val);
|
|
||||||
// check for single statement commands that evaluate to true or false
|
|
||||||
} elseif (in_array($command, array('tidy', 'prune', 'autodetect_on_failure'))) {
|
|
||||||
$config->$command = ($val == 'yes');
|
|
||||||
// check for single statement commands stored as strings
|
|
||||||
} elseif (in_array($command, array('test_url', 'parser'))) {
|
|
||||||
$config->$command = $val;
|
|
||||||
} elseif ((substr($command, -1) == ')') && preg_match('!^([a-z0-9_]+)\((.*?)\)$!i', $command, $match)) {
|
|
||||||
if (in_array($match[1], array('replace_string'))) {
|
|
||||||
$command = $match[1];
|
|
||||||
array_push($config->$command, array($match[2], $val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $config;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
24
inc/3rdparty/feedwriter/DummySingleItemFeed.php
vendored
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
// create single item dummy feed object
|
|
||||||
class DummySingleItemFeed {
|
|
||||||
public $item;
|
|
||||||
function __construct($url) { $this->item = new DummySingleItem($url); }
|
|
||||||
public function get_title() { return ''; }
|
|
||||||
public function get_description() { return 'Content extracted from '.$this->item->url; }
|
|
||||||
public function get_link() { return $this->item->url; }
|
|
||||||
public function get_language() { return false; }
|
|
||||||
public function get_image_url() { return false; }
|
|
||||||
public function get_items($start=0, $max=1) { return array(0=>$this->item); }
|
|
||||||
}
|
|
||||||
class DummySingleItem {
|
|
||||||
public $url;
|
|
||||||
function __construct($url) { $this->url = $url; }
|
|
||||||
public function get_permalink() { return $this->url; }
|
|
||||||
public function get_title() { return ''; }
|
|
||||||
public function get_date($format='') { return false; }
|
|
||||||
public function get_author($key=0) { return null; }
|
|
||||||
public function get_authors() { return null; }
|
|
||||||
public function get_description() { return ''; }
|
|
||||||
public function get_enclosure($key=0, $prefer=null) { return null; }
|
|
||||||
public function get_enclosures() { return null; }
|
|
||||||
}
|
|
||||||
167
inc/3rdparty/feedwriter/FeedItem.php
vendored
@ -1,167 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Univarsel Feed Writer
|
|
||||||
*
|
|
||||||
* FeedItem class - Used as feed element in FeedWriter class
|
|
||||||
*
|
|
||||||
* @package UnivarselFeedWriter
|
|
||||||
* @author Anis uddin Ahmad <anisniit@gmail.com>
|
|
||||||
* @link http://www.ajaxray.com/projects/rss
|
|
||||||
*/
|
|
||||||
class FeedItem
|
|
||||||
{
|
|
||||||
private $elements = array(); //Collection of feed elements
|
|
||||||
private $version;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param contant (RSS1/RSS2/ATOM) RSS2 is default.
|
|
||||||
*/
|
|
||||||
function __construct($version = RSS2)
|
|
||||||
{
|
|
||||||
$this->version = $version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an element to elements array
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param srting The tag name of an element
|
|
||||||
* @param srting The content of tag
|
|
||||||
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addElement($elementName, $content, $attributes = null)
|
|
||||||
{
|
|
||||||
$this->elements[$elementName]['name'] = $elementName;
|
|
||||||
$this->elements[$elementName]['content'] = $content;
|
|
||||||
$this->elements[$elementName]['attributes'] = $attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set multiple feed elements from an array.
|
|
||||||
* Elements which have attributes cannot be added by this method
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array array of elements in 'tagName' => 'tagContent' format.
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addElementArray($elementArray)
|
|
||||||
{
|
|
||||||
if(! is_array($elementArray)) return;
|
|
||||||
foreach ($elementArray as $elementName => $content)
|
|
||||||
{
|
|
||||||
$this->addElement($elementName, $content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the collection of elements in this feed item
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getElements()
|
|
||||||
{
|
|
||||||
return $this->elements;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper functions ------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'dscription' element of feed item
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string The content of 'description' element
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setDescription($description)
|
|
||||||
{
|
|
||||||
$tag = ($this->version == ATOM)? 'summary' : 'description';
|
|
||||||
$this->addElement($tag, $description);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @desc Set the 'title' element of feed item
|
|
||||||
* @access public
|
|
||||||
* @param string The content of 'title' element
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setTitle($title)
|
|
||||||
{
|
|
||||||
$this->addElement('title', $title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'date' element of feed item
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string The content of 'date' element
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setDate($date)
|
|
||||||
{
|
|
||||||
if(! is_numeric($date))
|
|
||||||
{
|
|
||||||
$date = strtotime($date);
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->version == ATOM)
|
|
||||||
{
|
|
||||||
$tag = 'updated';
|
|
||||||
$value = date(DATE_ATOM, $date);
|
|
||||||
}
|
|
||||||
elseif($this->version == RSS2)
|
|
||||||
{
|
|
||||||
$tag = 'pubDate';
|
|
||||||
$value = date(DATE_RSS, $date);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$tag = 'dc:date';
|
|
||||||
$value = date("Y-m-d", $date);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addElement($tag, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'link' element of feed item
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string The content of 'link' element
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setLink($link)
|
|
||||||
{
|
|
||||||
if($this->version == RSS2 || $this->version == RSS1)
|
|
||||||
{
|
|
||||||
$this->addElement('link', $link);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->addElement('link','',array('href'=>$link));
|
|
||||||
$this->addElement('id', FeedWriter::uuid($link,'urn:uuid:'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'encloser' element of feed item
|
|
||||||
* For RSS 2.0 only
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string The url attribute of encloser tag
|
|
||||||
* @param string The length attribute of encloser tag
|
|
||||||
* @param string The type attribute of encloser tag
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setEncloser($url, $length, $type)
|
|
||||||
{
|
|
||||||
$attributes = array('url'=>$url, 'length'=>$length, 'type'=>$type);
|
|
||||||
$this->addElement('enclosure','',$attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end of class FeedItem
|
|
||||||
?>
|
|
||||||
434
inc/3rdparty/feedwriter/FeedWriter.php
vendored
@ -1,434 +0,0 @@
|
|||||||
<?php
|
|
||||||
define('RSS2', 1, true);
|
|
||||||
define('JSON', 2, true);
|
|
||||||
define('ATOM', 3, true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Univarsel Feed Writer class
|
|
||||||
*
|
|
||||||
* Genarate RSS2 or JSON (original: RSS 1.0, RSS2.0 and ATOM Feed)
|
|
||||||
*
|
|
||||||
* Modified for FiveFilters.org's Full-Text RSS project
|
|
||||||
* to allow for inclusion of hubs, JSON output.
|
|
||||||
* Stripped RSS1 and ATOM support.
|
|
||||||
*
|
|
||||||
* @package UnivarselFeedWriter
|
|
||||||
* @author Anis uddin Ahmad <anisniit@gmail.com>
|
|
||||||
* @link http://www.ajaxray.com/projects/rss
|
|
||||||
*/
|
|
||||||
class FeedWriter
|
|
||||||
{
|
|
||||||
private $self = null; // self URL - http://feed2.w3.org/docs/warning/MissingAtomSelfLink.html
|
|
||||||
private $hubs = array(); // PubSubHubbub hubs
|
|
||||||
private $channels = array(); // Collection of channel elements
|
|
||||||
private $items = array(); // Collection of items as object of FeedItem class.
|
|
||||||
private $data = array(); // Store some other version wise data
|
|
||||||
private $CDATAEncoding = array(); // The tag names which have to encoded as CDATA
|
|
||||||
private $xsl = null; // stylesheet to render RSS (used by Chrome)
|
|
||||||
private $json = null; // JSON object
|
|
||||||
|
|
||||||
private $version = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param constant the version constant (RSS2 or JSON).
|
|
||||||
*/
|
|
||||||
function __construct($version = RSS2)
|
|
||||||
{
|
|
||||||
$this->version = $version;
|
|
||||||
|
|
||||||
// Setting default value for assential channel elements
|
|
||||||
$this->channels['title'] = $version . ' Feed';
|
|
||||||
$this->channels['link'] = 'http://www.ajaxray.com/blog';
|
|
||||||
|
|
||||||
//Tag names to encode in CDATA
|
|
||||||
$this->CDATAEncoding = array('description', 'content:encoded', 'content', 'subtitle', 'summary');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setFormat($format) {
|
|
||||||
$this->version = $format;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start # public functions ---------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a channel element
|
|
||||||
* @access public
|
|
||||||
* @param srting name of the channel tag
|
|
||||||
* @param string content of the channel tag
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setChannelElement($elementName, $content)
|
|
||||||
{
|
|
||||||
$this->channels[$elementName] = $content ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set multiple channel elements from an array. Array elements
|
|
||||||
* should be 'channelName' => 'channelContent' format.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array array of channels
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setChannelElementsFromArray($elementArray)
|
|
||||||
{
|
|
||||||
if(! is_array($elementArray)) return;
|
|
||||||
foreach ($elementArray as $elementName => $content)
|
|
||||||
{
|
|
||||||
$this->setChannelElement($elementName, $content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Genarate the actual RSS/JSON file
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function genarateFeed()
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2) {
|
|
||||||
header('Content-type: text/xml; charset=UTF-8');
|
|
||||||
} elseif ($this->version == JSON) {
|
|
||||||
header('Content-type: application/json; charset=UTF-8');
|
|
||||||
$this->json = new stdClass();
|
|
||||||
}
|
|
||||||
$this->printHead();
|
|
||||||
$this->printChannels();
|
|
||||||
$this->printItems();
|
|
||||||
$this->printTale();
|
|
||||||
if ($this->version == JSON) {
|
|
||||||
echo json_encode($this->json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new FeedItem.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return object instance of FeedItem class
|
|
||||||
*/
|
|
||||||
public function createNewItem()
|
|
||||||
{
|
|
||||||
$Item = new FeedItem($this->version);
|
|
||||||
return $Item;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a FeedItem to the main class
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param object instance of FeedItem class
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addItem($feedItem)
|
|
||||||
{
|
|
||||||
$this->items[] = $feedItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper functions -------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'title' channel element
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param srting value of 'title' channel tag
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setTitle($title)
|
|
||||||
{
|
|
||||||
$this->setChannelElement('title', $title);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a hub to the channel element
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string URL
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function addHub($hub)
|
|
||||||
{
|
|
||||||
$this->hubs[] = $hub;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set XSL URL
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string URL
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setXsl($xsl)
|
|
||||||
{
|
|
||||||
$this->xsl = $xsl;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set self URL
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string URL
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setSelf($self)
|
|
||||||
{
|
|
||||||
$this->self = $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'description' channel element
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param srting value of 'description' channel tag
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setDescription($desciption)
|
|
||||||
{
|
|
||||||
$tag = ($this->version == ATOM)? 'subtitle' : 'description';
|
|
||||||
$this->setChannelElement($tag, $desciption);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'link' channel element
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param srting value of 'link' channel tag
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setLink($link)
|
|
||||||
{
|
|
||||||
$this->setChannelElement('link', $link);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the 'image' channel element
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param srting title of image
|
|
||||||
* @param srting link url of the imahe
|
|
||||||
* @param srting path url of the image
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function setImage($title, $link, $url)
|
|
||||||
{
|
|
||||||
$this->setChannelElement('image', array('title'=>$title, 'link'=>$link, 'url'=>$url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// End # public functions ----------------------------------------------
|
|
||||||
|
|
||||||
// Start # private functions ----------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the xml and rss namespace
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function printHead()
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2)
|
|
||||||
{
|
|
||||||
$out = '<?xml version="1.0" encoding="utf-8"?>'."\n";
|
|
||||||
if ($this->xsl) $out .= '<?xml-stylesheet type="text/xsl" href="'.htmlspecialchars($this->xsl).'"?>' . PHP_EOL;
|
|
||||||
$out .= '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">' . PHP_EOL;
|
|
||||||
echo $out;
|
|
||||||
}
|
|
||||||
elseif ($this->version == JSON)
|
|
||||||
{
|
|
||||||
$this->json->rss = array('@attributes' => array('version' => '2.0'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the open tags at the end of file
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function printTale()
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2)
|
|
||||||
{
|
|
||||||
echo '</channel>',PHP_EOL,'</rss>';
|
|
||||||
}
|
|
||||||
// do nothing for JSON
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a single node as xml format
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string name of the tag
|
|
||||||
* @param mixed tag value as string or array of nested tags in 'tagName' => 'tagValue' format
|
|
||||||
* @param array Attributes(if any) in 'attrName' => 'attrValue' format
|
|
||||||
* @return string formatted xml tag
|
|
||||||
*/
|
|
||||||
private function makeNode($tagName, $tagContent, $attributes = null)
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2)
|
|
||||||
{
|
|
||||||
$nodeText = '';
|
|
||||||
$attrText = '';
|
|
||||||
if (is_array($attributes))
|
|
||||||
{
|
|
||||||
foreach ($attributes as $key => $value)
|
|
||||||
{
|
|
||||||
$attrText .= " $key=\"$value\" ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$nodeText .= "<{$tagName}{$attrText}>";
|
|
||||||
if (is_array($tagContent))
|
|
||||||
{
|
|
||||||
foreach ($tagContent as $key => $value)
|
|
||||||
{
|
|
||||||
$nodeText .= $this->makeNode($key, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//$nodeText .= (in_array($tagName, $this->CDATAEncoding))? $tagContent : htmlentities($tagContent);
|
|
||||||
$nodeText .= htmlspecialchars($tagContent);
|
|
||||||
}
|
|
||||||
//$nodeText .= (in_array($tagName, $this->CDATAEncoding))? "]]></$tagName>" : "</$tagName>";
|
|
||||||
$nodeText .= "</$tagName>";
|
|
||||||
return $nodeText . PHP_EOL;
|
|
||||||
}
|
|
||||||
elseif ($this->version == JSON)
|
|
||||||
{
|
|
||||||
$tagName = (string)$tagName;
|
|
||||||
$tagName = strtr($tagName, ':', '_');
|
|
||||||
$node = null;
|
|
||||||
if (!$tagContent && is_array($attributes) && count($attributes))
|
|
||||||
{
|
|
||||||
$node = array('@attributes' => $this->json_keys($attributes));
|
|
||||||
} else {
|
|
||||||
if (is_array($tagContent)) {
|
|
||||||
$node = $this->json_keys($tagContent);
|
|
||||||
} else {
|
|
||||||
$node = $tagContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $node;
|
|
||||||
}
|
|
||||||
return ''; // should not get here
|
|
||||||
}
|
|
||||||
|
|
||||||
private function json_keys(array $array) {
|
|
||||||
$new = array();
|
|
||||||
foreach ($array as $key => $val) {
|
|
||||||
if (is_string($key)) $key = strtr($key, ':', '_');
|
|
||||||
if (is_array($val)) {
|
|
||||||
$new[$key] = $this->json_keys($val);
|
|
||||||
} else {
|
|
||||||
$new[$key] = $val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @desc Print channels
|
|
||||||
* @access private
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function printChannels()
|
|
||||||
{
|
|
||||||
//Start channel tag
|
|
||||||
switch ($this->version)
|
|
||||||
{
|
|
||||||
case RSS2:
|
|
||||||
echo '<channel>' . PHP_EOL;
|
|
||||||
// add hubs
|
|
||||||
foreach ($this->hubs as $hub) {
|
|
||||||
//echo $this->makeNode('link', '', array('rel'=>'hub', 'href'=>$hub, 'xmlns'=>'http://www.w3.org/2005/Atom'));
|
|
||||||
echo '<link rel="hub" href="'.htmlspecialchars($hub).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
|
|
||||||
}
|
|
||||||
// add self
|
|
||||||
if (isset($this->self)) {
|
|
||||||
//echo $this->makeNode('link', '', array('rel'=>'self', 'href'=>$this->self, 'xmlns'=>'http://www.w3.org/2005/Atom'));
|
|
||||||
echo '<link rel="self" href="'.htmlspecialchars($this->self).'" xmlns="http://www.w3.org/2005/Atom" />' . PHP_EOL;
|
|
||||||
}
|
|
||||||
//Print Items of channel
|
|
||||||
foreach ($this->channels as $key => $value)
|
|
||||||
{
|
|
||||||
echo $this->makeNode($key, $value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case JSON:
|
|
||||||
$this->json->rss['channel'] = (object)$this->json_keys($this->channels);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints formatted feed items
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function printItems()
|
|
||||||
{
|
|
||||||
foreach ($this->items as $item)
|
|
||||||
{
|
|
||||||
$thisItems = $item->getElements();
|
|
||||||
|
|
||||||
echo $this->startItem();
|
|
||||||
|
|
||||||
if ($this->version == JSON) {
|
|
||||||
$json_item = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($thisItems as $feedItem )
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2) {
|
|
||||||
echo $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']);
|
|
||||||
} elseif ($this->version == JSON) {
|
|
||||||
$json_item[strtr($feedItem['name'], ':', '_')] = $this->makeNode($feedItem['name'], $feedItem['content'], $feedItem['attributes']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
echo $this->endItem();
|
|
||||||
if ($this->version == JSON) {
|
|
||||||
if (count($this->items) > 1) {
|
|
||||||
$this->json->rss['channel']->item[] = $json_item;
|
|
||||||
} else {
|
|
||||||
$this->json->rss['channel']->item = $json_item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make the starting tag of channels
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function startItem()
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2)
|
|
||||||
{
|
|
||||||
echo '<item>' . PHP_EOL;
|
|
||||||
}
|
|
||||||
// nothing for JSON
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes feed item tag
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
private function endItem()
|
|
||||||
{
|
|
||||||
if ($this->version == RSS2)
|
|
||||||
{
|
|
||||||
echo '</item>' . PHP_EOL;
|
|
||||||
}
|
|
||||||
// nothing for JSON
|
|
||||||
}
|
|
||||||
|
|
||||||
// End # private functions ----------------------------------------------
|
|
||||||
}
|
|
||||||
404
inc/3rdparty/humble-http-agent/CookieJar.php
vendored
@ -1,404 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Cookie Jar
|
|
||||||
*
|
|
||||||
* PHP class for handling cookies, as defined by the Netscape spec:
|
|
||||||
* <http://curl.haxx.se/rfc/cookie_spec.html>
|
|
||||||
*
|
|
||||||
* This class should be used to handle cookies (storing cookies from HTTP response messages, and
|
|
||||||
* sending out cookies in HTTP request messages). This has been adapted for FiveFilters.org
|
|
||||||
* from the original version used in HTTP Navigator. See http://www.keyvan.net/code/http-navigator/
|
|
||||||
*
|
|
||||||
* This class is mainly based on Cookies.pm <http://search.cpan.org/author/GAAS/libwww-perl-5.65/
|
|
||||||
* lib/HTTP/Cookies.pm> from the libwww-perl collection <http://www.linpro.no/lwp/>.
|
|
||||||
* Unlike Cookies.pm, this class only supports the Netscape cookie spec, not RFC 2965.
|
|
||||||
*
|
|
||||||
* @version 0.5
|
|
||||||
* @date 2011-03-15
|
|
||||||
* @see http://php.net/HttpRequestPool
|
|
||||||
* @author Keyvan Minoukadeh
|
|
||||||
* @copyright 2011 Keyvan Minoukadeh
|
|
||||||
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CookieJar
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Cookies - array containing all cookies.
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* Cookies are stored like this:
|
|
||||||
* [domain][path][name] = array
|
|
||||||
* where array is:
|
|
||||||
* 0 => value, 1 => secure, 2 => expires
|
|
||||||
* </pre>
|
|
||||||
* @var array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
public $cookies = array();
|
|
||||||
public $debug = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
function __construct() {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function debug($msg, $file=null, $line=null) {
|
|
||||||
if ($this->debug) {
|
|
||||||
$mem = round(memory_get_usage()/1024, 2);
|
|
||||||
$memPeak = round(memory_get_peak_usage()/1024, 2);
|
|
||||||
echo '* ',$msg;
|
|
||||||
if (isset($file, $line)) echo " ($file line $line)";
|
|
||||||
echo ' - mem used: ',$mem," (peak: $memPeak)\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get matching cookies
|
|
||||||
*
|
|
||||||
* Only use this method if you cannot use add_cookie_header(), for example, if you want to use
|
|
||||||
* this cookie jar class without using the request class.
|
|
||||||
*
|
|
||||||
* @param array $param associative array containing 'domain', 'path', 'secure' keys
|
|
||||||
* @return string
|
|
||||||
* @see add_cookie_header()
|
|
||||||
*/
|
|
||||||
public function getMatchingCookies($url)
|
|
||||||
{
|
|
||||||
if (($parts = @parse_url($url)) && isset($parts['scheme'], $parts['host'], $parts['path'])) {
|
|
||||||
$param['domain'] = $parts['host'];
|
|
||||||
$param['path'] = $parts['path'];
|
|
||||||
$param['secure'] = (strtolower($parts['scheme']) == 'https');
|
|
||||||
unset($parts);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// RFC 2965 notes:
|
|
||||||
// If multiple cookies satisfy the criteria above, they are ordered in
|
|
||||||
// the Cookie header such that those with more specific Path attributes
|
|
||||||
// precede those with less specific. Ordering with respect to other
|
|
||||||
// attributes (e.g., Domain) is unspecified.
|
|
||||||
$domain = $param['domain'];
|
|
||||||
if (strpos($domain, '.') === false) $domain .= '.local';
|
|
||||||
$request_path = $param['path'];
|
|
||||||
if ($request_path == '') $request_path = '/';
|
|
||||||
$request_secure = $param['secure'];
|
|
||||||
$now = time();
|
|
||||||
$matched_cookies = array();
|
|
||||||
// domain - find matching domains
|
|
||||||
$this->debug('Finding matching domains for '.$domain, __FILE__, __LINE__);
|
|
||||||
while (strpos($domain, '.') !== false) {
|
|
||||||
if (isset($this->cookies[$domain])) {
|
|
||||||
$this->debug(' domain match found: '.$domain);
|
|
||||||
$cookies =& $this->cookies[$domain];
|
|
||||||
} else {
|
|
||||||
$domain = $this->_reduce_domain($domain);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// paths - find matching paths starting from most specific
|
|
||||||
$this->debug(' - Finding matching paths for '.$request_path);
|
|
||||||
$paths = array_keys($cookies);
|
|
||||||
usort($paths, array($this, '_cmp_length'));
|
|
||||||
foreach ($paths as $path) {
|
|
||||||
// continue to next cookie if request path does not path-match cookie path
|
|
||||||
if (!$this->_path_match($request_path, $path)) continue;
|
|
||||||
// loop through cookie names
|
|
||||||
$this->debug(' path match found: '.$path);
|
|
||||||
foreach ($cookies[$path] as $name => $values) {
|
|
||||||
// if this cookie is secure but request isn't, continue to next cookie
|
|
||||||
if ($values[1] && !$request_secure) continue;
|
|
||||||
// if cookie is not a session cookie and has expired, continue to next cookie
|
|
||||||
if (is_int($values[2]) && ($values[2] < $now)) continue;
|
|
||||||
// cookie matches request
|
|
||||||
$this->debug(' cookie match: '.$name.'='.$values[0]);
|
|
||||||
$matched_cookies[] = $name.'='.$values[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$domain = $this->_reduce_domain($domain);
|
|
||||||
}
|
|
||||||
// return cookies
|
|
||||||
return implode('; ', $matched_cookies);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse Set-Cookie values.
|
|
||||||
*
|
|
||||||
* Only use this method if you cannot use extract_cookies(), for example, if you want to use
|
|
||||||
* this cookie jar class without using the response class.
|
|
||||||
*
|
|
||||||
* @param array $set_cookies array holding 1 or more "Set-Cookie" header values
|
|
||||||
* @param array $param associative array containing 'host', 'path' keys
|
|
||||||
* @return void
|
|
||||||
* @see extract_cookies()
|
|
||||||
*/
|
|
||||||
public function storeCookies($url, $set_cookies)
|
|
||||||
{
|
|
||||||
if (count($set_cookies) == 0) return;
|
|
||||||
$param = @parse_url($url);
|
|
||||||
if (!is_array($param) || !isset($param['host'])) return;
|
|
||||||
$request_host = $param['host'];
|
|
||||||
if (strpos($request_host, '.') === false) $request_host .= '.local';
|
|
||||||
$request_path = @$param['path'];
|
|
||||||
if ($request_path == '') $request_path = '/';
|
|
||||||
//
|
|
||||||
// loop through set-cookie headers
|
|
||||||
//
|
|
||||||
foreach ($set_cookies as $set_cookie) {
|
|
||||||
$this->debug('Parsing: '.$set_cookie);
|
|
||||||
// temporary cookie store (before adding to jar)
|
|
||||||
$tmp_cookie = array();
|
|
||||||
$param = explode(';', $set_cookie);
|
|
||||||
// loop through params
|
|
||||||
for ($x=0; $x<count($param); $x++) {
|
|
||||||
$key_val = explode('=', $param[$x], 2);
|
|
||||||
if (count($key_val) != 2) {
|
|
||||||
// if the first param isn't a name=value pair, continue to the next set-cookie
|
|
||||||
// header
|
|
||||||
if ($x == 0) continue 2;
|
|
||||||
// check for secure flag
|
|
||||||
if (strtolower(trim($key_val[0])) == 'secure') $tmp_cookie['secure'] = true;
|
|
||||||
// continue to next param
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
list($key, $val) = array_map('trim', $key_val);
|
|
||||||
// first name=value pair is the cookie name and value
|
|
||||||
// the name and value are stored under 'name' and 'value' to avoid conflicts
|
|
||||||
// with later parameters.
|
|
||||||
if ($x == 0) {
|
|
||||||
$tmp_cookie = array('name'=>$key, 'value'=>$val);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$key = strtolower($key);
|
|
||||||
if (in_array($key, array('expires', 'path', 'domain', 'secure'))) {
|
|
||||||
$tmp_cookie[$key] = $val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// set cookie
|
|
||||||
//
|
|
||||||
// check domain
|
|
||||||
if (isset($tmp_cookie['domain']) && ($tmp_cookie['domain'] != $request_host) &&
|
|
||||||
($tmp_cookie['domain'] != ".$request_host")) {
|
|
||||||
$domain = $tmp_cookie['domain'];
|
|
||||||
if ((strpos($domain, '.') === false) && ($domain != 'local')) {
|
|
||||||
$this->debug(' - domain "'.$domain.'" has no dot and is not a local domain');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (preg_match('/\.[0-9]+$/', $domain)) {
|
|
||||||
$this->debug(' - domain "'.$domain.'" appears to be an ip address');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (substr($domain, 0, 1) != '.') $domain = ".$domain";
|
|
||||||
if (!$this->_domain_match($request_host, $domain)) {
|
|
||||||
$this->debug(' - request host "'.$request_host.'" does not domain-match "'.$domain.'"');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if domain is not specified in the set-cookie header, domain will default to
|
|
||||||
// the request host
|
|
||||||
$domain = $request_host;
|
|
||||||
}
|
|
||||||
// check path
|
|
||||||
if (isset($tmp_cookie['path']) && ($tmp_cookie['path'] != '')) {
|
|
||||||
$path = urldecode($tmp_cookie['path']);
|
|
||||||
if (!$this->_path_match($request_path, $path)) {
|
|
||||||
$this->debug(' - request path "'.$request_path.'" does not path-match "'.$path.'"');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$path = $request_path;
|
|
||||||
$path = substr($path, 0, strrpos($path, '/'));
|
|
||||||
if ($path == '') $path = '/';
|
|
||||||
}
|
|
||||||
// check if secure
|
|
||||||
$secure = (isset($tmp_cookie['secure'])) ? true : false;
|
|
||||||
// check expiry
|
|
||||||
if (isset($tmp_cookie['expires'])) {
|
|
||||||
if (($expires = strtotime($tmp_cookie['expires'])) < 0) {
|
|
||||||
$expires = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$expires = null;
|
|
||||||
}
|
|
||||||
// set cookie
|
|
||||||
$this->set_cookie($domain, $path, $tmp_cookie['name'], $tmp_cookie['value'], $secure, $expires);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return array of set-cookie values extracted from HTTP response headers (string $h)
|
|
||||||
public function extractCookies($h) {
|
|
||||||
$x = 0;
|
|
||||||
$lines = 0;
|
|
||||||
$headers = array();
|
|
||||||
$last_match = false;
|
|
||||||
$h = explode("\n", $h);
|
|
||||||
foreach ($h as $line) {
|
|
||||||
$line = rtrim($line);
|
|
||||||
$lines++;
|
|
||||||
|
|
||||||
$trimmed_line = trim($line);
|
|
||||||
if (isset($line_last)) {
|
|
||||||
// check if we have \r\n\r\n (indicating the end of headers)
|
|
||||||
// some servers will not use CRLF (\r\n), so we make CR (\r) optional.
|
|
||||||
// if (preg_match('/\015?\012\015?\012/', $line_last.$line)) {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// As an alternative, we can check if the current trimmed line is empty
|
|
||||||
if ($trimmed_line == '') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for continuation line...
|
|
||||||
// RFC 2616 Section 2.2 "Basic Rules":
|
|
||||||
// HTTP/1.1 header field values can be folded onto multiple lines if the
|
|
||||||
// continuation line begins with a space or horizontal tab. All linear
|
|
||||||
// white space, including folding, has the same semantics as SP. A
|
|
||||||
// recipient MAY replace any linear white space with a single SP before
|
|
||||||
// interpreting the field value or forwarding the message downstream.
|
|
||||||
if ($last_match && preg_match('/^\s+(.*)/', $line, $match)) {
|
|
||||||
// append to previous header value
|
|
||||||
$headers[$x-1] .= ' '.rtrim($match[1]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$line_last = $line;
|
|
||||||
|
|
||||||
// split header name and value
|
|
||||||
if (preg_match('/^Set-Cookie\s*:\s*(.*)/i', $line, $match)) {
|
|
||||||
$headers[$x++] = rtrim($match[1]);
|
|
||||||
$last_match = true;
|
|
||||||
} else {
|
|
||||||
$last_match = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Cookie
|
|
||||||
* @param string $domain
|
|
||||||
* @param string $path
|
|
||||||
* @param string $name cookie name
|
|
||||||
* @param string $value cookie value
|
|
||||||
* @param bool $secure
|
|
||||||
* @param int $expires expiry time (null if session cookie, <= 0 will delete cookie)
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function set_cookie($domain, $path, $name, $value, $secure=false, $expires=null)
|
|
||||||
{
|
|
||||||
if ($domain == '') return;
|
|
||||||
if ($path == '') return;
|
|
||||||
if ($name == '') return;
|
|
||||||
// check if cookie needs to go
|
|
||||||
if (isset($expires) && ($expires <= 0)) {
|
|
||||||
if (isset($this->cookies[$domain][$path][$name])) unset($this->cookies[$domain][$path][$name]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($value == '') return;
|
|
||||||
$this->cookies[$domain][$path][$name] = array($value, $secure, $expires);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear cookies - [domain [,path [,name]]] - call method with no arguments to clear all cookies.
|
|
||||||
* @param string $domain
|
|
||||||
* @param string $path
|
|
||||||
* @param string $name
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function clear($domain=null, $path=null, $name=null)
|
|
||||||
{
|
|
||||||
if (!isset($domain)) {
|
|
||||||
$this->cookies = array();
|
|
||||||
} elseif (!isset($path)) {
|
|
||||||
if (isset($this->cookies[$domain])) unset($this->cookies[$domain]);
|
|
||||||
} elseif (!isset($name)) {
|
|
||||||
if (isset($this->cookies[$domain][$path])) unset($this->cookies[$domain][$path]);
|
|
||||||
} elseif (isset($name)) {
|
|
||||||
if (isset($this->cookies[$domain][$path][$name])) unset($this->cookies[$domain][$path][$name]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare string length - used for sorting
|
|
||||||
* @access private
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
function _cmp_length($a, $b)
|
|
||||||
{
|
|
||||||
$la = strlen($a); $lb = strlen($b);
|
|
||||||
if ($la == $lb) return 0;
|
|
||||||
return ($la > $lb) ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reduce domain
|
|
||||||
* @param string $domain
|
|
||||||
* @return string
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _reduce_domain($domain)
|
|
||||||
{
|
|
||||||
if ($domain == '') return '';
|
|
||||||
if (substr($domain, 0, 1) == '.') return substr($domain, 1);
|
|
||||||
return substr($domain, strpos($domain, '.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path match - check if path1 path-matches path2
|
|
||||||
*
|
|
||||||
* From RFC 2965:
|
|
||||||
* <i>For two strings that represent paths, P1 and P2, P1 path-matches P2
|
|
||||||
* if P2 is a prefix of P1 (including the case where P1 and P2 string-
|
|
||||||
* compare equal). Thus, the string /tec/waldo path-matches /tec.</i>
|
|
||||||
* @param string $path1
|
|
||||||
* @param string $path2
|
|
||||||
* @return bool
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _path_match($path1, $path2)
|
|
||||||
{
|
|
||||||
return (substr($path1, 0, strlen($path2)) == $path2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Domain match - check if domain1 domain-matches domain2
|
|
||||||
*
|
|
||||||
* A few extracts from RFC 2965:
|
|
||||||
* - A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com
|
|
||||||
* would be rejected, because H is y.x and contains a dot.
|
|
||||||
*
|
|
||||||
* - A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com
|
|
||||||
* would be accepted.
|
|
||||||
*
|
|
||||||
* - A Set-Cookie2 with Domain=.com or Domain=.com., will always be
|
|
||||||
* rejected, because there is no embedded dot.
|
|
||||||
*
|
|
||||||
* - A Set-Cookie2 from request-host example for Domain=.local will
|
|
||||||
* be accepted, because the effective host name for the request-
|
|
||||||
* host is example.local, and example.local domain-matches .local.
|
|
||||||
*
|
|
||||||
* I'm ignoring the first point for now (must check to see how other browsers handle
|
|
||||||
* this rule for Set-Cookie headers)
|
|
||||||
*
|
|
||||||
* @param string $domain1
|
|
||||||
* @param string $domain2
|
|
||||||
* @return bool
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _domain_match($domain1, $domain2)
|
|
||||||
{
|
|
||||||
$domain1 = strtolower($domain1);
|
|
||||||
$domain2 = strtolower($domain2);
|
|
||||||
while (strpos($domain1, '.') !== false) {
|
|
||||||
if ($domain1 == $domain2) return true;
|
|
||||||
$domain1 = $this->_reduce_domain($domain1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
720
inc/3rdparty/humble-http-agent/HumbleHttpAgent.php
vendored
@ -1,720 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Humble HTTP Agent
|
|
||||||
*
|
|
||||||
* This class is designed to take advantage of parallel HTTP requests
|
|
||||||
* offered by PHP's PECL HTTP extension or the curl_multi_* functions.
|
|
||||||
* For environments which do not have these options, it reverts to standard sequential
|
|
||||||
* requests (using file_get_contents())
|
|
||||||
*
|
|
||||||
* @version 1.0
|
|
||||||
* @date 2012-02-09
|
|
||||||
* @see http://php.net/HttpRequestPool
|
|
||||||
* @author Keyvan Minoukadeh
|
|
||||||
* @copyright 2011-2012 Keyvan Minoukadeh
|
|
||||||
* @license http://www.gnu.org/licenses/agpl-3.0.html AGPL v3
|
|
||||||
*/
|
|
||||||
|
|
||||||
class HumbleHttpAgent
|
|
||||||
{
|
|
||||||
const METHOD_REQUEST_POOL = 1;
|
|
||||||
const METHOD_CURL_MULTI = 2;
|
|
||||||
const METHOD_FILE_GET_CONTENTS = 4;
|
|
||||||
//const UA_BROWSER = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0.1) Gecko/20100101 Firefox/4.0.1';
|
|
||||||
const UA_BROWSER = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.92 Safari/535.2';
|
|
||||||
const UA_PHP = 'PHP/5.2';
|
|
||||||
const REF_GOOGLE = 'http://www.google.co.uk/url?sa=t&source=web&cd=1';
|
|
||||||
|
|
||||||
protected $requests = array();
|
|
||||||
protected $redirectQueue = array();
|
|
||||||
protected $requestOptions;
|
|
||||||
protected $maxParallelRequests = 5;
|
|
||||||
protected $cache = null; //TODO
|
|
||||||
protected $httpContext;
|
|
||||||
protected $minimiseMemoryUse = false; //TODO
|
|
||||||
protected $debug = false;
|
|
||||||
protected $method;
|
|
||||||
protected $cookieJar;
|
|
||||||
public $rewriteHashbangFragment = true; // see http://code.google.com/web/ajaxcrawling/docs/specification.html
|
|
||||||
public $maxRedirects = 5;
|
|
||||||
public $userAgentMap = array();
|
|
||||||
public $rewriteUrls = array();
|
|
||||||
public $userAgentDefault;
|
|
||||||
public $referer;
|
|
||||||
//public $userAgent = 'Mozilla/5.0';
|
|
||||||
|
|
||||||
// Prevent certain file/mime types
|
|
||||||
// HTTP responses which match these content types will
|
|
||||||
// be returned without body.
|
|
||||||
public $headerOnlyTypes = array();
|
|
||||||
// URLs ending with one of these extensions will
|
|
||||||
// prompt Humble HTTP Agent to send a HEAD request first
|
|
||||||
// to see if returned content type matches $headerOnlyTypes.
|
|
||||||
public $headerOnlyClues = array('pdf','mp3','zip','exe','gif','gzip','gz','jpeg','jpg','mpg','mpeg','png','ppt','mov');
|
|
||||||
|
|
||||||
//TODO: set max file size
|
|
||||||
//TODO: normalise headers
|
|
||||||
|
|
||||||
function __construct($requestOptions=null, $method=null) {
|
|
||||||
$this->userAgentDefault = self::UA_BROWSER;
|
|
||||||
$this->referer = self::REF_GOOGLE;
|
|
||||||
// set the request method
|
|
||||||
if (in_array($method, array(1,2,4))) {
|
|
||||||
$this->method = $method;
|
|
||||||
} else {
|
|
||||||
if (class_exists('HttpRequestPool')) {
|
|
||||||
$this->method = self::METHOD_REQUEST_POOL;
|
|
||||||
} elseif (function_exists('curl_multi_init')) {
|
|
||||||
$this->method = self::METHOD_CURL_MULTI;
|
|
||||||
} else {
|
|
||||||
$this->method = self::METHOD_FILE_GET_CONTENTS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($this->method == self::METHOD_CURL_MULTI) {
|
|
||||||
require_once(dirname(__FILE__).'/RollingCurl.php');
|
|
||||||
}
|
|
||||||
// create cookie jar
|
|
||||||
$this->cookieJar = new CookieJar();
|
|
||||||
// set request options (redirect must be 0)
|
|
||||||
$this->requestOptions = array(
|
|
||||||
'timeout' => 15,
|
|
||||||
'redirect' => 0 // we handle redirects manually so we can rewrite the new hashbang URLs that are creeping up over the web
|
|
||||||
// TODO: test onprogress?
|
|
||||||
);
|
|
||||||
if (is_array($requestOptions)) {
|
|
||||||
$this->requestOptions = array_merge($this->requestOptions, $requestOptions);
|
|
||||||
}
|
|
||||||
$this->httpContext = array(
|
|
||||||
'http' => array(
|
|
||||||
'ignore_errors' => true,
|
|
||||||
'timeout' => $this->requestOptions['timeout'],
|
|
||||||
'max_redirects' => $this->requestOptions['redirect'],
|
|
||||||
'header' => "Accept: */*\r\n"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function debug($msg) {
|
|
||||||
if ($this->debug) {
|
|
||||||
$mem = round(memory_get_usage()/1024, 2);
|
|
||||||
$memPeak = round(memory_get_peak_usage()/1024, 2);
|
|
||||||
echo '* ',$msg;
|
|
||||||
echo ' - mem used: ',$mem," (peak: $memPeak)\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getUserAgent($url, $asArray=false) {
|
|
||||||
$host = @parse_url($url, PHP_URL_HOST);
|
|
||||||
if (strtolower(substr($host, 0, 4)) == 'www.') {
|
|
||||||
$host = substr($host, 4);
|
|
||||||
}
|
|
||||||
if ($host) {
|
|
||||||
$try = array($host);
|
|
||||||
$split = explode('.', $host);
|
|
||||||
if (count($split) > 1) {
|
|
||||||
array_shift($split);
|
|
||||||
$try[] = '.'.implode('.', $split);
|
|
||||||
}
|
|
||||||
foreach ($try as $h) {
|
|
||||||
if (isset($this->userAgentMap[$h])) {
|
|
||||||
$ua = $this->userAgentMap[$h];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isset($ua)) $ua = $this->userAgentDefault;
|
|
||||||
if ($asArray) {
|
|
||||||
return array('User-Agent' => $ua);
|
|
||||||
} else {
|
|
||||||
return 'User-Agent: '.$ua;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewriteHashbangFragment($url) {
|
|
||||||
// return $url if there's no '#!'
|
|
||||||
if (strpos($url, '#!') === false) return $url;
|
|
||||||
// split $url and rewrite
|
|
||||||
// TODO: is SimplePie_IRI included?
|
|
||||||
$iri = new SimplePie_IRI($url);
|
|
||||||
$fragment = substr($iri->fragment, 1); // strip '!'
|
|
||||||
$iri->fragment = null;
|
|
||||||
if (isset($iri->query)) {
|
|
||||||
parse_str($iri->query, $query);
|
|
||||||
} else {
|
|
||||||
$query = array();
|
|
||||||
}
|
|
||||||
$query['_escaped_fragment_'] = (string)$fragment;
|
|
||||||
$iri->query = str_replace('%2F', '/', http_build_query($query)); // needed for some sites
|
|
||||||
return $iri->get_iri();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeFragment($url) {
|
|
||||||
$pos = strpos($url, '#');
|
|
||||||
if ($pos === false) {
|
|
||||||
return $url;
|
|
||||||
} else {
|
|
||||||
return substr($url, 0, $pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewriteUrls($url) {
|
|
||||||
foreach ($this->rewriteUrls as $find => $action) {
|
|
||||||
if (strpos($url, $find) !== false) {
|
|
||||||
if (is_array($action)) {
|
|
||||||
return strtr($url, $action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function enableDebug($bool=true) {
|
|
||||||
$this->debug = (bool)$bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function minimiseMemoryUse($bool = true) {
|
|
||||||
$this->minimiseMemoryUse = $bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setMaxParallelRequests($max) {
|
|
||||||
$this->maxParallelRequests = $max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validateUrl($url) {
|
|
||||||
$url = filter_var($url, FILTER_SANITIZE_URL);
|
|
||||||
$test = filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
|
|
||||||
// deal with bug http://bugs.php.net/51192 (present in PHP 5.2.13 and PHP 5.3.2)
|
|
||||||
if ($test === false) {
|
|
||||||
$test = filter_var(strtr($url, '-', '_'), FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED);
|
|
||||||
}
|
|
||||||
if ($test !== false && $test !== null && preg_match('!^https?://!', $url)) {
|
|
||||||
return $url;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fetchAll(array $urls) {
|
|
||||||
$this->fetchAllOnce($urls, $isRedirect=false);
|
|
||||||
$redirects = 0;
|
|
||||||
while (!empty($this->redirectQueue) && ++$redirects <= $this->maxRedirects) {
|
|
||||||
$this->debug("Following redirects #$redirects...");
|
|
||||||
$this->fetchAllOnce($this->redirectQueue, $isRedirect=true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch all URLs without following redirects
|
|
||||||
public function fetchAllOnce(array $urls, $isRedirect=false) {
|
|
||||||
if (!$isRedirect) $urls = array_unique($urls);
|
|
||||||
if (empty($urls)) return;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// parallel (HttpRequestPool)
|
|
||||||
if ($this->method == self::METHOD_REQUEST_POOL) {
|
|
||||||
$this->debug('Starting parallel fetch (HttpRequestPool)');
|
|
||||||
try {
|
|
||||||
while (count($urls) > 0) {
|
|
||||||
$this->debug('Processing set of '.min($this->maxParallelRequests, count($urls)));
|
|
||||||
$subset = array_splice($urls, 0, $this->maxParallelRequests);
|
|
||||||
$pool = new HttpRequestPool();
|
|
||||||
foreach ($subset as $orig => $url) {
|
|
||||||
if (!$isRedirect) $orig = $url;
|
|
||||||
unset($this->redirectQueue[$orig]);
|
|
||||||
$this->debug("...$url");
|
|
||||||
if (!$isRedirect && isset($this->requests[$url])) {
|
|
||||||
$this->debug("......in memory");
|
|
||||||
/*
|
|
||||||
} elseif ($this->isCached($url)) {
|
|
||||||
$this->debug("......is cached");
|
|
||||||
if (!$this->minimiseMemoryUse) {
|
|
||||||
$this->requests[$url] = $this->getCached($url);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
$this->debug("......adding to pool");
|
|
||||||
$req_url = $this->rewriteUrls($url);
|
|
||||||
$req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
|
|
||||||
$req_url = $this->removeFragment($req_url);
|
|
||||||
if (!empty($this->headerOnlyTypes) && !isset($this->requests[$orig]['wrongGuess']) && $this->possibleUnsupportedType($req_url)) {
|
|
||||||
$_meth = HttpRequest::METH_HEAD;
|
|
||||||
} else {
|
|
||||||
$_meth = HttpRequest::METH_GET;
|
|
||||||
unset($this->requests[$orig]['wrongGuess']);
|
|
||||||
}
|
|
||||||
$httpRequest = new HttpRequest($req_url, $_meth, $this->requestOptions);
|
|
||||||
// send cookies, if we have any
|
|
||||||
if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
|
|
||||||
$this->debug("......sending cookies: $cookies");
|
|
||||||
$httpRequest->addHeaders(array('Cookie' => $cookies));
|
|
||||||
}
|
|
||||||
//$httpRequest->addHeaders(array('User-Agent' => $this->userAgent));
|
|
||||||
$httpRequest->addHeaders($this->getUserAgent($req_url, true));
|
|
||||||
// add referer for picky sites
|
|
||||||
$httpRequest->addheaders(array('Referer' => $this->referer));
|
|
||||||
$this->requests[$orig] = array('headers'=>null, 'body'=>null, 'httpRequest'=>$httpRequest);
|
|
||||||
$this->requests[$orig]['original_url'] = $orig;
|
|
||||||
$pool->attach($httpRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// did we get anything into the pool?
|
|
||||||
if (count($pool) > 0) {
|
|
||||||
$this->debug('Sending request...');
|
|
||||||
try {
|
|
||||||
$pool->send();
|
|
||||||
} catch (HttpRequestPoolException $e) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
$this->debug('Received responses');
|
|
||||||
foreach($subset as $orig => $url) {
|
|
||||||
if (!$isRedirect) $orig = $url;
|
|
||||||
$request = $this->requests[$orig]['httpRequest'];
|
|
||||||
//$this->requests[$orig]['headers'] = $this->headersToString($request->getResponseHeader());
|
|
||||||
// getResponseHeader() doesn't return status line, so, for consistency...
|
|
||||||
$this->requests[$orig]['headers'] = substr($request->getRawResponseMessage(), 0, $request->getResponseInfo('header_size'));
|
|
||||||
// check content type
|
|
||||||
// TODO: use getResponseHeader('content-type') or getResponseInfo()
|
|
||||||
if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
|
|
||||||
$this->requests[$orig]['body'] = '';
|
|
||||||
$_header_only_type = true;
|
|
||||||
$this->debug('Header only type returned');
|
|
||||||
} else {
|
|
||||||
$this->requests[$orig]['body'] = $request->getResponseBody();
|
|
||||||
$_header_only_type = false;
|
|
||||||
}
|
|
||||||
$this->requests[$orig]['effective_url'] = $request->getResponseInfo('effective_url');
|
|
||||||
$this->requests[$orig]['status_code'] = $status_code = $request->getResponseCode();
|
|
||||||
// is redirect?
|
|
||||||
if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && $request->getResponseHeader('location')) {
|
|
||||||
$redirectURL = $request->getResponseHeader('location');
|
|
||||||
if (!preg_match('!^https?://!i', $redirectURL)) {
|
|
||||||
$redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
|
|
||||||
}
|
|
||||||
if ($this->validateURL($redirectURL)) {
|
|
||||||
$this->debug('Redirect detected. Valid URL: '.$redirectURL);
|
|
||||||
// store any cookies
|
|
||||||
$cookies = $request->getResponseHeader('set-cookie');
|
|
||||||
if ($cookies && !is_array($cookies)) $cookies = array($cookies);
|
|
||||||
if ($cookies) $this->cookieJar->storeCookies($url, $cookies);
|
|
||||||
$this->redirectQueue[$orig] = $redirectURL;
|
|
||||||
} else {
|
|
||||||
$this->debug('Redirect detected. Invalid URL: '.$redirectURL);
|
|
||||||
}
|
|
||||||
} elseif (!$_header_only_type && $request->getMethod() === HttpRequest::METH_HEAD) {
|
|
||||||
// the response content-type did not match our 'header only' types,
|
|
||||||
// but we'd issues a HEAD request because we assumed it would. So
|
|
||||||
// let's queue a proper GET request for this item...
|
|
||||||
$this->debug('Wrong guess at content-type, queing GET request');
|
|
||||||
$this->requests[$orig]['wrongGuess'] = true;
|
|
||||||
$this->redirectQueue[$orig] = $this->requests[$orig]['effective_url'];
|
|
||||||
}
|
|
||||||
//die($url.' -multi- '.$request->getResponseInfo('effective_url'));
|
|
||||||
$pool->detach($request);
|
|
||||||
unset($this->requests[$orig]['httpRequest'], $request);
|
|
||||||
/*
|
|
||||||
if ($this->minimiseMemoryUse) {
|
|
||||||
if ($this->cache($url)) {
|
|
||||||
unset($this->requests[$url]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (HttpException $e) {
|
|
||||||
$this->debug($e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
|
||||||
// parallel (curl_multi_*)
|
|
||||||
elseif ($this->method == self::METHOD_CURL_MULTI) {
|
|
||||||
$this->debug('Starting parallel fetch (curl_multi_*)');
|
|
||||||
while (count($urls) > 0) {
|
|
||||||
$this->debug('Processing set of '.min($this->maxParallelRequests, count($urls)));
|
|
||||||
$subset = array_splice($urls, 0, $this->maxParallelRequests);
|
|
||||||
$pool = new RollingCurl(array($this, 'handleCurlResponse'));
|
|
||||||
$pool->window_size = count($subset);
|
|
||||||
|
|
||||||
foreach ($subset as $orig => $url) {
|
|
||||||
if (!$isRedirect) $orig = $url;
|
|
||||||
unset($this->redirectQueue[$orig]);
|
|
||||||
$this->debug("...$url");
|
|
||||||
if (!$isRedirect && isset($this->requests[$url])) {
|
|
||||||
$this->debug("......in memory");
|
|
||||||
/*
|
|
||||||
} elseif ($this->isCached($url)) {
|
|
||||||
$this->debug("......is cached");
|
|
||||||
if (!$this->minimiseMemoryUse) {
|
|
||||||
$this->requests[$url] = $this->getCached($url);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
$this->debug("......adding to pool");
|
|
||||||
$req_url = $this->rewriteUrls($url);
|
|
||||||
$req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
|
|
||||||
$req_url = $this->removeFragment($req_url);
|
|
||||||
if (!empty($this->headerOnlyTypes) && !isset($this->requests[$orig]['wrongGuess']) && $this->possibleUnsupportedType($req_url)) {
|
|
||||||
$_meth = 'HEAD';
|
|
||||||
} else {
|
|
||||||
$_meth = 'GET';
|
|
||||||
unset($this->requests[$orig]['wrongGuess']);
|
|
||||||
}
|
|
||||||
$headers = array();
|
|
||||||
//$headers[] = 'User-Agent: '.$this->userAgent;
|
|
||||||
$headers[] = $this->getUserAgent($req_url);
|
|
||||||
// add referer for picky sites
|
|
||||||
$headers[] = 'Referer: '.$this->referer;
|
|
||||||
// send cookies, if we have any
|
|
||||||
if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
|
|
||||||
$this->debug("......sending cookies: $cookies");
|
|
||||||
$headers[] = 'Cookie: '.$cookies;
|
|
||||||
}
|
|
||||||
$httpRequest = new RollingCurlRequest($req_url, $_meth, null, $headers, array(
|
|
||||||
CURLOPT_CONNECTTIMEOUT => $this->requestOptions['timeout'],
|
|
||||||
CURLOPT_TIMEOUT => $this->requestOptions['timeout']
|
|
||||||
));
|
|
||||||
$httpRequest->set_original_url($orig);
|
|
||||||
$this->requests[$orig] = array('headers'=>null, 'body'=>null, 'httpRequest'=>$httpRequest);
|
|
||||||
$this->requests[$orig]['original_url'] = $orig; // TODO: is this needed anymore?
|
|
||||||
$pool->add($httpRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// did we get anything into the pool?
|
|
||||||
if (count($pool) > 0) {
|
|
||||||
$this->debug('Sending request...');
|
|
||||||
$pool->execute(); // this will call handleCurlResponse() and populate $this->requests[$orig]
|
|
||||||
$this->debug('Received responses');
|
|
||||||
foreach($subset as $orig => $url) {
|
|
||||||
if (!$isRedirect) $orig = $url;
|
|
||||||
// $this->requests[$orig]['headers']
|
|
||||||
// $this->requests[$orig]['body']
|
|
||||||
// $this->requests[$orig]['effective_url']
|
|
||||||
// check content type
|
|
||||||
if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
|
|
||||||
$this->requests[$orig]['body'] = '';
|
|
||||||
$_header_only_type = true;
|
|
||||||
$this->debug('Header only type returned');
|
|
||||||
} else {
|
|
||||||
$_header_only_type = false;
|
|
||||||
}
|
|
||||||
$status_code = $this->requests[$orig]['status_code'];
|
|
||||||
if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && isset($this->requests[$orig]['location'])) {
|
|
||||||
$redirectURL = $this->requests[$orig]['location'];
|
|
||||||
if (!preg_match('!^https?://!i', $redirectURL)) {
|
|
||||||
$redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
|
|
||||||
}
|
|
||||||
if ($this->validateURL($redirectURL)) {
|
|
||||||
$this->debug('Redirect detected. Valid URL: '.$redirectURL);
|
|
||||||
// store any cookies
|
|
||||||
$cookies = $this->cookieJar->extractCookies($this->requests[$orig]['headers']);
|
|
||||||
if (!empty($cookies)) $this->cookieJar->storeCookies($url, $cookies);
|
|
||||||
$this->redirectQueue[$orig] = $redirectURL;
|
|
||||||
} else {
|
|
||||||
$this->debug('Redirect detected. Invalid URL: '.$redirectURL);
|
|
||||||
}
|
|
||||||
} elseif (!$_header_only_type && $this->requests[$orig]['method'] == 'HEAD') {
|
|
||||||
// the response content-type did not match our 'header only' types,
|
|
||||||
// but we'd issues a HEAD request because we assumed it would. So
|
|
||||||
// let's queue a proper GET request for this item...
|
|
||||||
$this->debug('Wrong guess at content-type, queing GET request');
|
|
||||||
$this->requests[$orig]['wrongGuess'] = true;
|
|
||||||
$this->redirectQueue[$orig] = $this->requests[$orig]['effective_url'];
|
|
||||||
}
|
|
||||||
// die($url.' -multi- '.$request->getResponseInfo('effective_url'));
|
|
||||||
unset($this->requests[$orig]['httpRequest'], $this->requests[$orig]['method']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////
|
|
||||||
// sequential (file_get_contents)
|
|
||||||
else {
|
|
||||||
$this->debug('Starting sequential fetch (file_get_contents)');
|
|
||||||
$this->debug('Processing set of '.count($urls));
|
|
||||||
foreach ($urls as $orig => $url) {
|
|
||||||
if (!$isRedirect) $orig = $url;
|
|
||||||
unset($this->redirectQueue[$orig]);
|
|
||||||
$this->debug("...$url");
|
|
||||||
if (!$isRedirect && isset($this->requests[$url])) {
|
|
||||||
$this->debug("......in memory");
|
|
||||||
/*
|
|
||||||
} elseif ($this->isCached($url)) {
|
|
||||||
$this->debug("......is cached");
|
|
||||||
if (!$this->minimiseMemoryUse) {
|
|
||||||
$this->requests[$url] = $this->getCached($url);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
$this->debug("Sending request for $url");
|
|
||||||
$this->requests[$orig]['original_url'] = $orig;
|
|
||||||
$req_url = $this->rewriteUrls($url);
|
|
||||||
$req_url = ($this->rewriteHashbangFragment) ? $this->rewriteHashbangFragment($req_url) : $req_url;
|
|
||||||
$req_url = $this->removeFragment($req_url);
|
|
||||||
// send cookies, if we have any
|
|
||||||
$httpContext = $this->httpContext;
|
|
||||||
$httpContext['http']['header'] .= $this->getUserAgent($req_url)."\r\n";
|
|
||||||
// add referer for picky sites
|
|
||||||
$httpContext['http']['header'] .= 'Referer: '.$this->referer."\r\n";
|
|
||||||
if ($cookies = $this->cookieJar->getMatchingCookies($req_url)) {
|
|
||||||
$this->debug("......sending cookies: $cookies");
|
|
||||||
$httpContext['http']['header'] .= 'Cookie: '.$cookies."\r\n";
|
|
||||||
}
|
|
||||||
if (false !== ($html = @file_get_contents($req_url, false, stream_context_create($httpContext)))) {
|
|
||||||
$this->debug('Received response');
|
|
||||||
// get status code
|
|
||||||
if (!isset($http_response_header[0]) || !preg_match('!^HTTP/\d+\.\d+\s+(\d+)!', trim($http_response_header[0]), $match)) {
|
|
||||||
$this->debug('Error: no status code found');
|
|
||||||
// TODO: handle error - no status code
|
|
||||||
} else {
|
|
||||||
$this->requests[$orig]['headers'] = $this->headersToString($http_response_header, false);
|
|
||||||
// check content type
|
|
||||||
if ($this->headerOnlyType($this->requests[$orig]['headers'])) {
|
|
||||||
$this->requests[$orig]['body'] = '';
|
|
||||||
} else {
|
|
||||||
$this->requests[$orig]['body'] = $html;
|
|
||||||
}
|
|
||||||
$this->requests[$orig]['effective_url'] = $req_url;
|
|
||||||
$this->requests[$orig]['status_code'] = $status_code = (int)$match[1];
|
|
||||||
unset($match);
|
|
||||||
// handle redirect
|
|
||||||
if (preg_match('/^Location:(.*?)$/m', $this->requests[$orig]['headers'], $match)) {
|
|
||||||
$this->requests[$orig]['location'] = trim($match[1]);
|
|
||||||
}
|
|
||||||
if ((in_array($status_code, array(300, 301, 302, 303, 307)) || $status_code > 307 && $status_code < 400) && isset($this->requests[$orig]['location'])) {
|
|
||||||
$redirectURL = $this->requests[$orig]['location'];
|
|
||||||
if (!preg_match('!^https?://!i', $redirectURL)) {
|
|
||||||
$redirectURL = SimplePie_Misc::absolutize_url($redirectURL, $url);
|
|
||||||
}
|
|
||||||
if ($this->validateURL($redirectURL)) {
|
|
||||||
$this->debug('Redirect detected. Valid URL: '.$redirectURL);
|
|
||||||
// store any cookies
|
|
||||||
$cookies = $this->cookieJar->extractCookies($this->requests[$orig]['headers']);
|
|
||||||
if (!empty($cookies)) $this->cookieJar->storeCookies($url, $cookies);
|
|
||||||
$this->redirectQueue[$orig] = $redirectURL;
|
|
||||||
} else {
|
|
||||||
$this->debug('Redirect detected. Invalid URL: '.$redirectURL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->debug('Error retrieving URL');
|
|
||||||
//print_r($req_url);
|
|
||||||
//print_r($http_response_header);
|
|
||||||
//print_r($html);
|
|
||||||
|
|
||||||
// TODO: handle error - failed to retrieve URL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleCurlResponse($response, $info, $request) {
|
|
||||||
$orig = $request->url_original;
|
|
||||||
$this->requests[$orig]['headers'] = substr($response, 0, $info['header_size']);
|
|
||||||
$this->requests[$orig]['body'] = substr($response, $info['header_size']);
|
|
||||||
$this->requests[$orig]['method'] = $request->method;
|
|
||||||
$this->requests[$orig]['effective_url'] = $info['url'];
|
|
||||||
$this->requests[$orig]['status_code'] = (int)$info['http_code'];
|
|
||||||
if (preg_match('/^Location:(.*?)$/m', $this->requests[$orig]['headers'], $match)) {
|
|
||||||
$this->requests[$orig]['location'] = trim($match[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function headersToString(array $headers, $associative=true) {
|
|
||||||
if (!$associative) {
|
|
||||||
return implode("\n", $headers);
|
|
||||||
} else {
|
|
||||||
$str = '';
|
|
||||||
foreach ($headers as $key => $val) {
|
|
||||||
if (is_array($val)) {
|
|
||||||
foreach ($val as $v) $str .= "$key: $v\n";
|
|
||||||
} else {
|
|
||||||
$str .= "$key: $val\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rtrim($str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get($url, $remove=false, $gzdecode=true) {
|
|
||||||
$url = "$url";
|
|
||||||
if (isset($this->requests[$url]) && isset($this->requests[$url]['body'])) {
|
|
||||||
$this->debug("URL already fetched - in memory ($url, effective: {$this->requests[$url]['effective_url']})");
|
|
||||||
$response = $this->requests[$url];
|
|
||||||
/*
|
|
||||||
} elseif ($this->isCached($url)) {
|
|
||||||
$this->debug("URL already fetched - in disk cache ($url)");
|
|
||||||
$response = $this->getCached($url);
|
|
||||||
$this->requests[$url] = $response;
|
|
||||||
*/
|
|
||||||
} else {
|
|
||||||
$this->debug("Fetching URL ($url)");
|
|
||||||
$this->fetchAll(array($url));
|
|
||||||
if (isset($this->requests[$url]) && isset($this->requests[$url]['body'])) {
|
|
||||||
$response = $this->requests[$url];
|
|
||||||
} else {
|
|
||||||
$this->debug("Request failed");
|
|
||||||
$response = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if ($this->minimiseMemoryUse && $response) {
|
|
||||||
$this->cache($url);
|
|
||||||
unset($this->requests[$url]);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if ($remove && $response) unset($this->requests[$url]);
|
|
||||||
if ($gzdecode && stripos($response['headers'], 'Content-Encoding: gzip')) {
|
|
||||||
if ($html = gzdecode($response['body'])) {
|
|
||||||
$response['body'] = $html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function parallelSupport() {
|
|
||||||
return class_exists('HttpRequestPool') || function_exists('curl_multi_init');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function headerOnlyType($headers) {
|
|
||||||
if (preg_match('!^Content-Type:\s*(([a-z-]+)/([^;\r\n ]+))!im', $headers, $match)) {
|
|
||||||
// look for full mime type (e.g. image/jpeg) or just type (e.g. image)
|
|
||||||
$match[1] = strtolower(trim($match[1]));
|
|
||||||
$match[2] = strtolower(trim($match[2]));
|
|
||||||
foreach (array($match[1], $match[2]) as $mime) {
|
|
||||||
if (in_array($mime, $this->headerOnlyTypes)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function possibleUnsupportedType($url) {
|
|
||||||
$path = @parse_url($url, PHP_URL_PATH);
|
|
||||||
if ($path && strpos($path, '.') !== false) {
|
|
||||||
$ext = strtolower(trim(pathinfo($path, PATHINFO_EXTENSION)));
|
|
||||||
return in_array($ext, $this->headerOnlyClues);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// gzdecode from http://www.php.net/manual/en/function.gzdecode.php#82930
|
|
||||||
if (!function_exists('gzdecode')) {
|
|
||||||
function gzdecode($data,&$filename='',&$error='',$maxlength=null)
|
|
||||||
{
|
|
||||||
$len = strlen($data);
|
|
||||||
if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
|
|
||||||
$error = "Not in GZIP format.";
|
|
||||||
return null; // Not GZIP format (See RFC 1952)
|
|
||||||
}
|
|
||||||
$method = ord(substr($data,2,1)); // Compression method
|
|
||||||
$flags = ord(substr($data,3,1)); // Flags
|
|
||||||
if ($flags & 31 != $flags) {
|
|
||||||
$error = "Reserved bits not allowed.";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// NOTE: $mtime may be negative (PHP integer limitations)
|
|
||||||
$mtime = unpack("V", substr($data,4,4));
|
|
||||||
$mtime = $mtime[1];
|
|
||||||
$xfl = substr($data,8,1);
|
|
||||||
$os = substr($data,8,1);
|
|
||||||
$headerlen = 10;
|
|
||||||
$extralen = 0;
|
|
||||||
$extra = "";
|
|
||||||
if ($flags & 4) {
|
|
||||||
// 2-byte length prefixed EXTRA data in header
|
|
||||||
if ($len - $headerlen - 2 < 8) {
|
|
||||||
return false; // invalid
|
|
||||||
}
|
|
||||||
$extralen = unpack("v",substr($data,8,2));
|
|
||||||
$extralen = $extralen[1];
|
|
||||||
if ($len - $headerlen - 2 - $extralen < 8) {
|
|
||||||
return false; // invalid
|
|
||||||
}
|
|
||||||
$extra = substr($data,10,$extralen);
|
|
||||||
$headerlen += 2 + $extralen;
|
|
||||||
}
|
|
||||||
$filenamelen = 0;
|
|
||||||
$filename = "";
|
|
||||||
if ($flags & 8) {
|
|
||||||
// C-style string
|
|
||||||
if ($len - $headerlen - 1 < 8) {
|
|
||||||
return false; // invalid
|
|
||||||
}
|
|
||||||
$filenamelen = strpos(substr($data,$headerlen),chr(0));
|
|
||||||
if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
|
|
||||||
return false; // invalid
|
|
||||||
}
|
|
||||||
$filename = substr($data,$headerlen,$filenamelen);
|
|
||||||
$headerlen += $filenamelen + 1;
|
|
||||||
}
|
|
||||||
$commentlen = 0;
|
|
||||||
$comment = "";
|
|
||||||
if ($flags & 16) {
|
|
||||||
// C-style string COMMENT data in header
|
|
||||||
if ($len - $headerlen - 1 < 8) {
|
|
||||||
return false; // invalid
|
|
||||||
}
|
|
||||||
$commentlen = strpos(substr($data,$headerlen),chr(0));
|
|
||||||
if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
|
|
||||||
return false; // Invalid header format
|
|
||||||
}
|
|
||||||
$comment = substr($data,$headerlen,$commentlen);
|
|
||||||
$headerlen += $commentlen + 1;
|
|
||||||
}
|
|
||||||
$headercrc = "";
|
|
||||||
if ($flags & 2) {
|
|
||||||
// 2-bytes (lowest order) of CRC32 on header present
|
|
||||||
if ($len - $headerlen - 2 < 8) {
|
|
||||||
return false; // invalid
|
|
||||||
}
|
|
||||||
$calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
|
|
||||||
$headercrc = unpack("v", substr($data,$headerlen,2));
|
|
||||||
$headercrc = $headercrc[1];
|
|
||||||
if ($headercrc != $calccrc) {
|
|
||||||
$error = "Header checksum failed.";
|
|
||||||
return false; // Bad header CRC
|
|
||||||
}
|
|
||||||
$headerlen += 2;
|
|
||||||
}
|
|
||||||
// GZIP FOOTER
|
|
||||||
$datacrc = unpack("V",substr($data,-8,4));
|
|
||||||
$datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF);
|
|
||||||
$isize = unpack("V",substr($data,-4));
|
|
||||||
$isize = $isize[1];
|
|
||||||
// decompression:
|
|
||||||
$bodylen = $len-$headerlen-8;
|
|
||||||
if ($bodylen < 1) {
|
|
||||||
// IMPLEMENTATION BUG!
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$body = substr($data,$headerlen,$bodylen);
|
|
||||||
$data = "";
|
|
||||||
if ($bodylen > 0) {
|
|
||||||
switch ($method) {
|
|
||||||
case 8:
|
|
||||||
// Currently the only supported compression method:
|
|
||||||
$data = gzinflate($body,$maxlength);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$error = "Unknown compression method.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} // zero-byte body content is allowed
|
|
||||||
// Verifiy CRC32
|
|
||||||
$crc = sprintf("%u",crc32($data));
|
|
||||||
$crcOK = $crc == $datacrc;
|
|
||||||
$lenOK = $isize == strlen($data);
|
|
||||||
if (!$lenOK || !$crcOK) {
|
|
||||||
$error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
402
inc/3rdparty/humble-http-agent/RollingCurl.php
vendored
@ -1,402 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
Authored by Josh Fraser (www.joshfraser.com)
|
|
||||||
Released under Apache License 2.0
|
|
||||||
|
|
||||||
Maintained by Alexander Makarov, http://rmcreative.ru/
|
|
||||||
|
|
||||||
Modified by Keyvan Minoukadeh for the Five Filters project: http://fivefilters.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that represent a single curl request
|
|
||||||
*/
|
|
||||||
class RollingCurlRequest {
|
|
||||||
public $url = false;
|
|
||||||
public $url_original = false; // used for tracking redirects
|
|
||||||
public $method = 'GET';
|
|
||||||
public $post_data = null;
|
|
||||||
public $headers = null;
|
|
||||||
public $options = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $url
|
|
||||||
* @param string $method
|
|
||||||
* @param $post_data
|
|
||||||
* @param $headers
|
|
||||||
* @param $options
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
|
|
||||||
$this->url = $url;
|
|
||||||
$this->url_original = $url;
|
|
||||||
$this->method = $method;
|
|
||||||
$this->post_data = $post_data;
|
|
||||||
$this->headers = $headers;
|
|
||||||
$this->options = $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $url
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function set_original_url($url) {
|
|
||||||
$this->url_original = $url;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __destruct() {
|
|
||||||
unset($this->url, $this->url_original, $this->method, $this->post_data, $this->headers, $this->options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RollingCurl custom exception
|
|
||||||
*/
|
|
||||||
class RollingCurlException extends Exception {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class that holds a rolling queue of curl requests.
|
|
||||||
*
|
|
||||||
* @throws RollingCurlException
|
|
||||||
*/
|
|
||||||
class RollingCurl implements Countable {
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*
|
|
||||||
* Window size is the max number of simultaneous connections allowed.
|
|
||||||
*
|
|
||||||
* REMEMBER TO RESPECT THE SERVERS:
|
|
||||||
* Sending too many requests at one time can easily be perceived
|
|
||||||
* as a DOS attack. Increase this window_size if you are making requests
|
|
||||||
* to multiple servers or have permission from the receving server admins.
|
|
||||||
*/
|
|
||||||
private $window_size = 5;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var float
|
|
||||||
*
|
|
||||||
* Timeout is the timeout used for curl_multi_select.
|
|
||||||
*/
|
|
||||||
private $timeout = 10;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string|array
|
|
||||||
*
|
|
||||||
* Callback function to be applied to each result.
|
|
||||||
*/
|
|
||||||
private $callback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*
|
|
||||||
* Set your base options that you want to be used with EVERY request.
|
|
||||||
*/
|
|
||||||
protected $options = array(
|
|
||||||
CURLOPT_SSL_VERIFYPEER => 0,
|
|
||||||
CURLOPT_RETURNTRANSFER => 1,
|
|
||||||
CURLOPT_CONNECTTIMEOUT => 30,
|
|
||||||
CURLOPT_TIMEOUT => 30
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $headers = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Request[]
|
|
||||||
*
|
|
||||||
* The request queue
|
|
||||||
*/
|
|
||||||
private $requests = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var RequestMap[]
|
|
||||||
*
|
|
||||||
* Maps handles to request indexes
|
|
||||||
*/
|
|
||||||
private $requestMap = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $callback
|
|
||||||
* Callback function to be applied to each result.
|
|
||||||
*
|
|
||||||
* Can be specified as 'my_callback_function'
|
|
||||||
* or array($object, 'my_callback_method').
|
|
||||||
*
|
|
||||||
* Function should take three parameters: $response, $info, $request.
|
|
||||||
* $response is response body, $info is additional curl info.
|
|
||||||
* $request is the original request
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function __construct($callback = null) {
|
|
||||||
$this->callback = $callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function __get($name) {
|
|
||||||
return (isset($this->{$name})) ? $this->{$name} : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
* @param mixed $value
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function __set($name, $value) {
|
|
||||||
// append the base options & headers
|
|
||||||
if ($name == "options" || $name == "headers") {
|
|
||||||
$this->{$name} = $value + $this->{$name};
|
|
||||||
} else {
|
|
||||||
$this->{$name} = $value;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Count number of requests added (Countable interface)
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function count() {
|
|
||||||
return count($this->requests);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a request to the request queue
|
|
||||||
*
|
|
||||||
* @param Request $request
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function add($request) {
|
|
||||||
$this->requests[] = $request;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new Request and add it to the request queue
|
|
||||||
*
|
|
||||||
* @param string $url
|
|
||||||
* @param string $method
|
|
||||||
* @param $post_data
|
|
||||||
* @param $headers
|
|
||||||
* @param $options
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
|
|
||||||
$this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform GET request
|
|
||||||
*
|
|
||||||
* @param string $url
|
|
||||||
* @param $headers
|
|
||||||
* @param $options
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function get($url, $headers = null, $options = null) {
|
|
||||||
return $this->request($url, "GET", null, $headers, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform POST request
|
|
||||||
*
|
|
||||||
* @param string $url
|
|
||||||
* @param $post_data
|
|
||||||
* @param $headers
|
|
||||||
* @param $options
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function post($url, $post_data = null, $headers = null, $options = null) {
|
|
||||||
return $this->request($url, "POST", $post_data, $headers, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute processing
|
|
||||||
*
|
|
||||||
* @param int $window_size Max number of simultaneous connections
|
|
||||||
* @return string|bool
|
|
||||||
*/
|
|
||||||
public function execute($window_size = null) {
|
|
||||||
// rolling curl window must always be greater than 1
|
|
||||||
if (sizeof($this->requests) == 1) {
|
|
||||||
return $this->single_curl();
|
|
||||||
} else {
|
|
||||||
// start the rolling curl. window_size is the max number of simultaneous connections
|
|
||||||
return $this->rolling_curl($window_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a single curl request
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function single_curl() {
|
|
||||||
$ch = curl_init();
|
|
||||||
$request = array_shift($this->requests);
|
|
||||||
$options = $this->get_options($request);
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
$output = curl_exec($ch);
|
|
||||||
$info = curl_getinfo($ch);
|
|
||||||
|
|
||||||
// it's not neccesary to set a callback for one-off requests
|
|
||||||
if ($this->callback) {
|
|
||||||
$callback = $this->callback;
|
|
||||||
if (is_callable($this->callback)) {
|
|
||||||
call_user_func($callback, $output, $info, $request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return $output;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs multiple curl requests
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @throws RollingCurlException
|
|
||||||
* @param int $window_size Max number of simultaneous connections
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function rolling_curl($window_size = null) {
|
|
||||||
if ($window_size)
|
|
||||||
$this->window_size = $window_size;
|
|
||||||
|
|
||||||
// make sure the rolling window isn't greater than the # of urls
|
|
||||||
if (sizeof($this->requests) < $this->window_size)
|
|
||||||
$this->window_size = sizeof($this->requests);
|
|
||||||
|
|
||||||
if ($this->window_size < 2) {
|
|
||||||
throw new RollingCurlException("Window size must be greater than 1");
|
|
||||||
}
|
|
||||||
|
|
||||||
$master = curl_multi_init();
|
|
||||||
|
|
||||||
// start the first batch of requests
|
|
||||||
for ($i = 0; $i < $this->window_size; $i++) {
|
|
||||||
$ch = curl_init();
|
|
||||||
|
|
||||||
$options = $this->get_options($this->requests[$i]);
|
|
||||||
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
curl_multi_add_handle($master, $ch);
|
|
||||||
|
|
||||||
// Add to our request Maps
|
|
||||||
$key = (string) $ch;
|
|
||||||
$this->requestMap[$key] = $i;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
|
|
||||||
if ($execrun != CURLM_OK)
|
|
||||||
break;
|
|
||||||
// a request was just completed -- find out which one
|
|
||||||
while ($done = curl_multi_info_read($master)) {
|
|
||||||
|
|
||||||
// get the info and content returned on the request
|
|
||||||
$info = curl_getinfo($done['handle']);
|
|
||||||
$output = curl_multi_getcontent($done['handle']);
|
|
||||||
|
|
||||||
// send the return values to the callback function.
|
|
||||||
$callback = $this->callback;
|
|
||||||
if (is_callable($callback)) {
|
|
||||||
$key = (string) $done['handle'];
|
|
||||||
$request = $this->requests[$this->requestMap[$key]];
|
|
||||||
unset($this->requestMap[$key]);
|
|
||||||
call_user_func($callback, $output, $info, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
// start a new request (it's important to do this before removing the old one)
|
|
||||||
if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
|
|
||||||
$ch = curl_init();
|
|
||||||
$options = $this->get_options($this->requests[$i]);
|
|
||||||
curl_setopt_array($ch, $options);
|
|
||||||
curl_multi_add_handle($master, $ch);
|
|
||||||
|
|
||||||
// Add to our request Maps
|
|
||||||
$key = (string) $ch;
|
|
||||||
$this->requestMap[$key] = $i;
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the curl handle that just completed
|
|
||||||
curl_multi_remove_handle($master, $done['handle']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block for data in / output; error handling is done by curl_multi_exec
|
|
||||||
//if ($running) curl_multi_select($master, $this->timeout);
|
|
||||||
// removing timeout as it causes problems on Windows with PHP 5.3.5 and Curl 7.20.0
|
|
||||||
if ($running) curl_multi_select($master);
|
|
||||||
|
|
||||||
} while ($running);
|
|
||||||
curl_multi_close($master);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to set up a new request by setting the appropriate options
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param Request $request
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function get_options($request) {
|
|
||||||
// options for this entire curl object
|
|
||||||
$options = $this->__get('options');
|
|
||||||
// We're managing reirects in PHP - allows us to intervene and rewrite/block URLs
|
|
||||||
// before the next request goes out.
|
|
||||||
$options[CURLOPT_FOLLOWLOCATION] = 0;
|
|
||||||
$options[CURLOPT_MAXREDIRS] = 0;
|
|
||||||
//if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
|
|
||||||
// $options[CURLOPT_FOLLOWLOCATION] = 1;
|
|
||||||
// $options[CURLOPT_MAXREDIRS] = 5;
|
|
||||||
//}
|
|
||||||
$headers = $this->__get('headers');
|
|
||||||
// append custom headers for this specific request
|
|
||||||
if ($request->headers) {
|
|
||||||
$headers = $headers + $request->headers;
|
|
||||||
}
|
|
||||||
|
|
||||||
// append custom options for this specific request
|
|
||||||
if ($request->options) {
|
|
||||||
$options = $request->options + $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the request URL
|
|
||||||
$options[CURLOPT_URL] = $request->url;
|
|
||||||
|
|
||||||
if ($headers) {
|
|
||||||
$options[CURLOPT_HTTPHEADER] = $headers;
|
|
||||||
}
|
|
||||||
// return response headers
|
|
||||||
$options[CURLOPT_HEADER] = 1;
|
|
||||||
|
|
||||||
// send HEAD request?
|
|
||||||
if ($request->method == 'HEAD') {
|
|
||||||
$options[CURLOPT_NOBODY] = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __destruct() {
|
|
||||||
unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Humble HTTP Agent extension for SimplePie_File
|
|
||||||
*
|
|
||||||
* This class is designed to extend and override SimplePie_File
|
|
||||||
* in order to prevent duplicate HTTP requests being sent out.
|
|
||||||
* The idea is to initialise an instance of Humble HTTP Agent
|
|
||||||
* and attach it, to a static class variable, of this class.
|
|
||||||
* SimplePie will then automatically initialise this class
|
|
||||||
*
|
|
||||||
* @date 2011-02-28
|
|
||||||
*/
|
|
||||||
|
|
||||||
class SimplePie_HumbleHttpAgent extends SimplePie_File
|
|
||||||
{
|
|
||||||
protected static $agent;
|
|
||||||
var $url;
|
|
||||||
var $useragent;
|
|
||||||
var $success = true;
|
|
||||||
var $headers = array();
|
|
||||||
var $body;
|
|
||||||
var $status_code;
|
|
||||||
var $redirects = 0;
|
|
||||||
var $error;
|
|
||||||
var $method = SIMPLEPIE_FILE_SOURCE_NONE;
|
|
||||||
|
|
||||||
public static function set_agent(HumbleHttpAgent $agent) {
|
|
||||||
self::$agent = $agent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __construct($url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false) {
|
|
||||||
if (class_exists('idna_convert'))
|
|
||||||
{
|
|
||||||
$idn = new idna_convert();
|
|
||||||
$parsed = SimplePie_Misc::parse_url($url);
|
|
||||||
$url = SimplePie_Misc::compress_parse_url($parsed['scheme'], $idn->encode($parsed['authority']), $parsed['path'], $parsed['query'], $parsed['fragment']);
|
|
||||||
}
|
|
||||||
$this->url = $url;
|
|
||||||
$this->useragent = $useragent;
|
|
||||||
if (preg_match('/^http(s)?:\/\//i', $url))
|
|
||||||
{
|
|
||||||
if (!is_array($headers))
|
|
||||||
{
|
|
||||||
$headers = array();
|
|
||||||
}
|
|
||||||
$this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
|
|
||||||
$headers2 = array();
|
|
||||||
foreach ($headers as $key => $value) {
|
|
||||||
$headers2[] = "$key: $value";
|
|
||||||
}
|
|
||||||
//TODO: allow for HTTP headers
|
|
||||||
// curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
|
|
||||||
|
|
||||||
$response = self::$agent->get($url);
|
|
||||||
|
|
||||||
if ($response === false || !isset($response['status_code'])) {
|
|
||||||
$this->error = 'failed to fetch URL';
|
|
||||||
$this->success = false;
|
|
||||||
} else {
|
|
||||||
// The extra lines at the end are there to satisfy SimplePie's HTTP parser.
|
|
||||||
// The class expects a full HTTP message, whereas we're giving it only
|
|
||||||
// headers - the new lines indicate the start of the body.
|
|
||||||
$parser = new SimplePie_HTTP_Parser($response['headers']."\r\n\r\n");
|
|
||||||
if ($parser->parse()) {
|
|
||||||
$this->headers = $parser->headers;
|
|
||||||
//$this->body = $parser->body;
|
|
||||||
$this->body = $response['body'];
|
|
||||||
$this->status_code = $parser->status_code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->error = 'invalid URL';
|
|
||||||
$this->success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
202
inc/3rdparty/paginator.php
vendored
@ -1,202 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* PHP Pagination Class
|
|
||||||
*
|
|
||||||
* @author David Carr - dave@daveismyname.com - http://www.daveismyname.com
|
|
||||||
* @version 1.0
|
|
||||||
* @date October 20, 2013
|
|
||||||
*/
|
|
||||||
class Paginator{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the number of items per page.
|
|
||||||
*
|
|
||||||
* @var numeric
|
|
||||||
*/
|
|
||||||
private $_perPage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set get parameter for fetching the page number
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $_instance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sets the page number.
|
|
||||||
*
|
|
||||||
* @var numeric
|
|
||||||
*/
|
|
||||||
private $_page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the limit for the data source
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private $_limit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set the total number of records/items.
|
|
||||||
*
|
|
||||||
* @var numeric
|
|
||||||
*/
|
|
||||||
private $_totalRows = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __construct
|
|
||||||
*
|
|
||||||
* pass values when class is istantiated
|
|
||||||
*
|
|
||||||
* @param numeric $_perPage sets the number of iteems per page
|
|
||||||
* @param numeric $_instance sets the instance for the GET parameter
|
|
||||||
*/
|
|
||||||
public function __construct($perPage,$instance){
|
|
||||||
$this->_instance = $instance;
|
|
||||||
$this->_perPage = $perPage;
|
|
||||||
$this->set_instance();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get_start
|
|
||||||
*
|
|
||||||
* creates the starting point for limiting the dataset
|
|
||||||
* @return numeric
|
|
||||||
*/
|
|
||||||
private function get_start(){
|
|
||||||
return ($this->_page * $this->_perPage) - $this->_perPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set_instance
|
|
||||||
*
|
|
||||||
* sets the instance parameter, if numeric value is 0 then set to 1
|
|
||||||
*
|
|
||||||
* @var numeric
|
|
||||||
*/
|
|
||||||
private function set_instance(){
|
|
||||||
$this->_page = (int) (!isset($_GET[$this->_instance]) ? 1 : $_GET[$this->_instance]);
|
|
||||||
$this->_page = ($this->_page == 0 ? 1 : $this->_page);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* set_total
|
|
||||||
*
|
|
||||||
* collect a numberic value and assigns it to the totalRows
|
|
||||||
*
|
|
||||||
* @var numeric
|
|
||||||
*/
|
|
||||||
public function set_total($_totalRows){
|
|
||||||
$this->_totalRows = $_totalRows;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get_limit
|
|
||||||
*
|
|
||||||
* returns the limit for the data source, calling the get_start method and passing in the number of items perp page
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function get_limit(){
|
|
||||||
if (STORAGE == 'postgres') {
|
|
||||||
return "LIMIT ".$this->_perPage." OFFSET ".$this->get_start();
|
|
||||||
} else {
|
|
||||||
return "LIMIT ".$this->get_start().",".$this->_perPage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* page_links
|
|
||||||
*
|
|
||||||
* create the html links for navigating through the dataset
|
|
||||||
*
|
|
||||||
* @var sting $path optionally set the path for the link
|
|
||||||
* @var sting $ext optionally pass in extra parameters to the GET
|
|
||||||
* @return string returns the html menu
|
|
||||||
*/
|
|
||||||
public function page_links($path='?',$ext=null)
|
|
||||||
{
|
|
||||||
$adjacents = "2";
|
|
||||||
$prev = $this->_page - 1;
|
|
||||||
$next = $this->_page + 1;
|
|
||||||
$lastpage = ceil($this->_totalRows/$this->_perPage);
|
|
||||||
$lpm1 = $lastpage - 1;
|
|
||||||
|
|
||||||
$pagination = "";
|
|
||||||
if($lastpage > 1)
|
|
||||||
{
|
|
||||||
$pagination .= "<div class='pagination'>";
|
|
||||||
if ($this->_page > 1)
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$prev"."$ext'>« previous</a>";
|
|
||||||
else
|
|
||||||
$pagination.= "<span class='disabled'>« previous</span>";
|
|
||||||
|
|
||||||
if ($lastpage < 7 + ($adjacents * 2))
|
|
||||||
{
|
|
||||||
for ($counter = 1; $counter <= $lastpage; $counter++)
|
|
||||||
{
|
|
||||||
if ($counter == $this->_page)
|
|
||||||
$pagination.= "<span class='current'>$counter</span>";
|
|
||||||
else
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif($lastpage > 5 + ($adjacents * 2))
|
|
||||||
{
|
|
||||||
if($this->_page < 1 + ($adjacents * 2))
|
|
||||||
{
|
|
||||||
for ($counter = 1; $counter < 4 + ($adjacents * 2); $counter++)
|
|
||||||
{
|
|
||||||
if ($counter == $this->_page)
|
|
||||||
$pagination.= "<span class='current'>$counter</span>";
|
|
||||||
else
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
|
|
||||||
}
|
|
||||||
$pagination.= "...";
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
|
|
||||||
}
|
|
||||||
elseif($lastpage - ($adjacents * 2) > $this->_page && $this->_page > ($adjacents * 2))
|
|
||||||
{
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
|
|
||||||
$pagination.= "...";
|
|
||||||
for ($counter = $this->_page - $adjacents; $counter <= $this->_page + $adjacents; $counter++)
|
|
||||||
{
|
|
||||||
if ($counter == $this->_page)
|
|
||||||
$pagination.= "<span class='current'>$counter</span>";
|
|
||||||
else
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
|
|
||||||
}
|
|
||||||
$pagination.= "..";
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$lpm1"."$ext'>$lpm1</a>";
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$lastpage"."$ext'>$lastpage</a>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=1"."$ext'>1</a>";
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=2"."$ext'>2</a>";
|
|
||||||
$pagination.= "..";
|
|
||||||
for ($counter = $lastpage - (2 + ($adjacents * 2)); $counter <= $lastpage; $counter++)
|
|
||||||
{
|
|
||||||
if ($counter == $this->_page)
|
|
||||||
$pagination.= "<span class='current'>$counter</span>";
|
|
||||||
else
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$counter"."$ext'>$counter</a>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_page < $counter - 1)
|
|
||||||
$pagination.= "<a href='".$path."$this->_instance=$next"."$ext'>next »</a>";
|
|
||||||
else
|
|
||||||
$pagination.= "<span class='disabled'>next »</span>";
|
|
||||||
$pagination.= "</div>\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $pagination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1722
inc/3rdparty/simple_html_dom.php
vendored
26
inc/3rdparty/simplepie/LICENSE.txt
vendored
@ -1,26 +0,0 @@
|
|||||||
Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
of conditions and the following disclaimer in the documentation and/or other materials
|
|
||||||
provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the SimplePie Team nor the names of its contributors may be used
|
|
||||||
to endorse or promote products derived from this software without specific prior
|
|
||||||
written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
|
||||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
||||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
|
|
||||||
AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
||||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
POSSIBILITY OF SUCH DAMAGE.
|
|
||||||