tinycm view index.cgi @ rev 65

Small fix to blog and ls
author Christophe Lincoln <pankso@slitaz.org>
date Sun Feb 16 23:36:43 2014 +0100 (2014-02-16)
parents d73353aed02d
children e3cba575c564
line source
1 #!/bin/sh
2 #
3 # TinyCM - Small, fast and elegant CGI/SHell Content Manager
4 #
5 # Copyright (C) 2012-2014 SliTaz GNU/Linux - BSD License
6 #
7 . /usr/lib/slitaz/httphelper
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 &hearts; <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 adding 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 # Display user public profile.
271 public_people() {
272 echo "</pre>"
273 # Display personal user profile
274 if [ -f "$PEOPLE/$USER/profile.txt" ]; then
275 cat $PEOPLE/$USER/profile.txt | wiki_parser
276 fi
277 }
279 # Display authenticated user profile. TODO: change password
280 auth_people() {
281 cat << EOT
282 Email : $MAIL
283 Secure key : $KEY
284 </pre>
285 EOT
286 # Each user can have personal profile page
287 if [ -f "$PEOPLE/$USER/profile.txt" ]; then
288 cat $PEOPLE/$USER/profile.txt | wiki_parser
289 cat << EOT
290 <div id="tools">
291 <a href="$script?edit=profile">$(gettext "Edit profile")</a>
292 <a href="$script?dashboard">Dashboard</a>
293 </div>
294 EOT
295 else
296 cat << EOT
297 <div id="tools">
298 <a href="$script?edit=profile">$(gettext "Create a profile page")</a>
299 <a href="$script?dashboard">Dashboard</a>
300 </div>
301 EOT
302 fi
303 }
305 # The CM style parser. Just a title, simple text formatting and internal
306 # links, as well as images and use HTML for other stuff. Keep it fast!
307 # To make TinyCM as easy as possible we have a small HTML editor/helper
308 # written in Javascript
309 wiki_parser() {
310 doc="[0-9a-zA-Z\.\#/~\_%=\?\&,\+\:@;!\(\)\*\$'\-]*"
311 sed \
312 -e s"#====\([^']*\)====#<h2>\1</h2>#"g \
313 -e s"#===\([^']*\)===#<h3>\1</h3>#"g \
314 -e s"#==\([^']*\)==#<h4>\1</h4>#"g \
315 -e s"#\*\*\([^']*\)\*\*#<b>\1</b>#"g \
316 -e s"#''\([^']*\)''#<em>\1</em>#"g \
317 -e s"#__\([^']*\)__#<u>\1</u>#"g \
318 -e s"#\[\([^]]*\)|\($doc\)\]#<a href='$script?d=\2'>\1</a>#"g \
319 -e s"#\[\([^]]*\)!\($doc\)\]#<a href='\2'>\1</a>#"g \
320 -e s"#\[\(http://*[^]]*.png\)\]#<img src='\1' />#"g \
321 -e s"#\[\([^]]*.png\)\]#<img src='content/cloud/\1' />#"g
322 }
324 link_user() {
325 echo "<a href='$(basename $script)?user=$user'>$user</a>"
326 }
328 # Save a document. Do we need more than 1 backup and diff ?
329 save_document() {
330 mkdir -p $cache/$d $(dirname $wiki/$d)
331 # May be a new page.
332 if [ ! -f "$wiki/$d.txt" ]; then
333 new=0
334 touch $wiki/$d.txt
335 fi
336 cp $wiki/$d.txt $cache/$d/last.bak
337 sed "s/$(echo -en '\r') /\n/g" > $wiki/$d.txt << EOT
338 $(GET content)
339 EOT
340 diff $cache/$d/last.bak $wiki/$d.txt > $cache/$d/last.diff
341 # Log
342 if [ "$new" ]; then
343 echo "Page created by: $(link_user)" | log
344 echo "New document: <a href='$script?d=$d'>$d</a>" | log_activity
345 if [ "$HG" == "yes" ]; then
346 cd $content && hg -q add
347 hg commit -q -u "$NAME <$MAIL>" -m "Created new document: $d"
348 cd $tiny
349 fi
350 else
351 # Here we may clean log: cat && tail -n 40
352 echo "Page edited by: $(link_user)" | log
353 if [ "$HG" == "yes" ]; then
354 cd $content && hg commit -q -u "$NAME <$MAIL>" \
355 -m "Edited document: $d"
356 cd $tiny
357 fi
358 fi
359 }
361 # Save a user profile.
362 save_profile() {
363 path="$PEOPLE/$user"
364 cp -f ${path}/${d}.txt ${path}/${d}.bak
365 sed "s/$(echo -en '\r') /\n/g" > ${path}/${d}.txt << EOT
366 $(GET content)
367 EOT
368 }
370 # CM tools (edit, diff, etc) for auth users
371 wiki_tools() {
372 if check_auth; then
373 cat << EOT
374 <div id="tools">
375 <a href="$script?edit=$d">$(gettext "Edit document")</a>
376 <a href="$script?log=$d">$(gettext "File log")</a>
377 <a href="$script?diff=$d">$(gettext "Last diff")</a>
378 $PLUGINS_TOOLS
379 EOT
380 [ "$HG" == "yes" ] && echo "<a href='$script?hg'>Hg Log</a>"
381 echo "</div>"
382 fi
383 }
385 # Built-in tools such as log/ls and PLUGINS_TOOLS
386 tiny_tools() {
387 if check_auth; then
388 cat << EOT
389 <div id='tools'>
390 <a href='$script?log'>Activity log</a>
391 <a href='$script?ls'>Pages list</a>
392 $PLUGINS_TOOLS
393 </div>
394 EOT
395 fi
396 }
398 # Get and display Gravatar image: get_gravatar email size
399 # Link to profile: <a href="http://www.gravatar.com/$md5">...</a>
400 get_gravatar() {
401 email=$1
402 size=$2
403 [ "$size" ] || size=48
404 url="http://www.gravatar.com/avatar"
405 md5=$(md5crypt $email)
406 echo "<img src='$url/$md5?d=identicon&s=$size' alt='&lowast;' />"
407 }
409 # List hg logs
410 hg_log() {
411 cd $content
412 cat << EOT
413 <table>
414 <thead>
415 <td>$(gettext "User")</td>
416 <td>$(gettext "Description")</td>
417 <td>$(gettext "Revision")</td>
418 </thead>
419 EOT
420 hg log --template "<tr><td>{author}</td><td>{desc}</td><td>{rev}</td></tr>\n"
421 echo '</table>'
422 }
424 #
425 # POST actions
426 #
428 case " $(POST) " in
429 *\ auth\ *)
430 # Authenticate user. Create a session file in $sessions to be used
431 # by check_auth. We have the user login name and a peer session
432 # md5 string in the COOKIE.
433 user="$(POST auth)"
434 pass="$(md5crypt "$(POST pass)")"
435 valid=$(fgrep "${user}:" $AUTH_FILE | cut -d ":" -f 2)
436 if [ "$pass" == "$valid" ] && [ "$pass" != "" ]; then
437 md5session=$(echo -n "$$:$user:$pass:$$" | md5sum | awk '{print $1}')
438 [ -d $sessions ] || mkdir -p $sessions
439 date '+%Y-%m-%d' > ${PEOPLE}/${user}/last
440 echo "$md5session" > $sessions/$user
441 header "Location: $script" \
442 "Set-Cookie: auth=$user:$md5session; HttpOnly"
443 else
444 header "Location: $script?login&error"
445 fi ;;
446 *\ signup\ *)
447 # POST action for signup
448 name="$(POST name)"
449 user="$(POST user)"
450 mail="$(POST mail)"
451 pass="$(md5crypt "$(POST pass)")"
452 if ! grep "^${user}:" $AUTH_FILE; then
453 new_user_config
454 header "Location: $script?login"
455 else
456 header
457 html_header
458 user_box
459 echo "<h2>$(gettext 'User already exists:') $user</h2>"
460 html_footer
461 fi ;;
462 esac
464 #
465 # Plugins
466 #
467 for p in $(ls -1 $plugins)
468 do
469 [ -f "$plugins/$p/$p.conf" ] && . $plugins/$p/$p.conf
470 [ -x "$plugins/$p/$p.cgi" ] && . $plugins/$p/$p.cgi
471 done
473 #
474 # GET actions
475 #
477 case " $(GET) " in
478 *\ edit\ *)
479 d="$(GET edit)"
480 header
481 html_header
482 user_box
483 get_lang
484 wiki_tools
485 if check_auth; then
486 if [ "$doc" == "profile" ]; then
487 wiki="$PEOPLE/$user"
488 fi
489 cat << EOT
490 <h2>$(gettext "Edit $doc [ $i18n ]")</h2>
492 <div id="edit">
494 <form method="get" action="$script" name="editor">
495 <input type="hidden" name="save" value="$d" />
496 <textarea name="content">$(cat "$wiki/$d.txt")</textarea>
497 <input type="submit" value="$(gettext "Save document")" />
498 $(gettext "Code Helper:")
499 $(cat lib/jseditor.html)
500 </form>
502 </div>
503 EOT
504 else
505 gettext "You must be logged in to edit pages"
506 fi
507 html_footer ;;
509 *\ save\ *)
510 d="$(GET save)"
511 if check_auth; then
512 # User profile
513 if [ "$d" == "profile" ]; then
514 save_profile
515 header "Location: $script?user=$user"
516 else
517 save_document
518 fi
519 fi
520 header "Location: $script?d=$d" ;;
522 *\ log\ *)
523 d="$(GET log)"
524 header
525 html_header
526 user_box
527 # Main activity
528 if [ "$d" == "log" ]; then
529 tiny_tools
530 echo "<h2>$(gettext "Activity log")</h2>"
531 echo '<pre>'
532 if [ -f "$cache/log/activity.log" ]; then
533 IFS="|"
534 tac $cache/log/activity.log | while read USER DATE LOG
535 do
536 . ${PEOPLE}/${USER}/account.conf
537 cat << EOT
538 <a href='$script?user=$USER'>$(get_gravatar $MAIL 24)</a>\
539 <span class='date'>$DATE -</span> $LOG
540 EOT
541 done
542 unset IFS
543 else
544 gettext "No activity log yet"; echo
545 fi
546 echo '</pre>'
547 html_footer && exit 0
548 fi
549 # Document activity
550 get_lang
551 wiki_tools
552 echo "<h2>$(gettext "Activity for:") <a href='$script?d=$d'>$d</a></h2>"
553 echo '<pre>'
554 if [ -f "$cache/$d/activity.log" ]; then
555 tac $cache/$d/activity.log
556 else
557 gettext "No log for: $d"; echo
558 fi
559 echo '</pre>'
560 html_footer ;;
562 *\ ls\ *)
563 d="Document list"
564 header
565 html_header
566 user_box
567 tiny_tools
568 [ ! check_auth ] && auth=0
569 echo "<h2>$(gettext "Pages list")</h2>"
570 echo '<pre>'
571 cd ${wiki}
572 for d in $(find . -type f | sed s'/.\///')
573 do
574 echo -n "<a href='$script?d=${d%.txt}'>${d%.txt}</a>"
575 if [ "$auth" ]; then
576 cat << EOT
577 : <a href="$script?edit=$d">$(gettext "Edit")</a> || \
578 <a href="$script?rm=$d">$(gettext "Remove")</a>
579 EOT
580 else
581 echo ""
582 fi
583 done && unset auth
584 echo '</pre>'
585 html_footer ;;
587 *\ rm\ *)
588 [ ! check_auth ] && exit 1
589 d="$(GET rm)"
590 rm ${wiki}/"${d}"
591 rm -rf ${cache}/"${d%.txt}"
592 header "Location: $script?ls" ;;
594 *\ diff\ *)
595 d="$(GET diff)"
596 date="last"
597 header
598 html_header
599 user_box
600 get_lang
601 wiki_tools
602 echo "<h2>$(gettext "Diff for:") <a href='$script?d=$d'>$d</a></h2>"
603 echo '<pre>'
604 if [ -f "$cache/$d/$date.diff" ]; then
605 cat $cache/$d/$date.diff | sed \
606 -e 's|&|\&amp;|g' -e 's|<|\&lt;|g' -e 's|>|\&gt;|g' \
607 -e s"#^-\([^']*\).#<span style='color: red;'>\0</span>#"g \
608 -e s"#^+\([^']*\).#<span style='color: green;'>\0</span>#"g \
609 -e s"#@@\([^']*\)@@#<span style='color: blue;'>@@\1@@</span>#"g
610 else
611 gettext "No diff for:"; echo " $d"
612 fi
613 echo '</pre>'
614 html_footer ;;
616 *\ login\ *)
617 # The login page
618 d="Login"
619 [ "$(GET error)" ] && \
620 error="<p class="error">$(gettext "Bad login or pass")</p>"
621 header
622 html_header
623 user_box
624 login_page
625 html_footer ;;
627 *\ signup\ *)
628 # The login page
629 d="$(gettext "Sign Up")"
630 header
631 html_header
632 user_box
633 echo "<h2>$d</h2>"
634 if [ "$ONLINE_SIGNUP" == "yes" ]; then
635 signup_page
636 else
637 gettext "Online registration is disabled"
638 fi
639 html_footer ;;
641 *\ logout\ *)
642 # Set a Cookie in the past to logout.
643 expires="Expires=Wed, 01-Jan-1980 00:00:00 GMT"
644 if check_auth; then
645 rm -f "$sessions/$user"
646 header "Location: $script" "Set-Cookie: auth=none; $expires; HttpOnly"
647 fi ;;
649 *\ user\ *)
650 # User profile
651 d="$(GET user)"
652 last="$(cat $PEOPLE/"$(GET user)"/last)"
653 header
654 html_header
655 user_box
656 . $PEOPLE/"$(GET user)"/account.conf
657 cat << EOT
658 <h2>$(get_gravatar $MAIL) $NAME</h2>
660 <pre>
661 $(gettext "User name :") $USER
662 $(gettext "Last login :") $last
663 EOT
664 if check_auth && [ "$(GET user)" == "$user" ]; then
665 auth_people
666 else
667 # check_auth will set VARS to current logged user: re-source
668 . $PEOPLE/"$(GET user)"/account.conf
669 public_people
670 fi
671 html_footer ;;
673 *\ hg\ *)
674 d="Hg Log"
675 header
676 html_header
677 user_box
678 [ "$HG" != "yes" ] && gettext "Hg is disabled" && exit 0
679 [ ! -x /usr/bin/hg ] && gettext "Hg is not installed" && exit 0
680 echo "<h2>$d</h2>"
681 case " $(GET hg) " in
682 *\ init\ *)
683 if check_auth; then
684 [ -d "$content/.hg" ] && exit 0
685 echo '<pre>'
686 gettext "Executing: hg init"; echo
687 cd $content/ && hg init
688 echo '[hooks]' > .hg/hgrc
689 echo 'incoming = hg update' >> .hg/hgrc
690 gettext "Adding current content and committing"; echo
691 [ ! -f "$wiki/index.txt" ] && default_index
692 hg add && hg commit -u "$NAME <$MAIL>" \
693 -m "Initial commit with current content"
694 echo '</pre>' && cd ..
695 fi ;;
696 esac
697 hg_log
698 html_footer ;;
700 *)
701 # Display requested page
702 d="$(GET d)"
703 [ "$d" ] || d=$index
704 header
705 html_header
706 user_box
707 get_lang
709 # Generate a default index on first run
710 if [ ! -f "$wiki/$index.txt" ]; then
711 if ! default_index; then
712 echo "<pre class='error'>Directory : content/ is not writeable</pre>"
713 html_footer && exit 0
714 fi
715 fi
717 # Check cache dir
718 if [ ! -w "$cache" ]; then
719 echo "<pre class='error'>Directory : cache/ is not writeable"
720 echo "Command : install -m 0777 -d $tiny/cache</pre>"
721 html_footer && exit 0
722 fi
724 # Hg warning if enabled but not initiated
725 if [ "$HG" == "yes" ] && [ ! -d "$content/.hg" ]; then
726 echo '<p class="error box">'
727 gettext "Mercurial is enabled but no repository found"
728 echo ": <a href='$script?hg=init'>Hg init</a>"
729 echo '</p>'
730 fi
732 # Wiki tools
733 wiki_tools
735 # Wiki document
736 if [ ! -f "$wiki/$d.txt" ]; then
737 echo "<h2>$d</h2>"
738 gettext "The document does not exist. You can create it or read the"
739 echo " <a href='$script?d=en/help'>help</a>"
740 else
741 if fgrep -q [NOWIKI] $wiki/$d.txt; then
742 cat $wiki/$d.txt | sed '/\[NOWIKI\]/'d
743 else
744 cat $wiki/$d.txt | wiki_parser
745 fi
746 fi
747 html_footer ;;
748 esac
750 exit 0