tinycm view index.cgi @ rev 104

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