forked from github/kensanata.oddmuse
Compare commits
594 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1d39e3195 | ||
|
|
48a00a6ff6 | ||
|
|
a3ee3c60ce | ||
|
|
d69063599e | ||
|
|
0146225c4f | ||
|
|
50fca72f82 | ||
|
|
1a4e6aa527 | ||
|
|
cdf8b561a6 | ||
|
|
905d8c930e | ||
|
|
f64c6d470b | ||
|
|
0657d84769 | ||
|
|
0181d8b944 | ||
|
|
fae5f1e345 | ||
|
|
81b179acac | ||
|
|
bc810ee0ce | ||
|
|
3a4236bc45 | ||
|
|
8642ae63a2 | ||
|
|
3c5373f76b | ||
|
|
58e297b092 | ||
|
|
cd4f6dc64c | ||
|
|
5a112b64b3 | ||
|
|
a7b0c661c8 | ||
|
|
b2f9a0044b | ||
|
|
d0cdd451e4 | ||
|
|
86334d6532 | ||
|
|
733752727d | ||
|
|
6635803807 | ||
|
|
7f3488baaa | ||
|
|
dcc318f34e | ||
|
|
17edc1c523 | ||
|
|
85912f211b | ||
|
|
52d7239400 | ||
|
|
9c691e5b9b | ||
|
|
0e45ea2e99 | ||
|
|
8773242dba | ||
|
|
267cd53adb | ||
|
|
ce82a328b6 | ||
|
|
b925805800 | ||
|
|
c8c50b4e81 | ||
|
|
4a976278d5 | ||
|
|
1255fe8168 | ||
|
|
081e8243d7 | ||
|
|
a20fc60617 | ||
|
|
770de2986a | ||
|
|
0551018de1 | ||
|
|
aa77f2ce2f | ||
|
|
471994f7b1 | ||
|
|
9d2c0216f6 | ||
|
|
82d888f0ea | ||
|
|
ccaf283204 | ||
|
|
3fb5319562 | ||
|
|
76c92f027c | ||
|
|
d828454511 | ||
|
|
34c6e93780 | ||
|
|
0a6f473098 | ||
|
|
4d67f9bfd2 | ||
|
|
6e80adc293 | ||
|
|
dc3fb65317 | ||
|
|
6a652de193 | ||
|
|
4747235fe7 | ||
|
|
0ecbeeb2c4 | ||
|
|
65378d91cb | ||
|
|
cb6a6bf4a6 | ||
|
|
db67c34203 | ||
|
|
ccf8fe2314 | ||
|
|
e336086cf0 | ||
|
|
5315b3f6ad | ||
|
|
e31abd57bc | ||
|
|
5bf60bb5d8 | ||
|
|
ad672aff28 | ||
|
|
e49af47d30 | ||
|
|
ecbe6a859a | ||
|
|
413228c56c | ||
|
|
a905de7ab5 | ||
|
|
a09409f375 | ||
|
|
764c0ffcf1 | ||
|
|
5962745937 | ||
|
|
d380062ec6 | ||
|
|
cd8066233c | ||
|
|
6f04d2044f | ||
|
|
f41ded592b | ||
|
|
8a2c9eca9c | ||
|
|
d712a17f82 | ||
|
|
504190b752 | ||
|
|
56e515a791 | ||
|
|
36feb62052 | ||
|
|
239f15cdbc | ||
|
|
f39cfd3235 | ||
|
|
329699a6aa | ||
|
|
28965bdaa6 | ||
|
|
ed42d2dad5 | ||
|
|
45b21cbdb8 | ||
|
|
b540093c2c | ||
|
|
d164d47e24 | ||
|
|
a56b92ecb3 | ||
|
|
114d914754 | ||
|
|
875051ea84 | ||
|
|
003357acad | ||
|
|
e1c77c4ba6 | ||
|
|
88475c3e41 | ||
|
|
e80f05301d | ||
|
|
a624e78975 | ||
|
|
8cd869f0f9 | ||
|
|
b09b3f8f8e | ||
|
|
b9043ffd98 | ||
|
|
54d3dc400a | ||
|
|
3962068385 | ||
|
|
a0b74ac3c6 | ||
|
|
870d75ac64 | ||
|
|
42d8260ce4 | ||
|
|
bfda4abe54 | ||
|
|
4b0d411564 | ||
|
|
6790de2d6a | ||
|
|
2784628544 | ||
|
|
dd8c687b2b | ||
|
|
9f4ceb2d72 | ||
|
|
0f8a4fa1df | ||
|
|
3b16b58880 | ||
|
|
192a902932 | ||
|
|
aedf77cff8 | ||
|
|
728547f309 | ||
|
|
33f5484441 | ||
|
|
a225486709 | ||
|
|
3f7f9ec1eb | ||
|
|
174aac5570 | ||
|
|
954232f7c8 | ||
|
|
067658fd10 | ||
|
|
b2b2b0f6cc | ||
|
|
408d169729 | ||
|
|
0eddbd5806 | ||
|
|
210a28afd4 | ||
|
|
94a16bd463 | ||
|
|
4005e246f7 | ||
|
|
6b3cd0437f | ||
|
|
c961748b49 | ||
|
|
7327eb8e0c | ||
|
|
1b0d595945 | ||
|
|
b7e2a04bb4 | ||
|
|
848eb65ad0 | ||
|
|
7ae98f4ed9 | ||
|
|
dc792691d4 | ||
|
|
0a57a8e89b | ||
|
|
f75d415322 | ||
|
|
1654562236 | ||
|
|
ba0535f39d | ||
|
|
e5b069f70b | ||
|
|
d34b9f669b | ||
|
|
ae2061fcaf | ||
|
|
65475cf2e8 | ||
|
|
ce2e63be6b | ||
|
|
007ce8db86 | ||
|
|
5997c3ea02 | ||
|
|
6895428844 | ||
|
|
2bc2d1f927 | ||
|
|
873ce10ced | ||
|
|
3855c83a7e | ||
|
|
281736a082 | ||
|
|
63d8e24c2f | ||
|
|
c66f1a6f8e | ||
|
|
6418cab98c | ||
|
|
040d51bc93 | ||
|
|
41c3245a51 | ||
|
|
ffc2a0b12f | ||
|
|
90c632f4ab | ||
|
|
9e8def306a | ||
|
|
f4e551111a | ||
|
|
1ead545561 | ||
|
|
f5fc46821f | ||
|
|
ce9f8d9b74 | ||
|
|
1104ecf72f | ||
|
|
13fc4cc0a4 | ||
|
|
31f7b330c5 | ||
|
|
ad3909888b | ||
|
|
f7b99d44fd | ||
|
|
2cf943d178 | ||
|
|
b2b1c16247 | ||
|
|
f91041b677 | ||
|
|
30b0faa70e | ||
|
|
174c4f6642 | ||
|
|
9ba5b119f8 | ||
|
|
b8ce7f07df | ||
|
|
c9a9db28a6 | ||
|
|
b21dc2db01 | ||
|
|
0f61a795ee | ||
|
|
ed30eeffb0 | ||
|
|
9473e08a14 | ||
|
|
16796b7fe5 | ||
|
|
1e704ece07 | ||
|
|
1f824d5838 | ||
|
|
d8bc7bd82f | ||
|
|
a2e1a73d10 | ||
|
|
68e71cc535 | ||
|
|
548bdf7d1c | ||
|
|
a6314a2c44 | ||
|
|
85e8800435 | ||
|
|
41b5c65e22 | ||
|
|
3ad0438a78 | ||
|
|
3ab80d0e3f | ||
|
|
2312787ec1 | ||
|
|
b8b2035151 | ||
|
|
4aefc9a648 | ||
|
|
c1cb7516a8 | ||
|
|
5aba2ae56f | ||
|
|
f958de8165 | ||
|
|
f79b188e82 | ||
|
|
dc182fde16 | ||
|
|
5fbc444a53 | ||
|
|
a7f83b2ba7 | ||
|
|
5bfe0073d9 | ||
|
|
3b3e707d3b | ||
|
|
ee932dee37 | ||
|
|
772bf2745a | ||
|
|
8cbd7dabec | ||
|
|
860d2f0bce | ||
|
|
ef4aac31b4 | ||
|
|
be1789f996 | ||
|
|
b7c1045783 | ||
|
|
54913a0131 | ||
|
|
a6036d2455 | ||
|
|
12d362106e | ||
|
|
b292ab5983 | ||
|
|
090c708ce9 | ||
|
|
67dc63c433 | ||
|
|
2606b846f0 | ||
|
|
ff66da7c65 | ||
|
|
76a6b4bfad | ||
|
|
ddadb21517 | ||
|
|
e62423ec84 | ||
|
|
083fdb3371 | ||
|
|
6cfe1ab29a | ||
|
|
4e43357daa | ||
|
|
7a82dd40ff | ||
|
|
584f23b08a | ||
|
|
b21f33951f | ||
|
|
acacd5ff01 | ||
|
|
a333fbf3b1 | ||
|
|
eb7de3c722 | ||
|
|
ac7ca05707 | ||
|
|
32ce6cde2d | ||
|
|
d922b7413c | ||
|
|
54b0fbcfaf | ||
|
|
d919dd5d94 | ||
|
|
48ec41b566 | ||
|
|
0d592e12f2 | ||
|
|
ee8b8db17f | ||
|
|
252033bff8 | ||
|
|
e21333faca | ||
|
|
ff3d7a9ea2 | ||
|
|
dd34aff9ef | ||
|
|
9eee99514b | ||
|
|
5ead2bdcf2 | ||
|
|
8b5267a013 | ||
|
|
423dad59b1 | ||
|
|
f79aa04c68 | ||
|
|
7535c859b9 | ||
|
|
ac34706853 | ||
|
|
b705fcb3f1 | ||
|
|
ae13fe5235 | ||
|
|
5830fbcd71 | ||
|
|
a2123aeb76 | ||
|
|
f07bdddb5d | ||
|
|
5da39c28a8 | ||
|
|
28f08d9583 | ||
|
|
0ba55b13d6 | ||
|
|
270e1aad9e | ||
|
|
ec1b7d1a46 | ||
|
|
718e17f07f | ||
|
|
48ff67a27c | ||
|
|
39eabb6cbb | ||
|
|
28d7d0a897 | ||
|
|
3f295e7a88 | ||
|
|
d6ef6bb56c | ||
|
|
f276ae6469 | ||
|
|
f6d4d5f517 | ||
|
|
0ee7a23018 | ||
|
|
61dec7317b | ||
|
|
7a1b4c38b1 | ||
|
|
0e97e7bee5 | ||
|
|
769bc55821 | ||
|
|
bd9cdf56bf | ||
|
|
be97b89b87 | ||
|
|
a9f5fe4374 | ||
|
|
a88878dc08 | ||
|
|
dcaabca09f | ||
|
|
d680d0173b | ||
|
|
3e8cb56f37 | ||
|
|
33ec1b1743 | ||
|
|
6f4b5451fc | ||
|
|
2770defdc4 | ||
|
|
1d6f435ca8 | ||
|
|
00856c0436 | ||
|
|
e0ee3bb24d | ||
|
|
8bbbed026d | ||
|
|
c9767ce84b | ||
|
|
b984f3ecad | ||
|
|
f58f784009 | ||
|
|
b6ee8da7c6 | ||
|
|
ae59ab3746 | ||
|
|
28ab5885a0 | ||
|
|
881dbc7094 | ||
|
|
2537a97900 | ||
|
|
20de100781 | ||
|
|
e1c3445136 | ||
|
|
e8be707079 | ||
|
|
680c3b96ce | ||
|
|
ba4ce729e6 | ||
|
|
d74da11382 | ||
|
|
9d0e6cb3cb | ||
|
|
027557999d | ||
|
|
41fb4d0c4b | ||
|
|
7cf0904b2b | ||
|
|
7330f1961b | ||
|
|
6e9d50204f | ||
|
|
629157d367 | ||
|
|
23af89d21e | ||
|
|
a3cf3dbb99 | ||
|
|
a28f380125 | ||
|
|
eb8fd17e01 | ||
|
|
4784e3bc88 | ||
|
|
cff287ee24 | ||
|
|
07eeffa3e3 | ||
|
|
fe7a5e564a | ||
|
|
00aa0761d7 | ||
|
|
f09a81b3b9 | ||
|
|
c43b0695c2 | ||
|
|
e9e436c0b8 | ||
|
|
c0194c1178 | ||
|
|
c6a0cb33ac | ||
|
|
e9375974cb | ||
|
|
a226902617 | ||
|
|
213774d6ff | ||
|
|
98d96fd1d8 | ||
|
|
ddb8fb06e6 | ||
|
|
25989f78a5 | ||
|
|
afdb7a9dcb | ||
|
|
2e79a843c8 | ||
|
|
54370da235 | ||
|
|
8ec456ed41 | ||
|
|
020df9098d | ||
|
|
43839ac1aa | ||
|
|
3c2f96250b | ||
|
|
9def2d2eb2 | ||
|
|
3ad40b84fb | ||
|
|
ecda4c3d98 | ||
|
|
74a0576c5d | ||
|
|
a6e07a9886 | ||
|
|
b76b61dc86 | ||
|
|
c3cb434973 | ||
|
|
62f82c2af2 | ||
|
|
d454973294 | ||
|
|
cba29c8981 | ||
|
|
4a812931c8 | ||
|
|
093a6da63d | ||
|
|
0ab5261bc6 | ||
|
|
1d4f3e4a28 | ||
|
|
6babcffd00 | ||
|
|
977cbba251 | ||
|
|
2fc4f4b054 | ||
|
|
53566c8434 | ||
|
|
563e5cd9c6 | ||
|
|
365d33b602 | ||
|
|
eef56e435d | ||
|
|
2044564981 | ||
|
|
50c9b79858 | ||
|
|
d99f62ea7e | ||
|
|
c11188fd3e | ||
|
|
dd22a852eb | ||
|
|
62b2e22da8 | ||
|
|
5483bbf386 | ||
|
|
8608464863 | ||
|
|
b0d983c817 | ||
|
|
26a5db86b0 | ||
|
|
5f58256543 | ||
|
|
c5c088deb1 | ||
|
|
a5b5af9c07 | ||
|
|
0dcf49e2cf | ||
|
|
f3885aa213 | ||
|
|
6136b399a6 | ||
|
|
5cc7d55152 | ||
|
|
4112d2acc4 | ||
|
|
f270a3ced4 | ||
|
|
7f74d3c211 | ||
|
|
375c844e37 | ||
|
|
efce35e250 | ||
|
|
cff4f1fd28 | ||
|
|
6f9ded7e41 | ||
|
|
40c01683fd | ||
|
|
ab3a7752ba | ||
|
|
08a4861dc3 | ||
|
|
d7c40d4dbe | ||
|
|
f8360bebad | ||
|
|
45a0558fcc | ||
|
|
f4ff56e69f | ||
|
|
0d7236c047 | ||
|
|
686f24251b | ||
|
|
0841c834b9 | ||
|
|
5225bded01 | ||
|
|
e0d18c31e2 | ||
|
|
670b69c118 | ||
|
|
f4d0f300e6 | ||
|
|
53a7a9a80c | ||
|
|
4f675de687 | ||
|
|
dffe5e3053 | ||
|
|
201970ba0b | ||
|
|
7e9137c6f8 | ||
|
|
9d81a1e3d2 | ||
|
|
2f58de9aa4 | ||
|
|
5ca2bf3efb | ||
|
|
96bc4e14fa | ||
|
|
ad1059dbb2 | ||
|
|
508396d1d1 | ||
|
|
6d457ff87b | ||
|
|
860cb15324 | ||
|
|
56e76a4883 | ||
|
|
3fa8e0a6b0 | ||
|
|
4c4ab98d47 | ||
|
|
ca62cbf446 | ||
|
|
ef3bde90ac | ||
|
|
7771c541bb | ||
|
|
6adabedefe | ||
|
|
a776c67cd6 | ||
|
|
0ddc1770a3 | ||
|
|
44fa8cfb5a | ||
|
|
96c21c2240 | ||
|
|
1c25325257 | ||
|
|
fd5b4e84b1 | ||
|
|
9beff3748b | ||
|
|
87dedeab85 | ||
|
|
1e73ae22d3 | ||
|
|
5e9b02b5b1 | ||
|
|
deec99c353 | ||
|
|
d1b0ac4ccb | ||
|
|
06881768c3 | ||
|
|
8e1f6c92e3 | ||
|
|
1ebc5192ff | ||
|
|
7c52b7b4c2 | ||
|
|
2936ace022 | ||
|
|
4504ef43ac | ||
|
|
50b71adf2d | ||
|
|
8bb0475ba2 | ||
|
|
0e66af495b | ||
|
|
be6752116b | ||
|
|
36577490a7 | ||
|
|
8e4dcc2240 | ||
|
|
dc4de8212a | ||
|
|
ba2de753dd | ||
|
|
6dd1b7e125 | ||
|
|
aec6e9fb30 | ||
|
|
7b7d90f9f9 | ||
|
|
c937258922 | ||
|
|
08aa098203 | ||
|
|
b0fc1e4cc0 | ||
|
|
ca9eef8c09 | ||
|
|
b90b6e9651 | ||
|
|
f10bbb4f81 | ||
|
|
0116618e36 | ||
|
|
d864045815 | ||
|
|
294e5745e7 | ||
|
|
afc4f7ecba | ||
|
|
d249792866 | ||
|
|
59cad086e7 | ||
|
|
cfac228f57 | ||
|
|
a4bd6383a2 | ||
|
|
df0f470998 | ||
|
|
d61bf19b15 | ||
|
|
e0659c4d60 | ||
|
|
70baed8088 | ||
|
|
ab3e187354 | ||
|
|
f17a67d817 | ||
|
|
601218c0b1 | ||
|
|
8af5095ff5 | ||
|
|
0a6cbfa20d | ||
|
|
1630b64fa5 | ||
|
|
ff4ad6e151 | ||
|
|
cc07341463 | ||
|
|
9fd20a9e93 | ||
|
|
1a561c3cb1 | ||
|
|
ca3740ca86 | ||
|
|
7a69437443 | ||
|
|
671f00701b | ||
|
|
af28957796 | ||
|
|
28c56373f6 | ||
|
|
d5fa00f1e2 | ||
|
|
66fe91efed | ||
|
|
3d07062e1f | ||
|
|
f7b94272bf | ||
|
|
9e2353aebc | ||
|
|
bf83cc5ca1 | ||
|
|
d5e7d58d7e | ||
|
|
806a8ba89b | ||
|
|
8602dfb324 | ||
|
|
6647d52e88 | ||
|
|
3dcf08a850 | ||
|
|
2a5454a732 | ||
|
|
ee239428d9 | ||
|
|
dd731569d3 | ||
|
|
d9640c2ef7 | ||
|
|
a57d26f520 | ||
|
|
98f5b48ceb | ||
|
|
4e790f7847 | ||
|
|
355874edad | ||
|
|
91cdb9888a | ||
|
|
ff28c5f79e | ||
|
|
004b0c0831 | ||
|
|
01d9cdf4e3 | ||
|
|
0226a82dca | ||
|
|
31fcd5dc99 | ||
|
|
2c69716295 | ||
|
|
e772254293 | ||
|
|
f8df77d1a6 | ||
|
|
de4af94e89 | ||
|
|
cdee73b859 | ||
|
|
70895ed631 | ||
|
|
14a6cc4e2f | ||
|
|
83eaa45077 | ||
|
|
3a9b92f4a3 | ||
|
|
6e82239616 | ||
|
|
8e2da8a1a9 | ||
|
|
872b914c90 | ||
|
|
1e6f732fa9 | ||
|
|
925f0788fb | ||
|
|
3c0c79a526 | ||
|
|
88e66e825e | ||
|
|
fb7566ae53 | ||
|
|
9b05ea62c5 | ||
|
|
4feccd6484 | ||
|
|
7d166842f0 | ||
|
|
c6943cad7b | ||
|
|
c29037a9d6 | ||
|
|
e1b429c3b7 | ||
|
|
c17c622c97 | ||
|
|
9d11d42e5e | ||
|
|
270e0f4932 | ||
|
|
d1f6e1bb37 | ||
|
|
47e4ad5e41 | ||
|
|
78dd013fc0 | ||
|
|
c04403ca66 | ||
|
|
8c8e23b21a | ||
|
|
fd9a715634 | ||
|
|
957729fd5d | ||
|
|
23fb0cf18b | ||
|
|
8e72af0a45 | ||
|
|
26135820e1 | ||
|
|
2c3abffd2e | ||
|
|
4b46c5385e | ||
|
|
5b7fdbdea4 | ||
|
|
61dae58368 | ||
|
|
6958b66bc5 | ||
|
|
512cbf4ae9 | ||
|
|
e188665a9b | ||
|
|
4898f970b0 | ||
|
|
d9a2db5b8d | ||
|
|
7b518f14f0 | ||
|
|
ff4b889f1c | ||
|
|
b4b6435826 | ||
|
|
011953370a | ||
|
|
40a0b7104a | ||
|
|
964f8c38c0 | ||
|
|
e46c89e90f | ||
|
|
99d8ff2b01 | ||
|
|
dfbd5ad47e | ||
|
|
68ea223940 | ||
|
|
4fc84fa623 | ||
|
|
33aa81d9c9 | ||
|
|
58690662df | ||
|
|
b2194ebdac | ||
|
|
d584c4dc68 | ||
|
|
105ccdf323 | ||
|
|
cb6d1cc17d | ||
|
|
be4bddab30 | ||
|
|
465b278303 | ||
|
|
a52bebdcd2 | ||
|
|
1caa4c55c0 | ||
|
|
9c77e56568 | ||
|
|
f5e86f4ddc | ||
|
|
f10dde33c8 | ||
|
|
ff64a0ed82 | ||
|
|
a046436a50 | ||
|
|
bcb9721499 | ||
|
|
609f037345 | ||
|
|
4ed8c4fb25 | ||
|
|
45ebd07cf4 | ||
|
|
51223c6297 | ||
|
|
08b7674ab4 | ||
|
|
a8780e75f5 | ||
|
|
dc43759ebd | ||
|
|
e8ba28bffe | ||
|
|
f3266288e1 | ||
|
|
8482c47383 | ||
|
|
1e4268597d | ||
|
|
dd05f824a8 | ||
|
|
6b2d119481 | ||
|
|
37bdb62db8 | ||
|
|
30c5c3798f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
||||
/Mac/pkg/
|
||||
*.dmg
|
||||
*.pkg
|
||||
.DS_Store
|
||||
|
||||
18
Makefile
18
Makefile
@@ -17,24 +17,22 @@ build:
|
||||
mkdir -p build
|
||||
|
||||
build/wiki.pl: wiki.pl
|
||||
sed "s/\\\$$q->a({-href=>'http:\/\/www.oddmuse.org\/'}, 'Oddmuse')/\\\$$q->a({-href=>'http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . &/" < $< > $@
|
||||
perl -lne "s/(\\\$$q->a\({-href=>'http:\/\/www.oddmuse.org\/'}, 'Oddmuse'\))/\\\$$q->a({-href=>'http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tag\/?id=$(VERSION_NO)'}, 'wiki.pl') . ' ($(VERSION_NO)), see ' . \$$1/; print" < $< > $@
|
||||
|
||||
build/%-utf8.pl: modules/translations/%-utf8.pl
|
||||
sed "s/<a href=\"http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tree\/modules\/translations\/\\(.*\\).pl\">\\(.*\\).pl<\/a>/<a href=\"http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tree\/modules\/translations\/\\1.pl?id=$(VERSION_NO)\">\\1.pl<\/a> (for $(VERSION_NO))/" < $< > $@
|
||||
|
||||
# Currently oddtrans introduces encoding errors!
|
||||
|
||||
# %-utf8.pl: wiki.pl $(MODULES)
|
||||
# perl oddtrans -l $@ $^ > $@-new && mv $@-new $@
|
||||
perl -lne "s/(AddModuleDescription\('[^']+', '[^']+')\)/\$$1, 'translations\/', '$(VERSION_NO)')/; print" < $< > $@
|
||||
|
||||
# from: http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/namespaces.pl
|
||||
# to: http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/namespaces.pl?id=2.1-11-gd4f1e27
|
||||
|
||||
build/%.pl: modules/%.pl
|
||||
sed "s/<a href=\"http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tree\/modules\/\\(.*\\).pl\">\\(.*\\).pl<\/a>/<a href=\"http:\/\/git.savannah.gnu.org\/cgit\/oddmuse.git\/tree\/modules\/\\1.pl?id=$(VERSION_NO)\">\\1.pl<\/a> (for $(VERSION_NO))/" < $< > $@
|
||||
|
||||
|
||||
perl -lne "s/(AddModuleDescription\('[^']+', '[^']+')\)/\$$1, undef, '$(VERSION_NO)')/; print" < $< > $@
|
||||
|
||||
translations: $(TRANSLATIONS)
|
||||
for f in $^; do \
|
||||
echo updating $$f...; \
|
||||
perl oddtrans -l $$f wiki.pl $(MODULES) > $$f-new && mv $$f-new $$f; \
|
||||
done
|
||||
|
||||
# UNTESTED/OBSOLETE: these targets have not been tested in a long time
|
||||
# and are potentially obsolete.
|
||||
|
||||
315
contrib/add-link.pl
Normal file
315
contrib/add-link.pl
Normal file
@@ -0,0 +1,315 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
# Copyright (C) 2011–2015 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
use LWP::UserAgent;
|
||||
use HTML::TreeBuilder;
|
||||
use JSON::PP;
|
||||
use utf8;
|
||||
|
||||
# load Oddmuse core
|
||||
$RunCGI = 0;
|
||||
do "wiki.pl";
|
||||
|
||||
# globals depending on the name of the script
|
||||
my ($self, $name, $wiki);
|
||||
if ($0 eq '/home/alex/campaignwiki.org/add-link.pl') {
|
||||
$self = "https://campaignwiki.org/add-link";
|
||||
$name = "OSR Links to Wisdom";
|
||||
$wiki = 'LinksToWisdom';
|
||||
} elsif ($0 eq '/home/alex/campaignwiki.org/add-adventure.pl') {
|
||||
$self = "https://campaignwiki.org/add-adventure";
|
||||
$name = "OSR Links to Adventures";
|
||||
$wiki = 'Adventures';
|
||||
} else {
|
||||
ReportError('Cannot determine wiki!', '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
|
||||
# derived variables
|
||||
my $site = "https://campaignwiki.org/wiki/$wiki";
|
||||
# my $site = "http://localhost/wiki.pl";
|
||||
my $home = "$site/$HomePage";
|
||||
# http://www.emacswiki.org/pics/star.png
|
||||
my $stardata = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEUAAHkAAACzdRTapx3twwD/9qb////1YCa0AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXAQYCJAu+WhwbAAAAKnRFWHRDb21tZW50AGJ5IFJhZG9taXIgJ1RoZSBTaGVlcCcgRG9waWVybGFza2kVfTXbAAAAYElEQVQI12NgQAKMMIaYAFTAzRDKCHOEMETCnEFyjIJhYS6OggwMoqGhaS7GRgIMjC6uYc5GikA5YRcXIyWwotBgJUWw7lAXsAyDaIihMlhK1FFA0AjEEAESQgJQu4EYAPAPC2XcokgQAAAAAElFTkSuQmCC';
|
||||
|
||||
main();
|
||||
|
||||
sub toc {
|
||||
# start with the homepage
|
||||
my @values;
|
||||
my %labels;
|
||||
for my $id (GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g) {
|
||||
push @values, $id;
|
||||
for my $item (GetPageContent(FreeToNormal($id)) =~ /(\*+ [^][\n]*)$/mg) {
|
||||
my $value = $item;
|
||||
my $label = $item;
|
||||
$value =~ s/\* *//g;
|
||||
push @values, $value;
|
||||
$label =~ s/\* */ /g; # EM SPACE
|
||||
$labels{$value} = $label;
|
||||
}
|
||||
}
|
||||
return \@values, \%labels;
|
||||
}
|
||||
|
||||
sub top {
|
||||
# start with the homepage
|
||||
my %blog;
|
||||
my $n;
|
||||
for my $id (GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g) {
|
||||
for my $item (GetPageContent(FreeToNormal($id)) =~ /^\*+\s+\[(https?:\/\/[^\/\n\t ]+)/mg) {
|
||||
$n++;
|
||||
# handle blogspot domain munging
|
||||
$item =~ s/blogspot(\.[a-z]+)+/blogspot.com/;
|
||||
$blog{$item}++;
|
||||
}
|
||||
}
|
||||
print $q->p("Total links counted: $n.");
|
||||
my @list = sort { $blog{$b} <=> $blog{$a} } keys %blog;
|
||||
# my $max = scalar @list;
|
||||
# $max = 20 if $max > 20;
|
||||
# @list = @list[0 .. $max -1];
|
||||
@list = map {
|
||||
my $domain = substr($_, index($_, '://') + 3);
|
||||
my $term = quotemeta($domain);
|
||||
# handle blogspot domain munging
|
||||
$term =~ s/blogspot\\\.com/blogspot(\\.[a-z]+)+/;
|
||||
$term = QuoteHtml($term);
|
||||
$q->a({-href => $_}, $domain)
|
||||
. " (" . $q->a({-href => "$self/match/$term"}, $blog{$_}) . ")";
|
||||
} @list;
|
||||
return \@list;
|
||||
}
|
||||
|
||||
sub match {
|
||||
my $term = shift;
|
||||
# start with the homepage
|
||||
my @list;
|
||||
my $title;
|
||||
for my $id (GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g) {
|
||||
for my $line (split /\n/, GetPageContent(FreeToNormal($id))) {
|
||||
if ($line =~ /^\*+\s+([^][\n]*)$/) {
|
||||
$title = $1;
|
||||
} elsif ($line =~ /$term/o) {
|
||||
if ($line =~ /^\*+\s+\[(https?:\S+)\s+([^]]+)\]/) {
|
||||
push (@list, $q->a({-href => $1}, $2) . " (" . $title . ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return \@list;
|
||||
}
|
||||
|
||||
sub html_toc {
|
||||
my ($values, $labels) = toc();
|
||||
return $q->radio_group(-name =>'toc',
|
||||
-values => $values,
|
||||
-labels => $labels,
|
||||
-linebreak=>'true');
|
||||
}
|
||||
|
||||
sub default {
|
||||
print $q->p("Add a link to the " . $q->a({-href=>$home}, $name) . ".");
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'url'}, T('URL:')) . ' '
|
||||
. $q->textfield(-name=>'url', -id=>'url', -size=>80));
|
||||
print $q->p({-style=>'font-size: 10pt'},
|
||||
"(Drag this bookmarklet to your bookmarks bar for easy access:",
|
||||
$q->a({-href=>q{javascript:location='}
|
||||
. $q->url()
|
||||
. qq{?url='+encodeURIComponent(window.location.href)}},
|
||||
"Submit $name") . ".)");
|
||||
print html_toc();
|
||||
print $q->submit('go', 'Add!');
|
||||
print $q->end_form();
|
||||
}
|
||||
|
||||
sub confirm {
|
||||
my ($url, $name, $toc) = @_;
|
||||
print $q->p("Please confirm that you want to add "
|
||||
. GetUrl($url, $name)
|
||||
. " to the section “$toc”.");
|
||||
print $q->start_form(-method=>'get');
|
||||
print $q->p($q->label({-for=>'name', -style=>'display: inline-block; width: 15em'},
|
||||
T('Use a different link name:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:50ex',
|
||||
-name=>'name', -id=>'name', -size=>50, -default=>$name)
|
||||
. $q->br()
|
||||
. $q->label({-for=>'summary', -style=>'display: inline-block; width:15em'},
|
||||
T('An optional short summary:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:50ex',
|
||||
-name=>'summary', -id=>'summary', -size=>50)
|
||||
. $q->br()
|
||||
. $q->label({-for=>'username', -style=>'display: inline-block; width:15em'},
|
||||
T('Your name for the log file:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:50ex',
|
||||
-name=>'username', -id=>'username', -size=>50));
|
||||
my $star = $q->img({-src=>$stardata, -class=>'smiley', -alt=>'☆'});
|
||||
print '<p>Optionally: Do you want to rate it?<br />';
|
||||
my $i = 0;
|
||||
foreach my $label ($q->span({-style=>'display: inline-block; width:3em'}, $star)
|
||||
. 'I might use this for my campaign',
|
||||
$q->span({-style=>'display: inline-block; width:3em'}, $star x 2)
|
||||
. 'I have used this in a campaign and it worked as intended',
|
||||
$q->span({-style=>'display: inline-block; width:3em'}, $star x 3)
|
||||
. 'I have used this in a campaign and it was ' . $q->em('great')) {
|
||||
$i++;
|
||||
print qq{<label><input type="radio" name="stars" value="$i" $checked/>$label</label><br />};
|
||||
}
|
||||
print '</p>';
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('toc', $toc);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
}
|
||||
|
||||
# returns unquoted html
|
||||
sub get_name {
|
||||
my $url = shift;
|
||||
my $tree = HTML::TreeBuilder->new_from_content(GetRaw($url));
|
||||
my $h = $tree->look_down('_tag', 'title');
|
||||
$h = $tree->look_down('_tag', 'h1') unless $h;
|
||||
$h = $h->as_text if $h;
|
||||
return $h;
|
||||
}
|
||||
|
||||
sub post_addition {
|
||||
my ($url, $name, $toc, $summary) = @_;
|
||||
my $id = FreeToNormal($name);
|
||||
my $display = $name;
|
||||
utf8::decode($display); # we're dealing with user input
|
||||
utf8::decode($summary); # we're dealing with user input
|
||||
print $q->p("Adding ", GetUrl($url, $display), " to “$toc”.");
|
||||
# start with the homepage
|
||||
my @pages = GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g;
|
||||
for my $id (@pages) {
|
||||
return post($id, undef, $name, $summary, $url, GetParam('stars', '')) if $id eq $toc;
|
||||
my $data = GetPageContent(FreeToNormal($id));
|
||||
while ($data =~ /(\*+ ([^][\n]*))$/mg) {
|
||||
return post($id, $1, $name, $summary, $url, GetParam('stars', '')) if $2 eq $toc;
|
||||
}
|
||||
}
|
||||
print $q->p("Whoops. I was unable to find “$toc” in the wiki. Sorry!");
|
||||
}
|
||||
|
||||
sub post {
|
||||
my ($id, $toc, $name, $summary, $url, $stars) = @_;
|
||||
my $data = GetPageContent(FreeToNormal($id));
|
||||
my $re = quotemeta($url);
|
||||
if ($data =~ /$re\s+(.*?)\]/) {
|
||||
my $display = $1;
|
||||
print $q->p($q->strong("Oops, we seem to have a problem!"));
|
||||
print $q->p(GetPageLink(NormalToFree($id)),
|
||||
" already links to the URL you submitted:",
|
||||
GetUrl($url, $display));
|
||||
return;
|
||||
}
|
||||
$stars = ' ' . (':star:' x $stars) if $stars;
|
||||
$summary = ': ' . $summary if $summary;
|
||||
if ($toc) {
|
||||
$toc =~ /^(\*+)/;
|
||||
my $depth = "*$1"; # one more!
|
||||
my $regexp = quotemeta($toc);
|
||||
$data =~ s/$regexp/$toc\n$depth \[$url $name\]$summary$stars/;
|
||||
} else {
|
||||
$data = "* [$url $name]$summary$stars\n" . $data;
|
||||
}
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my %params = (text => $data,
|
||||
title => $id,
|
||||
summary => $name,
|
||||
username => GetParam('username'),
|
||||
pwd => GetParam('pwd'));
|
||||
# spam fighting modules
|
||||
$params{$QuestionaskerSecretKey} = 1 if $QuestionaskerSecretKey;
|
||||
$params{$HoneyPotOk} = time if $HoneyPotOk;
|
||||
my $response = $ua->post($site, \%params);
|
||||
if ($response->is_error) {
|
||||
print $q->p("The submission failed!");
|
||||
print $response->content;
|
||||
} else {
|
||||
print $q->p("See for yourself: ", GetPageLink($id));
|
||||
}
|
||||
}
|
||||
|
||||
sub print_end_of_page {
|
||||
print $q->p('Questions? Send mail to Alex Schroeder <'
|
||||
. $q->a({-href=>'mailto:kensanata@gmail.com'},
|
||||
'kensanata@gmail.com') . '>');
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
sub main {
|
||||
$ConfigFile = "$DataDir/config"; # read the global config file
|
||||
$DataDir = "$DataDir/$wiki"; # but link to the local pages
|
||||
Init(); # read config file (no modules!)
|
||||
$ScriptName = $site; # undo setting in the config file
|
||||
$FullUrl = $site; #
|
||||
binmode(STDOUT,':utf8');
|
||||
$q->charset('utf8');
|
||||
if ($q->path_info eq '/source') {
|
||||
seek DATA, 0, 0;
|
||||
print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
|
||||
} elsif ($q->path_info eq '/structure') {
|
||||
my ($values, $labels) = toc();
|
||||
my @indented = map {
|
||||
($labels->{$_} || $_) =~ /^( *)/;
|
||||
[$_, length($1)]
|
||||
} @$values;
|
||||
print "Content-type: application/json; charset=UTF-8\r\n\r\n";
|
||||
binmode(STDOUT,':raw'); # because of encode_json
|
||||
print JSON::PP::encode_json(\@indented);
|
||||
} elsif ($q->path_info eq '/toc') {
|
||||
my ($values, $labels) = toc();
|
||||
print "Content-type: application/json; charset=UTF-8\r\n\r\n";
|
||||
binmode(STDOUT,':raw'); # because of encode_json
|
||||
print JSON::PP::encode_json($values);
|
||||
} elsif ($q->path_info eq '/top') {
|
||||
print GetHeader('', 'Top Blogs');
|
||||
print $q->start_div({-class=>'content top'});
|
||||
print $q->ol($q->li(top()));
|
||||
print_end_of_page();
|
||||
} elsif ($q->path_info =~ '^/match/(.*)') {
|
||||
my $term = $1;
|
||||
print GetHeader('', "Entries Matching '$term'");
|
||||
print $q->start_div({-class=>'content match'});
|
||||
print $q->ol($q->li(match($term)));
|
||||
print_end_of_page();
|
||||
} else {
|
||||
push(@UserGotoBarPages, 'Help');
|
||||
$UserGotoBar = $q->a({-href=>$q->url . '/source'}, 'Source');
|
||||
print GetHeader('', 'Submit a new link');
|
||||
print $q->start_div({-class=>'content index'});
|
||||
my $url = GetParam('url');
|
||||
my $name = UnquoteHtml(GetParam('name', get_name($url)));
|
||||
my $toc = GetParam('toc');
|
||||
my $confirm = GetParam('confirm');
|
||||
my $summary = GetParam('summary');
|
||||
if (not $url or not $toc) {
|
||||
default();
|
||||
} elsif (not $confirm) {
|
||||
confirm($url, $name, $toc);
|
||||
} else {
|
||||
post_addition($url, $name, $toc, $summary);
|
||||
}
|
||||
print_end_of_page();
|
||||
}
|
||||
}
|
||||
|
||||
__DATA__
|
||||
90
contrib/anonymize.pl
Normal file
90
contrib/anonymize.pl
Normal file
@@ -0,0 +1,90 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 Anonymize oldrc.log Files
|
||||
|
||||
This script will read your oldrc.log file and replace the host field
|
||||
with 'Anonymous'. This is what the main script started doing
|
||||
2013-11-30.
|
||||
|
||||
When you run this script, it sets the main lock to prevent maintenance
|
||||
from running. You can therefore run it on a live system.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
|
||||
sub verify_setup {
|
||||
if (not -f 'oldrc.log') {
|
||||
die "Run this script in your data directory.\n"
|
||||
. "The oldrc.log file should be in the same directory.\n";
|
||||
}
|
||||
if (not -d 'temp') {
|
||||
die "Run this script in your data directory.\n"
|
||||
. "The temp directory should be in the same directory.\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub request_lock {
|
||||
if (-d 'temp/lockmain') {
|
||||
die "The wiki is currently locked.\n"
|
||||
. "Rerun this script later.\n";
|
||||
}
|
||||
mkdir('temp/lockmain') or die "Could not create 'temp/lockmain'.\n"
|
||||
. "You probably don't have the file permissions necessary.\n";
|
||||
}
|
||||
|
||||
sub release_lock {
|
||||
rmdir('temp/lockmain') or die "Could not remove 'temp/lockmain'.\n"
|
||||
}
|
||||
|
||||
sub anonymize {
|
||||
open(F, 'oldrc.log') or die "Could not open 'oldrc.log' for reading.\n";
|
||||
open(B, '>oldrc.log~') or die "Could not open 'oldrc.log~' for writing.\n"
|
||||
. "I will not continue without having a backup available.\n";
|
||||
my $FS = "\x1e"; # The FS character is the RECORD SEPARATOR control char
|
||||
my @lines = ();
|
||||
while (my $line = <F>) {
|
||||
next if $line eq "\n"; # some rc.log files are broken and contain empty lines
|
||||
my ($ts, $id, $minor, $summary, $host, @rest) = split(/$FS/o, $line);
|
||||
if ($id eq '[[rollback]]') {
|
||||
# rollback markers are very different
|
||||
push(@lines, $line);
|
||||
} else {
|
||||
# anonymize
|
||||
push(@lines, join($FS, $ts, $id, $minor, $summary, 'Anonymous', @rest));
|
||||
}
|
||||
print B $line;
|
||||
}
|
||||
close(F);
|
||||
open(F, '>', 'oldrc.log') or die "Could not open 'oldrc.log' for writing.\n";
|
||||
for my $line (@lines) {
|
||||
print F $line; # @rest ends with a newline
|
||||
}
|
||||
close(F);
|
||||
print "Wrote anonymized 'oldrc.log'.\n";
|
||||
print "Saved a backup as 'oldrc.log~'\n";
|
||||
}
|
||||
|
||||
sub main {
|
||||
verify_setup();
|
||||
request_lock();
|
||||
anonymize();
|
||||
release_lock();
|
||||
}
|
||||
|
||||
main();
|
||||
37
contrib/campaignwiki/README
Normal file
37
contrib/campaignwiki/README
Normal file
@@ -0,0 +1,37 @@
|
||||
The files in this directory are used to run http://campaignwiki.org/
|
||||
|
||||
add-link.pl
|
||||
===========
|
||||
|
||||
This is used to add links to the Links To Wisdom wiki. This wiki is a
|
||||
bookmark site: A few pages make up a big unordered list of links in
|
||||
wiki format. add-link is a tool to help users contribute new links to
|
||||
the list.
|
||||
|
||||
http://campaignwiki.org/wiki/LinksToWisdom/HomePage
|
||||
|
||||
copy.pl
|
||||
=======
|
||||
|
||||
This is used to copy the text from a web page to a wiki page. The idea
|
||||
was to keep archive copies of cool pages somewhere. The Blog Archive
|
||||
never got used, though.
|
||||
|
||||
http://campaignwiki.org/wiki/BlogArchive/HomePage
|
||||
|
||||
monster-tag.pl
|
||||
==============
|
||||
|
||||
This is used to quickly tag many pages in the Monsters wiki. The
|
||||
Monsters wiki hasn't been used in a long time, though.
|
||||
|
||||
http://campaignwiki.org/wiki/Monsters/HomePage
|
||||
|
||||
submit.pl
|
||||
=========
|
||||
|
||||
This used to be used to add sites to the Old School RPG Planet. The
|
||||
aggregator was configured via a wiki page on the Planet wiki. It's now
|
||||
abandoned.
|
||||
|
||||
http://campaignwiki.org/wiki/Planet/HomePage
|
||||
262
contrib/campaignwiki/add-link.pl
Normal file → Executable file
262
contrib/campaignwiki/add-link.pl
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
# Copyright (C) 2011 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2011–2014 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -15,8 +15,10 @@
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
use LWP::UserAgent;
|
||||
use HTML::TreeBuilder;
|
||||
use JSON::PP;
|
||||
use utf8;
|
||||
|
||||
# load Oddmuse core
|
||||
@@ -24,11 +26,14 @@ $RunCGI = 0;
|
||||
do "wiki.pl";
|
||||
|
||||
# globals
|
||||
my $self = "http://campaignwiki.org/add-link";
|
||||
my $name = "OSR Links to Wisdom";
|
||||
my $wiki = 'LinksToWisdom';
|
||||
my $site = "http://campaignwiki.org/wiki/$wiki";
|
||||
# my $site = "http://localhost/wiki.pl";
|
||||
my $home = "$site/$HomePage";
|
||||
# http://www.emacswiki.org/pics/star.png
|
||||
my $stardata = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAFVBMVEUAAHkAAACzdRTapx3twwD/9qb////1YCa0AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXAQYCJAu+WhwbAAAAKnRFWHRDb21tZW50AGJ5IFJhZG9taXIgJ1RoZSBTaGVlcCcgRG9waWVybGFza2kVfTXbAAAAYElEQVQI12NgQAKMMIaYAFTAzRDKCHOEMETCnEFyjIJhYS6OggwMoqGhaS7GRgIMjC6uYc5GikA5YRcXIyWwotBgJUWw7lAXsAyDaIihMlhK1FFA0AjEEAESQgJQu4EYAPAPC2XcokgQAAAAAElFTkSuQmCC';
|
||||
|
||||
main();
|
||||
|
||||
@@ -47,9 +52,62 @@ sub toc {
|
||||
$labels{$value} = $label;
|
||||
}
|
||||
}
|
||||
return \@values, \%labels;
|
||||
}
|
||||
|
||||
sub top {
|
||||
# start with the homepage
|
||||
my %blog;
|
||||
my $n;
|
||||
for my $id (GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g) {
|
||||
for my $item (GetPageContent(FreeToNormal($id)) =~ /^\*+\s+\[(https?:\/\/[^\/\n\t ]+)/mg) {
|
||||
$n++;
|
||||
# handle blogspot domain munging
|
||||
$item =~ s/blogspot(\.[a-z]+)+/blogspot.com/;
|
||||
$blog{$item}++;
|
||||
}
|
||||
}
|
||||
print $q->p("Total links counted: $n.");
|
||||
my @list = sort { $blog{$b} <=> $blog{$a} } keys %blog;
|
||||
# my $max = scalar @list;
|
||||
# $max = 20 if $max > 20;
|
||||
# @list = @list[0 .. $max -1];
|
||||
@list = map {
|
||||
my $domain = substr($_, index($_, '://') + 3);
|
||||
my $term = quotemeta($domain);
|
||||
# handle blogspot domain munging
|
||||
$term =~ s/blogspot\\\.com/blogspot(\\.[a-z]+)+/;
|
||||
$term = QuoteHtml($term);
|
||||
$q->a({-href => $_}, $domain)
|
||||
. " (" . $q->a({-href => "$self/match/$term"}, $blog{$_}) . ")";
|
||||
} @list;
|
||||
return \@list;
|
||||
}
|
||||
|
||||
sub match {
|
||||
my $term = shift;
|
||||
# start with the homepage
|
||||
my @list;
|
||||
my $title;
|
||||
for my $id (GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g) {
|
||||
for my $line (split /\n/, GetPageContent(FreeToNormal($id))) {
|
||||
if ($line =~ /^\*+\s+([^][\n]*)$/) {
|
||||
$title = $1;
|
||||
} elsif ($line =~ /$term/o) {
|
||||
if ($line =~ /^\*+\s+\[(https?:\S+)\s+([^]]+)\]/) {
|
||||
push (@list, $q->a({-href => $1}, $2) . " (" . $title . ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return \@list;
|
||||
}
|
||||
|
||||
sub html_toc {
|
||||
my ($values, $labels) = toc();
|
||||
return $q->radio_group(-name =>'toc',
|
||||
-values => \@values,
|
||||
-labels => \%labels,
|
||||
-values => $values,
|
||||
-labels => $labels,
|
||||
-linebreak=>'true');
|
||||
}
|
||||
|
||||
@@ -57,102 +115,108 @@ sub default {
|
||||
print $q->p("Add a link to the " . $q->a({-href=>$home}, $name) . ".");
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'url'}, T('URL:')) . ' '
|
||||
. $q->textfield(-name=>'url', -id=>'url', -size=>50));
|
||||
print toc();
|
||||
print $q->submit('go', 'Add!');
|
||||
print $q->end_form();
|
||||
print $q->p("Drag this bookmarklet to your bookmarks bar for easy access:",
|
||||
. $q->textfield(-name=>'url', -id=>'url', -size=>80));
|
||||
print $q->p({-style=>'font-size: 10pt'},
|
||||
"(Drag this bookmarklet to your bookmarks bar for easy access:",
|
||||
$q->a({-href=>q{javascript:location='}
|
||||
. $q->url()
|
||||
. qq{?url='+encodeURIComponent(window.location.href)}},
|
||||
"Submit $name") . ".");
|
||||
"Submit $name") . ".)");
|
||||
print html_toc();
|
||||
print $q->submit('go', 'Add!');
|
||||
print $q->end_form();
|
||||
}
|
||||
|
||||
sub check_url {
|
||||
my $toc = GetParam('toc');
|
||||
return default() unless $toc;
|
||||
my $url = shift;
|
||||
if (not GetParam('confirm', 0)) {
|
||||
my $name = get_name($url);
|
||||
print $q->p("Please confirm that you want to add "
|
||||
. GetUrl($url, $name)
|
||||
. " to the section “$toc”.");
|
||||
print $q->start_form(-method=>'get');
|
||||
print $q->p($q->label({-for=>'name', -style=>'display: inline-block; width:30ex'},
|
||||
T('Use a different link name:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:60ex',
|
||||
-name=>'name', -id=>'name', -size=>50, -default=>$name)
|
||||
. $q->br()
|
||||
. $q->label({-for=>'username', -style=>'display: inline-block; width:30ex'},
|
||||
T('Your name for the log file:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:60ex',
|
||||
-name=>'username', -id=>'username', -size=>50));
|
||||
my $star = $q->img({-src=>'http://www.emacswiki.org/pics/star.png', -class=>'smiley',
|
||||
-alt=>'star'});
|
||||
print '<p>Optionally: Do you want to rate it?<br />';
|
||||
my $i = 0;
|
||||
foreach my $label ($q->span({-style=>'display: inline-block; width:15ex'}, $star)
|
||||
. 'I might use this for my next campaign',
|
||||
$q->span({-style=>'display: inline-block; width:15ex'}, $star x 2)
|
||||
. 'I have used this in a campaign and it worked as intended',
|
||||
$q->span({-style=>'display: inline-block; width:15ex'}, $star x 3)
|
||||
. 'I have used it in many of my campaigns',
|
||||
$q->span({-style=>'display: inline-block; width:15ex'}, $star x 4)
|
||||
. 'Everybody should give it a try',
|
||||
$q->span({-style=>'display: inline-block; width:15ex'}, $star x 5)
|
||||
. 'Everybody should use it, that is how awesome it is!') {
|
||||
$i++;
|
||||
print qq{<label><input type="radio" name="stars" value="$i" $checked/>$label</label><br />};
|
||||
}
|
||||
print '</p>';
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('toc', $toc);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
} else {
|
||||
post_addition($q->param('name'), $url, $toc);
|
||||
sub confirm {
|
||||
my ($url, $name, $toc) = @_;
|
||||
print $q->p("Please confirm that you want to add "
|
||||
. GetUrl($url, $name)
|
||||
. " to the section “$toc”.");
|
||||
print $q->start_form(-method=>'get');
|
||||
print $q->p($q->label({-for=>'name', -style=>'display: inline-block; width: 15em'},
|
||||
T('Use a different link name:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:50ex',
|
||||
-name=>'name', -id=>'name', -size=>50, -default=>$name)
|
||||
. $q->br()
|
||||
. $q->label({-for=>'summary', -style=>'display: inline-block; width:15em'},
|
||||
T('An optional short summary:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:50ex',
|
||||
-name=>'summary', -id=>'summary', -size=>50)
|
||||
. $q->br()
|
||||
. $q->label({-for=>'username', -style=>'display: inline-block; width:15em'},
|
||||
T('Your name for the log file:')) . ' '
|
||||
. $q->textfield(-style=>'display: inline-block; width:50ex',
|
||||
-name=>'username', -id=>'username', -size=>50));
|
||||
my $star = $q->img({-src=>$stardata, -class=>'smiley', -alt=>'☆'});
|
||||
print '<p>Optionally: Do you want to rate it?<br />';
|
||||
my $i = 0;
|
||||
foreach my $label ($q->span({-style=>'display: inline-block; width:3em'}, $star)
|
||||
. 'I might use this for my campaign',
|
||||
$q->span({-style=>'display: inline-block; width:3em'}, $star x 2)
|
||||
. 'I have used this in a campaign and it worked as intended',
|
||||
$q->span({-style=>'display: inline-block; width:3em'}, $star x 3)
|
||||
. 'I have used this in a campaign and it was ' . $q->em('great')) {
|
||||
$i++;
|
||||
print qq{<label><input type="radio" name="stars" value="$i" $checked/>$label</label><br />};
|
||||
}
|
||||
print '</p>';
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('toc', $toc);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
}
|
||||
|
||||
# returns unquoted html
|
||||
sub get_name {
|
||||
my $url = shift;
|
||||
my $tree = HTML::TreeBuilder->new_from_content(GetRaw($url));
|
||||
my $h = $tree->look_down('_tag', 'h1');
|
||||
$h = $tree->look_down('_tag', 'title') unless $h;
|
||||
my $h = $tree->look_down('_tag', 'title');
|
||||
$h = $tree->look_down('_tag', 'h1') unless $h;
|
||||
$h = $h->as_text if $h;
|
||||
return $h;
|
||||
}
|
||||
|
||||
sub post_addition {
|
||||
my ($name, $url, $toc) = @_;
|
||||
my ($url, $name, $toc, $summary) = @_;
|
||||
my $id = FreeToNormal($name);
|
||||
my $display = $name;
|
||||
utf8::decode($display); # we're dealing with user input
|
||||
utf8::decode($summary); # we're dealing with user input
|
||||
print $q->p("Adding ", GetUrl($url, $display), " to “$toc”.");
|
||||
# start with the homepage
|
||||
my @pages = GetPageContent($HomePage) =~ /\* \[\[(.*?)\]\]/g;
|
||||
for my $id (@pages) {
|
||||
return post($id, undef, $name, $url, GetParam('stars', '')) if $id eq $toc;
|
||||
return post($id, undef, $name, $summary, $url, GetParam('stars', '')) if $id eq $toc;
|
||||
my $data = GetPageContent(FreeToNormal($id));
|
||||
while ($data =~ /(\*+ ([^][\n]*))$/mg) {
|
||||
return post($id, $1, $name, $url, GetParam('stars', '')) if $2 eq $toc;
|
||||
return post($id, $1, $name, $summary, $url, GetParam('stars', '')) if $2 eq $toc;
|
||||
}
|
||||
}
|
||||
print $q->p("Whoops. I was unable to find “$toc” in the wiki. Sorry!");
|
||||
}
|
||||
|
||||
sub post {
|
||||
my ($id, $toc, $name, $url, $stars) = @_;
|
||||
my ($id, $toc, $name, $summary, $url, $stars) = @_;
|
||||
my $data = GetPageContent(FreeToNormal($id));
|
||||
my $re = quotemeta($url);
|
||||
if ($data =~ /$re\s+(.*?)\]/) {
|
||||
my $display = $1;
|
||||
print $q->p($q->strong("Oops, we seem to have a problem!"));
|
||||
print $q->p(GetPageLink(NormalToFree($id)),
|
||||
" already links to the URL you submitted:",
|
||||
GetUrl($url, $display));
|
||||
return;
|
||||
}
|
||||
$stars = ' ' . (':star:' x $stars) if $stars;
|
||||
$summary = ': ' . $summary if $summary;
|
||||
if ($toc) {
|
||||
$toc =~ /^(\*+)/;
|
||||
my $depth = "*$1"; # one more!
|
||||
my $regexp = quotemeta($toc);
|
||||
$data =~ s/$regexp/$toc\n$depth \[$url $name\]$stars/;
|
||||
$data =~ s/$regexp/$toc\n$depth \[$url $name\]$summary$stars/;
|
||||
} else {
|
||||
$data = "* [$url $name]$stars\n" . $data;
|
||||
$data = "* [$url $name]$summary$stars\n" . $data;
|
||||
}
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my %params = (text => $data,
|
||||
@@ -173,30 +237,68 @@ sub post {
|
||||
}
|
||||
}
|
||||
|
||||
sub main {
|
||||
$ConfigFile = "$DataDir/config"; # read the global config file
|
||||
$DataDir = "$DataDir/$wiki"; # but link to the local pages
|
||||
Init(); # read config file (no modules!)
|
||||
$ScriptName = $site; # undo setting in the config file
|
||||
binmode(STDOUT,':utf8');
|
||||
$q->charset('utf8');
|
||||
if ($q->path_info eq '/source') {
|
||||
seek DATA, 0, 0;
|
||||
print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
|
||||
} else {
|
||||
$UserGotoBar = $q->a({-href=>$q->url . '/source'}, 'Source');
|
||||
print GetHeader('', 'Submit a new link');
|
||||
print $q->start_div({-class=>'content index'});
|
||||
if (not GetParam('url')) {
|
||||
default();
|
||||
} else {
|
||||
check_url(GetParam('url'));
|
||||
}
|
||||
sub print_end_of_page {
|
||||
print $q->p('Questions? Send mail to Alex Schroeder <'
|
||||
. $q->a({-href=>'mailto:kensanata@gmail.com'},
|
||||
'kensanata@gmail.com') . '>');
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
sub main {
|
||||
$ConfigFile = "$DataDir/config"; # read the global config file
|
||||
$DataDir = "$DataDir/$wiki"; # but link to the local pages
|
||||
Init(); # read config file (no modules!)
|
||||
$ScriptName = $site; # undo setting in the config file
|
||||
$FullUrl = $site; #
|
||||
binmode(STDOUT,':utf8');
|
||||
$q->charset('utf8');
|
||||
if ($q->path_info eq '/source') {
|
||||
seek DATA, 0, 0;
|
||||
print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
|
||||
} elsif ($q->path_info eq '/structure') {
|
||||
my ($values, $labels) = toc();
|
||||
my @indented = map {
|
||||
($labels->{$_} || $_) =~ /^( *)/;
|
||||
[$_, length($1)]
|
||||
} @$values;
|
||||
print "Content-type: application/json; charset=UTF-8\r\n\r\n";
|
||||
binmode(STDOUT,':raw'); # because of encode_json
|
||||
print JSON::PP::encode_json(\@indented);
|
||||
} elsif ($q->path_info eq '/toc') {
|
||||
my ($values, $labels) = toc();
|
||||
print "Content-type: application/json; charset=UTF-8\r\n\r\n";
|
||||
binmode(STDOUT,':raw'); # because of encode_json
|
||||
print JSON::PP::encode_json($values);
|
||||
} elsif ($q->path_info eq '/top') {
|
||||
print GetHeader('', 'Top Blogs');
|
||||
print $q->start_div({-class=>'content top'});
|
||||
print $q->ol($q->li(top()));
|
||||
print_end_of_page();
|
||||
} elsif ($q->path_info =~ '^/match/(.*)') {
|
||||
my $term = $1;
|
||||
print GetHeader('', "Entries Matching '$term'");
|
||||
print $q->start_div({-class=>'content match'});
|
||||
print $q->ol($q->li(match($term)));
|
||||
print_end_of_page();
|
||||
} else {
|
||||
push(@UserGotoBarPages, 'Help');
|
||||
$UserGotoBar = $q->a({-href=>$q->url . '/source'}, 'Source');
|
||||
print GetHeader('', 'Submit a new link');
|
||||
print $q->start_div({-class=>'content index'});
|
||||
my $url = GetParam('url');
|
||||
my $name = UnquoteHtml(GetParam('name', get_name($url)));
|
||||
my $toc = GetParam('toc');
|
||||
my $confirm = GetParam('confirm');
|
||||
my $summary = GetParam('summary');
|
||||
if (not $url or not $toc) {
|
||||
default();
|
||||
} elsif (not $confirm) {
|
||||
confirm($url, $name, $toc);
|
||||
} else {
|
||||
post_addition($url, $name, $toc, $summary);
|
||||
}
|
||||
print_end_of_page();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#! /usr/bin/perl
|
||||
|
||||
# Copyright (C) 2011 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2011–2014 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -17,63 +17,62 @@
|
||||
package OddMuse;
|
||||
use LWP::UserAgent;
|
||||
use HTML::TreeBuilder;
|
||||
use utf8;
|
||||
|
||||
# load Oddmuse core
|
||||
$RunCGI = 0;
|
||||
do "wiki.pl";
|
||||
|
||||
# globals
|
||||
my $wiki = 'BlogArchive';
|
||||
my $site = "http://campaignwiki.org/wiki/$wiki";
|
||||
# my $site = "http://localhost/wiki.pl";
|
||||
my $home = "$site/HomePage";
|
||||
$default_namespace = 'NameOfYourWiki';
|
||||
|
||||
main();
|
||||
|
||||
sub default {
|
||||
print $q->p("Copy a blog article to the "
|
||||
. $q->a({-href=>$home}, $wiki) . ".");
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'url'}, T('URL:')) . ' '
|
||||
. $q->textfield(-name=>'url', -id=>'url', -size=>50));
|
||||
my ($url, $ns) = @_;
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'copy');
|
||||
print $q->p("This script helps you copy of a blog post to your Campaign Wiki.");
|
||||
print $q->p($q->label({-for=>'url', -style=>'display: inline-block; width: 20ex'}, 'Blog post URL:'),
|
||||
$q->textfield(-name=>'url', -id=>'url', -size=>50),
|
||||
$q->br(),
|
||||
$q->label({-for=>'ns', -style=>'display: inline-block; width: 20ex'}, 'Name of your wiki:'),
|
||||
$q->textfield(-name=>'ns', -id=>'ns', -size=>50, -default=>$default_namespace));
|
||||
if ($url and not $ns) {
|
||||
print $q->p($q->em('Please provide the name of your wiki. It is mandatory. Use “NameOfYourWiki” if you just want to test something.'));
|
||||
}
|
||||
print $q->submit('go', 'Go!');
|
||||
print $q->end_form();
|
||||
print $q->p("Please make sure you’re only submitting your own articles",
|
||||
"or articles with an appropriate license.");
|
||||
|
||||
print $q->p("Drag this bookmarklet to your bookmarks bar for easy access:",
|
||||
$q->a({-href=>q{javascript:location='http://campaignwiki.org/copy?url='+encodeURIComponent(window.location.href)}}, $wiki) . ".");
|
||||
$q->a({-href=>q{javascript:location='http://campaignwiki.org/copy?url='+encodeURIComponent(window.location.href)}}, 'Copy Blog Post') . ".");
|
||||
}
|
||||
|
||||
sub check_url {
|
||||
my $url = shift;
|
||||
print $q->p("Looking at ", $q->a({-href=>$url}, $url));
|
||||
my ($name, $data) = get_data($url);
|
||||
$name = GetParam('name', $name);
|
||||
if (name_exists($name) and not GetParam('confirm', 0)) {
|
||||
print $q->p("We already have a page with that name: ",
|
||||
$q->a({-href=>$duplicate}, $duplicate));
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'name'}, T('New name:')) . ' '
|
||||
. $q->textfield(-name=>'name', -id=>'name', -size=>50,
|
||||
-default=>$name));
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
} elsif (not GetParam('confirm', 0)) {
|
||||
print $q->p("Please confirm that you want to copy this article to the wiki.");
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'name'}, T('Name:')) . ' '
|
||||
. $q->textfield(-name=>'name', -id=>'name', -size=>50,
|
||||
-default=>$name));
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
} else {
|
||||
post_addition($name, $data, $url);
|
||||
}
|
||||
sub confirm_overwrite {
|
||||
my ($url, $ns, $name) = @_;
|
||||
print $q->p("We already have a page with that name: ", GetPageLink($name));
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'name'}, T('New name:')) . ' '
|
||||
. $q->textfield(-name=>'name', -id=>'name', -size=>50, -default=>$name));
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('ns', $ns);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
}
|
||||
|
||||
sub confirm_save {
|
||||
my ($url, $ns, $name) = @_;
|
||||
my $ns = GetParam('ns', $default_namespace);
|
||||
print $q->p("Please confirm that you want to copy",
|
||||
$q->a({-href=>$url}, "this article"), "to", GetPageLink($HomePage, $ns) . ".");
|
||||
print $q->start_multipart_form(-method=>'get', -class=>'submit');
|
||||
print $q->p($q->label({-for=>'name'}, T('Name:')) . ' '
|
||||
. $q->textfield(-name=>'name', -id=>'name', -size=>50, -default=>$name));
|
||||
print $q->hidden('url', $url);
|
||||
print $q->hidden('ns', $ns);
|
||||
print $q->hidden('confirm', 1);
|
||||
print $q->submit('go', 'Continue');
|
||||
print $q->end_form();
|
||||
}
|
||||
|
||||
sub get_data {
|
||||
@@ -82,24 +81,32 @@ sub get_data {
|
||||
my $h = $tree->look_down('_tag', 'h1');
|
||||
$h = $tree->look_down('_tag', 'title') unless $h;
|
||||
$h = $h->as_text if $h;
|
||||
my $b = $tree->look_down('_tag', 'body');
|
||||
if ($b = $tree->look_down('_tag', 'div',
|
||||
'class', qr/post-body/)) {
|
||||
# blogspot
|
||||
my $b;
|
||||
if ($b = $tree->look_down('_tag', 'div', 'class', qr/post-body/)) {
|
||||
# Blogspot
|
||||
$b = html($b);
|
||||
} elsif ($b = $tree->look_down('_tag', 'div', 'class', qr/content/)) {
|
||||
# Oddmuse
|
||||
$b = html($b);
|
||||
} else {
|
||||
# no idea, just get the text
|
||||
$b = $b->as_text if $b;
|
||||
# default: get it all
|
||||
$b = html($tree->look_down('_tag', 'body'));
|
||||
}
|
||||
# common illegal character for page names
|
||||
$h =~ s/:/,/g;
|
||||
return ($h, $b);
|
||||
}
|
||||
|
||||
sub html {
|
||||
my $tree = shift;
|
||||
my ($tree, $p) = @_;
|
||||
# $p indicates whether we need an empty line or not
|
||||
my $str;
|
||||
for my $element ($tree->content_list()) {
|
||||
if (not ref $element) {
|
||||
$str .= $element;
|
||||
} elsif ($element->tag() eq 'p') {
|
||||
$str .= ($p == 1 ? "\n\n" : "") . html($element);
|
||||
$p = 1;
|
||||
} elsif ($element->tag() eq 'br') {
|
||||
$str .= "\n\n";
|
||||
} elsif ($element->tag() eq 'span'
|
||||
@@ -107,6 +114,20 @@ sub html {
|
||||
$str .= "[b]" . html($element) . "[/b]";
|
||||
} elsif ($element->tag() =~ m/^(b|i|h[1-6])$/) {
|
||||
$str .= "[$1]" . html($element) . "[/$1]";
|
||||
} elsif ($element->tag() eq 'a'
|
||||
and $element->attr('href')) {
|
||||
$str .= "[url=" . $element->attr('href') . "]" . html($element) . "[/url]";
|
||||
} elsif ($element->tag() eq 'img'
|
||||
and $element->attr('src')) {
|
||||
$str .= "[img]" . $element->attr('src') . "[/img]";
|
||||
} elsif ($element->tag() eq 'pre') {
|
||||
$str .= "\n\n[code]\n" . $element->as_text() . "\n[/code]";
|
||||
$p = 1;
|
||||
} elsif ($element->tag() eq 'div'
|
||||
and ($element->attr('style') =~ /float: *(left|right)/
|
||||
or $element->attr('style') =~ /text-align: *(center)/)) {
|
||||
$str .= "\n[$1]" . html($element) . "[/$1]";
|
||||
$p = 1;
|
||||
} else {
|
||||
$str .= html($element);
|
||||
}
|
||||
@@ -123,25 +144,25 @@ sub name_exists {
|
||||
}
|
||||
|
||||
sub post_addition {
|
||||
my ($name, $data, $url) = @_;
|
||||
my ($url, $ns, $name, $data) = @_;
|
||||
my $id = FreeToNormal($name);
|
||||
print $q->p("Adding ", $q->a({-href=>$url}, $name));
|
||||
print $q->p("Copying ", $q->a({-href=>$url}, "the blog post") . "…");
|
||||
my $text = "Based on [$url $name].\n----\n" . $data;
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my %params = (text => $text,
|
||||
title => $id,
|
||||
summary => $name,
|
||||
username => GetParam('username'),
|
||||
ns => $ns,
|
||||
pwd => GetParam('pwd'));
|
||||
$params{$QuestionaskerSecretKey} = 1 if $QuestionaskerSecretKey;
|
||||
my $response = $ua->post($site, \%params);
|
||||
my $response = $ua->post($FullUrl, \%params);
|
||||
if ($response->is_error) {
|
||||
print $q->p("The submission failed!");
|
||||
print $q->pre($response->status_line . "\n"
|
||||
. $response->content);
|
||||
print $q->p("Copying failed!");
|
||||
print $q->p($q->strong($response->status_line));
|
||||
print $response->content;
|
||||
} else {
|
||||
print $q->p("See for yourself: ",
|
||||
$q->a({-href=>"$site/$id"}, $name));
|
||||
print $q->p("Your copy: ", GetPageLink($name) . ".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +173,22 @@ sub main {
|
||||
print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
|
||||
} else {
|
||||
$UserGotoBar .= $q->a({-href=>$q->url . '/source'}, 'Source');
|
||||
print GetHeader('', 'Submit a new blog article');
|
||||
print GetHeader('', 'Copy a blog article');
|
||||
print $q->start_div({-class=>'content index'});
|
||||
if (not GetParam('url')) {
|
||||
default();
|
||||
my $url = GetParam('url');
|
||||
my $ns = GetParam('ns');
|
||||
if (not $url or not $ns) {
|
||||
default($url, $ns);
|
||||
} else {
|
||||
check_url(GetParam('url'));
|
||||
my ($name, $data) = get_data($url);
|
||||
$name = GetParam('name', $name);
|
||||
if (name_exists($name) and not GetParam('confirm', 0)) {
|
||||
confirm_overwrite($url, $ns, $name);
|
||||
} elsif (not GetParam('confirm', 0)) {
|
||||
confirm_save($url, $ns, $name);
|
||||
} else {
|
||||
post_addition($url, $ns, $name, $data);
|
||||
}
|
||||
}
|
||||
print $q->p('Questions? Send mail to Alex Schröder <'
|
||||
. $q->a({-href=>'mailto:kensanata@gmail.com'},
|
||||
|
||||
0
contrib/campaignwiki/monster-tag.pl
Normal file → Executable file
0
contrib/campaignwiki/monster-tag.pl
Normal file → Executable file
215
contrib/campaignwiki/submit.pl
Normal file → Executable file
215
contrib/campaignwiki/submit.pl
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Copyright (C) 2010 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2010, 2012 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
@@ -17,6 +17,7 @@
|
||||
package OddMuse;
|
||||
use URI;
|
||||
use LWP::UserAgent;
|
||||
use utf8;
|
||||
|
||||
# load Oddmuse core
|
||||
$RunCGI = 0;
|
||||
@@ -61,16 +62,16 @@ sub parse_blogs {
|
||||
return %cached_blogs if %cached_blogs;
|
||||
my @data = split(/\n/, GetRaw($src));
|
||||
my $url;
|
||||
my $name;
|
||||
my $paramref;
|
||||
foreach $_ (@data) {
|
||||
if (/^\[(.+)\]/) {
|
||||
$url = $1;
|
||||
$name = undef;
|
||||
} elsif (/^name *= *(.+)/) {
|
||||
$name = $1;
|
||||
$paramref = {};
|
||||
} elsif (/^([a-z_]+) *= *(.+)/) {
|
||||
$paramref->{$1} = $2;
|
||||
}
|
||||
if ($url && $name) {
|
||||
$cached_blogs{$url} = $name;
|
||||
if ($url && $paramref->{name}) {
|
||||
$cached_blogs{$url} = $paramref;
|
||||
}
|
||||
}
|
||||
return %cached_blogs;
|
||||
@@ -84,8 +85,16 @@ sub host_exists {
|
||||
}
|
||||
}
|
||||
|
||||
sub debug_url {
|
||||
my $url = $q->url(-path_info=>1) . "?debug=1;";
|
||||
$url .= join(";", map { $_ . "=" . GetParam($_) }
|
||||
qw(username confirmed candidate url));
|
||||
return $url;
|
||||
}
|
||||
|
||||
sub check_url {
|
||||
my $url = shift;
|
||||
print $q->p("Debug: url=$url") if GetParam("debug");
|
||||
my $frown = $q->img({-src=>"http://emacswiki.org/pics/smiles/sad.png",
|
||||
-alt=>":("});
|
||||
my $smile = $q->img({-src=>"http://emacswiki.org/pics/smiles/smile.png",
|
||||
@@ -97,26 +106,24 @@ sub check_url {
|
||||
$u = URI->new($url);
|
||||
eval {$u->host };
|
||||
}
|
||||
|
||||
# - not an url
|
||||
# - it's campaign wiki site
|
||||
# - no username
|
||||
# or read Feeds page and
|
||||
# - it's a duplicate
|
||||
# - it's a partial match: continue with confirmed=1
|
||||
# or read the list of alternatives from the url
|
||||
# - one of the feeds listed is known: continue with confirmed=2
|
||||
# - no feeds were listed: url is a feed or report it
|
||||
# - one feed was listed: try it
|
||||
# - some feeds were listed: pick one
|
||||
|
||||
if ($@) {
|
||||
# the prefixing of http:// above should make it really hard to reach this code
|
||||
print $q->p($q->a({-href=>$url}, $url) . qq{
|
||||
seems to be <strong>invalid</strong>. $frown Make sure you use something
|
||||
like the following: <tt>http://grognardia.blogspot.com/</tt>});
|
||||
} elsif (not GetParam('username', '')) {
|
||||
print $q->p(qq{As an anti-spam measure I'd really like you to <strong>provide a name</strong> for the log file. Sorry about that. $frown});
|
||||
} else {
|
||||
my %blogs = parse_blogs();
|
||||
my $duplicate = host_exists($u->host, %blogs);
|
||||
if ($duplicate
|
||||
&& !$blogs{$url}
|
||||
&& !GetParam('confirmed')) {
|
||||
print $q->p("We have a partial match: ",
|
||||
$q->a({-href=>$duplicate}, $duplicate));
|
||||
print GetFormStart();
|
||||
print $q->hidden('confirmed', 1);
|
||||
print $q->hidden('url', $url);
|
||||
print $q->submit('go', 'Proceed anyway!');
|
||||
print $q->end_form();
|
||||
} elsif ($url =~ /campaignwiki\.org/i) {
|
||||
print $q->p(qq{
|
||||
This looks <strong>familiar</strong>!
|
||||
@@ -124,17 +131,35 @@ I do not think that adding any of the wikis on this site is the right
|
||||
thing to do, though.});
|
||||
print $q->p(qq{Thanks for testing it. }
|
||||
. $q->img({-src=>"http://www.emacswiki.org/pics/grin.png"}));
|
||||
} elsif (not GetParam('username', '')) {
|
||||
print $q->p(qq{As an anti-spam measure I'd really like you to
|
||||
<strong>provide a name</strong> for the log file. Sorry about that. $frown});
|
||||
} else {
|
||||
my %blogs = parse_blogs();
|
||||
my $duplicate = host_exists($u->host, %blogs);
|
||||
if ($blogs{$url}) {
|
||||
print $q->p("We already list ",
|
||||
$q->a({-href=>$duplicate}, $duplicate));
|
||||
} elsif ($duplicate && !GetParam('confirmed')) {
|
||||
print $q->p("We have a partial match: ",
|
||||
$q->a({-href=>$duplicate}, $duplicate));
|
||||
print GetFormStart();
|
||||
print $q->hidden('confirmed', 1);
|
||||
print $q->hidden('url', $url);
|
||||
print $q->submit('go', 'Proceed anyway!');
|
||||
print $q->end_form();
|
||||
} else {
|
||||
my @alternatives = get_feeds($url, keys %blogs);
|
||||
if ($#alternatives > 0 && !GetParam('candidate')) {
|
||||
my ($status, @alternatives) = get_feeds($url, %blogs);
|
||||
if ($status eq 'known' && GetParam('confirmed') < 2) {
|
||||
print $q->p($q->a({-href=>$url},
|
||||
"The page you submitted")
|
||||
. " lists "
|
||||
. $q->a({-href=>$alternatives[0]},
|
||||
"a known feed") . ".");
|
||||
print GetFormStart();
|
||||
print $q->hidden('confirmed', 1);
|
||||
print $q->hidden('confirmed', 2);
|
||||
print $q->hidden('url', $url);
|
||||
print $q->p("You need to pick one of the candidates:");
|
||||
print $q->p(join($q->br(), map {
|
||||
$q->input({-type=>"radio", -name=>"candidate", -value=>$_},
|
||||
$q->a({-href=>$_}, QuoteHtml($_))) } @alternatives));
|
||||
print $q->submit('go', 'Submit');
|
||||
print $q->submit('go', 'Proceed anyway!');
|
||||
print $q->end_form();
|
||||
} elsif ($#alternatives < 0) {
|
||||
if (is_feed($url)) {
|
||||
@@ -143,20 +168,31 @@ thing to do, though.});
|
||||
print $q->p("Apparently " . $q->a({-href=>$url}, QuoteHtml($url))
|
||||
. " is not a feed and doesn't link to any feed. "
|
||||
. "There is nothing for me to add. " . $frown);
|
||||
print $q->p("If you feel like it, you could try to "
|
||||
. $q->a({-href=>debug_url()}, "debug")
|
||||
. " this.");
|
||||
}
|
||||
} elsif ($#alternatives == 0) {
|
||||
print $q->p($q->a({-href=>$url}, "The page you submitted")
|
||||
. " lists "
|
||||
. $q->a({-href=>$alternatives[0]},
|
||||
"one new feed")
|
||||
. ".");
|
||||
print GetFormStart();
|
||||
print $q->hidden('url', $alternatives[0]);
|
||||
print $q->submit('go', 'Take it!');
|
||||
print $q->end_form();
|
||||
print $q->p("If you feel like it, you could try to "
|
||||
. $q->a({-href=>debug_url()}, "debug")
|
||||
. " this.");
|
||||
} else {
|
||||
my $candidate = GetParam('candidate');
|
||||
$candidate = $alternatives[0] unless $candidate;
|
||||
if (is_feed($candidate)) {
|
||||
post_addition($candidate);
|
||||
} else {
|
||||
print $q->p($q->a({-href=>$candidate}, "The page you submitted")
|
||||
. " listed "
|
||||
. $q->a({-href=>$candidate}, QuoteHtml($candidate))
|
||||
. " as one of its feeds. "
|
||||
. "But it turns out that this is not a valid feed! "
|
||||
. "I can't add an invalid feed. " . $frown);
|
||||
}
|
||||
print GetFormStart();
|
||||
print $q->p("You need to pick one of the candidates:");
|
||||
print $q->p(join($q->br(), map {
|
||||
$q->input({-type=>"radio", -name=>"url", -value=>$_},
|
||||
$q->a({-href=>$_}, QuoteHtml($_))) } @alternatives));
|
||||
print $q->submit('go', 'Submit');
|
||||
print $q->end_form();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,23 +208,73 @@ sub is_feed {
|
||||
|
||||
sub get_feeds {
|
||||
my $url = shift;
|
||||
my %others = map { $_ => 1 } @_;
|
||||
my @links = GetRaw($url) =~ /<link\b *(.*?)>/g;
|
||||
my %others = @_;
|
||||
my $html = GetRaw($url);
|
||||
my @links = $html =~ /<link\b *(.*?)>/g;
|
||||
print $q->p("Debug: " . scalar(@links) . " links found") if GetParam("debug");
|
||||
print $q->pre($html) unless scalar(@links);
|
||||
print $q->p("Debug: no content returned") if GetParam("debug") and not $html;
|
||||
my @feeds;
|
||||
foreach my $link (@links) {
|
||||
my %link;
|
||||
foreach (split(/ /, lc($link))) {
|
||||
my ($attr, $val) = split(/=/, $_, 2);
|
||||
# strip quotes and garbage: "foo"/ -> foo
|
||||
my $to = index($val, substr($val, 0, 1), 1);
|
||||
$val = substr($val, 1, $to -1) if $to >= 0;
|
||||
$link{$attr} = $val;
|
||||
|
||||
print $q->p("Debug: $link")
|
||||
if GetParam("debug");
|
||||
|
||||
if ($link !~ /\brel=(['"])alternate\1/i) {
|
||||
print $q->p("Debug: missing rel='alternate'")
|
||||
if GetParam("debug");
|
||||
next;
|
||||
}
|
||||
next unless $link{rel} eq 'alternate';
|
||||
next unless $valid_content_type{$link{type}};
|
||||
push(@feeds, $link{href}) unless $others{$link{href}};
|
||||
|
||||
$link =~ /\btype=(['"])(.*?)\1/i;
|
||||
my $type = $2;
|
||||
if (not $valid_content_type{$type}) {
|
||||
print $q->p("Debug: type parameter is invalid ($type)")
|
||||
if GetParam("debug");
|
||||
next;
|
||||
}
|
||||
|
||||
$link =~ /\bhref=(['"])(.*?)\1/i;
|
||||
my $href = $2;
|
||||
# clean up blogspot urls and prefer atom format
|
||||
$href =~ s/\?alt=rss$//i if $href =~ /blogspot/i;
|
||||
if (not $href) {
|
||||
print $q->p("Debug: href missing")
|
||||
if GetParam("debug");
|
||||
next;
|
||||
}
|
||||
if ($others{$href}) {
|
||||
print $q->p("Debug: feed already known ($href)")
|
||||
if GetParam("debug");
|
||||
if ($q->param('confirmed') >= 2) {
|
||||
next;
|
||||
} else {
|
||||
# don't look for other alternatives!
|
||||
return 'known', $href;
|
||||
}
|
||||
}
|
||||
|
||||
push(@feeds, $href);
|
||||
}
|
||||
return @feeds;
|
||||
print $q->p("Debug: returning " . scalar(@feeds) . " links found")
|
||||
if GetParam("debug");
|
||||
return 'ok', @feeds;
|
||||
}
|
||||
|
||||
sub config {
|
||||
my %blogs = @_;
|
||||
my $result = qq{#! config file for the RPG Planet
|
||||
# format:
|
||||
# Feed URL in square brackets, followed by name = and the name of the feed
|
||||
};
|
||||
foreach my $url (sort {lc($blogs{$a}->{name}) cmp lc($blogs{$b}->{name})} keys %blogs) {
|
||||
$result .= "[$url]\n";
|
||||
$paramref = $blogs{$url};
|
||||
foreach my $key (sort keys %{$paramref}) {
|
||||
$result .= $key . " = " . $paramref->{$key} . "\n";
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub post_addition {
|
||||
@@ -206,14 +292,9 @@ sub post_addition {
|
||||
$title = $final_url unless $title;
|
||||
print $q->p("Adding ",
|
||||
$q->a({-href=>$final_url}, $title));
|
||||
$blogs{$url} = $title;
|
||||
my $result = qq{#! config file for the RPG Planet
|
||||
# format:
|
||||
# Feed URL in square brackets, followed by name = and the name of the feed
|
||||
};
|
||||
foreach $url (sort {lc($blogs{$a}) cmp lc($blogs{$b})} keys %blogs) {
|
||||
$result .= "[$url]\nname = " . $blogs{$url} . "\n";
|
||||
}
|
||||
my %param = (name => $title);
|
||||
$blogs{$url} = \%param;
|
||||
my $result = config(%blogs);
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my %params = (text => $result,
|
||||
title => $page,
|
||||
@@ -250,16 +331,18 @@ sub main {
|
||||
if ($q->path_info eq '/source') {
|
||||
seek DATA, 0, 0;
|
||||
print "Content-type: text/plain; charset=UTF-8\r\n\r\n", <DATA>;
|
||||
} elsif ($q->path_info eq '/test') {
|
||||
print "Content-type: text/plain; charset=UTF-8\r\n\r\n";
|
||||
print config(parse_blogs());
|
||||
} else {
|
||||
$UserGotoBar .= $q->a({-href=>$q->url . '/source'}, 'Source');
|
||||
print GetHeader('', 'Submit a new blog');
|
||||
print $q->start_div({-class=>'content index'});
|
||||
if (not GetParam('url')
|
||||
or not GetParam($HoneyPotOk)) {
|
||||
if (not GetParam('url')) {
|
||||
print $q->p("Debug: no url parameter provided.") if GetParam("debug");
|
||||
default();
|
||||
} else {
|
||||
SetParam('title', 'Feeds'); # required to trigger HoneyPotInspection()
|
||||
HoneyPotInspection();
|
||||
check_url(GetParam('url'));
|
||||
}
|
||||
print $q->p('Questions? Send mail to Alex Schröder <'
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
#!/usr/bin/perl
|
||||
# Copyright (C) 2005, 2006, 2007 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2005, 2006, 2007, 2012 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc.
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
@@ -38,6 +34,42 @@ my %indexes = (
|
||||
=> 'GNU Emacs Lisp reference manual, Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/elisp/index.html'
|
||||
=> 'GNU Emacs Lisp reference manual, Top Menu',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/message/Index.html'
|
||||
=> 'Message Manual, Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/gnus/Index.html'
|
||||
=> 'The Gnus Newsreader, Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/cl/Function-Index.html'
|
||||
=> 'Common Lisp Extensions, Function Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/ccmode/Variable-Index.html'
|
||||
=> 'CC Mode Manual, Variable Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/ccmode/Concept-and-Key-Index.html'
|
||||
=> 'CC Mode Manual, Command and Function Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/org/Index.html'
|
||||
=> 'Org Mode Manual, Index',
|
||||
'http://www.gnu.org/software/auctex/manual/auctex/Function-Index.html'
|
||||
=> 'AUCTeX Manual, Function Index',
|
||||
'http://www.gnu.org/software/auctex/manual/auctex/Variable-Index.html'
|
||||
=> 'AUCTeX Manual, Variable Index',
|
||||
'http://www.gnu.org/software/auctex/manual/auctex/Concept-Index.html'
|
||||
=> 'AUCTeX Manual, Concept Index',
|
||||
'http://www.gnu.org/software/texinfo/manual/texinfo/html_node/index.html'
|
||||
=> 'Texinfo, Command and Variable Index',
|
||||
'http://www.gnu.org/software/texinfo/manual/texinfo/html_node/General-Index.html'
|
||||
=> 'Texinfo, General Index',
|
||||
'http://www.gnu.org/software/texinfo/manual/info/html_node/Index.html'
|
||||
=> 'Info, Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/dired-x/Command-Index.html'
|
||||
=> 'Dired Extra, Function Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/dired-x/Variable-Index.html'
|
||||
=> 'Dired Extra, Variable Index',
|
||||
'http://www.gnu.org/software/coreutils/manual/html_node/Concept-index.html'
|
||||
=> 'Coreutils, Index',
|
||||
'http://www.gnu.org/software/diffutils/manual/html_node/Index.html'
|
||||
=> 'Diffutils, Index',
|
||||
'http://www.gnu.org/software/findutils/manual/html_node/find_html/Primary-Index.html'
|
||||
=> 'Findutils, Primary Index',
|
||||
'http://www.gnu.org/software/emacs/manual/html_node/ediff/Index.html'
|
||||
=> 'Edfiff, Index',
|
||||
);
|
||||
|
||||
my $db = '/org/org.emacswiki/htdocs/emacs/info-ref.dat';
|
||||
@@ -63,7 +95,9 @@ sub ProcessRequest {
|
||||
sub ShowForm {
|
||||
print $q->header, $q->start_html,
|
||||
$q->start_form, "Index entry: ", $q->textfield('find'), $q->submit, $q->end_form,
|
||||
$q->p('$Id: info-ref,v 1.1 2007/07/13 23:20:57 as Exp $'),
|
||||
$q->p($q->a({-href=>"http://www.emacswiki.org/scripts/info-ref"}, "Source"), $q->br(),
|
||||
'Last DB update: ', TimeToText((stat($db))[9]),
|
||||
' (' . $q->a({-href=>$q->url . '?init=1'}, "update") . ')'),
|
||||
$q->end_html;
|
||||
}
|
||||
|
||||
@@ -74,9 +108,11 @@ sub Find {
|
||||
foreach my $line (split(/$nl/, $data)) {
|
||||
my ($key, $rest) = split(/$fs/, $line);
|
||||
$map{$key} = ();
|
||||
foreach my $a (split(/$gs/, $rest)) {
|
||||
my ($link, $label) = split(/$rs/, $a);
|
||||
$map{$key}{$link} = $label;
|
||||
if ($rest) {
|
||||
foreach my $a (split(/$gs/, $rest)) {
|
||||
my ($link, $label) = split(/$rs/, $a);
|
||||
$map{$key}{$link} = $label;
|
||||
}
|
||||
}
|
||||
}
|
||||
my @links = keys %{$map{$str}};
|
||||
@@ -150,7 +186,7 @@ sub GetRaw {
|
||||
return unless eval { require LWP::UserAgent; };
|
||||
my $ua = LWP::UserAgent->new;
|
||||
my $response = $ua->get($uri);
|
||||
return $response->content;
|
||||
return $response->decoded_content;
|
||||
}
|
||||
|
||||
sub ReadFile {
|
||||
@@ -189,3 +225,18 @@ sub ReportError { # fatal!
|
||||
print $q->start_html, $q->h2($errmsg), $q->end_html;
|
||||
exit (1);
|
||||
}
|
||||
|
||||
sub CalcDay {
|
||||
my ($sec, $min, $hour, $mday, $mon, $year) = gmtime(shift);
|
||||
return sprintf('%4d-%02d-%02d', $year+1900, $mon+1, $mday);
|
||||
}
|
||||
|
||||
sub CalcTime {
|
||||
my ($sec, $min, $hour, $mday, $mon, $year) = gmtime(shift);
|
||||
return sprintf('%02d:%02d UTC', $hour, $min);
|
||||
}
|
||||
|
||||
sub TimeToText {
|
||||
my $t = shift;
|
||||
return CalcDay($t) . ' ' . CalcTime($t);
|
||||
}
|
||||
|
||||
1054
contrib/oddmuse-curl.el
Normal file
1054
contrib/oddmuse-curl.el
Normal file
File diff suppressed because it is too large
Load Diff
108
contrib/oddmuse_stats
Executable file
108
contrib/oddmuse_stats
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/perl -w
|
||||
# -*- perl -*-
|
||||
|
||||
=head1 NAME
|
||||
|
||||
oddmuse-stats - Plugin to monitor Oddmuse edits
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
Set env.parent_dirs in the config file. The directories in this list
|
||||
are searched for data directories containing rc.log files. No
|
||||
whitespace in the directory names, sorry.
|
||||
|
||||
Example:
|
||||
|
||||
[oddmuse_stats]
|
||||
user www-data
|
||||
env.parent_dirs /home/alex /home/alex/campaignwiki
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Original Author: Alex Schroeder
|
||||
|
||||
=head1 LICENSE
|
||||
|
||||
GPLv3
|
||||
|
||||
=head1 MAGIC MARKERS
|
||||
|
||||
#%# family=auto
|
||||
#%# capabilities=autoconf
|
||||
|
||||
=cut
|
||||
|
||||
use Munin::Plugin;
|
||||
use File::Basename;
|
||||
|
||||
# The wiki directories may not contain any spaces.
|
||||
# Use the config file to set the environment variable!
|
||||
my @parent_dirs = ();
|
||||
my %logfiles = ();
|
||||
my %names = ();
|
||||
my $debug = $ENV{MUNIN_DEBUG};
|
||||
|
||||
if ($ENV{'parent_dirs'}) {
|
||||
@parent_dirs = split(/ /, $ENV{'parent_dirs'});
|
||||
} else {
|
||||
die "The parent_dirs environment variable must be set.\n";
|
||||
}
|
||||
|
||||
for my $parent_dir (@parent_dirs) {
|
||||
warn "opening $parent_dir\n" if $debug;
|
||||
if (opendir(my $dh, $parent_dir)) {
|
||||
while(readdir $dh) {
|
||||
next if $_ eq '.' or $_ eq '..';
|
||||
if (-r "$parent_dir/$_/rc.log") {
|
||||
my $basename = basename($_);
|
||||
$names{clean_fieldname($basename)}
|
||||
= $basename;
|
||||
$logfiles{clean_fieldname($basename)}
|
||||
= "$parent_dir/$_/rc.log";
|
||||
} else {
|
||||
warn "discarding $_\n" if $debug;
|
||||
}
|
||||
}
|
||||
closedir $dh;
|
||||
}
|
||||
}
|
||||
|
||||
my $yesterday = time() - 86400;
|
||||
|
||||
if ($ARGV[0]) {
|
||||
if ($ARGV[0] eq 'autoconf') {
|
||||
if (keys %logfiles) {
|
||||
print "yes\n";
|
||||
exit 0;
|
||||
} else {
|
||||
print "no (no logfiles found in " . join(", ", @parent_dirs) . ")\n";
|
||||
exit 0;
|
||||
}
|
||||
} elsif ($ARGV[0] eq 'config') {
|
||||
print "graph_title Oddmuse Wikis\n";
|
||||
print "graph_category wikis\n";
|
||||
print "graph_info This graph shows how many edits the wiki had in the last 24h.\n";
|
||||
print "graph_vlabel edits/day\n";
|
||||
print "graph_order";
|
||||
for my $wiki (sort keys %logfiles) {
|
||||
print " $wiki";
|
||||
};
|
||||
print "\n";
|
||||
for my $wiki (sort keys %logfiles) {
|
||||
my $name = $names{$wiki};
|
||||
print "$wiki.label $name\n";
|
||||
}
|
||||
exit 0;
|
||||
}
|
||||
}
|
||||
|
||||
for my $wiki (sort keys %logfiles) {
|
||||
open (my $fh, '<', $logfiles{$wiki})
|
||||
or die "cannot open " . $logfiles{$wiki} . ": $!";
|
||||
my $value = 0;
|
||||
while (<$fh>) {
|
||||
my ($ts) = split(/\x1e/);
|
||||
$value++ if $ts and $ts >= $yesterday;
|
||||
}
|
||||
print "$wiki.value $value\n";
|
||||
}
|
||||
51
contrib/twitter
Normal file
51
contrib/twitter
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/perl
|
||||
# Copyright (C) 2009, 2012 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use CGI qw/:standard/;
|
||||
use CGI::Carp qw(fatalsToBrowser);
|
||||
use LWP::UserAgent;
|
||||
use XML::RSS;
|
||||
|
||||
if (not param('feed')) {
|
||||
print header(),
|
||||
start_html('Description Stripping'),
|
||||
h1('Description Stripping'),
|
||||
p('Removes the description of an article if it matches the title. This is most useful for Twitter and other microblogging services.'),
|
||||
p('Example input:', code('http://api.twitter.com/1/statuses/user_timeline.rss?screen_name=kensanata')),
|
||||
start_form(-method=>'GET'),
|
||||
p('Feed: ', textfield('feed', '', 40), checkbox('Strip username'),
|
||||
submit()),
|
||||
end_form(),
|
||||
end_html();
|
||||
exit;
|
||||
}
|
||||
|
||||
$ua = LWP::UserAgent->new;
|
||||
$request = HTTP::Request->new('GET', param('feed'));
|
||||
$response = $ua->request($request);
|
||||
$data = $response->content;
|
||||
exit unless $data;
|
||||
|
||||
print header(-type=>$response->content_type);
|
||||
|
||||
$rss = new XML::RSS;
|
||||
$rss->parse($data);
|
||||
|
||||
foreach my $i (@{$rss->{items}}) {
|
||||
$i->{description} = undef if $i->{description} eq $i->{title};
|
||||
$i->{title} =~ s/^.*?: // if param('Strip username');
|
||||
}
|
||||
|
||||
print $rss->as_string;
|
||||
168
contrib/vc-oddmuse.el
Normal file
168
contrib/vc-oddmuse.el
Normal file
@@ -0,0 +1,168 @@
|
||||
;;; vc-oddmuse.el -- add VC support to oddmuse-curl
|
||||
;;
|
||||
;; Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
;;
|
||||
;; Latest version:
|
||||
;; http://git.savannah.gnu.org/cgit/oddmuse.git/plain/contrib/vc-oddmuse.el
|
||||
;; Discussion, feedback:
|
||||
;; http://www.emacswiki.org/cgi-bin/wiki/OddmuseCurl
|
||||
;;
|
||||
;; This program is free software: you can redistribute it and/or modify it
|
||||
;; under the terms of the GNU General Public License as published by the Free
|
||||
;; Software Foundation, either version 3 of the License, or (at your option)
|
||||
;; any later version.
|
||||
;;
|
||||
;; This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
;; more details.
|
||||
;;
|
||||
;; You should have received a copy of the GNU General Public License along
|
||||
;; with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
;; Add the following to your init file:
|
||||
;;
|
||||
;; (add-to-list 'vc-handled-backends 'oddmuse)
|
||||
|
||||
(add-to-list 'vc-handled-backends 'oddmuse)
|
||||
|
||||
(require 'oddmuse-curl)
|
||||
(require 'diff)
|
||||
|
||||
(defun vc-oddmuse-revision-granularity () 'file)
|
||||
|
||||
(defun vc-oddmuse-registered (file)
|
||||
"Handle files in `oddmuse-directory'."
|
||||
(string-match (concat "^" (expand-file-name oddmuse-directory))
|
||||
(file-name-directory file)))
|
||||
|
||||
(defun vc-oddmuse-state (file)
|
||||
"Return the current version control state of FILE.
|
||||
For a list of possible values, see `vc-state'."
|
||||
;; Avoid downloading the current version from the wiki and comparing
|
||||
;; the text: Too much traffic!
|
||||
'edited)
|
||||
|
||||
(defun vc-oddmuse-working-revision (file)
|
||||
"The current revision based on `oddmuse-revisions'."
|
||||
(oddmuse-revision-get oddmuse-wiki oddmuse-page-name))
|
||||
|
||||
(defun vc-oddmuse-checkout-model (files)
|
||||
"No locking."
|
||||
'implicit)
|
||||
|
||||
(defun vc-oddmuse-create-repo (file)
|
||||
(error "You cannot create Oddmuse wikis using Emacs."))
|
||||
|
||||
(defun vc-oddmuse-register (files &optional rev comment)
|
||||
"This always works.")
|
||||
|
||||
(defun vc-oddmuse-revert (file &optional contents-done)
|
||||
"No idea"
|
||||
nil)
|
||||
|
||||
(defvar vc-oddmuse-log-command
|
||||
(concat "curl --silent %w"
|
||||
" --form action=rc"
|
||||
" --form showedit=1"
|
||||
" --form all=1"
|
||||
" --form from=1"
|
||||
" --form raw=1"
|
||||
" --form match='%r'")
|
||||
"Command to use for publishing index pages.
|
||||
It must print the page to stdout.
|
||||
|
||||
See `oddmuse-format-command' for the formatting options.")
|
||||
|
||||
(defun vc-oddmuse-print-log (files buffer &optional shortlog start-revision limit)
|
||||
"Load complete recent changes for the files."
|
||||
;; Derive `oddmuse-wiki' from the first file
|
||||
(with-oddmuse-file (car files)
|
||||
;; The wiki expects a Perl regular expression!
|
||||
(let ((regexp (concat "^(" (mapconcat 'file-name-nondirectory files "|") ")$")))
|
||||
(oddmuse-run "Getting recent changes" vc-oddmuse-log-command nil nil buffer)))
|
||||
(with-current-buffer buffer
|
||||
(oddmuse-render-rss3))
|
||||
'limit-unsupported)
|
||||
|
||||
(defun vc-oddmuse-log-outgoing ()
|
||||
(error "This is not supported."))
|
||||
|
||||
(defun vc-oddmuse-log-incoming ()
|
||||
(error "This is not supported."))
|
||||
|
||||
(defvar vc-oddmuse-get-revision-command
|
||||
(concat "curl --silent"
|
||||
" --form action=browse"
|
||||
" --form id=%t"
|
||||
" --form revision=%v"
|
||||
" --form raw=1"
|
||||
" '%w'")
|
||||
"Command to use to get older revisions of a page.
|
||||
It must print the page to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'
|
||||
%t Page title as provided by `oddmuse-page-name'
|
||||
%v Revision to retrieve as provided by `oddmuse-revision'")
|
||||
|
||||
(defun oddmuse-revision-filename (rev)
|
||||
"Return filename for revision REV.
|
||||
This uses `oddmuse-directory', `wiki' and `pagename' as bound by
|
||||
`with-oddmuse-file'."
|
||||
(concat oddmuse-directory
|
||||
"/" wiki
|
||||
"/" pagename
|
||||
".~" rev "~"))
|
||||
|
||||
(defun vc-oddmuse-diff (files &optional rev1 rev2 buffer)
|
||||
"Report the differences for FILES."
|
||||
(setq buffer (or buffer (get-buffer-create "*vc-diff*")))
|
||||
(dolist (file files)
|
||||
(with-oddmuse-file file
|
||||
(setq rev1 (or rev1 (oddmuse-get-latest-revision wiki pagename)))
|
||||
(dolist (rev (list rev1 rev2))
|
||||
(when (and rev (not (file-readable-p (oddmuse-revision-filename rev))))
|
||||
(let* ((oddmuse-revision rev)
|
||||
(command vc-oddmuse-get-revision-command)
|
||||
(filename (oddmuse-revision-filename rev)))
|
||||
(with-temp-buffer
|
||||
(oddmuse-run
|
||||
(concat "Downloading revision " rev)
|
||||
command wiki pagename)
|
||||
(write-file filename)))))
|
||||
(diff-no-select
|
||||
(if rev1 (oddmuse-revision-filename rev1) file)
|
||||
(if rev2 (oddmuse-revision-filename rev2) file)
|
||||
nil
|
||||
(vc-switches 'oddmuse 'diff)
|
||||
buffer))))
|
||||
|
||||
(defun vc-oddmuse-revert (file &optional contents-done)
|
||||
"Revert FILE back to the wiki revision.
|
||||
If optional arg CONTENTS-DONE is non-nil, then nothing needs to
|
||||
be done, as the contents of FILE have already been reverted from
|
||||
a version backup."
|
||||
(unless contents-done
|
||||
(with-oddmuse-file file
|
||||
(let ((command (oddmuse-format-command vc-oddmuse-get-revision-command)))
|
||||
(with-temp-buffer
|
||||
(oddmuse-run "Loading" command)
|
||||
(write-file file))))))
|
||||
|
||||
(defun vc-oddmuse-checkin (files rev comment)
|
||||
"Commit changes in FILES to this backend.
|
||||
REV is a historical artifact and should be ignored. COMMENT is
|
||||
used as a check-in comment."
|
||||
(dolist (file files)
|
||||
(with-oddmuse-file file
|
||||
(let* ((summary comment)
|
||||
(command (oddmuse-format-command oddmuse-post-command))
|
||||
(buf (get-buffer-create " *oddmuse-response*")))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(oddmuse-run "Posting" command wiki pagename buf t 302))))))
|
||||
|
||||
(provide 'vc-oddmuse)
|
||||
@@ -1,15 +1,18 @@
|
||||
@font-face {
|
||||
font-family: 'Garamond';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Garamond'), local('GaramondNo8'), local('EB Garamond'), local('EBGaramond'), url(https://themes.googleusercontent.com/static/fonts/ebgaramond/v4/kYZt1bJ8UsGAPRGnkXPeFdIh4imgI8P11RFo6YPCPC0.woff) format('woff');
|
||||
}
|
||||
|
||||
body, rss {
|
||||
font-family: Garamond, GaramondNo8, "Bookman Old Style", Cochin, Baskerville, serif;
|
||||
font-family: Garamond, serif;
|
||||
font-size: 16pt;
|
||||
line-height: 20pt;
|
||||
margin:1em 3em;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
body.sans {
|
||||
font-family: Franklin Gothic Book, Corbel, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* headings: we can use larger sizes if we use a lighter color.
|
||||
we cannot inherit the font-family because header and footer use a narrow font. */
|
||||
|
||||
@@ -71,6 +74,55 @@ h1 a:visited, h2 a:visited, h3 a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* for download buttons and the like */
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 120%;
|
||||
cursor: pointer;
|
||||
padding: 0.4em 0.6em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset, 0px 1px 3px black;
|
||||
}
|
||||
|
||||
.button .icon {
|
||||
color: #363;
|
||||
text-shadow: 0px -1px 1px white, 0px 1px 3px #666;
|
||||
}
|
||||
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* links */
|
||||
|
||||
a.pencil {
|
||||
padding-left: 1ex;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
visible: hidden;
|
||||
transition: visibility 0s 1s, opacity 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
*:hover > a.pencil {
|
||||
visible: visible;
|
||||
transition: opacity .5s linear;
|
||||
opacity: 1;
|
||||
}
|
||||
@media print {
|
||||
a.pencil {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.number {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* stop floating content from flowing over the footer */
|
||||
|
||||
hr {
|
||||
@@ -107,6 +159,15 @@ pre, code, tt {
|
||||
line-height: 110%;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow:hidden;
|
||||
white-space: pre-wrap; /* CSS 3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
/* styling for divs that will be invisible when printing
|
||||
when printing. */
|
||||
|
||||
@@ -149,9 +210,6 @@ div.refer {
|
||||
line-height: 13pt;
|
||||
}
|
||||
|
||||
div.refer a:first-child:before { content: "" }
|
||||
div.refer a:before { content: "• " }
|
||||
|
||||
div.message {
|
||||
background-color:#fee;
|
||||
color:#000;
|
||||
@@ -179,7 +237,6 @@ div.sister hr {
|
||||
}
|
||||
div.sister img {
|
||||
border:none;
|
||||
background-color:#ffe;
|
||||
}
|
||||
|
||||
div.near, div.definition {
|
||||
@@ -197,9 +254,6 @@ div.sidebar ul {
|
||||
|
||||
/* replacements, features */
|
||||
|
||||
del {
|
||||
color: #666;
|
||||
}
|
||||
ins {
|
||||
color: #b33;
|
||||
text-decoration: none;
|
||||
@@ -232,9 +286,15 @@ div.commentshown {
|
||||
p.comment {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.comment {
|
||||
font-size: 14pt;
|
||||
}
|
||||
div.comment h2 {
|
||||
margin-top: 5em;
|
||||
}
|
||||
/* comment pages with username, homepage, and email subscription */
|
||||
.comment span { display: block; }
|
||||
.comment span label { display: inline-block; width: 10em; }
|
||||
.comment form span { display: block; }
|
||||
.comment form span label { display: inline-block; width: 10em; }
|
||||
/* IE sucks */
|
||||
.comment input#username,
|
||||
.comment input#homepage,
|
||||
@@ -334,6 +394,10 @@ div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.left { float:left; margin-right: 1em; }
|
||||
.right { float:right; margin-left: 1em; }
|
||||
.half a img { height: 50%; width: 50%; }
|
||||
@@ -384,78 +448,78 @@ div.left p + p { display:table-caption; caption-side:bottom; }
|
||||
p.table a { float:left; width:20ex; }
|
||||
p.table + p { clear:both; }
|
||||
|
||||
/* no bleeding
|
||||
@media screen {
|
||||
/* no bleeding */
|
||||
div.content, div.rc {
|
||||
overflow:hidden;
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12pt;
|
||||
line-height: 13pt;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
font-size: 12pt;
|
||||
line-height: 13pt;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* hide all the crap */
|
||||
div.diff, div.diff+hr, div.refer, div.near, div.definition, div.sister,
|
||||
div.cal, div.footer, span.specialdays, span.gotobar, a.edit, a.number span,
|
||||
div.rc form, form.tiny, p.comment {
|
||||
display:none;
|
||||
div.rc form, form.tiny, p.comment, p#plus1, div.g-plusone {
|
||||
display:none;
|
||||
}
|
||||
a,
|
||||
a:visited,
|
||||
div.content a.near:link,
|
||||
div.content a.near:visited,
|
||||
div.content a.near:active {
|
||||
color:inherit;
|
||||
font-weight: bold;
|
||||
color:inherit;
|
||||
font-weight: bold;
|
||||
}
|
||||
div.content a.feed {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
div.content a.book,
|
||||
div.content a.movie {
|
||||
text-decoration: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
a cite {
|
||||
font-style: italic;
|
||||
font-style: italic;
|
||||
}
|
||||
/* no difference */
|
||||
pre, code, tt {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
/* no dotted underlines */
|
||||
acronym, abbr {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
/* headings */
|
||||
h1 {
|
||||
color: inherit;
|
||||
margin-top: 2em;
|
||||
}
|
||||
h2 {
|
||||
color:inherit;
|
||||
margin: 1em 0;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
h3 {
|
||||
font-weight:inherit;
|
||||
font-style:italic;
|
||||
color:inherit;
|
||||
margin: 1em 0;
|
||||
}
|
||||
h1 a, h2 a, h3 a {
|
||||
color: inherit;
|
||||
}
|
||||
div.journal h1 a:visited,
|
||||
div.journal h2 a:visited,
|
||||
div.journal h3 a:visited {
|
||||
color: inherit;
|
||||
color: inherit;
|
||||
margin-top: 2em;
|
||||
}
|
||||
h2 {
|
||||
color:inherit;
|
||||
margin: 1em 0;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
h3 {
|
||||
font-weight:inherit;
|
||||
font-style:italic;
|
||||
color:inherit;
|
||||
margin: 1em 0;
|
||||
}
|
||||
h1 a, h2 a, h3 a {
|
||||
color: inherit;
|
||||
}
|
||||
div.journal h1 a:visited,
|
||||
div.journal h2 a:visited,
|
||||
div.journal h3 a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
632
css/alex-2014.css
Normal file
632
css/alex-2014.css
Normal file
@@ -0,0 +1,632 @@
|
||||
/* font-face includes TTF for PDF generation */
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text'), local('NoticiaText-Regular)'), url('/fonts/NoticiaText-Regular.woff') format('woff') url('/fonts/NoticiaText-Regular.ttf') format('truetype');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text'), local('NoticiaText-Regular)'), url('/fonts/NoticiaText-Regular.woff') format('woff') url('/fonts/NoticiaText-Regular.ttf') format('truetype');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text'), local('NoticiaText-Regular)'), url('/fonts/NoticiaText-Regular.woff') format('woff') url('/fonts/NoticiaText-Regular.ttf') format('truetype');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold'), local('NoticiaText-Bold)'), url('/fonts/NoticiaText-Bold.woff') format('woff') url('/fonts/NoticiaText-Bold.ttf') format('truetype');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold'), local('NoticiaText-Bold)'), url('/fonts/NoticiaText-Bold.woff') format('woff') url('/fonts/NoticiaText-Bold.ttf') format('truetype');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold'), local('NoticiaText-Bold)'), url('/fonts/NoticiaText-Bold.woff') format('woff') url('/fonts/NoticiaText-Bold.ttf') format('truetype');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text Italic'), local('NoticiaText-Italic)'), url('/fonts/NoticiaText-Italic.woff') format('woff') url('/fonts/NoticiaText-Italic.ttf') format('truetype');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text Italic'), local('NoticiaText-Italic)'), url('/fonts/NoticiaText-Italic.woff') format('woff') url('/fonts/NoticiaText-Italic.ttf') format('truetype');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text Italic'), local('NoticiaText-Italic)'), url('/fonts/NoticiaText-Italic.woff') format('woff') url('/fonts/NoticiaText-Italic.ttf') format('truetype');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold Italic'), local('NoticiaText-BoldItalic)'), url('/fonts/NoticiaText-BoldItalic.woff') format('woff') url('/fonts/NoticiaText-BoldItalic.ttf') format('truetype');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold Italic'), local('NoticiaText-BoldItalic)'), url('/fonts/NoticiaText-BoldItalic.woff') format('woff') url('/fonts/NoticiaText-BoldItalic.ttf') format('truetype');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold Italic'), local('NoticiaText-BoldItalic)'), url('/fonts/NoticiaText-BoldItalic.woff') format('woff') url('/fonts/NoticiaText-BoldItalic.ttf') format('truetype');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Symbola';
|
||||
src: local('Symbola'), url('/fonts/Symbola.woff') format('woff') url('/fonts/Symbola.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body, rss {
|
||||
font-family: "Noticia Text", Symbola, serif;
|
||||
font-style: normal;
|
||||
font-size: 14pt;
|
||||
margin: 1em 3em;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12pt;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* hide all the crap */
|
||||
div.diff, div.diff+hr, div.refer, div.near, div.definition, div.sister,
|
||||
div.cal, div.footer, span.specialdays, span.gotobar, a.edit, a.number span,
|
||||
div.rc form, form.tiny, p.comment, p#plus1, div.g-plusone, div.content a.feed {
|
||||
display:none;
|
||||
}
|
||||
div.content a.book,
|
||||
div.content a.movie {
|
||||
text-decoration: none;
|
||||
}
|
||||
a cite {
|
||||
font-style: italic;
|
||||
}
|
||||
img[alt="RSS"] { display: none }
|
||||
a.rss { font-size: 8pt }
|
||||
}
|
||||
|
||||
/* headings: we can use larger sizes if we use a lighter color.
|
||||
we cannot inherit the font-family because header and footer use a narrow font. */
|
||||
|
||||
h1, h2, h3, title {
|
||||
font-family: inherit;
|
||||
font-weight: normal;
|
||||
}
|
||||
h1, channel title {
|
||||
font-size: 32pt;
|
||||
margin: 1em 0 0.5em 0;
|
||||
padding: 0.4em 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18pt;
|
||||
margin: 2em 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
h3 {
|
||||
font-size: inherit;
|
||||
font-weight: bold;
|
||||
padding: 0;
|
||||
margin: 1em 0 0 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* headers in the journal are smaller */
|
||||
|
||||
div.journal h1, item title {
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
clear: both;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
div.journal h2 {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
div.journal h3 {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
font-style: italic;
|
||||
}
|
||||
div.journal hr {
|
||||
visibility: hidden;
|
||||
}
|
||||
p.more {
|
||||
margin-top: 3em;
|
||||
}
|
||||
/* Links in headings appear on journal pages. */
|
||||
|
||||
h1 a, h2 a, h3 a {
|
||||
color:inherit;
|
||||
text-decoration:none;
|
||||
font-weight: normal;
|
||||
}
|
||||
h1 a:visited, h2 a:visited, h3 a:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* for download buttons and the like */
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 120%;
|
||||
cursor: pointer;
|
||||
padding: 0.4em 0.6em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset, 0px 1px 3px black;
|
||||
}
|
||||
|
||||
.button .icon {
|
||||
color: #363;
|
||||
text-shadow: 0px -1px 1px white, 0px 1px 3px #666;
|
||||
}
|
||||
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* links */
|
||||
|
||||
a.pencil {
|
||||
padding-left: 1ex;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
visibility: hidden;
|
||||
transition: visibility 0s 1s, opacity 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
*:hover > a.pencil {
|
||||
visibility: visible;
|
||||
transition: opacity .5s linear;
|
||||
opacity: 1;
|
||||
}
|
||||
@media print {
|
||||
a.pencil {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a.number {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* stop floating content from flowing over the footer */
|
||||
|
||||
hr {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* the distance between links in the navigation bars */
|
||||
|
||||
span.bar a {
|
||||
margin-right: 1ex;
|
||||
}
|
||||
|
||||
a img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* search box in the top bar */
|
||||
|
||||
.header form, .header p {
|
||||
display: inline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
label[for="searchlang"], #searchlang, .header input[type="submit"] {
|
||||
/* don't use display: none! http://stackoverflow.com/questions/5665203/getting-iphone-go-button-to-submit-form */
|
||||
visibility: hidden; position: absolute;
|
||||
}
|
||||
/* wrap on the iphone */
|
||||
@media media only screen and (max-device-width: 480px) {
|
||||
}
|
||||
|
||||
.header input {
|
||||
width: 10ex;
|
||||
}
|
||||
|
||||
/* other form fields */
|
||||
|
||||
input[type="text"] {
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
line-height: 125%;
|
||||
}
|
||||
|
||||
/* code */
|
||||
|
||||
textarea, pre, code, tt {
|
||||
font-family: "Andale Mono", Monaco, "Courier New", Courier, monospace, "Symbola";
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
pre {
|
||||
overflow:hidden;
|
||||
white-space: pre-wrap; /* CSS 3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
/* styling for divs that will be invisible when printing
|
||||
when printing. */
|
||||
|
||||
div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
|
||||
font-size: 14pt;
|
||||
}
|
||||
@media print {
|
||||
div.header, div.footer, div.near, div.definition, p.comment, a.tag {
|
||||
font-size: 8pt;
|
||||
}
|
||||
}
|
||||
|
||||
div.footer form.search {
|
||||
display: none;
|
||||
}
|
||||
div.rc li + li {
|
||||
margin-top: 1em;
|
||||
}
|
||||
div.rc li strong, table.history strong, strong.description {
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
div.diff {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
font-size: 12pt;
|
||||
color: #000;
|
||||
|
||||
}
|
||||
div.old {
|
||||
background-color: #ffffaf;
|
||||
}
|
||||
div.new {
|
||||
background-color: #cfffcf;
|
||||
}
|
||||
|
||||
div.refer {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
div.message {
|
||||
background-color:#fee;
|
||||
color:#000;
|
||||
}
|
||||
|
||||
img.xml {
|
||||
border:none;
|
||||
padding:1px;
|
||||
}
|
||||
a.small img {
|
||||
max-width:300px;
|
||||
}
|
||||
a.large img {
|
||||
max-width:600px;
|
||||
}
|
||||
div.sister {
|
||||
margin-right:1ex;
|
||||
background-color:inherit;
|
||||
}
|
||||
div.sister p {
|
||||
margin-top:0;
|
||||
}
|
||||
div.sister hr {
|
||||
display:none;
|
||||
}
|
||||
div.sister img {
|
||||
border:none;
|
||||
}
|
||||
|
||||
div.near, div.definition {
|
||||
background-color:#efe;
|
||||
}
|
||||
|
||||
div.sidebar {
|
||||
float:right;
|
||||
border:1px dotted #000;
|
||||
padding:0 1em;
|
||||
}
|
||||
div.sidebar ul {
|
||||
padding-left:1em;
|
||||
}
|
||||
|
||||
/* replacements, features */
|
||||
|
||||
ins {
|
||||
color: #b33;
|
||||
text-decoration: none;
|
||||
}
|
||||
acronym, abbr {
|
||||
letter-spacing:0.1em;
|
||||
font-variant:small-caps;
|
||||
}
|
||||
|
||||
/* Interlink prefix not shown */
|
||||
a .site, a .separator {
|
||||
display: none;
|
||||
}
|
||||
a cite { font:inherit; }
|
||||
/* browser borkage */
|
||||
textarea[name="text"] { width:97%; height:80%; }
|
||||
textarea[name="summary"] { width:97%; height:3em; }
|
||||
/* comments */
|
||||
textarea[name="aftertext"] { width:97%; height:10em; }
|
||||
div.commentshown {
|
||||
font-size: 12pt;
|
||||
padding: 2em 0;
|
||||
}
|
||||
div.commenthidden {
|
||||
display:none;
|
||||
}
|
||||
div.commentshown {
|
||||
display:block;
|
||||
}
|
||||
p.comment {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.comment {
|
||||
font-size: 14pt;
|
||||
}
|
||||
div.comment h2 {
|
||||
margin-top: 5em;
|
||||
}
|
||||
/* comment pages with username, homepage, and email subscription */
|
||||
.comment form span { display: block; }
|
||||
.comment form span label { display: inline-block; width: 10em; }
|
||||
/* IE sucks */
|
||||
.comment input#username,
|
||||
.comment input#homepage,
|
||||
.comment input#mail { width: 20em; }
|
||||
|
||||
/* cal */
|
||||
div.month { padding:0; margin:0 2ex; }
|
||||
body > div.month {
|
||||
float:right;
|
||||
background-color: inherit;
|
||||
border:solid thin;
|
||||
padding:0 1ex;
|
||||
}
|
||||
div.year > div.month {
|
||||
float:left;
|
||||
}
|
||||
div.footer {
|
||||
clear:both;
|
||||
}
|
||||
div.content div.month a.edit {
|
||||
color:inherit;
|
||||
font-weight:inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* history tables and other tables */
|
||||
table.history {
|
||||
border: none;
|
||||
}
|
||||
td.history {
|
||||
border: none;
|
||||
}
|
||||
|
||||
table.user {
|
||||
border: none;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 1em;
|
||||
margin: 1em 2em;
|
||||
}
|
||||
table.user tr td, table.user tr th {
|
||||
border: none;
|
||||
padding: 0.2em 0.5em;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.arab tr th {
|
||||
font-weight:normal;
|
||||
text-align:left;
|
||||
vertical-align:top;
|
||||
}
|
||||
table.arab, table.arab tr th, table.arab tr td {
|
||||
border:none;
|
||||
}
|
||||
th.nobreak {
|
||||
white-space:nowrap;
|
||||
}
|
||||
table.full { width:99%; margin-left:1px; }
|
||||
table.j td, table.j th, table tr td.j, table tr th.j, .j { text-align:justify; }
|
||||
table.l td, table.l th, table tr td.l, table tr th.l, .l { text-align:left; }
|
||||
table.r td, table.r th, table tr td.r, table tr th.r, .r { text-align:right; }
|
||||
table.c td, table.c th, table tr td.c, table tr th.c, .c { text-align:center; }
|
||||
table.t td { vertical-align: top; }
|
||||
td.half { width:50%; }
|
||||
td.third { width:33%; }
|
||||
|
||||
form table td { padding:5px; }
|
||||
|
||||
/* lists */
|
||||
dd { padding-bottom:0.5ex; }
|
||||
dl.inside dt { float:left; }
|
||||
/* search */
|
||||
div.search span.result { font-size:larger; }
|
||||
div.search span.info { font-size:smaller; font-style:italic; }
|
||||
div.search p.result { display:none; }
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
margin: 0 0 0 1ex;
|
||||
padding: 0;
|
||||
border: 1px solid #000;
|
||||
opacity: 0.3;
|
||||
background-color:#ffe;
|
||||
}
|
||||
|
||||
/* images */
|
||||
|
||||
div.content a.feed img, div.journal a.feed img,
|
||||
div.content a img.smiley, div.journal a img.smiley, img.smiley,
|
||||
div.content a.inline img, div.journal a.inline img,
|
||||
div.content li a.image img, div.journal li a.image img {
|
||||
margin: 0; padding: 0; border: none;
|
||||
}
|
||||
div.image a img {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.left { float:left; margin-right: 1em; }
|
||||
.right { float:right; margin-left: 1em; }
|
||||
.half a img { height: 50%; width: 50%; }
|
||||
div.left .left, div.right .right {
|
||||
float:none;
|
||||
}
|
||||
.center { text-align:center; }
|
||||
table.aside {
|
||||
float:right;
|
||||
width:40%;
|
||||
margin-left: 1em;
|
||||
padding: 1ex;
|
||||
border: 1px dotted #666;
|
||||
}
|
||||
table.aside td {
|
||||
text-align:left;
|
||||
}
|
||||
div.sidebar {
|
||||
float:right; width: 250px;
|
||||
text-align: right;
|
||||
border: none;
|
||||
margin: 1ex;
|
||||
}
|
||||
|
||||
.bigsidebar {
|
||||
float:right;
|
||||
width: 500px;
|
||||
border: none;
|
||||
margin-left: 1ex;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
dl.irc dt { width:20ex; float:left; text-align:right; clear:left; }
|
||||
dl.irc dt span.time { float:left; }
|
||||
dl.irc dd { margin-left:22ex; }
|
||||
|
||||
/* portrait */
|
||||
|
||||
div.footer, div.comment, hr { clear: both; }
|
||||
.portrait { float: left; font-size: small; margin-right: 1em; }
|
||||
.portrait a { color: #999; }
|
||||
|
||||
div.left { float:left; margin:1em; padding: 0.5em; }
|
||||
div.left p { display:table-cell; }
|
||||
div.left p + p { display:table-caption; caption-side:bottom; }
|
||||
|
||||
p.table a { float:left; width:20ex; }
|
||||
p.table + p { clear:both; }
|
||||
|
||||
/* no bleeding
|
||||
@media screen {
|
||||
div.content, div.rc {
|
||||
overflow:hidden;
|
||||
}
|
||||
} */
|
||||
|
||||
/* rss */
|
||||
|
||||
channel * { display: block; }
|
||||
|
||||
channel title {
|
||||
margin-top: 30pt;
|
||||
}
|
||||
copyright {
|
||||
font-size: 14pt;
|
||||
margin-top: 1em;
|
||||
}
|
||||
channel > link:before {
|
||||
font-size: 18pt;
|
||||
display: block;
|
||||
margin: 1em;
|
||||
padding: 0.5em;
|
||||
content: "This is an RSS feed, designed to be read in a feed reader.";
|
||||
color: red;
|
||||
border: 1px solid red;
|
||||
}
|
||||
link, license {
|
||||
font-size: 11pt;
|
||||
margin-bottom: 9pt;
|
||||
}
|
||||
username:before { content: "Last edited by "; }
|
||||
username:after { content: "."; }
|
||||
generator:before { content: "Feed generated by "; }
|
||||
generator:after { content: "."; }
|
||||
channel description {
|
||||
font-weight: bold;
|
||||
}
|
||||
item description {
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
docs, language,
|
||||
pubDate, lastBuildDate, ttl, guid, category, comments,
|
||||
docs, image title, image link,
|
||||
status, version, diff, history, importance {
|
||||
display: none;
|
||||
}
|
||||
@@ -108,6 +108,35 @@ a:active {
|
||||
color:#a41;
|
||||
background-color: inherit;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 150%;
|
||||
cursor: pointer;
|
||||
padding: 0.3em 0.5em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset,
|
||||
0px 1px 3px black;
|
||||
}
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* table of contents */
|
||||
.toc {
|
||||
font-size: smaller;
|
||||
border-left: 1em solid #886;
|
||||
}
|
||||
.toc ol {
|
||||
list-style-type: none;
|
||||
padding-left: 1em;
|
||||
}
|
||||
.toc a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* images with links, captions, etc */
|
||||
div.image { display: inline; margin: 1em; font-size: 90%; text-align: center; }
|
||||
@@ -179,7 +208,7 @@ div.rc hr { display: none; }
|
||||
|
||||
/* Tables */
|
||||
table.user {
|
||||
margin: 1em 4em;
|
||||
margin: 1em 0;
|
||||
padding: 0 1em;
|
||||
border-top: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
|
||||
347
css/bootstrap.css
vendored
Normal file
347
css/bootstrap.css
vendored
Normal file
@@ -0,0 +1,347 @@
|
||||
/* Public Domain
|
||||
Written by Alex Schroeder and Evgkeni Sampelnikof */
|
||||
textarea { width:100%; }
|
||||
h1 a { color: inherit }
|
||||
div.journal h1 { font-size:large; }
|
||||
table { margin-bottom: 1em; }
|
||||
|
||||
div.diff { padding-left:5%; padding-right:5%; }
|
||||
div.old { background-color:#FFFFAF; }
|
||||
div.new { background-color:#CFFFCF; }
|
||||
|
||||
img.portrait { float: left; clear: left; margin: 1ex; border:#999 1px solid; }
|
||||
div.footer, div.comment, hr { clear: both; }
|
||||
div.portrait { float: left; clear: left; font-size: xx-small; margin-right: 1em; }
|
||||
div.portrait img.portrait { float: none; margin: 0; }
|
||||
div.portrait a { text-decoration: none; color: #999; }
|
||||
div.color {
|
||||
clear: both;
|
||||
padding: 1ex 2em;
|
||||
margin: 0 -1em;
|
||||
box-shadow: inset 40px 0px 20px -20px #EEEEEE,
|
||||
inset -40px 0px 20px -20px #EEEEEE;
|
||||
}
|
||||
|
||||
.left { float:left; margin-right:1em; }
|
||||
.right { float:right; margin-left:1em; }
|
||||
|
||||
div.two, div.one {
|
||||
color: #444;
|
||||
background-color: #f8f8f8;
|
||||
margin: 7px -1em;
|
||||
box-shadow: inset 40px 0px 20px -20px #EEEEEE,
|
||||
inset -40px 0px 20px -20px #EEEEEE,
|
||||
0px 8px 4px -8px #ccc,
|
||||
0px -6px 4px -8px #ccc;
|
||||
}
|
||||
|
||||
.irc .time { display: none; }
|
||||
dl.irc dt { float: left; text-align: right; width: 13ex; }
|
||||
dl.irc dd { margin-left: 15ex; display: block; }
|
||||
|
||||
div.toc {
|
||||
background-color: #FAFAFA;
|
||||
border: 1px solid #dddddd;
|
||||
font-family: sans-serif;
|
||||
font-size: 80%;
|
||||
line-height: 90%;
|
||||
margin: 3em 0 1em;
|
||||
padding: 1em 0px 0px 1em;
|
||||
border-radius: 3px;
|
||||
}
|
||||
div.toc li {
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.ell .toc li {
|
||||
display: inline;
|
||||
padding-right: 1em;
|
||||
}
|
||||
div.letter { column-count: 3; -webkit-column-count: 3; -moz-column-count: 3 }
|
||||
|
||||
.footer .edit.bar {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.specialdays {
|
||||
line-height: 1em; /* has no effect: set for div.header instead? */
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.footer .time {
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
line-height: 12px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
color: #888;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.footer .legal {
|
||||
text-align: justify;
|
||||
-moz-text-align-last: center;
|
||||
text-align-last: center;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.4em;
|
||||
margin: 0 120px 0;
|
||||
padding: 1em 0 0;
|
||||
}
|
||||
.footer .legal a {
|
||||
color: #888;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.translation.bar {
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
/* .include.WikiLanguageMenu could share those styles,
|
||||
(altough it might be better to leave it left-aligned) */
|
||||
|
||||
.translation.bar a:nth-child(n+2) {
|
||||
border-left: 1px solid #999;
|
||||
}
|
||||
|
||||
.translation.bar a {
|
||||
padding: 6px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.navbar .nav > li > a.brand {
|
||||
color: #C76A0D;
|
||||
padding: 5px 8px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #C76A0D;
|
||||
}
|
||||
a:hover {
|
||||
color: #8F3E0F;
|
||||
}
|
||||
|
||||
body {
|
||||
word-wrap: break-word;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
background-color: #EEEEEE;
|
||||
color: #000;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 1em;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border-top: #ccc 1px solid;
|
||||
border-bottom: #fff 1px solid;
|
||||
}
|
||||
|
||||
.footer_wrapper {
|
||||
background: #EEEEEE;
|
||||
background: linear-gradient(to bottom, #EEEEEE, #CCCCCC);
|
||||
padding-bottom: 20px;
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.footer.container hr:first-child {
|
||||
display: none;
|
||||
}
|
||||
.footer hr {
|
||||
margin: 10px 100px 0;
|
||||
}
|
||||
.footer.container {
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
background: radial-gradient(
|
||||
50% 8px at top,
|
||||
rgba(0, 0, 0, 0.3) 0%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
box-shadow: 0 -1px 2px -2px white;
|
||||
/* border-top: 1px #ccc solid; */
|
||||
}
|
||||
body, li {
|
||||
line-height: 2em;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
text-shadow: 1px 1px white;
|
||||
}
|
||||
.navbar-inner {
|
||||
border-radius: 0 0 4px 4px;
|
||||
border-width: 0 1px 1px;
|
||||
}
|
||||
.navbar .nav > li > a {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
label[for="searchlang"], input#searchlang {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width:480px){
|
||||
h1 { font-size: 1.8em; }
|
||||
.navbar .nav > li > a {
|
||||
padding: 0px 2px;
|
||||
line-height: 10px;
|
||||
}
|
||||
/* hide CC logo */
|
||||
.footer .licence {
|
||||
display: none;
|
||||
}
|
||||
/* make legal foo*/
|
||||
.footer .legal {
|
||||
margin: 5px 5px 5px;
|
||||
}
|
||||
.footer .bar a {
|
||||
margin: 2px 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Right-alignment. Will make it harder to achieve responsive behaviour:
|
||||
|
||||
twitter.github.com/bootstrap/components.html#navbar
|
||||
Look for "Responsive navbar" heading.
|
||||
|
||||
If this is undesirable, remove the lines with the "RA" comment;
|
||||
*/
|
||||
|
||||
.navbar .nav {
|
||||
text-align: right; /* RA */
|
||||
*text-align: left; /* RA */
|
||||
width: 100%; /* RA */
|
||||
}
|
||||
|
||||
.navbar .nav > li:first-child {
|
||||
float: left; /* RA */
|
||||
}
|
||||
|
||||
.navbar .nav > li {
|
||||
display: inline-block; /* RA */
|
||||
float: none; /* RA */
|
||||
*float: left;
|
||||
*display: inline;
|
||||
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
input[type="text"]:focus,
|
||||
input[type="password"]:focus,
|
||||
input[type="datetime"]:focus,
|
||||
input[type="datetime-local"]:focus,
|
||||
input[type="date"]:focus,
|
||||
input[type="month"]:focus,
|
||||
input[type="time"]:focus,
|
||||
input[type="week"]:focus,
|
||||
input[type="number"]:focus,
|
||||
input[type="email"]:focus,
|
||||
input[type="url"]:focus,
|
||||
input[type="search"]:focus,
|
||||
input[type="tel"]:focus,
|
||||
input[type="color"]:focus,
|
||||
.uneditable-input:focus {
|
||||
border-color: rgba(236,160,73,.8);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),
|
||||
0 0 8px rgba(236,160,73,.6);
|
||||
}
|
||||
|
||||
/* IE7/8 Flexibility */
|
||||
|
||||
.container,
|
||||
.navbar-static-top .container,
|
||||
.navbar-fixed-top .container,
|
||||
.navbar-fixed-bottom .container {
|
||||
width: auto;
|
||||
max-width: 940px;
|
||||
}
|
||||
|
||||
/* Don't widen the layout past 940 */
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.container,
|
||||
.navbar-static-top .container,
|
||||
.navbar-fixed-top .container,
|
||||
.navbar-fixed-bottom .container {
|
||||
width: 940px;
|
||||
}
|
||||
}
|
||||
|
||||
div.comment {
|
||||
background: radial-gradient(
|
||||
50% 8px at top,
|
||||
rgba(0, 0, 0, 0.3) 0%,
|
||||
rgba(0, 0, 0, 0) 100%
|
||||
);
|
||||
background-repeat: no-repeat;
|
||||
box-shadow: 0 -1px 2px -2px white;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
div.comment p:nth-child(2) {
|
||||
color: #666;
|
||||
/* line-height: 15px; */
|
||||
font-size: 0.9em;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.comment p:nth-child(1) {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.comment textarea {
|
||||
width: 100%;
|
||||
*width: auto;
|
||||
resize: vertical;
|
||||
*resize: both;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* highlighting source code */
|
||||
|
||||
span.builtin { color: #483d8b; } /* DarkSlateBlue */
|
||||
span.comment { color: #b22222; } /* Firebrick */
|
||||
span.constant { color: #008b8b; } /* DarkCyan */
|
||||
span.function { color: #0000ff; } /* Blue1 */
|
||||
span.keyword { color: #7f007f; } /* Purple */
|
||||
span.string { color: #8b475d; } /* VioletRed4 */
|
||||
span.type { color: #228b22; } /* ForestGreen */
|
||||
span.warning { color: #ff0000; font-weight: bold; } /* Red1 */
|
||||
span.comment span,
|
||||
span.string span { color: inherit; }
|
||||
span.comment span.important.constant,
|
||||
span.string span.important.constant { color: #008b8b; }
|
||||
|
||||
/* old: Equivalent to Output::HTML */
|
||||
|
||||
span.linecomment { color: #b22222; } /* firebrick */
|
||||
span.blockcomment { color: #b22222; } /* firebrick */
|
||||
span.prepro { color: purple; }
|
||||
span.select { font-weight: bold; }
|
||||
span.quote { color: #8b475d; } /* VioletRed4 */
|
||||
span.category_1 { color: teal; }
|
||||
span.category_2 { color: blue; }
|
||||
span.category_3 { color: blue; }
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* Local Variables: */
|
||||
/* css-indent-offset: 4 */
|
||||
/* End: */
|
||||
@@ -115,6 +115,7 @@ div.toc h2 {
|
||||
|
||||
/* get rid of useless "10 results found" when using indexed search. */
|
||||
div.search p.result { display:none; }
|
||||
label[for="searchlang"], input#searchlang { display: none; }
|
||||
|
||||
form.tiny, form.tiny p {
|
||||
display:inline;
|
||||
@@ -237,9 +238,6 @@ a.near:link {
|
||||
a.near:visited {
|
||||
color:#550;
|
||||
}
|
||||
a.tag:before {
|
||||
content:"\2601\ ";
|
||||
}
|
||||
ol, ul, dl {
|
||||
padding-top:0.5em;
|
||||
}
|
||||
@@ -249,7 +247,7 @@ dd, li {
|
||||
|
||||
/* elisp files and other scripts for download */
|
||||
|
||||
p.download a {
|
||||
p.download a, a.download {
|
||||
padding: 0.5em;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
@@ -338,7 +336,22 @@ div.month a.local {
|
||||
|
||||
/* highlighting */
|
||||
|
||||
/* Equivalent to Output::HTML */
|
||||
/* highlighting source code */
|
||||
|
||||
span.builtin { color: #483d8b; } /* DarkSlateBlue */
|
||||
span.comment { color: #b22222; } /* Firebrick */
|
||||
span.constant { color: #008b8b; } /* DarkCyan */
|
||||
span.function { color: #0000ff; } /* Blue1 */
|
||||
span.keyword { color: #7f007f; } /* Purple */
|
||||
span.string { color: #8b475d; } /* VioletRed4 */
|
||||
span.type { color: #228b22; } /* ForestGreen */
|
||||
span.warning { color: #ff0000; font-weight: bold; } /* Red1 */
|
||||
span.comment span,
|
||||
span.string span { color: inherit; }
|
||||
span.comment span.important.constant,
|
||||
span.string span.important.constant { color: #008b8b; }
|
||||
|
||||
/* old: Equivalent to Output::HTML */
|
||||
|
||||
span.linecomment { color: green; }
|
||||
span.blockcomment { color: green; }
|
||||
|
||||
384
css/light.css
Normal file
384
css/light.css
Normal file
@@ -0,0 +1,384 @@
|
||||
/* This file is in the public domain. */
|
||||
|
||||
/* Esteban is nice, but bold is not so nice, and on Windows it suffers.
|
||||
@import url(http://fonts.googleapis.com/css?family=Esteban&subset=latin,latin-ext);
|
||||
|
||||
For campaignwiki.org, we need to use the same URL in the config file when
|
||||
calling wkhtmltopdf.
|
||||
|
||||
@import url(https://fonts.googleapis.com/css?family=Noticia+Text:400,400italic,700italic,700&subset=latin,latin-ext); */
|
||||
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text'), local('NoticiaText-Regular)'), url('/fonts/NoticiaText-Regular.woff') format('woff');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text'), local('NoticiaText-Regular)'), url('/fonts/NoticiaText-Regular.woff') format('woff');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text'), local('NoticiaText-Regular)'), url('/fonts/NoticiaText-Regular.woff') format('woff');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold'), local('NoticiaText-Bold)'), url('/fonts/NoticiaText-Bold.woff') format('woff');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold'), local('NoticiaText-Bold)'), url('/fonts/NoticiaText-Bold.woff') format('woff');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold'), local('NoticiaText-Bold)'), url('/fonts/NoticiaText-Bold.woff') format('woff');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text Italic'), local('NoticiaText-Italic)'), url('/fonts/NoticiaText-Italic.woff') format('woff');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text Italic'), local('NoticiaText-Italic)'), url('/fonts/NoticiaText-Italic.woff') format('woff');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Noticia Text Italic'), local('NoticiaText-Italic)'), url('/fonts/NoticiaText-Italic.woff') format('woff');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold Italic'), local('NoticiaText-BoldItalic)'), url('/fonts/NoticiaText-BoldItalic.woff') format('woff');
|
||||
unicode-range: U+0102-0103, U+1EA0-1EF1, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold Italic'), local('NoticiaText-BoldItalic)'), url('/fonts/NoticiaText-BoldItalic.woff') format('woff');
|
||||
unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Noticia Text';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Noticia Text Bold Italic'), local('NoticiaText-BoldItalic)'), url('/fonts/NoticiaText-BoldItalic.woff') format('woff');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Symbola';
|
||||
src: local('Symbola'), url('/fonts/Symbola.woff') format('woff');
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Noticia Text", Symbola, serif;
|
||||
font-size: 14pt;
|
||||
color: #000;
|
||||
background-color: #eed;
|
||||
margin:1em 2em;
|
||||
}
|
||||
|
||||
textarea, pre, code, tt {
|
||||
font-family: "Andale Mono", Monaco, "Courier New", Courier, monospace, Symbola;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background-color: white;
|
||||
font-family: Times, serif;
|
||||
font-size:10pt;
|
||||
}
|
||||
}
|
||||
|
||||
/* iPhone */
|
||||
|
||||
@media only screen and (max-device-width: 480px) {
|
||||
img { max-width: 480px !important; }
|
||||
}
|
||||
|
||||
/* iPad */
|
||||
|
||||
@media only screen and (min-device-width: 481px) and (max-device-width: 900px) {
|
||||
body { font-size: 150%; }
|
||||
textarea,input { font-size: 100%; }
|
||||
img { max-width: 550px !important; }
|
||||
}
|
||||
|
||||
/* general */
|
||||
.browse { min-height: 3em; }
|
||||
.header form, .header p { margin: 0; }
|
||||
/* hide the buttons but don't use display:none because of
|
||||
http://stackoverflow.com/questions/5665203/getting-iphone-go-button-to-submit-form */
|
||||
.header input[type="submit"] { position: absolute; visibility: hidden; }
|
||||
.header input { width: 5em; font-size: 80%; }
|
||||
.footer { clear:both; font-size: 90%; }
|
||||
.content input { font-size: 80%; line-height: 125%; }
|
||||
|
||||
/* comments, footer */
|
||||
div.commentshown {
|
||||
padding-bottom: 1ex;
|
||||
padding-left: 2em;
|
||||
border-left: 2px solid black;
|
||||
font-size: smaller;
|
||||
}
|
||||
div.commenthidden { display:none; }
|
||||
div.commentshown { display:block; }
|
||||
/* comment pages with username, homepage, and email subscription */
|
||||
.comment span { display: block; }
|
||||
.comment span label {
|
||||
display: inline-block; width: 10em;
|
||||
}
|
||||
input#mail, input#homepage, input#username {
|
||||
display: inline-block; width: 20em;
|
||||
}
|
||||
|
||||
/* titles */
|
||||
h1 {
|
||||
font-weight: bold;
|
||||
font-size: 150%;
|
||||
padding: 1em 0;
|
||||
}
|
||||
h1 a:link, h1 a:visited {
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
font-size: 130%;
|
||||
padding: 1em 0;
|
||||
clear: both;
|
||||
}
|
||||
@media print {
|
||||
h1 a, h2 a, h3 a, h4 a { font-style: normal; }
|
||||
}
|
||||
|
||||
/* links */
|
||||
a:link {
|
||||
color: #851;
|
||||
background-color: inherit;
|
||||
}
|
||||
a:visited {
|
||||
color: #542;
|
||||
background-color: inherit;
|
||||
}
|
||||
a:active {
|
||||
color:#a41;
|
||||
background-color: inherit;
|
||||
}
|
||||
.button {
|
||||
display: inline-block;
|
||||
font-size: 150%;
|
||||
cursor: pointer;
|
||||
padding: 0.3em 0.5em;
|
||||
text-shadow: 0px -1px 0px #ccc;
|
||||
background-color: #cfa;
|
||||
border: 1px solid #9d8;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0px 1px 3px white inset,
|
||||
0px 1px 3px black;
|
||||
}
|
||||
.button a {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
.bar a { padding-right: 1em; }
|
||||
@media print {
|
||||
a, a:link, a:visited {
|
||||
color:#000;
|
||||
text-decoration:none;
|
||||
font-weight: normal;
|
||||
}
|
||||
a.edit, div.footer, form, span.gotobar, a.number span { display:none; }
|
||||
a[class="url number"]:after, a[class="inter number"]:after {
|
||||
content:"[" attr(href) "]";
|
||||
}
|
||||
a[class="local number"]:after { content:"[" attr(title) "]"; }
|
||||
img[smiley] { line-height: inherit; }
|
||||
}
|
||||
|
||||
/* edit paragraphs */
|
||||
a.pencil {
|
||||
padding-left: 1ex;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
visible: hidden;
|
||||
transition: visibility 0s 1s, opacity 1s linear;
|
||||
opacity: 0;
|
||||
}
|
||||
*:hover > a.pencil {
|
||||
visible: visible;
|
||||
transition: opacity .5s linear;
|
||||
opacity: 1;
|
||||
}
|
||||
@media print {
|
||||
a.pencil {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
table a.pencil {
|
||||
position: absolute;
|
||||
right: inherit;
|
||||
}
|
||||
|
||||
/* table of contents */
|
||||
.toc {
|
||||
font-size: smaller;
|
||||
border-left: 1em solid #886;
|
||||
}
|
||||
.toc ol {
|
||||
list-style-type: none;
|
||||
padding-left: 1em;
|
||||
}
|
||||
.toc a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* images with links, captions, etc */
|
||||
div.image { display: inline; margin: 1em; font-size: 90%; text-align: center; }
|
||||
.left { float: left; margin-right: 1em; }
|
||||
.right { float: right; margin-left: 1em; }
|
||||
div.right .right { float: none; }
|
||||
div.left .left { float: none; }
|
||||
.caption { padding: 0 1em; }
|
||||
.license { font-size: small; }
|
||||
.aside {
|
||||
font-size: small;
|
||||
width: 30%;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
margin-bottom: 1em;
|
||||
padding-left: 1em;
|
||||
}
|
||||
.aside img.smiley { height: 1em; }
|
||||
.narrow {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
a img { border: 1px solid #333; }
|
||||
.fit img { width: 80%; text-align: center; margin: 2em 8%; }
|
||||
.half img { width: 50%; height: 50%; text-align: center; margin: 2em 8%; }
|
||||
.noborder img { border: none; }
|
||||
.twenty img { max-width: 20em; }
|
||||
img.logo {
|
||||
float: right;
|
||||
clear: right;
|
||||
border-style:none;
|
||||
margin-left: 1em;
|
||||
margin-bottom: 1ex;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
/* fancy bold underline */
|
||||
em.underline { font-weight: bold; }
|
||||
|
||||
/* editing, previewing */
|
||||
textarea { width:100%; }
|
||||
div.edit { padding-right: 1em; }
|
||||
div.diff { padding-left:5%; padding-right:5%; }
|
||||
div.old { background-color:#FFFFAF; }
|
||||
div.new { background-color:#CFFFCF; }
|
||||
/* div.message { background-color:#FEE; } */
|
||||
div.message {
|
||||
background-color: inherit;
|
||||
font-size: smaller;
|
||||
}
|
||||
table.history { border-style:none; }
|
||||
td.history { border-style:none; }
|
||||
span.result { font-size:larger; }
|
||||
span.info { font-size:smaller; font-style:italic; }
|
||||
div.rc hr { display: none; }
|
||||
div.rc li { padding-bottom: 0.5em; }
|
||||
|
||||
/* Tables */
|
||||
table.user {
|
||||
margin: 1em 0;
|
||||
padding: 0 1em;
|
||||
border-top: 1px solid black;
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
div.aside table.user {
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
}
|
||||
table.user td, table.user th {
|
||||
border-style: none;
|
||||
padding:5px 10px;
|
||||
vertical-align: top;
|
||||
}
|
||||
table.user th { font-weight:bold; }
|
||||
table.user td.r { text-align:right; }
|
||||
table.user td.l { text-align:left; }
|
||||
table.user td.c { text-align:center; }
|
||||
table.user td.j { text-align:justify; }
|
||||
table.user td.mark { background-color:yellow; }
|
||||
tr:empty { display: block; height: 0.5em; }
|
||||
@media print {
|
||||
table {
|
||||
font-size: 9pt;
|
||||
margin: 0;
|
||||
}
|
||||
table.user td, table.user th {
|
||||
padding: 0 1ex;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calendar */
|
||||
div.month { margin:0; padding:0; font-size:x-small; float:right; }
|
||||
div.content div.month { float:none; }
|
||||
div.year div.month { float:left; font-size:medium; padding:1ex; }
|
||||
div.month pre { margin:0; padding:0 0 0 1ex; }
|
||||
div.month a { text-decoration:none; font: inherit; }
|
||||
div.month span.title a { font: inherit; }
|
||||
/* no difference between a.exact and a.collection */
|
||||
div.month a.local { font-weight: bold; }
|
||||
div.month a.local:link { color: #562; }
|
||||
div.month a.local:visited { color: #542; }
|
||||
div.month a.today { background-color: #faa; }
|
||||
div.month span.title a.local { font-weight: normal; color: #842; }
|
||||
@media print {
|
||||
div.month { display: none; }
|
||||
div.year div.month { display: block; }
|
||||
div.year div.month a { display: inline; }
|
||||
}
|
||||
230
css/oddmuse-2013.css
Normal file
230
css/oddmuse-2013.css
Normal file
@@ -0,0 +1,230 @@
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic'), local('GentiumBasic'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/KCktj43blvLkhOTolFn-MVhr3SzZVY8L1R-AhaesIwA.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold'), local('GentiumBasic-Bold'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/2qL6yulgGf0wwgOp-UqGyKuvVGpDTHxx0YlM6XbRIFE.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic Italic'), local('GentiumBasic-Italic'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/qoFz4NSMaYC2UmsMAG3lyajIwExuvJl80GezUi4i-sM.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold Italic'), local('GentiumBasic-BoldItalic'), url(http://themes.googleusercontent.com/static/fonts/gentiumbasic/v5/8N9-c_aQDJ8LbI1NGVMrwjBWbH-5CKom31QWlI8zOIM.woff) format('woff');
|
||||
}
|
||||
|
||||
body {
|
||||
background:#fff;
|
||||
padding:2% 5%;
|
||||
margin:0;
|
||||
font-family: "Gentium Basic", "Bookman Old Style", "Times New Roman", serif;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
div.header h1 {
|
||||
margin-top:2ex;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #a00;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #d88;
|
||||
}
|
||||
|
||||
div.header h1 a:hover, h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover,
|
||||
a:hover, span.caption a.image:hover {
|
||||
background:#fee;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
clear: right;
|
||||
border-style:none;
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
img {
|
||||
padding: 0.5em;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
a.image:hover {
|
||||
background:inherit;
|
||||
}
|
||||
|
||||
a.image:hover img {
|
||||
background:#fee;
|
||||
}
|
||||
|
||||
/* a.definition soll aussehen wie h2 */
|
||||
h2, p a.definition {
|
||||
display:block;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
/* Such Link im h1 soll nicht auffallen. */
|
||||
h1, h2, h3, h4, h1 a, h1 a:visited, p a.definition {
|
||||
color:#666;
|
||||
font-size: 30pt;
|
||||
font-weight: normal;
|
||||
margin: 4ex 0 1ex 0;
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
h3, h4 {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
div.diff {
|
||||
padding: 1em 3em;
|
||||
}
|
||||
div.old {
|
||||
background-color:#FFFFAF;
|
||||
}
|
||||
div.new {
|
||||
background-color:#CFFFCF;
|
||||
}
|
||||
div.old p, div.new p {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
div.refer { padding-left:5%; padding-right:5%; font-size:smaller; }
|
||||
div[class="content refer"] p { margin-top:2em; }
|
||||
div.content div.refer hr { display:none; }
|
||||
div.content div.refer { padding:0; font-size:medium; }
|
||||
div.content div.refer p { margin:0; }
|
||||
div.refer a { display:block; }
|
||||
table.history { border-style:none; }
|
||||
td.history { border-style:none; }
|
||||
|
||||
table.user {
|
||||
border-style: none;
|
||||
margin-left: 3em;
|
||||
}
|
||||
table.user tr td {
|
||||
border-style: none;
|
||||
padding:0.5ex 1ex;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight:bold;
|
||||
}
|
||||
dd {
|
||||
margin-bottom:1ex;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width:100%;
|
||||
height:80%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
textarea#summary { height: 3em; }
|
||||
input {
|
||||
font-size: 12pt;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
li img, img.smiley, .noborder img {
|
||||
border:none;
|
||||
padding:0;
|
||||
margin:0;
|
||||
background:#fff;
|
||||
color:#000;
|
||||
}
|
||||
/* Google +1 */
|
||||
a#plus1 img {
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.header img, div.footer img { border:0; padding:0; margin:0; }
|
||||
|
||||
.left { float:left; }
|
||||
.right { float:right; }
|
||||
div.left .left, div.right .right {
|
||||
float:none;
|
||||
}
|
||||
.center { text-align:center; }
|
||||
|
||||
span.author {
|
||||
color: #501;
|
||||
}
|
||||
span.bar a {
|
||||
padding-right:1ex;
|
||||
}
|
||||
|
||||
.rc .author {
|
||||
color: #655;
|
||||
}
|
||||
|
||||
.rc strong {
|
||||
font-weight: normal;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.rc li {
|
||||
position:relative;
|
||||
padding: 1ex 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border:none;
|
||||
color:black;
|
||||
background-color:#000;
|
||||
height:2px;
|
||||
margin-top:2ex;
|
||||
}
|
||||
|
||||
div.footer hr {
|
||||
height:4px;
|
||||
margin: 2em 0 1ex 0;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
div.content > div.comment {
|
||||
border-top: none;
|
||||
padding-top: none;
|
||||
border-left: 1ex solid #bbb;
|
||||
padding-left: 1ex;
|
||||
}
|
||||
|
||||
div.wrapper > div.comment {
|
||||
border-top: 2px solid #000;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0.5em;
|
||||
margin-left: 1em;
|
||||
margin-right: 2em;
|
||||
white-space: pre;
|
||||
overflow:hidden;
|
||||
white-space: pre-wrap; /* CSS 3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
tt, pre, code {
|
||||
font-size: 80%;
|
||||
};
|
||||
250
css/oddmuse-2014.css
Normal file
250
css/oddmuse-2014.css
Normal file
@@ -0,0 +1,250 @@
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic'), local('GentiumBasic'), url(/fonts/GenBasR.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold'), local('GentiumBasic-Bold'), url(/fonts/GenBasB.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Basic Italic'), local('GentiumBasic-Italic'), url(/fonts/GenBasI.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Basic';
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
src: local('Gentium Basic Bold Italic'), local('GentiumBasic-BoldItalic'), url(/fonts/GenBasBI.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gentium Plus';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Plus'), local('GentiumPlus'), url(/fonts/GentiumPlus-R.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Gentium Plus';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Gentium Plus Italic'), local('GentiumPlus-Italic'), url(/fonts/GentiumPlus-I.woff) format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Symbola';
|
||||
src: local('Symbola'), url('/fonts/Symbola.woff') format('woff') url('/fonts/Symbola.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body {
|
||||
background:#fff;
|
||||
padding:2% 5%;
|
||||
margin:0;
|
||||
font-family: "Gentium Basic", "Gentium Plus", "Symbola", serif;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
div.header h1 {
|
||||
margin-top:2ex;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #a00;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #d88;
|
||||
}
|
||||
|
||||
div.header h1 a:hover, h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover,
|
||||
a:hover, span.caption a.image:hover {
|
||||
background:#fee;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
float: right;
|
||||
clear: right;
|
||||
border-style:none;
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
img {
|
||||
padding: 0.5em;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
a.image:hover {
|
||||
background:inherit;
|
||||
}
|
||||
|
||||
a.image:hover img {
|
||||
background:#fee;
|
||||
}
|
||||
|
||||
/* a.definition soll aussehen wie h2 */
|
||||
h2, p a.definition {
|
||||
display:block;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
/* Such Link im h1 soll nicht auffallen. */
|
||||
h1, h2, h3, h4, h1 a, h1 a:visited, p a.definition {
|
||||
color:#666;
|
||||
font-size: 30pt;
|
||||
font-weight: normal;
|
||||
margin: 4ex 0 1ex 0;
|
||||
padding: 0;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
h3, h4 {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
div.diff {
|
||||
padding: 1em 3em;
|
||||
}
|
||||
div.old {
|
||||
background-color:#FFFFAF;
|
||||
}
|
||||
div.new {
|
||||
background-color:#CFFFCF;
|
||||
}
|
||||
div.old p, div.new p {
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
div.refer { padding-left:5%; padding-right:5%; font-size:smaller; }
|
||||
div[class="content refer"] p { margin-top:2em; }
|
||||
div.content div.refer hr { display:none; }
|
||||
div.content div.refer { padding:0; font-size:medium; }
|
||||
div.content div.refer p { margin:0; }
|
||||
div.refer a { display:block; }
|
||||
table.history { border-style:none; }
|
||||
td.history { border-style:none; }
|
||||
|
||||
table.user {
|
||||
border-style: none;
|
||||
margin-left: 3em;
|
||||
}
|
||||
table.user tr td {
|
||||
border-style: none;
|
||||
padding:0.5ex 1ex;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight:bold;
|
||||
}
|
||||
dd {
|
||||
margin-bottom:1ex;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width:100%;
|
||||
height:80%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
textarea#summary { height: 3em; }
|
||||
input {
|
||||
font-size: 12pt;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
li img, img.smiley, .noborder img {
|
||||
border:none;
|
||||
padding:0;
|
||||
margin:0;
|
||||
background:#fff;
|
||||
color:#000;
|
||||
}
|
||||
/* Google +1 */
|
||||
a#plus1 img {
|
||||
background-color: #fff;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.header img, div.footer img { border:0; padding:0; margin:0; }
|
||||
/* No goto bar at the bottom. */
|
||||
.footer .gotobar, .footer .edit br { display: none; }
|
||||
|
||||
.left { float:left; }
|
||||
.right { float:right; }
|
||||
div.left .left, div.right .right {
|
||||
float:none;
|
||||
}
|
||||
.center { text-align:center; }
|
||||
|
||||
span.author {
|
||||
color: #501;
|
||||
}
|
||||
span.bar a {
|
||||
padding-right:1ex;
|
||||
}
|
||||
|
||||
.rc .author {
|
||||
color: #655;
|
||||
}
|
||||
|
||||
.rc strong {
|
||||
font-weight: normal;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.rc li {
|
||||
position:relative;
|
||||
padding: 1ex 0;
|
||||
}
|
||||
|
||||
hr {
|
||||
border:none;
|
||||
color:black;
|
||||
background-color:#000;
|
||||
height:2px;
|
||||
margin-top:2ex;
|
||||
}
|
||||
|
||||
div.footer hr {
|
||||
height:4px;
|
||||
margin: 2em 0 1ex 0;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
div.content > div.comment {
|
||||
border-top: none;
|
||||
padding-top: none;
|
||||
border-left: 1ex solid #bbb;
|
||||
padding-left: 1ex;
|
||||
}
|
||||
|
||||
div.wrapper > div.comment {
|
||||
border-top: 2px solid #000;
|
||||
padding-top: 2em;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0.5em;
|
||||
margin-left: 1em;
|
||||
margin-right: 2em;
|
||||
white-space: pre;
|
||||
overflow:hidden;
|
||||
white-space: pre-wrap; /* CSS 3 */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
}
|
||||
|
||||
tt, pre, code {
|
||||
font-size: 80%;
|
||||
};
|
||||
1
description
Normal file
1
description
Normal file
@@ -0,0 +1 @@
|
||||
Oddmuse
|
||||
2
modules/aawrapperdiv.pl
Executable file → Normal file
2
modules/aawrapperdiv.pl
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/aawrapperdiv.pl">aawrapperdiv.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/WrapperDiv_Module">WrapperDiv Module</a></p>';
|
||||
AddModuleDescription('aawrapperdiv.pl', 'WrapperDiv Module');
|
||||
|
||||
|
||||
*OldGetHeader = *GetHeader;
|
||||
|
||||
36
modules/accesskeys.pl
Normal file
36
modules/accesskeys.pl
Normal file
@@ -0,0 +1,36 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('accesskeys.pl', 'Links With AccessKeys Extension');
|
||||
|
||||
push(@MyRules, \&LinksWithAccessKeys);
|
||||
sub LinksWithAccessKeys {
|
||||
if (m/\G(\[\[$FreeLinkPattern\{(.)\}\]\])/cog) {
|
||||
my ($id, $key) = ($2, $3);
|
||||
Dirty($1);
|
||||
$id = FreeToNormal($id);
|
||||
my ($class, $resolved, $title, $exists) = ResolveId($id);
|
||||
my $text = NormalToFree($id);
|
||||
if ($resolved) { # anchors don't exist as pages, therefore do not use $exists
|
||||
print ScriptLink(UrlEncode($resolved), $text, $class, undef, $title, $key);
|
||||
} else {
|
||||
print "[[" . QuoteHtml($text) . GetEditLink($id, '?') . "]]";
|
||||
}
|
||||
return ''; # this is a dirty rule that depends the definition of other pages
|
||||
}
|
||||
return undef; # the rule didn't match
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/admin.pl">admin.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Admin_Power_Extension">Admin Power Extension</a></p>';
|
||||
AddModuleDescription('admin.pl', 'Admin Power Extension');
|
||||
|
||||
$Action{delete} = \&AdminPowerDelete;
|
||||
$Action{rename} = \&AdminPowerRename;
|
||||
@@ -62,13 +62,13 @@ sub AdminPowerRename {
|
||||
# Regenerate index on next request -- remove this before errors can occur!
|
||||
unlink($IndexFile);
|
||||
# page file
|
||||
CreatePageDir($PageDir, $new); # It might not exist yet
|
||||
CreateDir($PageDir); # It might not exist yet
|
||||
rename($fname, $newfname)
|
||||
or ReportError(Tss('Cannot rename %1 to %2', $fname, $newfname) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
# keep directory
|
||||
my $kdir = GetKeepDir($id);
|
||||
my $newkdir = GetKeepDir($new);
|
||||
CreatePageDir($KeepDir, $new); # It might not exist yet (only the parent directory!)
|
||||
CreateDir($KeepDir); # It might not exist yet (only the parent directory!)
|
||||
rename($kdir, $newkdir)
|
||||
or ReportError(Tss('Cannot rename %1 to %2', $kdir, $newkdir) . ": $!", '500 INTERNAL SERVER ERROR')
|
||||
if -d $kdir;
|
||||
@@ -76,7 +76,7 @@ sub AdminPowerRename {
|
||||
if (defined(&GetRefererFile)) {
|
||||
my $rdir = GetRefererFile($id);
|
||||
my $newrdir = GetRefererFile($new);
|
||||
CreatePageDir($RefererDir, $new); # It might not exist yet
|
||||
CreateDir($RefererDir); # It might not exist yet
|
||||
rename($rdir, $newrdir)
|
||||
or ReportError(Tss('Cannot rename %1 to %2', $rdir, $newrdir) . ": $!", '500 INTERNAL SERVER ERROR')
|
||||
if -d $rdir;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/adsense.pl">adsense.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/AdSense_Module">AdSense Module</a></p>';
|
||||
AddModuleDescription('adsense.pl', 'AdSense Module');
|
||||
|
||||
use vars qw($AdSensePageName $AdSensePlace);
|
||||
|
||||
|
||||
35
modules/advanced-uploads.pl
Normal file
35
modules/advanced-uploads.pl
Normal file
@@ -0,0 +1,35 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('advanced-uploads.pl', 'Advanced File Upload Extension');
|
||||
|
||||
$HtmlHeaders .= '<script type="text/javascript" src="/js/uploader.js"></script>';
|
||||
|
||||
*AdvancedUploadsOldGetTextArea = *GetTextArea;
|
||||
*GetTextArea = *AdvancedUploadsNewGetTextArea;
|
||||
|
||||
sub AdvancedUploadsNewGetTextArea {
|
||||
my ($name, $text, $rows) = @_;
|
||||
return AdvancedUploadsOldGetTextArea(@_) . $q->br() . ($name =~ 'text|aftertext' ? GetUploadForm() : '');
|
||||
}
|
||||
|
||||
sub GetUploadForm {
|
||||
return $q->span({-class=>'upload'}, $q->label({-for=>'fileToUpload'}, T('Attach file:')),
|
||||
$q->filefield(-name=>'fileToUpload', -id=>'fileToUpload', -multiple=>'multiple', -onChange=>'fileSelected()', -size=>20),
|
||||
$q->span({-id=>'fileSize'}, ''),
|
||||
$q->button(-name=>'uploadButton', -value=>T('Upload'), -onClick=>'uploadFile()'),
|
||||
$q->span({-id=>'progressNumber'}));
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/age.pl">age.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Age_Indication_Extension">Age Indication Extension</a></p>';
|
||||
AddModuleDescription('age.pl', 'Age Indication Extension');
|
||||
|
||||
use vars qw(%AgeEffect $AgeParameter);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/aggregate.pl">aggregate.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Front_Page_Extension">Front Page Extension</a></p>';
|
||||
AddModuleDescription('aggregate.pl', 'Front Page Extension');
|
||||
|
||||
push(@MyRules, \&AggregateRule);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/all.pl">all.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/All_Action">All Action</a></p>';
|
||||
AddModuleDescription('all.pl', 'All Action');
|
||||
|
||||
$Action{all} = \&DoPrintAllPages;
|
||||
|
||||
@@ -28,7 +28,7 @@ sub DoPrintAllPages {
|
||||
print GetHeader('', T('Complete Content'))
|
||||
. $q->p(Ts('The main page is %s.', $q->a({-href=>'#' . $HomePage}, $HomePage)));
|
||||
print $q->p($q->b(Ts('(for %s)', GetParam('lang', 0)))) if GetParam('lang', 0);
|
||||
PrintAllPages(0, 0, undef, AllPagesList());
|
||||
PrintAllPages(0, 0, undef, undef, AllPagesList());
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/anchors.pl">anchors.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Comments_on_Local_Anchor_Extension">Comments on Local Anchor Extension</a></p>';
|
||||
AddModuleDescription('anchors.pl', 'Local Anchor Extension');
|
||||
|
||||
push(@MyRules, \&AnchorsRule);
|
||||
|
||||
|
||||
2
modules/antispam.pl
Executable file → Normal file
2
modules/antispam.pl
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/antispam.pl">antispam.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Antispam_Module">Antispam Module</a></p>';
|
||||
AddModuleDescription('antispam.pl', 'Antispam Module');
|
||||
|
||||
use vars qw($DoMaskEmail $CreateMailtoLinks);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/archive.pl">archive.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Archive_Extension">Archive Extension</a></p>';
|
||||
AddModuleDescription('archive.pl', 'Archive Extension');
|
||||
|
||||
*OldArchiveGetHeader = *GetHeader;
|
||||
*GetHeader = *NewArchiveGetHeader;
|
||||
|
||||
63
modules/askpage.pl
Normal file
63
modules/askpage.pl
Normal file
@@ -0,0 +1,63 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('askpage.pl', 'Ask Page Extension');
|
||||
|
||||
use Fcntl qw(:DEFAULT :flock);
|
||||
|
||||
use vars qw($AskPage $QuestionPage $NewQuestion);
|
||||
# Don't forget to set your $CommentsPattern to include both $AskPage and $QuestionPage
|
||||
$AskPage = 'Ask';
|
||||
$QuestionPage = 'Question_';
|
||||
$NewQuestion = 'Write your question here:';
|
||||
|
||||
sub IncrementInFile {
|
||||
my $filename = shift;
|
||||
sysopen my $fh, $filename, O_RDWR|O_CREAT or die "can't open $filename: $!";
|
||||
flock $fh, LOCK_EX or die "can't flock $filename: $!";
|
||||
my $num = <$fh> || 1;
|
||||
seek $fh, 0, 0 or die "can't rewind $filename: $!";
|
||||
truncate $fh, 0 or die "can't truncate $filename: $!";
|
||||
(print $fh $num+1, "\n") or die "can't write $filename: $!";
|
||||
close $fh or die "can't close $filename: $!";
|
||||
return $num;
|
||||
}
|
||||
|
||||
*OldAskPageDoPost=*DoPost;
|
||||
*DoPost=*NewAskPageDoPost;
|
||||
sub NewAskPageDoPost {
|
||||
my $id = FreeToNormal(shift);
|
||||
if ($id eq $AskPage and not GetParam('text', undef)) { # comment, not a regular edit
|
||||
my $currentQuestion = IncrementInFile("$DataDir/curquestion");
|
||||
$currentQuestion =~ s/[\s\n]//g;
|
||||
return OldAskPageDoPost($QuestionPage . $currentQuestion, @_); # hack page name
|
||||
}
|
||||
OldAskPageDoPost($id, @_); # keep original functionality for regular edits
|
||||
}
|
||||
|
||||
*OldAskPageGetCommentForm=*GetCommentForm;
|
||||
*GetCommentForm=*NewAskPageGetCommentForm;
|
||||
sub NewAskPageGetCommentForm {
|
||||
my ($id, $rev, $comment) = @_;
|
||||
$NewComment = $NewQuestion if $id eq $AskPage;
|
||||
OldAskPageGetCommentForm(@_);
|
||||
}
|
||||
|
||||
*OldAskPageJournalSort=*JournalSort;
|
||||
*JournalSort=NewAskPageJournalSort;
|
||||
sub NewAskPageJournalSort {
|
||||
return OldAskPageJournalSort() unless $a =~ m/^$QuestionPage\d+$/ and $b =~ m/^$QuestionPage\d+$/;
|
||||
($b =~ m/$QuestionPage(\d+)/)[0] <=> ($a =~ m/$QuestionPage(\d+)/)[0];
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004, 2006, 2008 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004, 2006, 2008, 2014 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -17,7 +17,7 @@ use XML::Atom::Entry;
|
||||
use XML::Atom::Link;
|
||||
use XML::Atom::Person;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/atom.pl">atom.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Atom_Extension">Atom Extension</a></p>';
|
||||
AddModuleDescription('atom.pl', 'Atom Extension');
|
||||
|
||||
push(@MyInitVariables, \&AtomInit);
|
||||
|
||||
@@ -140,11 +140,11 @@ sub DoAtomSave {
|
||||
my $title = $entry->title();
|
||||
my $author = $entry->author();
|
||||
SetParam('username', $author->name) if $author; # Used in Save()
|
||||
my $id = FreeToNormal($title) if ValidIdOrDie($title);
|
||||
my $id = FreeToNormal($title);
|
||||
UserCanEditOrDie($id);
|
||||
$oldid = $id unless $oldid;
|
||||
ValidIdOrDie($oldid);
|
||||
my $summary = $entry->summary();
|
||||
ReportError(Ts('Editing not allowed for %s.', $id), '403 FORBIDDEN') unless UserCanEdit($id, 1);
|
||||
# Lock before getting old page to prevent races
|
||||
RequestLockOrError(); # fatal
|
||||
OpenPage($oldid);
|
||||
|
||||
@@ -25,7 +25,7 @@ directory of your Oddmuse Wiki.
|
||||
=cut
|
||||
package OddMuse;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/autolock.pl">autolock.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Autolock_Extension">Autolock Extension</a></p>';
|
||||
AddModuleDescription('autolock.pl', 'Autolock Extension');
|
||||
|
||||
# ....................{ CONFIGURATION }....................
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# ie: http://search.cpan.org/CPAN/authors/id/C/CH/CHAMAS/MLDBM-2.01.tar.gz
|
||||
|
||||
use MLDBM qw( DB_File Storable );
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/backlinkage.pl">backlinkage.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Inline_Backlinks">Inline Backlinks</a></p>';
|
||||
AddModuleDescription('backlinkage.pl', 'Inline Backlinks');
|
||||
|
||||
my $debug=1; # Set Text Output Verbosity when compiling
|
||||
my $backfile = $DataDir . '/backlinks.db'; # Where data lives
|
||||
|
||||
2
modules/backlinks.pl
Executable file → Normal file
2
modules/backlinks.pl
Executable file → Normal file
@@ -10,7 +10,7 @@
|
||||
# For user doc, see:
|
||||
# http://www.oddmuse.org/cgi-bin/oddmuse/Backlinks_Extension
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/backlinks.pl">backlinks.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Backlinks_Extension">Backlinks Extension</a></p>';
|
||||
AddModuleDescription('backlinks.pl', 'Backlinks Extension');
|
||||
|
||||
*OldGetSearchLink = *GetSearchLink;
|
||||
*GetSearchLink = *NewGetSearchLink;
|
||||
|
||||
175
modules/balanced-page-directories.pl
Normal file
175
modules/balanced-page-directories.pl
Normal file
@@ -0,0 +1,175 @@
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2014 Aki Goto <tyatsumi@gmail.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 Balanced Page Directories
|
||||
|
||||
B<WARNING: This module is deprecated.> Oddmuse no longer disperses
|
||||
page data files into 27 directories based on the first character of
|
||||
the page name. The directories used to be "A" to "Z", and "other". If
|
||||
you uses your wiki as a blog, all the pages starting with a date ended
|
||||
up in "other". If your page names started with letters other than "A"
|
||||
to "Z", all the pages ended up in "other". If you were using comment
|
||||
pages, all your comment pages ended in "C". This module was intended
|
||||
to create more subdirectories and spread them more evenly. This is no
|
||||
longer necessary, as the typical filesystem's performance no longer
|
||||
degrades with tens of thousands of files in a directory. I'm assuming
|
||||
most Oddmuse hosts to use some form of GNU/Linux with ext3 or ext4
|
||||
with dir_index option.
|
||||
|
||||
The remaining info for this module is all deprecated.
|
||||
|
||||
=over
|
||||
|
||||
The ext2 inode specification allows for over 100 trillion files to
|
||||
reside in a single directory, however because of the current
|
||||
linked-list directory implementation, only about 10-15 thousand files
|
||||
can realistically be stored in a single directory. – L<haversian-ga on
|
||||
09 Dec 2002 22:56
|
||||
PST|http://answers.google.com/answers/threadview?id=122241>
|
||||
|
||||
=back
|
||||
|
||||
CAUTION: When this extension is installed, your data structure I<must>
|
||||
change. Make sure you have a backup of your data directory somewhere.
|
||||
|
||||
=head2 Finding the right directory
|
||||
|
||||
On the command line, finding the right subdirectory can be a problem.
|
||||
Here's how to use md5sum. Note that the -n option to echo prevents the
|
||||
trailing newline. Its inclusion would change the checksum.
|
||||
|
||||
echo -n HomePage | md5sum | cut -c 1-2
|
||||
c1
|
||||
echo -n ホームページ | md5sum | cut -c 1-2
|
||||
10
|
||||
|
||||
=head2 $BalancedPageDirectoriesSize
|
||||
|
||||
If you have more than 2560000 pages (w00t!) you might want to set
|
||||
$BalancedPageDirectoriesSize to 3. This will give you 16× more
|
||||
directories, which should let you have 40960000 pages. Also, please
|
||||
let us know about your wiki. :)
|
||||
|
||||
=head2 Migration
|
||||
|
||||
Once you install the code, reload any page. This should trigger
|
||||
migration. No output is produced during migration. Migration is
|
||||
triggered whenever a page file isn't found but a page is found at the
|
||||
default old location. If, for example, $PageDir/c1/HomePage.pg doesn't
|
||||
exist but $PageDir/h/HomePage.pg does, and the wiki can be locked, the
|
||||
wiki is locked and migration is started.
|
||||
|
||||
=cut
|
||||
|
||||
AddModuleDescription('balanced-page-directories.pl', 'Balanced Page Directories Extension');
|
||||
|
||||
use Digest::MD5 qw(md5_hex);
|
||||
use File::Find qw(finddepth);
|
||||
use vars qw($BalancedPageDirectoriesSize);
|
||||
|
||||
$BalancedPageDirectoriesSize = 2;
|
||||
|
||||
*OldBalancedPageDirectoriesGetPageDirectory = *GetPageDirectory;
|
||||
*GetPageDirectory = *NewBalancedPageDirectoriesGetPageDirectory;
|
||||
|
||||
sub NewBalancedPageDirectoriesGetPageDirectory {
|
||||
my $id = shift;
|
||||
utf8::encode($id);
|
||||
return substr(md5_hex($id), 0, $BalancedPageDirectoriesSize);
|
||||
}
|
||||
|
||||
*OldBalancedPageDirectoriesOpenPage = *OpenPage;
|
||||
*OpenPage = *NewBalancedPageDirectoriesOpenPage;
|
||||
|
||||
sub NewBalancedPageDirectoriesOpenPage {
|
||||
my $id = shift;
|
||||
if (! -f GetPageFile($id)) {
|
||||
BalancedPageDirectoriesMigrate($id);
|
||||
}
|
||||
return OldBalancedPageDirectoriesOpenPage($id, @_);
|
||||
}
|
||||
|
||||
sub BalancedPageDirectoriesMigrate {
|
||||
my $id = shift;
|
||||
|
||||
# This code is called if the page file does not exist. Perhaps we
|
||||
# need to migrate? Check if the old page file exists. If it does
|
||||
# not, there is no point in migration.
|
||||
*GetPageDirectory = *OldBalancedPageDirectoriesGetPageDirectory;
|
||||
if (not -f GetPageFile($id)) {
|
||||
*GetPageDirectory = *NewBalancedPageDirectoriesGetPageDirectory;
|
||||
return;
|
||||
}
|
||||
|
||||
# Make sure we can change the data structure now.
|
||||
RequestLockOrError();
|
||||
|
||||
# Now we know that we need to migrate. The list of pages is scanned
|
||||
# using globbing.
|
||||
SetParam('refresh', 1);
|
||||
|
||||
for $id (AllPagesList()) {
|
||||
|
||||
*GetPageDirectory = *OldBalancedPageDirectoriesGetPageDirectory;
|
||||
my $page_from = GetPageFile($id);
|
||||
my $keep_from = GetKeepDir($id);
|
||||
my $lock_from = GetLockedPageFile($id);
|
||||
my $joiner_from = $JoinerDir . '/' . GetPageDirectory($username) if $JoinerDir;
|
||||
my $joiner_email_from = $JoinerEmailDir . '/' . GetPageDirectory($username) if $JoinerEmailDir;
|
||||
my $referrer_from = $RefererDir . '/' . GetPageDirectory($id) if $RefererDir;
|
||||
*GetPageDirectory = *NewBalancedPageDirectoriesGetPageDirectory;
|
||||
my $page_to = GetPageFile($id);
|
||||
my $keep_to = GetKeepDir($id);
|
||||
my $lock_to = GetLockedPageFile($id);
|
||||
my $joiner_to = $JoinerDir . '/' . GetPageDirectory($username) if $JoinerDir;
|
||||
my $joiner_email_to = $JoinerEmailDir . '/' . GetPageDirectory($username) if $JoinerEmailDir;
|
||||
my $referrer_to = $RefererDir . '/' . GetPageDirectory($id) if $RefererDir;
|
||||
|
||||
# no clobbering
|
||||
if (! -f $page_to) {
|
||||
CreatePageDir($PageDir, $id);
|
||||
rename $page_from, $page_to || ReportError("Cannot rename $page_from");
|
||||
}
|
||||
if (-f $lock_from and ! -f $lock_to) {
|
||||
rename $lock_from, $lock_to || ReportError("Cannot rename $lock_from");
|
||||
}
|
||||
if (-d $keep_from and ! -d $keep_to) {
|
||||
CreateKeepDir($KeepDir, $id);
|
||||
rename $keep_from, $keep_to || ReportError("Cannot rename $keep_from");
|
||||
}
|
||||
if ($joiner_from and -d $joiner_from and ! -d $joiner_to) {
|
||||
CreatePageDir($JoinerDir, $id);
|
||||
rename $joiner_from, $joiner_to || ReportError("Cannot rename $joiner_from");
|
||||
}
|
||||
if ($joiner_email_from and -d $joiner_email_from and ! -d $joiner_email_to) {
|
||||
CreatePageDir($JoinerEmailDir, $id);
|
||||
rename $joiner_email_from, $joiner_email_to || ReportError("Cannot rename $joiner_email_from");
|
||||
}
|
||||
if ($referrer_from and -d $referrer_from and ! -d $referrer_to) {
|
||||
CreateRefererDir($RefererDir, $id);
|
||||
rename $referrer_from, $referrer_to || ReportError("Cannot rename $referrer_from");
|
||||
}
|
||||
}
|
||||
|
||||
# Delete empty subdirectories. Actually, attempt to delete all the
|
||||
# directories, depth first. It will simply fail for the non-empty
|
||||
# directories. http://www.perlmonks.org/?node_id=520791
|
||||
for my $parent ($PageDir, $KeepDir, $JoinerDir, $JoinerEmailDir, $RefererDir) {
|
||||
next unless $parent;
|
||||
finddepth(sub { rmdir $_ if -d }, $parent);
|
||||
}
|
||||
|
||||
ReleaseLock();
|
||||
}
|
||||
157
modules/ban-contributors.pl
Normal file
157
modules/ban-contributors.pl
Normal file
@@ -0,0 +1,157 @@
|
||||
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
=head1 Ban Contributors Extension
|
||||
|
||||
This module adds "Ban contributors" to the administration page. If you
|
||||
click on it, it will list all the recent contributors to the page
|
||||
you've been looking at. Each contributor (IP or hostname) will be
|
||||
compared to the list of regular expressions on the C<BannedHosts> page
|
||||
(see C<$BannedHosts>). If the contributor is already banned, this is
|
||||
mentioned. If the contributor is not banned, you'll see a button
|
||||
allowing you to ban him or her immediately. If you click the button,
|
||||
the IP or hostname will be added to the C<BannedHosts> page for you.
|
||||
|
||||
=cut
|
||||
|
||||
AddModuleDescription('ban-contributors.pl', 'Ban Contributors Extension');
|
||||
|
||||
push(@MyAdminCode, \&BanMenu);
|
||||
|
||||
sub BanMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
if ($id and UserIsAdmin()) {
|
||||
push(@$menuref, ScriptLink('action=ban;id=' . UrlEncode($id),
|
||||
T('Ban contributors')));
|
||||
}
|
||||
}
|
||||
|
||||
$Action{ban} = \&DoBanHosts;
|
||||
|
||||
sub IsItBanned {
|
||||
my ($it, $regexps) = @_;
|
||||
my $re = undef;
|
||||
foreach my $regexp (@$regexps) {
|
||||
eval { $re = qr/$regexp/i; };
|
||||
if (defined($re) && $it =~ $re) {
|
||||
return $it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub DoBanHosts {
|
||||
my $id = shift;
|
||||
my $content = GetParam('content', '');
|
||||
my $host = GetParam('host', '');
|
||||
if ($content) {
|
||||
SetParam('text', GetPageContent($BannedContent)
|
||||
. $content . " # " . CalcDay($Now) . " "
|
||||
. NormalToFree($id) . "\n");
|
||||
SetParam('summary', NormalToFree($id));
|
||||
DoPost($BannedContent);
|
||||
} elsif ($host) {
|
||||
$host =~ s/\./\\./g;
|
||||
SetParam('text', GetPageContent($BannedHosts)
|
||||
. "^" . $host . " # " . CalcDay($Now) . " "
|
||||
. NormalToFree($id) . "\n");
|
||||
SetParam('summary', NormalToFree($id));
|
||||
DoPost($BannedHosts);
|
||||
} else {
|
||||
ValidIdOrDie($id);
|
||||
print GetHeader('', Ts('Ban Contributors to %s', NormalToFree($id)));
|
||||
SetParam('rcidonly', $id);
|
||||
SetParam('all', 1);
|
||||
SetParam('showedit', 1);
|
||||
my %contrib = ();
|
||||
for my $line (GetRcLines()) {
|
||||
$contrib{$line->[4]}->{$line->[5]} = 1 if $line->[4];
|
||||
}
|
||||
my @regexps = ();
|
||||
foreach (split(/\n/, GetPageContent($BannedHosts))) {
|
||||
if (/^\s*([^#]\S+)/) { # all lines except empty lines and comments, trim whitespace
|
||||
push(@regexps, $1);
|
||||
}
|
||||
}
|
||||
print '<div class="content ban">';
|
||||
foreach (sort(keys %contrib)) {
|
||||
my $name = $_;
|
||||
delete $contrib{$_}{''};
|
||||
$name .= " (" . join(", ", sort(keys(%{$contrib{$_}}))) . ")";
|
||||
if (IsItBanned($_, \@regexps)) {
|
||||
print $q->p(Ts("%s is banned", $name));
|
||||
} else {
|
||||
print GetFormStart(undef, 'get', 'ban'),
|
||||
GetHiddenValue('action', 'ban'),
|
||||
GetHiddenValue('id', $id),
|
||||
GetHiddenValue('host', $_),
|
||||
GetHiddenValue('recent_edit', 'on'),
|
||||
$q->p($name, $q->submit(T('Ban!'))), $q->end_form();
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
=head2 Rollback
|
||||
|
||||
If you are an admin and rolled back a single page, this extension will
|
||||
list the URLs your rollback removed (assuming that those URLs are part
|
||||
of the spam) and it will allow you to provide a regular expression
|
||||
that will be added to BannedHosts.
|
||||
|
||||
=cut
|
||||
|
||||
*OldBanContributorsWriteRcLog = *WriteRcLog;
|
||||
*WriteRcLog = *NewBanContributorsWriteRcLog;
|
||||
|
||||
sub NewBanContributorsWriteRcLog {
|
||||
my ($tag, $id, $to) = @_;
|
||||
if ($tag eq '[[rollback]]' and $id and $to > 0
|
||||
and $OpenPageName eq $id and UserIsAdmin()) {
|
||||
# we currently have the clean page loaded, so we need to reload
|
||||
# the spammed revision (there is a possible race condition here)
|
||||
my ($old) = GetTextRevision($Page{revision}-1, 1);
|
||||
my %urls = map {$_ => 1 } $old =~ /$UrlPattern/og;
|
||||
# we open the file again to force a load of the despammed page
|
||||
foreach my $url ($Page{text} =~ /$UrlPattern/og) {
|
||||
delete($urls{$url});
|
||||
}
|
||||
# we also remove any candidates that are already banned
|
||||
my @regexps = ();
|
||||
foreach (split(/\n/, GetPageContent($BannedContent))) {
|
||||
if (/^\s*([^#]\S+)/) { # all lines except empty lines and comments, trim whitespace
|
||||
push(@regexps, $1);
|
||||
}
|
||||
}
|
||||
foreach my $url (keys %urls) {
|
||||
delete($urls{$url}) if IsItBanned($url, \@regexps);
|
||||
}
|
||||
if (keys %urls) {
|
||||
print $q->p(Ts("These URLs were rolled back. Perhaps you want to add a regular expression to %s?",
|
||||
GetPageLink($BannedContent)));
|
||||
print $q->pre(join("\n", sort keys %urls));
|
||||
print GetFormStart(undef, 'get', 'ban'),
|
||||
GetHiddenValue('action', 'ban'),
|
||||
GetHiddenValue('id', $id),
|
||||
GetHiddenValue('recent_edit', 'on'),
|
||||
$q->p($q->label({-for=>'content'}, T('Regular expression:')), " ",
|
||||
$q->textfield(-name=>'content', -size=>30), " ",
|
||||
$q->submit(T('Ban!'))),
|
||||
$q->end_form();
|
||||
};
|
||||
print $q->p(T("Consider banning the IP number as well: "),
|
||||
ScriptLink('action=ban;id=' . UrlEncode($id), T('Ban contributors')));
|
||||
};
|
||||
return OldBanContributorsWriteRcLog(@_);
|
||||
}
|
||||
37
modules/ban-quick-editors.pl
Normal file
37
modules/ban-quick-editors.pl
Normal file
@@ -0,0 +1,37 @@
|
||||
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# This file must load before logbannedcontent.pl such that quick
|
||||
# editors will be logged.
|
||||
|
||||
AddModuleDescription('ban-quick-editors.pl', 'Banning Quick Editors');
|
||||
|
||||
*BanQuickOldUserIsBanned = *UserIsBanned;
|
||||
*UserIsBanned = *BanQuickNewUserIsBanned;
|
||||
|
||||
sub BanQuickNewUserIsBanned {
|
||||
my $rule = BanQuickOldUserIsBanned(@_);
|
||||
if (not $rule
|
||||
and $SurgeProtection # need surge protection
|
||||
and GetParam('title')) {
|
||||
my $name = GetParam('username', GetRemoteHost());
|
||||
my @entries = @{$RecentVisitors{$name}};
|
||||
# $entry[0] is $Now after AddRecentVisitor
|
||||
my $ts = $entries[1];
|
||||
if ($Now - $ts < 5) {
|
||||
return "fast editing spam bot";
|
||||
}
|
||||
}
|
||||
return $rule;
|
||||
}
|
||||
84
modules/banned-regexps.pl
Normal file
84
modules/banned-regexps.pl
Normal file
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('banned-regexps.pl', 'Banning Regular Expressions');
|
||||
|
||||
=h1 Compatibility
|
||||
|
||||
This extension works with logbannedcontent.pl.
|
||||
|
||||
=h1 Example content for the BannedRegexps page:
|
||||
|
||||
# This page lists regular expressions that prevent the saving of a page.
|
||||
# The regexps are matched against any page or comment submitted.
|
||||
# The format is simple: # comments to the end of the line. Empty lines are ignored.
|
||||
# Everything else is a regular expression. If the regular expression is followed by
|
||||
# a comment, this is used as the explanation when a user is denied editing. If the
|
||||
# comment starts with a date, this date is not included in the explanation.
|
||||
# The date could help us decide which regular expressions to delete in the future.
|
||||
# In other words:
|
||||
# ^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$
|
||||
# Group 1 is the regular expression to use.
|
||||
# Group 4 is the explanation to use.
|
||||
|
||||
порно # 2012-12-31 Russian porn
|
||||
<a\s+href=["']?http # 2012-12-31 HTML anchor tags usually mean spam
|
||||
\[url= # 2012-12-31 bbCode links usually mean spam
|
||||
\s+https?:\S+[ .\r\n]*$ # 2012-12-31 ending with a link usually means spam
|
||||
(?s)\s+https?:\S+.*\s+https?:\S+.*\s+https?:\S+.* # 2012-12-31 three naked links usually mean spam
|
||||
|
||||
=cut
|
||||
|
||||
use vars qw($BannedRegexps);
|
||||
|
||||
$BannedRegexps = 'BannedRegexps';
|
||||
|
||||
push(@MyInitVariables, sub {
|
||||
$AdminPages{$BannedRegexps} = 1;
|
||||
$LockOnCreation{$BannedRegexps} = 1;
|
||||
$PlainTextPages{$BannedRegexps} = 1;
|
||||
});
|
||||
|
||||
*RegexpOldBannedContent = *BannedContent;
|
||||
*BannedContent = *RegexpNewBannedContent;
|
||||
|
||||
# the above also changes the mapping for the variable!
|
||||
$BannedContent = $RegexpOldBannedContent;
|
||||
|
||||
sub RegexpNewBannedContent {
|
||||
my $str = shift;
|
||||
my $rule = RegexpOldBannedContent($str, @_);
|
||||
if (not $rule) {
|
||||
foreach (split(/\n/, GetPageContent($BannedRegexps))) {
|
||||
next unless m/^\s*([^#]+?)\s*(#\s*(\d\d\d\d-\d\d-\d\d\s*)?(.*))?$/;
|
||||
my ($regexp, $comment, $re) = ($1, $4, undef);
|
||||
eval { $re = qr/$regexp/i; };
|
||||
if (defined($re) && $str =~ $re) {
|
||||
my $group1 = $1;
|
||||
my $explanation = ($group1
|
||||
? Tss('Regular expression "%1" matched "%2" on this page.', QuoteHtml($regexp), $group1)
|
||||
: Ts('Regular expression "%s" matched on this page.', QuoteHtml($regexp)));
|
||||
$rule = $explanation . ' '
|
||||
. ($comment ? Ts('Reason: %s.', $comment) : T('Reason unknown.')) . ' '
|
||||
. Ts('See %s for more information.', GetPageLink($BannedRegexps));
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
LogWrite($rule) if $rule and $BannedFile;
|
||||
return $rule if $rule;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,22 +1,23 @@
|
||||
# Copyright (C) 2007, 2008 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2007, 2008, 2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/bbcode.pl">bbcode.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/bbCode_Extension">bbCode Extension</a></p>';
|
||||
AddModuleDescription('bbcode.pl', 'bbCode Extension');
|
||||
|
||||
push(@MyRules, \&bbCodeRule);
|
||||
|
||||
$RuleOrder{\&bbCodeRule} = 100; # must come after PortraitSupportRule
|
||||
|
||||
use vars qw($bbBlock);
|
||||
my %bbTitle = qw(h1 1 h2 1 h3 1 h4 1 h5 1 h6 1);
|
||||
|
||||
@@ -37,6 +38,12 @@ sub bbCodeRule {
|
||||
. qq{font-style: normal;"}); }
|
||||
elsif ($tag eq 's' or $tag eq 'strike') {
|
||||
return AddHtmlEnvironment('del'); }
|
||||
elsif ($tag eq 'tt') {
|
||||
return AddHtmlEnvironment('tt'); }
|
||||
elsif ($tag eq 'sub') {
|
||||
return AddHtmlEnvironment('sub'); }
|
||||
elsif ($tag eq 'sup') {
|
||||
return AddHtmlEnvironment('sup'); }
|
||||
elsif ($tag eq 'color') {
|
||||
return AddHtmlEnvironment('em', qq{style="color: $option; }
|
||||
. qq{font-style: normal;"}); }
|
||||
@@ -96,7 +103,8 @@ sub bbCodeRule {
|
||||
%translate = qw{b b i i u em color em size em font span url a
|
||||
quote blockquote h1 h1 h2 h2 h3 h3 h4 h4 h5 h5
|
||||
h6 h6 center div left div right div list ul
|
||||
s del strike del highlight strong};
|
||||
s del strike del sub sub sup sup highlight strong
|
||||
tt tt};
|
||||
# closing a block level element closes all elements
|
||||
if ($bbBlock eq $translate{$tag}) {
|
||||
/\G([ \t]*\n)*/cg; # eat whitespace after closing block level element
|
||||
@@ -114,15 +122,15 @@ sub bbCodeRule {
|
||||
# smiley
|
||||
elsif (/\G(:-?[()])/cg) {
|
||||
if (substr($1,-1) eq ')') {
|
||||
# '☺' 0009786 00263a WHITE SMILING FACE, So, 0, ON, N,
|
||||
return '☺'; }
|
||||
# 😊 1F60A SMILING FACE WITH SMILING EYES
|
||||
return '😊'; }
|
||||
else {
|
||||
# '☹' 0009785 002639 WHITE FROWNING FACE, So, 0, ON, N,
|
||||
return '☹'; }}
|
||||
# 😟 1F61F WORRIED FACE
|
||||
return '😟'; }}
|
||||
elsif (/\G:(?:smile|happy):/cg) {
|
||||
return '☺'; }
|
||||
return '😊'; }
|
||||
elsif (/\G:(?:sad|frown):/cg) {
|
||||
return '☹'; }
|
||||
return '😟'; }
|
||||
# no match
|
||||
return undef;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/beautify.pl">beautify.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Source_Code_Beautification_Extension">Source Code Beautification Extension</a></p>';
|
||||
AddModuleDescription('beautify.pl', 'Source Code Beautification Extension');
|
||||
|
||||
use Beautifier::Core;
|
||||
use Output::HTML;
|
||||
|
||||
@@ -15,14 +15,18 @@
|
||||
|
||||
package OddMuse;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/big-brother.pl">big-brother.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Big_Brother_Extension">Big Brother Extension</a></p>';
|
||||
AddModuleDescription('big-brother.pl', 'Big Brother Extension');
|
||||
|
||||
use vars qw($VisitorTime);
|
||||
use vars qw($VisitorTime @BigBrotherSecretParameters);
|
||||
|
||||
my $US = "\x1f";
|
||||
|
||||
$VisitorTime = 7200; # keep visitor data arround for 2 hours.
|
||||
|
||||
# normal password parameter from wiki.pl
|
||||
# password parameters from login.pl
|
||||
@BigBrotherSecretParameters = qw(pwd pwd1 pwd2 oldpwd);
|
||||
|
||||
push(@MyAdminCode, \&BigBrotherVisitors);
|
||||
|
||||
sub BigBrotherVisitors {
|
||||
@@ -47,6 +51,13 @@ sub AddRecentVisitor {
|
||||
$ts++ while $entries{$ts};
|
||||
my $action = GetParam('action', 'browse');
|
||||
my $id = GetId(); # script/p/q -> q
|
||||
my %params = map { $_ => 1 } $q->param;
|
||||
for $bad (@BigBrotherSecretParameters) {
|
||||
delete $params{$bad};
|
||||
}
|
||||
my $url = ScriptUrl(join(';', "action=$action;id=" . UrlEncode($id),
|
||||
map { $_ . '=' . UrlEncode(GetParam($_)) }
|
||||
keys %params));
|
||||
my $url = $q->url(-path_info=>1,-query=>1);
|
||||
my $download = GetParam('action', 'browse') eq 'download'
|
||||
|| GetParam('download', 0)
|
||||
|
||||
2
modules/blockquote.pl
Executable file → Normal file
2
modules/blockquote.pl
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/blockquote.pl">blockquote.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Comments_on_Text_Formatting_Rules">Comments on Text Formatting Rules</a></p>';
|
||||
AddModuleDescription('blockquote.pl', 'Comments on Text Formatting Rules');
|
||||
|
||||
push(@MyRules, \&BlockQuoteRule);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
# is useless. This extension will not work under Windows/IIS unless cal
|
||||
# is installed.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/cal3.pl">cal3.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Cal3_Extension">Cal3 Extension</a></p>';
|
||||
AddModuleDescription('cal3.pl', 'Cal3 Extension');
|
||||
|
||||
*OldCalendarGetHeader = *GetHeader;
|
||||
*GetHeader = *NewCalendarGetHeader;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/calendar.pl">calendar.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Calendar_Extension">Calendar Extension</a></p>';
|
||||
AddModuleDescription('calendar.pl', 'Calendar Extension');
|
||||
|
||||
use vars qw($CalendarOnEveryPage $CalAsTable $CalStartMonday);
|
||||
|
||||
@@ -104,7 +104,7 @@ sub DoCollect {
|
||||
# Now save information required for saving the cache of the current page.
|
||||
local (%Page, $OpenPageName);
|
||||
print $q->start_div({-class=>'content journal collection'});
|
||||
PrintAllPages(1, 1, undef, @pages);
|
||||
PrintAllPages(1, 1, undef, undef, @pages);
|
||||
print $q->end_div();
|
||||
}
|
||||
$CollectingJournal = 0;
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/canonical.pl">canonical.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Canonical_Names">Canonical Names</a></p>';
|
||||
AddModuleDescription('canonical.pl', 'Canonical Names');
|
||||
|
||||
use utf8;
|
||||
|
||||
*OldCanonicalResolveId = *ResolveId;
|
||||
*ResolveId = *NewCanonicalResolveId;
|
||||
@@ -39,14 +41,9 @@ sub NewCanonicalResolveId {
|
||||
|
||||
# If the page AlexSchröder exists, [[alexschroder]] will link to it.
|
||||
|
||||
use utf8;
|
||||
use Encode;
|
||||
sub CanonicalName {
|
||||
my $str = shift;
|
||||
$DebugInfo .= ' ' . $str;
|
||||
$str = decode('utf-8', $str);
|
||||
$str =~ tr/äáàâëéèêïíìîöóòôüúùû/aaaaeeeeiiiioooouuuu/;
|
||||
$str = lc($str);
|
||||
$DebugInfo .= '->' . $str;
|
||||
return $str;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/checkbox.pl">checkbox.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Checklist_Extension">Checklist Extension</a></p>';
|
||||
AddModuleDescription('checkbox.pl', 'Checklist Extension');
|
||||
|
||||
# [[ : To do]]
|
||||
# [[X: Done]]
|
||||
|
||||
10
modules/clustermap.pl
Executable file → Normal file
10
modules/clustermap.pl
Executable file → Normal file
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/clustermap.pl">clustermap.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/ClusterMap_Module">ClusterMap Module</a></p>';
|
||||
AddModuleDescription('clustermap.pl', 'ClusterMap Module');
|
||||
|
||||
use vars qw($ClusterMapPage $ClusterMapTOC $FilterUnclusteredRegExp @ClusterMapAdminPages);
|
||||
|
||||
@@ -40,8 +40,8 @@ $PrintTOCAnchor = 0;
|
||||
|
||||
%ClusterMap = ();
|
||||
|
||||
*OldDoRc = *DoRc;
|
||||
*DoRc = *ClusterMapDoRc;
|
||||
*OldPrintRcHtml = *PrintRcHtml;
|
||||
*PrintRcHtml = *ClusterMapPrintRcHtml;
|
||||
|
||||
push(@MyAdminCode, \&ClusterMapAdminRule);
|
||||
|
||||
@@ -178,7 +178,7 @@ sub CreateClusterMap {
|
||||
}
|
||||
}
|
||||
|
||||
sub ClusterMapDoRc {
|
||||
sub ClusterMapPrintRcHtml {
|
||||
my ( @options ) = @_;
|
||||
my $page = "";
|
||||
my $cluster = GetParam(rcclusteronly);
|
||||
@@ -195,7 +195,7 @@ sub ClusterMapDoRc {
|
||||
print "</ul>";
|
||||
}
|
||||
|
||||
OldDoRc(@options);
|
||||
OldPrintRcHtml(@options);
|
||||
}
|
||||
|
||||
sub PrintUnclusteredMap {
|
||||
|
||||
55
modules/comment-div-wrapper.pl
Normal file
55
modules/comment-div-wrapper.pl
Normal file
@@ -0,0 +1,55 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('comment-div-wrapper.pl', 'Comment Div Wrapper Extension');
|
||||
|
||||
my $CommentDiv = 0;
|
||||
push(@MyRules, \&CommentDivWrapper);
|
||||
$RuleOrder{\&CommentDivWrapper} = -50;
|
||||
|
||||
sub CommentDivWrapper {
|
||||
if (substr($OpenPageName, 0, length($CommentsPrefix)) eq $CommentsPrefix) {
|
||||
if (pos == 0 and not $CommentDiv) {
|
||||
$CommentDiv = 1;
|
||||
return $q->start_div({-class=>'userComment'});
|
||||
}
|
||||
}
|
||||
if ($OpenPageName =~ /$CommentsPattern/o) {
|
||||
if ($bol and m/\G(\s*\n)*----+[ \t]*\n?/cg) {
|
||||
my $html = CloseHtmlEnvironments()
|
||||
. ($CommentDiv++ > 0 ? $q->end_div() : $q->h2({-class=>'commentsHeading'}, T('Comments:'))) . $q->start_div({-class=>'userComment'})
|
||||
. AddHtmlEnvironment('p');
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
# close final div
|
||||
*OldCommentDivApplyRules = *ApplyRules;
|
||||
*ApplyRules = *NewCommentDivApplyRules;
|
||||
|
||||
sub NewCommentDivApplyRules {
|
||||
my ($blocks, $flags) = OldCommentDivApplyRules(@_);
|
||||
if ($CommentDiv) {
|
||||
print $q->end_div();
|
||||
$blocks .= $FS . $q->end_div();
|
||||
$flags .= $FS . 0;
|
||||
$CommentDiv = 0;
|
||||
}
|
||||
return ($blocks, $flags);
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/commentcount.pl">commentcount.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Comment_Count_Extension">Comment Count Extension</a></p>';
|
||||
AddModuleDescription('commentcount.pl', 'Comment Count Extension');
|
||||
|
||||
*OldCommentcountAddComment = *AddComment;
|
||||
*AddComment = *NewCommentcountAddComment;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/compilation.pl">compilation.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Compilation_Extension">Compilation Extension</a></p>';
|
||||
AddModuleDescription('compilation.pl', 'Compilation Extension');
|
||||
|
||||
$Action{compilation} = \&DoCompilation;
|
||||
|
||||
@@ -49,7 +49,7 @@ sub PrintCompilation {
|
||||
local %Page;
|
||||
local $OpenPageName='';
|
||||
print '<div class="compilation">';
|
||||
PrintAllPages(1, 1, undef, @pages);
|
||||
PrintAllPages(1, 1, undef, undef, @pages);
|
||||
print '</div>';
|
||||
}
|
||||
return @pages;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/config.pl">config.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Plans">Plans</a></p>';
|
||||
AddModuleDescription('config.pl', 'Plans');
|
||||
|
||||
$Action{config} = \&DoConfig;
|
||||
$Action{clone} = \&DoClone;
|
||||
@@ -37,7 +37,7 @@ sub DoConfig {
|
||||
$SurgeProtection $SurgeProtectionTime $SurgeProtectionViews
|
||||
$DeletedPage $RCName @RcDays $RcDefault $KeepDays
|
||||
$KeepMajor $SummaryHours $SummaryDefaultLength $ShowEdits
|
||||
$UseLookup $RecentTop $RecentLink $PageCluster
|
||||
$RecentTop $RecentLink $PageCluster
|
||||
$InterWikiMoniker $SiteDescription $RssImageUrl $RssRights
|
||||
$RssExclude $RssCacheHours $RssStyleSheet $UploadAllowed
|
||||
@UploadTypes $EmbedWiki $FooterNote $EditNote $TopLinkBar
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/context.pl">context.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Calendar_Extension">Calendar Extension</a></p>';
|
||||
AddModuleDescription('context.pl', 'Calendar Extension');
|
||||
|
||||
push (@MyInitVariables, \&ContextMenuItem);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#
|
||||
# Of course, you can customize this to store more information
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/creationdate.pl">creationdate.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/CreationDate_Module">CreationDate Module</a></p>';
|
||||
AddModuleDescription('creationdate.pl', 'CreationDate Module');
|
||||
|
||||
*CreationDateOldOpenPage = *OpenPage;
|
||||
*OpenPage = CreationDateOpenPage;
|
||||
|
||||
@@ -14,7 +14,7 @@ directory for your Oddmuse Wiki.
|
||||
=cut
|
||||
package OddMuse;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/creole.pl">creole.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Creole_Markup_Extension">Creole Markup Extension</a></p>';
|
||||
AddModuleDescription('creole.pl', 'Creole Markup Extension');
|
||||
|
||||
# ....................{ CONFIGURATION }....................
|
||||
|
||||
@@ -242,6 +242,7 @@ sub CreoleRule {
|
||||
-class=> 'image outside'},
|
||||
$q->img({-src=> UnquoteHtml($1),
|
||||
-alt=> UnquoteHtml($3),
|
||||
-title=> UnquoteHtml($3),
|
||||
-class=> 'url outside'})));
|
||||
}
|
||||
# image link: [[link|{{pic}}]] and [[link|{{pic|text}}]]
|
||||
@@ -252,6 +253,7 @@ sub CreoleRule {
|
||||
ScriptLink(UrlEncode(FreeToNormal($2)),
|
||||
$q->img({-src=> GetDownloadLink(FreeToNormal($3), 2),
|
||||
-alt=> UnquoteHtml($text),
|
||||
-title=> UnquoteHtml($text),
|
||||
-class=> 'upload'}), 'image')), $text);
|
||||
}
|
||||
# image link: [[link|{{url}}]] and [[link|{{url|text}}]]
|
||||
@@ -262,6 +264,7 @@ sub CreoleRule {
|
||||
ScriptLink(UrlEncode(FreeToNormal($2)),
|
||||
$q->img({-src=> UnquoteHtml($3),
|
||||
-alt=> UnquoteHtml($text),
|
||||
-title=> UnquoteHtml($text),
|
||||
-class=> 'url outside'}), 'image')), $text);
|
||||
}
|
||||
# image link: [[url|{{pic}}]] and [[url|{{pic|text}}]]
|
||||
@@ -272,6 +275,7 @@ sub CreoleRule {
|
||||
$q->a({-href=> UnquoteHtml($2), -class=> 'image outside'},
|
||||
$q->img({-src=> GetDownloadLink(FreeToNormal($3), 2),
|
||||
-alt=> UnquoteHtml($text),
|
||||
-title=> UnquoteHtml($text),
|
||||
-class=> 'upload'}))), $text);
|
||||
}
|
||||
# image link: [[url|{{url}}]] and [[url|{{url|text}}]]
|
||||
@@ -281,6 +285,7 @@ sub CreoleRule {
|
||||
$q->a({-href=> UnquoteHtml($1), -class=> 'image outside'},
|
||||
$q->img({-src=> UnquoteHtml($2),
|
||||
-alt=> UnquoteHtml($4),
|
||||
-title=> UnquoteHtml($4),
|
||||
-class=> 'url outside'})));
|
||||
}
|
||||
# link: [[url]] and [[url|text]]
|
||||
@@ -391,7 +396,7 @@ sub CreoleRule {
|
||||
my $is_right_justified = $3;
|
||||
|
||||
# Now that we've retrieved all numbered matches, match another lookahead.
|
||||
my $is_left_justified = m/\G(?=.*?[ \t]+\|)/;
|
||||
my $is_left_justified = m/\G(?=[^\n|]*?[ \t]+\|)/;
|
||||
my $attributes = $column_span == 1 ? '' : qq{colspan="$column_span"};
|
||||
|
||||
if ($is_left_justified and
|
||||
@@ -456,21 +461,16 @@ sub CreoleListAndNewLineRule {
|
||||
my $is_in_list_item = InElement('li');
|
||||
|
||||
# # numbered list
|
||||
if (($bol and m/\G[ \t]*(#)[ \t]*/cg) or
|
||||
($is_in_list_item and m/\G[ \t]*\n+[ \t]*(#+)[ \t]*/cg)) {
|
||||
# * bullet list (nestable; needs space when nested to disambiguate from bold)
|
||||
if (($bol and m/\G[ \t]*([#*])[ \t]*/cg) or
|
||||
($is_in_list_item and m/\G[ \t]*\n+[ \t]*(#+)[ \t]*/cg) or
|
||||
($is_in_list_item and m/\G[ \t]*\n+[ \t]*(\*+)[ \t]+/cg)) {
|
||||
# Note: the first line of this return statement is --not-- equivalent to:
|
||||
# "return CloseHtmlEnvironmentUntil('li')", as that line does not permit
|
||||
# modules overriding the CloseHtmlEnvironments() function to "have a say."
|
||||
return ($is_in_list_item ? CloseHtmlEnvironmentUntil('li') : CloseHtmlEnvironments())
|
||||
.OpenHtmlEnvironment('ol', length($1))
|
||||
.AddHtmlEnvironment ('li');
|
||||
}
|
||||
# * bullet list (nestable; needs space when nested to disambiguate from bold)
|
||||
elsif (($bol and m/\G[ \t]*(\*)[ \t]*/cg) or
|
||||
($is_in_list_item and m/\G[ \t]*\n+[ \t]*(\*+)[ \t]+/cg)) {
|
||||
return ($is_in_list_item ? CloseHtmlEnvironmentUntil('li') : CloseHtmlEnvironments())
|
||||
.OpenHtmlEnvironment('ul', length($1))
|
||||
.AddHtmlEnvironment ('li');
|
||||
.OpenHtmlEnvironment(substr($1, 0, 1) eq '#' ? 'ol' : 'ul', length($1), '', 'ol|ul')
|
||||
.AddHtmlEnvironment('li');
|
||||
}
|
||||
# - bullet list (not nestable; always needs space)
|
||||
elsif ($CreoleDashStyleUnorderedLists and (
|
||||
|
||||
@@ -22,7 +22,7 @@ creoleaddition is simply installable; simply:
|
||||
=cut
|
||||
package OddMuse;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/creoleaddition.pl">creoleaddition.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Creole_Addition_Markup_Extension">Creole Addition Markup Extension</a></p>';
|
||||
AddModuleDescription('creoleaddition.pl', 'Creole Addition Markup Extension');
|
||||
|
||||
# ....................{ CONFIGURATION }....................
|
||||
|
||||
|
||||
2
modules/crossbar.pl
Executable file → Normal file
2
modules/crossbar.pl
Executable file → Normal file
@@ -33,7 +33,7 @@ crossbar is easily installable; move this file into the B<wiki/modules/>
|
||||
directory for your Oddmuse Wiki.
|
||||
|
||||
=cut
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/crossbar.pl">crossbar.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Crossbar_Extension">Crossbar Extension</a></p>';
|
||||
AddModuleDescription('crossbar.pl', 'Crossbar Extension');
|
||||
|
||||
# ....................{ CONFIGURATION }....................
|
||||
use vars qw($CrossbarPageName
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/crumbs.pl">crumbs.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/List_Parent_Pages_Extension">List Parent Pages Extension</a></p>';
|
||||
AddModuleDescription('crumbs.pl', 'List Parent Pages Extension');
|
||||
|
||||
push(@MyRules, \&CrumbsRule);
|
||||
$RuleOrder{\&CrumbsRule} = -10; # run before default rules!
|
||||
|
||||
@@ -15,7 +15,7 @@ automatically.
|
||||
|
||||
=cut
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/css-install.pl">css-install.pl</a></p>';
|
||||
AddModuleDescription('css-install.pl');
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/csv.pl">csv.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Comments_on_Long_Table_Markup_Extension">Comments on Long Table Markup Extension</a></p>';
|
||||
AddModuleDescription('csv.pl', 'Comments on Long Table Markup Extension');
|
||||
|
||||
push(@MyRules, \&CsvRule);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/dates.pl">dates.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Dates_Extension">Dates Extension</a></p>';
|
||||
AddModuleDescription('dates.pl', 'Dates Extension');
|
||||
|
||||
push(@MyAdminCode, \&DatesMenu);
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('debug.pl');
|
||||
|
||||
@Debugging = (\&DebugInterLinks);
|
||||
|
||||
sub DebugInterLinks {
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
# Copyright (C) 2006 Alex Schroeder <alex@emacswiki.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc.
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
# Copyright (C) 2006-2013 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/delete-all.pl">delete-all.pl</a></p>';
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('delete-all.pl');
|
||||
|
||||
use vars qw($DeletedAge);
|
||||
|
||||
@@ -27,6 +23,7 @@ $DeleteAge = 172800; # 2*24*60*60
|
||||
|
||||
# All pages will be deleted after two days of inactivity!
|
||||
sub NewDelPageDeletable {
|
||||
return 1 if $Now - $Page{ts} > $DeleteAge;
|
||||
return 1 if $Now - $Page{ts} > $DeleteAge
|
||||
and not $LockOnCreation{$OpenPageName};
|
||||
return OldDelPageDeletable(@_);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/despam.pl">despam.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Despam_Extension">Despam Extension</a></p>';
|
||||
AddModuleDescription('despam.pl', 'Despam Extension');
|
||||
|
||||
push(@MyAdminCode, \&DespamMenu);
|
||||
|
||||
@@ -91,14 +91,14 @@ sub DespamBannedContent {
|
||||
foreach my $url (@urls) {
|
||||
if ($url =~ /($regexp)/i) {
|
||||
return Tss('Rule "%1" matched "%2" on this page.',
|
||||
QuoteHtml($regexp), $url);
|
||||
QuoteHtml($regexp), QuoteHtml($url));
|
||||
}
|
||||
}
|
||||
}
|
||||
# depends on strange-spam.pl!
|
||||
foreach (@DespamStrangeRules) {
|
||||
my $regexp = $_;
|
||||
if ($str =~ /($regexp)/) {
|
||||
if ($str =~ /($regexp)/i) {
|
||||
my $match = $1;
|
||||
$match =~ s/\n/ /g;
|
||||
return Tss('Rule "%1" matched "%2" on this page.',
|
||||
|
||||
84
modules/diff.pl
Normal file
84
modules/diff.pl
Normal file
@@ -0,0 +1,84 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('diff.pl', 'Diff Action Extension');
|
||||
|
||||
push(@MyRules, \&DiffActionRule);
|
||||
$Action{pagediff} = \&DoDiffAction;
|
||||
|
||||
sub DiffActionRule {
|
||||
return PrintDiffActionChooser($3) if (m/\G(<diff( (.*))>)/cgi);
|
||||
return undef; # the rule didn't match
|
||||
}
|
||||
|
||||
sub DoDiffAction {
|
||||
print GetHeader('', T('Page diff'), '');
|
||||
my $page1 = GetParam('page1');
|
||||
my $page2 = GetParam('page2');
|
||||
my $pattern = GetParam('pattern');
|
||||
$pattern ||= '.*';
|
||||
print PrintDiffActionChooser($pattern);
|
||||
ValidIdOrDie($page1);
|
||||
ValidIdOrDie($page2);
|
||||
my $diff = DoUnifiedDiff("1\n \n" . GetPageContent($page1), "2\n \n" . GetPageContent($page2)); # add extra lines, otherwise diff between identical files will print nothing # TODO fix this, otherwise one day this will fail...
|
||||
$diff = QuoteHtml($diff);
|
||||
$diff =~ tr/\r//d; # TODO is this required? # probably not
|
||||
for (split /\n/, $diff) {
|
||||
s/(^.)//;
|
||||
my $type = $1;
|
||||
if ($type eq '+') {
|
||||
print '<span class="diffactionnew">' . $type;
|
||||
} elsif ($type eq '-') {
|
||||
print '<span class="diffactionold">' . $type;
|
||||
}
|
||||
ApplyRules($_);
|
||||
print '</span>' if $type =~ /[+-]/;
|
||||
print '<br/>';
|
||||
}
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
sub PrintDiffActionChooser {
|
||||
my $pattern = shift;
|
||||
$pattern ||= '.*';
|
||||
my @chosenPages = ();
|
||||
for (@IndexList) {
|
||||
push @chosenPages, $_ if m/$pattern/;
|
||||
}
|
||||
return GetFormStart(undef, 'get', 'pagediff')
|
||||
. GetHiddenValue('action', 'pagediff')
|
||||
. GetHiddenValue('pattern', $pattern)
|
||||
. $q->popup_menu(-name=>'page1', -values=>\@chosenPages) . ' '
|
||||
. $q->popup_menu(-name=>'page2', -values=>\@chosenPages) . ' '
|
||||
. $q->submit(-name=>'', -value=>T('Diff'))
|
||||
. $q->end_form();
|
||||
}
|
||||
|
||||
sub DoUnifiedDiff { # copied from DoDiff
|
||||
CreateDir($TempDir);
|
||||
my $oldName = "$TempDir/old";
|
||||
my $newName = "$TempDir/new";
|
||||
RequestLockDir('diff') or return '';
|
||||
WriteStringToFile($oldName, $_[0]);
|
||||
WriteStringToFile($newName, $_[1]);
|
||||
my $diff_out = `diff -U 99999 -- \Q$oldName\E \Q$newName\E | tail -n +7`; # should be +4, but we always add extra line # TODO that workaround is ugly, fix it!
|
||||
utf8::decode($diff_out); # needs decoding
|
||||
$diff_out =~ s/\n\K\\ No newline.*\n//g; # Get rid of common complaint.
|
||||
ReleaseLockDir('diff');
|
||||
# No need to unlink temp files--next diff will just overwrite.
|
||||
return $diff_out;
|
||||
}
|
||||
39
modules/div-foo.pl
Normal file
39
modules/div-foo.pl
Normal file
@@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('div-foo.pl', 'Div Foo Extension');
|
||||
|
||||
use vars qw($DivFooPrefix);
|
||||
$DivFooPrefix = 'foo_';
|
||||
|
||||
push(@MyRules, \&DivFooRule);
|
||||
|
||||
sub DivFooRule {
|
||||
if (m/\G \< ([a-z-_][a-z-_ ]+[a-z-_]) \> \s*\n /cgx) {
|
||||
return CloseHtmlEnvironment('p') . AddHtmlEnvironment('div', 'class="' . join(' ', map {"$DivFooPrefix$_"} split /\s+/, $1) . '"');
|
||||
}
|
||||
if (m/\G \< ([a-z-_][a-z-_ ]+[a-z-_]) (\?(.*?(?=\>)))? \> /cgx) {
|
||||
my $title = $3 ? ' title="' . QuoteHtml($3) . '"' : '';
|
||||
return AddHtmlEnvironment('span', 'class="' . join(' ', map {"$DivFooPrefix$_"} split /\s+/, $1) . '"' . $title);
|
||||
}
|
||||
if (m/\G \< \/ \/ \> /cgx) {
|
||||
return CloseHtmlEnvironment('div') . (InElement('div') ? '' : AddHtmlEnvironment('p'));
|
||||
}
|
||||
if (m/\G \< \/ \> /cgx) {
|
||||
return CloseHtmlEnvironment('span');
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/dojo.pl">dojo.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Using_Dojo_Instead_Of_Wiki_Markup">Using Dojo Instead Of Wiki Markup</a></p>';
|
||||
AddModuleDescription('dojo.pl', 'Using Dojo Instead Of Wiki Markup');
|
||||
|
||||
use vars qw(@DojoPlugins $DojoTheme);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# edit a page by double-clicking on it. The user must have
|
||||
# JavaScript enabled for this to work.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/doubleclick.pl">doubleclick.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Doubleclick_Extension">Doubleclick Extension</a></p>';
|
||||
AddModuleDescription('doubleclick.pl', 'Doubleclick Extension');
|
||||
|
||||
*OldDoubleclickGetHeader = *GetHeader;
|
||||
*GetHeader = NewDoubleclickGetHeader;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/download.pl">download.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Download_Extension">Download Extension</a></p>';
|
||||
AddModuleDescription('download.pl', 'Download Extension');
|
||||
|
||||
push( @MyRules, \&DownloadSupportRule );
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
use File::Glob ':glob';
|
||||
use vars qw($DraftDir);
|
||||
|
||||
AddModuleDescription('drafts.pl');
|
||||
|
||||
$DraftDir = $DataDir."/draft"; # directory for drafts
|
||||
|
||||
push(@MyInitVariables, \&DraftInit);
|
||||
|
||||
49
modules/duckduckgo-search.pl
Normal file
49
modules/duckduckgo-search.pl
Normal file
@@ -0,0 +1,49 @@
|
||||
# Copyright (C) 2007–2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('duckduckgo-search.pl', 'Use DuckDuckGo For Searches');
|
||||
|
||||
use vars qw($DuckDuckGoSearchDomain);
|
||||
|
||||
$DuckDuckGoSearchDomain = undef;
|
||||
|
||||
$Action{search} = \&DoDuckDuckGoSearch;
|
||||
|
||||
push(@MyInitVariables, \&DuckDuckGoSearchInit);
|
||||
|
||||
sub DuckDuckGoSearchInit {
|
||||
# If $ScriptName does not contain a hostname, this extension will
|
||||
# have no effect. Domain regexp based on RFC 2396 section 3.2.2.
|
||||
if (!$DuckDuckGoSearchDomain) {
|
||||
my $alpha = '[a-zA-Z]';
|
||||
my $alphanum = '[a-zA-Z0-9]';
|
||||
my $alphanumdash = '[-a-zA-Z0-9]';
|
||||
my $domainlabel = "$alphanum($alphanumdash*$alphanum)?";
|
||||
my $toplabel = "$alpha($alphanumdash*$alphanum)?";
|
||||
if ($ScriptName =~ m!^(https?://)?([^/]+\.)?($domainlabel\.$toplabel)\.?(:|/|\z)!) {
|
||||
$DuckDuckGoSearchDomain = $3;
|
||||
}
|
||||
}
|
||||
if ($DuckDuckGoSearchDomain
|
||||
and GetParam('search', undef)
|
||||
and not GetParam('action', undef)
|
||||
and not GetParam('old', 0)) {
|
||||
SetParam('action', 'search');
|
||||
}
|
||||
}
|
||||
|
||||
sub DoDuckDuckGoSearch {
|
||||
my $search = UrlEncode(GetParam('search', undef));
|
||||
print $q->redirect({-uri=>"https://www.duckduckgo.com/?q=$search+site%3A$DuckDuckGoSearchDomain"});
|
||||
}
|
||||
@@ -1,22 +1,18 @@
|
||||
# Copyright (C) 2005 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2005-2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the
|
||||
# Free Software Foundation, Inc.
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/dynamic-comments.pl">dynamic-comments.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Dynamic_Comments_Extension">Dynamic Comments Extension</a></p>';
|
||||
AddModuleDescription('dynamic-comments.pl', 'Dynamic Comments Extension');
|
||||
|
||||
push(@MyInitVariables, \&DynamicCommentsAddScript);
|
||||
|
||||
@@ -51,7 +47,7 @@ sub DynamicCommentsNewGetPageLink {
|
||||
my $anchor = "id" . $num++;
|
||||
return qq{<a href="javascript:togglecomments('$anchor')">$title</a>}
|
||||
. '</p>' # close p before opening div
|
||||
. $q->div({-class=>commenthidden, -id=>$anchor},
|
||||
. $q->div({-class=>'commenthidden', -id=>$anchor},
|
||||
$page,
|
||||
$q->p(DynamicCommentsOldGetPageLink($id, T('Add Comment'))))
|
||||
. '<p>'; # open an empty p that will be closed in PrintAllPages
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
# $LogoUrl = GetDynLogoUrl();
|
||||
#
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/dynlogo.pl">dynlogo.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Dynamic_Logo">Dynamic Logo</a></p>';
|
||||
AddModuleDescription('dynlogo.pl', 'Dynamic Logo');
|
||||
|
||||
use vars qw($DynLogoDirectory $DynLogoDefault %DynLogoMap);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/edit-assist.pl">edit-assist.pl</a></p>';
|
||||
AddModuleDescription('edit-assist.pl');
|
||||
|
||||
push (@MyInitVariables,
|
||||
sub {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
AddModuleDescription('edit-cluster.pl');
|
||||
|
||||
$EditCluster = 'EditCluster';
|
||||
|
||||
sub GetRc {
|
||||
@@ -88,7 +90,7 @@ sub EditClusterNewRcHeader {
|
||||
} else {
|
||||
print $q->h2((GetParam('days', $RcDefault) != 1)
|
||||
? Ts('Updates in the last %s days', GetParam('days', $RcDefault))
|
||||
: Ts('Updates in the last %s day', GetParam('days', $RcDefault)))
|
||||
: Ts('Updates in the last day'))
|
||||
}
|
||||
my $action;
|
||||
my ($idOnly, $userOnly, $hostOnly, $clusterOnly, $filterOnly, $match, $lang) =
|
||||
|
||||
75
modules/edit-paragraphs.js
Normal file
75
modules/edit-paragraphs.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Copyright 2014 Alex Schroeder <alex@gnu.org>
|
||||
based on http://git.savannah.gnu.org/cgit/oddmuse.git/plain/plinks.js
|
||||
for more information see http://oddmuse.org/wiki/Purple_Numbers_Extension
|
||||
based on http://simon.incutio.com/archive/2004/05/30/plinks#p-13
|
||||
Copyright 2004 Simon Willison
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
function add_edit_links() {
|
||||
/* Only show edit links on ordinary pages: They either use
|
||||
* path_info or keywords in the URL, not parameters. */
|
||||
if (/=/.test(document.location.href)) {
|
||||
return;
|
||||
}
|
||||
// find all the pencil links
|
||||
var links = new Array;
|
||||
var elem = document.getElementsByTagName('a');
|
||||
for (var i = 0; i < elem.length; i++) {
|
||||
var atr = elem[i].getAttribute('class');
|
||||
if (atr != null) {
|
||||
var classes = atr.split(" ");
|
||||
for (var j = 0; j < classes.length; j++) {
|
||||
if (classes[j] == 'pencil') {
|
||||
links.push(elem[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// make them invisible
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var link = links[i];
|
||||
var func = function(thislink) {
|
||||
return function() {
|
||||
if (thislink.style.visibility == "visible") {
|
||||
thislink.style.transition = "visibility 0s 1s, opacity 1s linear";
|
||||
thislink.style.visibility = "hidden";
|
||||
thislink.style.opacity = "0";
|
||||
} else {
|
||||
thislink.style.transition = "opacity 1s linear";
|
||||
thislink.style.visibility = "visible";
|
||||
thislink.style.opacity = "1";
|
||||
};
|
||||
}
|
||||
};
|
||||
link.style.transition = "visibility 0s 1s, opacity 1s linear";
|
||||
link.style.visibility = "hidden";
|
||||
link.style.opacity = "0";
|
||||
link.parentNode.onclick = func(link);
|
||||
}
|
||||
}
|
||||
|
||||
function add_load_event(func) {
|
||||
var oldonload = window.onload;
|
||||
if (typeof window.onload != 'function') {
|
||||
window.onload = func;
|
||||
} else {
|
||||
window.onload = function() {
|
||||
oldonload();
|
||||
func();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_load_event(add_edit_links);
|
||||
213
modules/edit-paragraphs.pl
Normal file
213
modules/edit-paragraphs.pl
Normal file
@@ -0,0 +1,213 @@
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('edit-paragraph.pl', 'Edit Paragraphs Extension');
|
||||
|
||||
# edit icon
|
||||
# http://publicdomainvectors.org/en/free-clipart/Pencil-vector-icon/9221.html
|
||||
# q{<img width="30" height="30" title="" alt="edit" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAE2UlEQVRIx53WW0sbaRwG8GcmSsxhdTKT5K60FFqa4CmKh96uq62gtR563butN1KNQSgESqFemBjr2mKjMRpBt1uln2FJe9ObHsyu3d2yyH6EFoTVhr7PXrQTJslMmuwLA0mYvL95/vOebPjSJJMLJZ+t7iu9pw9AHEAQwAGAE4v/Wnasf5dNHsQK/R7A37IsEwAB/ATAY9W/VUpjh9Wi7wHw5s2b4vz588KAKyb9fDMVqniwPgB/AWA0GhX5fF68fftWBAIBHV82wf83bET/AMDJyUnx4cMHwa/t1atXRry07AW0FlhvPwA4dDgclCRJtLe3ixcvXtDYXr9+bcRXjLhcQ+JS9LeGhgbeunVLXL58WQBgIBDg8+fPy/BgMKjjD3VcrrHUOppraGjg3NycWFpaEmfOnKGiKLTC37x5I1paWnT8US2w3voBHDgcDkYiEZFIJITP56PH42E0GmVLSwsB8NKlS8xms0V4JpMRdXV1+lSrWNbS3/sBHLhcLs7OzopEIiH8fj8VReHy8jIjkQhdLhfPnTtXhudyOdHV1UVJkgSAPVQxV4uSulwuhsNhsbi4KPx+P1VV5dLSEsPhMGVZZnNzMxcWFnj27FkCYDAY5P7+vmhra9OT/gLAWe0yOADgwO12c2ZmpoBqmsbFxUWGw2HabDY2Nzdza2uLN27coCzL7Ojo0BPq6C4Ad7Ur0gCAnNvtLkrq9XoZj8cLSVtbW7mxscHx8XEC4NDQEBcWFoSqqjq6DeC7ass7ACBXWl6v18tYLFZA29vbub6+zomJCQLg8PAwt7e3RW9vL7+u22kATWYrlxl6BcDvTqeTkUhExONx4fP5CklnZ2dps9kYCoWYTCYL6PXr15lOp0V3d7eeNGWxUVii7xwOB+fm5spQPWlHR0cROjY2xlQqJXp6enQ0CUD71u5kRP+02+28c+eOiMfj9Hq91DSNiUSCMzMzpuj4+DjX1tZEb2+vjq4C8FZ4pUXoVQDv6+vrGY1GRSwWE5qmUVVVPnjwoICGQiGura0VBtLExASTyaQRfQTAV+U4wlV9E797966IxWJUVZWKonBlZYXT09OUZZltbW1MpVKV0IeSJPnN3qkZPAjgCADv3bsnYrEYPR4PGxsbubq6ytu3b1OSJLa2tjKdTnNsbMyqvCsmqGWprwD4R9/E79+/z6amJrrdbq6vr3NqaqqwImUyGY6OjhIAR0dHSwfSyjeSlrV3dXV1nJ+fFx8/fuSTJ0/Y2NjIjY0NTk1NUZIkBgIB7uzscGRkhAA4MjLCzc1N45R5KEmSr8LZzbRRURTx6dMnkuTx8TGz2SwnJycJgBcvXuTTp0957dq1osWhq6vLOJAqjl6rB6CqqoIkhfhyasnn80yn07xw4QL39/c5PDxMABwcHOTu7q7o7Ow0Thmt2tFbBmuaRiMshODJyQlfvnzJoaEhAuDAwAD39vZEKBQyLg6eGk6h5XB9fT0zmUwRTpKnp6d8/Pgx+/v7+ezZM+PWlrI4sko1lfrre+bm5mYZ/vnzZ2azWREMBnU0XbLLSFVureawjm9tbRUdV3K5nAgEAgQgAGQAuGpALRNLBlgCAKfTib6+PtjtdpyenvLw8FA6OjoSAH4G8COAfyuVzyKYafvVmNrkygPYAWCvMWnFUv8Hwmcq2TCQ4MwAAAAASUVORK5CYII=" />};
|
||||
|
||||
our $EditParagraphPencil = '✎';
|
||||
|
||||
# Allow editing of substrings
|
||||
|
||||
$Action{'edit-paragraph'} = \&DoEditParagraph;
|
||||
|
||||
sub DoEditParagraph {
|
||||
my $id = UnquoteHtml(GetParam('title', ''));
|
||||
UserCanEditOrDie($id);
|
||||
|
||||
my $old = UnquoteHtml(GetParam('paragraph', ''));
|
||||
$old =~ s/\r//g;
|
||||
return DoEdit($id) unless $old;
|
||||
|
||||
my $around = GetParam('around', undef);
|
||||
|
||||
# Find text to edit
|
||||
my $new = GetParam('text', '');
|
||||
OpenPage($id);
|
||||
if ($new) {
|
||||
my $myoldtime = GetParam('oldtime', ''); # maybe empty!
|
||||
my $text;
|
||||
if ($myoldtime and $myoldtime != $LastUpdate) {
|
||||
($text) = GetTextAtTime($myoldtime);
|
||||
} else {
|
||||
$text = $Page{text};
|
||||
}
|
||||
|
||||
my $done;
|
||||
if ($around) {
|
||||
# The tricky part is that the numbers refer to the HTML quoted text. What a pain.
|
||||
my $qold = QuoteHtml($old);
|
||||
my $qtext = QuoteHtml($text);
|
||||
|
||||
if (substr($qtext, $around - length($qold), length($qold)) eq $qold) {
|
||||
$text = UnquoteHtml(substr($qtext, 0, $around - length($qold))
|
||||
. QuoteHtml($new) . substr($qtext, $around));
|
||||
$done = 1;
|
||||
}
|
||||
} else {
|
||||
# simple case, just do it
|
||||
my $search_term = quotemeta($old);
|
||||
$done = $text =~ s/$search_term/$new/;
|
||||
}
|
||||
|
||||
if ($done) {
|
||||
SetParam('text', UnquoteHtml($text));
|
||||
return DoPost($id);
|
||||
} else {
|
||||
$text = substr($text, 0, $around)
|
||||
. "\n### around here ###\n"
|
||||
. substr($text, $around)
|
||||
if $around;
|
||||
ReportError(T('Could not identify the paragraph you were editing'),
|
||||
'500 INTERNAL SERVER ERROR',
|
||||
undef,
|
||||
$q->p(T('This is the section you edited:'))
|
||||
. $q->pre(QuoteHtml($old))
|
||||
. $q->p(T('This is the current page:'))
|
||||
. $q->pre($text));
|
||||
}
|
||||
}
|
||||
print GetHeader('', Ts('Editing %s', NormalToFree($id)));
|
||||
print $q->start_div({-class=>'content edit paragraph'});
|
||||
my $form = GetEditForm($id, undef, $old);
|
||||
my $param = GetHiddenValue('paragraph', $old);
|
||||
$param .= GetHiddenValue('action', 'edit-paragraph'); # add action
|
||||
$param .= GetHiddenValue('around', $around); # add around position
|
||||
$form =~ s!</form>!$param</form>!;
|
||||
print $form;
|
||||
print $q->end_div();
|
||||
PrintFooter($id, 'edit');
|
||||
}
|
||||
|
||||
# When PrintWikiToHTML is called for the current revision of a page we
|
||||
# initialize our data structure. The data structure simply divides the
|
||||
# page up into blocks based on what one would like to edit. By
|
||||
# default, that's just paragraph breaks and list items. When using
|
||||
# Creole, ordered list items and table rows are added.
|
||||
|
||||
my @EditParagraphs = ();
|
||||
|
||||
*EditParagraphOldPrintWikiToHTML = *PrintWikiToHTML;
|
||||
*PrintWikiToHTML = *EditParagraphNewPrintWikiToHTML;
|
||||
|
||||
sub EditParagraphNewPrintWikiToHTML {
|
||||
my ($text, $is_saving_cache, $revision, $is_locked) = @_;
|
||||
# We need to use quoted HTML because that's what the rules will applied to!
|
||||
my $quoted_text = QuoteHtml($text);
|
||||
if ($quoted_text and not $revision) {
|
||||
my ($start, $end) = (0, 0);
|
||||
# This grouping with zero-width positive look-ahead assertion makes sure that this chunk of text does not include
|
||||
# markup need for the next chunk of text.
|
||||
if (grep { $_ eq \&CreoleRule } @MyRules) {
|
||||
$regexp = "\n+(\n|(?=[*#-=|]))";
|
||||
} else {
|
||||
$regexp = "\n+(\n|(?=[*]))";
|
||||
}
|
||||
while ($quoted_text =~ /$regexp/g) {
|
||||
$end = pos($quoted_text);
|
||||
push(@EditParagraphs,
|
||||
[$start, $end, substr($quoted_text, $start, $end - $start)]);
|
||||
$start = $end;
|
||||
}
|
||||
# Only do this if we have at least two paragraphs and the end isn't just some empty lines.
|
||||
if (@EditParagraphs and $start and $start < length($quoted_text)) {
|
||||
push(@EditParagraphs, [$start, length($quoted_text), substr($quoted_text, $start)]);
|
||||
}
|
||||
}
|
||||
# warn join('', '', map { $_->[0] . "-" . $_->[1] .": " . $_->[2]; } @EditParagraphs);
|
||||
return EditParagraphOldPrintWikiToHTML(@_);
|
||||
}
|
||||
|
||||
# Whenever an important element is closed, we try to add a link.
|
||||
|
||||
*EditParagraphOldCloseHtmlEnvironments = *CloseHtmlEnvironments;
|
||||
*CloseHtmlEnvironments = *EditParagraphNewCloseHtmlEnvironments;
|
||||
|
||||
sub EditParagraphNewCloseHtmlEnvironments {
|
||||
EditParagraph();
|
||||
return EditParagraphOldCloseHtmlEnvironments(@_);
|
||||
}
|
||||
|
||||
*EditParagraphOldCloseHtmlEnvironmentUntil = *CloseHtmlEnvironmentUntil;
|
||||
*CloseHtmlEnvironmentUntil = *EditParagraphNewCloseHtmlEnvironmentUntil;
|
||||
|
||||
sub EditParagraphNewCloseHtmlEnvironmentUntil {
|
||||
my $tag = $_[0];
|
||||
if ($tag =~ /^(p|li|table|h[1-6])$/i) {
|
||||
EditParagraph();
|
||||
}
|
||||
return EditParagraphOldCloseHtmlEnvironmentUntil(@_);
|
||||
}
|
||||
|
||||
sub EditParagraph {
|
||||
my $text;
|
||||
my $pos = pos; # pos is empty for the last link
|
||||
if (@EditParagraphs) {
|
||||
if ($pos) {
|
||||
while (@EditParagraphs and $EditParagraphs[0]->[1] <= $pos) {
|
||||
$pos = $EditParagraphs[0]->[1]; # just in case we're overshooting
|
||||
$text .= $EditParagraphs[0]->[2];
|
||||
shift(@EditParagraphs);
|
||||
}
|
||||
} else {
|
||||
# the last one
|
||||
$text = $EditParagraphs[-1]->[2];
|
||||
}
|
||||
}
|
||||
if ($text) {
|
||||
|
||||
# Huge Hack Alert: We are appending to $Fragment, which is what Clean appends to. We do this so that we can handle
|
||||
# headers and other block elements. Without this fix we'd see something like this:
|
||||
# <h2>...</h2><p><a ...>✎</a></p>
|
||||
# Usually this would look as follows:
|
||||
# <h2>...</h2><p></p>
|
||||
# This is eliminated in Dirty. But it won't be eliminated if we leave the link in there. What we want is this:
|
||||
# <h2>...<a ...>✎</a></h2><p></p>
|
||||
#
|
||||
# The same issue arises for other block level elements. What happens at the end of a table? Without this fix we'd
|
||||
# see something like this:
|
||||
# <table><tr><td>...</td></tr></table><p><a ...>✎</a></p>
|
||||
# What we want, I guess, is this:
|
||||
# <table><tr><td>...<a ...>✎</a></td></tr></table></p>
|
||||
|
||||
$pos = $pos || length(QuoteHtml($Page{text})); # make sure we have an around value
|
||||
my $title = UrlEncode($OpenPageName);
|
||||
my $paragraph = UrlEncode(UnquoteHtml($text));
|
||||
my $link = ScriptLink("action=edit-paragraph;title=$title;around=$pos;paragraph=$paragraph",
|
||||
$EditParagraphPencil, 'pencil');
|
||||
|
||||
if ($Fragment =~ s!((:?</h[1-6]>|</t[dh]></tr></table>|</pre>)<p>)$!!) {
|
||||
# $Fragment .= '<!-- moved inside -->';
|
||||
$Fragment .= $link . $1;
|
||||
} elsif ($Fragment =~ s!(</p>\s*</form>)$!!) {
|
||||
# $Fragment .= '<!-- HTML fixes for <html> -->';
|
||||
# Since anything can appear in raw HTML tags, there is no one-size fits all rule.
|
||||
# I usually use the <html> tags to embed forms, and forms need to contain a <p>.
|
||||
# so that's what I'm handling.
|
||||
$Fragment .= $link . $1;
|
||||
} elsif ($pos and $Fragment =~ /<(p|tr)>$/) {
|
||||
# Do nothing: this is either an empty paragraph and will be
|
||||
# eliminated, or an empty row which will not be shown.
|
||||
# $Fragment .= '<!-- empty -->';
|
||||
} else {
|
||||
# This is the default: add the link.
|
||||
# $Fragment .= '<!-- default -->';
|
||||
$Fragment .= $link;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
# For user doc, see:
|
||||
# http://www.oddmuse.org/cgi-bin/oddmuse/Email_Quote_Extension
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/emailquote.pl">emailquote.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Email_Quote_Extension">Email Quote Extension</a></p>';
|
||||
AddModuleDescription('emailquote.pl', 'Email Quote Extension');
|
||||
|
||||
push(@MyRules, \&EmailQuoteRule);
|
||||
|
||||
|
||||
66
modules/emoji.pl
Normal file
66
modules/emoji.pl
Normal file
@@ -0,0 +1,66 @@
|
||||
# Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('emoji.pl', 'Smilies');
|
||||
|
||||
push(@MyRules, \&EmojiRule);
|
||||
# this must come before tex.pl because of \o/ turning into ø/
|
||||
$RuleOrder{\&EmojiRule} = 150;
|
||||
|
||||
# Some relevant links
|
||||
# https://en.wikipedia.org/wiki/List_of_emoticons
|
||||
|
||||
sub EmojiRule {
|
||||
if (m/\G:-?D/cg) {
|
||||
# 😀 1F600 GRINNING FACE
|
||||
return '😀';
|
||||
} elsif (/\G:[-o]?\)/cg) {
|
||||
# 😊 1F60A SMILING FACE WITH SMILING EYES
|
||||
return '😊';
|
||||
} elsif (/\G\s+:3/cg) {
|
||||
# 😸 1F638 GRINNING CAT FACE WITH SMILING EYES
|
||||
return ' 😸';
|
||||
} elsif (/\G:-?\(/cg) {
|
||||
# 😟 1F61F WORRIED FACE
|
||||
return '😟';
|
||||
} elsif (/\G;-?\)/cg) {
|
||||
# 😉 1F609 WINKING FACE
|
||||
return '😉';
|
||||
} elsif (/\G:'\(/cg) {
|
||||
# 😢 1F622 CRYING FACE
|
||||
return '😢';
|
||||
} elsif (/\G>:-?\(/cg) {
|
||||
# 😠 1F620 ANGRY FACE
|
||||
return '😠';
|
||||
} elsif (/\G:-?[Ppb]/cg) {
|
||||
# 😝 1F61D FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES
|
||||
return '😝';
|
||||
} elsif (/\G<3/cg) {
|
||||
# ❤ 2764 HEAVY BLACK HEART
|
||||
return '❤';
|
||||
} elsif (/\G\^_*\^/cg) {
|
||||
# 😄 1F604 SMILING FACE WITH OPEN MOUTH AND SMILING EYES
|
||||
return '😄';
|
||||
} elsif (/\G\b[Oo]_[Oo]\b/cg) {
|
||||
# 😲 1F632 ASTONISHED FACE
|
||||
return '😲';
|
||||
} elsif (/\G\\o\//cg) {
|
||||
# 🙌 1F64C PERSON RAISING BOTH HANDS IN CELEBRATION
|
||||
return '🙌';
|
||||
} elsif (/\G\\m\//cg) {
|
||||
# ✊ 270A RAISED FIST
|
||||
return '✊';
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/enclosure.pl">enclosure.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Podcasting">Podcasting</a></p>';
|
||||
AddModuleDescription('enclosure.pl', 'Podcasting');
|
||||
|
||||
use vars qw(@Enclosures);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
# Additionally, lines starting with Q: and A: are rendered using
|
||||
# the css classes div.question and div.answer.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/faq.pl">faq.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/FAQ_Extension">FAQ Extension</a></p>';
|
||||
AddModuleDescription('faq.pl', 'FAQ Extension');
|
||||
|
||||
$FaqHeaderText = "Questions on this page:" unless $FaqHeaderText;
|
||||
$FaqQuestionText = "Question: " unless $FaqQuestionText;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA,
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/fckeditor.pl">fckeditor.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Using_FCKeditor_In_Addition_To_Wiki_Markup">Using FCKeditor In Addition To Wiki Markup</a></p>';
|
||||
AddModuleDescription('fckeditor.pl', 'Using FCKeditor In Addition To Wiki Markup');
|
||||
|
||||
use vars qw($FCKeditorHeight);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
# For user doc, see:
|
||||
# http://www.oddmuse.org/cgi-bin/oddmuse/Field_List_Extension
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/fieldlist.pl">fieldlist.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Field_List_Extension">Field List Extension</a></p>';
|
||||
AddModuleDescription('fieldlist.pl', 'Field List Extension');
|
||||
|
||||
push(@MyRules, \&FieldListRules);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/find.pl">find.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Find_Extension">Find Extension</a></p>';
|
||||
AddModuleDescription('find.pl', 'Find Extension');
|
||||
|
||||
$Action{find} = \&DoFind;
|
||||
|
||||
|
||||
57
modules/fix-encoding.pl
Normal file
57
modules/fix-encoding.pl
Normal file
@@ -0,0 +1,57 @@
|
||||
# Copyright (C) 2012 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('fix-encoding.pl', 'Fix Encoding');
|
||||
|
||||
$Action{'fix-encoding'} = \&FixEncoding;
|
||||
|
||||
sub FixEncoding {
|
||||
my $id = shift;
|
||||
ValidIdOrDie($id);
|
||||
RequestLockOrError();
|
||||
OpenPage($id);
|
||||
my $text = $Page{text};
|
||||
utf8::decode($text);
|
||||
Save($id, $text, T('Fix character encoding'), 1) if $text ne $Page{text};
|
||||
ReleaseLock();
|
||||
ReBrowsePage($id);
|
||||
}
|
||||
|
||||
$Action{'fix-escaping'} = \&FixEscaping;
|
||||
|
||||
sub FixEscaping {
|
||||
my $id = shift;
|
||||
ValidIdOrDie($id);
|
||||
RequestLockOrError();
|
||||
OpenPage($id);
|
||||
my $text = UnquoteHtml($Page{text});
|
||||
Save($id, $text, T('Fix HTML escapes'), 1) if $text ne $Page{text};
|
||||
ReleaseLock();
|
||||
ReBrowsePage($id);
|
||||
}
|
||||
|
||||
push(@MyAdminCode, \&FixEncodingMenu);
|
||||
|
||||
sub FixEncodingMenu {
|
||||
my ($id, $menuref, $restref) = @_;
|
||||
if ($id && GetParam('username')) {
|
||||
push(@$menuref,
|
||||
ScriptLink('action=fix-encoding;id=' . UrlEncode($id),
|
||||
T('Fix character encoding')));
|
||||
push(@$menuref,
|
||||
ScriptLink('action=fix-escaping;id=' . UrlEncode($id),
|
||||
T('Fix HTML escapes')));
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ directory of your Oddmuse Wiki.
|
||||
=cut
|
||||
package OddMuse;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/flashbox.pl">flashbox.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Flashbox_Extension">Flashbox Extension</a></p>';
|
||||
AddModuleDescription('flashbox.pl', 'Flashbox Extension');
|
||||
|
||||
# ....................{ CONFIGURATION }....................
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/flickrgallery.pl">flickrgallery.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/FlickrGallery_Module">FlickrGallery Module</a></p>';
|
||||
AddModuleDescription('flickrgallery.pl', 'FlickrGallery Module');
|
||||
|
||||
# NOTE: This API key for Flickr is NOT to be used in any other products
|
||||
# INCLUDING derivative works. The rest of the code can be used as licensed
|
||||
|
||||
@@ -13,7 +13,7 @@ directory for your Oddmuse Wiki.
|
||||
=cut
|
||||
package OddMuse;
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/footnotes.pl">footnotes.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Footnotes_Extension">Footnotes Extension</a></p>';
|
||||
AddModuleDescription('footnotes.pl', 'Footnotes Extension');
|
||||
|
||||
# ....................{ CONFIGURATION }....................
|
||||
|
||||
|
||||
106
modules/form_timeout.pl
Normal file
106
modules/form_timeout.pl
Normal file
@@ -0,0 +1,106 @@
|
||||
# form_timeout.pl - a form timeout based anti-spam module for Oddmuse
|
||||
#
|
||||
# Copyright (C) 2014 Aki Goto <tyatsumi@gmail.com>
|
||||
#
|
||||
# Original code is in PHP from http://textcaptcha.com/really
|
||||
# by Rob Tuley <hello@rob.cx>. Used with permission.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('form_timeout.pl');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is an anti-spam module for Oddmuse using form timeout method.
|
||||
Edit permission is timed out in specified duration (default is 30 minutes)
|
||||
after viewing the edit form. When edit content is posted directly by a spam bot
|
||||
without viewing the edit form, edit will be denied.
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
$FormTimeoutSalt
|
||||
Mandatory. Token hash salt. Specify arbitrary string.
|
||||
Default = undef.
|
||||
|
||||
$FormTimeoutTimeout
|
||||
The form timeout in seconds.
|
||||
Default = 60 * 30 (30 minutes).
|
||||
|
||||
=cut
|
||||
|
||||
use vars qw($FormTimeoutSalt $FormTimeoutTimeout);
|
||||
use Digest::MD5 qw(md5_hex);
|
||||
|
||||
$FormTimeoutSalt = undef;
|
||||
$FormTimeoutTimeout = 60 * 30; # 30 minutes
|
||||
|
||||
push(@MyInitVariables, \&FormTimeoutInitVariables);
|
||||
|
||||
sub FormTimeoutInitVariables {
|
||||
if (!defined($FormTimeoutSalt)) {
|
||||
ReportError(T('Set $FormTimeoutSalt.'), '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
}
|
||||
|
||||
sub FormTimeoutGetHash {
|
||||
my ($when) = @_;
|
||||
return md5_hex($FormTimeoutSalt . $when);
|
||||
}
|
||||
|
||||
sub FormTimeoutGetToken {
|
||||
return $Now . '#' . FormTimeoutGetHash($Now);
|
||||
}
|
||||
|
||||
sub FormTimeoutGetTime {
|
||||
my ($token) = @_;
|
||||
my ($when, $hash) = split /#/, $token;
|
||||
my $valid_hash = FormTimeoutGetHash($when);
|
||||
if ($hash ne $valid_hash) {
|
||||
return '';
|
||||
}
|
||||
return $when;
|
||||
}
|
||||
|
||||
sub FormTimeoutCheck {
|
||||
my $token = GetParam('form_timeout_token', '');
|
||||
my $when = FormTimeoutGetTime($token);
|
||||
if ($when eq '' || $when < $Now - $FormTimeoutTimeout) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
*OldFormTimeoutGetFormStart = *GetFormStart;
|
||||
*GetFormStart = *NewFormTimeoutGetFormStart;
|
||||
|
||||
sub NewFormTimeoutGetFormStart {
|
||||
my ($ignore, $method, $class) = @_;
|
||||
my $form = OldFormTimeoutGetFormStart($ignore, $method, $class);
|
||||
my $token = FormTimeoutGetToken();
|
||||
$form .= $q->input({-type=>'hidden', -name=>'form_timeout_token',
|
||||
-value=>$token});
|
||||
return $form;
|
||||
}
|
||||
|
||||
*OldFormTimeoutDoEdit = *DoEdit;
|
||||
*DoEdit = *NewFormTimeoutDoEdit;
|
||||
|
||||
sub NewFormTimeoutDoEdit {
|
||||
my ($id, $newText, $preview) = @_;
|
||||
if (!FormTimeoutCheck()) {
|
||||
ReportError(T('Form Timeout'), '403 FORBIDDEN', undef,
|
||||
$q->p(Ts('Editing not allowed: %s is read-only.', NormalToFree($id))));
|
||||
}
|
||||
OldFormTimeoutDoEdit($id, $newText, $preview);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env perl
|
||||
# ====================[ forms.pl ]====================
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/forms.pl">forms.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Form_Extension">Form Extension</a></p>';
|
||||
AddModuleDescription('forms.pl', 'Form Extension');
|
||||
|
||||
# ....................{ MARKUP }....................
|
||||
push(@MyRules, \&FormsRule);
|
||||
|
||||
60
modules/fractions.pl
Normal file
60
modules/fractions.pl
Normal file
@@ -0,0 +1,60 @@
|
||||
# Copyright (C) 2013 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('fractions.pl', 'Fractions');
|
||||
|
||||
push(@MyRules, \&FractionsRule);
|
||||
|
||||
# usage: ^1/32
|
||||
sub FractionsRule {
|
||||
if (/\G\^([0-9]+)\/([0-9]+)/cg) {
|
||||
if ($1 == 1 and $2 == 4) { return "\¼"; }
|
||||
elsif ($1 == 1 and $2 == 2) { return "\½"; }
|
||||
elsif ($1 == 3 and $2 == 4) { return "\¾"; }
|
||||
elsif ($1 == 1 and $2 == 7) { return "\⅐"; }
|
||||
elsif ($1 == 1 and $2 == 9) { return "\⅑"; }
|
||||
elsif ($1 == 1 and $2 == 10) { return "\⅒"; }
|
||||
elsif ($1 == 1 and $2 == 3) { return "\⅓"; }
|
||||
elsif ($1 == 2 and $2 == 3) { return "\⅔"; }
|
||||
elsif ($1 == 1 and $2 == 5) { return "\⅕"; }
|
||||
elsif ($1 == 2 and $2 == 5) { return "\⅖"; }
|
||||
elsif ($1 == 3 and $2 == 5) { return "\⅗"; }
|
||||
elsif ($1 == 4 and $2 == 5) { return "\⅘"; }
|
||||
elsif ($1 == 1 and $2 == 6) { return "\⅙"; }
|
||||
elsif ($1 == 5 and $2 == 6) { return "\⅚"; }
|
||||
elsif ($1 == 1 and $2 == 8) { return "\⅛"; }
|
||||
elsif ($1 == 3 and $2 == 8) { return "\⅜"; }
|
||||
elsif ($1 == 5 and $2 == 8) { return "\⅝"; }
|
||||
elsif ($1 == 7 and $2 == 8) { return "\⅞"; }
|
||||
else {
|
||||
my $html;
|
||||
# superscripts
|
||||
for my $char (split(//, $1)) {
|
||||
if ($char eq '1') { $html .= "\¹"; }
|
||||
elsif ($char eq '2') { $html .= "\²"; }
|
||||
elsif ($char eq '3') { $html .= "\³"; }
|
||||
else { $html .= "\ȇ$char;"; }
|
||||
}
|
||||
# fraction slash
|
||||
$html .= '⁄';
|
||||
# subscripts
|
||||
for my $char (split(//, $2)) {
|
||||
$html .= "\Ȉ$char;";
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
398
modules/gd_security_image.pl
Normal file
398
modules/gd_security_image.pl
Normal file
@@ -0,0 +1,398 @@
|
||||
# GdSecurityImage - a CAPTCHA module for Oddmuse using GD::SecurityImage module
|
||||
#
|
||||
# Copyright (C) 2014 Aki Goto <tyatsumi@gmail.com>
|
||||
#
|
||||
# Codes reused from MwfCaptcha.pm in mwForum - Web-based discussion forum
|
||||
# Copyright (c) 1999-2014 Markus Wichitill
|
||||
#
|
||||
# Codes reused from questionasker.pl for Oddmuse
|
||||
# Copyright (C) 2004 Brock Wilcox <awwaiid@thelackthereof.org>
|
||||
# Copyright (C) 2006, 2007 Alex Schroeder <alex@gnu.org>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AddModuleDescription('gd_security_image.pl');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a CAPTCHA module for Oddmuse using GD::SecurityImage module.
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
$GdSecurityImageFont
|
||||
Mandatory.
|
||||
Set a TTF font file used for generating CAPTCHA images.
|
||||
Example: '/usr/share/fonts/truetype/ttf-bitstream-vera/VeraBd.ttf'.
|
||||
|
||||
$GdSecurityImageRememberAnswer
|
||||
If 1, once CAPTCHA is answered, the result is cached on cookies
|
||||
and you need not to re-answer CAPTCHAs for some duration specified
|
||||
by $GdSecurityImageDuration.
|
||||
If 0, CAPTCHA is requested everytime you try to submit forms.
|
||||
Default = 1.
|
||||
|
||||
$GdSecurityImageDuration
|
||||
The duration a CAPTCHA ticket is valid in seconds.
|
||||
Default = 60 * 10 (10 minutes).
|
||||
|
||||
$GdSecurityImageRequiredList
|
||||
The page name for exceptions, if defined. Every page linked to via
|
||||
WikiWord or [[free link]] is considered to be a page which needs
|
||||
questions asked. All other pages do not require questions asked. If
|
||||
not set, then all pages need questions asked.
|
||||
|
||||
%GdSecurityImageProtectedForms
|
||||
Forms using one of the specified classes are protected.
|
||||
Default: ('comment' => 1, 'edit upload' => 1, 'edit text' => 1,).
|
||||
|
||||
$GdSecurityImageDataDir
|
||||
When using with Namespaces Extension, specify original root data directory
|
||||
to concentrate GdSecurityImage data files in it.
|
||||
Default: $DataDir.
|
||||
|
||||
$GdSecurityImageWidth
|
||||
Default: 250.
|
||||
|
||||
$GdSecurityImageHeight
|
||||
Default: 60.
|
||||
|
||||
$GdSecurityImagePtsize
|
||||
Default: 16.
|
||||
|
||||
$GdSecurityImageScramble
|
||||
Default: 1.
|
||||
|
||||
$GdSecurityImageChars
|
||||
Default: [qw(A B C D E F G H I J K L M O P R S T U V W X Y)].
|
||||
|
||||
=head1 API
|
||||
|
||||
You can use this module in other modules by using following APIs.
|
||||
|
||||
GdSecurityImageGetHtml
|
||||
returns CAPTCHA HTML form element for embedding in HTML form clause.
|
||||
|
||||
GdSecurityImageCheck
|
||||
returns whether CAPTCHA is answered correctly or not.
|
||||
|
||||
=head1 DATA STRUCTURE
|
||||
|
||||
Image data and ticket data are stored in $DataDir/gd_security_image directory.
|
||||
Old data are deleted partially whenever CAPTCHA form is accessed.
|
||||
You can delete this directory totally harmlessly, although it forces users to
|
||||
re-answer CAPTCHA.
|
||||
|
||||
=cut
|
||||
|
||||
use vars qw($GdSecurityImageFont $GdSecurityImageRememberAnswer
|
||||
$GdSecurityImageDuration $GdSecurityImageRequiredList
|
||||
%GdSecurityImageProtectedForms $GdSecurityImageDataDir
|
||||
$GdSecurityImageWidth $GdSecurityImageHeight
|
||||
$GdSecurityImagePtsize $GdSecurityImageScramble $GdSecurityImageChars
|
||||
$GdSecurityImageAA);
|
||||
use vars qw($GdSecurityImageDir $GdSecurityImageId $GdSecurityImagePngToAA);
|
||||
|
||||
use Digest::MD5;
|
||||
use File::Glob ':glob';
|
||||
|
||||
$GdSecurityImageRequiredList = '';
|
||||
|
||||
$Action{gd_security_image} = \&GdSecurityImageDoImage;
|
||||
|
||||
push(@MyInitVariables, \&GdSecurityImageInitVariables);
|
||||
|
||||
sub GdSecurityImageGetImageFile {
|
||||
my ($id) = @_;
|
||||
return "$GdSecurityImageDir/$id.png";
|
||||
}
|
||||
|
||||
sub GdSecurityImageGetTicketFile {
|
||||
my ($id) = @_;
|
||||
return "$GdSecurityImageDir/$id.ticket";
|
||||
}
|
||||
|
||||
sub GdSecurityImageGenerate {
|
||||
# Load modules
|
||||
my $gd = eval { require GD };
|
||||
eval { require Image::Magick }
|
||||
or ReportError(T('GD or Image::Magick modules not available.'), '500 INTERNAL SERVER ERROR') if !$gd;
|
||||
eval { require GD::SecurityImage }
|
||||
or ReportError(T('GD::SecurityImage module not available.'));
|
||||
|
||||
# Generate captcha image
|
||||
GD::SecurityImage->import($gd ? () : (use_magick => 1));
|
||||
my $img = GD::SecurityImage->new(
|
||||
width => $GdSecurityImageWidth,
|
||||
height => $GdSecurityImageHeight,
|
||||
font => $GdSecurityImageFont,
|
||||
ptsize => $GdSecurityImagePtsize,
|
||||
scramble => $GdSecurityImageScramble,
|
||||
rnd_data => $GdSecurityImageChars,
|
||||
bgcolor => '#000000',
|
||||
);
|
||||
$img->random();
|
||||
my $newCaptchaStr = $img->random_str();
|
||||
$img->create('ttf', int(rand(2)) ? 'default' : 'ec', '#ffffff', '#ffffff');
|
||||
$img->particle(3000);
|
||||
|
||||
### experimental ###
|
||||
#my $raw = $img->raw;
|
||||
#my $w2 = $GdSecurityImageWidth * 2 / 3;
|
||||
#my $h2 = $GdSecurityImageHeight * 2 / 3;
|
||||
#my $raw2 = GD::Image->new($w2, $h2);
|
||||
#$raw2->copyResampled($raw, 0, 0, 0, 0, $w2, $h2, $raw->getBounds);
|
||||
#my $png = $raw2->png;
|
||||
|
||||
# Store captcha image
|
||||
my ($imgData) = $img->out(force => 'png');
|
||||
my $ticketId = Digest::MD5::md5_hex(rand());
|
||||
CreateDir($GdSecurityImageDir);
|
||||
my $file = GdSecurityImageGetImageFile($ticketId);
|
||||
open my $fh, ">:raw", $file
|
||||
or ReportError(Ts('Image storing failed. (%s)', $!), '500 INTERNAL SERVER ERROR');
|
||||
print $fh $imgData;
|
||||
#print $fh $png; ### experimental ###
|
||||
close $fh;
|
||||
|
||||
# Insert captcha ticket
|
||||
my %page = ();
|
||||
$page{id} = $ticketId;
|
||||
$page{generation_time} = $Now;
|
||||
$page{string} = $newCaptchaStr;
|
||||
CreateDir($GdSecurityImageDir);
|
||||
WriteStringToFile(GdSecurityImageGetTicketFile($ticketId), EncodePage(%page));
|
||||
|
||||
return $ticketId;
|
||||
}
|
||||
|
||||
sub GdSecurityImageIsValidId {
|
||||
my ($id) = @_;
|
||||
return $id =~ /^[0-9a-f]+$/;
|
||||
}
|
||||
|
||||
sub GdSecurityImageReadImageFile {
|
||||
my $file = shift;
|
||||
utf8::encode($file); # filenames are bytes!
|
||||
if (open(IN, '<:raw', $file)) {
|
||||
local $/ = undef; # Read complete files
|
||||
my $data=<IN>;
|
||||
close IN;
|
||||
return (1, $data);
|
||||
}
|
||||
return (0, '');
|
||||
}
|
||||
|
||||
sub GdSecurityImageDoImage {
|
||||
my $id = GetParam('gd_security_image_id', '');
|
||||
|
||||
if (!GdSecurityImageIsValidId($id)) {
|
||||
ReportError(T('Bad gd_security_image_id.'), '400 BAD REQUEST');
|
||||
}
|
||||
|
||||
my ($status, $data) = GdSecurityImageReadImageFile(GdSecurityImageGetImageFile($id));
|
||||
|
||||
binmode(STDOUT, ":raw");
|
||||
print $q->header(-type=>'image/png');
|
||||
print $data;
|
||||
|
||||
unlink(GdSecurityImageGetImageFile($id));
|
||||
}
|
||||
|
||||
sub GdSecurityImageCleanup {
|
||||
my ($id) = @_;
|
||||
if (!GdSecurityImageIsValidId($id)) {
|
||||
return;
|
||||
}
|
||||
my @files = (bsd_glob("$GdSecurityImageDir/*.png"), bsd_glob("$GdSecurityImageDir/*.ticket"));
|
||||
foreach my $file (@files) {
|
||||
if ($Now - (stat $file)[9] > $GdSecurityImageDuration) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub GdSecurityImageCheck {
|
||||
if (defined($GdSecurityImageId)) {
|
||||
return $GdSecurityImageId eq '';
|
||||
}
|
||||
|
||||
my $id = GetParam('gd_security_image_id', '');
|
||||
my $answer = GetParam('gd_security_image_answer', '');
|
||||
|
||||
GdSecurityImageCleanup($id);
|
||||
|
||||
if ($answer ne '' && GdSecurityImageIsValidId($id)) {
|
||||
my ($status, $data) = ReadFile(GdSecurityImageGetTicketFile($id));
|
||||
if ($status) {
|
||||
my %page = ParseData($data);
|
||||
if ($page{generation_time} + $GdSecurityImageDuration > $Now) {
|
||||
if ($answer eq $page{string}) {
|
||||
$GdSecurityImageId = '';
|
||||
if (!$GdSecurityImageRememberAnswer) {
|
||||
SetParam('gd_security_image_id', '');
|
||||
SetParam('gd_security_image_answer', '');
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GdSecurityImageIsValidId($id)) {
|
||||
unlink(GdSecurityImageGetTicketFile($id));
|
||||
}
|
||||
|
||||
$GdSecurityImageId = GdSecurityImageGenerate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub GdSecurityImageGetHtml {
|
||||
if (GdSecurityImageCheck()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
my $form = '';
|
||||
|
||||
SetParam('gd_security_image_answer', '');
|
||||
|
||||
$form .= $q->start_div({-class=>'gd_security_image'});
|
||||
|
||||
$form .= $q->start_div();
|
||||
$form .= T('Please type the six characters from the anti-spam image');
|
||||
$form .= $q->end_div();
|
||||
|
||||
$form .= $q->start_div();
|
||||
$form .= $q->input({-type=>'hidden', -name=>'gd_security_image_id', -value=>$GdSecurityImageId});
|
||||
$form .= $q->textfield(-name=>'gd_security_image_answer', -id=>'gd_security_image_answer');
|
||||
$form .= $q->submit(-name=>'Submit', -value=>T('Submit'));
|
||||
$form .= $q->end_div();
|
||||
|
||||
$form .= $q->start_div();
|
||||
$form .= $q->img({-src=>"$FullUrl?action=gd_security_image&gd_security_image_id=$GdSecurityImageId", -alt=>T('CAPTCHA'), -width=>$GdSecurityImageWidth, -height=>$GdSecurityImageHeight});
|
||||
$form .= $q->end_div();
|
||||
|
||||
if ($GdSecurityImageAA) {
|
||||
$form .= $q->start_div({class=>'aa_captcha'});
|
||||
$form .= $q->start_pre();
|
||||
my $png_file = GdSecurityImageGetImageFile($GdSecurityImageId);
|
||||
$form .= `$GdSecurityImagePngToAA $png_file`;
|
||||
$form .= $q->end_pre();
|
||||
$form .= $q->end_div();
|
||||
}
|
||||
|
||||
$form .= $q->end_div();
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
*OldGdSecurityImageDoPost = *DoPost;
|
||||
*DoPost = *NewGdSecurityImageDoPost;
|
||||
|
||||
sub NewGdSecurityImageDoPost {
|
||||
my(@params) = @_;
|
||||
my $id = FreeToNormal(GetParam('title', undef));
|
||||
my $preview = GetParam('Preview', undef); # case matters!
|
||||
unless (UserIsEditor()
|
||||
or $preview
|
||||
or GdSecurityImageCheck()
|
||||
or GdSecurityImageException($id)) {
|
||||
print GetHeader('', T('Edit Denied'), undef, undef, '403 FORBIDDEN');
|
||||
print $q->p(T('You did not answer correctly.'));
|
||||
print GetFormStart(), GdSecurityImageGetHtml(),
|
||||
(map { $q->input({-type=>'hidden', -name=>$_, -value=>UnquoteHtml(GetParam($_))}) }
|
||||
qw(title text oldtime summary recent_edit aftertext)), $q->end_form;
|
||||
PrintFooter();
|
||||
# logging to the error log file of the server
|
||||
# warn "Q: '$QuestionaskerQuestions[$question_num][0]', A: '$answer'\n";
|
||||
return;
|
||||
}
|
||||
return (OldGdSecurityImageDoPost(@params));
|
||||
}
|
||||
|
||||
*OldGdSecurityImageGetEditForm = *GetEditForm;
|
||||
*GetEditForm = *NewGdSecurityImageGetEditForm;
|
||||
|
||||
sub NewGdSecurityImageGetEditForm {
|
||||
return GdSecurityImageAddTo(OldGdSecurityImageGetEditForm(@_));
|
||||
}
|
||||
|
||||
*OldGdSecurityImageGetCommentForm = *GetCommentForm;
|
||||
*GetCommentForm = *NewGdSecurityImageGetCommentForm;
|
||||
|
||||
sub NewGdSecurityImageGetCommentForm {
|
||||
return GdSecurityImageAddTo(OldGdSecurityImageGetCommentForm(@_));
|
||||
}
|
||||
|
||||
sub GdSecurityImageAddTo {
|
||||
my $form = shift;
|
||||
if (not $upload
|
||||
and not GdSecurityImageException(GetId())
|
||||
and not UserIsEditor()) {
|
||||
my $question = GdSecurityImageGetHtml();
|
||||
$form =~ s/(.*)<p>(.*?)<label for="username">/$1$question<p>$2<label for="username">/;
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
sub GdSecurityImageException {
|
||||
my $id = shift;
|
||||
return 0 unless $GdSecurityImageRequiredList and $id;
|
||||
my $data = GetPageContent($GdSecurityImageRequiredList);
|
||||
if ($WikiLinks) {
|
||||
while ($data =~ /$LinkPattern/g) {
|
||||
return 0 if FreeToNormal($1) eq $id;
|
||||
}
|
||||
}
|
||||
if ($FreeLinks) {
|
||||
while ($data =~ /\[\[$FreeLinkPattern\]\]/g) {
|
||||
return 0 if FreeToNormal($1) eq $id;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub GdSecurityImageInitVariables {
|
||||
ReportError(T('$GdSecurityImageFont is not set.'), '500 INTERNAL SERVER ERROR') unless defined $GdSecurityImageFont;
|
||||
$GdSecurityImageRememberAnswer = 1 unless defined $GdSecurityImageRememberAnswer;
|
||||
$GdSecurityImageDuration = 60 * 10 unless defined $GdSecurityImageDuration;
|
||||
|
||||
$GdSecurityImageRequiredList = FreeToNormal($GdSecurityImageRequiredList);
|
||||
|
||||
# Forms using one of the following classes are protected.
|
||||
%GdSecurityImageProtectedForms = ('comment' => 1,
|
||||
'edit upload' => 1,
|
||||
'edit text' => 1,)
|
||||
unless defined %GdSecurityImageProtectedForms;
|
||||
|
||||
$GdSecurityImageDataDir = $DataDir unless defined $GdSecurityImageDataDir;
|
||||
|
||||
$GdSecurityImageWidth = 240 unless defined $GdSecurityImageWidth;
|
||||
$GdSecurityImageHeight = 75 unless defined $GdSecurityImageHeight;
|
||||
$GdSecurityImagePtsize = 16.75 unless defined $GdSecurityImagePtsize;
|
||||
$GdSecurityImageScramble = 1 unless defined $GdSecurityImageScramble;
|
||||
$GdSecurityImageChars = [qw(A B C D E F G H I J K L M O P R S T U V W X Y)] unless defined $GdSecurityImageChars;
|
||||
|
||||
$GdSecurityImageAA = 0 unless defined $GdSecurityImageAA;
|
||||
|
||||
$GdSecurityImageDir = "$GdSecurityImageDataDir/gd_security_image";
|
||||
|
||||
$GdSecurityImageId = undef;
|
||||
|
||||
$GdSecurityImagePngToAA = "$ModuleDir/pngtoaa";
|
||||
|
||||
$CookieParameters{'gd_security_image_id'} = '';
|
||||
$InvisibleCookieParameters{'gd_security_image_id'} = 1;
|
||||
$CookieParameters{'gd_security_image_answer'} = '';
|
||||
$InvisibleCookieParameters{'gd_security_image_answer'} = 1;
|
||||
}
|
||||
72
modules/git-another.pl
Normal file
72
modules/git-another.pl
Normal file
@@ -0,0 +1,72 @@
|
||||
# Copyright (C) 2014 Alex-Daniel Jakimenko <alex.jakimenko@gmail.com>
|
||||
# Copyright (C) 2011 Alex Schroeder <alex@gnu.org>
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation, either version 3 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package OddMuse;
|
||||
|
||||
AddModuleDescription('git-another.pl', 'Another Git Extension');
|
||||
|
||||
use Cwd;
|
||||
use Capture::Tiny ':all';
|
||||
use vars qw($GitBinary $GitMail);
|
||||
|
||||
$GitBinary = 'git';
|
||||
$GitMail = 'unknown@oddmuse.org';
|
||||
|
||||
sub GitCommit {
|
||||
my ($message, $author) = @_;
|
||||
my $oldDir = cwd;
|
||||
chdir("$DataDir/page");
|
||||
capture {
|
||||
system($GitBinary, qw(add -A));
|
||||
system($GitBinary, qw(commit -q -m), $message, "--author=$author <$GitMail>");
|
||||
};
|
||||
chdir($oldDir);
|
||||
}
|
||||
|
||||
sub GitInitRepository {
|
||||
return if -d "$DataDir/page/.git";
|
||||
capture {
|
||||
system($GitBinary, qw(init -q --), "$DataDir/page");
|
||||
};
|
||||
GitCommit('Initial import', 'Oddmuse');
|
||||
}
|
||||
|
||||
sub RenderHtmlCacheWithoutPrinting { # requires an open page
|
||||
$FootnoteNumber = 0;
|
||||
my $blocks, $flags;
|
||||
capture {
|
||||
($blocks, $flags) = ApplyRules(QuoteHtml($Page{text}), 1, 1, $Page{revision}, 'p');
|
||||
};
|
||||
if ($Page{blocks} ne $blocks and $Page{flags} ne $flags) {
|
||||
$Page{blocks} = $blocks;
|
||||
$Page{flags} = $flags;
|
||||
SavePage();
|
||||
}
|
||||
}
|
||||
|
||||
*GitOldSave = *Save;
|
||||
*Save = *GitNewSave;
|
||||
|
||||
sub GitNewSave {
|
||||
GitInitRepository();
|
||||
GitCommit('No description available', 'Oddmuse'); # commit any changes before this edit
|
||||
GitOldSave(@_);
|
||||
RenderHtmlCacheWithoutPrinting();
|
||||
my $message = $Page{summary};
|
||||
$message =~ s/^\s+$//;
|
||||
$message ||= T('No summary provided');
|
||||
my $author = $Page{username} || T('Anonymous');
|
||||
GitCommit($message, $author); # commit this edit
|
||||
}
|
||||
161
modules/git.pl
161
modules/git.pl
@@ -31,7 +31,7 @@ Set these variables in the B<config> file within your data directory.
|
||||
|
||||
=head2 $GitBinary
|
||||
|
||||
Default: C</usr/bin/git>
|
||||
Default: C<git>
|
||||
|
||||
The fully qualified name for the binary to run. Your PATH will not be searched.
|
||||
|
||||
@@ -48,31 +48,56 @@ Default: C<unknown@oddmuse.org>
|
||||
|
||||
The email address used to identify users in git.
|
||||
|
||||
=head2 $GitDebug
|
||||
|
||||
Default: 0
|
||||
|
||||
If set, we capture the output of the git command and store it in
|
||||
$GitResult. This is useful when writing tests.
|
||||
|
||||
=head2 $GitResult
|
||||
|
||||
If $GitDebug is set, this variable holds STDOUT of the git command.
|
||||
|
||||
=cut
|
||||
|
||||
use vars qw($GitBinary $GitRepo $GitMail);
|
||||
use Cwd;
|
||||
use File::Temp ();
|
||||
use vars qw($GitBinary $GitRepo $GitMail $GitPageFile $GitDebug $GitResult);
|
||||
|
||||
$GitBinary = '/usr/bin/git';
|
||||
AddModuleDescription('git.pl', 'Git Extension');
|
||||
|
||||
$GitBinary = 'git';
|
||||
$GitMail = 'unknown@oddmuse.org';
|
||||
$GitPageFile = 0;
|
||||
|
||||
push(@MyInitVariables, \&GitInitVariables);
|
||||
|
||||
sub GitRun {
|
||||
my $result = '';
|
||||
local *STDOUT;
|
||||
open(STDOUT, '>', \$result) or die "Can't open memory file: $!";
|
||||
system($GitBinary, @_) == 0
|
||||
or ReportError("git failed: $!",
|
||||
"500 INTERNAL SERVER ERROR",
|
||||
undef,
|
||||
$q->p($q->tt(join(' ', $GitBinary, map {
|
||||
if (index($_, ' ') == -1) {
|
||||
$_;
|
||||
} else {
|
||||
"'$_'";
|
||||
}
|
||||
} @_))),
|
||||
$q->pre($result));
|
||||
my $oldDir = cwd;
|
||||
my $exitStatus;
|
||||
# warn join(' ', $GitBinary, @_) . "\n";
|
||||
|
||||
chdir($GitRepo);
|
||||
if ($GitDebug) {
|
||||
# capture the output of the git comand in a temporary file
|
||||
my $fh = File::Temp->new();
|
||||
open(my $oldout, ">&STDOUT") or die "Can't dup STDOUT: $!";
|
||||
open(STDOUT, '>', $fh) or die "Can't redirect STDOUT: $!";
|
||||
# run git in the work directory
|
||||
$exitStatus = system($GitBinary, @_);
|
||||
# read the temporary file with the output
|
||||
close($fh);
|
||||
open(STDOUT, ">&", $oldout) or die "Can't dup \$oldout: $!";
|
||||
open(F, '<', $fh) or die "Can't open temp file for reading: $!";
|
||||
local $/ = undef; # Read complete files
|
||||
$GitResult = <F>;
|
||||
close(F);
|
||||
} else {
|
||||
$exitStatus = system($GitBinary, @_);
|
||||
}
|
||||
chdir($oldDir);
|
||||
return $exitStatus;
|
||||
}
|
||||
|
||||
sub GitInitVariables {
|
||||
@@ -80,39 +105,50 @@ sub GitInitVariables {
|
||||
}
|
||||
|
||||
sub GitInitRepository {
|
||||
if (not -d "$GitRepo/.git") {
|
||||
CreateDir($GitRepo);
|
||||
chdir($GitRepo); # important for all the git commands that follow!
|
||||
GitRun('init', '--quiet');
|
||||
foreach my $id (AllPagesList()) {
|
||||
OpenPage($id);
|
||||
WriteStringToFile("$GitRepo/$id", $Page{text});
|
||||
GitRun('add', $id);
|
||||
}
|
||||
GitRun('commit', '--quiet', '-m', 'initial import',
|
||||
"--author=Oddmuse <$GitMail>");
|
||||
} else {
|
||||
chdir($GitRepo); # important for all the git commands that follow!
|
||||
return if -d "$GitRepo/.git";
|
||||
my $exception = shift;
|
||||
CreateDir($GitRepo);
|
||||
GitRun(qw(init --quiet));
|
||||
# Add legacy pages: If you installed this extension for an older
|
||||
# wiki, all the existing pages need to be added. We do this for all
|
||||
# the pages except for the one we just saved. That page will get a
|
||||
# better author and log message.
|
||||
foreach my $id (AllPagesList()) {
|
||||
next if $id eq $exception;
|
||||
OpenPage($id);
|
||||
WriteStringToFile("$GitRepo/$id", $GitPageFile ? EncodePage(%Page) : $Page{text});
|
||||
GitRun(qw(add --), $id);
|
||||
}
|
||||
GitRun(qw(commit --quiet -m), 'initial import', "--author=Oddmuse <$GitMail>");
|
||||
}
|
||||
|
||||
*GitOldSave = *Save;
|
||||
*Save = *GitNewSave;
|
||||
|
||||
sub GitNewSave {
|
||||
|
||||
# Save is called within lock, with opened page. That's why we cannot
|
||||
# call GitInitRepository right away, because it opens all the legacy
|
||||
# pages to save them, too. We need to save first.
|
||||
GitOldSave(@_);
|
||||
GitInitRepository();
|
||||
my ($id) = @_;
|
||||
WriteStringToFile("$GitRepo/$id", $Page{text});
|
||||
if ($Page{revision} == 1) {
|
||||
GitRun('add', $id);
|
||||
}
|
||||
|
||||
# We also need to save all the data from the open page.
|
||||
my $message = $Page{summary};
|
||||
$message =~ s/^\s+$//;
|
||||
$message ||= T('no summary available');
|
||||
my $author = $Page{username} || T('Anonymous');
|
||||
GitRun('commit', '--quiet', '-m', $message,
|
||||
"--author=$author <$GitMail>", $id);
|
||||
my $data = $GitPageFile ? EncodePage(%Page) : $Page{text};
|
||||
my $id = shift;
|
||||
# GitInitRepository will try to add and commit all the pages already
|
||||
# in the wiki. These are assumed to be legacy pages. The page we
|
||||
# just saved, however, should not be committed as a legacy page
|
||||
# because legacy pages are committed with a default author and log
|
||||
# message!
|
||||
GitInitRepository($id);
|
||||
WriteStringToFile("$GitRepo/$id", $data);
|
||||
GitRun(qw(add --), $id);
|
||||
GitRun(qw(commit --quiet -m), $message,
|
||||
"--author=$author <$GitMail>", '--', $id);
|
||||
}
|
||||
|
||||
*GitOldDeletePage = *DeletePage;
|
||||
@@ -123,36 +159,59 @@ sub GitNewDeletePage {
|
||||
return $error if $error;
|
||||
GitInitRepository();
|
||||
my ($id) = @_;
|
||||
GitRun('rm', '--quiet', '--ignore-unmatch', $id);
|
||||
GitRun(qw(rm --quiet --ignore-unmatch --), $id);
|
||||
my $message = T('page was marked for deletion');
|
||||
my $author = T('Oddmuse');
|
||||
GitRun('commit', '--quiet', '-m', $message,
|
||||
"--author=$author <$GitMail>", $id);
|
||||
GitRun(qw(commit --quiet -m), $message,
|
||||
"--author=$author <$GitMail>", '--', $id);
|
||||
return ''; # no error
|
||||
}
|
||||
|
||||
push(@MyMaintenance, \&GitCleanup);
|
||||
|
||||
$Action{git} = \&DoGitCleanup;
|
||||
|
||||
sub DoGitCleanup {
|
||||
UserIsAdminOrError();
|
||||
print GetHeader('', 'Git', '');
|
||||
print $q->start_div({-class=>'content git'});
|
||||
RequestLockOrError();
|
||||
print $q->p(T('Main lock obtained.')), '<p>', T('Cleaning up git repository');
|
||||
GitCleanup();
|
||||
ReleaseLock();
|
||||
print $q->p(T('Main lock released.')), $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
|
||||
sub GitCleanup {
|
||||
if (-d $GitRepo) {
|
||||
print $q->p('Git cleanup starting');
|
||||
AllPagesList();
|
||||
# delete all the files including all the files starting with a dot
|
||||
opendir(DIR, $GitRepo) or ReportError("cannot open directory $GitRepo: $!");
|
||||
foreach my $file (readdir(DIR)) {
|
||||
next if $file eq '.git' or $file eq '.' or $file eq '..';
|
||||
unlink "$GitRepo/$file" or ReportError("cannot delete $GitRepo/$file: $!");
|
||||
my $name = $file;
|
||||
utf8::decode($name); # filenames are bytes
|
||||
next if $file eq '.git' or $file eq '.' or $file eq '..' or $IndexHash{$name};
|
||||
print $q->p("Deleting left over file $name");
|
||||
unlink "$GitRepo/$file" or ReportError("cannot delete $GitRepo/$name: $!");
|
||||
}
|
||||
closedir DIR;
|
||||
# write all the files again
|
||||
foreach my $id (AllPagesList()) {
|
||||
# write all the files again, just to be sure
|
||||
print $q->p('Rewriting all the files, just to be sure');
|
||||
foreach my $id (@IndexList) {
|
||||
OpenPage($id);
|
||||
WriteStringToFile("$GitRepo/$id", $Page{text});
|
||||
WriteStringToFile("$GitRepo/$id", $GitPageFile ? EncodePage(%Page) : $Page{text});
|
||||
}
|
||||
# run git!
|
||||
chdir($GitRepo); # important for all the git commands that follow!
|
||||
# add any new files
|
||||
GitRun('add', '.');
|
||||
print $q->p('Adding new files, if any');
|
||||
GitRun(qw(add -A));
|
||||
# commit the new state
|
||||
GitRun('commit', '--quiet', '-a', '-m', 'maintenance job',
|
||||
"--author=Oddmuse <$GitMail>");
|
||||
print $q->p('Committing changes, if any');
|
||||
my $exitStatus = GitRun(qw(commit --quiet -m), 'maintenance job',
|
||||
"--author=Oddmuse <$GitMail>");
|
||||
print $q->p('git commit finished with ' . $exitStatus . ' exit status.');
|
||||
print $q->p('Git done');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use vars qw($GoogleCustomSearchEngine);
|
||||
|
||||
$GoogleCustomSearchEngine = 'http://www.google.com/cse?cx=004774160799092323420:6-ff2s0o6yi&q=';
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/google-custom-search.pl">google-custom-search.pl</a></p>';
|
||||
AddModuleDescription('google-custom-search.pl');
|
||||
|
||||
# disable search form
|
||||
sub GetSearchForm {}
|
||||
@@ -50,7 +50,7 @@ sub NewGoogleCustomGetSearchLink {
|
||||
|
||||
sub NewGoogleCustomGetHeader {
|
||||
my $html = OldGoogleCustomGetHeader(@_);
|
||||
$form .= qq {
|
||||
my $form = qq {
|
||||
<!-- Google CSE Search Box Begins -->
|
||||
<form class="tiny" action="http://www.google.com/cse" id="searchbox_004774160799092323420:6-ff2s0o6yi"><p>
|
||||
<input type="hidden" name="cx" value="004774160799092323420:6-ff2s0o6yi" />
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# 59 Temple Place, Suite 330
|
||||
# Boston, MA 02111-1307 USA
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/google-search.pl">google-search.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Use_Google_For_Searches">Use Google For Searches</a></p>';
|
||||
AddModuleDescription('google-search.pl', 'Use Google For Searches');
|
||||
|
||||
use vars qw($GoogleSearchDomain $GoogleSearchExclusive);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user