Compare commits

..

29 Commits
2.2.6 ... 2.3.0

Author SHA1 Message Date
Alex Schroeder
25989f78a5 Merge branch 'as/no-more-page-subdirectories' 2014-06-21 21:30:27 +02:00
Aki Goto
afdb7a9dcb form_timeout.pl: an anti-spam module for Oddmuse
This is an anti-spam module for Oddmuse using form timeout. Edit
permission times out after a specified duration (the default is 30
minutes). When edit content is posted directly by a spam bot without
viewing the edit form, edit will be denied.
2014-06-21 12:18:26 +02:00
Alex Schroeder
2e79a843c8 oddmuse-curl.el: Fix URL 2014-06-21 12:01:08 +02:00
Alex Schroeder
54370da235 oddmuse-curl.el: whitespace 2014-06-21 12:00:26 +02:00
Alex Schroeder
43839ac1aa upgrade.t: Testing for the changes to upgrade.pl 2014-06-21 10:45:21 +02:00
Alex Schroeder
3c2f96250b upgrade.pl: Use variables; print utf8.
Instead of fiddling with @MyInitVariables, set the options naming
pages to the empty string.
2014-06-21 04:44:33 -04:00
Alex Schroeder
9def2d2eb2 upgrade.pl: Disable some module initialisations
Some modules read a page file when initializing. As these cannot be
found before the upgrade, we need to disable them.
2014-06-21 04:18:16 -04:00
Alex Schroeder
3ad40b84fb .gitignore: .DS_Store 2014-06-20 22:39:37 +02:00
Alex Schroeder
ecda4c3d98 oddmuse-curl.el: Preview shows just the preview
Don't show the header, footer and textarea; show just the preview div.
2014-06-20 22:36:09 +02:00
Alex Schroeder
74a0576c5d oddmuse-curl.el: Font lock restructuring
All the font-locking functions are now modifying font-lock-defaults
instead of calling font-lock-add-keywords because that's what it says
in the elisp manual.
2014-06-20 21:23:30 +02:00
Alex Schroeder
a6e07a9886 oddmuse-curl.el: Preview command
The preview command uses shr, a built in HTML renderer.
2014-06-20 18:31:33 +02:00
Alex Schroeder
b76b61dc86 .gitignore: .DS_Store 2014-06-19 23:11:04 +02:00
Alex Schroeder
c3cb434973 upgrade.pl: handle name spaces 2014-06-19 23:00:18 +02:00
Alex Schroeder
62f82c2af2 upgrade.pl: New interface.
Don't rely on a separate upgrade action. Any request except for login
and unlock will trigger an upgrade, if you're an admin. Once the upgrade
is complete, the module will rename itself such that it will no longer
load.
2014-06-17 13:14:13 +02:00
Alex Schroeder
d454973294 oddmuse-curl.el: fix order of font-locking
oddmuse-basic-markup must come last (which prepends its rules to the
front) such that links get precedence over other markup.
2014-06-17 12:26:50 +02:00
Alex Schroeder
cba29c8981 oddmuse-curl.el: added oddmuse-new
A function to make it easy to blog by offering the current ISO date as
page name.
2014-06-17 10:39:05 +02:00
Alex Schroeder
4a812931c8 oddmuse-curl.el: do not break links
Added a fill-nobreak-predicate to prevent links from breaking. This uses
the face property and checks for the link face.
2014-06-17 10:37:57 +02:00
Alex-Daniel Jakimenko
093a6da63d askpage.pl: Don't keep old value of $NewComment
Resetting the value was dead code. Instead of fixing this, the old value
is now discarded. There is no point in keeping it around.
2014-06-16 15:40:13 +02:00
Aki Goto
0ab5261bc6 load-lang.pl: add $LoadLanguageDir
The new option $LoadLanguageDir specifies a directory for the language
files.
2014-06-16 15:33:53 +02:00
Alex Schroeder
1d4f3e4a28 Ask Page extension
This is a simple extension to create a page for asking questions. The
process of writing a question is similar to writing a comment, for the
only exception that after pressing the ‘Save’ button the user will be
redirected to the newly created page containing his question.
2014-06-16 11:25:13 +02:00
Alex Jakimenko
6babcffd00 Consistency in file permissions.
Some modules are executable where as others are not. This patch fixes
this.
2014-06-16 11:19:24 +02:00
Alex Jakimenko
977cbba251 Fix two issues with $NewComment.
Display $NewComment above comment textarea.

'c' is now an access key to focus comment textarea.
2014-06-16 11:06:20 +02:00
Alex Schroeder
2fc4f4b054 Merge branch 'master' of git.sv.gnu.org:/srv/git/oddmuse into as/no-more-page-subdirectories 2014-06-16 10:32:31 +02:00
Alex Schroeder
53566c8434 Use a function instead of $ENV{REMOTE_ADDR}
When the webserver is behind a reverse proxy, $ENV{REMOTE_ADDR} is not
the actual remote client's address but the reverse proxy's address. The
actual remote client's address is available from an environment
variable. The name of this variable depends on the proxy, e.g. pound
uses $ENV{HTTP_X_FORWARDED_FOR}.

As suggested by tyatsumi on the wiki, all access $ENV{REMOTE_ADDR} now
happens via a new function which allows users to override it in their
config file.
2014-06-16 09:50:05 +02:00
Alex Schroeder
563e5cd9c6 upgrade.pl: delete empty directories after upgrade 2014-06-07 17:08:18 +02:00
Alex Schroeder
365d33b602 Get rid of one letter sub-directories.
Recent GNU/Linux systems use ext3 or ext4 file systems. These use HTree
to index files. Wikipedia says: "HTree indexing improved the scalability
of Linux ext2 based filesystems from a practical limit of a few thousand
files, into the range of tens of millions of files per directory. [...]
HTree indexes are available in ext3 when the dir_index feature is
enabled. [...] HTree indexes are turned on by default in ext4."

Thus, instead of working on balanced-page-directories.pl, we decided to
get rid of these sub-directories altogether.

Unfortunately, this is backwards incompatible. Users wanting to upgrade
will need to install the upgrade.pl extension in order to upgrade the
file database.
2014-06-06 17:32:44 +02:00
Alex Schroeder
eef56e435d questionasker.t: fixed number of tests 2014-06-05 17:07:15 +02:00
Alex Schroeder
2044564981 Merge branch 'master' of ssh://git.sv.gnu.org/srv/git/oddmuse 2014-06-05 05:34:08 -04:00
Alex Schroeder
7f74d3c211 Use smaller headers 2014-04-09 08:34:14 -04:00
36 changed files with 4767 additions and 154 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
/Mac/pkg/
*.dmg
*.pkg
.DS_Store

View File

@@ -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)))))

View File

@@ -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
View File

0
modules/antispam.pl Executable file → Normal file
View File

64
modules/askpage.pl Normal file
View 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
View File

View 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

View File

@@ -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
View File

0
modules/clustermap.pl Executable file → Normal file
View File

0
modules/crossbar.pl Executable file → Normal file
View File

106
modules/form_timeout.pl Normal file
View 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
View File

0
modules/htmllinks.pl Executable file → Normal file
View File

View 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);

View File

@@ -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.

View File

@@ -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
View 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");
}

View File

@@ -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
View File

View 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')
);

View File

@@ -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
View File

0
modules/slideshow.pl Executable file → Normal file
View File

View 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();
}

View File

@@ -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 或是网络已被禁止连接

View File

@@ -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
View 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();
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2008 Alex Schroeder <alex@gnu.org>
# Copyright (C) 20082014 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');

View File

@@ -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
View 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');

View File

@@ -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
View File

@@ -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);