Compare commits
1274 Commits
1.5.1.1
...
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 | |||
| 60ca369cd3 | |||
| 01cd443441 | |||
| 5e98c2183a |
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/wallabag/wallabag/issues/new).
|
||||
|
||||
To fix the bug quickly, we need some infos:
|
||||
* your wallabag version (in ./index.php)
|
||||
* 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 wallabag and work with **the dev branch** only. **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 @@
|
||||
wallabag 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 Maylis Agniel https://github.com/wallabag/logo
|
||||
* 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.
|
||||
|
||||
wallabag is developed by Nicolas Lœuillet under the Do What the Fuck You Want to Public License
|
||||
|
||||
Contributors : https://github.com/wallabag/wallabag/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
|
||||
26
README.md
@ -1,10 +1,26 @@
|
||||
# what is wallabag ?
|
||||
wallabag is a self hostable application allowing you to not miss any content anymore. Click, save, read it when you can. It extracts content so that you can read it when you have time.
|
||||
[](https://travis-ci.org/wallabag/wallabag)
|
||||
[](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
|
||||
[](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
|
||||
|
||||
# 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.
|
||||
|
||||
More informations on our website: [wallabag.org](http://wallabag.org)
|
||||
|
||||
# Want to test the v2?
|
||||
|
||||
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.org>
|
||||
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.
|
||||
|
||||
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>
|
||||
1
cache/.gitignore
vendored
@ -1 +0,0 @@
|
||||
!.htaccess
|
||||
2
cache/.htaccess
vendored
@ -1,2 +0,0 @@
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
// PHP 5.3 minimum
|
||||
if (version_compare(PHP_VERSION, '5.3.3', '<')) {
|
||||
die('This software require PHP 5.3.3 minimum');
|
||||
}
|
||||
|
||||
// Short tags must be enabled for PHP < 5.4
|
||||
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
|
||||
|
||||
if (! ini_get('short_open_tag')) {
|
||||
die('This software require to have short tags enabled, check your php.ini => "short_open_tag = On"');
|
||||
}
|
||||
}
|
||||
|
||||
// Check PDO Sqlite
|
||||
if (! extension_loaded('pdo_sqlite')) {
|
||||
die('PHP extension required: pdo_sqlite');
|
||||
}
|
||||
|
||||
// Check ZIP
|
||||
if (! extension_loaded('zip')) {
|
||||
die('PHP extension required: zip');
|
||||
}
|
||||
|
||||
// Check if /cache is writeable
|
||||
if (! is_writable('cache')) {
|
||||
die('The directory "cache" must be writeable by your web server user');
|
||||
}
|
||||
|
||||
// Check if /db is writeable
|
||||
if (! is_writable('db')) {
|
||||
die('The directory "db" must be writeable by your web server user');
|
||||
}
|
||||
|
||||
// install folder still present, need to install wallabag
|
||||
if (is_dir('install')) {
|
||||
require('install/index.php');
|
||||
exit;
|
||||
}
|
||||
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
themes/baggy/css/font.css → data/assets/.gitignore
vendored
Executable file → Normal file
0
themes/courgette/css/knacss.css → data/db/.gitignore
vendored
Executable file → Normal file
@ -1,2 +0,0 @@
|
||||
Order deny,allow
|
||||
Deny from all
|
||||
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
|
||||
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 (is_object($result) && $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
286
inc/3rdparty/Session.class.php
vendored
@ -1,286 +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 = 86400;
|
||||
// Extra timeout for long sessions (if enabled) (82800 sec. = 23 hours)
|
||||
public static $longSessionTimeout = 31536000;
|
||||
// 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,
|
||||
$longlastingsession,
|
||||
$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;
|
||||
if ($longlastingsession) {
|
||||
$_SESSION['longlastingsession'] = self::$longSessionTimeout;
|
||||
$_SESSION['expires_on'] += $_SESSION['longlastingsession'];
|
||||
}
|
||||
|
||||
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['longlastingsession'], $_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
|
||||
?>
|
||||
405
inc/3rdparty/config.php
vendored
@ -1,405 +0,0 @@
|
||||
<?php
|
||||
/* Full-Text RSS config */
|
||||
|
||||
// ......IMPORTANT......................................
|
||||
// .....................................................
|
||||
// Please do not change this file (config.php) directly.
|
||||
// Save a copy as custom_config.php and make your
|
||||
// changes to that instead. It will automatically
|
||||
// override anything in config.php. Because config.php
|
||||
// always gets loaded anyway, you can simply specify
|
||||
// options you'd like to override in custom_config.php.
|
||||
// .....................................................
|
||||
|
||||
// Create config object
|
||||
if (!isset($options)) $options = new stdClass();
|
||||
|
||||
// Enable service
|
||||
// ----------------------
|
||||
// Set this to false if you want to disable the service.
|
||||
// If set to false, no feed is produced and users will
|
||||
// be told that the service is disabled.
|
||||
$options->enabled = true;
|
||||
|
||||
// Debug mode
|
||||
// ----------------------
|
||||
// Enable or disable debugging. When enabled debugging works by passing
|
||||
// &debug to the makefulltextfeed.php querystring.
|
||||
// Valid values:
|
||||
// true or 'user' (default) - let user decide
|
||||
// 'admin' - debug works only for logged in admin users
|
||||
// false - disabled
|
||||
$options->debug = true;
|
||||
|
||||
// Default entries (without access key)
|
||||
// ----------------------
|
||||
// The number of feed items to process when no API key is supplied
|
||||
// and no &max=x value is supplied in the querystring.
|
||||
$options->default_entries = 5;
|
||||
|
||||
// Max entries (without access key)
|
||||
// ----------------------
|
||||
// The maximum number of feed items to process when no access key is supplied.
|
||||
// This limits the user-supplied &max=x value. For example, if the user
|
||||
// asks for 20 items to be processed (&max=20), if max_entries is set to
|
||||
// 10, only 10 will be processed.
|
||||
$options->max_entries = 10;
|
||||
|
||||
// Rewrite relative URLs
|
||||
// ----------------------
|
||||
// With this enabled relative URLs found in the extracted content
|
||||
// block are automatically rewritten as absolute URLs.
|
||||
$options->rewrite_relative_urls = true;
|
||||
|
||||
// Exclude items if extraction fails
|
||||
// ---------------------------------
|
||||
// Excludes items from the resulting feed
|
||||
// if we cannot extract any content from the
|
||||
// item URL.
|
||||
// Possible values...
|
||||
// Enable: true
|
||||
// Disable: false (default)
|
||||
// User decides: 'user' (this option will appear on the form)
|
||||
$options->exclude_items_on_fail = 'user';
|
||||
|
||||
// Enable multi-page support
|
||||
// -------------------------
|
||||
// If enabled, we will try to follow next page links on multi-page articles.
|
||||
// Currently this only happens for sites where next_page_link has been defined
|
||||
// in a site config file.
|
||||
$options->multipage = true;
|
||||
|
||||
// Enable caching
|
||||
// ----------------------
|
||||
// Enable this if you'd like to cache results
|
||||
// for 10 minutes. Cache files are written to disk (in cache/ subfolders
|
||||
// - which must be writable).
|
||||
// Initially it's best to keep this disabled to make sure everything works
|
||||
// as expected. If you have APC enabled, please also see smart_cache in the
|
||||
// advanced section.
|
||||
$options->caching = false;
|
||||
|
||||
// Cache directory
|
||||
// ----------------------
|
||||
// Only used if caching is true
|
||||
$options->cache_dir = dirname(__FILE__).'/cache';
|
||||
|
||||
// Message to prepend (without access key)
|
||||
// ----------------------
|
||||
// HTML to insert at the beginning of each feed item when no access key is supplied.
|
||||
// Substitution tags:
|
||||
// {url} - Feed item URL
|
||||
// {effective-url} - Feed item URL after we've followed all redirects
|
||||
$options->message_to_prepend = '';
|
||||
|
||||
// Message to append (without access key)
|
||||
// ----------------------
|
||||
// HTML to insert at the end of each feed item when no access key is supplied.
|
||||
// Substitution tags:
|
||||
// {url} - Feed item URL
|
||||
// {effective-url} - Feed item URL after we've followed all redirects
|
||||
$options->message_to_append = '';
|
||||
|
||||
// Error message when content extraction fails (without access key)
|
||||
// ----------------------
|
||||
$options->error_message = '[unable to retrieve full-text content]';
|
||||
|
||||
// Keep enclosure in feed items
|
||||
// If enabled, we will try to preserve enclosures if present.
|
||||
// ----------------------
|
||||
$options->keep_enclosures = true;
|
||||
|
||||
// Detect language
|
||||
// ---------------
|
||||
// Should we try and find/guess the language of the article being processed?
|
||||
// Values will be placed inside the <dc:language> element inside each <item> element
|
||||
// Possible values:
|
||||
// * Ignore language: 0
|
||||
// * Use article/feed metadata (e.g. HTML lang attribute): 1 (default)
|
||||
// * As above, but guess if not present: 2
|
||||
// * Always guess: 3
|
||||
// * User decides: 'user' (value of 0-3 can be passed in querystring: e.g. &l=2)
|
||||
$options->detect_language = 1;
|
||||
|
||||
// Registration key
|
||||
// ---------------
|
||||
// The registration key is optional. It is not required to use Full-Text RSS,
|
||||
// and does not affect the normal operation of Full-Text RSS. It is currently
|
||||
// only used on admin pages which help you update site patterns with the
|
||||
// latest version offered by FiveFilters.org. For these admin-related
|
||||
// tasks to complete, we will require a valid registration key.
|
||||
// If you would like one, you can purchase the latest version of Full-Text RSS
|
||||
// at http://fivefilters.org/content-only/
|
||||
// Your registration key will automatically be sent in the confirmation email.
|
||||
// Once you have it, simply copy and paste it here.
|
||||
$options->registration_key = '';
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
/// RESTRICT ACCESS /////////////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
// Admin credentials
|
||||
// ----------------------
|
||||
// Certain pages/actions, e.g. updating site patterns with our online tool, will require admin credentials.
|
||||
// To use these pages, enter a password here and you'll be prompted for it when you try to access those pages.
|
||||
// If no password or username is set, pages requiring admin privelages will be inaccessible.
|
||||
// The default username is 'admin'.
|
||||
// If overriding with an environment variable, separate username and password with a colon, e.g.:
|
||||
// ftr_admin_credentials: admin:my-secret-password
|
||||
// Example: $options->admin_credentials = array('username'=>'admin', 'password'=>'my-secret-password');
|
||||
$options->admin_credentials = array('username'=>'admin', 'password'=>'admin');
|
||||
|
||||
// URLs to allow
|
||||
// ----------------------
|
||||
// List of URLs (or parts of a URL) which the service will accept.
|
||||
// If the list is empty, all URLs (except those specified in the blocked list below)
|
||||
// will be permitted.
|
||||
// Empty: array();
|
||||
// Non-empty example: array('example.com', 'anothersite.org');
|
||||
$options->allowed_urls = array();
|
||||
|
||||
// URLs to block
|
||||
// ----------------------
|
||||
// List of URLs (or parts of a URL) which the service will not accept.
|
||||
// Note: this list is ignored if allowed_urls is not empty
|
||||
$options->blocked_urls = array();
|
||||
|
||||
// Key holder(s) only?
|
||||
// ----------------------
|
||||
// Set this to true if you want to restrict access only to
|
||||
// those with a key (see below to specify key(s)).
|
||||
// If set to true, no feed is produced unless a valid
|
||||
// key is provided.
|
||||
$options->key_required = false;
|
||||
|
||||
// Favour item titles in feed
|
||||
// ----------------------
|
||||
// By default, when processing feeds, we assume item titles in the feed
|
||||
// have not been truncated. So after processing web pages, the extracted titles
|
||||
// are not used in the generated feed. If you prefer to have extracted titles in
|
||||
// the feed you can either set this to false, in which case we will always favour
|
||||
// extracted titles. Alternatively, if set to 'user' (default) we'll use the
|
||||
// extracted title if you pass '&use_extracted_title' in the querystring.
|
||||
// Possible values:
|
||||
// * Favour feed titles: true
|
||||
// * Favour extracted titles: false
|
||||
// * Favour feed titles with user override: 'user' (default)
|
||||
// Note: this has no effect when the input URL is to a web page - in these cases
|
||||
// we always use the extracted title in the generated feed.
|
||||
$options->favour_feed_titles = 'user';
|
||||
|
||||
// Access keys (password protected access)
|
||||
// ------------------------------------
|
||||
// NOTE: You do not need an API key from fivefilters.org to run your own
|
||||
// copy of the code. This is here if you'd like to restrict access to
|
||||
// _your_ copy.
|
||||
// Keys let you group users - those with a key and those without - and
|
||||
// restrict access to the service to those without a key.
|
||||
// If you want everyone to access the service in the same way, you can
|
||||
// leave the array below empty and ignore the access key options further down.
|
||||
// The options further down let you control how the service should behave
|
||||
// in each mode.
|
||||
// Note: Explicitly including the index number (1 and 2 in the examples below)
|
||||
// is highly recommended (when generating feeds, we encode the key and
|
||||
// refer to it by index number and hash).
|
||||
$options->api_keys = array();
|
||||
// Example:
|
||||
// $options->api_keys[1] = 'secret-key-1';
|
||||
// $options->api_keys[2] = 'secret-key-2';
|
||||
|
||||
// Default entries (with access key)
|
||||
// ----------------------
|
||||
// The number of feed items to process when a valid access key is supplied.
|
||||
$options->default_entries_with_key = 5;
|
||||
|
||||
// Max entries (with access key)
|
||||
// ----------------------
|
||||
// The maximum number of feed items to process when a valid access key is supplied.
|
||||
$options->max_entries_with_key = 10;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
/// ADVANCED OPTIONS ////////////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
// Enable XSS filter?
|
||||
// ----------------------
|
||||
// We have not enabled this by default because we assume the majority of
|
||||
// our users do not display the HTML retrieved by Full-Text RSS
|
||||
// in a web page without further processing. If you subscribe to our generated
|
||||
// feeds in your news reader application, it should, if it's good software, already
|
||||
// filter the resulting HTML for XSS attacks, making it redundant for
|
||||
// Full-Text RSS do the same. Similarly with frameworks/CMS which display
|
||||
// feed content - the content should be treated like any other user-submitted content.
|
||||
//
|
||||
// If you are writing an application yourself which is processing feeds generated by
|
||||
// Full-Text RSS, you can either filter the HTML yourself to remove potential XSS attacks
|
||||
// or enable this option. This might be useful if you are processing our generated
|
||||
// feeds with JavaScript on the client side - although there's client side xss
|
||||
// filtering available too, e.g. https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer
|
||||
//
|
||||
// If enabled, we'll pass retrieved HTML content through htmLawed with
|
||||
// safe flag on and style attributes denied, see
|
||||
// http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm#s3.6
|
||||
// Note: if enabled this will also remove certain elements you may want to preserve, such as iframes.
|
||||
//
|
||||
// Valid values:
|
||||
// true - enabled, all content will be filtered
|
||||
// 'user' (default) - user must pass &xss in makefulltextfeed.php querystring to enable
|
||||
// false - disabled
|
||||
$options->xss_filter = 'user';
|
||||
|
||||
// Allowed parsers
|
||||
// ----------------------
|
||||
// Full-Text RSS attempts to use PHP's libxml extension to process HTML.
|
||||
// While fast, on some sites it may not always produce good results.
|
||||
// For these sites, you can specify an alternative HTML parser:
|
||||
// parser: html5lib
|
||||
// The html5lib parser is bundled with Full-Text RSS.
|
||||
// see http://code.google.com/p/html5lib/
|
||||
//
|
||||
// To disable HTML parsing with html5lib, you can remove it from this list.
|
||||
// By default we allow both: libxml and html5lib.
|
||||
$options->allowed_parsers = array('libxml', 'html5lib');
|
||||
//$options->allowed_parsers = array('libxml'); //disable html5lib - forcing libxml in all cases
|
||||
|
||||
// Enable Cross-Origin Resource Sharing (CORS)
|
||||
// ----------------------
|
||||
// If enabled we'll send the following HTTP header
|
||||
// Access-Control-Allow-Origin: *
|
||||
// see http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
|
||||
$options->cors = false;
|
||||
|
||||
// Use APC user cache?
|
||||
// ----------------------
|
||||
// If enabled we will store site config files (when requested
|
||||
// for the first time) in APC's user cache. Keys prefixed with 'sc.'
|
||||
// This improves performance by reducing disk access.
|
||||
// Note: this has no effect if APC is unavailable on your server.
|
||||
$options->apc = true;
|
||||
|
||||
// Smart cache (experimental)
|
||||
// ----------------------
|
||||
// With this option enabled we will not cache to disk immediately.
|
||||
// We will store the cache key in APC and if it's requested again
|
||||
// we will cache results to disk. Keys prefixed with 'cache.'
|
||||
// This improves performance by reducing disk access.
|
||||
// Note: this has no effect if APC is disabled or unavailable on your server,
|
||||
// or if you have caching disabled.
|
||||
$options->smart_cache = true;
|
||||
|
||||
// Fingerprints
|
||||
// ----------------------
|
||||
// key is fingerprint (fragment to find in HTML)
|
||||
// value is host name to use for site config lookup if fingerprint matches
|
||||
$options->fingerprints = array(
|
||||
// Posterous
|
||||
'<meta name="generator" content="Posterous"' => array('hostname'=>'fingerprint.posterous.com', 'head'=>true),
|
||||
// Blogger
|
||||
'<meta content=\'blogger\' name=\'generator\'' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
|
||||
'<meta name="generator" content="Blogger"' => array('hostname'=>'fingerprint.blogspot.com', 'head'=>true),
|
||||
// WordPress (hosted)
|
||||
// '<meta name="generator" content="WordPress.com"' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true),
|
||||
// WordPress (self-hosted and hosted)
|
||||
'<meta name="generator" content="WordPress' => array('hostname'=>'fingerprint.wordpress.com', 'head'=>true)
|
||||
);
|
||||
|
||||
// User Agent strings - mapping domain names
|
||||
// ----------------------
|
||||
// e.g. $options->user_agents = array('example.org' => 'PHP/5.2');
|
||||
$options->user_agents = array( 'lifehacker.com' => 'PHP/5.2',
|
||||
'gawker.com' => 'PHP/5.2',
|
||||
'deadspin.com' => 'PHP/5.2',
|
||||
'kotaku.com' => 'PHP/5.2',
|
||||
'jezebel.com' => 'PHP/5.2',
|
||||
'io9.com' => 'PHP/5.2',
|
||||
'jalopnik.com' => 'PHP/5.2',
|
||||
'gizmodo.com' => 'PHP/5.2',
|
||||
'.wikipedia.org' => 'Mozilla/5.2',
|
||||
'.fok.nl' => 'Googlebot/2.1',
|
||||
'getpocket.com' => 'PHP/5.2'
|
||||
);
|
||||
|
||||
// URL Rewriting
|
||||
// ----------------------
|
||||
// Currently allows simple string replace of URLs.
|
||||
// Useful for rewriting certain URLs to point to a single page
|
||||
// or HTML view. Although using the single_page_link site config
|
||||
// instruction is the preferred way to do this, sometimes, as
|
||||
// with Google Docs URLs, it's not possible.
|
||||
// Note: this might move to the site config file at some point.
|
||||
$options->rewrite_url = array(
|
||||
// Rewrite public Google Docs URLs to point to HTML view:
|
||||
// if a URL contains docs.google.com, replace /Doc? with /View?
|
||||
'docs.google.com' => array('/Doc?' => '/View?'),
|
||||
'tnr.com' => array('tnr.com/article/' => 'tnr.com/print/article/'),
|
||||
'.m.wikipedia.org' => array('.m.wikipedia.org' => '.wikipedia.org'),
|
||||
'm.vanityfair.com' => array('m.vanityfair.com' => 'www.vanityfair.com')
|
||||
);
|
||||
|
||||
// Content-Type exceptions
|
||||
// -----------------------
|
||||
// Here you can define different actions based
|
||||
// on the Content-Type header returned by server.
|
||||
// MIME type as key, action as value.
|
||||
// Valid actions:
|
||||
// * 'exclude' - exclude this item from the result
|
||||
// * 'link' - create HTML link to the item
|
||||
$options->content_type_exc = array(
|
||||
'application/pdf' => array('action'=>'link', 'name'=>'PDF'),
|
||||
'image' => array('action'=>'link', 'name'=>'Image'),
|
||||
'audio' => array('action'=>'link', 'name'=>'Audio'),
|
||||
'video' => array('action'=>'link', 'name'=>'Video')
|
||||
);
|
||||
|
||||
// Cache directory level
|
||||
// ----------------------
|
||||
// Spread cache files over different directories (only used if caching is enabled).
|
||||
// Used to prevent large number of files in one directory.
|
||||
// This corresponds to Zend_Cache's hashed_directory_level
|
||||
// see http://framework.zend.com/manual/en/zend.cache.backends.html
|
||||
// It's best not to change this if you're unsure.
|
||||
$options->cache_directory_level = 0;
|
||||
|
||||
// Cache cleanup
|
||||
// -------------
|
||||
// 0 = script will not clean cache (rename cachecleanup.php and use it for scheduled (e.g. cron) cache cleanup)
|
||||
// 1 = clean cache everytime the script runs (not recommended)
|
||||
// 100 = clean cache roughly once every 100 script runs
|
||||
// x = clean cache roughly once every x script runs
|
||||
// ...you get the idea :)
|
||||
$options->cache_cleanup = 100;
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
/// DO NOT CHANGE ANYTHING BELOW THIS ///////////
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
if (!defined('_FF_FTR_VERSION')) define('_FF_FTR_VERSION', '3.1');
|
||||
|
||||
if (basename(__FILE__) == 'config.php') {
|
||||
if (file_exists(dirname(__FILE__).'/custom_config.php')) {
|
||||
require_once dirname(__FILE__).'/custom_config.php';
|
||||
}
|
||||
|
||||
// check for environment variables - often used on cloud platforms
|
||||
// environment variables should be prefixed with 'ftr_', e.g.
|
||||
// ftr_max_entries: 1
|
||||
// will set the max_entries value to 1.
|
||||
foreach ($options as $_key=>&$_val) {
|
||||
$_key = "ftr_$_key";
|
||||
if (($_env = getenv($_key)) !== false) {
|
||||
if (is_array($_val)) {
|
||||
if ($_key === 'ftr_admin_credentials') {
|
||||
$_val = array_combine(array('username', 'password'), array_map('trim', explode(':', $_env, 2)));
|
||||
if ($_val === false) $_val = array('username'=>'admin', 'password'=>'');
|
||||
}
|
||||
} elseif ($_env === 'true' || $_env === 'false') {
|
||||
$_val = ($_env === 'true');
|
||||
} elseif (is_numeric($_env)) {
|
||||
$_val = (int)$_env;
|
||||
} else { // string
|
||||
$_val = $_env;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($_key, $_val, $_env);
|
||||
}
|
||||
250
inc/3rdparty/libraries/Zend/Cache.php
vendored
@ -1,250 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Cache
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: Cache.php 24656 2012-02-26 06:02:53Z adamlundrigan $
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @package Zend_Cache
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
abstract class Zend_Cache
|
||||
{
|
||||
|
||||
/**
|
||||
* Standard frontends
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $standardFrontends = array('Core', 'Output', 'Class', 'File', 'Function', 'Page');
|
||||
|
||||
/**
|
||||
* Standard backends
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $standardBackends = array('File', 'Sqlite', 'Memcached', 'Libmemcached', 'Apc', 'ZendPlatform',
|
||||
'Xcache', 'TwoLevels', 'WinCache', 'ZendServer_Disk', 'ZendServer_ShMem');
|
||||
|
||||
/**
|
||||
* Standard backends which implement the ExtendedInterface
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $standardExtendedBackends = array('File', 'Apc', 'TwoLevels', 'Memcached', 'Libmemcached', 'Sqlite', 'WinCache');
|
||||
|
||||
/**
|
||||
* Only for backward compatibility (may be removed in next major release)
|
||||
*
|
||||
* @var array
|
||||
* @deprecated
|
||||
*/
|
||||
public static $availableFrontends = array('Core', 'Output', 'Class', 'File', 'Function', 'Page');
|
||||
|
||||
/**
|
||||
* Only for backward compatibility (may be removed in next major release)
|
||||
*
|
||||
* @var array
|
||||
* @deprecated
|
||||
*/
|
||||
public static $availableBackends = array('File', 'Sqlite', 'Memcached', 'Libmemcached', 'Apc', 'ZendPlatform', 'Xcache', 'WinCache', 'TwoLevels');
|
||||
|
||||
/**
|
||||
* Consts for clean() method
|
||||
*/
|
||||
const CLEANING_MODE_ALL = 'all';
|
||||
const CLEANING_MODE_OLD = 'old';
|
||||
const CLEANING_MODE_MATCHING_TAG = 'matchingTag';
|
||||
const CLEANING_MODE_NOT_MATCHING_TAG = 'notMatchingTag';
|
||||
const CLEANING_MODE_MATCHING_ANY_TAG = 'matchingAnyTag';
|
||||
|
||||
/**
|
||||
* Factory
|
||||
*
|
||||
* @param mixed $frontend frontend name (string) or Zend_Cache_Frontend_ object
|
||||
* @param mixed $backend backend name (string) or Zend_Cache_Backend_ object
|
||||
* @param array $frontendOptions associative array of options for the corresponding frontend constructor
|
||||
* @param array $backendOptions associative array of options for the corresponding backend constructor
|
||||
* @param boolean $customFrontendNaming if true, the frontend argument is used as a complete class name ; if false, the frontend argument is used as the end of "Zend_Cache_Frontend_[...]" class name
|
||||
* @param boolean $customBackendNaming if true, the backend argument is used as a complete class name ; if false, the backend argument is used as the end of "Zend_Cache_Backend_[...]" class name
|
||||
* @param boolean $autoload if true, there will no require_once for backend and frontend (useful only for custom backends/frontends)
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return Zend_Cache_Core|Zend_Cache_Frontend
|
||||
*/
|
||||
public static function factory($frontend, $backend, $frontendOptions = array(), $backendOptions = array(), $customFrontendNaming = false, $customBackendNaming = false, $autoload = false)
|
||||
{
|
||||
if (is_string($backend)) {
|
||||
$backendObject = self::_makeBackend($backend, $backendOptions, $customBackendNaming, $autoload);
|
||||
} else {
|
||||
if ((is_object($backend)) && (in_array('Zend_Cache_Backend_Interface', class_implements($backend)))) {
|
||||
$backendObject = $backend;
|
||||
} else {
|
||||
self::throwException('backend must be a backend name (string) or an object which implements Zend_Cache_Backend_Interface');
|
||||
}
|
||||
}
|
||||
if (is_string($frontend)) {
|
||||
$frontendObject = self::_makeFrontend($frontend, $frontendOptions, $customFrontendNaming, $autoload);
|
||||
} else {
|
||||
if (is_object($frontend)) {
|
||||
$frontendObject = $frontend;
|
||||
} else {
|
||||
self::throwException('frontend must be a frontend name (string) or an object');
|
||||
}
|
||||
}
|
||||
$frontendObject->setBackend($backendObject);
|
||||
return $frontendObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backend Constructor
|
||||
*
|
||||
* @param string $backend
|
||||
* @param array $backendOptions
|
||||
* @param boolean $customBackendNaming
|
||||
* @param boolean $autoload
|
||||
* @return Zend_Cache_Backend
|
||||
*/
|
||||
public static function _makeBackend($backend, $backendOptions, $customBackendNaming = false, $autoload = false)
|
||||
{
|
||||
if (!$customBackendNaming) {
|
||||
$backend = self::_normalizeName($backend);
|
||||
}
|
||||
if (in_array($backend, Zend_Cache::$standardBackends)) {
|
||||
// we use a standard backend
|
||||
$backendClass = 'Zend_Cache_Backend_' . $backend;
|
||||
// security controls are explicit
|
||||
require_once realpath(dirname(__FILE__).'/..').DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
|
||||
} else {
|
||||
// we use a custom backend
|
||||
if (!preg_match('~^[\w\\\\]+$~D', $backend)) {
|
||||
Zend_Cache::throwException("Invalid backend name [$backend]");
|
||||
}
|
||||
if (!$customBackendNaming) {
|
||||
// we use this boolean to avoid an API break
|
||||
$backendClass = 'Zend_Cache_Backend_' . $backend;
|
||||
} else {
|
||||
$backendClass = $backend;
|
||||
}
|
||||
if (!$autoload) {
|
||||
$file = str_replace('_', DIRECTORY_SEPARATOR, $backendClass) . '.php';
|
||||
if (!(self::_isReadable($file))) {
|
||||
self::throwException("file $file not found in include_path");
|
||||
}
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
return new $backendClass($backendOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frontend Constructor
|
||||
*
|
||||
* @param string $frontend
|
||||
* @param array $frontendOptions
|
||||
* @param boolean $customFrontendNaming
|
||||
* @param boolean $autoload
|
||||
* @return Zend_Cache_Core|Zend_Cache_Frontend
|
||||
*/
|
||||
public static function _makeFrontend($frontend, $frontendOptions = array(), $customFrontendNaming = false, $autoload = false)
|
||||
{
|
||||
if (!$customFrontendNaming) {
|
||||
$frontend = self::_normalizeName($frontend);
|
||||
}
|
||||
if (in_array($frontend, self::$standardFrontends)) {
|
||||
// we use a standard frontend
|
||||
// For perfs reasons, with frontend == 'Core', we can interact with the Core itself
|
||||
$frontendClass = 'Zend_Cache_' . ($frontend != 'Core' ? 'Frontend_' : '') . $frontend;
|
||||
// security controls are explicit
|
||||
require_once realpath(dirname(__FILE__).'/..').DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
|
||||
} else {
|
||||
// we use a custom frontend
|
||||
if (!preg_match('~^[\w\\\\]+$~D', $frontend)) {
|
||||
Zend_Cache::throwException("Invalid frontend name [$frontend]");
|
||||
}
|
||||
if (!$customFrontendNaming) {
|
||||
// we use this boolean to avoid an API break
|
||||
$frontendClass = 'Zend_Cache_Frontend_' . $frontend;
|
||||
} else {
|
||||
$frontendClass = $frontend;
|
||||
}
|
||||
if (!$autoload) {
|
||||
$file = str_replace('_', DIRECTORY_SEPARATOR, $frontendClass) . '.php';
|
||||
if (!(self::_isReadable($file))) {
|
||||
self::throwException("file $file not found in include_path");
|
||||
}
|
||||
require_once $file;
|
||||
}
|
||||
}
|
||||
return new $frontendClass($frontendOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception
|
||||
*
|
||||
* Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic
|
||||
* @param string $msg Message for the exception
|
||||
* @throws Zend_Cache_Exception
|
||||
*/
|
||||
public static function throwException($msg, Exception $e = null)
|
||||
{
|
||||
// For perfs reasons, we use this dynamic inclusion
|
||||
require_once 'Zend/Cache/Exception.php';
|
||||
throw new Zend_Cache_Exception($msg, 0, $e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize frontend and backend names to allow multiple words TitleCased
|
||||
*
|
||||
* @param string $name Name to normalize
|
||||
* @return string
|
||||
*/
|
||||
protected static function _normalizeName($name)
|
||||
{
|
||||
$name = ucfirst(strtolower($name));
|
||||
$name = str_replace(array('-', '_', '.'), ' ', $name);
|
||||
$name = ucwords($name);
|
||||
$name = str_replace(' ', '', $name);
|
||||
if (stripos($name, 'ZendServer') === 0) {
|
||||
$name = 'ZendServer_' . substr($name, strlen('ZendServer'));
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the $filename is readable, or FALSE otherwise.
|
||||
* This function uses the PHP include_path, where PHP's is_readable()
|
||||
* does not.
|
||||
*
|
||||
* Note : this method comes from Zend_Loader (see #ZF-2891 for details)
|
||||
*
|
||||
* @param string $filename
|
||||
* @return boolean
|
||||
*/
|
||||
private static function _isReadable($filename)
|
||||
{
|
||||
if (!$fh = @fopen($filename, 'r', true)) {
|
||||
return false;
|
||||
}
|
||||
@fclose($fh);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
290
inc/3rdparty/libraries/Zend/Cache/Backend.php
vendored
@ -1,290 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Cache
|
||||
* @subpackage Zend_Cache_Backend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: Backend.php 24989 2012-06-21 07:24:13Z mabe $
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @package Zend_Cache
|
||||
* @subpackage Zend_Cache_Backend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Cache_Backend
|
||||
{
|
||||
/**
|
||||
* Frontend or Core directives
|
||||
*
|
||||
* =====> (int) lifetime :
|
||||
* - Cache lifetime (in seconds)
|
||||
* - If null, the cache is valid forever
|
||||
*
|
||||
* =====> (int) logging :
|
||||
* - if set to true, a logging is activated throw Zend_Log
|
||||
*
|
||||
* @var array directives
|
||||
*/
|
||||
protected $_directives = array(
|
||||
'lifetime' => 3600,
|
||||
'logging' => false,
|
||||
'logger' => null
|
||||
);
|
||||
|
||||
/**
|
||||
* Available options
|
||||
*
|
||||
* @var array available options
|
||||
*/
|
||||
protected $_options = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $options Associative array of options
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
while (list($name, $value) = each($options)) {
|
||||
$this->setOption($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frontend directives
|
||||
*
|
||||
* @param array $directives Assoc of directives
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setDirectives($directives)
|
||||
{
|
||||
if (!is_array($directives)) Zend_Cache::throwException('Directives parameter must be an array');
|
||||
while (list($name, $value) = each($directives)) {
|
||||
if (!is_string($name)) {
|
||||
Zend_Cache::throwException("Incorrect option name : $name");
|
||||
}
|
||||
$name = strtolower($name);
|
||||
if (array_key_exists($name, $this->_directives)) {
|
||||
$this->_directives[$name] = $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->_loggerSanity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an option
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
if (!is_string($name)) {
|
||||
Zend_Cache::throwException("Incorrect option name : $name");
|
||||
}
|
||||
$name = strtolower($name);
|
||||
if (array_key_exists($name, $this->_options)) {
|
||||
$this->_options[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an option
|
||||
*
|
||||
* @param string $name Optional, the options name to return
|
||||
* @throws Zend_Cache_Exceptions
|
||||
* @return mixed
|
||||
*/
|
||||
public function getOption($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if (array_key_exists($name, $this->_options)) {
|
||||
return $this->_options[$name];
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->_directives)) {
|
||||
return $this->_directives[$name];
|
||||
}
|
||||
|
||||
Zend_Cache::throwException("Incorrect option name : {$name}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the life time
|
||||
*
|
||||
* if $specificLifetime is not false, the given specific life time is used
|
||||
* else, the global lifetime is used
|
||||
*
|
||||
* @param int $specificLifetime
|
||||
* @return int Cache life time
|
||||
*/
|
||||
public function getLifetime($specificLifetime)
|
||||
{
|
||||
if ($specificLifetime === false) {
|
||||
return $this->_directives['lifetime'];
|
||||
}
|
||||
return $specificLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the automatic cleaning is available for the backend
|
||||
*
|
||||
* DEPRECATED : use getCapabilities() instead
|
||||
*
|
||||
* @deprecated
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAutomaticCleaningAvailable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine system TMP directory and detect if we have read access
|
||||
*
|
||||
* inspired from Zend_File_Transfer_Adapter_Abstract
|
||||
*
|
||||
* @return string
|
||||
* @throws Zend_Cache_Exception if unable to determine directory
|
||||
*/
|
||||
public function getTmpDir()
|
||||
{
|
||||
$tmpdir = array();
|
||||
foreach (array($_ENV, $_SERVER) as $tab) {
|
||||
foreach (array('TMPDIR', 'TEMP', 'TMP', 'windir', 'SystemRoot') as $key) {
|
||||
if (isset($tab[$key]) && is_string($tab[$key])) {
|
||||
if (($key == 'windir') or ($key == 'SystemRoot')) {
|
||||
$dir = realpath($tab[$key] . '\\temp');
|
||||
} else {
|
||||
$dir = realpath($tab[$key]);
|
||||
}
|
||||
if ($this->_isGoodTmpDir($dir)) {
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$upload = ini_get('upload_tmp_dir');
|
||||
if ($upload) {
|
||||
$dir = realpath($upload);
|
||||
if ($this->_isGoodTmpDir($dir)) {
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
if (function_exists('sys_get_temp_dir')) {
|
||||
$dir = sys_get_temp_dir();
|
||||
if ($this->_isGoodTmpDir($dir)) {
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
// Attemp to detect by creating a temporary file
|
||||
$tempFile = tempnam(md5(uniqid(rand(), TRUE)), '');
|
||||
if ($tempFile) {
|
||||
$dir = realpath(dirname($tempFile));
|
||||
unlink($tempFile);
|
||||
if ($this->_isGoodTmpDir($dir)) {
|
||||
return $dir;
|
||||
}
|
||||
}
|
||||
if ($this->_isGoodTmpDir('/tmp')) {
|
||||
return '/tmp';
|
||||
}
|
||||
if ($this->_isGoodTmpDir('\\temp')) {
|
||||
return '\\temp';
|
||||
}
|
||||
Zend_Cache::throwException('Could not determine temp directory, please specify a cache_dir manually');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the given temporary directory is readable and writable
|
||||
*
|
||||
* @param string $dir temporary directory
|
||||
* @return boolean true if the directory is ok
|
||||
*/
|
||||
protected function _isGoodTmpDir($dir)
|
||||
{
|
||||
if (is_readable($dir)) {
|
||||
if (is_writable($dir)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure if we enable logging that the Zend_Log class
|
||||
* is available.
|
||||
* Create a default log object if none is set.
|
||||
*
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
protected function _loggerSanity()
|
||||
{
|
||||
if (!isset($this->_directives['logging']) || !$this->_directives['logging']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->_directives['logger'])) {
|
||||
if ($this->_directives['logger'] instanceof Zend_Log) {
|
||||
return;
|
||||
}
|
||||
Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.');
|
||||
}
|
||||
|
||||
// Create a default logger to the standard output stream
|
||||
require_once 'Zend/Log.php';
|
||||
require_once 'Zend/Log/Writer/Stream.php';
|
||||
require_once 'Zend/Log/Filter/Priority.php';
|
||||
$logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
|
||||
$logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
|
||||
$this->_directives['logger'] = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message at the WARN (4) priority.
|
||||
*
|
||||
* @param string $message
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
protected function _log($message, $priority = 4)
|
||||
{
|
||||
if (!$this->_directives['logging']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($this->_directives['logger'])) {
|
||||
Zend_Cache::throwException('Logging is enabled but logger is not set.');
|
||||
}
|
||||
$logger = $this->_directives['logger'];
|
||||
if (!$logger instanceof Zend_Log) {
|
||||
Zend_Cache::throwException('Logger object is not an instance of Zend_Log class.');
|
||||
}
|
||||
$logger->log($message, $priority);
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Cache
|
||||
* @subpackage Zend_Cache_Backend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: ExtendedInterface.php 24593 2012-01-05 20:35:02Z matthew $
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see Zend_Cache_Backend_Interface
|
||||
*/
|
||||
//require_once 'Zend/Cache/Backend/Interface.php';
|
||||
require_once dirname(__FILE__).'/Interface.php';
|
||||
|
||||
/**
|
||||
* @package Zend_Cache
|
||||
* @subpackage Zend_Cache_Backend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
interface Zend_Cache_Backend_ExtendedInterface extends Zend_Cache_Backend_Interface
|
||||
{
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids
|
||||
*
|
||||
* @return array array of stored cache ids (string)
|
||||
*/
|
||||
public function getIds();
|
||||
|
||||
/**
|
||||
* Return an array of stored tags
|
||||
*
|
||||
* @return array array of stored tags (string)
|
||||
*/
|
||||
public function getTags();
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids which match given tags
|
||||
*
|
||||
* In case of multiple tags, a logical AND is made between tags
|
||||
*
|
||||
* @param array $tags array of tags
|
||||
* @return array array of matching cache ids (string)
|
||||
*/
|
||||
public function getIdsMatchingTags($tags = array());
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids which don't match given tags
|
||||
*
|
||||
* In case of multiple tags, a logical OR is made between tags
|
||||
*
|
||||
* @param array $tags array of tags
|
||||
* @return array array of not matching cache ids (string)
|
||||
*/
|
||||
public function getIdsNotMatchingTags($tags = array());
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids which match any given tags
|
||||
*
|
||||
* In case of multiple tags, a logical AND is made between tags
|
||||
*
|
||||
* @param array $tags array of tags
|
||||
* @return array array of any matching cache ids (string)
|
||||
*/
|
||||
public function getIdsMatchingAnyTags($tags = array());
|
||||
|
||||
/**
|
||||
* Return the filling percentage of the backend storage
|
||||
*
|
||||
* @return int integer between 0 and 100
|
||||
*/
|
||||
public function getFillingPercentage();
|
||||
|
||||
/**
|
||||
* Return an array of metadatas for the given cache id
|
||||
*
|
||||
* The array must include these keys :
|
||||
* - expire : the expire timestamp
|
||||
* - tags : a string array of tags
|
||||
* - mtime : timestamp of last modification time
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @return array array of metadatas (false if the cache id is not found)
|
||||
*/
|
||||
public function getMetadatas($id);
|
||||
|
||||
/**
|
||||
* Give (if possible) an extra lifetime to the given cache id
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @param int $extraLifetime
|
||||
* @return boolean true if ok
|
||||
*/
|
||||
public function touch($id, $extraLifetime);
|
||||
|
||||
/**
|
||||
* Return an associative array of capabilities (booleans) of the backend
|
||||
*
|
||||
* The array must include these keys :
|
||||
* - automatic_cleaning (is automating cleaning necessary)
|
||||
* - tags (are tags supported)
|
||||
* - expired_read (is it possible to read expired cache records
|
||||
* (for doNotTestCacheValidity option for example))
|
||||
* - priority does the backend deal with priority when saving
|
||||
* - infinite_lifetime (is infinite lifetime can work with this backend)
|
||||
* - get_list (is it possible to get the list of cache ids and the complete list of tags)
|
||||
*
|
||||
* @return array associative of with capabilities
|
||||
*/
|
||||
public function getCapabilities();
|
||||
|
||||
}
|
||||
1034
inc/3rdparty/libraries/Zend/Cache/Backend/File.php
vendored
@ -1,99 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Cache
|
||||
* @subpackage Zend_Cache_Backend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: Interface.php 24593 2012-01-05 20:35:02Z matthew $
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @package Zend_Cache
|
||||
* @subpackage Zend_Cache_Backend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
interface Zend_Cache_Backend_Interface
|
||||
{
|
||||
/**
|
||||
* Set the frontend directives
|
||||
*
|
||||
* @param array $directives assoc of directives
|
||||
*/
|
||||
public function setDirectives($directives);
|
||||
|
||||
/**
|
||||
* Test if a cache is available for the given id and (if yes) return it (false else)
|
||||
*
|
||||
* Note : return value is always "string" (unserialization is done by the core not by the backend)
|
||||
*
|
||||
* @param string $id Cache id
|
||||
* @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
|
||||
* @return string|false cached datas
|
||||
*/
|
||||
public function load($id, $doNotTestCacheValidity = false);
|
||||
|
||||
/**
|
||||
* Test if a cache is available or not (for the given id)
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @return mixed|false (a cache is not available) or "last modified" timestamp (int) of the available cache record
|
||||
*/
|
||||
public function test($id);
|
||||
|
||||
/**
|
||||
* Save some string datas into a cache record
|
||||
*
|
||||
* Note : $data is always "string" (serialization is done by the
|
||||
* core not by the backend)
|
||||
*
|
||||
* @param string $data Datas to cache
|
||||
* @param string $id Cache id
|
||||
* @param array $tags Array of strings, the cache record will be tagged by each string entry
|
||||
* @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
|
||||
* @return boolean true if no problem
|
||||
*/
|
||||
public function save($data, $id, $tags = array(), $specificLifetime = false);
|
||||
|
||||
/**
|
||||
* Remove a cache record
|
||||
*
|
||||
* @param string $id Cache id
|
||||
* @return boolean True if no problem
|
||||
*/
|
||||
public function remove($id);
|
||||
|
||||
/**
|
||||
* Clean some cache records
|
||||
*
|
||||
* Available modes are :
|
||||
* Zend_Cache::CLEANING_MODE_ALL (default) => remove all cache entries ($tags is not used)
|
||||
* Zend_Cache::CLEANING_MODE_OLD => remove too old cache entries ($tags is not used)
|
||||
* Zend_Cache::CLEANING_MODE_MATCHING_TAG => remove cache entries matching all given tags
|
||||
* ($tags can be an array of strings or a single string)
|
||||
* Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG => remove cache entries not {matching one of the given tags}
|
||||
* ($tags can be an array of strings or a single string)
|
||||
* Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG => remove cache entries matching any given tags
|
||||
* ($tags can be an array of strings or a single string)
|
||||
*
|
||||
* @param string $mode Clean mode
|
||||
* @param array $tags Array of tags
|
||||
* @return boolean true if no problem
|
||||
*/
|
||||
public function clean($mode = Zend_Cache::CLEANING_MODE_ALL, $tags = array());
|
||||
|
||||
}
|
||||
765
inc/3rdparty/libraries/Zend/Cache/Core.php
vendored
@ -1,765 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Cache
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: Core.php 24989 2012-06-21 07:24:13Z mabe $
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @package Zend_Cache
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Cache_Core
|
||||
{
|
||||
/**
|
||||
* Messages
|
||||
*/
|
||||
const BACKEND_NOT_SUPPORTS_TAG = 'tags are not supported by the current backend';
|
||||
const BACKEND_NOT_IMPLEMENTS_EXTENDED_IF = 'Current backend doesn\'t implement the Zend_Cache_Backend_ExtendedInterface, so this method is not available';
|
||||
|
||||
/**
|
||||
* Backend Object
|
||||
*
|
||||
* @var Zend_Cache_Backend_Interface $_backend
|
||||
*/
|
||||
protected $_backend = null;
|
||||
|
||||
/**
|
||||
* Available options
|
||||
*
|
||||
* ====> (boolean) write_control :
|
||||
* - Enable / disable write control (the cache is read just after writing to detect corrupt entries)
|
||||
* - Enable write control will lightly slow the cache writing but not the cache reading
|
||||
* Write control can detect some corrupt cache files but maybe it's not a perfect control
|
||||
*
|
||||
* ====> (boolean) caching :
|
||||
* - Enable / disable caching
|
||||
* (can be very useful for the debug of cached scripts)
|
||||
*
|
||||
* =====> (string) cache_id_prefix :
|
||||
* - prefix for cache ids (namespace)
|
||||
*
|
||||
* ====> (boolean) automatic_serialization :
|
||||
* - Enable / disable automatic serialization
|
||||
* - It can be used to save directly datas which aren't strings (but it's slower)
|
||||
*
|
||||
* ====> (int) automatic_cleaning_factor :
|
||||
* - Disable / Tune the automatic cleaning process
|
||||
* - The automatic cleaning process destroy too old (for the given life time)
|
||||
* cache files when a new cache file is written :
|
||||
* 0 => no automatic cache cleaning
|
||||
* 1 => systematic cache cleaning
|
||||
* x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
|
||||
*
|
||||
* ====> (int) lifetime :
|
||||
* - Cache lifetime (in seconds)
|
||||
* - If null, the cache is valid forever.
|
||||
*
|
||||
* ====> (boolean) logging :
|
||||
* - If set to true, logging is activated (but the system is slower)
|
||||
*
|
||||
* ====> (boolean) ignore_user_abort
|
||||
* - If set to true, the core will set the ignore_user_abort PHP flag inside the
|
||||
* save() method to avoid cache corruptions in some cases (default false)
|
||||
*
|
||||
* @var array $_options available options
|
||||
*/
|
||||
protected $_options = array(
|
||||
'write_control' => true,
|
||||
'caching' => true,
|
||||
'cache_id_prefix' => null,
|
||||
'automatic_serialization' => false,
|
||||
'automatic_cleaning_factor' => 10,
|
||||
'lifetime' => 3600,
|
||||
'logging' => false,
|
||||
'logger' => null,
|
||||
'ignore_user_abort' => false
|
||||
);
|
||||
|
||||
/**
|
||||
* Array of options which have to be transfered to backend
|
||||
*
|
||||
* @var array $_directivesList
|
||||
*/
|
||||
protected static $_directivesList = array('lifetime', 'logging', 'logger');
|
||||
|
||||
/**
|
||||
* Not used for the core, just a sort a hint to get a common setOption() method (for the core and for frontends)
|
||||
*
|
||||
* @var array $_specificOptions
|
||||
*/
|
||||
protected $_specificOptions = array();
|
||||
|
||||
/**
|
||||
* Last used cache id
|
||||
*
|
||||
* @var string $_lastId
|
||||
*/
|
||||
private $_lastId = null;
|
||||
|
||||
/**
|
||||
* True if the backend implements Zend_Cache_Backend_ExtendedInterface
|
||||
*
|
||||
* @var boolean $_extendedBackend
|
||||
*/
|
||||
protected $_extendedBackend = false;
|
||||
|
||||
/**
|
||||
* Array of capabilities of the backend (only if it implements Zend_Cache_Backend_ExtendedInterface)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_backendCapabilities = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array|Zend_Config $options Associative array of options or Zend_Config instance
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($options = array())
|
||||
{
|
||||
if ($options instanceof Zend_Config) {
|
||||
$options = $options->toArray();
|
||||
}
|
||||
if (!is_array($options)) {
|
||||
Zend_Cache::throwException("Options passed were not an array"
|
||||
. " or Zend_Config instance.");
|
||||
}
|
||||
while (list($name, $value) = each($options)) {
|
||||
$this->setOption($name, $value);
|
||||
}
|
||||
$this->_loggerSanity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set options using an instance of type Zend_Config
|
||||
*
|
||||
* @param Zend_Config $config
|
||||
* @return Zend_Cache_Core
|
||||
*/
|
||||
public function setConfig(Zend_Config $config)
|
||||
{
|
||||
$options = $config->toArray();
|
||||
while (list($name, $value) = each($options)) {
|
||||
$this->setOption($name, $value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the backend
|
||||
*
|
||||
* @param Zend_Cache_Backend $backendObject
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setBackend(Zend_Cache_Backend $backendObject)
|
||||
{
|
||||
$this->_backend= $backendObject;
|
||||
// some options (listed in $_directivesList) have to be given
|
||||
// to the backend too (even if they are not "backend specific")
|
||||
$directives = array();
|
||||
foreach (Zend_Cache_Core::$_directivesList as $directive) {
|
||||
$directives[$directive] = $this->_options[$directive];
|
||||
}
|
||||
$this->_backend->setDirectives($directives);
|
||||
if (in_array('Zend_Cache_Backend_ExtendedInterface', class_implements($this->_backend))) {
|
||||
$this->_extendedBackend = true;
|
||||
$this->_backendCapabilities = $this->_backend->getCapabilities();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the backend
|
||||
*
|
||||
* @return Zend_Cache_Backend backend object
|
||||
*/
|
||||
public function getBackend()
|
||||
{
|
||||
return $this->_backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public frontend to set an option
|
||||
*
|
||||
* There is an additional validation (relatively to the protected _setOption method)
|
||||
*
|
||||
* @param string $name Name of the option
|
||||
* @param mixed $value Value of the option
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
public function setOption($name, $value)
|
||||
{
|
||||
if (!is_string($name)) {
|
||||
Zend_Cache::throwException("Incorrect option name!");
|
||||
}
|
||||
$name = strtolower($name);
|
||||
if (array_key_exists($name, $this->_options)) {
|
||||
// This is a Core option
|
||||
$this->_setOption($name, $value);
|
||||
return;
|
||||
}
|
||||
if (array_key_exists($name, $this->_specificOptions)) {
|
||||
// This a specic option of this frontend
|
||||
$this->_specificOptions[$name] = $value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public frontend to get an option value
|
||||
*
|
||||
* @param string $name Name of the option
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return mixed option value
|
||||
*/
|
||||
public function getOption($name)
|
||||
{
|
||||
$name = strtolower($name);
|
||||
|
||||
if (array_key_exists($name, $this->_options)) {
|
||||
// This is a Core option
|
||||
return $this->_options[$name];
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $this->_specificOptions)) {
|
||||
// This a specic option of this frontend
|
||||
return $this->_specificOptions[$name];
|
||||
}
|
||||
|
||||
Zend_Cache::throwException("Incorrect option name : $name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an option
|
||||
*
|
||||
* @param string $name Name of the option
|
||||
* @param mixed $value Value of the option
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
private function _setOption($name, $value)
|
||||
{
|
||||
if (!is_string($name) || !array_key_exists($name, $this->_options)) {
|
||||
Zend_Cache::throwException("Incorrect option name : $name");
|
||||
}
|
||||
if ($name == 'lifetime' && empty($value)) {
|
||||
$value = null;
|
||||
}
|
||||
$this->_options[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a new lifetime
|
||||
*
|
||||
* The new value is set for the core/frontend but for the backend too (directive)
|
||||
*
|
||||
* @param int $newLifetime New lifetime (in seconds)
|
||||
* @return void
|
||||
*/
|
||||
public function setLifetime($newLifetime)
|
||||
{
|
||||
$this->_options['lifetime'] = $newLifetime;
|
||||
$this->_backend->setDirectives(array(
|
||||
'lifetime' => $newLifetime
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a cache is available for the given id and (if yes) return it (false else)
|
||||
*
|
||||
* @param string $id Cache id
|
||||
* @param boolean $doNotTestCacheValidity If set to true, the cache validity won't be tested
|
||||
* @param boolean $doNotUnserialize Do not serialize (even if automatic_serialization is true) => for internal use
|
||||
* @return mixed|false Cached datas
|
||||
*/
|
||||
public function load($id, $doNotTestCacheValidity = false, $doNotUnserialize = false)
|
||||
{
|
||||
if (!$this->_options['caching']) {
|
||||
return false;
|
||||
}
|
||||
$id = $this->_id($id); // cache id may need prefix
|
||||
$this->_lastId = $id;
|
||||
self::_validateIdOrTag($id);
|
||||
|
||||
$this->_log("Zend_Cache_Core: load item '{$id}'", 7);
|
||||
$data = $this->_backend->load($id, $doNotTestCacheValidity);
|
||||
if ($data===false) {
|
||||
// no cache available
|
||||
return false;
|
||||
}
|
||||
if ((!$doNotUnserialize) && $this->_options['automatic_serialization']) {
|
||||
// we need to unserialize before sending the result
|
||||
return unserialize($data);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a cache is available for the given id
|
||||
*
|
||||
* @param string $id Cache id
|
||||
* @return int|false Last modified time of cache entry if it is available, false otherwise
|
||||
*/
|
||||
public function test($id)
|
||||
{
|
||||
if (!$this->_options['caching']) {
|
||||
return false;
|
||||
}
|
||||
$id = $this->_id($id); // cache id may need prefix
|
||||
self::_validateIdOrTag($id);
|
||||
$this->_lastId = $id;
|
||||
|
||||
$this->_log("Zend_Cache_Core: test item '{$id}'", 7);
|
||||
return $this->_backend->test($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save some data in a cache
|
||||
*
|
||||
* @param mixed $data Data to put in cache (can be another type than string if automatic_serialization is on)
|
||||
* @param string $id Cache id (if not set, the last cache id will be used)
|
||||
* @param array $tags Cache tags
|
||||
* @param int $specificLifetime If != false, set a specific lifetime for this cache record (null => infinite lifetime)
|
||||
* @param int $priority integer between 0 (very low priority) and 10 (maximum priority) used by some particular backends
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return boolean True if no problem
|
||||
*/
|
||||
public function save($data, $id = null, $tags = array(), $specificLifetime = false, $priority = 8)
|
||||
{
|
||||
if (!$this->_options['caching']) {
|
||||
return true;
|
||||
}
|
||||
if ($id === null) {
|
||||
$id = $this->_lastId;
|
||||
} else {
|
||||
$id = $this->_id($id);
|
||||
}
|
||||
self::_validateIdOrTag($id);
|
||||
self::_validateTagsArray($tags);
|
||||
if ($this->_options['automatic_serialization']) {
|
||||
// we need to serialize datas before storing them
|
||||
$data = serialize($data);
|
||||
} else {
|
||||
if (!is_string($data)) {
|
||||
Zend_Cache::throwException("Datas must be string or set automatic_serialization = true");
|
||||
}
|
||||
}
|
||||
|
||||
// automatic cleaning
|
||||
if ($this->_options['automatic_cleaning_factor'] > 0) {
|
||||
$rand = rand(1, $this->_options['automatic_cleaning_factor']);
|
||||
if ($rand==1) {
|
||||
// new way || deprecated way
|
||||
if ($this->_extendedBackend || method_exists($this->_backend, 'isAutomaticCleaningAvailable')) {
|
||||
$this->_log("Zend_Cache_Core::save(): automatic cleaning running", 7);
|
||||
$this->clean(Zend_Cache::CLEANING_MODE_OLD);
|
||||
} else {
|
||||
$this->_log("Zend_Cache_Core::save(): automatic cleaning is not available/necessary with current backend", 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->_log("Zend_Cache_Core: save item '{$id}'", 7);
|
||||
if ($this->_options['ignore_user_abort']) {
|
||||
$abort = ignore_user_abort(true);
|
||||
}
|
||||
if (($this->_extendedBackend) && ($this->_backendCapabilities['priority'])) {
|
||||
$result = $this->_backend->save($data, $id, $tags, $specificLifetime, $priority);
|
||||
} else {
|
||||
$result = $this->_backend->save($data, $id, $tags, $specificLifetime);
|
||||
}
|
||||
if ($this->_options['ignore_user_abort']) {
|
||||
ignore_user_abort($abort);
|
||||
}
|
||||
|
||||
if (!$result) {
|
||||
// maybe the cache is corrupted, so we remove it !
|
||||
$this->_log("Zend_Cache_Core::save(): failed to save item '{$id}' -> removing it", 4);
|
||||
$this->_backend->remove($id);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->_options['write_control']) {
|
||||
$data2 = $this->_backend->load($id, true);
|
||||
if ($data!=$data2) {
|
||||
$this->_log("Zend_Cache_Core::save(): write control of item '{$id}' failed -> removing it", 4);
|
||||
$this->_backend->remove($id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a cache
|
||||
*
|
||||
* @param string $id Cache id to remove
|
||||
* @return boolean True if ok
|
||||
*/
|
||||
public function remove($id)
|
||||
{
|
||||
if (!$this->_options['caching']) {
|
||||
return true;
|
||||
}
|
||||
$id = $this->_id($id); // cache id may need prefix
|
||||
self::_validateIdOrTag($id);
|
||||
|
||||
$this->_log("Zend_Cache_Core: remove item '{$id}'", 7);
|
||||
return $this->_backend->remove($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean cache entries
|
||||
*
|
||||
* Available modes are :
|
||||
* 'all' (default) => remove all cache entries ($tags is not used)
|
||||
* 'old' => remove too old cache entries ($tags is not used)
|
||||
* 'matchingTag' => remove cache entries matching all given tags
|
||||
* ($tags can be an array of strings or a single string)
|
||||
* 'notMatchingTag' => remove cache entries not matching one of the given tags
|
||||
* ($tags can be an array of strings or a single string)
|
||||
* 'matchingAnyTag' => remove cache entries matching any given tags
|
||||
* ($tags can be an array of strings or a single string)
|
||||
*
|
||||
* @param string $mode
|
||||
* @param array|string $tags
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return boolean True if ok
|
||||
*/
|
||||
public function clean($mode = 'all', $tags = array())
|
||||
{
|
||||
if (!$this->_options['caching']) {
|
||||
return true;
|
||||
}
|
||||
if (!in_array($mode, array(Zend_Cache::CLEANING_MODE_ALL,
|
||||
Zend_Cache::CLEANING_MODE_OLD,
|
||||
Zend_Cache::CLEANING_MODE_MATCHING_TAG,
|
||||
Zend_Cache::CLEANING_MODE_NOT_MATCHING_TAG,
|
||||
Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG))) {
|
||||
Zend_Cache::throwException('Invalid cleaning mode');
|
||||
}
|
||||
self::_validateTagsArray($tags);
|
||||
|
||||
return $this->_backend->clean($mode, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids which match given tags
|
||||
*
|
||||
* In case of multiple tags, a logical AND is made between tags
|
||||
*
|
||||
* @param array $tags array of tags
|
||||
* @return array array of matching cache ids (string)
|
||||
*/
|
||||
public function getIdsMatchingTags($tags = array())
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
if (!($this->_backendCapabilities['tags'])) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
|
||||
}
|
||||
|
||||
$ids = $this->_backend->getIdsMatchingTags($tags);
|
||||
|
||||
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
|
||||
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
|
||||
$prefix = & $this->_options['cache_id_prefix'];
|
||||
$prefixLen = strlen($prefix);
|
||||
foreach ($ids as &$id) {
|
||||
if (strpos($id, $prefix) === 0) {
|
||||
$id = substr($id, $prefixLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids which don't match given tags
|
||||
*
|
||||
* In case of multiple tags, a logical OR is made between tags
|
||||
*
|
||||
* @param array $tags array of tags
|
||||
* @return array array of not matching cache ids (string)
|
||||
*/
|
||||
public function getIdsNotMatchingTags($tags = array())
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
if (!($this->_backendCapabilities['tags'])) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
|
||||
}
|
||||
|
||||
$ids = $this->_backend->getIdsNotMatchingTags($tags);
|
||||
|
||||
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
|
||||
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
|
||||
$prefix = & $this->_options['cache_id_prefix'];
|
||||
$prefixLen = strlen($prefix);
|
||||
foreach ($ids as &$id) {
|
||||
if (strpos($id, $prefix) === 0) {
|
||||
$id = substr($id, $prefixLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids which match any given tags
|
||||
*
|
||||
* In case of multiple tags, a logical OR is made between tags
|
||||
*
|
||||
* @param array $tags array of tags
|
||||
* @return array array of matching any cache ids (string)
|
||||
*/
|
||||
public function getIdsMatchingAnyTags($tags = array())
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
if (!($this->_backendCapabilities['tags'])) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
|
||||
}
|
||||
|
||||
$ids = $this->_backend->getIdsMatchingAnyTags($tags);
|
||||
|
||||
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
|
||||
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
|
||||
$prefix = & $this->_options['cache_id_prefix'];
|
||||
$prefixLen = strlen($prefix);
|
||||
foreach ($ids as &$id) {
|
||||
if (strpos($id, $prefix) === 0) {
|
||||
$id = substr($id, $prefixLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of stored cache ids
|
||||
*
|
||||
* @return array array of stored cache ids (string)
|
||||
*/
|
||||
public function getIds()
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
|
||||
$ids = $this->_backend->getIds();
|
||||
|
||||
// we need to remove cache_id_prefix from ids (see #ZF-6178, #ZF-7600)
|
||||
if (isset($this->_options['cache_id_prefix']) && $this->_options['cache_id_prefix'] !== '') {
|
||||
$prefix = & $this->_options['cache_id_prefix'];
|
||||
$prefixLen = strlen($prefix);
|
||||
foreach ($ids as &$id) {
|
||||
if (strpos($id, $prefix) === 0) {
|
||||
$id = substr($id, $prefixLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of stored tags
|
||||
*
|
||||
* @return array array of stored tags (string)
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
if (!($this->_backendCapabilities['tags'])) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_SUPPORTS_TAG);
|
||||
}
|
||||
return $this->_backend->getTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filling percentage of the backend storage
|
||||
*
|
||||
* @return int integer between 0 and 100
|
||||
*/
|
||||
public function getFillingPercentage()
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
return $this->_backend->getFillingPercentage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of metadatas for the given cache id
|
||||
*
|
||||
* The array will include these keys :
|
||||
* - expire : the expire timestamp
|
||||
* - tags : a string array of tags
|
||||
* - mtime : timestamp of last modification time
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @return array array of metadatas (false if the cache id is not found)
|
||||
*/
|
||||
public function getMetadatas($id)
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
$id = $this->_id($id); // cache id may need prefix
|
||||
return $this->_backend->getMetadatas($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Give (if possible) an extra lifetime to the given cache id
|
||||
*
|
||||
* @param string $id cache id
|
||||
* @param int $extraLifetime
|
||||
* @return boolean true if ok
|
||||
*/
|
||||
public function touch($id, $extraLifetime)
|
||||
{
|
||||
if (!$this->_extendedBackend) {
|
||||
Zend_Cache::throwException(self::BACKEND_NOT_IMPLEMENTS_EXTENDED_IF);
|
||||
}
|
||||
$id = $this->_id($id); // cache id may need prefix
|
||||
|
||||
$this->_log("Zend_Cache_Core: touch item '{$id}'", 7);
|
||||
return $this->_backend->touch($id, $extraLifetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a cache id or a tag (security, reliable filenames, reserved prefixes...)
|
||||
*
|
||||
* Throw an exception if a problem is found
|
||||
*
|
||||
* @param string $string Cache id or tag
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
protected static function _validateIdOrTag($string)
|
||||
{
|
||||
if (!is_string($string)) {
|
||||
Zend_Cache::throwException('Invalid id or tag : must be a string');
|
||||
}
|
||||
if (substr($string, 0, 9) == 'internal-') {
|
||||
Zend_Cache::throwException('"internal-*" ids or tags are reserved');
|
||||
}
|
||||
if (!preg_match('~^[a-zA-Z0-9_]+$~D', $string)) {
|
||||
Zend_Cache::throwException("Invalid id or tag '$string' : must use only [a-zA-Z0-9_]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a tags array (security, reliable filenames, reserved prefixes...)
|
||||
*
|
||||
* Throw an exception if a problem is found
|
||||
*
|
||||
* @param array $tags Array of tags
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
protected static function _validateTagsArray($tags)
|
||||
{
|
||||
if (!is_array($tags)) {
|
||||
Zend_Cache::throwException('Invalid tags array : must be an array');
|
||||
}
|
||||
foreach($tags as $tag) {
|
||||
self::_validateIdOrTag($tag);
|
||||
}
|
||||
reset($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure if we enable logging that the Zend_Log class
|
||||
* is available.
|
||||
* Create a default log object if none is set.
|
||||
*
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
protected function _loggerSanity()
|
||||
{
|
||||
if (!isset($this->_options['logging']) || !$this->_options['logging']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($this->_options['logger']) && $this->_options['logger'] instanceof Zend_Log) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a default logger to the standard output stream
|
||||
require_once 'Zend/Log.php';
|
||||
require_once 'Zend/Log/Writer/Stream.php';
|
||||
require_once 'Zend/Log/Filter/Priority.php';
|
||||
$logger = new Zend_Log(new Zend_Log_Writer_Stream('php://output'));
|
||||
$logger->addFilter(new Zend_Log_Filter_Priority(Zend_Log::WARN, '<='));
|
||||
$this->_options['logger'] = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message at the WARN (4) priority.
|
||||
*
|
||||
* @param string $message
|
||||
* @throws Zend_Cache_Exception
|
||||
* @return void
|
||||
*/
|
||||
protected function _log($message, $priority = 4)
|
||||
{
|
||||
if (!$this->_options['logging']) {
|
||||
return;
|
||||
}
|
||||
if (!(isset($this->_options['logger']) || $this->_options['logger'] instanceof Zend_Log)) {
|
||||
Zend_Cache::throwException('Logging is enabled but logger is not set');
|
||||
}
|
||||
$logger = $this->_options['logger'];
|
||||
$logger->log($message, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make and return a cache id
|
||||
*
|
||||
* Checks 'cache_id_prefix' and returns new id with prefix or simply the id if null
|
||||
*
|
||||
* @param string $id Cache id
|
||||
* @return string Cache id (with or without prefix)
|
||||
*/
|
||||
protected function _id($id)
|
||||
{
|
||||
if (($id !== null) && isset($this->_options['cache_id_prefix'])) {
|
||||
return $this->_options['cache_id_prefix'] . $id; // return with prefix
|
||||
}
|
||||
return $id; // no prefix, just return the $id passed
|
||||
}
|
||||
|
||||
}
|
||||
32
inc/3rdparty/libraries/Zend/Cache/Exception.php
vendored
@ -1,32 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend_Cache
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: Exception.php 24593 2012-01-05 20:35:02Z matthew $
|
||||
*/
|
||||
|
||||
/**
|
||||
* @see Zend_Exception
|
||||
*/
|
||||
require_once 'Zend/Exception.php';
|
||||
|
||||
/**
|
||||
* @package Zend_Cache
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Cache_Exception extends Zend_Exception {}
|
||||
96
inc/3rdparty/libraries/Zend/Exception.php
vendored
@ -1,96 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Zend Framework
|
||||
*
|
||||
* LICENSE
|
||||
*
|
||||
* This source file is subject to the new BSD license that is bundled
|
||||
* with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://framework.zend.com/license/new-bsd
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@zend.com so we can send you a copy immediately.
|
||||
*
|
||||
* @category Zend
|
||||
* @package Zend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
* @version $Id: Exception.php 24593 2012-01-05 20:35:02Z matthew $
|
||||
*/
|
||||
|
||||
/**
|
||||
* @category Zend
|
||||
* @package Zend
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
|
||||
* @license http://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
class Zend_Exception extends Exception
|
||||
{
|
||||
/**
|
||||
* @var null|Exception
|
||||
*/
|
||||
private $_previous = null;
|
||||
|
||||
/**
|
||||
* Construct the exception
|
||||
*
|
||||
* @param string $msg
|
||||
* @param int $code
|
||||
* @param Exception $previous
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($msg = '', $code = 0, Exception $previous = null)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
parent::__construct($msg, (int) $code);
|
||||
$this->_previous = $previous;
|
||||
} else {
|
||||
parent::__construct($msg, (int) $code, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloading
|
||||
*
|
||||
* For PHP < 5.3.0, provides access to the getPrevious() method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, array $args)
|
||||
{
|
||||
if ('getprevious' == strtolower($method)) {
|
||||
return $this->_getPrevious();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation of the exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
if (null !== ($e = $this->getPrevious())) {
|
||||
return $e->__toString()
|
||||
. "\n\nNext "
|
||||
. parent::__toString();
|
||||
}
|
||||
}
|
||||
return parent::__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns previous Exception
|
||||
*
|
||||
* @return Exception|null
|
||||
*/
|
||||
protected function _getPrevious()
|
||||
{
|
||||
return $this->_previous;
|
||||
}
|
||||
}
|
||||
@ -1,728 +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 1.0
|
||||
* @date 2013-02-05
|
||||
* @author Keyvan Minoukadeh
|
||||
* @copyright 2013 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;
|
||||
protected $nextPageUrl;
|
||||
public $allowedParsers = array('libxml', 'html5lib');
|
||||
public $fingerprints = array();
|
||||
public $readability;
|
||||
public $debug = false;
|
||||
public $debugVerbose = 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;
|
||||
if ($this->debugVerbose) echo ' - mem used: ',$mem," (peak: $memPeak)";
|
||||
echo "\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->nextPageUrl = 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;
|
||||
}
|
||||
}
|
||||
$this->debug('No fingerprint matches');
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns SiteConfig instance (joined in order: exact match, wildcard, fingerprint, global, default)
|
||||
public function buildSiteConfig($url, $html='', $add_to_cache=true) {
|
||||
// extract host name
|
||||
$host = @parse_url($url, PHP_URL_HOST);
|
||||
$host = strtolower($host);
|
||||
if (substr($host, 0, 4) == 'www.') $host = substr($host, 4);
|
||||
// is merged version already cached?
|
||||
if (SiteConfig::is_cached("$host.merged")) {
|
||||
$this->debug("Returning cached and merged site config for $host");
|
||||
return SiteConfig::build("$host.merged");
|
||||
}
|
||||
// let's build from site_config/custom/ and standard/
|
||||
$config = SiteConfig::build($host);
|
||||
if ($add_to_cache && $config && !SiteConfig::is_cached("$host")) {
|
||||
SiteConfig::add_to_cache($host, $config);
|
||||
}
|
||||
// if no match, use defaults
|
||||
if (!$config) $config = new SiteConfig();
|
||||
// load fingerprint config?
|
||||
if ($config->autodetect_on_failure()) {
|
||||
// check HTML for fingerprints
|
||||
if (!empty($this->fingerprints) && ($_fphost = $this->findHostUsingFingerprints($html))) {
|
||||
if ($config_fingerprint = SiteConfig::build($_fphost)) {
|
||||
$this->debug("Appending site config settings from $_fphost (fingerprint match)");
|
||||
$config->append($config_fingerprint);
|
||||
if ($add_to_cache && !SiteConfig::is_cached($_fphost)) {
|
||||
//$config_fingerprint->cache_in_apc = true;
|
||||
SiteConfig::add_to_cache($_fphost, $config_fingerprint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// load global config?
|
||||
if ($config->autodetect_on_failure()) {
|
||||
if ($config_global = SiteConfig::build('global', true)) {
|
||||
$this->debug('Appending site config settings from global.txt');
|
||||
$config->append($config_global);
|
||||
if ($add_to_cache && !SiteConfig::is_cached('global')) {
|
||||
//$config_global->cache_in_apc = true;
|
||||
SiteConfig::add_to_cache('global', $config_global);
|
||||
}
|
||||
}
|
||||
}
|
||||
// store copy of merged config
|
||||
if ($add_to_cache) {
|
||||
// do not store in APC if wildcard match
|
||||
$use_apc = ($host == $config->cache_key);
|
||||
$config->cache_key = null;
|
||||
SiteConfig::add_to_cache("$host.merged", $config, $use_apc);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
// 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();
|
||||
$this->config = $this->buildSiteConfig($url, $html);
|
||||
|
||||
// do string replacements
|
||||
if (!empty($this->config->find_string)) {
|
||||
if (count($this->config->find_string) == count($this->config->replace_string)) {
|
||||
$html = str_replace($this->config->find_string, $this->config->replace_string, $html, $_count);
|
||||
$this->debug("Strings replaced: $_count (find_string and/or replace_string)");
|
||||
} else {
|
||||
$this->debug('Skipped string replacement - incorrect number of find-replace strings in site config');
|
||||
}
|
||||
unset($_count);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
unset($tidy);
|
||||
}
|
||||
|
||||
// load and parse html
|
||||
$_parser = $this->config->parser();
|
||||
if (!in_array($_parser, $this->allowedParsers)) {
|
||||
$this->debug("HTML parser $_parser not listed, using libxml instead");
|
||||
$_parser = 'libxml';
|
||||
}
|
||||
$this->debug("Attempting to parse HTML with $_parser");
|
||||
$this->readability = new Readability($html, $url, $_parser);
|
||||
|
||||
// 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 next page link
|
||||
foreach ($this->config->next_page_link as $pattern) {
|
||||
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
|
||||
if (is_string($elems)) {
|
||||
$this->nextPageUrl = trim($elems);
|
||||
break;
|
||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
||||
foreach ($elems as $item) {
|
||||
if ($item instanceof DOMElement && $item->hasAttribute('href')) {
|
||||
$this->nextPageUrl = $item->getAttribute('href');
|
||||
break 2;
|
||||
} elseif ($item instanceof DOMAttr && $item->value) {
|
||||
$this->nextPageUrl = $item->value;
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to get title
|
||||
foreach ($this->config->title as $pattern) {
|
||||
// $this->debug("Trying $pattern");
|
||||
$elems = @$xpath->evaluate($pattern, $this->readability->dom);
|
||||
if (is_string($elems)) {
|
||||
$this->title = trim($elems);
|
||||
$this->debug('Title expression evaluated as string: '.$this->title);
|
||||
$this->debug("...XPath match: $pattern");
|
||||
break;
|
||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
||||
$this->title = $elems->item(0)->textContent;
|
||||
$this->debug('Title matched: '.$this->title);
|
||||
$this->debug("...XPath match: $pattern");
|
||||
// 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)) {
|
||||
if (trim($elems) != '') {
|
||||
$this->author[] = trim($elems);
|
||||
$this->debug('Author expression evaluated as string: '.trim($elems));
|
||||
$this->debug("...XPath match: $pattern");
|
||||
break;
|
||||
}
|
||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
||||
foreach ($elems as $elem) {
|
||||
if (!isset($elem->parentNode)) continue;
|
||||
$this->author[] = trim($elem->textContent);
|
||||
$this->debug('Author matched: '.trim($elem->textContent));
|
||||
}
|
||||
if (!empty($this->author)) {
|
||||
$this->debug("...XPath match: $pattern");
|
||||
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);
|
||||
$this->debug('Language matched: '.$this->language);
|
||||
break;
|
||||
}
|
||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
||||
foreach ($elems as $elem) {
|
||||
if (!isset($elem->parentNode)) continue;
|
||||
$this->language = trim($elem->textContent);
|
||||
$this->debug('Language matched: '.$this->language);
|
||||
}
|
||||
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->date = strtotime(trim($elems, "; \t\n\r\0\x0B"));
|
||||
} elseif ($elems instanceof DOMNodeList && $elems->length > 0) {
|
||||
$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 {
|
||||
$this->debug('Date matched: '.date('Y-m-d H:i:s', $this->date));
|
||||
$this->debug("...XPath match: $pattern");
|
||||
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');
|
||||
$this->debug("...XPath match: $pattern");
|
||||
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);
|
||||
}
|
||||
}
|
||||
if ($this->body->hasChildNodes()) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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->title = $elems->item(0)->textContent;
|
||||
$this->debug('hNews: found entry-title: '.$this->title);
|
||||
// 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->date = strtotime(trim($elems->item(0)->textContent));
|
||||
// remove date from document
|
||||
//$elems->item(0)->parentNode->removeChild($elems->item(0));
|
||||
if ($this->date) {
|
||||
$this->debug('hNews: found publication date: '.date('Y-m-d H:i:s', $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) {
|
||||
$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);
|
||||
$this->debug('hNews: found author: '.trim($_fn->textContent));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (trim($author->textContent) != '') {
|
||||
$this->author[] = trim($author->textContent);
|
||||
$this->debug('hNews: found 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->title = $elems->item(0)->textContent;
|
||||
$this->debug('Title found (.instapaper_title): '.$this->title);
|
||||
// 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) {
|
||||
$author = trim($elems->item(0)->textContent);
|
||||
if ($author != '') {
|
||||
$this->debug("Author found (rel=\"author\"): $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->date = strtotime(trim($elems->item(0)->textContent));
|
||||
// remove date from document
|
||||
//$elems->item(0)->parentNode->removeChild($elems->item(0));
|
||||
if ($this->date) {
|
||||
$this->debug('Date found (pubdate marked time element): '.date('Y-m-d H:i:s', $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);
|
||||
}
|
||||
}
|
||||
// prevent self-closing iframes
|
||||
$elems = $this->body->getElementsByTagName('iframe');
|
||||
for ($i = $elems->length-1; $i >= 0; $i--) {
|
||||
$e = $elems->item($i);
|
||||
if (!$e->hasChildNodes()) {
|
||||
$e->appendChild($this->body->ownerDocument->createTextNode('[embedded content]'));
|
||||
}
|
||||
}
|
||||
// remove image lazy loading - WordPress plugin http://wordpress.org/extend/plugins/lazy-load/
|
||||
// the plugin replaces the src attribute to point to a 1x1 gif and puts the original src
|
||||
// inside the data-lazy-src attribute. It also places the original image inside a noscript element
|
||||
// next to the amended one.
|
||||
$elems = @$xpath->query("//img[@data-lazy-src]", $this->body);
|
||||
for ($i = $elems->length-1; $i >= 0; $i--) {
|
||||
$e = $elems->item($i);
|
||||
// let's see if we can grab image from noscript
|
||||
if ($e->nextSibling !== null && $e->nextSibling->nodeName === 'noscript') {
|
||||
$_new_elem = $e->ownerDocument->createDocumentFragment();
|
||||
@$_new_elem->appendXML($e->nextSibling->innerHTML);
|
||||
$e->nextSibling->parentNode->replaceChild($_new_elem, $e->nextSibling);
|
||||
$e->parentNode->removeChild($e);
|
||||
} else {
|
||||
// Use data-lazy-src as src value
|
||||
$e->setAttribute('src', $e->getAttribute('data-lazy-src'));
|
||||
$e->removeAttribute('data-lazy-src');
|
||||
}
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
public function getNextPageUrl() {
|
||||
return $this->nextPageUrl;
|
||||
}
|
||||
}
|
||||
?>
|
||||
@ -1,338 +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.7
|
||||
* @date 2012-08-27
|
||||
* @author Keyvan Minoukadeh
|
||||
* @copyright 2012 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 (bool or null if undeclared)
|
||||
public $tidy = null;
|
||||
|
||||
protected $default_tidy = true; // used if undeclared
|
||||
|
||||
// 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).
|
||||
// bool or null if undeclared
|
||||
public $autodetect_on_failure = null;
|
||||
protected $default_autodetect_on_failure = true; // used if undeclared
|
||||
|
||||
// Clean up content block - attempt to remove elements that appear to be superfluous
|
||||
// bool or null if undeclared
|
||||
public $prune = null;
|
||||
protected $default_prune = true; // used if undeclared
|
||||
|
||||
// Test URL - if present, can be used to test the config above
|
||||
public $test_url = array();
|
||||
|
||||
// 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();
|
||||
|
||||
public $next_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();
|
||||
|
||||
// Which parser to use for turning raw HTML into a DOMDocument (either 'libxml' or 'html5lib')
|
||||
// string or null if undeclared
|
||||
public $parser = null;
|
||||
protected $default_parser = 'libxml'; // used if undeclared
|
||||
|
||||
// Strings to search for in HTML before processing begins (used with $replace_string)
|
||||
public $find_string = array();
|
||||
// Strings to replace those found in $find_string before HTML processing begins
|
||||
public $replace_string = array();
|
||||
|
||||
// the options below cannot be set in the config files which this class represents
|
||||
|
||||
//public $cache_in_apc = false; // used to decide if we should cache in apc or not
|
||||
public $cache_key = null;
|
||||
public static $debug = false;
|
||||
protected static $apc = 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";
|
||||
echo "\n";
|
||||
ob_flush();
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
// enable APC caching of certain site config files?
|
||||
// If enabled the following site config files will be
|
||||
// cached in APC cache (when requested for first time):
|
||||
// * anything in site_config/custom/ and its corresponding file in site_config/standard/
|
||||
// * the site config files associated with HTML fingerprints
|
||||
// * the global site config file
|
||||
// returns true if enabled, false otherwise
|
||||
public static function use_apc($apc=true) {
|
||||
if (!function_exists('apc_add')) {
|
||||
if ($apc) self::debug('APC will not be used (function apc_add does not exist)');
|
||||
return false;
|
||||
}
|
||||
self::$apc = $apc;
|
||||
return $apc;
|
||||
}
|
||||
|
||||
// return bool or null
|
||||
public function tidy($use_default=true) {
|
||||
if ($use_default) return (isset($this->tidy)) ? $this->tidy : $this->default_tidy;
|
||||
return $this->tidy;
|
||||
}
|
||||
|
||||
// return bool or null
|
||||
public function prune($use_default=true) {
|
||||
if ($use_default) return (isset($this->prune)) ? $this->prune : $this->default_prune;
|
||||
return $this->prune;
|
||||
}
|
||||
|
||||
// return string or null
|
||||
public function parser($use_default=true) {
|
||||
if ($use_default) return (isset($this->parser)) ? $this->parser : $this->default_parser;
|
||||
return $this->parser;
|
||||
}
|
||||
|
||||
// return bool or null
|
||||
public function autodetect_on_failure($use_default=true) {
|
||||
if ($use_default) return (isset($this->autodetect_on_failure)) ? $this->autodetect_on_failure : $this->default_autodetect_on_failure;
|
||||
return $this->autodetect_on_failure;
|
||||
}
|
||||
|
||||
public static function set_config_path($path, $fallback=null) {
|
||||
self::$config_path = $path;
|
||||
self::$config_path_fallback = $fallback;
|
||||
}
|
||||
|
||||
public static function add_to_cache($key, SiteConfig $config, $use_apc=true) {
|
||||
$key = strtolower($key);
|
||||
if (substr($key, 0, 4) == 'www.') $key = substr($key, 4);
|
||||
if ($config->cache_key) $key = $config->cache_key;
|
||||
self::$config_cache[$key] = $config;
|
||||
if (self::$apc && $use_apc) {
|
||||
self::debug("Adding site config to APC cache with key sc.$key");
|
||||
apc_add("sc.$key", $config);
|
||||
}
|
||||
self::debug("Cached site config with key $key");
|
||||
}
|
||||
|
||||
public static function is_cached($key) {
|
||||
$key = strtolower($key);
|
||||
if (substr($key, 0, 4) == 'www.') $key = substr($key, 4);
|
||||
if (array_key_exists($key, self::$config_cache)) {
|
||||
return true;
|
||||
} elseif (self::$apc && (bool)apc_fetch("sc.$key")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function append(SiteConfig $newconfig) {
|
||||
// check for commands where we accept multiple statements (no test_url)
|
||||
foreach (array('title', 'body', 'author', 'date', 'strip', 'strip_id_or_class', 'strip_image_src', 'single_page_link', 'single_page_link_in_feed', 'next_page_link', 'http_header', 'find_string', 'replace_string') as $var) {
|
||||
// append array elements for this config variable from $newconfig to this config
|
||||
//$this->$var = $this->$var + $newconfig->$var;
|
||||
$this->$var = array_unique(array_merge($this->$var, $newconfig->$var));
|
||||
}
|
||||
// check for single statement commands
|
||||
// we do not overwrite existing non null values
|
||||
foreach (array('tidy', 'prune', 'parser', 'autodetect_on_failure') as $var) {
|
||||
if ($this->$var === null) $this->$var = $newconfig->$var;
|
||||
}
|
||||
}
|
||||
|
||||
// returns SiteConfig instance if an appropriate one is found, false otherwise
|
||||
// if $exact_host_match is true, we will not look for wildcard config matches
|
||||
// by default if host is 'test.example.org' we will look for and load '.example.org.txt' if it exists
|
||||
public static function build($host, $exact_host_match=false) {
|
||||
$host = strtolower($host);
|
||||
if (substr($host, 0, 4) == 'www.') $host = substr($host, 4);
|
||||
if (!$host || (strlen($host) > 200) || !preg_match(self::HOSTNAME_REGEX, ltrim($host, '.'))) return false;
|
||||
// check for site configuration
|
||||
$try = array($host);
|
||||
// should we look for wildcard matches
|
||||
if (!$exact_host_match) {
|
||||
$split = explode('.', $host);
|
||||
if (count($split) > 1) {
|
||||
array_shift($split);
|
||||
$try[] = '.'.implode('.', $split);
|
||||
}
|
||||
}
|
||||
|
||||
// look for site config file in primary folder
|
||||
self::debug(". looking for site config for $host in primary folder");
|
||||
foreach ($try as $h) {
|
||||
if (array_key_exists($h, self::$config_cache)) {
|
||||
self::debug("... site config for $h already loaded in this request");
|
||||
return self::$config_cache[$h];
|
||||
} elseif (self::$apc && ($sconfig = apc_fetch("sc.$h"))) {
|
||||
self::debug("... site config for $h in APC cache");
|
||||
return $sconfig;
|
||||
} elseif (file_exists(self::$config_path."/$h.txt")) {
|
||||
self::debug("... found site config ($h.txt)");
|
||||
$file_primary = self::$config_path."/$h.txt";
|
||||
$matched_name = $h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we found site config, process it
|
||||
if (isset($file_primary)) {
|
||||
$config_lines = file($file_primary, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if (!$config_lines || !is_array($config_lines)) return false;
|
||||
$config = self::build_from_array($config_lines);
|
||||
// if APC caching is available and enabled, mark this for cache
|
||||
//$config->cache_in_apc = true;
|
||||
$config->cache_key = $matched_name;
|
||||
|
||||
// if autodetec on failure is off (on by default) we do not need to look
|
||||
// in secondary folder
|
||||
if (!$config->autodetect_on_failure()) {
|
||||
self::debug('... autodetect on failure is disabled (no other site config files will be loaded)');
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
// look for site config file in secondary folder
|
||||
if (isset(self::$config_path_fallback)) {
|
||||
self::debug(". looking for site config for $host in secondary folder");
|
||||
foreach ($try as $h) {
|
||||
if (file_exists(self::$config_path_fallback."/$h.txt")) {
|
||||
self::debug("... found site config in secondary folder ($h.txt)");
|
||||
$file_secondary = self::$config_path_fallback."/$h.txt";
|
||||
$matched_name = $h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isset($file_secondary)) {
|
||||
self::debug("... no site config match in secondary folder");
|
||||
}
|
||||
}
|
||||
|
||||
// return false if no config file found
|
||||
if (!isset($file_primary) && !isset($file_secondary)) {
|
||||
self::debug("... no site config match for $host");
|
||||
return false;
|
||||
}
|
||||
|
||||
// return primary config if secondary not found
|
||||
if (!isset($file_secondary) && isset($config)) {
|
||||
return $config;
|
||||
}
|
||||
|
||||
// process secondary config file
|
||||
$config_lines = file($file_secondary, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if (!$config_lines || !is_array($config_lines)) {
|
||||
// failed to process secondary
|
||||
if (isset($config)) {
|
||||
// return primary config
|
||||
return $config;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// merge with primary and return
|
||||
if (isset($config)) {
|
||||
self::debug('. merging config files');
|
||||
$config->append(self::build_from_array($config_lines));
|
||||
return $config;
|
||||
} else {
|
||||
// return just secondary
|
||||
$config = self::build_from_array($config_lines);
|
||||
// if APC caching is available and enabled, mark this for cache
|
||||
//$config->cache_in_apc = true;
|
||||
$config->cache_key = $matched_name;
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
|
||||
public static function build_from_array(array $lines) {
|
||||
$config = new SiteConfig();
|
||||
foreach ($lines 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', 'next_page_link', 'http_header', 'test_url', 'find_string', 'replace_string'))) {
|
||||
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('parser'))) {
|
||||
$config->$command = $val;
|
||||
// check for replace_string(find): replace
|
||||
} 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->find_string, $match[2]);
|
||||
array_push($config->$command, $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
?>
|
||||
185
inc/3rdparty/libraries/feedwriter/FeedItem.php
vendored
@ -1,185 +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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set element (overwrites existing elements with $elementName)
|
||||
*
|
||||
* @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 setElement($elementName, $content, $attributes = null)
|
||||
{
|
||||
if (isset($this->elements[$elementName])) {
|
||||
unset($this->elements[$elementName]);
|
||||
}
|
||||
$this->addElement($elementName, $content, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$i = 0;
|
||||
if (isset($this->elements[$elementName])) {
|
||||
$i = count($this->elements[$elementName]);
|
||||
} else {
|
||||
$this->elements[$elementName] = array();
|
||||
}
|
||||
$this->elements[$elementName][$i]['name'] = $elementName;
|
||||
$this->elements[$elementName][$i]['content'] = $content;
|
||||
$this->elements[$elementName][$i]['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 = 'description';
|
||||
$this->setElement($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->setElement('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 == RSS2)
|
||||
{
|
||||
$tag = 'pubDate';
|
||||
$value = date(DATE_RSS, $date);
|
||||
}
|
||||
else
|
||||
{
|
||||
$tag = 'dc:date';
|
||||
$value = date("Y-m-d", $date);
|
||||
}
|
||||
|
||||
$this->setElement($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->setElement('link', $link);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->setElement('link','',array('href'=>$link));
|
||||
$this->setElement('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->setElement('enclosure','',$attributes);
|
||||
}
|
||||
|
||||
} // end of class FeedItem
|
||||
?>
|
||||