Compare commits
1438 Commits
1.1.0
...
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 | |||
| 8e76eaad19 | |||
| 01e671f4d1 |
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
|
||||
46
.gitignore
vendored
@ -1,6 +1,40 @@
|
||||
assets/*
|
||||
cache/*
|
||||
vendor
|
||||
composer.phar
|
||||
db/poche.sqlite
|
||||
inc/poche/config.inc.php
|
||||
# Cache, logs & sessions
|
||||
/var/*
|
||||
!/var/cache
|
||||
/var/cache/*
|
||||
!var/cache/.gitkeep
|
||||
!/var/logs
|
||||
/var/logs/*
|
||||
!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
|
||||
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:
|
||||
* your poche version (in ./inc/poche/myconfig.inc.php)
|
||||
* the link you want to poche and which causes problem
|
||||
* your wallabag version (on top of the ./index.php file, and also on config page)
|
||||
* 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
|
||||
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).
|
||||
|
||||
33
COPYING.md
@ -1,14 +1,19 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
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.
|
||||
|
||||
Copyright (c) 2013-2016 Nicolas Lœuillet
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
16
CREDITS.md
@ -1,15 +1,3 @@
|
||||
poche is based on :
|
||||
* 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
|
||||
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.
|
||||
|
||||
poche is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
|
||||
|
||||
Contributors : https://github.com/inthepoche/poche/graphs/contributors
|
||||
Thank you [to others contributors](https://github.com/wallabag/wallabag/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 ?
|
||||
Abandon Pocket, Instapaper and other Readability service : adopt poche. It is the same, but it is free (like in freedom) and open source.
|
||||
[](https://travis-ci.org/wallabag/wallabag)
|
||||
[](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
|
||||
[](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
|
||||
|
||||
## Some features
|
||||
* adding, deleting, archiving and setting as favorite a link
|
||||
* import from pocket / readability / instapaper
|
||||
* 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).
|
||||
* ...
|
||||
# What is wallabag?
|
||||
wallabag is a self hostable application allowing you to not miss any content anymore.
|
||||
Click, save and read it when you can. It extracts content so that you can read it when you have time.
|
||||
|
||||
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
|
||||
Read the [INSTALL.md file](https://github.com/inthepoche/poche/blob/master/INSTALL.md).
|
||||
Keep in mind it's an **unstable** branch, everything can be broken :)
|
||||
|
||||
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
|
||||
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
|
||||
terms of the Do What The Fuck You Want To Public License, Version 2,
|
||||
as published by Sam Hocevar. See the COPYING file for more details.
|
||||
terms of the MIT License. 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>
|
||||
122
composer.json
@ -1,7 +1,119 @@
|
||||
{
|
||||
"name": "wallabag/wallabag",
|
||||
"type": "project",
|
||||
"description": "open source self hostable read-it-later web application",
|
||||
"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": {
|
||||
"twig/twig": "1.*",
|
||||
"twig/extensions": "1.0.*",
|
||||
"umpirsky/twig-gettext-extractor": "1.1.*"
|
||||
}
|
||||
}
|
||||
"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
|
||||
}
|
||||
|
||||
6992
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;
|
||||
}
|
||||
}
|
||||
49
inc/3rdparty/FlattrItem.class.php
vendored
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* Class for Flattr querying
|
||||
*/
|
||||
class FlattrItem {
|
||||
|
||||
public $status;
|
||||
public $urltoflattr;
|
||||
public $flattrItemURL;
|
||||
public $numflattrs;
|
||||
|
||||
public function checkItem($urltoflattr,$id) {
|
||||
$this->cacheflattrfile($urltoflattr, $id);
|
||||
$flattrResponse = file_get_contents(CACHE . "/flattr/".$id.".cache");
|
||||
if($flattrResponse != FALSE) {
|
||||
$result = json_decode($flattrResponse);
|
||||
if (isset($result->message)){
|
||||
if ($result->message == "flattrable") {
|
||||
$this->status = FLATTRABLE;
|
||||
}
|
||||
}
|
||||
elseif ($result->link) {
|
||||
$this->status = FLATTRED;
|
||||
$this->flattrItemURL = $result->link;
|
||||
$this->numflattrs = $result->flattrs;
|
||||
}
|
||||
else {
|
||||
$this->status = NOT_FLATTRABLE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->status = "FLATTR_ERR_CONNECTION";
|
||||
}
|
||||
}
|
||||
|
||||
private function cacheflattrfile($urltoflattr, $id) {
|
||||
if (!is_dir(CACHE . '/flattr')) {
|
||||
mkdir(CACHE . '/flattr', 0777);
|
||||
}
|
||||
|
||||
// if a cache flattr file for this url already exists and it's been less than one day than it have been updated, see in /cache
|
||||
if ((!file_exists(CACHE . "/flattr/".$id.".cache")) || (time() - filemtime(CACHE . "/flattr/".$id.".cache") > 86400)) {
|
||||
$askForFlattr = Tools::getFile(FLATTR_API . $urltoflattr);
|
||||
$flattrCacheFile = fopen(CACHE . "/flattr/".$id.".cache", 'w+');
|
||||
fwrite($flattrCacheFile, $askForFlattr);
|
||||
fclose($flattrCacheFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
279
inc/3rdparty/Session.class.php
vendored
@ -1,279 +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 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
|
||||
* - Brute force protection with ban management
|
||||
*
|
||||
* TODOs
|
||||
* - Replace globals with variables in Session class
|
||||
*
|
||||
* How to use:
|
||||
* - http://tontof.net/kriss/php5/session
|
||||
*/
|
||||
class Session
|
||||
{
|
||||
// Personnalize PHP session name
|
||||
public static $sessionName = '';
|
||||
// If the user does not access any page within this time,
|
||||
// his/her session is considered expired (3600 sec. = 1 hour)
|
||||
public static $inactivityTimeout = 3600;
|
||||
// If you get disconnected often or if your IP address changes often.
|
||||
// Let you disable session cookie hijacking protection
|
||||
public static $disableSessionProtection = false;
|
||||
// Ban IP after this many failures.
|
||||
public static $banAfter = 4;
|
||||
// Ban duration for IP address after login failures (in seconds).
|
||||
// (1800 sec. = 30 minutes)
|
||||
public static $banDuration = 1800;
|
||||
// File storage for failures and bans. If empty, no ban management.
|
||||
public static $banFile = '';
|
||||
|
||||
/**
|
||||
* Initialize session
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
// Force cookie path (but do not change lifetime)
|
||||
$cookie = session_get_cookie_params();
|
||||
// Default cookie expiration and path.
|
||||
$cookiedir = '';
|
||||
if (dirname($_SERVER['SCRIPT_NAME'])!='/') {
|
||||
$cookiedir = dirname($_SERVER["SCRIPT_NAME"]).'/';
|
||||
}
|
||||
$ssl = false;
|
||||
if (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") {
|
||||
$ssl = true;
|
||||
}
|
||||
session_set_cookie_params($cookie['lifetime'], $cookiedir, $_SERVER['HTTP_HOST'], $ssl);
|
||||
// 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);
|
||||
if (!empty(self::$sessionName)) {
|
||||
session_name(self::$sessionName);
|
||||
}
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IP address
|
||||
* (Used to prevent session cookie hijacking.)
|
||||
*
|
||||
* @return string IP addresses
|
||||
*/
|
||||
private static function _allIPs()
|
||||
{
|
||||
$ip = $_SERVER["REMOTE_ADDR"];
|
||||
$ip.= isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? '_'.$_SERVER['HTTP_X_FORWARDED_FOR'] : '';
|
||||
$ip.= isset($_SERVER['HTTP_CLIENT_IP']) ? '_'.$_SERVER['HTTP_CLIENT_IP'] : '';
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that user/password is correct and then init some SESSION variables.
|
||||
*
|
||||
* @param string $login Login reference
|
||||
* @param string $password Password reference
|
||||
* @param string $loginTest Login to compare with login reference
|
||||
* @param string $passwordTest Password to compare with password reference
|
||||
* @param array $pValues Array of variables to store in SESSION
|
||||
*
|
||||
* @return true|false True if login and password are correct, false
|
||||
* otherwise
|
||||
*/
|
||||
public static function login (
|
||||
$login,
|
||||
$password,
|
||||
$loginTest,
|
||||
$passwordTest,
|
||||
$pValues = array())
|
||||
{
|
||||
self::banInit();
|
||||
if (self::banCanLogin()) {
|
||||
if ($login === $loginTest && $password === $passwordTest) {
|
||||
self::banLoginOk();
|
||||
// Generate unique random number to sign forms (HMAC)
|
||||
$_SESSION['uid'] = sha1(uniqid('', true).'_'.mt_rand());
|
||||
$_SESSION['ip'] = self::_allIPs();
|
||||
$_SESSION['username'] = $login;
|
||||
// Set session expiration.
|
||||
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
|
||||
|
||||
foreach ($pValues as $key => $value) {
|
||||
$_SESSION[$key] = $value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
self::banLoginFailed();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset SESSION variable to force logout
|
||||
*/
|
||||
public static function logout()
|
||||
{
|
||||
unset($_SESSION['uid'],$_SESSION['ip'],$_SESSION['expires_on'],$_SESSION['tokens'], $_SESSION['login'], $_SESSION['pass'], $_SESSION['poche_user']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure user is logged in.
|
||||
*
|
||||
* @return true|false True if user is logged in, false otherwise
|
||||
*/
|
||||
public static function isLogged()
|
||||
{
|
||||
if (!isset ($_SESSION['uid'])
|
||||
|| (self::$disableSessionProtection === false
|
||||
&& $_SESSION['ip'] !== self::_allIPs())
|
||||
|| time() >= $_SESSION['expires_on']) {
|
||||
self::logout();
|
||||
|
||||
return false;
|
||||
}
|
||||
// User accessed a page : Update his/her session expiration date.
|
||||
$_SESSION['expires_on'] = time() + self::$inactivityTimeout;
|
||||
if (!empty($_SESSION['longlastingsession'])) {
|
||||
$_SESSION['expires_on'] += $_SESSION['longlastingsession'];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a token, store it in SESSION and return it
|
||||
*
|
||||
* @param string $salt to prevent birthday attack
|
||||
*
|
||||
* @return string Token created
|
||||
*/
|
||||
public static function getToken($salt = '')
|
||||
{
|
||||
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().$salt);
|
||||
$_SESSION['tokens'][$rnd]=1;
|
||||
|
||||
return $rnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if a token is ok. Using this function will destroy the token.
|
||||
*
|
||||
* @param string $token Token to test
|
||||
*
|
||||
* @return true|false True if token is correct, false otherwise
|
||||
*/
|
||||
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.
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal a failed login. Will ban the IP if too many failures:
|
||||
*/
|
||||
public static function banLoginFailed()
|
||||
{
|
||||
if (self::$banFile !== '') {
|
||||
$ip = $_SERVER["REMOTE_ADDR"];
|
||||
$gb = $GLOBALS['IPBANS'];
|
||||
|
||||
if (!isset($gb['FAILURES'][$ip])) {
|
||||
$gb['FAILURES'][$ip] = 0;
|
||||
}
|
||||
$gb['FAILURES'][$ip]++;
|
||||
if ($gb['FAILURES'][$ip] > (self::$banAfter - 1)) {
|
||||
$gb['BANS'][$ip]= time() + self::$banDuration;
|
||||
}
|
||||
|
||||
$GLOBALS['IPBANS'] = $gb;
|
||||
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals a successful login. Resets failed login counter.
|
||||
*/
|
||||
public static function banLoginOk()
|
||||
{
|
||||
if (self::$banFile !== '') {
|
||||
$ip = $_SERVER["REMOTE_ADDR"];
|
||||
$gb = $GLOBALS['IPBANS'];
|
||||
unset($gb['FAILURES'][$ip]); unset($gb['BANS'][$ip]);
|
||||
$GLOBALS['IPBANS'] = $gb;
|
||||
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ban init
|
||||
*/
|
||||
public static function banInit()
|
||||
{
|
||||
if (self::$banFile !== '') {
|
||||
if (!is_file(self::$banFile)) {
|
||||
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export(array('FAILURES'=>array(), 'BANS'=>array()), true).";\n?>");
|
||||
}
|
||||
include self::$banFile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user CAN login. If 'true', the user can try to login.
|
||||
*
|
||||
* @return boolean true if user is banned, false otherwise
|
||||
*/
|
||||
public static function banCanLogin()
|
||||
{
|
||||
if (self::$banFile !== '') {
|
||||
$ip = $_SERVER["REMOTE_ADDR"];
|
||||
$gb = $GLOBALS['IPBANS'];
|
||||
if (isset($gb['BANS'][$ip])) {
|
||||
// User is banned. Check if the ban has expired:
|
||||
if ($gb['BANS'][$ip] <= time()) {
|
||||
// Ban expired, user can try to login again.
|
||||
unset($gb['FAILURES'][$ip]);
|
||||
unset($gb['BANS'][$ip]);
|
||||
file_put_contents(self::$banFile, "<?php\n\$GLOBALS['IPBANS']=".var_export($gb, true).";\n?>");
|
||||
|
||||
return true; // Ban has expired, user can login.
|
||||
}
|
||||
|
||||
return false; // User is banned.
|
||||
}
|
||||
}
|
||||
|
||||
return true; // User is not banned.
|
||||
}
|
||||
}
|
||||
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 PocheReadability($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;
|
||||
}
|
||||
}
|
||||