tinycm view index.cgi @ rev 103
Small fixes
author | Christophe Lincoln <pankso@slitaz.org> |
---|---|
date | Mon Feb 20 18:29:02 2017 +0100 (2017-02-20) |
parents | 69214a677729 |
children | 348289f0b48d |
line source
1 #!/bin/sh
2 #
3 # TinyCM - Small, fast and elegant CGI/SHell Content Manager
4 #
5 # Copyright (C) 2012-2017 SliTaz GNU/Linux - BSD License
6 #
7 . /usr/lib/slitaz/httphelper.sh
9 # Let's have a peer site config file with a .cgi extension so content
10 # is secure even if left in a web server directory.
11 . ./config.cgi
13 tiny="$PWD"
14 content="content"
15 wiki="$content/wiki"
16 index="index"
17 cache="cache"
18 plugins="plugins"
19 tmp="/tmp/tinycm"
20 sessions="$tmp/sessions"
21 script="$SCRIPT_NAME"
22 activity="$cache/log/activity.log"
24 # Content negotiation for Gettext
25 IFS=","
26 for lang in $HTTP_ACCEPT_LANGUAGE
27 do
28 lang=${lang%;*} lang=${lang# } lang=${lang%-*}
29 case "$lang" in
30 en) lang="C" && break ;;
31 fr) lang="fr_FR" && break ;;
32 pt) lang="pt_BR" && break ;;
33 ru) lang="ru_RU" && break ;;
34 esac
35 done
36 unset IFS
37 export LANG=$lang LC_ALL=$lang
39 # Internationalization
40 . /usr/bin/gettext.sh
41 TEXTDOMAIN='tinycm'
42 export TEXTDOMAIN
44 #
45 # Functions
46 #
48 # Used by edit to display language name and the language box. This is
49 # for CM content not gettext support.
50 get_lang() {
51 dlang=$(echo $d | cut -d "/" -f 1)
52 doc=${d#$dlang/}
53 echo '<div id="lang">'
54 for l in $LANGUAGES
55 do
56 case $dlang in
57 en) i18n="English" ;;
58 fr) i18n="Français" ;;
59 pt) i18n="Português" ;;
60 ru) i18n="Русский" ;;
61 *) i18n="*" ;;
62 esac
63 echo "<a href='?d=$l/$doc'>$l</a>"
64 done
65 echo '</div>'
66 }
68 # HTML 5 header.
69 html_header() {
70 if [ -f "$tiny/lib/header.html" ]; then
71 cat $tiny/lib/header.html | sed -e s!'%TITLE%'!"$TITLE - $d"!g
72 else
73 cat << EOT
74 <!DOCTYPE html>
75 <html xmlns="http://www.w3.org/1999/xhtml">
76 <head>
77 <title>$TITLE</title>
78 <meta charset="utf-8" />
79 <style type="text/css">body { margin: 40px 120px; }</style>
80 </head>
81 <body>
82 <!-- Content -->
83 <div id="content">
84 EOT
85 fi
86 }
88 # HTML 5 footer.
89 html_footer() {
90 if [ -f "$tiny/lib/footer.html" ]; then
91 cat $tiny/lib/footer.html
92 else
93 cat << EOT
95 <!-- End content -->
96 </div>
98 <div id="footer">
99 I ♥ <a href="http://tinycm.slitaz.org/">TinyCM</a>
100 </div>
102 </body>
103 </html>
104 EOT
105 fi
106 }
108 # Default index if missing
109 default_index() {
110 mkdir -p "$wiki"
111 cat > $wiki/$index.txt << EOT
112 ==== Welcome ====
114 <p>
115 This is the default index page of your TinyCM, you can login then start to
116 edit and add some content. You can read the help about text formating
117 and functions: [Help page|en/help]
118 </p>
120 EOT
121 }
123 # Log main activity.
124 log_activity() {
125 [ -d "$cache/log" ] || mkdir -p ${cache}/log
126 #gravatar="$(get_gravatar $MAIL 24)"
127 grep ^[A-Z] | \
128 sed s"#^[A-Z]\([^']*\)#$user|$(date '+%Y-%m-%d')|\0#" \
129 >> $cache/log/activity.log
130 }
132 # Log documents activity.
133 log() {
134 grep ^[A-Z] | \
135 sed s"#^[A-Z]\([^']*\)#$(date '+%Y-%m-%d %H:%M') : \0#" \
136 >> $cache/$d/activity.log
137 }
139 # Check if user is auth
140 check_auth() {
141 auth="$(COOKIE auth)"
142 user="$(echo $auth | cut -d ":" -f 1)"
143 md5cookie="$(echo $auth | cut -d ":" -f 2)"
144 [ -f "$sessions/$user" ] && md5session="$(cat $sessions/$user)"
145 if [ "$md5cookie" == "$md5session" ] && [ "$auth" ]; then
146 . $PEOPLE/$user/account.conf
147 return 0
148 else
149 return 1
150 fi
151 }
153 # Check if user is admin
154 admin_user() {
155 fgrep -w -q "$user" ${ADMIN_USERS}
156 }
158 # Authenticated or not
159 user_box() {
160 if check_auth; then
161 cat << EOT
163 <div id="user">
164 <a href="$script?user=$user">$(get_gravatar $MAIL 20)</a>
165 <a href="$script?logout">Logout</a>
166 </div>
168 EOT
169 else
170 cat << EOT
172 <div id="user">
173 <a href="$script?login"><img src="images/avatar.png" alt="[ User ]" /></a>
174 <a href="$script?login">Login</a>
175 </div>
177 EOT
178 fi
179 cat << EOT
180 <!--
181 <div id="search">
182 <form method="get" action="$script">
183 <input type="text" name="search" placeholder="$(gettext "Search")" />
184 </form>
185 </div>
186 -->
187 EOT
188 }
190 # Link for online signup if enabled.
191 online_signup() {
192 if [ "$ONLINE_SIGNUP" == "yes" ]; then
193 echo -n "<p><a href='$script?signup'>"
194 gettext "Create a new account"
195 echo '</a></p>'
196 fi
197 }
199 # Login page
200 login_page() {
201 cat << EOT
202 <h2>$(gettext "Login")</h2>
204 <div id="account-info">
205 $(gettext "No account yet or trouble with your account? Please send
206 a request to $ADMIN_MAIL with your real name, user name, mail and password.")
207 $(online_signup)
208 </div>
210 <div id="login">
211 <form method="post" action="$script">
212 <input type="text" name="auth" placeholder="$(gettext "User name")" />
213 <input type="password" name="pass" placeholder="$(gettext "Password")" />
214 <div>
215 <input type="submit" value="Login" /> $error
216 </div>
217 </form>
218 </div>
220 <div style="clear: both;"></div>
221 EOT
222 }
224 # Signup page
225 signup_page() {
226 cat << EOT
228 <div id="signup">
229 <form method="post" name="signup" action="$script" onsubmit="return checkSignup();">
230 <input type="hidden" name="signup" value="new" />
231 <input type="text" name="name" placeholder="$(gettext "Real name")" />
232 <input type="text" name="user" placeholder="$(gettext "User name")" />
233 <input type="text" name="mail" placeholder="$(gettext "Email")" />
234 <input type="password" name="pass" placeholder="$(gettext "Password")" />
235 <div>
236 <input type="submit" value="$(gettext "Create new account")" />
237 </div>
238 </form>
239 </div>
241 EOT
242 }
244 # Create a new user in AUTH_FILE and PEOPLE
245 new_user_config() {
246 if [ ! -f "$AUTH_FILE" ];then
247 touch $AUTH_FILE
248 chmod 0600 $AUTH_FILE
249 fi
250 echo "$user:$pass" >> $AUTH_FILE
251 mkdir -p $PEOPLE/$user/
252 cat > $PEOPLE/$user/account.conf << EOT
253 # SliTaz user configuration
254 #
256 NAME="$name"
257 USER="$user"
258 MAIL="$mail"
260 EOT
261 chmod 0600 $PEOPLE/$user/account.conf
262 # First created user is admin
263 if [ $(ls ${PEOPLE} | wc -l) == "1" ]; then
264 echo 'ADMIN_USER="yes"' >> $PEOPLE/$user/account.conf
265 fi
266 }
268 # The CM style parser. Just a title, simple text formatting and internal
269 # links, as well as images and use HTML for other stuff. Keep it fast!
270 # To make TinyCM as easy as possible we have a small HTML editor/helper
271 # written in Javascript
272 wiki_parser() {
273 doc="[0-9a-zA-Z\.\#/~\_%=\?\&,\+\:@;!\(\)\*\$'\-]*"
274 sed \
275 -e s"#====\([^']*\)====#<h2>\1</h2>#"g \
276 -e s"#===\([^']*\)===#<h3>\1</h3>#"g \
277 -e s"#==\([^']*\)==#<h4>\1</h4>#"g \
278 -e s"#\*\*\([^']*\)\*\*#<b>\1</b>#"g \
279 -e s"#''\([^']*\)''#<em>\1</em>#"g \
280 -e s"#__\([^']*\)__#<u>\1</u>#"g \
281 -e s"#\[\([^]]*\)|\($doc\)\]#<a href='$script?d=\2'>\1</a>#"g \
282 -e s"#\[\([^]]*\)!\($doc\)\]#<a href='\2'>\1</a>#"g \
283 -e s"#\[\(http://*[^]]*.png\)\]#<img src='\1' />#"g \
284 -e s"#\[\([^]]*.png\)\]#<img src='content/cloud/\1' />#"g \
285 -e s"#@\([^']*\)@#<a href='$script?user=\1'>\1</a>#"g
286 }
288 link_user() {
289 echo "<a href='$(basename $script)?user=$user'>$user</a>"
290 }
292 # Save a document. Do we need more than 1 backup and diff ?
293 save_document() {
294 mkdir -p $cache/$d $(dirname $wiki/$d)
295 # May be a new page.
296 if [ ! -f "$wiki/$d.txt" ]; then
297 new=0
298 touch $wiki/$d.txt
299 fi
300 cp $wiki/$d.txt $cache/$d/last.bak
301 sed "s/$(echo -en '\r') /\n/g" > $wiki/$d.txt << EOT
302 $(GET content)
303 EOT
304 diff $cache/$d/last.bak $wiki/$d.txt > $cache/$d/last.diff
305 # Log
306 if [ "$new" ]; then
307 echo "Page created by: $(link_user)" | log
308 echo "New document: <a href='$script?d=$d'>$d</a>" | log_activity
309 if [ "$HG" == "yes" ]; then
310 cd $content && hg -q add
311 hg commit -q -u "$NAME <$MAIL>" -m "Created new document: $d"
312 cd $tiny
313 fi
314 else
315 # Here we may clean log: cat && tail -n 40
316 echo "Page edited by: $(link_user)" | log
317 if [ "$HG" == "yes" ]; then
318 cd $content && hg commit -q -u "$NAME <$MAIL>" \
319 -m "Edited document: $d"
320 cd $tiny
321 fi
322 fi
323 }
325 # CM tools (edit, diff, etc) for auth users
326 wiki_tools() {
327 if check_auth; then
328 cat << EOT
329 <div id="tools">
330 <a href="$script?edit=$d">$(gettext "Edit document")</a>
331 <a href="$script?log=$d">$(gettext "File log")</a>
332 <a href="$script?diff=$d">$(gettext "Last diff")</a>
333 $PLUGINS_TOOLS
334 EOT
335 [ "$HG" == "yes" ] && echo "<a href='$script?hg'>Hg Log</a>"
336 echo "</div>"
337 fi
338 }
340 # Built-in tools such as log/ls and PLUGINS_TOOLS
341 tiny_tools() {
342 if check_auth; then
343 cat << EOT
344 <div id='tools'>
345 <a href='$script?log'>Activity log</a>
346 <a href='$script?ls'>Pages list</a>
347 $PLUGINS_TOOLS
348 </div>
349 EOT
350 fi
351 }
353 # Get and display Gravatar image: get_gravatar email size
354 # Link to profile: <a href="http://www.gravatar.com/$md5">...</a>
355 get_gravatar() {
356 email=$1
357 size=$2
358 [ "$size" ] || size=48
359 url="http://www.gravatar.com/avatar"
360 md5=$(md5crypt $email)
361 echo "<img src='$url/$md5?d=identicon&s=$size' alt='∗' />"
362 }
364 # List hg logs
365 hg_log() {
366 cd $content
367 cat << EOT
368 <table>
369 <thead>
370 <td>$(gettext "User")</td>
371 <td>$(gettext "Description")</td>
372 <td>$(gettext "Revision")</td>
373 </thead>
374 EOT
375 hg log --template "<tr><td>{author}</td><td>{desc}</td><td>{rev}</td></tr>\n"
376 echo '</table>'
377 }
379 #
380 # POST actions
381 #
383 case " $(POST) " in
384 *\ auth\ *)
385 # Authenticate user. Create a session file in $sessions to be used
386 # by check_auth. We have the user login name and a peer session
387 # md5 string in the COOKIE.
388 user="$(POST auth)"
389 pass="$(md5crypt "$(POST pass)")"
390 valid=$(fgrep "${user}:" $AUTH_FILE | cut -d ":" -f 2)
391 if [ "$pass" == "$valid" ] && [ "$pass" != "" ]; then
392 md5session=$(echo -n "$$:$user:$pass:$$" | md5sum | awk '{print $1}')
393 [ -d $sessions ] || mkdir -p $sessions
394 date '+%Y-%m-%d' > ${PEOPLE}/${user}/last
395 echo "$md5session" > $sessions/$user
396 header "Location: $script" \
397 "Set-Cookie: auth=$user:$md5session; HttpOnly"
398 else
399 header "Location: $script?login&error"
400 fi ;;
401 *\ signup\ *)
402 # POST action for signup
403 name="$(POST name)"
404 user="$(POST user)"
405 mail="$(POST mail)"
406 pass="$(md5crypt "$(POST pass)")"
407 if ! grep "^${user}:" $AUTH_FILE; then
408 new_user_config
409 header "Location: $script?login"
410 else
411 header
412 html_header
413 user_box
414 echo "<h2>$(gettext 'User already exists:') $user</h2>"
415 html_footer
416 fi ;;
417 esac
419 #
420 # Plugins
421 #
422 for p in $(ls -1 $plugins)
423 do
424 [ -f "$plugins/$p/$p.conf" ] && . $plugins/$p/$p.conf
425 [ -x "$plugins/$p/$p.cgi" ] && . $plugins/$p/$p.cgi
426 done
428 #
429 # GET actions
430 #
432 case " $(GET) " in
433 *\ edit\ *)
434 d="$(GET edit)"
435 header
436 html_header
437 user_box
438 get_lang
439 wiki_tools
440 if check_auth; then
441 cat << EOT
442 <h2>$(gettext "Edit $doc [ $i18n ]")</h2>
444 <div id="edit">
446 <form method="get" action="$script" name="editor">
447 <input type="hidden" name="save" value="$d" />
448 <textarea name="content">$(cat "$wiki/$d.txt")</textarea>
449 <input type="submit" value="$(gettext "Save document")" />
450 $(gettext "Code Helper:")
451 $(cat lib/jseditor.html)
452 </form>
454 </div>
455 EOT
456 else
457 gettext "You must be logged in to edit pages"
458 fi
459 html_footer ;;
461 *\ save\ *)
462 d="$(GET save)"
463 if check_auth; then
464 save_document
465 fi
466 header "Location: $script?d=$d" ;;
468 *\ log\ *)
469 d="$(GET log)"
470 header
471 html_header
472 user_box
473 # Main activity
474 if [ "$d" == "log" ]; then
475 tiny_tools
476 echo "<h2>$(gettext "Activity log")</h2>"
477 echo '<pre>'
478 if [ -f "$cache/log/activity.log" ]; then
479 IFS="|"
480 tac $cache/log/activity.log | while read USER DATE LOG
481 do
482 . ${PEOPLE}/${USER}/account.conf
483 cat << EOT
484 <a href='$script?user=$USER'>$(get_gravatar $MAIL 24)</a>\
485 <span class='date'>$DATE -</span> $LOG
486 EOT
487 done
488 unset IFS
489 else
490 gettext "No activity log yet"; echo
491 fi
492 echo '</pre>'
493 html_footer && exit 0
494 fi
495 # Document activity
496 get_lang
497 wiki_tools
498 echo "<h2>$(gettext "Activity for:") <a href='$script?d=$d'>$d</a></h2>"
499 echo '<pre>'
500 if [ -f "$cache/$d/activity.log" ]; then
501 tac $cache/$d/activity.log
502 else
503 gettext "No log for: $d"; echo
504 fi
505 echo '</pre>'
506 html_footer ;;
508 *\ ls\ *)
509 d="Document list"
510 header
511 html_header
512 user_box
513 tiny_tools
514 [ ! check_auth ] && auth=0
515 echo "<h2>$(gettext "Pages list")</h2>"
516 echo '<pre>'
517 cd ${wiki}
518 for d in $(find . -type f | sed s'/.\///')
519 do
520 echo -n "<a href='$script?d=${d%.txt}'>${d%.txt}</a>"
521 if [ "$auth" ]; then
522 cat << EOT
523 : <a href="$script?edit=$d">$(gettext "Edit")</a> || \
524 <a href="$script?rm=$d">$(gettext "Remove")</a>
525 EOT
526 else
527 echo ""
528 fi
529 done && unset auth
530 echo '</pre>'
531 html_footer ;;
533 *\ rm\ *)
534 [ ! check_auth ] && exit 1
535 d="$(GET rm)"
536 rm ${wiki}/"${d}"
537 rm -rf ${cache}/"${d%.txt}"
538 header "Location: $script?ls" ;;
540 *\ diff\ *)
541 d="$(GET diff)"
542 date="last"
543 header
544 html_header
545 user_box
546 get_lang
547 wiki_tools
548 echo "<h2>$(gettext "Diff for:") <a href='$script?d=$d'>$d</a></h2>"
549 echo '<pre>'
550 if [ -f "$cache/$d/$date.diff" ]; then
551 cat $cache/$d/$date.diff | sed \
552 -e 's|&|\&|g' -e 's|<|\<|g' -e 's|>|\>|g' \
553 -e s"#^-\([^']*\).#<span style='color: red;'>\0</span>#"g \
554 -e s"#^+\([^']*\).#<span style='color: green;'>\0</span>#"g \
555 -e s"#@@\([^']*\)@@#<span style='color: blue;'>@@\1@@</span>#"g
556 else
557 gettext "No diff for:"; echo " $d"
558 fi
559 echo '</pre>'
560 html_footer ;;
562 *\ login\ *)
563 # The login page
564 d="Login"
565 [ "$(GET error)" ] && \
566 error="<p class="error">$(gettext "Bad login or pass")</p>"
567 header
568 html_header
569 user_box
570 login_page
571 html_footer ;;
573 *\ signup\ *)
574 # The login page
575 d="$(gettext "Sign Up")"
576 header
577 html_header
578 user_box
579 echo "<h2>$d</h2>"
580 if [ "$ONLINE_SIGNUP" == "yes" ]; then
581 signup_page
582 else
583 gettext "Online registration is disabled"
584 fi
585 html_footer ;;
587 *\ logout\ *)
588 # Set a Cookie in the past to logout.
589 expires="Expires=Wed, 01-Jan-1980 00:00:00 GMT"
590 if check_auth; then
591 rm -f "$sessions/$user"
592 header "Location: $script" "Set-Cookie: auth=none; $expires; HttpOnly"
593 fi ;;
595 *\ user\ *)
596 # Basic user profile. Use the users plugin for more functions
597 d="$(GET user)"
598 last="$(cat $PEOPLE/"$(GET user)"/last)"
599 header
600 html_header
601 user_box
602 . $PEOPLE/"$(GET user)"/account.conf
603 cat << EOT
604 <h2>$(get_gravatar $MAIL) $NAME</h2>
606 <pre>
607 $(gettext "User name :") $USER
608 $(gettext "Last login :") $last
609 </pre>
610 EOT
611 html_footer ;;
613 *\ hg\ *)
614 d="Hg Log"
615 header
616 html_header
617 user_box
618 [ "$HG" != "yes" ] && gettext "Hg is disabled" && exit 0
619 [ ! -x /usr/bin/hg ] && gettext "Hg is not installed" && exit 0
620 echo "<h2>$d</h2>"
621 case " $(GET hg) " in
622 *\ init\ *)
623 if check_auth; then
624 [ -d "$content/.hg" ] && exit 0
625 echo '<pre>'
626 gettext "Executing: hg init"; echo
627 cd $content/ && hg init
628 echo '[hooks]' > .hg/hgrc
629 echo 'incoming = hg update' >> .hg/hgrc
630 gettext "Adding current content and committing"; echo
631 [ ! -f "$wiki/index.txt" ] && default_index
632 hg add && hg commit -u "$NAME <$MAIL>" \
633 -m "Initial commit with current content"
634 echo '</pre>' && cd ..
635 fi ;;
636 esac
637 hg_log
638 html_footer ;;
640 *)
641 # Display requested page
642 d="$(GET d)"
643 [ "$d" ] || d=$index
644 header
645 html_header
646 user_box
647 get_lang
649 # Generate a default index on first run
650 if [ ! -f "$wiki/$index.txt" ]; then
651 if ! default_index; then
652 echo "<pre class='error'>Directory : content/ is not writeable</pre>"
653 html_footer && exit 0
654 fi
655 fi
657 # Check cache dir
658 if [ ! -w "$cache" ]; then
659 echo "<pre class='error'>Directory : cache/ is not writeable"
660 echo "Command : install -m 0777 -d $tiny/cache</pre>"
661 html_footer && exit 0
662 fi
664 # Hg warning if enabled but not initiated
665 if [ "$HG" == "yes" ] && [ ! -d "$content/.hg" ]; then
666 echo '<p class="error box">'
667 gettext "Mercurial is enabled but no repository found"
668 echo ": <a href='$script?hg=init'>Hg init</a>"
669 echo '</p>'
670 fi
672 # Wiki tools
673 wiki_tools
675 # Wiki document
676 if [ ! -f "$wiki/$d.txt" ]; then
677 echo "<h2>$d</h2>"
678 gettext "The document does not exist. You can create it or read the"
679 echo " <a href='$script?d=en/help'>help</a>"
680 else
681 if fgrep -q [NOWIKI] $wiki/$d.txt; then
682 cat $wiki/$d.txt | sed '/\[NOWIKI\]/'d
683 else
684 cat $wiki/$d.txt | wiki_parser
685 fi
686 fi
687 html_footer ;;
688 esac
690 exit 0