[첫화면으로]UseModWiki소스수정/blog매크로시리즈

마지막으로 [b]

blog_ 매크로 시리즈

1. 이 패치는...
2. 사용법
2.1. blog_newpost 매크로
2.2. blog_calendar 매크로
2.3. blog_includeorder 매크로
2.4. blog_includeperiod 매크로
2.5. blog_listorder 매크로
2.6. blog_listperiod 매크로
2.7. blog_prevnext 매크로
2.8. blog_prevnextmonth 매크로
2.9. blog_rss 매크로
2.10. blog_rss 액션
3. 소스 수정
3.1. macros/blog_newpost.pl 추가
3.2. macros/blog_calendar.pl 추가
3.3. macros/blog_includeorder.pl 추가
3.4. macros/blog_includeperiod.pl 추가
3.5. macros/blog_listorder.pl 추가
3.6. macros/blog_listperiod.pl 추가
3.7. macros/blog_prevnext.pl 추가
3.8. macors/blog_prevnextmonth.pl 추가
3.9. macros/blog_library.pl 추가
3.10. action/blog_newpost.pl 추가
3.11. action/comments.pl 수정
3.12. action/trackback.pl 수정
3.13. macros/blog_rss.pl 추가
3.14. action/blog_rss.pl 추가
4. 추가 업데이트 내역
5. 사용자 의견

1. 이 패치는...

위키의블로그화프로젝트를 위해 만들어진 매크로들.

2. 사용법

2.1. blog_newpost 매크로

<blog_newpost(목차페이지이름)>
* [[/제목]] 날짜

2.2. blog_calendar 매크로

<blog_calendar(목차페이지이름,년,월)>

2.3. blog_includeorder 매크로

See also 다른페이지의내용을포함하여출력하기
<blog_includeorder(목차페이지,시작순서,끝순서)>

2.4. blog_includeperiod 매크로

See also 다른페이지의내용을포함하여출력하기
<blog_includeperiod(목차페이지,시작날짜,끝날짜)>

2.5. blog_listorder 매크로

<blog_listorder(목차페이지,시작순서,끝순서[,날짜출력방식])>

2.6. blog_listperiod 매크로

<blog_listperiod(목차페이지,시작날짜,끝날짜[,날짜출력방식])>

2.7. blog_prevnext 매크로

<blog_prevnext(목차페이지)>

2.8. blog_prevnextmonth 매크로

<blog_prevnext>

2.9. blog_rss 매크로

<blog_rss(목차페이지[,블로그페이지])>
또는
<blog_rss>
<channel>
[ 채널에 관한 엘리멘트들 ]
[ <item>
아이템에 관한 엘리멘트들
</item> ]
</channel>
</blog_rss>
<blog_rss>                             - 필수
<channel>                              - 필수
<title>블로그제목</title>                 --+
<link>http://...블로그주소</link>           +-- 이 부분은 채널에 관한 정보. (생략 가능)
<description>블로그 설명</description>    --+   채널에 관한 정보는 목차페이지에 있을 때만 의미가 있다.
<item>                                    --+ 
<author>저자</author>                       +-- 이것은 아이템에 관한 정보. (생략 가능)
</item>                                   --+   목차페이지에 있는 경우는, RSS의 각 아이템에 일괄 적용된다.
                                                포스트 페이지에 있는 경우는, 그 페이지에 해당하는 아이템에만 적용된다.
</channel>                             - 필수
</blog_rss>                            - 필수

2.10. blog_rss 액션

wiki.pl?action=blog_rss&listpage=목차페이지[&blogpage=블로그페이지][&items=아이템갯수]

3. 소스 수정

3.1. macros/blog_newpost.pl 추가

# <blog_newpost(목차페이지)>
# 목차페이지에 새 글 제목과 날짜를 입력하는 폼을 출력
# 저장 버튼을 누르면 이 매크로 있는 자리에 치환됨

sub blog_newpost {
    my ($txt) = @_;

    $txt =~ s/\&__LT__;blog_newpost\((.+)\)\&__GT__;/&MacroBlogNewPost($1)/gei;

    return $txt;
}

sub MacroBlogNewPost {
    use strict;
    my ($id) = @_;
    my $txt;

    $id = &RemoveLink($id);
    $id = &FreeToNormal($id);

    if (my $temp = &ValidId($id)) {
        return "<font color='red'>$temp</font>";
    }

    my ($readonly_true, $readonly_style, $readonly_msg);
    my ($title_field, $date_field, $submit_button);

    if (!&UserCanEdit($id,1)) {
        $readonly_true = "true";
        $readonly_style = "background-color: #f0f0f0;";
        $readonly_msg = T('Editing Denied');
        $submit_button = "";
        $title_field = $q->textfield(-name=>"title",
                                    -class=>"comments",
                                    -size=>"50",
                                    -maxlength=>"100",
                                    -readonly=>"$readonly_true",
                                    -style=>"$readonly_style",
                                    -default=>"$readonly_msg");
        $date_field = $q->textfield(-name=>"date",
                                    -class=>"comments",
                                    -size=>"10",
                                    -maxlength=>"10",
                                    -readonly=>"$readonly_true",
                                    -style=>"$readonly_style",
                                    -default=>"");
    } else {
        my $today = &CalcDay($Now);
        $today =~ s/-(\d)-/-0$1-/g;
        $today =~ s/-(\d)$/-0$1/g;
        $submit_button = $q->submit(-name=>"Submit",-value=>T("Submit"));
        $title_field = $q->textfield(-name=>"title",
                                    -class=>"comments",
                                    -size=>"50",
                                    -maxlength=>"100",
                                    -default=>"");
        $date_field = $q->textfield(-name=>"date",
                                    -class=>"comments",
                                    -size=>"10",
                                    -maxlength=>"10",
                                    -default=>"$today");
    }

    $txt =
        $q->startform(-name=>"newpost",-method=>"POST",-action=>"$ScriptName") .
        &GetHiddenValue("action","blog_newpost") .
        &GetHiddenValue("id","$id") .
        &GetHiddenValue("pageid","$pageid") .
        T('Title will be converted into [[/Title]] automatically.')."<BR>".
        T('Title:')." ".
        $title_field."&nbsp;".
        T('Date').": ".
        $date_field."&nbsp;".
        $submit_button.
        $q->endform;

    return $txt;
}

1;

3.2. macros/blog_calendar.pl 추가

# <blog_calendar(목차페이지,년,월)>

sub blog_calendar {
    my ($txt) = @_;

    $txt =~ s/\&__LT__;blog_calendar\(([^,\n]+),([-+]?\d+),([-+]?\d+)\)\&__GT__;/&MacroBlogCalendar($1, $2, $3)/gei;

    return $txt;
}

sub MacroBlogCalendar {
    use Time::Local;
    use strict;
    my ($tocpage, $cal_year, $cal_month) = @_;

    my $result='';
    my $cal_result='';
    my $cal_page;
    my @cal_color = ("red", "black", "black", "black", "black", "black", "blue", "green");
    my @cal_dow = (T('Su'), T('Mo'), T('Tu'), T('We'), T('Th'), T('Fr'), T('Sa'));
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($Now+$TimeZoneOffset);
    my ($this_year, $this_month, $this_day) = ($year, $mon, $mday);
    my $cal_time;
    my ($td_class, $span_style);
    my $temp;

    # 달의 값이 13 이상이면 무효
    if (!($cal_month =~ /[-+]/) && ($cal_month > 12)) {
        return "&lt;calendar($tocpage,$cal_year,$cal_month)&gt;";
    }

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "<font color='red'>blog_library.pl not found</font>";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "$toc_mainpage";
    }

    # 리스트를 하나로 연결
    my $tocitems = join("\n", @tocitem_List);

    # 년도나 달에 0 을 인자로 받으면 올해 또는 이번 달
    $cal_year = $this_year+1900 if ($cal_year == 0);
    $cal_month = $this_month+1 if ($cal_month == 0);

    # 년도에 + 또는 - 가 있으면 올해로부터 변위 계산
    if ($cal_year =~ /\+(\d+)/ ) {
        $cal_year = $this_year+1900 + $1;
    } elsif ($cal_year =~ /-(\d+)/ ) {
        $cal_year = $this_year+1900 - $1;
    }

    # 달에 + 또는 - 가 있으면 이번 달로부터 변위 계산
    if ($cal_month =~ /\+(\d+)/ ) {
        $cal_month = $this_month+1 + $1;
        while ($cal_month > 12)  {
            $cal_month -= 12;
            $cal_year++;
        }
    } elsif ($cal_month =~ /-(\d+)/ ) {
        $cal_month = $this_month+1 - $1;
        while ($cal_month < 1) {
            $cal_month += 12;
            $cal_year--;
        }
    }

    # 1902년부터 2037년 사이만 지원함. 그 범위를 벗어나면 1902년과 2037년으로 계산
    $cal_year = 2037 if ($cal_year > 2037);
    $cal_year = 1902 if ($cal_year < 1902);

    # 1월~9월은 01~09로 만듦
    if ($cal_month < 10) {
        $cal_month = "0" . $cal_month;
    }

    # 달력 제목 출력
    $result .= "<TABLE class='calendar'>";
    $result .= "<CAPTION class='calendar'>"
        ."<a href=\"$ScriptName?$toc_mainpage/$cal_year-$cal_month\">"
        .(length($toc_mainpage)?"$toc_mainpage<br>":"")
        ."$cal_year-$cal_month"
        ."</a>"
        ."</CAPTION>";

    # 상단의 요일 출력
    $result .= "<TR class='calendar'>";
    for (0..6) {
        $result .= "<TH class='calendar'>"
            . "<span style='color:$cal_color[$_]'>$cal_dow[$_]</span></TH>";
    }
    $result .= "</TR>";

    # 인자로 주어진 달의 1일날을 찾음
    $cal_time = timelocal(0,0,0,1,$cal_month-1,$cal_year);
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($cal_time);
    # 달력의 첫번째 날 찾음
    $cal_time -= $wday * (60 * 60 * 24);
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($cal_time);

    # 달력 그림
    my ($temp_month, $temp_day);

    for (1..6) {
        $result .= "<TR class='calendar'>";
        for (0..6) {

            # 1~9는 01~09로 만듦
            ($temp_month, $temp_day) = ($mon + 1, $mday);
            $temp_month = "0".$temp_month if ($temp_month < 10);
            $temp_day = "0".$temp_day if ($temp_day < 10);
            $cal_page = ($year + 1900)."-".($temp_month)."-".($temp_day);

            $cal_result = $mday;
            $span_style = "";
            if (($year == $this_year) && ($mon == $this_month) && ($mday == $this_day)) {
                $td_class = "calendartoday";
                $span_style = "text-decoration: underline; ";
            } else {
                $td_class = "calendar";
            }

# 해당 날짜에 포스트가 있는 경우
            my ($page, $pagename) = ("", "");
            if ($tocitems =~ /^\[\[(.+?)(\|.*)?\]\] $cal_page$/m) {
                ($page, $pagename) = ($1, $1);
                $page =~ s|^/|$toc_mainpage/|;
                $page = &FreeToNormal($page);
                $span_style .= "font-weight: bold; text-decoration: underline; ";
                $wday = 7;
# 현재 보는 페이지에 해당하는 날짜인 경우
                if ($page eq $OpenPageName) {
                    $td_class .= "thispage";
                }
            }

            if ($cal_month != ($mon+1)) {
                $span_style .= "font-size: 0.9em; ";
            }

            $result .= "<td class='$td_class'>"
                .(($page)?"<a href=\"$ScriptName?$page\" title=\"$pagename\">":"")
                ."<span style='color:$cal_color[$wday]; $span_style'>"
                .$cal_result
                ."</span>"
                .(($page)?"</a>":"")
                ."</td>";
            $cal_time += (60 * 60 * 24);
            ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($cal_time);
        }
        $result .= "</TR>";
        # 4 또는 5 줄로 끝낼 수 있으면 끝냄
        last if (($mon+1 > $cal_month) || ($year+1900 > $cal_year));
    }

    $result .= "</table>";
    return $result;
}

1;

3.3. macros/blog_includeorder.pl 추가

# <blog_includeorder(목차페이지,시작순서,끝순서)>
# 목차페이지를 읽어서 시작순서부터 끝순서까지의 페이지를 include한다.

sub blog_includeorder {
    my ($txt) = @_;

    $txt =~ s/(^|\n)<blog_includeorder\(([^,]+),([-+]?\d+),([-+]?\d+)\)>([\r\f]*\n)/$1.&MacroBlogIncludeOrder($2,$3,$4).$5/geim;

    return $txt;
}

sub MacroBlogIncludeOrder {
    use strict;
    my ($tocpage, $start, $end) = @_;

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "<font color='red'>blog_library.pl not found</font>";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "$toc_mainpage";
    }

    # 조건에 맞는 리스트를 구성
    ($status, @tocitem_List) = &BlogGetListOrder($start, $end, @tocitem_List);
    if (!$status) {
        return "@tocitem_List";
    }

    # 리스트의 각 페이지를 읽어서 include함
    my $txt;
    my ($page, $date);
    foreach my $item (@tocitem_List) {
        if ($item =~ /^(.+)$FS1(.*)$FS1(.+)$/) {
            ($page, $date) = ($1, $3);
        }
        $page =~ s|^/|$toc_mainpage/|;
        $page = &FreeToNormal($page);
        $txt .= &MacroInclude($page);
    }

    return $txt;
}

1;

3.4. macros/blog_includeperiod.pl 추가

# <blog_includeperiod(목차페이지,시작날짜,끝날짜)>
# 목차페이지를 읽어서 시작날짜부터 끝날짜까지의 페이지를 include한다.

sub blog_includeperiod {
    my ($txt) = @_;

    $txt =~ s/(^|\n)<blog_includeperiod\(([^,]+),([\d-]+),([\d-]+)\)>([\r\f]*\n)/$1.&MacroBlogIncludePeriod($2,$3,$4).$5/geim;

    return $txt;
}

sub MacroBlogIncludePeriod {
    use strict;
    my ($tocpage, $startdate, $enddate) = @_;

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "<font color='red'>blog_library.pl not found</font>";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "$toc_mainpage";
    }

    # 조건에 맞는 리스트를 구성
    ($status, @tocitem_List) = &BlogGetListPeriod($startdate, $enddate, @tocitem_List);
    if (!$status) {
        return "@tocitem_List";
    }

    # 리스트의 각 페이지를 읽어서 include함
    my $txt;
    my ($page, $date);
    foreach my $item (@tocitem_List) {
        if ($item =~ /^(.+)$FS1(.*)$FS1(.+)$/) {
            ($page, $date) = ($1, $3);
        }
        $page =~ s|^/|$toc_mainpage/|;
        $page = &FreeToNormal($page);
        $txt .= &MacroInclude($page);
    }

    return $txt;
}

1;

3.5. macros/blog_listorder.pl 추가

# <blog_listorder(목차페이지,시작순서,끝순서[,날짜출력방식])>
# 목차페이지를 읽어서 시작순서부터 끝순서까지의 페이지의 목록을 출력

sub blog_listorder {
    my ($txt) = @_;

    $txt =~ s/\&__LT__;blog_listorder\(([^,]+),([-+]?\d+),([-+]?\d+)(,([-+]?\d))?\)\&__GT__;/&MacroBlogListOrder($1,$2,$3,$5)/gei;

    return $txt;
}

sub MacroBlogListOrder {
    use strict;
    my ($tocpage, $start, $end, $showdate) = @_;

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "<font color='red'>blog_library.pl not found</font>";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "$toc_mainpage";
    }

    # 조건에 맞는 리스트를 구성
    ($status, @tocitem_List) = &BlogGetListOrder($start, $end, @tocitem_List);
    if (!$status) {
        return "@tocitem_List";
    }

    # 리스트의 각 페이지를 목록 출력
    my $txt;
    $txt = "<UL>";
    my ($page, $pagename, $date, $pageid);
    foreach my $item (@tocitem_List) {
        if ($item =~ /^(.+)$FS1(.*)$FS1(.+)$/) {
            ($page, $pagename, $date) = ($1, $2, $3);
        }
        $pageid = $page;
        $pageid =~ s|^/|$toc_mainpage/|;
        $pageid = &FreeToNormal($pageid);
        $page = $pagename if ($pagename);
        if ($showdate == 0) {
            $txt .= "<LI>".&GetPageOrEditLink($pageid,$page);
        } elsif ($showdate < 0) {
            $txt .= "<LI>($date) ".&GetPageOrEditLink($pageid,$page);
        } else {
            $txt .= "<LI>".&GetPageOrEditLink($pageid,$page)." ($date)";
        }

    }
    $txt .= "</UL>";

    return $txt;
}

1;

3.6. macros/blog_listperiod.pl 추가

# <blog_listperiod(목차페이지,시작날짜,끝날짜[,날짜출력방식])>
# 목차페이지를 읽어서 시작날짜부터 끝날짜까지의 페이지의 목록을 출력

sub blog_listperiod {
    my ($txt) = @_;

    $txt =~ s/\&__LT__;blog_listperiod\(([^,]+),([\d-]+),([\d-]+)(,([-+]?\d))?\)\&__GT__;/&MacroBlogListPeriod($1,$2,$3,$5)/gei;

    return $txt;
}

sub MacroBlogListPeriod {
    use strict;
    my ($tocpage, $startdate, $enddate, $showdate) = @_;

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "<font color='red'>blog_library.pl not found</font>";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "$toc_mainpage";
    }

    # 조건에 맞는 리스트를 구성
    ($status, @tocitem_List) = &BlogGetListPeriod($startdate, $enddate, @tocitem_List);
    if (!$status) {
        return "@tocitem_List";
    }

    # 리스트의 각 페이지를 목록 출력
    my $txt;
    $txt = "<UL>";
    my ($page, $pagename, $date, $pageid);
    foreach my $item (@tocitem_List) {
        if ($item =~ /^(.+)$FS1(.*)$FS1(.+)$/) {
            ($page, $pagename, $date) = ($1, $2, $3);
        }
        $pageid = $page;
        $pageid =~ s|^/|$toc_mainpage/|;
        $pageid = &FreeToNormal($pageid);
        $page = $pagename if ($pagename);
        if ($showdate == 0) {
            $txt .= "<LI>".&GetPageOrEditLink($pageid,$page);
        } elsif ($showdate < 0) {
            $txt .= "<LI>($date) ".&GetPageOrEditLink($pageid,$page);
        } else {
            $txt .= "<LI>".&GetPageOrEditLink($pageid,$page)." ($date)";
        }
    }
    $txt .= "</UL>";

    return $txt;
}

1;

3.7. macros/blog_prevnext.pl 추가

# <blog_prevnext(목차페이지)>
# 목차페이지를 읽어서, 현재 보고 있는 페이지의 이전 페이지와 다음 페이지의 링크를 출력

sub blog_prevnext {
    my ($txt) = @_;

    $txt =~ s/&__LT__;blog_prevnext\((.*?)\)&__GT__;/&MacroBlogPrevNext($1)/gei;

    return $txt;
}

sub MacroBlogPrevNext {
    use strict;
    my ($tocpage) = @_;
    my ($mainpage, $subpage);
    my $txt;

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "<font color='red'>blog_library.pl not found</font>";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "$toc_mainpage";
    }

    if ($OpenPageName =~ m|(.*)/(.*)|) {
        ($mainpage, $subpage) = ($1,$2);
    } else {
        $mainpage = $OpenPageName;
    }

    # 목차에서 현재 페이지의 위치를 찾음
    my $idx = 0;
    for ($idx = 0; $idx <= $#tocitem_List; $idx++) {
        my $line = $tocitem_List[$idx];
        if ($line =~ m/\[\[$FreeLinkPattern(\|[^\]]+)?\]\]/) {
            my $link = $1;
            $link =~ s/ /_/g;
            if (($link eq $OpenPageName) || ($link eq "/$subpage")) {
                last;
            }
        } elsif ($line =~ m/$LinkPattern/) {
            my $link = $1;
            $link =~ s/ /_/g;
            if (($link eq $OpenPageName) || ($link eq "/$subpage")) {
                last;
            }
        }
    }

    # 이전페이지와 다음페이지 이름 찾음
    my ($prev, $next);
    if ($idx > $#tocitem_List) {
        return "";
#       return "[Not found this page:$OpenPageName in TOC]";
    }

    my $item;
    my ($thispage, $thispagename, $thisdate);
    $item = $tocitem_List[$idx];
    if ($item =~ /\[\[(.+?)(\|.*)?\]\] (\d+-\d+-\d+)/) {
        ($thispage, $thispagename, $thisdate) = ($1, $1, $3);
        $thispage =~ s|^/|$toc_mainpage/|;
        $thispage = &FreeToNormal($thispage);
    }

    my ($prevpage, $prevpagename, $prevdate);
    my ($nextpage, $nextpagename, $nextdate);
    if ($idx > 0) {
        $item = $tocitem_List[$idx-1];
        if ($item =~ /\[\[(.+?)(\|.*)?\]\] (\d+-\d+-\d+)/) {
            ($prevpage, $prevpagename, $prevdate) = ($1, $1, $3);
            $prevpage =~ s|^/|$toc_mainpage/|;
            $prevpage = &FreeToNormal($prevpage);
        }
    }
    if ($idx < $#tocitem_List) {
        $item = $tocitem_List[$idx+1];
        if ($item =~ /\[\[(.+?)(\|.*)?\]\] (\d+-\d+-\d+)/) {
            ($nextpage, $nextpagename, $nextdate) = ($1, $1, $3);
            $nextpage =~ s|^/|$toc_mainpage/|;
            $nextpage = &FreeToNormal($nextpage);
        }
    }

    # 출력
    my $shortCutUrl = "$ScriptName".&ScriptLinkChar();
    my $shortCutKey;
    $txt = "<CENTER>";
    if ($prevpage) {
        $txt .= "<b>&lt;&lt;</b>&nbsp;&nbsp; ".
            &GetPageOrEditLink($prevpage, $prevpagename).
            " ($prevdate)[p]";
        $shortCutKey .= "key['p'] = \"${shortCutUrl}$prevpage\";\n"
    }
    $txt .= " &nbsp;&nbsp<b>|</b> $thispagename ($thisdate) <b>|</b>&nbsp;&nbsp; ";
    if ($nextpage) {
        $txt .= &GetPageOrEditLink($nextpage, $nextpagename).
            " ($nextdate)[n]".
            " &nbsp;&nbsp;<b>&gt;&gt;</b>";
        $shortCutKey .= "key['n'] = \"${shortCutUrl}$nextpage\";\n"
    }
    $txt .= "</CENTER>\n";
    if ($UseShortcut && $shortCutKey) {
        $txt .= "<script>\n".
            "<!--\n".
            $shortCutKey.
            "-->\n".
            "</script>";
    }

    return $txt;
}

1;

3.8. macors/blog_prevnextmonth.pl 추가

# <blog_prevnextmonth>
# 이전 달과 다음 달로 가는 링크를 만듦

sub blog_prevnextmonth {
    my ($txt) = @_;

    $txt =~ s/&__LT__;blog_prevnextmonth&__GT__;/&MacroBlogPrevNextMonth()/gei;

    return $txt;
}

sub MacroBlogPrevNextMonth {
    use strict;

    if ($OpenPageName =~ m|^(.*/)?(\d{4})-(\d{2})$|) {
        my ($mainpage, $year, $month) = ($1, $2, $3);
        my ($prev_year, $prev_month) = ($year, $month - 1);
        my ($next_year, $next_month) = ($year, $month + 1);
        if ($prev_month <= 0) {
            $prev_year--;
            $prev_month = 12;
        } elsif ($next_month >= 13) {
            $next_year++;
            $next_month = 1;
        }
        if ($prev_month < 10) {
            $prev_month = "0" . $prev_month;
        }
        if ($next_month < 10) {
            $next_month = "0" . $next_month;
        }

        my $prev_page = "$mainpage$prev_year-$prev_month";
        my $next_page = "$mainpage$next_year-$next_month";

        my $shortCutUrl = "$ScriptName".&ScriptLinkChar();
        my $txt;
        $txt = "<CENTER>".
            "<b>&lt;&lt;&lt;</b>&nbsp;&nbsp; ".
            &GetPageOrEditLink($next_page).
            " [p]".
            " &nbsp;&nbsp<b>|</b> ".T('Monthly View')." <b>|</b>&nbsp;&nbsp; ".
            &GetPageOrEditLink($prev_page).
            " [n]".
            " &nbsp;&nbsp;<b>&gt;&gt;&gt;</b>".
            "</CENTER>";

        $txt .= <<EOF;
<script>
<!--
key['p'] = "${shortCutUrl}$next_page";
key['n'] = "${shortCutUrl}$prev_page";
-->
</script>
EOF
        return $txt;
    } else {
        return "";
    }
}

1;

3.9. macros/blog_library.pl 추가

# blog_library.pl
# blog_*** 매크로 시리즈들이 공통으로 사용하는 함수들을 모아 둔 라이브러리


# param: 목차페이지
# return: ($status, $page, @list);
#  $status : 성공하면 1, 실패하면 0
#  $page : 성공하면 목차페이지의 상위페이지 이름, 실패하면 에러메시지
#  @list : 목차페이지로부터 읽은 목차 리스트
sub BlogReadToc($tocpage) {
    use strict;
    my ($tocpage) = @_;

    # 목차페이지 이름 분석
    $tocpage = &FreeToNormal(&RemoveLink($tocpage));
    if (my $temp = &ValidId($tocpage)) {
        return (0, "<font color='red'>$temp</font>");
    }

    my ($toc_mainpage, $toc_subpage);
    if ($tocpage =~ m|(.*)/(.*)|) {
        ($toc_mainpage, $toc_subpage) = ($1,$2);
    } else {
        $toc_mainpage = $tocpage;
    }

    # 페이지 목록 읽음
    my ($fname, $status, $data);
    $fname = &GetPageFile($tocpage);
    if (!(-f $fname)) {
        return (0, "<font color='red'>No such page: $tocpage</font>");
    }

    ($status, $data) = &ReadFile($fname);
    if (!$status) {
        return (0, "<font color='red'>Error in read pagefile: $tocpage</font>");
    }

    my %temp_Page = split(/$FS1/, $data, -1);
    my %temp_Section = split(/$FS2/, $temp_Page{'text_default'}, -1);
    my %temp_Text = split(/$FS3/, $temp_Section{'data'}, -1);
    my $tocpage_Text = $temp_Text{'text'};

    # 라인 별로 분리
    my @tocpage_Lines = split('\n',$tocpage_Text);
    my @tocitem_List;

    # 유효한 라인만 추출
    foreach my $line (@tocpage_Lines) {
        if ($line =~ m/^* (\[\[.+?\]\] \d+-\d+-\d+)\s*$/) {
            push(@tocitem_List, $1);
        }
    }

    return (1, $toc_mainpage, @tocitem_List);
}


# param: 시작순서, 끝순서, 목차리스트
# return: ($status, @list)
#  $status : 성공하면 1, 실패하면 0
#  @list : 성공하면 시작순서부터 끝순서까지의 리스트. 실패하면 에러메시지
sub BlogGetListOrder {
    use strict;
    my ($start, $end, @tocitem_List) = @_;

    if (($start == 0) || ($end == 0)) {
        return (0, "<font color='red'>Invalid parameter: 0</font>");
    }

    if ($start > 0) {
        $start--;
    } else {
        $start = $#tocitem_List + $start + 1;
    }
    if ($end > 0) {
        $end--;
    } else {
        $end = $#tocitem_List + $end + 1;
    }
    $start = 0 if ($start < 0);
    $start = $#tocitem_List if ($start > $#tocitem_List);
    $end = 0 if ($end < 0);
    $end = $#tocitem_List if ($end > $#tocitem_List);

    if ($start <= $end) {
        @tocitem_List = @tocitem_List[$start .. $end];
    } else {
        @tocitem_List = reverse(@tocitem_List[$end .. $start]);
    }

    my @list;
    my ($page, $pagename, $date);
    foreach my $item (@tocitem_List) {
        if ($item =~ /\[\[(.+?)(\|.*)?\]\] (\d+)-(\d+)-(\d+)/) {
            $page = $1;
            $pagename = $2;
            $date = sprintf("%4d-%02d-%02d",$3,$4,$5);
            $pagename =~ s/^\|//;
            push(@list, "$page$FS1$pagename$FS1$date");
        }
    }
    return ("1", @list);
}


# param: 시작날짜, 끝날짜, 목차리스트
# return: ($status, @list)
#  $status : 성공하면 1, 실패하면 0
#  @list : 성공하면 시작날짜부터 끝날짜까지의 리스트. 실패하면 에러메시지
sub BlogGetListPeriod {
    use strict;
    my ($startdate, $enddate, @tocitem_List) = @_;

    if ($startdate =~ /^(\d{4})-(\d{1,2})-(\d{1,2})$/) {
        $startdate = sprintf("%4d%02d%02d",$1,$2,$3);
    } else {
        return (0,"<font color='red'>Invalid parameter: $startdate</font>");
    }
    if ($enddate =~ /^(\d+)-(\d+)-(\d+)$/) {
        $enddate = sprintf("%4d%02d%02d",$1,$2,$3);
    } else {
        return (0,"<font color='red'>Invalid parameter: $enddate</font>");
    }

    my @list;
    my ($page, $pagename, $date, $reverse);
    if ($startdate > $enddate) {
        ($startdate, $enddate, $reverse) = ($enddate, $startdate, 1);
    }
    foreach my $item (@tocitem_List) {
        if ($item =~ /\[\[(.+?)(\|.*)?\]\] (\d+)-(\d+)-(\d+)/) {
            $page = $1;
            $pagename = $2;
            $date = sprintf("%4d%02d%02d",$3,$4,$5);
            last if ($date < $startdate);
            if ($date <= $enddate) {
                $date = sprintf("%4d-%02d-%02d",$3,$4,$5);
                $pagename =~ s/^\|//;
                push(@list, "$page$FS1$pagename$FS1$date");
            }
        }
    }
    @list = reverse @list if ($reverse);
    return (1, @list);
}

1;

3.10. action/blog_newpost.pl 추가

sub action_blog_newpost {
    use strict;
    my $id = &GetParam("id","");
    my $pageid = &GetParam("pageid","");
    my $title = &GetParam("title","");
    my $date = &GetParam("date","");

    $title =~ s/^\s*//g;
    $title =~ s/\s*$//g;
    $title = "[[/$title]]";

    if ($date =~ /^(\d+)-(\d+)-(\d+)$/) {
        $date = sprintf("%4d-%02d-%02d",$1,$2,$3);
    }

    &OpenPage($id);
    &OpenDefaultText();
    my $string = $Text{'text'};

    $string =~ s/(<blog_newpost\($id\)>)/$1\n* $title $date/;

    if (!&UserCanEdit($id,1)) {
        $pageid = "";
    }

    &DoPostMain($string, $id, "*", $Section{'ts'}, 0, 1, $pageid);
    return;
}

1;

3.11. action/comments.pl 수정

"블로그 지원을 위한 꽁수"라고 적은 단락을 추가

sub action_comments {
    use strict;
    my $id = &GetParam("id", "");
    my $pageid = &GetParam("pageid", "");
    my $name = &GetParam("name", "");
    my $newcomments = &GetParam("comment", "");
    my $up   = &GetParam("up", "");
    my ($timestamp) = CalcDay($Now) . " " . CalcTime($Now);
    my $string;
    my $long = &GetParam("long", "");

    &ValidIdOrDie($id);

    # 블로그 지원을 위한 꽁수
    my ($blogrcpage, $blogrccomment);
    if ($id =~ m/(.+)(\/|%2f|%2F)(.+)/) {
        $blogrcpage = "$1/BlogRc";
    } else {
        $blogrcpage = "BlogRc";
    }
    if (-f &GetPageFile($blogrcpage)) {
        $blogrccomment = $newcomments;
    } else {
        $blogrcpage = "";
    }

    # thread
    my $threadindent = &GetParam("threadindent", "");
    my $abs_up = abs($up);
    my ($threshold1, $threshold2) = (100000000, 1000000000);

    if ($newcomments =~ /^\s*$/) {
        &ReBrowsePage($pageid, "", 0);
        return;
    }

    $name = &GetRemoteHost(0) if ($name eq "");
    $name =~ s/,/./g;
    $newcomments = &QuoteHtml($newcomments);

    &OpenPage($id);
    &OpenDefaultText();
    $string = $Text{'text'};

    if ($threadindent ne '') {      # thread
        $newcomments =~ s/^\s*//g;
        $newcomments =~ s/\s*$//g;
        $newcomments =~ s/(\n)\s*(\r?\n)/$1$2/g;
        $newcomments =~ s/(\r?\n)/ \\\\$1/g;

        my ($comment_head, $comment_tail) = ("", "");
        my $newup;

        if (($abs_up >= 100) && ($abs_up <= $threshold2)) { # 커멘트 권한 상속
            $newup = $Now - $threshold2;
        } else {
            $newup = $Now;
        }

        $comment_tail = "<thread($id,$newup," . ($threadindent+1) . ")>";

        if ($threadindent >= 1) {
            for (1 .. $threadindent) {
                $comment_head .= ":";
            }
            $comment_head .= " ";
        } else {    # 새글
#           $comment_head = "<thread>\n";
#           $comment_tail .= "\n</thread>";
        }

        if (($up > 0) && ($up < $threshold1)) {     # 위로 달리는 새글
            $string =~ s/(\<thread\($id,$up(,\d+)?\)\>)/$comment_head$newcomments <mysign($name,$timestamp)>\n$comment_tail\n\n$1/;
        } else {                                    # 리플 or 아래로 달리는 새글
            $string =~ s/(\<thread\($id,$up(,\d+)?\)\>)/$1\n\n$comment_head$newcomments <mysign($name,$timestamp)>\n$comment_tail/;
        }
    } elsif ($long) {               # longcomments
        $newcomments =~ s/^\s*//g;
        $newcomments =~ s/\s*$//g;
        $newcomments =~ s/(\n)\s*(\r?\n)/$1$2/g;
        $newcomments =~ s/(\r?\n)/ \\\\$1/g;

        if ($up > 0) {
            $string =~ s/(\<longcomments\($id,$up\)\>)/\n$newcomments <mysign($name,$timestamp)>\n$1/;
        } else {
            $string =~ s/(\<longcomments\($id,$up\)\>)/$1\n$newcomments <mysign($name,$timestamp)>\n/;
        }
    } else {                        # comments
        $newcomments =~ s/(----+)/<nowiki>$1<\/nowiki>/g;
        if ($up > 0) {
            $string =~ s/\<comments\($id,$up\)\>/* ''' $name ''' : $newcomments - <small>$timestamp<\/small>\n\<comments\($id,$up\)\>/;
        } else {
            $string =~ s/\<comments\($id,$up\)\>/\<comments\($id,$up\)\>\n* ''' $name ''' : $newcomments - <small>$timestamp<\/small>/;
        }
    }

    if (((!&UserCanEdit($id,1)) && (($abs_up < 100) || ($abs_up > $threshold2))) || (&UserIsBanned())) {        # 에디트 불가
        $pageid = "";
    }

# 블로그 지원을 위한 꽁수
    if ($pageid && $blogrcpage) {
        $blogrccomment =~ s/(\r?\n)/ /g;
        $blogrccomment =~ s/\[/{/g;
        $blogrccomment =~ s/\]/}/g;
        if (length($blogrccomment) > 30) {
            $blogrccomment = substr($blogrccomment, 0, 27);
            $blogrccomment =~ s/(([\x80-\xff].)*)[\x80-\xff]?$/$1/;
            $blogrccomment .= "...";
        }
        $blogrccomment = &QuoteHtml($blogrccomment);
        $blogrccomment =~ s/----+/---/g;
        $blogrccomment =~ s/^ *//;
        $blogrccomment = "C) $blogrccomment";

        my ($fname, $status, $data);
        $fname = &GetPageFile($blogrcpage);
        if (-f $fname) {
            ($status, $data) = &ReadFile($fname);
            if ($status) {
                my %temp_Page = split(/$FS1/, $data, -1);
                my %temp_Section = split(/$FS2/, $temp_Page{'text_default'}, -1);
                my %temp_Text = split(/$FS3/, $temp_Section{'data'}, -1);
                my $blogrc_Text = $temp_Text{'text'};

                my $date = &CalcDayNow();
                if ($date =~ /(\d+)-(\d+)-(\d+)/) {
                    $date = sprintf("%4d-%02d-%02d",$1,$2,$3);
                }
                if ($blogrc_Text =~ /^\*/m) {
                    $blogrc_Text =~ s/^\*/* [[$id|$blogrccomment]] $date\n*/m;
                } else {
                    $blogrc_Text .= "\n* [[$id|$blogrccomment]] $date";
                }
                my $backup = $Section{'ts'};
                &DoPostMain($blogrc_Text, $blogrcpage, "", $temp_Section{'ts'}, 0, 1, "!!");
                $Section{'ts'} = $backup;
            }
        }
    }

    DoPostMain($string, $id, "*", $Section{'ts'}, 0, 0, $pageid);
    return;
}

1;

3.12. action/trackback.pl 수정

"블로그 지원을 위한 꽁수"라고 적은 단락을 추가

sub action_trackback {
    use strict;
    my $id = &GetParam("id","");
    my $normal_id = $id;

    my $url = &GetParam('url');
    my $title = &GetParam('title', $url);
    my $blog_name = &GetParam('blog_name');
    my $excerpt = &GetParam('excerpt');

# 블로그 지원을 위한 꽁수
    my ($blogrcpage, $blogrccomment);
    if ($id =~ m/(.+)(\/|%2f|%2F)(.+)/) {
        $blogrcpage = "$1/BlogRc";
    } else {
        $blogrcpage = "BlogRc";
    }
    if (-f &GetPageFile($blogrcpage)) {
        $blogrccomment = $excerpt;
    } else {
        $blogrcpage = "";
    }

    if (length($excerpt) > 255) {
        $excerpt = substr($excerpt, 0, 252);
        $excerpt =~ s/(([\x80-\xff].)*)[\x80-\xff]?$/$1/;
        $excerpt .= "...";
    }
    $excerpt =~ s/(\r?\n)/ /g;
    $excerpt = &QuoteHtml($excerpt);
    $excerpt = "<nowiki>$excerpt</nowiki>";

    if ($FreeLinks) {
        $normal_id = &FreeToNormal($id);
    }

    if ($url eq '') {
        &SendTrackbackResponse("1", "No URL (url)");
    } elsif ($id eq '') {
        &SendTrackbackResponse("1", "No Pagename (id)");
    } elsif (!&PageCanReceiveTrackbackPing($normal_id)) {
        &SendTrackbackResponse("1", "Invalid Pagename (Page is missing, or Trackback is not allowed)");
    } elsif (my $bannedText = &TextIsBanned($blog_name.$url.$title.$excerpt)) {
        &SendTrackbackResponse("1", "[$bannedText] is a Banned text");
    } else {
        &OpenPage($normal_id);
        &OpenDefaultText();
        my $string = $Text{'text'};
        my $macro = "\<trackbackreceived\>";
        if (!($string =~ /$macro/)) {
            $string .= "\n$macro\n";
        }
        my $timestamp = &CalcDay($Now) . " " . &CalcTime($Now);
        my $newtrackbackreceived = "* " .
            &Ts('Trackback from %s', "'''<nowiki>$blog_name</nowiki>'''") .
            " $timestamp\n" .
            "** " . &T('Title:') . " [$url $title]\n" .
            "** " . &T('Content:') . " $excerpt";
        $string =~ s/($macro)/$newtrackbackreceived\n$1/;

# 블로그 지원을 위한 꽁수
        if ($blogrcpage) {
            $blogrccomment =~ s/(\r?\n)/ /g;
            $blogrccomment =~ s/\[/{/g;
            $blogrccomment =~ s/\]/}/g;
            if (length($blogrccomment) > 30) {
                $blogrccomment = substr($blogrccomment, 0, 27);
                $blogrccomment =~ s/(([\x80-\xff].)*)[\x80-\xff]?$/$1/;
                $blogrccomment .= "...";
            }
            $blogrccomment = &QuoteHtml($blogrccomment);
            $blogrccomment =~ s/----+/---/g;
            $blogrccomment =~ s/^ *//;
            $blogrccomment = "T) $blogrccomment";

            my ($fname, $status, $data);
            $fname = &GetPageFile($blogrcpage);
            if (-f $fname) {
                ($status, $data) = &ReadFile($fname);
                if ($status) {
                    my %temp_Page = split(/$FS1/, $data, -1);
                    my %temp_Section = split(/$FS2/, $temp_Page{'text_default'}, -1);
                    my %temp_Text = split(/$FS3/, $temp_Section{'data'}, -1);
                    my $blogrc_Text = $temp_Text{'text'};

                    my $date = &CalcDayNow();
                    if ($date =~ /(\d+)-(\d+)-(\d+)/) {
                        $date = sprintf("%4d-%02d-%02d",$1,$2,$3);
                    }
                    if ($blogrc_Text =~ /^\*/m) {
                        $blogrc_Text =~ s/^\*/* [[$id|$blogrccomment]] $date\n*/m;
                    } else {
                        $blogrc_Text .= "\n* [[$id|$blogrccomment]] $date";
                    }
                    my $backup = $Section{'ts'};
                    &DoPostMain($blogrc_Text, $blogrcpage, "", $temp_Section{'ts'}, 0, 1, "!!");
                    $Section{'ts'} = $backup;
                }
            }
        }

        &DoPostMain($string, $id, &T('New Trackback Received'), $Section{'ts'}, 0, 0, "!!");
        &SendTrackbackResponse(0, "");
    }
}

sub SendTrackbackResponse {
    my ($code, $message) = @_;

    if ($code == 0) {
        print <<END;
Content-Type: application/xml; charset: iso-8859-1\n
<?xml version="1.0" encoding="iso-8859-1"?>
<response>
<error>0</error>
</response>
END
    } else {
        print <<END;
Content-Type: application/xml; charset: iso-8859-1\n
<?xml version="1.0" encoding="iso-8859-1"?>
<response>
<error>$code</error>
<message>$message</message>
</response>
END
    }
}

1;

3.13. macros/blog_rss.pl 추가

# <blog_rss(목차페이지[,블로그페이지])>
# XML아이콘과 RSS의 URL 링크를 출력

sub blog_rss {
    my ($txt) = @_;

    $txt =~ s/\&__LT__;blog_rss\((.*?)\)&__GT__;/&MacroBlogRss($1)/gei;
    $txt =~ s/&__LT__;blog_rss&__GT__;.*?&__LT__;\/blog_rss&__GT__;//geis;

    return $txt;
}

sub MacroBlogRss {
    use strict;
    my ($arg) = @_;
    my $txt;

    my ($listpage, $blogpage);
    if ($arg =~ /^([^,]+)(,([^,]+))?$/) {
        ($listpage, $blogpage) = ($1, $3);
    } else {
        return "<font color='red'>Invalid args</font>";
    }

    my $blogname;
    $listpage = &RemoveLink($listpage);
    $blogname = $listpage;
    $listpage = &FreeToNormal($listpage);
    $blogpage = &RemoveLink($blogpage);
    $blogname = $blogpage if ($blogpage);
    $blogpage = &FreeToNormal($blogpage);

    $txt = &ScriptLink("action=blog_rss&listpage=$listpage&blogpage=$blogpage",
            "<img align='absmiddle' src='$IconDir/xml_rss.gif'> Get RSS of $blogname");

    return $txt;
}

1;

3.14. action/blog_rss.pl 추가

# blog_rss 액션

my @ChannelField = ('title','link','description','language',
        'copyright','manageingEditor','webMaster','pubDate',
        'lastBuildDate','category','generator','docs','could',
        'ttl','image','rating','textInput','skipHours','skipDays');
my @ItemField = ('title','link','description','author','category',
        'comments','enclosure','guid','pubDate','source');
my %NeedCdata = map { $_ => 1 } ('description');    # CDATA 로 묶어 줘야 할 필드
my (%RssChannelField, %RssItemFieldInList, %RssItemField, $ListPageAuthor);

sub action_blog_rss {
    use strict;
    my $listpage = &GetParam("listpage","");        # 참조할 목차 페이지
    my $blogpage = &GetParam("blogpage","");        # RSS의 link항목에 들어갈 페이지
    my $num_items = &GetParam("items",15);          # xml파일의 item 갯수

    my $xml = "";

    my $cachefile = $listpage."__".$blogpage."__".$num_items;
    $cachefile =~ s/(\W)/uc sprintf "_%02x", ord($1)/eg;
    $cachefile = "$TempDir/rss_$cachefile.xml";

    if (-f $cachefile) {
        # cache 파일의 마지막 수정 시각 이후에 사이트에 변동이 없는 경우
        # cache 파일을 읽어서 출력
        my $cache_mtime = (stat($cachefile))[9];
        my $rclog_mtime = (stat($RcFile))[9];
        if ($cache_mtime > $rclog_mtime) {
            my ($status, $data) = &ReadFile($cachefile);
            if ($status) {
                $xml = $data;
            }
        }
    }

    if ($xml eq "") {
        # xml을 새로 생성함
        my ($rssHeader, $rssBody, $rssFooter);

# 채널 정보의 디폴트 값을 먼저 설정
# 사이트 제목
        $RssChannelField{'title'} = &QuoteHtml($SiteName);
# 사이트 링크
        $FullUrl = $q->url(-full=>1)  if ($FullUrl eq "");
        $QuotedFullUrl = &QuoteHtml($FullUrl);
        $RssChannelField{'link'} = $QuotedFullUrl;
        $RssChannelField{'link'} .= &ScriptLinkChar() . $blogpage if ($blogpage);
# 사이트 설명
        $RssChannelField{'description'} = $SiteDescription;
# 언어 - how to detect?
        $RssChannelField{'language'} = "ko";
# xml작성 시각
        $RssChannelField{'pubDate'} = &BlogRssGetPubDate($Now);

# 리스트 페이지에 사용자가 정의한 값을 읽어서 덮어 씀
        &OpenPage($listpage);
        &OpenDefaultText;
        $ListPageAuthor = $Section{'username'};
        &BlogRssGetUserDefinedValue($Text{'text'}, "list");

# rss header 생성
        $rssHeader = <<EOF;
<?xml version="1.0" encoding="$HttpCharset" ?>
<rss version="2.0">
<channel>
EOF
        foreach my $field (@ChannelField) {
            if ($RssChannelField{$field} ne "") {
                $rssHeader .=
                    "<$field>".
                    (($NeedCdata{$field})?("<![CDATA[".$RssChannelField{$field}."]]>"):($RssChannelField{$field})).
                    "</$field>".
                    "\n";
            }
        }

# rss footer 생성
        $rssFooter = <<EOF;
</channel>
</rss>
EOF

# header와 footer사이의 body 생성
        $rssBody = &BlogRssGetItems($listpage, $num_items);

# 전체 xml 생성
        $xml = $rssHeader.
            $rssBody.
            $rssFooter;

# cache 파일에 저장
        &WriteStringToFile($cachefile, $xml);
    }

# 최종 출력
    print "Content-type: text/xml\n\n";
    print $xml;

    return;
}


# param: 목차페이지, 아이템 갯수
# return: $txt
#  $txt : Rss 파일의 <item>...</item>항목들
sub BlogRssGetItems {
    use strict;
    my ($tocpage, $num_items) = @_;

    # 라이브러리 읽음
    my ($MacrosDir, $MyMacrosDir) = ("./macros/", "./mymacros/");
    if (-f "$MyMacrosDir/blog_library.pl") {
        require "./$MyMacrosDir/blog_library.pl";
    } elsif (-f "$MacrosDir/blog_library.pl") {
        require "./$MacrosDir/blog_library.pl";
    } else {
        return "";
    }

    # 목차페이지로부터 목차리스트를 얻어냄
    my ($status, $toc_mainpage, @tocitem_List) = &BlogReadToc($tocpage);
    if (!$status) {
        return "";
    }

    # 조건에 맞는 리스트를 구성
    ($status, @tocitem_List) = &BlogGetListOrder(1, $num_items, @tocitem_List);
    if (!$status) {
        return "";
    }

    # 리스트의 각 페이지를 읽어서 item 형식으로 만들어 반환함
    my $txt;

    my ($page, $pagename, $date, $pageid);
    foreach my $item (@tocitem_List) {
        if ($item =~ /^(.+)$FS1(.*)$FS1(.+)$/) {
            ($page, $pagename, $date) = ($1, $2, $3);
        }
        $pageid = $page;
        $pageid =~ s|^/|$toc_mainpage/|;
        $pageid = &FreeToNormal($pageid);

# 페이지가 존재하지 않으면 통과
        next if (not -f &GetPageFile($pageid));

# 아이템 필드 초기화
        %RssItemField = %RssItemFieldInList;

# 아이템 정보의 디폴트 값을 먼저 설정
        &OpenPage($pageid);
        &OpenDefaultText();
# 제목
        $RssItemField{'title'} = $page;
# 링크
        $RssItemField{'link'} = $QuotedFullUrl.&ScriptLinkChar().&EncodeUrl($pageid);
# 내용
        my $description = $Text{'text'};
        $description =~ s/<noinclude>.*?<\/noinclude>//igs;
        $description =~ s/<blog_rss>.*?<\/blog_rss>//igs;
        $description =~ s/\n/<br \/>\n/g;
        $RssItemField{'description'} = $description;
# 작성자 - 목차 페이지에 명시되어 있으면 그 값 사용.
#          없으면 목차 페이지의 마지막 수정자의 아이디를 사용.
        if (not $RssItemField{'author'}) {
            $RssItemField{'author'} = $ListPageAuthor;
        }
# 카테고리 - 대책없음
#       $RssItemField{'category'} = "";
# 작성시각
        $RssItemField{'pubDate'} = &BlogRssGetPubDate($Page{'tscreate'});

# 포스트 페이지에 사용자가 정의한 값을 읽어서 덮어 씀
        &BlogRssGetUserDefinedValue($Text{'text'});

        $txt .= "<item>\n";
        foreach my $field (@ItemField) {
            if ($RssItemField{$field} ne "") {
                $txt .=
                    "<$field>".
                    (($NeedCdata{$field})?"<![CDATA[".$RssItemField{$field}."]]>":$RssItemField{$field}).
                    "</$field>".
                    "\n";
            }
        }
        $txt .= "</item>\n";

    }

    return $txt;
}


# param: timestamp값
# return: $pubDate
#  $pubDate : <pubDate>항목 안에 들어갈 날짜와 시각 포맷
sub BlogRssGetPubDate {
    my ($ts) = @_;

    my @dow = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
    my @month = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
    my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime($ts);
    my $pubDate = sprintf("%3s, %02d %3s %04d %02d:%02d:%02d +%02d00",
            $dow[$wday], $mday, $month[$mon], $year+1900, $hour, $min, $sec, $RssTimeZone);

    return $pubDate;
}


# param: 텍스트[,출처]
# 텍스트에서 <blog_rss> </blog_rss> 부분을 찾아서 파싱하여 전역변수에 저장
sub BlogRssGetUserDefinedValue {
    my ($text, $where) = @_;
    my ($text_channel, $text_item);

    my $text_blog;
    while ($text =~ /<blog_rss>(.+?)<\/blog_rss>/igs) {
        $text_blog .= $1;
    }
    while ($text_blog =~ /<channel>(.+?)<\/channel>/igs) {
        $text_channel .= $1;
    }
    while ($text_channel =~ s/<item>(.+?)<\/item>//igs) {
        $text_item .= $1;
    }

    while ($text_channel =~ /<(.+?)>(.+?)<\/\1>/gs) {
        $RssChannelField{$1} = $2;
    }
    while ($text_item =~ /<(.+?)>(.+?)<\/\1>/gs) {
        if ($where eq "list") {
            $RssItemFieldInList{$1} = $2;
        } else {
            $RssItemField{$1} = $2;
        }
    }
}

1;

4. 추가 업데이트 내역

ext1.82a - 목차 페이지에 [[페이지이름|문자열]] 형식으로 쓰여진 것을 처리할 수 있게 함.

ext1.82c - blog_prevnextmonth 매크로 추가.

ext1.82d - /Comments매크로/TrackBack 코드를 고쳐서, 코멘트나 트랙백이 달릴 때 그 내역을 기록하게 함.

ext1.83 - blog_rss 매크로, blog_rss 액션 추가

ext1.83a - 사용자가 RSS출력을 제어할 수 있게 함.

ext1.90 - /매크로파라메터에이중대괄호허용

-- Raymundo 2005-11-19 6:07 pm

ext2.1b - blog_includeperiod나 blog_listperiod에서, 시작날짜와 끝날짜를 거꾸로 주면 출력순서도 반대가 되게 함

ext2.1d - blog_includeperiod, blog_includeorder 매크로는 다른 include시리즈처럼 한 줄에 그 매크로만 있고 좌우에 다른 문자나 공백이 없을 때만 동작하게 함.

5. 사용자 의견

다음 두 가지에 대해 누가 좀 가르쳐 주세요.

1. 각 글의 description 항목에서, 태터와 이글루스를 보면

<item>
<title>글제목</title>
<link>글주소</link>
<description><![CDATA[ 글내용]]><description>
...
</item>
위와 같이 "<![CDATA[ "와 "]]>"로 내용을 묶던데 이게 무슨 의미인지요? 꼭 필요한 건가요? 다른 사이트를 보면 이게 없는 곳도 있고...

2. 1을 쓰는 동안 잊어버렸습니다 -_-;;

-- Raymundo 2005-3-12 8:30 am

1은 CDATA가 없으면 글 내용에 HTML 태그가 들어가 있을 때 그걸 XML 엔트리(라고 하던가?)로 해석하게 되기 때문에 문제가 될 수 있습니다. 예를 들어 HTML에서는 줄바꿀 때 <br>만 해주면 되지만 이게 XML로 가면 닫는 태그가 없다고 에러가 나겠죠.
-- 조프 2005-3-12 11:15 am

그렇군요. 그럼 사용해야겠네요.
-- Raymundo 2005-3-12 12:34 pm

description 뿐 아니라 title, link, pubDate 등 URL이나 날짜 항목도 CDATA로 묶어도 되는 걸까요? 지금 소스를 좀 고치고 있는데 일부 필드만 CDATA를 쓰고 일부 필드는 그렇지 않게 하려니 너무 지저분해지네요.

-- Raymundo 2005-3-12 2:08 pm

으음... 온갖 편법과 꽁수가 난무하고 소스도 무지 늘어났지만... NoSmok:저자동고유연성의 극치를 달리는 블로그 기능이로군요. 한 위키 안에 여러 블로그를 두고 각각 RSS를 따로 제공할 수 있습니다. 그리고 모듈화 덕분에 wiki.pl도 거의 고치지 않고 되었으니 뭐... 이만하면 만족.

-- Raymundo 2005-3-14 2:26 pm
이름:  
Homepage:
내용:
 

위키위키분류

마지막 편집일: 2008-6-16 8:48 am (변경사항 [d])
1954 hits | Permalink | 변경내역 보기 [h] | 페이지 소스 보기