forked from github/kensanata.oddmuse
Compare commits
70 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25989f78a5 | ||
|
|
afdb7a9dcb | ||
|
|
2e79a843c8 | ||
|
|
54370da235 | ||
|
|
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 | ||
|
|
5f58256543 | ||
|
|
c5c088deb1 | ||
|
|
a5b5af9c07 | ||
|
|
0dcf49e2cf | ||
|
|
f3885aa213 | ||
|
|
6136b399a6 | ||
|
|
5cc7d55152 | ||
|
|
4112d2acc4 | ||
|
|
f270a3ced4 | ||
|
|
7f74d3c211 | ||
|
|
375c844e37 | ||
|
|
efce35e250 | ||
|
|
cff4f1fd28 | ||
|
|
6f9ded7e41 | ||
|
|
40c01683fd | ||
|
|
08a4861dc3 | ||
|
|
d7c40d4dbe | ||
|
|
f8360bebad | ||
|
|
45a0558fcc | ||
|
|
f4ff56e69f | ||
|
|
0d7236c047 | ||
|
|
686f24251b | ||
|
|
0841c834b9 | ||
|
|
5225bded01 | ||
|
|
e0d18c31e2 | ||
|
|
670b69c118 | ||
|
|
f4d0f300e6 | ||
|
|
53a7a9a80c | ||
|
|
4f675de687 | ||
|
|
dffe5e3053 | ||
|
|
201970ba0b | ||
|
|
7e9137c6f8 | ||
|
|
9d81a1e3d2 | ||
|
|
2f58de9aa4 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
||||
/Mac/pkg/
|
||||
*.dmg
|
||||
*.pkg
|
||||
.DS_Store
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
;;; oddmuse-curl.el -- edit pages on an Oddmuse wiki using curl
|
||||
;;
|
||||
;; Copyright (C) 2006–2013 Alex Schroeder <alex@gnu.org>
|
||||
;; Copyright (C) 2006–2014 Alex Schroeder <alex@gnu.org>
|
||||
;; (C) 2007 rubikitch <rubikitch@ruby-lang.org>
|
||||
;;
|
||||
;; Latest version:
|
||||
;; http://git.savannah.gnu.org/cgit/oddmuse.git/plain/contrib/oddmuse-curl.el
|
||||
;; Discussion, feedback:
|
||||
;; http://www.emacswiki.org/cgi-bin/wiki/OddmuseMode
|
||||
;; http://www.emacswiki.org/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
|
||||
@@ -42,25 +42,21 @@
|
||||
(require 'sgml-mode)
|
||||
(require 'skeleton))
|
||||
|
||||
(require 'goto-addr)
|
||||
(require 'info)
|
||||
(require 'goto-addr); URL regexp
|
||||
(require 'info); link face
|
||||
(require 'shr); preview
|
||||
(require 'xml); preview munging
|
||||
|
||||
(defcustom oddmuse-directory "~/emacs/oddmuse"
|
||||
(defcustom oddmuse-directory "~/.emacs.d/oddmuse"
|
||||
"Directory to store oddmuse pages."
|
||||
:type '(string)
|
||||
:group 'oddmuse)
|
||||
|
||||
(defcustom oddmuse-wikis
|
||||
'(("TestWiki" "http://www.emacswiki.org/cgi-bin/test"
|
||||
utf-8 "question" nil)
|
||||
("EmacsWiki" "http://www.emacswiki.org/cgi-bin/emacs"
|
||||
'(("EmacsWiki" "http://www.emacswiki.org/cgi-bin/emacs"
|
||||
utf-8 "uihnscuskc" nil)
|
||||
("CommunityWiki" "http://www.communitywiki.org/cw"
|
||||
utf-8 "question" nil)
|
||||
("OddmuseWiki" "http://www.oddmuse.org/cgi-bin/oddmuse"
|
||||
utf-8 "question" nil)
|
||||
("CampaignWiki" "http://www.campaignwiki.org/wiki/NameOfYourWiki"
|
||||
utf-8 "ts" nil))
|
||||
utf-8 "question" nil))
|
||||
"Alist mapping wiki names to URLs.
|
||||
|
||||
The elements in this list are:
|
||||
@@ -160,17 +156,18 @@ It must print the RSS 3.0 text format to stdout.
|
||||
|
||||
(defvar oddmuse-post-command
|
||||
(concat "curl --silent --write-out '%{http_code}'"
|
||||
" --form title=%t"
|
||||
" --form summary=%s"
|
||||
" --form username=%u"
|
||||
" --form password=%p"
|
||||
" --form title='%t'"
|
||||
" --form summary='%s'"
|
||||
" --form username='%u'"
|
||||
" --form password='%p'"
|
||||
" --form %q=1"
|
||||
" --form recent_edit=%m"
|
||||
" --form oldtime=%o"
|
||||
" --form text=\"<-\""
|
||||
" %w")
|
||||
" --form text='<-'"
|
||||
" '%w'")
|
||||
"Command to use for publishing pages.
|
||||
It must accept the page on stdin.
|
||||
It must accept the page on stdin and print the HTTP status code
|
||||
on stdout.
|
||||
|
||||
%? '?' character
|
||||
%t pagename
|
||||
@@ -182,6 +179,29 @@ It must accept the page on stdin.
|
||||
%o oldtime, a timestamp provided by Oddmuse
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'")
|
||||
|
||||
(defvar oddmuse-preview-command
|
||||
(concat "curl --silent"
|
||||
" --form title='%t'"
|
||||
" --form username='%u'"
|
||||
" --form password='%p'"
|
||||
" --form %q=1"
|
||||
" --form recent_edit=%m"
|
||||
" --form oldtime=%o"
|
||||
" --form Preview=Preview"; the only difference
|
||||
" --form text='<-'"
|
||||
" '%w'")
|
||||
"Command to use for previewing pages.
|
||||
It must accept the page on stdin and print the HTML on stdout.
|
||||
|
||||
%? '?' character
|
||||
%t pagename
|
||||
%u username
|
||||
%p password
|
||||
%q question-asker cookie
|
||||
%m minor edit
|
||||
%o oldtime, a timestamp provided by Oddmuse
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'")
|
||||
|
||||
(defvar oddmuse-link-pattern
|
||||
"\\<[[:upper:]]+[[:lower:]]+\\([[:upper:]]+[[:lower:]]*\\)+\\>"
|
||||
"The pattern used for finding WikiName.")
|
||||
@@ -218,61 +238,102 @@ This is used by Oddmuse to merge changes.")
|
||||
|
||||
(defun oddmuse-creole-markup ()
|
||||
"Implement markup rules for the Creole markup extension."
|
||||
(font-lock-add-keywords
|
||||
nil
|
||||
'(("^=[^=\n]+" 0 '(face info-title-1 help-echo "Creole H1")); = h1
|
||||
("^==[^=\n]+" 0 '(face info-title-2 help-echo "Creole H2")); == h2
|
||||
("^===[^=\n]+" 0 '(face info-title-3 help-echo "Creole H3")); === h3
|
||||
("^====+[^=\n]+" 0 '(face info-title-4 help-echo "Creole H4")); ====h4
|
||||
("\\_<//\\(.*\n\\)*?.*?//" 0 '(face italic help-echo "Creole italic")); //italic//
|
||||
("\\*\\*\\(.*\n\\)*?.*?\\*\\*" 0 '(face bold help-echo "Creole bold")); **bold**
|
||||
("__\\(.*\n\\)*?.*?__" 0 '(face underline help-echo "Creole underline")); __underline__
|
||||
("|+=?" 0 '(face font-lock-string-face help-echo "Creole table cell"))
|
||||
("\\\\\\\\[ \t]+" 0 '(face font-lock-warning-face help-echo "Creole line break"))
|
||||
("^#+ " 0 '(face font-lock-constant-face help-echo "Creole ordered list"))
|
||||
("^- " 0 '(face font-lock-constant-face help-echo "Creole ordered list")))))
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("^=[^=\n]+"
|
||||
0 '(face info-title-1
|
||||
help-echo "Creole H1")); = h1
|
||||
("^==[^=\n]+"
|
||||
0 '(face info-title-2
|
||||
help-echo "Creole H2")); == h2
|
||||
("^===[^=\n]+"
|
||||
0 '(face info-title-3
|
||||
help-echo "Creole H3")); === h3
|
||||
("^====+[^=\n]+"
|
||||
0 '(face info-title-4
|
||||
help-echo "Creole H4")); ====h4
|
||||
("\\_<//\\(.*\n\\)*?.*?//"
|
||||
0 '(face italic
|
||||
help-echo "Creole italic")); //italic//
|
||||
("\\*\\*\\(.*\n\\)*?.*?\\*\\*"
|
||||
0 '(face bold
|
||||
help-echo "Creole bold")); **bold**
|
||||
("__\\(.*\n\\)*?.*?__"
|
||||
0 '(face underline
|
||||
help-echo "Creole underline")); __underline__
|
||||
("|+=?"
|
||||
0 '(face font-lock-string-face
|
||||
help-echo "Creole table cell"))
|
||||
("\\\\\\\\[ \t]+"
|
||||
0 '(face font-lock-warning-face
|
||||
help-echo "Creole line break"))
|
||||
("^#+ "
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Creole ordered list"))
|
||||
("^- "
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Creole ordered list")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-bbcode-markup ()
|
||||
"Implement markup rules for the bbcode markup extension."
|
||||
(font-lock-add-keywords
|
||||
nil
|
||||
`(("\\[b\\]\\(.*\n\\)*?.*?\\[/b\\]"
|
||||
0 '(face bold help-echo "BB code bold"))
|
||||
("\\[i\\]\\(.*\n\\)*?.*?\\[/i\\]"
|
||||
0 '(face italic help-echo "BB code italic"))
|
||||
("\\[u\\]\\(.*\n\\)*?.*?\\[/u\\]"
|
||||
0 '(face underline help-echo "BB code underline"))
|
||||
(,(concat "\\[url=" goto-address-url-regexp "\\]")
|
||||
0 '(face font-lock-builtin-face help-echo "BB code url"))
|
||||
("\\[/?\\(img\\|url\\)\\]"
|
||||
0 '(face font-lock-builtin-face help-echo "BB code url or img"))
|
||||
("\\[s\\(trike\\)?\\]\\(.*\n\\)*?.*?\\[/s\\(trike\\)?\\]"
|
||||
0 '(face strike help-echo "BB code strike"))
|
||||
("\\[/?\\(left\\|right\\|center\\)\\]"
|
||||
0 '(face font-lock-constant-face help-echo "BB code alignment")))))
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
`(("\\[b\\]\\(.*\n\\)*?.*?\\[/b\\]"
|
||||
0 '(face bold
|
||||
help-echo "BB code bold"))
|
||||
("\\[i\\]\\(.*\n\\)*?.*?\\[/i\\]"
|
||||
0 '(face italic
|
||||
help-echo "BB code italic"))
|
||||
("\\[u\\]\\(.*\n\\)*?.*?\\[/u\\]"
|
||||
0 '(face underline
|
||||
help-echo "BB code underline"))
|
||||
(,(concat "\\[url=" goto-address-url-regexp "\\]")
|
||||
0 '(face font-lock-builtin-face
|
||||
help-echo "BB code url"))
|
||||
("\\[/?\\(img\\|url\\)\\]"
|
||||
0 '(face font-lock-builtin-face
|
||||
help-echo "BB code url or img"))
|
||||
("\\[s\\(trike\\)?\\]\\(.*\n\\)*?.*?\\[/s\\(trike\\)?\\]"
|
||||
0 '(face strike
|
||||
help-echo "BB code strike"))
|
||||
("\\[/?\\(left\\|right\\|center\\)\\]"
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "BB code alignment")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-usemod-markup ()
|
||||
"Implement markup rules for the Usemod markup extension."
|
||||
(font-lock-add-keywords
|
||||
nil
|
||||
'(("^=[^=\n]+=$"
|
||||
0 '(face info-title-1 help-echo "Usemod H1"))
|
||||
("^==[^=\n]+==$"
|
||||
0 '(face info-title-2 help-echo "Usemod H2"))
|
||||
("^===[^=\n]+===$"
|
||||
0 '(face info-title-3 help-echo "Usemod H3"))
|
||||
("^====+[^=\n]+====$"
|
||||
0 '(face info-title-4 help-echo "Usemod H4"))
|
||||
("^ .+?$"
|
||||
0 '(face font-lock-comment-face help-echo "Usemod block"))
|
||||
("^[#]+ "
|
||||
0 '(face font-lock-constant-face help-echo "Usemod ordered list")))))
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("^=[^=\n]+=$"
|
||||
0 '(face info-title-1
|
||||
help-echo "Usemod H1"))
|
||||
("^==[^=\n]+==$"
|
||||
0 '(face info-title-2
|
||||
help-echo "Usemod H2"))
|
||||
("^===[^=\n]+===$"
|
||||
0 '(face info-title-3
|
||||
help-echo "Usemod H3"))
|
||||
("^====+[^=\n]+====$"
|
||||
0 '(face info-title-4
|
||||
help-echo "Usemod H4"))
|
||||
("^ .+?$"
|
||||
0 '(face font-lock-comment-face
|
||||
help-echo "Usemod block"))
|
||||
("^[#]+ "
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Usemod ordered list")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-usemod-html-markup ()
|
||||
"Implement markup rules for the HTML option in the Usemod markup extension."
|
||||
(font-lock-add-keywords
|
||||
nil
|
||||
'(("<\\(/?[a-z]+\\)" 1 '(face font-lock-function-name-face help-echo "Usemod HTML"))))
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("<\\(/?[a-z]+\\)"
|
||||
1 '(face font-lock-function-name-face
|
||||
help-echo "Usemod HTML")))
|
||||
(car font-lock-defaults)))
|
||||
(set (make-local-variable 'sgml-tag-alist)
|
||||
`(("b") ("code") ("em") ("i") ("strong") ("nowiki")
|
||||
("pre" \n) ("tt") ("u")))
|
||||
@@ -280,42 +341,70 @@ This is used by Oddmuse to merge changes.")
|
||||
|
||||
(defun oddmuse-extended-markup ()
|
||||
"Implement markup rules for the Markup extension."
|
||||
(font-lock-add-keywords
|
||||
nil
|
||||
'(("\\*\\w+[[:word:]-%.,:;\'\"!? ]*\\*"
|
||||
0 '(face bold help-echo "Markup bold"))
|
||||
("\\_</\\w+[[:word:]-%.,:;\'\"!? ]*/"
|
||||
0 '(face italic help-echo "Markup italic"))
|
||||
("_\\w+[[:word:]-%.,:;\'\"!? ]*_"
|
||||
0 '(face underline help-echo "Markup underline")))))
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
'(("\\*\\w+[[:word:]-%.,:;\'\"!? ]*\\*"
|
||||
0 '(face bold
|
||||
help-echo "Markup bold"))
|
||||
("\\_</\\w+[[:word:]-%.,:;\'\"!? ]*/"
|
||||
0 '(face italic
|
||||
help-echo "Markup italic"))
|
||||
("_\\w+[[:word:]-%.,:;\'\"!? ]*_"
|
||||
0 '(face underline
|
||||
help-echo "Markup underline")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
(defun oddmuse-basic-markup ()
|
||||
"Implement markup rules for the basic Oddmuse setup without extensions.
|
||||
This function should come come last in `oddmuse-markup-functions'
|
||||
because of such basic patterns as [.*] which are very generic."
|
||||
(font-lock-add-keywords
|
||||
nil
|
||||
`((,oddmuse-link-pattern
|
||||
0 '(face link help-echo "Basic wiki name"))
|
||||
("\\[\\[.*?\\]\\]"
|
||||
0 '(face link help-echo "Basic free link"))
|
||||
(,(concat "\\[" goto-address-url-regexp "\\( .+?\\)?\\]")
|
||||
0 '(face link help-echo "Basic external free link"))
|
||||
("^\\([*]+\\)"
|
||||
0 '(face font-lock-constant-face help-echo "Basic bullet list"))))
|
||||
(goto-address))
|
||||
(setcar font-lock-defaults
|
||||
(append
|
||||
`((,oddmuse-link-pattern
|
||||
0 '(face link
|
||||
help-echo "Basic wiki name"))
|
||||
("\\[\\[.*?\\]\\]"
|
||||
0 '(face link
|
||||
help-echo "Basic free link"))
|
||||
(,(concat "\\[" goto-address-url-regexp "\\( .+?\\)?\\]")
|
||||
0 '(face link
|
||||
help-echo "Basic external free link"))
|
||||
("^\\([*]+\\)"
|
||||
0 '(face font-lock-constant-face
|
||||
help-echo "Basic bullet list")))
|
||||
(car font-lock-defaults))))
|
||||
|
||||
;; Should determine this automatically based on the version? And cache it per wiki?
|
||||
;; http://emacswiki.org/wiki?action=version
|
||||
;; Should determine this automatically based on the version? And cache
|
||||
;; it per wiki? http://emacswiki.org/wiki?action=version
|
||||
(defvar oddmuse-markup-functions
|
||||
'(oddmuse-basic-markup
|
||||
oddmuse-extended-markup
|
||||
'(oddmuse-creole-markup
|
||||
oddmuse-usemod-markup
|
||||
oddmuse-creole-markup
|
||||
oddmuse-bbcode-markup)
|
||||
oddmuse-bbcode-markup
|
||||
oddmuse-extended-markup
|
||||
oddmuse-basic-markup
|
||||
goto-address)
|
||||
"The list of functions to call when `oddmuse-mode' runs.
|
||||
Later functions take precedence because they call `font-lock-add-keywords'
|
||||
which adds the expressions to the front of the existing list.")
|
||||
If these functions add font-locking, they should modify
|
||||
`font-lock-defaults'. See `font-lock-keywords' for documentation.
|
||||
If these functions all prepend their keywords, you should list
|
||||
the most important function last.
|
||||
|
||||
Here's a template for your code:
|
||||
|
||||
\(setcar font-lock-defaults
|
||||
(append
|
||||
'((REGEXP
|
||||
0 '(face FACE
|
||||
help-echo DOCSTRING)))
|
||||
(car font-lock-defaults)))")
|
||||
|
||||
(defun oddmuse-nobreak-p ()
|
||||
"Prevent line break of links.
|
||||
This depends on the `link' face."
|
||||
(let ((face (get-text-property (point) 'face)))
|
||||
(if (listp face)
|
||||
(memq 'link face)
|
||||
(eq 'link face))))
|
||||
|
||||
(define-derived-mode oddmuse-mode text-mode "Odd"
|
||||
"Simple mode to edit wiki pages.
|
||||
@@ -335,6 +424,7 @@ Customize `oddmuse-wikis' to add more wikis to the list.
|
||||
Font-locking is controlled by `oddmuse-markup-functions'.
|
||||
|
||||
\\{oddmuse-mode-map}"
|
||||
(setq font-lock-defaults '(nil))
|
||||
(mapc 'funcall oddmuse-markup-functions)
|
||||
(font-lock-mode 1)
|
||||
(when buffer-file-name
|
||||
@@ -353,14 +443,18 @@ Font-locking is controlled by `oddmuse-markup-functions'.
|
||||
(prog1 (match-string 1)
|
||||
(replace-match "")
|
||||
(set-buffer-modified-p nil)))))
|
||||
(set (make-local-variable 'fill-nobreak-predicate)
|
||||
'(oddmuse-nobreak-p))
|
||||
(setq indent-tabs-mode nil))
|
||||
|
||||
(autoload 'sgml-tag "sgml-mode" t)
|
||||
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-t") 'sgml-tag)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-o") 'oddmuse-follow)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-n") 'oddmuse-new)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-m") 'oddmuse-toggle-minor)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-c") 'oddmuse-post)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-p") 'oddmuse-preview)
|
||||
(define-key oddmuse-mode-map (kbd "C-x C-v") 'oddmuse-revert)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-f") 'oddmuse-edit)
|
||||
(define-key oddmuse-mode-map (kbd "C-c C-i") 'oddmuse-insert-pagename)
|
||||
@@ -397,11 +491,11 @@ Font-locking is controlled by `oddmuse-markup-functions'.
|
||||
("%p" . oddmuse-password)
|
||||
("%q" . question)
|
||||
("%o" . oddmuse-revision)
|
||||
("%r" . regexp)
|
||||
("%\\?" . hatena)))
|
||||
(when (and (boundp (cdr pair)) (stringp (symbol-value (cdr pair))))
|
||||
(setq command (replace-regexp-in-string (car pair)
|
||||
(shell-quote-argument
|
||||
(symbol-value (cdr pair)))
|
||||
(symbol-value (cdr pair))
|
||||
command t t))))
|
||||
command))
|
||||
|
||||
@@ -450,23 +544,37 @@ Use a prefix argument to force a reload of the page."
|
||||
(pop-to-buffer (get-buffer name))
|
||||
(let* ((wiki-data (assoc wiki oddmuse-wikis))
|
||||
(url (nth 1 wiki-data))
|
||||
(oddmuse-page-name pagename)
|
||||
(oddmuse-page-name pagename)
|
||||
(command (oddmuse-format-command oddmuse-get-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(buf (find-file-noselect (concat oddmuse-directory "/" wiki "/"
|
||||
pagename)))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding))
|
||||
;; don't use let for dynamically bound variable
|
||||
(set-buffer buf)
|
||||
(unless (equal name (buffer-name)) (rename-buffer name))
|
||||
(erase-buffer)
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run "Loading" command buf nil))
|
||||
(oddmuse-run "Loading" command buf))
|
||||
(pop-to-buffer buf)
|
||||
(oddmuse-mode)))))
|
||||
|
||||
(defalias 'oddmuse-go 'oddmuse-edit)
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-new (wiki pagename)
|
||||
"Create a new page on a wiki.
|
||||
WIKI is the name of the wiki as defined in `oddmuse-wikis'.
|
||||
The pagename begins with the current date."
|
||||
(interactive
|
||||
(list (completing-read "Wiki: " oddmuse-wikis nil t oddmuse-wiki)
|
||||
(replace-regexp-in-string
|
||||
" +" "_"
|
||||
(read-from-minibuffer "Pagename: "
|
||||
(format-time-string "%Y-%m-%d ")))))
|
||||
(oddmuse-edit wiki pagename))
|
||||
|
||||
(autoload 'word-at-point "thingatpt")
|
||||
|
||||
;;;###autoload
|
||||
@@ -474,8 +582,9 @@ Use a prefix argument to force a reload of the page."
|
||||
"Figure out what page we need to visit
|
||||
and call `oddmuse-edit' on it."
|
||||
(interactive "P")
|
||||
(let ((pagename (if arg (oddmuse-read-pagename oddmuse-wiki)
|
||||
(oddmuse-pagename-at-point))))
|
||||
(let ((pagename (or (and arg (oddmuse-read-pagename oddmuse-wiki))
|
||||
(oddmuse-pagename-at-point)
|
||||
(oddmuse-read-pagename oddmuse-wiki))))
|
||||
(oddmuse-edit (or oddmuse-wiki
|
||||
(read-from-minibuffer "URL: "))
|
||||
pagename)))
|
||||
@@ -494,46 +603,57 @@ and call `oddmuse-edit' on it."
|
||||
(defun oddmuse-pagename-at-point ()
|
||||
"Page name at point."
|
||||
(let ((pagename (word-at-point)))
|
||||
(cond ((oddmuse-current-free-link-contents))
|
||||
((oddmuse-wikiname-p pagename)
|
||||
pagename)
|
||||
(t
|
||||
(error "No link found at point")))))
|
||||
(or (oddmuse-current-free-link-contents)
|
||||
(oddmuse-wikiname-p pagename))))
|
||||
|
||||
(defun oddmuse-wikiname-p (pagename)
|
||||
"Whether PAGENAME is WikiName or not."
|
||||
(let (case-fold-search)
|
||||
(string-match (concat "^" oddmuse-link-pattern "$") pagename)))
|
||||
(when pagename
|
||||
(let (case-fold-search)
|
||||
(when (string-match (concat "^" oddmuse-link-pattern "$") pagename)
|
||||
pagename))))
|
||||
|
||||
;; (oddmuse-wikiname-p nil)
|
||||
;; (oddmuse-wikiname-p "WikiName")
|
||||
;; (oddmuse-wikiname-p "not-wikiname")
|
||||
;; (oddmuse-wikiname-p "notWikiName")
|
||||
|
||||
(defun oddmuse-run (mesg command buf on-region)
|
||||
(defun oddmuse-run (mesg command buf &optional on-region expected-code)
|
||||
"Print MESG and run COMMAND on the current buffer.
|
||||
MESG should be appropriate for the following uses:
|
||||
\"MESG...\"
|
||||
\"MESG...done\"
|
||||
\"MESG failed: REASON\"
|
||||
Save outpout in BUF and report an appropriate error.
|
||||
|
||||
ON-REGION indicates whether the commands runs on the region
|
||||
such as when posting, or whether it just runs by itself such
|
||||
as when loading a page."
|
||||
(message "%s..." mesg)
|
||||
as when loading a page.
|
||||
|
||||
If ON-REGION is not nil, the command output is compared to
|
||||
EXPECTED-CODE. The command is supposed to print the HTTP status
|
||||
code on stdout, so usually we want to provide either 302 or 200
|
||||
as EXPECTED-CODE."
|
||||
(message "%s using %s..." mesg command)
|
||||
(when (numberp expected-code)
|
||||
(setq expected-code (number-to-string expected-code)))
|
||||
;; If ON-REGION, the resulting HTTP CODE is found in BUF, so check
|
||||
;; that, too.
|
||||
(if (and (= 0 (if on-region
|
||||
(shell-command-on-region (point-min) (point-max) command buf)
|
||||
(shell-command-on-region (point-min) (point-max)
|
||||
command buf)
|
||||
(shell-command command buf)))
|
||||
(or (not on-region)
|
||||
(string= "302" (with-current-buffer buf
|
||||
(buffer-string)))))
|
||||
(not expected-code)
|
||||
(string= expected-code
|
||||
(with-current-buffer buf
|
||||
(buffer-string)))))
|
||||
(message "%s...done" mesg)
|
||||
(let ((err "Unknown error"))
|
||||
(with-current-buffer buf
|
||||
(when (re-search-forward "<h1>\\(.*?\\)\\.?</h1>" nil t)
|
||||
(setq err (match-string 1))))
|
||||
(error "%s...%s" mesg err))))
|
||||
(error "Error %s: %s" mesg err))))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-post (summary)
|
||||
@@ -560,7 +680,60 @@ The current wiki is taken from `oddmuse-wiki'."
|
||||
(buf (get-buffer-create " *oddmuse-response*"))
|
||||
(text (buffer-string)))
|
||||
(and buffer-file-name (basic-save-buffer))
|
||||
(oddmuse-run "Posting" command buf t)))
|
||||
(oddmuse-run "Posting" command buf t 302)))
|
||||
|
||||
;;;###autoload
|
||||
(defun oddmuse-preview ()
|
||||
"Preview the current buffer for the current wiki.
|
||||
The current wiki is taken from `oddmuse-wiki'."
|
||||
(interactive)
|
||||
;; when using prefix or on a buffer that is not in oddmuse-mode
|
||||
(when (or (not oddmuse-wiki) current-prefix-arg)
|
||||
(set (make-local-variable 'oddmuse-wiki)
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(when (not oddmuse-page-name)
|
||||
(set (make-local-variable 'oddmuse-page-name)
|
||||
(read-from-minibuffer "Pagename: " (buffer-name))))
|
||||
(let* ((list (assoc oddmuse-wiki oddmuse-wikis))
|
||||
(url (nth 1 list))
|
||||
(oddmuse-minor (if oddmuse-minor "on" "off"))
|
||||
(coding (nth 2 list))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding)
|
||||
(question (nth 3 list))
|
||||
(oddmuse-username (or (nth 4 list)
|
||||
oddmuse-username))
|
||||
(command (oddmuse-format-command oddmuse-preview-command))
|
||||
(buf (get-buffer-create " *oddmuse-response*"))
|
||||
(text (buffer-string)))
|
||||
(and buffer-file-name (basic-save-buffer))
|
||||
(oddmuse-run "Previewing" command buf t); no status code on stdout
|
||||
(message "Rendering...")
|
||||
(pop-to-buffer "*Preview*")
|
||||
(erase-buffer)
|
||||
(shr-insert-document
|
||||
(with-current-buffer (get-buffer " *oddmuse-response*")
|
||||
(let ((html (libxml-parse-html-region (point-min) (point-max))))
|
||||
(oddmuse-find-node
|
||||
(lambda (node)
|
||||
(and (eq (xml-node-name node) 'div)
|
||||
(string= (xml-get-attribute node 'class) "preview")))
|
||||
html))))
|
||||
(goto-char (point-min))
|
||||
(message "Rendering...done")))
|
||||
|
||||
(defun oddmuse-find-node (test node)
|
||||
"Return the child of NODE that satisfies TEST.
|
||||
TEST is a function that takes a node as an argument. NODE is a
|
||||
node as returned by `libxml-parse-html-region' or
|
||||
`xml-parse-region'. The function recurses through the node tree."
|
||||
(if (funcall test node)
|
||||
node
|
||||
(dolist (child (xml-node-children node))
|
||||
(when (listp child)
|
||||
(let ((result (oddmuse-find-node test child)))
|
||||
(when result
|
||||
(return result)))))))
|
||||
|
||||
(defun oddmuse-make-completion-table (wiki)
|
||||
"Create pagename completion table for WIKI.
|
||||
@@ -615,7 +788,7 @@ With universal argument, reload."
|
||||
(unless (equal name (buffer-name)) (rename-buffer name))
|
||||
(erase-buffer)
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run "Load recent changes" command buf nil))
|
||||
(oddmuse-run "Load recent changes" command buf))
|
||||
(oddmuse-rc-buffer)
|
||||
(set (make-local-variable 'oddmuse-wiki) wiki)))))
|
||||
|
||||
|
||||
187
contrib/vc-oddmuse.el
Normal file
187
contrib/vc-oddmuse.el
Normal file
@@ -0,0 +1,187 @@
|
||||
;;; 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)
|
||||
(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)
|
||||
"No idea."
|
||||
'up-to-date)
|
||||
|
||||
(defun vc-oddmuse-working-revision (file)
|
||||
"No idea")
|
||||
|
||||
(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
|
||||
"curl --silent %w\"?action=rc;showedit=1;all=1;from=1;raw=1;match=%r\""
|
||||
"Command to use for publishing index pages.
|
||||
It must print the page to stdout.
|
||||
|
||||
%? '?' character
|
||||
%w URL of the wiki as provided by `oddmuse-wikis'
|
||||
%r Regular expression, URL encoded, of the pages to limit ourselves to.
|
||||
This uses the free variable `regexp'.")
|
||||
|
||||
(defun vc-oddmuse-print-log (files buffer &optional shortlog
|
||||
start-revision limit)
|
||||
"Load complete recent changes for the files."
|
||||
(let* ((wiki (or oddmuse-wiki
|
||||
(completing-read "Wiki: " oddmuse-wikis nil t)))
|
||||
(wiki-data (assoc wiki oddmuse-wikis))
|
||||
(url (nth 1 wiki-data))
|
||||
(regexp (concat
|
||||
"^(" ;; Perl regular expression!
|
||||
(mapconcat 'file-name-nondirectory files "|")
|
||||
")$"))
|
||||
(command (oddmuse-format-command vc-oddmuse-log-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding)
|
||||
(max-mini-window-height 1))
|
||||
(oddmuse-run "Getting recent changes" command buffer nil))
|
||||
;; Parse current buffer as RSS 3.0 and display it correctly.
|
||||
(save-excursion
|
||||
(with-current-buffer buffer
|
||||
(let (result)
|
||||
(dolist (item (cdr (split-string (buffer-string) "\n\n")));; skip first item
|
||||
(let ((data (mapcar (lambda (line)
|
||||
(when (string-match "^\\(.*?\\): \\(.*\\)" line)
|
||||
(cons (match-string 1 line)
|
||||
(match-string 2 line))))
|
||||
(split-string item "\n"))))
|
||||
(setq result (cons data result))))
|
||||
(dolist (item (nreverse result))
|
||||
(insert "title: " (cdr (assoc "title" item)) "\n"
|
||||
"version: " (cdr (assoc "revision" item)) "\n"
|
||||
"generator: " (cdr (assoc "generator" item)) "\n"
|
||||
"timestamp: " (cdr (assoc "last-modified" item)) "\n\n"
|
||||
" " (or (cdr (assoc "description" item)) ""))
|
||||
(fill-paragraph)
|
||||
(insert "\n\n"))
|
||||
(goto-char (point-min))))))
|
||||
|
||||
(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
|
||||
"curl --silent %w\"?action=browse;id=%t;revision=%o;raw=1\""
|
||||
"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'
|
||||
%o Revision to retrieve as provided by `oddmuse-revision'")
|
||||
|
||||
(defvar vc-oddmuse-get-history-command
|
||||
"curl --silent %w\"?action=history;id=%t;raw=1\""
|
||||
"Command to use to get the history 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'")
|
||||
|
||||
(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)
|
||||
(setq oddmuse-page-name (file-name-nondirectory file)
|
||||
oddmuse-wiki (or oddmuse-wiki
|
||||
(file-name-nondirectory
|
||||
(directory-file-name
|
||||
(file-name-directory file)))))
|
||||
(let* ((wiki-data (or (assoc oddmuse-wiki oddmuse-wikis)
|
||||
(error "Cannot find data for wiki %s" oddmuse-wiki)))
|
||||
(url (nth 1 wiki-data)))
|
||||
(unless rev1
|
||||
;; Since we don't know the most recent revision we have to fetch
|
||||
;; it from the server every time.
|
||||
(with-temp-buffer
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run "Determining latest revision"
|
||||
(oddmuse-format-command vc-oddmuse-get-history-command)
|
||||
(current-buffer) nil))
|
||||
(if (re-search-forward "^revision: \\([0-9]+\\)$" nil t)
|
||||
(setq rev1 (match-string 1))
|
||||
(error "Cannot determine the latest revision from the page history"))))
|
||||
(dolist (rev (list rev1 rev2))
|
||||
(when (and rev
|
||||
(not (file-readable-p (concat oddmuse-directory
|
||||
"/" oddmuse-wiki "/"
|
||||
oddmuse-page-name
|
||||
".~" rev "~"))))
|
||||
(let* ((oddmuse-revision rev)
|
||||
(command (oddmuse-format-command vc-oddmuse-get-revision-command))
|
||||
(coding (nth 2 wiki-data))
|
||||
(filename (concat oddmuse-directory "/" oddmuse-wiki "/"
|
||||
oddmuse-page-name ".~" rev "~"))
|
||||
(coding-system-for-read coding)
|
||||
(coding-system-for-write coding))
|
||||
(with-temp-buffer
|
||||
(let ((max-mini-window-height 1))
|
||||
(oddmuse-run (concat "Downloading revision " rev)
|
||||
command (current-buffer) nil))
|
||||
(write-file filename)))))
|
||||
(diff-no-select
|
||||
(if rev1
|
||||
(concat oddmuse-directory "/" oddmuse-wiki "/" oddmuse-page-name ".~" rev1 "~")
|
||||
file)
|
||||
(if rev2
|
||||
(concat oddmuse-directory "/" oddmuse-wiki "/" oddmuse-page-name ".~" rev2 "~")
|
||||
file)
|
||||
nil
|
||||
(vc-switches 'oddmuse 'diff)
|
||||
buffer))))
|
||||
|
||||
(provide 'vc-oddmuse)
|
||||
@@ -237,9 +237,6 @@ a.near:link {
|
||||
a.near:visited {
|
||||
color:#550;
|
||||
}
|
||||
a.tag:before {
|
||||
content:"\2601\ ";
|
||||
}
|
||||
ol, ul, dl {
|
||||
padding-top:0.5em;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ body {
|
||||
padding:2% 5%;
|
||||
margin:0;
|
||||
font-family: "Gentium Basic", "Bookman Old Style", "Times New Roman", serif;
|
||||
font-size: 18pt;
|
||||
font-size: 16pt;
|
||||
}
|
||||
|
||||
div.header h1 {
|
||||
@@ -129,9 +129,15 @@ dd {
|
||||
margin-bottom:1ex;
|
||||
}
|
||||
|
||||
textarea { width:100%; height:80%; }
|
||||
textarea {
|
||||
width:100%;
|
||||
height:80%;
|
||||
font-size: 12pt;
|
||||
}
|
||||
textarea#summary { height: 3em; }
|
||||
|
||||
input {
|
||||
font-size: 12pt;
|
||||
}
|
||||
div.image span.caption {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
0
modules/aawrapperdiv.pl
Executable file → Normal file
0
modules/aawrapperdiv.pl
Executable file → Normal file
0
modules/antispam.pl
Executable file → Normal file
0
modules/antispam.pl
Executable file → Normal file
64
modules/askpage.pl
Normal file
64
modules/askpage.pl
Normal file
@@ -0,0 +1,64 @@
|
||||
# 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/>.
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/askpage.pl">askpage.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Ask_Page_Extension">Ask Page Extension</a></p>';
|
||||
|
||||
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)) {
|
||||
my $currentId = IncrementInFile("$DataDir/curquestion");
|
||||
$currentQuestion =~ s/[\s\n]//g;
|
||||
return OldAskPageDoPost($QuestionPage . $currentQuestion, @_);
|
||||
} else {
|
||||
return OldAskPageDoPost($id, @_);
|
||||
}
|
||||
}
|
||||
|
||||
*OldAskPageGetCommentForm=*GetCommentForm;
|
||||
*GetCommentForm=*NewAskPageGetCommentForm;
|
||||
sub NewAskPageGetCommentForm {
|
||||
my ($id, $rev, $comment) = @_;
|
||||
$NewComment = $NewQuestion if $id eq $AskPage;
|
||||
return 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];
|
||||
}
|
||||
0
modules/backlinks.pl
Executable file → Normal file
0
modules/backlinks.pl
Executable file → Normal file
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
|
||||
|
||||
$ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git/tree/modules/balanced-page-directories.pl">balanced-page-directories.pl</a>, see <a href="http://www.oddmuse.org/cgi-bin/oddmuse/Balanced_Page_Directories_Extension">Balanced Page Directories Extension</a>';
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -25,7 +25,7 @@ sub BanQuickNewUserIsBanned {
|
||||
if (not $rule
|
||||
and $SurgeProtection # need surge protection
|
||||
and GetParam('title')) {
|
||||
my $name = GetParam('username', $ENV{'REMOTE_ADDR'});
|
||||
my $name = GetParam('username', GetRemoteAddress());
|
||||
my @entries = @{$RecentVisitors{$name}};
|
||||
# $entry[0] is $Now after AddRecentVisitor
|
||||
my $ts = $entries[1];
|
||||
|
||||
0
modules/blockquote.pl
Executable file → Normal file
0
modules/blockquote.pl
Executable file → Normal file
0
modules/clustermap.pl
Executable file → Normal file
0
modules/clustermap.pl
Executable file → Normal file
0
modules/crossbar.pl
Executable file → Normal file
0
modules/crossbar.pl
Executable file → Normal file
@@ -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.',
|
||||
|
||||
@@ -44,6 +44,6 @@ sub DuckDuckGoSearchInit {
|
||||
}
|
||||
|
||||
sub DoDuckDuckGoSearch {
|
||||
my $search = GetParam('search', undef);
|
||||
my $search = UrlEncode(GetParam('search', undef));
|
||||
print $q->redirect({-uri=>"https://www.duckduckgo.com/?q=$search+site%3A$DuckDuckGoSearchDomain"});
|
||||
}
|
||||
|
||||
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/>.
|
||||
|
||||
$ModulesDescription .= '<p>form_timeout_token.pl</p>';
|
||||
|
||||
=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);
|
||||
}
|
||||
0
modules/hibernal.pl
Executable file → Normal file
0
modules/hibernal.pl
Executable file → Normal file
0
modules/htmllinks.pl
Executable file → Normal file
0
modules/htmllinks.pl
Executable file → Normal file
@@ -24,7 +24,7 @@ sub DoPrintableIndex {
|
||||
print GetHeader('', T('Index'), '');
|
||||
my @pages = PrintableIndexPages();
|
||||
my %hash;
|
||||
map { push(@{$hash{GetPageDirectory($_)}}, $_); } @pages;
|
||||
map { push(@{$hash{substr($_,0,1)}}, $_); } @pages;
|
||||
print '<div class="content printable index">';
|
||||
print $q->p($q->a({-name=>"top"}),
|
||||
map { $q->a({-href=>"#$_"}, $_); } sort keys %hash);
|
||||
|
||||
1131
modules/joiner.pl
Normal file
1131
modules/joiner.pl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ $ModulesDescription .= '<p><a href="http://git.savannah.gnu.org/cgit/oddmuse.git
|
||||
|
||||
$CookieParameters{interface} = '';
|
||||
|
||||
use vars qw($CurrentLanguage);
|
||||
use vars qw($CurrentLanguage $LoadLanguageDir);
|
||||
|
||||
my %library= ('bg' => 'bulgarian-utf8.pl',
|
||||
'de' => 'german-utf8.pl',
|
||||
@@ -60,6 +60,7 @@ sub LoadLanguage {
|
||||
foreach $_ (@prefs) {
|
||||
last if $Lang{$_} eq 'en'; # the default
|
||||
my $file = $library{$Lang{$_}};
|
||||
$file = "$LoadLanguageDir/$file" if defined $LoadLanguageDir;
|
||||
if (-r $file) {
|
||||
do $file;
|
||||
do "$ConfigFile-$Lang{$_}" if -r "$ConfigFile-$Lang{$_}";
|
||||
|
||||
2
modules/logbannedcontent.pl
Executable file → Normal file
2
modules/logbannedcontent.pl
Executable file → Normal file
@@ -45,6 +45,6 @@ sub LogWrite {
|
||||
my $rule = shift;
|
||||
my $id = $OpenPageName || GetId();
|
||||
AppendStringToFile($BannedFile,
|
||||
join("\t", TimeToW3($Now), $ENV{'REMOTE_ADDR'}, $id, $rule)
|
||||
join("\t", TimeToW3($Now), GetRemoteAddress(), $id, $rule)
|
||||
. "\n");
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ $Action{$SelfBan} = \&DoSelfBan;
|
||||
|
||||
sub DoSelfBan {
|
||||
my $date = &TimeToText($Now);
|
||||
my $str = '^' . quotemeta($ENV{REMOTE_ADDR});
|
||||
my $str = '^' . quotemeta(GetRemoteAddress());
|
||||
OpenPage($BannedHosts);
|
||||
Save ($BannedHosts, $Page{text} . "\n\nself-ban on $date\n $str",
|
||||
Ts("Self-ban by %s", $ENV{REMOTE_ADDR}), 1); # minor edit
|
||||
Ts("Self-ban by %s", GetRemoteAddress()), 1); # minor edit
|
||||
ReportError(T("You have banned your own IP."));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ sub OpenProxyNewDoEdit {
|
||||
|
||||
sub BanOpenProxy {
|
||||
my ($force) = @_;
|
||||
my $ip = $ENV{REMOTE_ADDR};
|
||||
my $ip = GetRemoteAddress();
|
||||
my $limit = 60*60*24*30; # rescan after 30 days
|
||||
# Only check each IP address once a month
|
||||
my %proxy = split(/\s+/, ReadFile($OpenProxies));
|
||||
|
||||
0
modules/poetry.pl
Executable file → Normal file
0
modules/poetry.pl
Executable file → Normal file
@@ -78,7 +78,8 @@ sub NewQuestionaskerDoPost {
|
||||
print GetHeader('', T('Edit Denied'), undef, undef, '403 FORBIDDEN');
|
||||
print $q->p(T('You did not answer correctly.'));
|
||||
print GetFormStart(), QuestionaskerGetQuestion(1),
|
||||
(map { $q->hidden($_, '') }
|
||||
(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
|
||||
|
||||
@@ -252,7 +252,7 @@ sub ReCaptchaCheckAnswer {
|
||||
eval "use Captcha::reCAPTCHA";
|
||||
my $result = Captcha::reCAPTCHA->new()->check_answer(
|
||||
$ReCaptchaPrivateKey,
|
||||
$ENV{'REMOTE_ADDR'},
|
||||
GetRemoteAddress(),
|
||||
GetParam('recaptcha_challenge_field'),
|
||||
GetParam('recaptcha_response_field')
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ sub RefererNewDeletePage {
|
||||
|
||||
sub GetRefererFile {
|
||||
my $id = shift;
|
||||
return $RefererDir . '/' . GetPageDirectory($id) . "/$id.rf";
|
||||
return "$RefererDir/$id.rf";
|
||||
}
|
||||
|
||||
sub ReadReferers {
|
||||
|
||||
0
modules/sabifoo.pl
Executable file → Normal file
0
modules/sabifoo.pl
Executable file → Normal file
0
modules/slideshow.pl
Executable file → Normal file
0
modules/slideshow.pl
Executable file → Normal file
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2004-2013 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2004-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
|
||||
@@ -35,6 +35,7 @@ sub DoStatic {
|
||||
}
|
||||
CreateDir($StaticDir);
|
||||
%StaticFiles = ();
|
||||
print '<p>' unless $raw;
|
||||
StaticWriteFiles();
|
||||
print '</p>' unless $raw;
|
||||
PrintFooter() unless $raw;
|
||||
@@ -56,15 +57,21 @@ sub StaticMimeTypes {
|
||||
|
||||
sub StaticWriteFiles {
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $html = GetParam('html', 0);
|
||||
local *ScriptLink = *StaticScriptLink;
|
||||
local *GetDownloadLink = *StaticGetDownloadLink;
|
||||
# get rid of subscribe link in the footer by mail.pl
|
||||
local *GetCommentForm = *MailOldGetCommentForm if defined &MailNewGetCommentForm;
|
||||
foreach my $id (AllPagesList()) {
|
||||
if ($StaticAlways > 1
|
||||
or GetParam('html', 0)
|
||||
or $html
|
||||
or PageIsUploadedFile($id)) {
|
||||
StaticWriteFile($id);
|
||||
StaticWriteFile($id, $html);
|
||||
}
|
||||
}
|
||||
if ($StaticAlways > 1 or $html) {
|
||||
StaticWriteCss();
|
||||
}
|
||||
}
|
||||
|
||||
sub StaticScriptLink {
|
||||
@@ -120,15 +127,19 @@ sub StaticFileName {
|
||||
}
|
||||
|
||||
sub StaticWriteFile {
|
||||
my $id = shift;
|
||||
my ($id, $html) = @_;
|
||||
my $raw = GetParam('raw', 0);
|
||||
my $filename = StaticFileName($id);
|
||||
OpenPage($id);
|
||||
my ($mimetype, $encoding, $data) = $Page{text} =~ /^\#FILE ([^ \n]+) ?([^ \n]*)\n(.*)/s;
|
||||
open(F,"> $StaticDir/$filename") or ReportError(Ts('Cannot write %s', $filename));
|
||||
my ($mimetype, $encoding, $data) =
|
||||
$Page{text} =~ /^\#FILE ([^ \n]+) ?([^ \n]*)\n(.*)/s;
|
||||
open(F,"> $StaticDir/$filename")
|
||||
or ReportError(Ts('Cannot write %s', $filename));
|
||||
if ($data) {
|
||||
binmode(F);
|
||||
StaticFile($id, $mimetype, $data);
|
||||
} elsif ($html) {
|
||||
binmode(F, ':utf8');
|
||||
StaticHtml($id);
|
||||
} else {
|
||||
print "no data for ";
|
||||
@@ -141,7 +152,6 @@ sub StaticWriteFile {
|
||||
sub StaticFile {
|
||||
my ($id, $type, $data) = @_;
|
||||
require MIME::Base64;
|
||||
binmode(F);
|
||||
print F MIME::Base64::decode($data);
|
||||
}
|
||||
|
||||
@@ -200,7 +210,8 @@ EOT
|
||||
print F $q->div({-class=>'content'}, PageHtml($id)); # this reopens the page currently open
|
||||
# footer
|
||||
my $links = '';
|
||||
if ($OpenPageName !~ /^$CommentsPrefix/) { # fails if $CommentsPrefix is empty!
|
||||
if ($OpenPageName !~ /^$CommentsPrefix/ # fails if $CommentsPrefix is empty!
|
||||
and $IndexHash{$CommentsPrefix . $OpenPageName}) {
|
||||
$links .= ScriptLink(UrlEncode($CommentsPrefix . $OpenPageName),
|
||||
T('Comments on this page'));
|
||||
}
|
||||
@@ -216,6 +227,21 @@ EOT
|
||||
print F '</body></html>';
|
||||
}
|
||||
|
||||
sub StaticWriteCss {
|
||||
my $css;
|
||||
if ($StyleSheet) {
|
||||
$css = GetRaw($StyleSheet);
|
||||
}
|
||||
if (not $css and $IndexHash{$StyleSheetPage}) {
|
||||
$css = GetPageContent($StyleSheetPage);
|
||||
}
|
||||
if (not $css) {
|
||||
$css = GetRaw('http://www.oddmuse.org/default.css');
|
||||
}
|
||||
WriteStringToFile("$StaticDir/static.css", $css) if $css;
|
||||
chmod 0644,"$StaticDir/static.css";
|
||||
}
|
||||
|
||||
*StaticFilesOldSave = *Save;
|
||||
*Save = *StaticFilesNewSave;
|
||||
|
||||
|
||||
@@ -422,7 +422,7 @@ sub StaticNewDoRollback {
|
||||
} elsif (!UserCanEdit($id, 1)) {
|
||||
print Ts('Editing not allowed for %s.', $id), $q->br();
|
||||
} else {
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne $ENV{REMOTE_ADDR}));
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne GetRemoteAddress()));
|
||||
StaticDeleteFile($id);
|
||||
print Ts('%s rolled back', GetPageLink($id)), ($ts ? ' ' . Ts('to %s', TimeToText($to)) : ''), $q->br();
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ Go!
|
||||
(minor)
|
||||
(次要的)
|
||||
rollback
|
||||
回復
|
||||
回滾
|
||||
new
|
||||
新增
|
||||
All changes for %s
|
||||
@@ -168,13 +168,13 @@ Revision %s
|
||||
Contributors to %s
|
||||
編寫 %s 的作者
|
||||
Missing target for rollback.
|
||||
找不到要回復的目標
|
||||
找不到要回滾的目標
|
||||
Target for rollback is too far back.
|
||||
要回復的目標已太久以前了。
|
||||
要回滾的目標已太久以前了。
|
||||
A username is required for ordinary users.
|
||||
需使用普通用戶名稱
|
||||
Rolling back changes
|
||||
回復修改
|
||||
回滾修改
|
||||
The two revisions are the same.
|
||||
二個版本相同
|
||||
Editing not allowed for %s.
|
||||
@@ -182,9 +182,9 @@ Editing not allowed for %s.
|
||||
Rollback of %s would restore banned content.
|
||||
|
||||
Rollback to %s
|
||||
回復至 %s
|
||||
回滾至 %s
|
||||
%s rolled back
|
||||
%s 已回復
|
||||
%s 已回滾
|
||||
to %s
|
||||
到 %s
|
||||
Index of all pages
|
||||
@@ -644,7 +644,7 @@ SPAM 廣告頁面
|
||||
Cannot find revision %s.
|
||||
無法取得版本 %s 。
|
||||
Revert to revision %1: %2
|
||||
回復至版本 %1: %2
|
||||
回滾至版本 %1: %2
|
||||
Marked as %s.
|
||||
標記為 %s 。
|
||||
Cannot find unspammed revision.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
103
modules/upgrade.pl
Normal file
103
modules/upgrade.pl
Normal file
@@ -0,0 +1,103 @@
|
||||
# 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/>.
|
||||
|
||||
use utf8;
|
||||
|
||||
# We are now running in InitModules. InitVariables will be called later.
|
||||
# We want to prevent any calls to GetPageContent and the like.
|
||||
|
||||
*UpgradeOldInitVariables = *InitVariables;
|
||||
*InitVariables = *UpgradeNewInitVariables;
|
||||
|
||||
sub UpgradeNewInitVariables {
|
||||
$InterMap = undef;
|
||||
$LocalNamesPage = undef;
|
||||
$SidebarName = undef;
|
||||
$NearMap = undef;
|
||||
UpgradeOldInitVariables(@_);
|
||||
}
|
||||
|
||||
*DoBrowseRequest = *DoUpgrade;
|
||||
|
||||
sub DoUpgrade {
|
||||
|
||||
# The only thing allowed besides upgrading is login and unlock
|
||||
my $action = lc(GetParam('action', ''));
|
||||
if (($action eq 'password' or $action eq 'unlock')
|
||||
and $Action{$action}) {
|
||||
return &{$Action{$action}}($id);
|
||||
}
|
||||
|
||||
# Only admins may upgrade
|
||||
ReportError(T('Upgrading Database'),
|
||||
'403 FORBIDDEN', 0,
|
||||
$q->p(T('This operation is restricted to administrators only...'))
|
||||
. $q->p(ScriptLink('action=password', T('Login'), 'password')))
|
||||
unless UserIsAdmin();
|
||||
|
||||
ReportError(T('Upgrading Database'),
|
||||
'403 FORBIDDEN', 0,
|
||||
$q->p(T('Did the previous upgrade end with an error? A lock was left behind.'))
|
||||
. $q->p(ScriptLink('action=unlock', T('Unlock wiki'), 'unlock')))
|
||||
unless RequestLockDir('main');
|
||||
|
||||
print GetHeader('', T('Upgrading Database')),
|
||||
$q->start_div({-class=>'content upgrade'});
|
||||
|
||||
if (-e $IndexFile) {
|
||||
unlink $IndexFile;
|
||||
}
|
||||
|
||||
print "<p>Renaming files...";
|
||||
|
||||
for my $ns ('', keys %InterSite) {
|
||||
next unless -d "$DataDir/$ns";
|
||||
print "<br />\n<strong>$ns</strong>" if $ns;
|
||||
for my $dir ($PageDir, $KeepDir, $RefererDir, $JoinerDir, $JoinerEmailDir) {
|
||||
next unless $dir;
|
||||
$dir =~ s/^$DataDir/$DataDir\/$ns/ if $ns;
|
||||
for my $old (bsd_glob("$dir/*/*", bsd_glob("$dir/*/.*"))) {
|
||||
next if $old eq '.' or $old eq '..';
|
||||
my $oldname = $old;
|
||||
utf8::decode($oldname);
|
||||
print "<br />\n$oldname";
|
||||
my $new = $old;
|
||||
$new =~ s!/([A-Z]|other)/!/!;
|
||||
if ($old eq $new) {
|
||||
print " does not fit the pattern!";
|
||||
} elsif (not rename $old, $new) {
|
||||
my $newname = $new;
|
||||
utf8::decode($newname);
|
||||
print " → $newname failed!";
|
||||
}
|
||||
}
|
||||
for my $subdir (grep(/\/([A-Z]|other)$/, bsd_glob("$dir/*"))) {
|
||||
rmdir $subdir; # ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print $q->end_p();
|
||||
|
||||
if (rename "$ModuleDir/upgrade.pl", "$ModuleDir/upgrade.done") {
|
||||
print $q->p(T("Upgrade complete."))
|
||||
} else {
|
||||
print $q->p(T("Upgrade complete. Please remove $ModuleDir/upgade.pl, now."))
|
||||
}
|
||||
|
||||
ReleaseLock();
|
||||
|
||||
print $q->end_p(), $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
44
t/balanced-page-directories.t
Normal file
44
t/balanced-page-directories.t
Normal file
@@ -0,0 +1,44 @@
|
||||
# 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 10;
|
||||
use utf8; # tests contain UTF-8 characters and it matters
|
||||
|
||||
clear_pages();
|
||||
|
||||
update_page('HomePage', 'Das ist ein Ei.');
|
||||
ok(-f GetPageFile('HomePage'), 'page file');
|
||||
|
||||
update_page('HomePage', 'This is an egg.');
|
||||
ok(-f GetKeepFile('HomePage', 1), 'keep file');
|
||||
|
||||
update_page('ホームページ', 'これが卵です。');
|
||||
ok(-f GetPageFile('ホームページ'), 'Japanese page file');
|
||||
|
||||
update_page($StyleSheetPage, '/* nothing to see */', '', 0, 1);
|
||||
ok(-f GetPageFile($StyleSheetPage), 'locked page file');
|
||||
ok(-f GetLockedPageFile($StyleSheetPage), 'page lock');
|
||||
|
||||
add_module('balanced-page-directories.pl');
|
||||
|
||||
test_page(get_page('HomePage'), 'This is an egg.');
|
||||
ok(-f GetKeepFile('HomePage', 1), 'keep file');
|
||||
test_page(get_page('ホームページ'), 'これが卵です。');
|
||||
ok(-f GetLockedPageFile($StyleSheetPage), 'page lock');
|
||||
|
||||
# create a new page
|
||||
test_page(update_page('サイトマップ', '日本語ユーザーに向けて'),
|
||||
'日本語ユーザーに向けて');
|
||||
32
t/comments.t
32
t/comments.t
@@ -1,24 +1,20 @@
|
||||
# Copyright (C) 2006 Alex Schroeder <alex@emacswiki.org>
|
||||
# Copyright (C) 2006–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 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 35;
|
||||
use Test::More tests => 36;
|
||||
clear_pages();
|
||||
|
||||
AppendStringToFile($ConfigFile, "\$CommentsPrefix = 'Comments on ';\n");
|
||||
@@ -89,11 +85,17 @@ test_page(get_page('Comments_on_Yadda'), 'This is my comment\.', '-- Alex');
|
||||
test_page(get_page('action=rc raw=1'), 'title: Comments on Yadda',
|
||||
'description: This is my comment.', 'generator: Alex');
|
||||
|
||||
# homepage
|
||||
get_page('title=Comments_on_Yadda', 'aftertext=This%20is%20another%20comment.',
|
||||
'username=Alex', 'homepage=http%3a%2f%2fwww%2eoddmuse%2eorg%2f');
|
||||
xpath_test(get_page('Comments_on_Yadda'),
|
||||
'//p[contains(text(),"This is my comment.")]',
|
||||
'//a[@class="url http outside"][@href="http://www.oddmuse.org/"][text()="Alex"]');
|
||||
# variant without protocol
|
||||
get_page('title=Comments_on_Yadda', 'aftertext=This%20is%20another%20comment.',
|
||||
'username=Berta', 'homepage=alexschroeder%2ech');
|
||||
xpath_test(get_page('Comments_on_Yadda'),
|
||||
'//a[@class="url http outside"][@href="http://alexschroeder.ch"][text()="Berta"]');
|
||||
|
||||
my $textarea = '//textarea[@name="aftertext"][@id="aftertext"]';
|
||||
xpath_test(get_page('Comments_on_Yadda'), $textarea);
|
||||
|
||||
@@ -39,7 +39,7 @@ update_page('ExpiredPage', "Still more spam from http://example.com.");
|
||||
|
||||
update_page('BannedContent', " example\\.com\n", 'required', 0, 1);
|
||||
|
||||
unlink("$DataDir/keep/E/ExpiredPage/1.kp")
|
||||
unlink("$DataDir/keep/ExpiredPage/1.kp")
|
||||
or die "Cannot delete kept revision: $!";
|
||||
|
||||
my $page = get_page('action=spam');
|
||||
|
||||
13
t/journal3.t
13
t/journal3.t
@@ -1,4 +1,4 @@
|
||||
# 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
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 34;
|
||||
use Test::More tests => 35;
|
||||
|
||||
clear_pages();
|
||||
|
||||
@@ -38,7 +38,7 @@ test_page($page, '2011-12-17', '2011-12-16', '2011-12-15',
|
||||
test_page_negative($page, '2011-12-12', '2011-12-11', '2011-12-10',
|
||||
'2011-12-09', '2011-12-08');
|
||||
|
||||
xpath_test($page, '//a[@href="http://localhost/wiki.pl?action=more;num=5;regexp=^\d\d\d\d-\d\d-\d\d;search=;mode=;offset=5"][text()="More..."]');
|
||||
xpath_test($page, '//a[@href="http://localhost/wiki.pl?action=more;num=5;regexp=%5e%5cd%5cd%5cd%5cd-%5cd%5cd-%5cd%5cd;search=;mode=;offset=5"][text()="More..."]');
|
||||
|
||||
# check that the link for more actually works
|
||||
|
||||
@@ -60,7 +60,12 @@ test_page($page, '2011-12-13', '2011-12-12', '2011-12-11',
|
||||
'2011-12-10', '2011-12-09');
|
||||
xpath_test($page, '//a[text()="More..."]');
|
||||
|
||||
# one las check
|
||||
# one last check
|
||||
|
||||
xpath_test_negative(get_page("action=more num=5 offset=6 "),
|
||||
'//a[text()="More..."]');
|
||||
|
||||
# check for unescaped URL
|
||||
|
||||
$page = update_page('Plus', "Using a plus:\n\n<journal 5 \"^.+\">");
|
||||
xpath_test($page, '//a[text()="More..."][@href="http://localhost/wiki.pl?action=more;num=5;regexp=%5e.%2b;search=;mode=;offset=5"]');
|
||||
|
||||
4042
t/oddmuse-2.2.6.pl
Normal file
4042
t/oddmuse-2.2.6.pl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2008 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 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
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 14;
|
||||
use Test::More tests => 15;
|
||||
|
||||
clear_pages();
|
||||
add_module('questionasker.pl');
|
||||
@@ -55,3 +55,7 @@ test_page(update_page('test', 'answer new question', undef, undef, undef,
|
||||
test_page(get_page('Comments_on_test'),
|
||||
'label for="username"',
|
||||
'say hi');
|
||||
|
||||
# test for corruption of Unicode text
|
||||
update_page('Umlaute', '<Schröder>');
|
||||
test_page($redirect, '<Schröder>')
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
# Copyright (C) 2007, 2008, 2009 Alex Schroeder <alex@gnu.org>
|
||||
# Copyright (C) 2007–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 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 29;
|
||||
use utf8;
|
||||
use Test::More tests => 36;
|
||||
clear_pages();
|
||||
|
||||
add_module('static-copy.pl');
|
||||
@@ -114,16 +115,37 @@ xpath_test(update_page('HomePage', "Static: [[image:Trogs]]"),
|
||||
# delete the static pages and regenerate it
|
||||
ok(unlink("$DataDir/static/Trogs.svgz"), "Deleted $DataDir/static/Trogs.svgz");
|
||||
ok(unlink("$DataDir/static/Logo.png"), "Deleted $DataDir/static/Logo.png");
|
||||
test_page(get_page('action=static raw=1 pwd=foo'), "Trogs", "Logo");
|
||||
ok(-f "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz exists");
|
||||
ok(-f "$DataDir/static/Logo.png", "$DataDir/static/Logo.png exists");
|
||||
ok(! -f "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html does not exist");
|
||||
test_page(get_page('action=static raw=1 pwd=foo html=1'), "Trogs", "Logo", "HomePage");
|
||||
ok(-f "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz exists");
|
||||
ok(-f "$DataDir/static/Logo.png", "$DataDir/static/Logo.png exists");
|
||||
ok(-f "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html exists");
|
||||
|
||||
# Make sure spaces are translated to underscores (fixed in image.pl)
|
||||
# StaticWriteFiles must write uploaded files only (since $StaticAlways = 1)
|
||||
$page = get_page('action=static raw=1 pwd=foo');
|
||||
test_page($page, "Trogs", "Logo"); # Remember, a rollback has restored Logo.png
|
||||
test_page_negative($page, "HomePage"); # since it an ordinary page
|
||||
|
||||
ok(-s "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz has nonzero size");
|
||||
ok(-s "$DataDir/static/Logo.png", "$DataDir/static/Logo.png has nonzero size");
|
||||
ok(! -e "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html does not exist");
|
||||
|
||||
# force generation of HomePage using html=1
|
||||
$page = get_page('action=static raw=1 pwd=foo html=1');
|
||||
test_page($page, "Trogs", "Logo", "HomePage");
|
||||
test_page_negative($page, "no data"); # must not skip HomePage!
|
||||
|
||||
ok(-s "$DataDir/static/Trogs.svgz", "$DataDir/static/Trogs.svgz has nonzero size");
|
||||
ok(-s "$DataDir/static/Logo.png", "$DataDir/static/Logo.png has nonzero size");
|
||||
ok(-s "$DataDir/static/HomePage.html", "$DataDir/static/HomePage.html has nonzero size");
|
||||
|
||||
# check that links between pages work as expected
|
||||
xpath_test(update_page("Test", "Link to HomePage. Testing Ümlaute."),
|
||||
'//a[text()="HomePage"][@href="http://localhost/wiki.pl/HomePage"]');
|
||||
test_page(get_page('action=static raw=1 pwd=foo html=1'), 'Test');
|
||||
xpath_test_file("$DataDir/static/Test.html",
|
||||
'//a[text()="HomePage"][@href="HomePage.html"]');
|
||||
test_file("$DataDir/static/Test.html",
|
||||
"Ümlaute");
|
||||
test_file("$DataDir/static/static.css",
|
||||
"body { background-color:#FFF; color:#000; margin:1em 2em; }");
|
||||
|
||||
# make sure spaces are translated to underscores (fixed in image.pl)
|
||||
add_module('image.pl');
|
||||
|
||||
# Now, create real pages. First, we'll use the ordinary image link to
|
||||
|
||||
25
t/test.pl
25
t/test.pl
@@ -108,7 +108,7 @@ sub get_page {
|
||||
sub name {
|
||||
$_ = shift;
|
||||
s/\n/\\n/g;
|
||||
$_ = '...' . substr($_, -60) if length > 63;
|
||||
$_ = '...' . substr($_, -67) if length > 70;
|
||||
return $_;
|
||||
}
|
||||
|
||||
@@ -158,6 +158,18 @@ sub test_page {
|
||||
}
|
||||
}
|
||||
|
||||
# one file, many tests
|
||||
sub test_file {
|
||||
my ($file, @tests) = @_;
|
||||
if (open(F, '< :utf8', $file)) {
|
||||
local $/ = undef;
|
||||
test_page(<F>, @tests);
|
||||
close(F);
|
||||
} else {
|
||||
warn "cannot open $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
# one string, many negative tests
|
||||
sub test_page_negative {
|
||||
my $page = shift;
|
||||
@@ -208,6 +220,17 @@ sub xpath_test {
|
||||
xpath_do(sub { shift > 0; }, "No Matches\n", @_);
|
||||
}
|
||||
|
||||
sub xpath_test_file {
|
||||
my ($file, @tests) = @_;
|
||||
if (open(F, '< :utf8', $file)) {
|
||||
local $/ = undef;
|
||||
xpath_test(<F>, @tests);
|
||||
close(F);
|
||||
} else {
|
||||
warn "cannot open $file\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub negative_xpath_test {
|
||||
xpath_do(sub { shift == 0; }, "Unexpected Matches\n", @_);
|
||||
}
|
||||
|
||||
119
t/upgrade.t
Normal file
119
t/upgrade.t
Normal file
@@ -0,0 +1,119 @@
|
||||
# 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/>.
|
||||
|
||||
require 't/test.pl';
|
||||
package OddMuse;
|
||||
use Test::More tests => 37;
|
||||
|
||||
clear_pages();
|
||||
|
||||
# Create a 2.2.6 wiki first.
|
||||
$page = qx(perl t/oddmuse-2.2.6.pl title=Test text=Hello);
|
||||
test_page($page, "Status: 302 Found");
|
||||
$page = qx(perl t/oddmuse-2.2.6.pl title=Test text=Hallo);
|
||||
test_page($page, "Status: 302 Found");
|
||||
$page = qx(perl t/oddmuse-2.2.6.pl action=pagelock id=Test set=1 pwd=foo);
|
||||
test_page($page, "created");
|
||||
|
||||
ok(-d "$PageDir/T", "T page directory exists");
|
||||
ok(-d "$KeepDir/T", "T keep directory exists");
|
||||
|
||||
add_module('upgrade.pl');
|
||||
|
||||
ok(-f "$ModuleDir/upgrade.pl", "upgrade.pl was installed");
|
||||
|
||||
test_page(get_page('Test'), 'Upgrading Database', 'action=password');
|
||||
|
||||
test_page(get_page('action=password'), 'You are a normal user');
|
||||
|
||||
$page = get_page('action=upgrade pwd=foo');
|
||||
|
||||
test_page($page,
|
||||
'page/T/Test.pg',
|
||||
'page/T/Test.lck',
|
||||
'keep/T/Test',
|
||||
'Upgrade complete');
|
||||
|
||||
test_page_negative($page, 'failed',
|
||||
'does not fit the pattern',
|
||||
'Please remove');
|
||||
|
||||
ok(! -d "$PageDir/T", "T directory has disappeared");
|
||||
ok(! -d "$KeepDir/T", "T keep directory has disappeared");
|
||||
ok(! -d $LockDir . 'main', "Lock was released");
|
||||
ok(! -f "$ModuleDir/upgrade.pl", "upgrade.pl was renamed");
|
||||
|
||||
test_page(get_page('action=browse id=Test revision=1'), 'Hello');
|
||||
|
||||
test_page(get_page('Test'), 'Hallo');
|
||||
|
||||
# you cannot run it again after a successful run
|
||||
test_page(get_page('action=upgrade pwd=foo'),
|
||||
'Invalid action parameter');
|
||||
|
||||
# reinstall it and run it again
|
||||
add_module('upgrade.pl');
|
||||
|
||||
test_page(get_page('action=upgrade pwd=foo'),
|
||||
'Upgrade complete');
|
||||
|
||||
# set up a wiki with namespaces
|
||||
|
||||
clear_pages();
|
||||
|
||||
add_module('namespaces.pl');
|
||||
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=Test text=Main%20Hello),
|
||||
"Status: 302 Found");
|
||||
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=Test text=Space%20Hello ns=Space),
|
||||
"Status: 302 Found");
|
||||
|
||||
add_module('upgrade.pl');
|
||||
|
||||
$page = get_page('action=upgrade pwd=foo');
|
||||
|
||||
test_page($page,
|
||||
'<strong>Space</strong>',
|
||||
'Upgrade complete');
|
||||
|
||||
test_page_negative($page, 'failed');
|
||||
|
||||
test_page(get_page('Test'), 'Main Hello');
|
||||
test_page(get_page("'/Space/Test?'"), 'Space Hello');
|
||||
|
||||
# Install modules which use GetPageContent in their init routine.
|
||||
|
||||
clear_pages();
|
||||
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$InterMap text=$InterMap),
|
||||
$InterMap);
|
||||
|
||||
add_module('localnames.pl');
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$LocalNamesPage text=$LocalNamesPage),
|
||||
$LocalNamesPage);
|
||||
|
||||
add_module('sidebar.pl');
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$SidebarName text=$SidebarName),
|
||||
$SidebarName);
|
||||
|
||||
add_module('near-links.pl');
|
||||
test_page(qx(perl t/oddmuse-2.2.6.pl title=$NearMap text=$NearMap),
|
||||
$NearMap);
|
||||
|
||||
add_module('upgrade.pl');
|
||||
test_page_negative(get_page('HomePage'), 'Cannot open');
|
||||
test_page(get_page('action=upgrade pwd=foo'),
|
||||
'Upgrade complete');
|
||||
@@ -54,6 +54,7 @@ sub rewrite {
|
||||
my %page = split(/$FS1/, read_file($file), -1);
|
||||
%section = split(/$FS2/, $page{text_default}, -1);
|
||||
%text = split(/$FS3/, $section{data}, -1);
|
||||
$file =~ s!/([A-Z]|other)/!/!;
|
||||
$file =~ s/\.db$/.pg/ or die "Invalid page name\n";
|
||||
print "Writing $file...\n";
|
||||
write_page_file($file);
|
||||
@@ -65,6 +66,7 @@ sub rewrite {
|
||||
print "Reading refer $file...\n";
|
||||
my $data = read_file($file);
|
||||
$data =~ s/$FS1/$NewFS/g;
|
||||
$file =~ s!/([A-Z]|other)/!/!;
|
||||
$file =~ s/\.rb$/.rf/ or die "Invalid page name\n";
|
||||
print "Writing $file...\n";
|
||||
write_file($file, $data);
|
||||
@@ -77,6 +79,7 @@ sub rewrite {
|
||||
my $data = read_file($file);
|
||||
my @list = split(/$FS1/, $data);
|
||||
my $out = $file;
|
||||
$out =~ s!/([A-Z]|other)/!/!;
|
||||
$out =~ s/\.kp$// or die "Invalid keep name\n";
|
||||
# We introduce a new variable $dir, here, instead of using $out,
|
||||
# because $out will be part of the filename later on, and the
|
||||
|
||||
150
wiki.pl
150
wiki.pl
@@ -39,7 +39,8 @@ use vars qw($RssLicense $RssCacheHours @RcDays $TempDir $LockDir $DataDir
|
||||
$KeepDir $PageDir $RcOldFile $IndexFile $BannedContent $NoEditFile $BannedHosts
|
||||
$ConfigFile $FullUrl $SiteName $HomePage $LogoUrl $RcDefault $RssDir
|
||||
$IndentLimit $RecentTop $RecentLink $EditAllowed $UseDiff $KeepDays $KeepMajor
|
||||
$EmbedWiki $BracketText $UseConfig $UseLookup $AdminPass $EditPass $NetworkFile
|
||||
$EmbedWiki $BracketText $UseConfig $UseLookup $AdminPass $EditPass
|
||||
$PassHashFunction $PassSalt $NetworkFile
|
||||
$BracketWiki $FreeLinks $WikiLinks $SummaryHours $FreeLinkPattern $RCName
|
||||
$RunCGI $ShowEdits $LinkPattern $RssExclude $InterLinkPattern $MaxPost $UseGrep
|
||||
$UrlPattern $UrlProtocols $ImageExtensions $InterSitePattern $FS $CookieName
|
||||
@@ -49,7 +50,7 @@ $RssImageUrl $ReadMe $RssRights $BannedCanRead $SurgeProtection $TopLinkBar
|
||||
$LanguageLimit $SurgeProtectionTime $SurgeProtectionViews $DeletedPage
|
||||
%Languages $InterMap $ValidatorLink %LockOnCreation $RssStyleSheet
|
||||
%CookieParameters @UserGotoBarPages $NewComment $HtmlHeaders $StyleSheetPage
|
||||
$ConfigPage $ScriptName $CommentsPrefix @UploadTypes $AllNetworkFiles
|
||||
$ConfigPage $ScriptName $CommentsPrefix $CommentsPattern @UploadTypes $AllNetworkFiles
|
||||
$UsePathInfo $UploadAllowed $LastUpdate $PageCluster %PlainTextPages
|
||||
$RssInterwikiTranslate $UseCache $Counter $ModuleDir $FullUrlPattern
|
||||
$SummaryDefaultLength $FreeInterLinkPattern %InvisibleCookieParameters
|
||||
@@ -95,12 +96,14 @@ $StyleSheetPage = 'css'; # Page for CSS sheet
|
||||
$LogoUrl = ''; # URL for site logo ('' for no logo)
|
||||
$NotFoundPg = ''; # Page for not-found links ('' for blank pg)
|
||||
|
||||
$NewText = "This page is empty.\n"; # New page text
|
||||
$NewComment = "Add your comment here.\n"; # New comment text
|
||||
$NewText = T('This page is empty.') . "\n"; # New page text
|
||||
$NewComment = T('Add your comment here:') . "\n"; # New comment text
|
||||
|
||||
$EditAllowed = 1; # 0 = no, 1 = yes, 2 = comments pages only, 3 = comments only
|
||||
$AdminPass = '' unless defined $AdminPass; # Whitespace separated passwords.
|
||||
$EditPass = '' unless defined $EditPass; # Whitespace separated passwords.
|
||||
$PassHashFunction = '' unless defined $PassHashFunction; # Name of the function to create hashes
|
||||
$PassSalt = '' unless defined $PassSalt; # Salt will be added to any password before hashing
|
||||
|
||||
$BannedHosts = 'BannedHosts'; # Page for banned hosts
|
||||
$BannedCanRead = 1; # 1 = banned cannot edit, 0 = banned cannot read
|
||||
@@ -151,6 +154,7 @@ $TopLinkBar = 1; # 1 = add a goto bar at the top of the page
|
||||
$UserGotoBar = ''; # HTML added to end of goto bar
|
||||
$ValidatorLink = 0; # 1 = Link to the W3C HTML validator service
|
||||
$CommentsPrefix = ''; # prefix for comment pages, eg. 'Comments_on_' to enable
|
||||
$CommentsPattern = undef; # regex used to match comment pages
|
||||
$HtmlHeaders = ''; # Additional stuff to put in the HTML <head> section
|
||||
$IndentLimit = 20; # Maximum depth of nested lists
|
||||
$LanguageLimit = 3; # Number of matches req. for each language
|
||||
@@ -289,6 +293,8 @@ sub InitVariables { # Init global session variables for mod_perl!
|
||||
(\$HomePage, \$RCName, \$BannedHosts, \$InterMap, \$StyleSheetPage, \$CommentsPrefix,
|
||||
\$ConfigPage, \$NotFoundPg, \$RssInterwikiTranslate, \$BannedContent, \$RssExclude, );
|
||||
$CommentsPrefix .= '_' if $add_space;
|
||||
$CommentsPattern = "^$CommentsPrefix(.*)"
|
||||
unless defined $CommentsPattern or not $CommentsPrefix;
|
||||
@UserGotoBarPages = ($HomePage, $RCName) unless @UserGotoBarPages;
|
||||
my @pages = sort($BannedHosts, $StyleSheetPage, $ConfigPage, $InterMap,
|
||||
$RssInterwikiTranslate, $BannedContent);
|
||||
@@ -465,12 +471,12 @@ sub ApplyRules {
|
||||
}
|
||||
Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
|
||||
($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
|
||||
} elsif ($bol && m/\G(\<journal(\s+(\d*))?(\s+"(.*?)")?(\s+(reverse|past|future))?(\s+search\s+(.*))?\>[ \t]*\n?)/cgi) {
|
||||
} elsif ($bol && m/\G(\<journal(\s+(\d*)(,(\d*))?)?(\s+"(.*?)")?(\s+(reverse|past|future))?(\s+search\s+(.*))?\>[ \t]*\n?)/cgi) {
|
||||
# <journal 10 "regexp"> includes 10 pages matching regexp
|
||||
Clean(CloseHtmlEnvironments());
|
||||
Dirty($1);
|
||||
my ($oldpos, $old_) = (pos, $_); # remember these because of the call to PrintJournal()
|
||||
PrintJournal($3, $5, $7, 0, $9); # no offset
|
||||
PrintJournal($3, $5, $7, $9, 0, $11); # no offset
|
||||
Clean(AddHtmlEnvironment('p')); # if dirty block is looked at later, this will disappear
|
||||
($_, pos) = ($old_, $oldpos); # restore \G (assignment order matters!)
|
||||
} elsif ($bol && m/\G(\<rss(\s+(\d*))?\s+(.*?)\>[ \t]*\n?)/cgis) {
|
||||
@@ -819,7 +825,7 @@ sub GetRaw {
|
||||
sub DoJournal {
|
||||
print GetHeader(undef, T('Journal'));
|
||||
print $q->start_div({-class=>'content'});
|
||||
PrintJournal(map { GetParam($_, ''); } qw(num regexp mode offset search));
|
||||
PrintJournal(map { GetParam($_, ''); } qw(num num regexp mode offset search));
|
||||
print $q->end_div();
|
||||
PrintFooter();
|
||||
}
|
||||
@@ -829,9 +835,10 @@ sub JournalSort { $b cmp $a }
|
||||
sub PrintJournal {
|
||||
return if $CollectingJournal; # avoid infinite loops
|
||||
local $CollectingJournal = 1;
|
||||
my ($num, $regexp, $mode, $offset, $search) = @_;
|
||||
my ($num, $numMore, $regexp, $mode, $offset, $search) = @_;
|
||||
$regexp = '^\d\d\d\d-\d\d-\d\d' unless $regexp;
|
||||
$num = 10 unless $num;
|
||||
$numMore = $num unless $numMore;
|
||||
$offset = 0 unless $offset;
|
||||
# FIXME: Should pass filtered list of pages to SearchTitleAndBody to save time?
|
||||
my @pages = sort JournalSort (grep(/$regexp/, $search ? SearchTitleAndBody($search) : AllPagesList()));
|
||||
@@ -860,7 +867,9 @@ sub PrintJournal {
|
||||
print $q->start_div({-class=>'journal'});
|
||||
my $next = $offset + PrintAllPages(1, 1, $num, @pages[$offset .. $#pages]);
|
||||
print $q->end_div();
|
||||
print $q->p({-class=>'more'}, ScriptLink("action=more;num=$num;regexp=$regexp;search=$search;mode=$mode;offset=$next", T('More...'), 'more')) if $pages[$next];
|
||||
$regexp = UrlEncode($regexp);
|
||||
$search = UrlEncode($search);
|
||||
print $q->p({-class=>'more'}, ScriptLink("action=more;num=$numMore;regexp=$regexp;search=$search;mode=$mode;offset=$next", T('More...'), 'more')) if $pages[$next];
|
||||
}
|
||||
|
||||
sub PrintAllPages {
|
||||
@@ -880,17 +889,21 @@ sub PrintAllPages {
|
||||
$q->h1($links ? GetPageLink($id)
|
||||
: $q->a({-name=>$id}, UrlEncode(FreeToNormal($id))));
|
||||
PrintPageHtml();
|
||||
if ($comments and $id !~ /^$CommentsPrefix/o) {
|
||||
print $q->p({-class=>'comment'},
|
||||
GetPageLink($CommentsPrefix . $id,
|
||||
T('Comments on this page')));
|
||||
}
|
||||
PrintPageCommentsLink($id, $comments);
|
||||
print $q->end_div();
|
||||
$n++; # pages actually printed
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
sub PrintPageCommentsLink {
|
||||
my ($id, $comments) = @_;
|
||||
if ($comments and $CommentsPattern and $id !~ /$CommentsPattern/o) {
|
||||
print $q->p({-class=>'comment'},
|
||||
GetPageLink($CommentsPrefix . $id, T('Comments on this page')));
|
||||
}
|
||||
}
|
||||
|
||||
sub RSS {
|
||||
return if $CollectingJournal; # avoid infinite loops when using full=1
|
||||
local $CollectingJournal = 1;
|
||||
@@ -1380,7 +1393,7 @@ sub BrowseResolvedPage {
|
||||
print $q->redirect({-uri=>$resolved});
|
||||
} elsif ($class && $class eq 'alias') { # an anchor was found instead of a page
|
||||
ReBrowsePage($resolved);
|
||||
} elsif (not $resolved and $NotFoundPg and $id !~ /^$CommentsPrefix/o) { # custom page-not-found message
|
||||
} elsif (not $resolved and $NotFoundPg and $id !~ /$CommentsPattern/o) { # custom page-not-found message
|
||||
BrowsePage($NotFoundPg);
|
||||
} elsif ($resolved) { # an existing page was found
|
||||
BrowsePage($resolved, GetParam('raw', 0));
|
||||
@@ -1942,7 +1955,7 @@ sub RssItem {
|
||||
$rss .= "<description>" . QuoteHtml($summary) . "</description>\n" if $summary;
|
||||
$rss .= "<pubDate>" . $date . "</pubDate>\n";
|
||||
$rss .= "<comments>" . ScriptUrl($CommentsPrefix . UrlEncode($id))
|
||||
. "</comments>\n" if $CommentsPrefix and $id !~ /^$CommentsPrefix/o;
|
||||
. "</comments>\n" if $CommentsPattern and $id !~ /$CommentsPattern/o;
|
||||
$rss .= "<dc:contributor>" . $username . "</dc:contributor>\n" if $username;
|
||||
$rss .= "<wiki:status>" . (1 == $revision ? 'new' : 'updated')
|
||||
. "</wiki:status>\n";
|
||||
@@ -2100,7 +2113,7 @@ sub DoRollback {
|
||||
} elsif (not UserIsEditor() and my $rule = BannedContent($text)) {
|
||||
print Ts('Rollback of %s would restore banned content.', $id), $rule, $q->br();
|
||||
} else {
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne $ENV{REMOTE_ADDR}));
|
||||
Save($id, $text, Ts('Rollback to %s', TimeToText($to)), $minor, ($Page{ip} ne GetRemoteAddress()));
|
||||
print Ts('%s rolled back', GetPageLink($id)), ($ts ? ' ' . Ts('to %s', TimeToText($to)) : ''), $q->br();
|
||||
}
|
||||
}
|
||||
@@ -2201,6 +2214,10 @@ sub ScriptLinkDiff {
|
||||
return ScriptLink($action, $text, 'diff');
|
||||
}
|
||||
|
||||
sub GetRemoteAddress {
|
||||
return $ENV{REMOTE_ADDR};
|
||||
}
|
||||
|
||||
sub GetAuthor {
|
||||
my ($host, $username) = @_;
|
||||
return $username . ' ' . Ts('from %s', $host) if $username and $host;
|
||||
@@ -2428,8 +2445,8 @@ sub GetFooterLinks {
|
||||
my ($id, $rev) = @_;
|
||||
my @elements;
|
||||
if ($id and $rev ne 'history' and $rev ne 'edit') {
|
||||
if ($CommentsPrefix) {
|
||||
if ($id =~ /^$CommentsPrefix(.*)/o) {
|
||||
if ($CommentsPattern) {
|
||||
if ($id =~ /$CommentsPattern/o) {
|
||||
push(@elements, GetPageLink($1, undef, 'original', T('a')));
|
||||
} else {
|
||||
push(@elements, GetPageLink($CommentsPrefix . $id, undef, 'comment', T('c')));
|
||||
@@ -2460,11 +2477,11 @@ sub GetFooterLinks {
|
||||
|
||||
sub GetCommentForm {
|
||||
my ($id, $rev, $comment) = @_;
|
||||
if ($CommentsPrefix ne '' and $id and $rev ne 'history' and $rev ne 'edit'
|
||||
and $id =~ /^$CommentsPrefix/o and UserCanEdit($id, 0, 1)) {
|
||||
if ($CommentsPattern ne '' and $id and $rev ne 'history' and $rev ne 'edit'
|
||||
and $id =~ /$CommentsPattern/o and UserCanEdit($id, 0, 1)) {
|
||||
return $q->div({-class=>'comment'}, GetFormStart(undef, undef, 'comment'), # protected by questionasker
|
||||
$q->p(GetHiddenValue('title', $id),
|
||||
GetTextArea('aftertext', $comment ? $comment : $NewComment, 10)), $EditNote,
|
||||
$q->p(GetHiddenValue('title', $id), $q->label({-for=>'aftertext', -accesskey=>T('c')}, $NewComment),
|
||||
$q->br(), GetTextArea('aftertext', $comment, 10)), $EditNote,
|
||||
$q->p($q->span({-class=>'username'},
|
||||
$q->label({-for=>'username'}, T('Username:')), ' ',
|
||||
$q->textfield(-name=>'username', -id=>'username',
|
||||
@@ -2706,7 +2723,6 @@ sub OpenPage { # Sets global variables
|
||||
local $/ = undef;
|
||||
$Page{text} = <F>;
|
||||
close F;
|
||||
} elsif ($CommentsPrefix and $id =~ /^$CommentsPrefix(.*)/o) { # do nothing
|
||||
}
|
||||
}
|
||||
$OpenPageName = $id;
|
||||
@@ -2758,17 +2774,17 @@ sub GetKeptRevision { # Call after OpenPage
|
||||
|
||||
sub GetPageFile {
|
||||
my ($id) = @_;
|
||||
return $PageDir . '/' . GetPageDirectory($id) . "/$id.pg";
|
||||
return "$PageDir/$id.pg";
|
||||
}
|
||||
|
||||
sub GetKeepFile {
|
||||
my ($id, $revision) = @_; die "No revision for $id" unless $revision; #FIXME
|
||||
return $KeepDir . '/' . GetPageDirectory($id) . "/$id/$revision.kp";
|
||||
return "$KeepDir/$id/$revision.kp";
|
||||
}
|
||||
|
||||
sub GetKeepDir {
|
||||
my $id = shift; die 'No id' unless $id; #FIXME
|
||||
return $KeepDir . '/' . GetPageDirectory($id) . '/' . $id;
|
||||
return "$KeepDir/$id";
|
||||
}
|
||||
|
||||
sub GetKeepFiles {
|
||||
@@ -2779,19 +2795,11 @@ sub GetKeepRevisions {
|
||||
return sort {$b <=> $a} map { m/([0-9]+)\.kp$/; $1; } GetKeepFiles(shift);
|
||||
}
|
||||
|
||||
sub GetPageDirectory {
|
||||
my $id = shift;
|
||||
if ($id =~ /^([a-zA-Z])/) {
|
||||
return uc($1);
|
||||
}
|
||||
return 'other';
|
||||
}
|
||||
|
||||
# Always call SavePage within a lock.
|
||||
sub SavePage { # updating the cache will not change timestamp and revision!
|
||||
ReportError(T('Cannot save a nameless page.'), '400 BAD REQUEST', 1) unless $OpenPageName;
|
||||
ReportError(T('Cannot save a page without revision.'), '400 BAD REQUEST', 1) unless $Page{revision};
|
||||
CreatePageDir($PageDir, $OpenPageName);
|
||||
CreateDir($PageDir);
|
||||
WriteStringToFile(GetPageFile($OpenPageName), EncodePage(%Page));
|
||||
}
|
||||
|
||||
@@ -2802,7 +2810,8 @@ sub SaveKeepFile {
|
||||
delete $Page{'diff-major'};
|
||||
delete $Page{'diff-minor'};
|
||||
$Page{'keep-ts'} = $Now; # expire only $KeepDays from $Now!
|
||||
CreateKeepDir($KeepDir, $OpenPageName);
|
||||
CreateDir($KeepDir);
|
||||
CreateDir("$KeepDir/$OpenPageName");
|
||||
WriteStringToFile(GetKeepFile($OpenPageName, $Page{revision}), EncodePage(%Page));
|
||||
}
|
||||
|
||||
@@ -2877,21 +2886,9 @@ sub CreateDir {
|
||||
or ReportError(Ts('Cannot create %s', $newdir) . ": $!", '500 INTERNAL SERVER ERROR');
|
||||
}
|
||||
|
||||
sub CreatePageDir {
|
||||
my ($dir, $id) = @_;
|
||||
CreateDir($dir);
|
||||
CreateDir($dir . '/' . GetPageDirectory($id));
|
||||
}
|
||||
|
||||
sub CreateKeepDir {
|
||||
my ($dir, $id) = @_;
|
||||
CreatePageDir($dir, $id);
|
||||
CreateDir($dir . '/' . GetPageDirectory($id) . '/' . $id);
|
||||
}
|
||||
|
||||
sub GetLockedPageFile {
|
||||
my $id = shift;
|
||||
return $PageDir . '/' . GetPageDirectory($id) . "/$id.lck";
|
||||
return "$PageDir/$id.lck";
|
||||
}
|
||||
|
||||
sub RequestLockDir {
|
||||
@@ -3017,13 +3014,13 @@ sub GetHiddenValue {
|
||||
|
||||
sub GetRemoteHost { # when testing, these variables are undefined.
|
||||
my $rhost = $ENV{REMOTE_HOST}; # tests are written to avoid -w warnings.
|
||||
if (not $rhost and $UseLookup and $ENV{REMOTE_ADDR}) {
|
||||
if (not $rhost and $UseLookup and GetRemoteAddress()) {
|
||||
# Catch errors (including bad input) without aborting the script
|
||||
eval 'use Socket; my $iaddr = inet_aton($ENV{REMOTE_ADDR});'
|
||||
eval 'use Socket; my $iaddr = inet_aton(GetRemoteAddress());'
|
||||
. '$rhost = gethostbyaddr($iaddr, AF_INET) if $iaddr;';
|
||||
}
|
||||
if (not $rhost) {
|
||||
$rhost = $ENV{REMOTE_ADDR};
|
||||
$rhost = GetRemoteAddress();
|
||||
}
|
||||
return $rhost;
|
||||
}
|
||||
@@ -3032,11 +3029,9 @@ sub FreeToNormal { # trim all spaces and convert them to underlines
|
||||
my $id = shift;
|
||||
return '' unless $id;
|
||||
$id =~ s/ /_/g;
|
||||
if (index($id, '_') > -1) { # Quick check for any space/underscores
|
||||
$id =~ s/__+/_/g;
|
||||
$id =~ s/^_//;
|
||||
$id =~ s/_$//;
|
||||
}
|
||||
$id =~ s/__+/_/g;
|
||||
$id =~ s/^_//;
|
||||
$id =~ s/_$//;
|
||||
return UnquoteHtml($id);
|
||||
}
|
||||
|
||||
@@ -3221,7 +3216,7 @@ sub UserCanEdit {
|
||||
return 1 if UserIsEditor();
|
||||
return 0 if !$EditAllowed or -f $NoEditFile;
|
||||
return 0 if $editing and UserIsBanned(); # this call is more expensive
|
||||
return 0 if $EditAllowed >= 2 and (not $CommentsPrefix or $id !~ /^$CommentsPrefix/o);
|
||||
return 0 if $EditAllowed >= 2 and (not $CommentsPattern or $id !~ /$CommentsPattern/o);
|
||||
return 1 if $EditAllowed >= 3 and ($comment or (GetParam('aftertext', '') and not GetParam('text', '')));
|
||||
return 0 if $EditAllowed >= 3;
|
||||
return 1;
|
||||
@@ -3230,7 +3225,7 @@ sub UserCanEdit {
|
||||
sub UserIsBanned {
|
||||
return 0 if GetParam('action', '') eq 'password'; # login is always ok
|
||||
my ($host, $ip);
|
||||
$ip = $ENV{'REMOTE_ADDR'};
|
||||
$ip = GetRemoteAddress();
|
||||
$host = GetRemoteHost();
|
||||
foreach (split(/\n/, GetPageContent($BannedHosts))) {
|
||||
if (/^\s*([^#]\S+)/) { # all lines except empty lines and comments, trim whitespace
|
||||
@@ -3243,19 +3238,22 @@ sub UserIsBanned {
|
||||
}
|
||||
|
||||
sub UserIsAdmin {
|
||||
return 0 if $AdminPass eq '';
|
||||
my $pwd = GetParam('pwd', '');
|
||||
foreach (split(/\s+/, $AdminPass)) {
|
||||
return 1 if $pwd eq $_;
|
||||
}
|
||||
return 0;
|
||||
return UserHasPassword(GetParam('pwd', ''), $AdminPass);
|
||||
}
|
||||
|
||||
sub UserIsEditor {
|
||||
return 1 if UserIsAdmin(); # Admin includes editor
|
||||
return 0 if $EditPass eq '';
|
||||
my $pwd = GetParam('pwd', ''); # Used for both passwords
|
||||
foreach (split(/\s+/, $EditPass)) {
|
||||
return UserHasPassword(GetParam('pwd', ''), $EditPass);
|
||||
}
|
||||
|
||||
sub UserHasPassword {
|
||||
my ($pwd, $pass) = @_;
|
||||
return 0 if not $pass;
|
||||
if ($PassHashFunction ne '') {
|
||||
no strict 'refs';
|
||||
$pwd = &$PassHashFunction($pwd . $PassSalt);
|
||||
}
|
||||
foreach (split(/\s+/, $pass)) {
|
||||
return 1 if $pwd eq $_;
|
||||
}
|
||||
return 0;
|
||||
@@ -3338,7 +3336,7 @@ sub AllPagesList {
|
||||
if (not $refresh and -f $IndexFile) {
|
||||
my ($status, $rawIndex) = ReadFile($IndexFile); # not fatal
|
||||
if ($status) {
|
||||
%IndexHash = split(/\s+/, $rawIndex);
|
||||
%IndexHash = split(/ /, $rawIndex);
|
||||
@IndexList = sort(keys %IndexHash);
|
||||
return @IndexList;
|
||||
}
|
||||
@@ -3348,7 +3346,7 @@ sub AllPagesList {
|
||||
%IndexHash = ();
|
||||
# If file exists and cannot be changed, error!
|
||||
my $locked = RequestLockDir('index', undef, undef, -f $IndexFile);
|
||||
foreach (bsd_glob("$PageDir/*/*.pg"), bsd_glob("$PageDir/*/.*.pg")) {
|
||||
foreach (bsd_glob("$PageDir/*.pg"), bsd_glob("$PageDir/.*.pg")) {
|
||||
next unless m|/.*/(.+)\.pg$|;
|
||||
my $id = $1;
|
||||
utf8::decode($id);
|
||||
@@ -3570,7 +3568,7 @@ sub Replace {
|
||||
if (eval "s{$from}{$to}gi") { # allows use of backreferences
|
||||
push (@result, $id);
|
||||
Save($id, $_, $from . ' -> ' . $to, 1,
|
||||
($Page{ip} ne $ENV{REMOTE_ADDR}));
|
||||
($Page{ip} ne GetRemoteAddress()));
|
||||
}
|
||||
}
|
||||
ReleaseLock();
|
||||
@@ -3649,7 +3647,7 @@ sub DoPost {
|
||||
if ($oldrev) { # the first author (no old revision) is not considered to be "new"
|
||||
# prefer usernames for potential new author detection
|
||||
$newAuthor = 1 if not $Page{username} or $Page{username} ne GetParam('username', '');
|
||||
$newAuthor = 1 if not $ENV{REMOTE_ADDR} or not $Page{ip} or $ENV{REMOTE_ADDR} ne $Page{ip};
|
||||
$newAuthor = 1 if not GetRemoteAddress() or not $Page{ip} or GetRemoteAddress() ne $Page{ip};
|
||||
}
|
||||
my $oldtime = $Page{ts};
|
||||
my $myoldtime = GetParam('oldtime', ''); # maybe empty!
|
||||
@@ -3711,7 +3709,7 @@ sub AddComment {
|
||||
my $author = GetParam('username', T('Anonymous'));
|
||||
my $homepage = GetParam('homepage', '');
|
||||
$homepage = 'http://' . $homepage
|
||||
if $homepage and not substr($homepage,0,7) eq 'http://';
|
||||
if $homepage and $homepage !~ /^($UrlProtocols):/;
|
||||
$author = "[$homepage $author]" if $homepage;
|
||||
$string .= "\n----\n\n" if $string and $string ne "\n";
|
||||
$string .= $comment . "\n\n"
|
||||
@@ -3742,7 +3740,7 @@ sub Save { # call within lock, with opened page
|
||||
$Page{revision} = $revision;
|
||||
$Page{summary} = $summary;
|
||||
$Page{username} = $user;
|
||||
$Page{ip} = $ENV{REMOTE_ADDR};
|
||||
$Page{ip} = GetRemoteAddress();
|
||||
$Page{host} = $host;
|
||||
$Page{minor} = $minor;
|
||||
$Page{text} = $new;
|
||||
@@ -3967,7 +3965,7 @@ sub DoDebug {
|
||||
sub DoSurgeProtection {
|
||||
return unless $SurgeProtection;
|
||||
my $name = GetParam('username','');
|
||||
$name = $ENV{'REMOTE_ADDR'} if not $name and $SurgeProtection;
|
||||
$name = GetRemoteAddress() if not $name and $SurgeProtection;
|
||||
return unless $name;
|
||||
ReadRecentVisitors();
|
||||
AddRecentVisitor($name);
|
||||
|
||||
Reference in New Issue
Block a user