tinycm view index.cgi @ rev 88
Small fix to commnity plugin
author | Christophe Lincoln <pankso@slitaz.org> |
---|---|
date | Sun Feb 12 01:23:48 2017 +0100 (2017-02-12) |
parents | 2778498112c0 |
children | 6a565d525bda |
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 -q 'ADMIN_USER="yes"' ${PEOPLE}/${user}/account.conf
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 key=$(echo -n "$user:$mail:$pass" | md5sum | awk '{print $1}')
251 echo "$user:$pass" >> $AUTH_FILE
252 mkdir -p $PEOPLE/$user/
253 cat > $PEOPLE/$user/account.conf << EOT
254 # SliTaz user configuration
255 #
257 NAME="$name"
258 USER="$user"
259 MAIL="$mail"
260 KEY="$key"
262 EOT
263 chmod 0600 $PEOPLE/$user/account.conf
264 # First created user is admin
265 if [ $(ls ${PEOPLE} | wc -l) == "1" ]; then
266 echo 'ADMIN_USER="yes"' >> $PEOPLE/$user/account.conf
267 fi
268 }
270 # The CM style parser. Just a title, simple text formatting and internal
271 # links, as well as images and use HTML for other stuff. Keep it fast!
272 # To make TinyCM as easy as possible we have a small HTML editor/helper
273 # written in Javascript
274 wiki_parser() {
275 doc="[0-9a-zA-Z\.\#/~\_%=\?\&,\+\:@;!\(\)\*\$'\-]*"
276 sed \
277 -e s"#====\([^']*\)====#<h2>\1</h2>#"g \
278 -e s"#===\([^']*\)===#<h3>\1</h3>#"g \
279 -e s"#==\([^']*\)==#<h4>\1</h4>#"g \
280 -e s"#\*\*\([^']*\)\*\*#<b>\1</b>#"g \
281 -e s"#''\([^']*\)''#<em>\1</em>#"g \
282 -e s"#__\([^']*\)__#<u>\1</u>#"g \
283 -e s"#\[\([^]]*\)|\($doc\)\]#<a href='$script?d=\2'>\1</a>#"g \
284 -e s"#\[\([^]]*\)!\($doc\)\]#<a href='\2'>\1</a>#"g \
285 -e s"#\[\(http://*[^]]*.png\)\]#<img src='\1' />#"g \
286 -e s"#\[\([^]]*.png\)\]#<img src='content/cloud/\1' />#"g
287 }
289 link_user() {
290 echo "<a href='$(basename $script)?user=$user'>$user</a>"
291 }
293 # Save a document. Do we need more than 1 backup and diff ?
294 save_document() {
295 mkdir -p $cache/$d $(dirname $wiki/$d)
296 # May be a new page.
297 if [ ! -f "$wiki/$d.txt" ]; then
298 new=0
299 touch $wiki/$d.txt
300 fi
301 cp $wiki/$d.txt $cache/$d/last.bak
302 sed "s/$(echo -en '\r') /\n/g" > $wiki/$d.txt << EOT
303 $(GET content)
304 EOT
305 diff $cache/$d/last.bak $wiki/$d.txt > $cache/$d/last.diff
306 # Log
307 if [ "$new" ]; then
308 echo "Page created by: $(link_user)" | log
309 echo "New document: <a href='$script?d=$d'>$d</a>" | log_activity
310 if [ "$HG" == "yes" ]; then
311 cd $content && hg -q add
312 hg commit -q -u "$NAME <$MAIL>" -m "Created new document: $d"
313 cd $tiny
314 fi
315 else
316 # Here we may clean log: cat && tail -n 40
317 echo "Page edited by: $(link_user)" | log
318 if [ "$HG" == "yes" ]; then
319 cd $content && hg commit -q -u "$NAME <$MAIL>" \
320 -m "Edited document: $d"
321 cd $tiny
322 fi
323 fi
324 }
326 # CM tools (edit, diff, etc) for auth users
327 wiki_tools() {
328 if check_auth; then
329 cat << EOT
330 <div id="tools">
331 <a href="$script?edit=$d">$(gettext "Edit document")</a>
332 <a href="$script?log=$d">$(gettext "File log")</a>
333 <a href="$script?diff=$d">$(gettext "Last diff")</a>
334 $PLUGINS_TOOLS
335 EOT
336 [ "$HG" == "yes" ] && echo "<a href='$script?hg'>Hg Log</a>"
337 echo "</div>"
338 fi
339 }
341 # Built-in tools such as log/ls and PLUGINS_TOOLS
342 tiny_tools() {
343 if check_auth; then
344 cat << EOT
345 <div id='tools'>
346 <a href='$script?log'>Activity log</a>
347 <a href='$script?ls'>Pages list</a>
348 $PLUGINS_TOOLS
349 </div>
350 EOT
351 fi
352 }
354 # Get and display Gravatar image: get_gravatar email size
355 # Link to profile: <a href="http://www.gravatar.com/$md5">...</a>
356 get_gravatar() {
357 email=$1
358 size=$2
359 [ "$size" ] || size=48
360 url="http://www.gravatar.com/avatar"
361 md5=$(md5crypt $email)
362 echo "<img src='$url/$md5?d=identicon&s=$size' alt='∗' />"
363 }
365 # List hg logs
366 hg_log() {
367 cd $content
368 cat << EOT
369 <table>
370 <thead>
371 <td>$(gettext "User")</td>
372 <td>$(gettext "Description")</td>
373 <td>$(gettext "Revision")</td>
374 </thead>
375 EOT
376 hg log --template "<tr><td>{author}</td><td>{desc}</td><td>{rev}</td></tr>\n"
377 echo '</table>'
378 }
380 #
381 # POST actions
382 #
384 case " $(POST) " in
385 *\ auth\ *)
386 # Authenticate user. Create a session file in $sessions to be used
387 # by check_auth. We have the user login name and a peer session
388 # md5 string in the COOKIE.
389 user="$(POST auth)"
390 pass="$(md5crypt "$(POST pass)")"
391 valid=$(fgrep "${user}:" $AUTH_FILE | cut -d ":" -f 2)
392 if [ "$pass" == "$valid" ] && [ "$pass" != "" ]; then
393 md5session=$(echo -n "$$:$user:$pass:$$" | md5sum | awk '{print $1}')
394 [ -d $sessions ] || mkdir -p $sessions
395 date '+%Y-%m-%d' > ${PEOPLE}/${user}/last
396 echo "$md5session" > $sessions/$user
397 header "Location: $script" \
398 "Set-Cookie: auth=$user:$md5session; HttpOnly"
399 else
400 header "Location: $script?login&error"
401 fi ;;
402 *\ signup\ *)
403 # POST action for signup
404 name="$(POST name)"
405 user="$(POST user)"
406 mail="$(POST mail)"
407 pass="$(md5crypt "$(POST pass)")"
408 if ! grep "^${user}:" $AUTH_FILE; then
409 new_user_config
410 header "Location: $script?login"
411 else
412 header
413 html_header
414 user_box
415 echo "<h2>$(gettext 'User already exists:') $user</h2>"
416 html_footer
417 fi ;;
418 esac
420 #
421 # Plugins
422 #
423 for p in $(ls -1 $plugins)
424 do
425 [ -f "$plugins/$p/$p.conf" ] && . $plugins/$p/$p.conf
426 [ -x "$plugins/$p/$p.cgi" ] && . $plugins/$p/$p.cgi
427 done
429 #
430 # GET actions
431 #
433 case " $(GET) " in
434 *\ edit\ *)
435 d="$(GET edit)"
436 header
437 html_header
438 user_box
439 get_lang
440 wiki_tools
441 if check_auth; then
442 cat << EOT
443 <h2>$(gettext "Edit $doc [ $i18n ]")</h2>
445 <div id="edit">
447 <form method="get" action="$script" name="editor">
448 <input type="hidden" name="save" value="$d" />
449 <textarea name="content">$(cat "$wiki/$d.txt")</textarea>
450 <input type="submit" value="$(gettext "Save document")" />
451 $(gettext "Code Helper:")
452 $(cat lib/jseditor.html)
453 </form>
455 </div>
456 EOT
457 else
458 gettext "You must be logged in to edit pages"
459 fi
460 html_footer ;;
462 *\ save\ *)
463 d="$(GET save)"
464 if check_auth; then
465 save_document
466 fi
467 header "Location: $script?d=$d" ;;
469 *\ log\ *)
470 d="$(GET log)"
471 header
472 html_header
473 user_box
474 # Main activity
475 if [ "$d" == "log" ]; then
476 tiny_tools
477 echo "<h2>$(gettext "Activity log")</h2>"
478 echo '<pre>'
479 if [ -f "$cache/log/activity.log" ]; then
480 IFS="|"
481 tac $cache/log/activity.log | while read USER DATE LOG
482 do
483 . ${PEOPLE}/${USER}/account.conf
484 cat << EOT
485 <a href='$script?user=$USER'>$(get_gravatar $MAIL 24)</a>\
486 <span class='date'>$DATE -</span> $LOG
487 EOT
488 done
489 unset IFS
490 else
491 gettext "No activity log yet"; echo
492 fi
493 echo '</pre>'
494 html_footer && exit 0
495 fi
496 # Document activity
497 get_lang
498 wiki_tools
499 echo "<h2>$(gettext "Activity for:") <a href='$script?d=$d'>$d</a></h2>"
500 echo '<pre>'
501 if [ -f "$cache/$d/activity.log" ]; then
502 tac $cache/$d/activity.log
503 else
504 gettext "No log for: $d"; echo
505 fi
506 echo '</pre>'
507 html_footer ;;
509 *\ ls\ *)
510 d="Document list"
511 header
512 html_header
513 user_box
514 tiny_tools
515 [ ! check_auth ] && auth=0
516 echo "<h2>$(gettext "Pages list")</h2>"
517 echo '<pre>'
518 cd ${wiki}
519 for d in $(find . -type f | sed s'/.\///')
520 do
521 echo -n "<a href='$script?d=${d%.txt}'>${d%.txt}</a>"
522 if [ "$auth" ]; then
523 cat << EOT
524 : <a href="$script?edit=$d">$(gettext "Edit")</a> || \
525 <a href="$script?rm=$d">$(gettext "Remove")</a>
526 EOT
527 else
528 echo ""
529 fi
530 done && unset auth
531 echo '</pre>'
532 html_footer ;;
534 *\ rm\ *)
535 [ ! check_auth ] && exit 1
536 d="$(GET rm)"
537 rm ${wiki}/"${d}"
538 rm -rf ${cache}/"${d%.txt}"
539 header "Location: $script?ls" ;;
541 *\ diff\ *)
542 d="$(GET diff)"
543 date="last"
544 header
545 html_header
546 user_box
547 get_lang
548 wiki_tools
549 echo "<h2>$(gettext "Diff for:") <a href='$script?d=$d'>$d</a></h2>"
550 echo '<pre>'
551 if [ -f "$cache/$d/$date.diff" ]; then
552 cat $cache/$d/$date.diff | sed \
553 -e 's|&|\&|g' -e 's|<|\<|g' -e 's|>|\>|g' \
554 -e s"#^-\([^']*\).#<span style='color: red;'>\0</span>#"g \
555 -e s"#^+\([^']*\).#<span style='color: green;'>\0</span>#"g \
556 -e s"#@@\([^']*\)@@#<span style='color: blue;'>@@\1@@</span>#"g
557 else
558 gettext "No diff for:"; echo " $d"
559 fi
560 echo '</pre>'
561 html_footer ;;
563 *\ login\ *)
564 # The login page
565 d="Login"
566 [ "$(GET error)" ] && \
567 error="<p class="error">$(gettext "Bad login or pass")</p>"
568 header
569 html_header
570 user_box
571 login_page
572 html_footer ;;
574 *\ signup\ *)
575 # The login page
576 d="$(gettext "Sign Up")"
577 header
578 html_header
579 user_box
580 echo "<h2>$d</h2>"
581 if [ "$ONLINE_SIGNUP" == "yes" ]; then
582 signup_page
583 else
584 gettext "Online registration is disabled"
585 fi
586 html_footer ;;
588 *\ logout\ *)
589 # Set a Cookie in the past to logout.
590 expires="Expires=Wed, 01-Jan-1980 00:00:00 GMT"
591 if check_auth; then
592 rm -f "$sessions/$user"
593 header "Location: $script" "Set-Cookie: auth=none; $expires; HttpOnly"
594 fi ;;
596 *\ user\ *)
597 # Basic user profile. Use the users plugin for more functions
598 d="$(GET user)"
599 last="$(cat $PEOPLE/"$(GET user)"/last)"
600 header
601 html_header
602 user_box
603 . $PEOPLE/"$(GET user)"/account.conf
604 cat << EOT
605 <h2>$(get_gravatar $MAIL) $NAME</h2>
607 <pre>
608 $(gettext "User name :") $USER
609 $(gettext "Last login :") $last
610 </pre>
611 EOT
612 html_footer ;;
614 *\ hg\ *)
615 d="Hg Log"
616 header
617 html_header
618 user_box
619 [ "$HG" != "yes" ] && gettext "Hg is disabled" && exit 0
620 [ ! -x /usr/bin/hg ] && gettext "Hg is not installed" && exit 0
621 echo "<h2>$d</h2>"
622 case " $(GET hg) " in
623 *\ init\ *)
624 if check_auth; then
625 [ -d "$content/.hg" ] && exit 0
626 echo '<pre>'
627 gettext "Executing: hg init"; echo
628 cd $content/ && hg init
629 echo '[hooks]' > .hg/hgrc
630 echo 'incoming = hg update' >> .hg/hgrc
631 gettext "Adding current content and committing"; echo
632 [ ! -f "$wiki/index.txt" ] && default_index
633 hg add && hg commit -u "$NAME <$MAIL>" \
634 -m "Initial commit with current content"
635 echo '</pre>' && cd ..
636 fi ;;
637 esac
638 hg_log
639 html_footer ;;
641 *)
642 # Display requested page
643 d="$(GET d)"
644 [ "$d" ] || d=$index
645 header
646 html_header
647 user_box
648 get_lang
650 # Generate a default index on first run
651 if [ ! -f "$wiki/$index.txt" ]; then
652 if ! default_index; then
653 echo "<pre class='error'>Directory : content/ is not writeable</pre>"
654 html_footer && exit 0
655 fi
656 fi
658 # Check cache dir
659 if [ ! -w "$cache" ]; then
660 echo "<pre class='error'>Directory : cache/ is not writeable"
661 echo "Command : install -m 0777 -d $tiny/cache</pre>"
662 html_footer && exit 0
663 fi
665 # Hg warning if enabled but not initiated
666 if [ "$HG" == "yes" ] && [ ! -d "$content/.hg" ]; then
667 echo '<p class="error box">'
668 gettext "Mercurial is enabled but no repository found"
669 echo ": <a href='$script?hg=init'>Hg init</a>"
670 echo '</p>'
671 fi
673 # Wiki tools
674 wiki_tools
676 # Wiki document
677 if [ ! -f "$wiki/$d.txt" ]; then
678 echo "<h2>$d</h2>"
679 gettext "The document does not exist. You can create it or read the"
680 echo " <a href='$script?d=en/help'>help</a>"
681 else
682 if fgrep -q [NOWIKI] $wiki/$d.txt; then
683 cat $wiki/$d.txt | sed '/\[NOWIKI\]/'d
684 else
685 cat $wiki/$d.txt | wiki_parser
686 fi
687 fi
688 html_footer ;;
689 esac
691 exit 0