forked from github/kensanata.oddmuse
Compare commits
29 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 | ||
|
|
7f74d3c211 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
||||
/Mac/pkg/
|
||||
*.dmg
|
||||
*.pkg
|
||||
.DS_Store
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
;; Latest version:
|
||||
;; http://git.savannah.gnu.org/cgit/oddmuse.git/plain/contrib/oddmuse-curl.el
|
||||
;; Discussion, feedback:
|
||||
;; http://www.emacswiki.org/cgi-bin/wiki/OddmuseCurl
|
||||
;; 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,8 +42,10 @@
|
||||
(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.d/oddmuse"
|
||||
"Directory to store oddmuse pages."
|
||||
@@ -164,7 +166,8 @@ It must print the RSS 3.0 text format to stdout.
|
||||
" --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
|
||||
@@ -176,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.")
|
||||
@@ -212,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")))
|
||||
@@ -274,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.
|
||||
@@ -329,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
|
||||
@@ -347,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)
|
||||
@@ -456,12 +556,25 @@ Use a prefix argument to force a reload of the page."
|
||||
(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
|
||||
@@ -505,31 +618,42 @@ and call `oddmuse-edit' on it."
|
||||
;; (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."
|
||||
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)
|
||||
@@ -556,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.
|
||||
@@ -611,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)))))
|
||||
|
||||
|
||||
@@ -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
@@ -15,14 +15,20 @@
|
||||
|
||||
=head1 Balanced Page Directories
|
||||
|
||||
By default, Oddmuse disperses page data files into 27 directories
|
||||
based on the first character of the page name. The directories are "A"
|
||||
to "Z", and "other". If you use your wiki as a blog, all the pages
|
||||
starting with a date end up in "other". If your page names start with
|
||||
letters other than "A" to "Z", all the pages end up in "other". If you
|
||||
are using comment pages, all your comment pages end in "C". This can
|
||||
turn into a problem if you reach ten thousand pages and more in a
|
||||
single directory.
|
||||
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
|
||||
|
||||
|
||||
@@ -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
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);
|
||||
|
||||
@@ -129,12 +129,12 @@ sub JoinerRequestLockOrError {
|
||||
|
||||
sub JoinerGetEmailFile {
|
||||
my ($email) = @_;
|
||||
return $JoinerEmailDir . '/' . GetPageDirectory($email) . "/$email.email";
|
||||
return "$JoinerEmailDir/$email.email";
|
||||
}
|
||||
|
||||
sub JoinerGetAccountFile {
|
||||
my ($username) = @_;
|
||||
return $JoinerDir . '/' . GetPageDirectory($username) . "/$username.account";
|
||||
return "$JoinerDir/$username.account";
|
||||
}
|
||||
|
||||
# Always call JoinerCreateAccount within a lock.
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ $CategoriesPage = '日志类别';
|
||||
%Translate = split(/\n/,<<END_OF_TRANSLATION);
|
||||
This page is empty.
|
||||
|
||||
Add your comment here.
|
||||
Add your comment here:
|
||||
|
||||
Reading not allowed: user, ip, or network is blocked.
|
||||
禁止读取:用户、IP 或是网络已被禁止连接。
|
||||
|
||||
@@ -20,7 +20,7 @@ $ModulesDescription .= '<p>japanese-utf8.pl</p>';
|
||||
%Translate = split(/\n/,<<END_OF_TRANSLATION);
|
||||
This page is empty.
|
||||
このページは空です。
|
||||
Add your comment here.
|
||||
Add your comment here:
|
||||
ここにコメントを追加してください。
|
||||
Reading not allowed: user, ip, or network is blocked.
|
||||
閲覧は許可されません: ユーザ、IP、またはネットワークがブロックされています。
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
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');
|
||||
|
||||
@@ -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 $_;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
63
wiki.pl
63
wiki.pl
@@ -97,7 +97,7 @@ $LogoUrl = ''; # URL for site logo ('' for no logo)
|
||||
$NotFoundPg = ''; # Page for not-found links ('' for blank pg)
|
||||
|
||||
$NewText = T('This page is empty.') . "\n"; # New page text
|
||||
$NewComment = T('Add your comment here.') . "\n"; # New comment 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.
|
||||
@@ -2113,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();
|
||||
}
|
||||
}
|
||||
@@ -2214,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;
|
||||
@@ -2476,8 +2480,8 @@ sub GetCommentForm {
|
||||
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',
|
||||
@@ -2770,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 {
|
||||
@@ -2791,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));
|
||||
}
|
||||
|
||||
@@ -2814,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));
|
||||
}
|
||||
|
||||
@@ -2889,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 {
|
||||
@@ -3029,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;
|
||||
}
|
||||
@@ -3240,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
|
||||
@@ -3361,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);
|
||||
@@ -3583,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();
|
||||
@@ -3662,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!
|
||||
@@ -3755,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;
|
||||
@@ -3980,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