xref: /dokuwiki/inc/Ui/MediaDiff.php (revision 675f74fb394468fd5b34f5bc8aa86154582a4abf)
163ab9afeSSatoshi Sahara<?php
263ab9afeSSatoshi Sahara
363ab9afeSSatoshi Saharanamespace dokuwiki\Ui;
463ab9afeSSatoshi Sahara
563ab9afeSSatoshi Saharause dokuwiki\ChangeLog\MediaChangeLog;
663ab9afeSSatoshi Saharause dokuwiki\Extension\Event;
763ab9afeSSatoshi Saharause dokuwiki\Form\Form;
863ab9afeSSatoshi Sahara
963ab9afeSSatoshi Sahara/**
1063ab9afeSSatoshi Sahara * DokuWiki MediaDiff Interface
1163ab9afeSSatoshi Sahara *
1263ab9afeSSatoshi Sahara * @package dokuwiki\Ui
1363ab9afeSSatoshi Sahara */
1463ab9afeSSatoshi Saharaclass MediaDiff extends Diff
1563ab9afeSSatoshi Sahara{
1663ab9afeSSatoshi Sahara    /* @var string */
1763ab9afeSSatoshi Sahara    protected $id;
1863ab9afeSSatoshi Sahara
1963ab9afeSSatoshi Sahara    /**
2063ab9afeSSatoshi Sahara     * MediaDiff Ui constructor
2163ab9afeSSatoshi Sahara     *
22edb50e6aSSatoshi Sahara     * @param string $id  media id
2363ab9afeSSatoshi Sahara     */
24edb50e6aSSatoshi Sahara    public function __construct($id)
2563ab9afeSSatoshi Sahara    {
2663ab9afeSSatoshi Sahara        $this->id = $id;
27edb50e6aSSatoshi Sahara
28edb50e6aSSatoshi Sahara        $this->preference['fromAjax'] = false; // see doluwiki\Ajax::callMediadiff()
29edb50e6aSSatoshi Sahara        $this->preference['showIntro'] = false;
30*675f74fbSSatoshi Sahara        $this->preference['difftype'] = null;  // both, opacity or portions.
3163ab9afeSSatoshi Sahara    }
3263ab9afeSSatoshi Sahara
3363ab9afeSSatoshi Sahara    /**
3463ab9afeSSatoshi Sahara     * Shows difference between two revisions of media
35*675f74fbSSatoshi Sahara     *
36*675f74fbSSatoshi Sahara     * @author Kate Arzamastseva <pshns@ukr.net>
37*675f74fbSSatoshi Sahara     * @param string $difftype diff view type for media (both, opacity or portions)
3863ab9afeSSatoshi Sahara     */
39*675f74fbSSatoshi Sahara    public function show($difftype = null)
4063ab9afeSSatoshi Sahara    {
41*675f74fbSSatoshi Sahara        global $conf;
42*675f74fbSSatoshi Sahara
4363ab9afeSSatoshi Sahara        $ns = getNS($this->id);
4463ab9afeSSatoshi Sahara        $auth = auth_quickaclcheck("$ns:*");
45*675f74fbSSatoshi Sahara
46*675f74fbSSatoshi Sahara        if ($auth < AUTH_READ || !$this->id || !$conf['mediarevisions']) return '';
47*675f74fbSSatoshi Sahara
48*675f74fbSSatoshi Sahara       // determine left and right revision
49*675f74fbSSatoshi Sahara        list($l_rev, $r_rev) = $this->getRevisionPair();
50*675f74fbSSatoshi Sahara
51*675f74fbSSatoshi Sahara        // prepare event data
52*675f74fbSSatoshi Sahara        // NOTE: MEDIA_DIFF event does not found in DokuWiki Event List?
53*675f74fbSSatoshi Sahara        $data = array();
54*675f74fbSSatoshi Sahara        $data[0] = $this->id;
55*675f74fbSSatoshi Sahara        $data[1] = $l_rev;
56*675f74fbSSatoshi Sahara        $data[2] = $r_rev;
57*675f74fbSSatoshi Sahara        $data[3] = $ns;
58*675f74fbSSatoshi Sahara        $data[4] = $auth; // permission level
59*675f74fbSSatoshi Sahara        $data[5] = $this->preference['fromAjax'];
60*675f74fbSSatoshi Sahara
61*675f74fbSSatoshi Sahara        // trigger event
62*675f74fbSSatoshi Sahara        Event::createAndTrigger('MEDIA_DIFF', $data, null, false);
63*675f74fbSSatoshi Sahara
64*675f74fbSSatoshi Sahara        if (is_array($data) && count($data) === 6) {
65*675f74fbSSatoshi Sahara            $this->id = $data[0];
66*675f74fbSSatoshi Sahara            $l_rev = $data[1];
67*675f74fbSSatoshi Sahara            $r_rev = $data[2];
68*675f74fbSSatoshi Sahara            $ns    = $data[3];
69*675f74fbSSatoshi Sahara            $auth  = $data[4];
70*675f74fbSSatoshi Sahara            $this->preference['fromAjax'] = $data[5];
71*675f74fbSSatoshi Sahara        } else {
72*675f74fbSSatoshi Sahara            return '';
73*675f74fbSSatoshi Sahara        }
74*675f74fbSSatoshi Sahara
75*675f74fbSSatoshi Sahara        $l_meta = new \JpegMeta(mediaFN($this->id, $l_rev));
76*675f74fbSSatoshi Sahara        $r_meta = new \JpegMeta(mediaFN($this->id, $r_rev));
77*675f74fbSSatoshi Sahara
78*675f74fbSSatoshi Sahara        $is_img = preg_match('/\.(jpe?g|gif|png)$/', $this->id);
79*675f74fbSSatoshi Sahara        if ($is_img) {
80*675f74fbSSatoshi Sahara            // get image width and height for the mediamanager preview panel
81*675f74fbSSatoshi Sahara            $l_size = media_image_preview_size($this->id, $l_rev, $l_meta);
82*675f74fbSSatoshi Sahara            $r_size = media_image_preview_size($this->id, $r_rev, $r_meta);
83*675f74fbSSatoshi Sahara            // re-check image, ensure minimum image width for showImageDiff()
84*675f74fbSSatoshi Sahara            $is_img = ($l_size && $r_size && ($l_size[0] >= 30 || $r_size[0] >= 30));
85*675f74fbSSatoshi Sahara        }
86*675f74fbSSatoshi Sahara
87*675f74fbSSatoshi Sahara        // determine requested diff view type
88*675f74fbSSatoshi Sahara        $difftype = $this->getDiffType($difftype);
89*675f74fbSSatoshi Sahara
90*675f74fbSSatoshi Sahara        // display intro
91*675f74fbSSatoshi Sahara        if ($this->preference['showIntro']) echo p_locale_xhtml('diff');
92*675f74fbSSatoshi Sahara
93*675f74fbSSatoshi Sahara        // print form to choose diff view type
94*675f74fbSSatoshi Sahara        if ($is_img && !$this->preference['fromAjax']) {
95*675f74fbSSatoshi Sahara            $this->showDiffViewSelector($l_rev, $r_rev);
96*675f74fbSSatoshi Sahara            echo '<div id="mediamanager__diff" >';
97*675f74fbSSatoshi Sahara        }
98*675f74fbSSatoshi Sahara
99*675f74fbSSatoshi Sahara        if ($is_img) {
100*675f74fbSSatoshi Sahara            switch ($difftype) {
101*675f74fbSSatoshi Sahara                case 'opacity':
102*675f74fbSSatoshi Sahara                case 'portions':
103*675f74fbSSatoshi Sahara                    $this->showImageDiff($l_rev, $r_rev, $l_size, $r_size, $difftype);
104*675f74fbSSatoshi Sahara                    break;
105*675f74fbSSatoshi Sahara                case 'both':
106*675f74fbSSatoshi Sahara                default:
107*675f74fbSSatoshi Sahara                    $this->showFileDiff($l_rev, $r_rev, $l_meta, $r_meta, $auth);
108*675f74fbSSatoshi Sahara                    break;
109*675f74fbSSatoshi Sahara            }
110*675f74fbSSatoshi Sahara        } else {
111*675f74fbSSatoshi Sahara            $this->showFileDiff($l_rev, $r_rev, $l_meta, $r_meta, $auth);
112*675f74fbSSatoshi Sahara        }
113*675f74fbSSatoshi Sahara
114*675f74fbSSatoshi Sahara        if ($is_img && !$this->preference['fromAjax']) {
115*675f74fbSSatoshi Sahara            echo '</div>';
116*675f74fbSSatoshi Sahara        }
1172db397b2SSatoshi Sahara    }
1182db397b2SSatoshi Sahara
1192db397b2SSatoshi Sahara    /**
120*675f74fbSSatoshi Sahara     * Determine requested diff view type for media
1212db397b2SSatoshi Sahara     *
122*675f74fbSSatoshi Sahara     * @param string $mode  diff view type (both, opacity or portions)
123*675f74fbSSatoshi Sahara     * @return string
1242db397b2SSatoshi Sahara     */
125*675f74fbSSatoshi Sahara    protected function getDiffType($mode = null)
1262db397b2SSatoshi Sahara    {
1272db397b2SSatoshi Sahara        global $INPUT;
128*675f74fbSSatoshi Sahara        $difftype =& $this->preference['difftype'];
1292db397b2SSatoshi Sahara
130*675f74fbSSatoshi Sahara        if (!isset($mode)) {
131*675f74fbSSatoshi Sahara            $difftype = $INPUT->str('difftype');
132*675f74fbSSatoshi Sahara        } elseif (in_array($mode, ['both', 'opacity', 'portions'])) {
133*675f74fbSSatoshi Sahara            $difftype = $mode;
134*675f74fbSSatoshi Sahara        }
135*675f74fbSSatoshi Sahara        return $this->preference['difftype'];
136*675f74fbSSatoshi Sahara    }
137*675f74fbSSatoshi Sahara
138*675f74fbSSatoshi Sahara    /**
139*675f74fbSSatoshi Sahara     * Determine requested revision(s)
140*675f74fbSSatoshi Sahara     *
141*675f74fbSSatoshi Sahara     * @return array
142*675f74fbSSatoshi Sahara     */
143*675f74fbSSatoshi Sahara    protected function getRevisions()
144*675f74fbSSatoshi Sahara    {
145*675f74fbSSatoshi Sahara        global $INPUT;
1462db397b2SSatoshi Sahara
1472db397b2SSatoshi Sahara        $rev1 = $INPUT->int('rev');
1482db397b2SSatoshi Sahara
1492db397b2SSatoshi Sahara        $rev2 = $INPUT->ref('rev2');
1502db397b2SSatoshi Sahara        if (is_array($rev2)) {
1512db397b2SSatoshi Sahara            $rev1 = (int) $rev2[0];
1522db397b2SSatoshi Sahara            $rev2 = (int) $rev2[1];
1532db397b2SSatoshi Sahara
1542db397b2SSatoshi Sahara            if (!$rev1) {
1552db397b2SSatoshi Sahara                $rev1 = $rev2;
156*675f74fbSSatoshi Sahara                $rev2 = null;
1572db397b2SSatoshi Sahara            }
1582db397b2SSatoshi Sahara        } else {
1592db397b2SSatoshi Sahara            $rev2 = $INPUT->int('rev2');
1602db397b2SSatoshi Sahara        }
161*675f74fbSSatoshi Sahara        return array($rev1, $rev2);
162*675f74fbSSatoshi Sahara    }
1632db397b2SSatoshi Sahara
164*675f74fbSSatoshi Sahara    /**
165*675f74fbSSatoshi Sahara     * Determine left and right revision
166*675f74fbSSatoshi Sahara     *
167*675f74fbSSatoshi Sahara     * @return array
168*675f74fbSSatoshi Sahara     */
169*675f74fbSSatoshi Sahara    protected function getRevisionPair()
170*675f74fbSSatoshi Sahara    {
171*675f74fbSSatoshi Sahara        // determine requested revision(s)
172*675f74fbSSatoshi Sahara        list($rev1, $rev2) = $this->getRevisions();
173*675f74fbSSatoshi Sahara
174*675f74fbSSatoshi Sahara        if ($rev1 && !file_exists(mediaFN($this->id, $rev1))) $rev1 = false;
175*675f74fbSSatoshi Sahara        if ($rev2 && !file_exists(mediaFN($this->id, $rev2))) $rev2 = false;
1762db397b2SSatoshi Sahara
1772db397b2SSatoshi Sahara        if ($rev1 && $rev2) {  // two specific revisions wanted
1782db397b2SSatoshi Sahara            // make sure order is correct (older on the left)
1792db397b2SSatoshi Sahara            if ($rev1 < $rev2) {
1802db397b2SSatoshi Sahara                $l_rev = $rev1;
1812db397b2SSatoshi Sahara                $r_rev = $rev2;
1822db397b2SSatoshi Sahara            } else {
1832db397b2SSatoshi Sahara                $l_rev = $rev2;
1842db397b2SSatoshi Sahara                $r_rev = $rev1;
1852db397b2SSatoshi Sahara            }
1862db397b2SSatoshi Sahara        } elseif ($rev1) {     // single revision given, compare to current
1872db397b2SSatoshi Sahara            $r_rev = '';
1882db397b2SSatoshi Sahara            $l_rev = $rev1;
1892db397b2SSatoshi Sahara        } else {               // no revision was given, compare previous to current
1902db397b2SSatoshi Sahara            $r_rev = '';
191*675f74fbSSatoshi Sahara            $medialog = new MediaChangeLog($this->id);
1922db397b2SSatoshi Sahara            $revs = $medialog->getRevisions(0, 1);
193*675f74fbSSatoshi Sahara            if (file_exists(mediaFN($this->id, $revs[0]))) {
1942db397b2SSatoshi Sahara                $l_rev = $revs[0];
1952db397b2SSatoshi Sahara            } else {
1962db397b2SSatoshi Sahara                $l_rev = '';
1972db397b2SSatoshi Sahara            }
1982db397b2SSatoshi Sahara        }
199*675f74fbSSatoshi Sahara        return array($l_rev, $r_rev);
2002db397b2SSatoshi Sahara    }
2012db397b2SSatoshi Sahara
2022db397b2SSatoshi Sahara    /**
203*675f74fbSSatoshi Sahara     * Print form to choose diff view type
204*675f74fbSSatoshi Sahara     * the dropdown is to be added through JavaScript, see lib/scripts/media.js
2052db397b2SSatoshi Sahara     *
206*675f74fbSSatoshi Sahara     * @param int $l_rev  revision timestamp of left side
207*675f74fbSSatoshi Sahara     * @param int $r_rev  revision timestamp of right side
2082db397b2SSatoshi Sahara     */
209*675f74fbSSatoshi Sahara    protected function showDiffViewSelector($l_rev, $r_rev)
2102db397b2SSatoshi Sahara    {
2112db397b2SSatoshi Sahara        $form = new Form([
2122db397b2SSatoshi Sahara            'id' => 'mediamanager__form_diffview',
2132db397b2SSatoshi Sahara            'action' => media_managerURL([], '&'),
2142db397b2SSatoshi Sahara            'method' => 'get',
2152db397b2SSatoshi Sahara            'class' => 'diffView',
2162db397b2SSatoshi Sahara        ]);
2172db397b2SSatoshi Sahara        $form->addTagOpen('div')->addClass('no');
2182db397b2SSatoshi Sahara        $form->setHiddenField('sectok', null);
2192db397b2SSatoshi Sahara        $form->setHiddenField('mediado', 'diff');
2202db397b2SSatoshi Sahara        $form->setHiddenField('rev2[0]', $l_rev);
2212db397b2SSatoshi Sahara        $form->setHiddenField('rev2[1]', $r_rev);
2222db397b2SSatoshi Sahara        $form->addTagClose('div');
2232db397b2SSatoshi Sahara        echo $form->toHTML();
2242db397b2SSatoshi Sahara    }
2252db397b2SSatoshi Sahara
226*675f74fbSSatoshi Sahara    /**
227*675f74fbSSatoshi Sahara     * Prints two images side by side
228*675f74fbSSatoshi Sahara     * and slider
229*675f74fbSSatoshi Sahara     *
230*675f74fbSSatoshi Sahara     * @author Kate Arzamastseva <pshns@ukr.net>
231*675f74fbSSatoshi Sahara     *
232*675f74fbSSatoshi Sahara     * @param int    $l_rev   revision timestamp, or empty string
233*675f74fbSSatoshi Sahara     * @param int    $r_rev   revision timestamp, or empty string
234*675f74fbSSatoshi Sahara     * @param array  $l_size  array with width and height
235*675f74fbSSatoshi Sahara     * @param array  $r_size  array with width and height
236*675f74fbSSatoshi Sahara     * @param string $type    diff type: opacity or portions
237*675f74fbSSatoshi Sahara     */
238*675f74fbSSatoshi Sahara    protected function showImageDiff($l_rev, $r_rev, $l_size, $r_size, $type = null)
239*675f74fbSSatoshi Sahara    {
240*675f74fbSSatoshi Sahara        if (!isset($type)) {
241*675f74fbSSatoshi Sahara            $type = $this->preference['difftype'];
242*675f74fbSSatoshi Sahara        }
243*675f74fbSSatoshi Sahara
244*675f74fbSSatoshi Sahara        // adjust image width, right side (newer) has priority
245*675f74fbSSatoshi Sahara        if ($l_size != $r_size) {
246*675f74fbSSatoshi Sahara            if ($r_size[0] > $l_size[0]) {
247*675f74fbSSatoshi Sahara                $l_size = $r_size;
2482db397b2SSatoshi Sahara            }
2492db397b2SSatoshi Sahara        }
2502db397b2SSatoshi Sahara
251*675f74fbSSatoshi Sahara        $l_src = ml($this->id, ['rev' => $l_rev, 'h' => $l_size[1], 'w' => $l_size[0]]);
252*675f74fbSSatoshi Sahara        $r_src = ml($this->id, ['rev' => $r_rev, 'h' => $l_size[1], 'w' => $l_size[0]]);
253*675f74fbSSatoshi Sahara
254*675f74fbSSatoshi Sahara        // slider
255*675f74fbSSatoshi Sahara        echo '<div class="slider" style="max-width: '.($l_size[0]-20).'px;" ></div>';
256*675f74fbSSatoshi Sahara
257*675f74fbSSatoshi Sahara        // two images in divs
258*675f74fbSSatoshi Sahara        echo '<div class="imageDiff '.$type.'">';
259*675f74fbSSatoshi Sahara        echo '<div class="image1" style="max-width: '.$l_size[0].'px;">';
260*675f74fbSSatoshi Sahara        echo '<img src="'.$l_src.'" alt="" />';
261*675f74fbSSatoshi Sahara        echo '</div>';
262*675f74fbSSatoshi Sahara        echo '<div class="image2" style="max-width: '.$l_size[0].'px;">';
263*675f74fbSSatoshi Sahara        echo '<img src="'.$r_src.'" alt="" />';
264*675f74fbSSatoshi Sahara        echo '</div>';
265*675f74fbSSatoshi Sahara        echo '</div>';
266*675f74fbSSatoshi Sahara    }
267*675f74fbSSatoshi Sahara
268*675f74fbSSatoshi Sahara    /**
269*675f74fbSSatoshi Sahara     * Shows difference between two revisions of media file
270*675f74fbSSatoshi Sahara     *
271*675f74fbSSatoshi Sahara     * @author Kate Arzamastseva <pshns@ukr.net>
272*675f74fbSSatoshi Sahara     *
273*675f74fbSSatoshi Sahara     * @param string|int $l_rev revision timestamp, or empty string
274*675f74fbSSatoshi Sahara     * @param string|int $r_rev revision timestamp, or empty string
275*675f74fbSSatoshi Sahara     * @param JpegMeta $l_meta
276*675f74fbSSatoshi Sahara     * @param JpegMeta $r_meta
277*675f74fbSSatoshi Sahara     * @param int $auth permission level
278*675f74fbSSatoshi Sahara     */
279*675f74fbSSatoshi Sahara    protected function showFileDiff($l_rev, $r_rev, $l_meta, $r_meta, $auth)
280*675f74fbSSatoshi Sahara    {
281*675f74fbSSatoshi Sahara        $medialog = new MediaChangeLog($this->id);
2822db397b2SSatoshi Sahara
2832db397b2SSatoshi Sahara        list($l_head, $r_head) = $this->diffHead($medialog, $l_rev, $r_rev);
2842db397b2SSatoshi Sahara
2852db397b2SSatoshi Sahara        echo '<div class="table">';
2862db397b2SSatoshi Sahara        echo '<table>';
2872db397b2SSatoshi Sahara        echo '<tr>';
2882db397b2SSatoshi Sahara        echo '<th>'. $l_head .'</th>';
2892db397b2SSatoshi Sahara        echo '<th>'. $r_head .'</th>';
290*675f74fbSSatoshi Sahara        echo '</tr>';
2912db397b2SSatoshi Sahara
2922db397b2SSatoshi Sahara        echo '<tr class="image">';
2932db397b2SSatoshi Sahara        echo '<td>';
294*675f74fbSSatoshi Sahara        media_preview($this->id, $auth, $l_rev, $l_meta); // $auth not used in media_preview()?
2952db397b2SSatoshi Sahara        echo '</td>';
2962db397b2SSatoshi Sahara
2972db397b2SSatoshi Sahara        echo '<td>';
298*675f74fbSSatoshi Sahara        media_preview($this->id, $auth, $r_rev, $r_meta);
2992db397b2SSatoshi Sahara        echo '</td>';
300*675f74fbSSatoshi Sahara        echo '</tr>';
3012db397b2SSatoshi Sahara
3022db397b2SSatoshi Sahara        echo '<tr class="actions">';
3032db397b2SSatoshi Sahara        echo '<td>';
304*675f74fbSSatoshi Sahara        media_preview_buttons($this->id, $auth, $l_rev); // $auth used in media_preview_buttons()
3052db397b2SSatoshi Sahara        echo '</td>';
3062db397b2SSatoshi Sahara
3072db397b2SSatoshi Sahara        echo '<td>';
308*675f74fbSSatoshi Sahara        media_preview_buttons($this->id, $auth, $r_rev);
3092db397b2SSatoshi Sahara        echo '</td>';
310*675f74fbSSatoshi Sahara        echo '</tr>';
3112db397b2SSatoshi Sahara
3122db397b2SSatoshi Sahara        $l_tags = media_file_tags($l_meta);
3132db397b2SSatoshi Sahara        $r_tags = media_file_tags($r_meta);
3142db397b2SSatoshi Sahara        // FIXME r_tags-only stuff
3152db397b2SSatoshi Sahara        foreach ($l_tags as $key => $l_tag) {
3162db397b2SSatoshi Sahara            if ($l_tag['value'] != $r_tags[$key]['value']) {
3172db397b2SSatoshi Sahara                $r_tags[$key]['highlighted'] = true;
3182db397b2SSatoshi Sahara                $l_tags[$key]['highlighted'] = true;
3192db397b2SSatoshi Sahara            } elseif (!$l_tag['value'] || !$r_tags[$key]['value']) {
3202db397b2SSatoshi Sahara                unset($r_tags[$key]);
3212db397b2SSatoshi Sahara                unset($l_tags[$key]);
3222db397b2SSatoshi Sahara            }
3232db397b2SSatoshi Sahara        }
3242db397b2SSatoshi Sahara
3252db397b2SSatoshi Sahara        echo '<tr>';
3262db397b2SSatoshi Sahara        foreach (array($l_tags, $r_tags) as $tags) {
327*675f74fbSSatoshi Sahara            echo '<td>';
3282db397b2SSatoshi Sahara
3292db397b2SSatoshi Sahara            echo '<dl class="img_tags">';
3302db397b2SSatoshi Sahara            foreach ($tags as $tag) {
3312db397b2SSatoshi Sahara                $value = cleanText($tag['value']);
3322db397b2SSatoshi Sahara                if (!$value) $value = '-';
3332db397b2SSatoshi Sahara                echo '<dt>'.$lang[$tag['tag'][1]].'</dt>';
3342db397b2SSatoshi Sahara                echo '<dd>';
3352db397b2SSatoshi Sahara                if ($tag['highlighted']) echo '<strong>';
3362db397b2SSatoshi Sahara                if ($tag['tag'][2] == 'date') {
3372db397b2SSatoshi Sahara                    echo dformat($value);
3382db397b2SSatoshi Sahara                } else {
3392db397b2SSatoshi Sahara                    echo hsc($value);
3402db397b2SSatoshi Sahara                }
3412db397b2SSatoshi Sahara                if ($tag['highlighted']) echo '</strong>';
3422db397b2SSatoshi Sahara                echo '</dd>';
3432db397b2SSatoshi Sahara            }
344*675f74fbSSatoshi Sahara            echo '</dl>';
3452db397b2SSatoshi Sahara
3462db397b2SSatoshi Sahara            echo '</td>';
3472db397b2SSatoshi Sahara        }
348*675f74fbSSatoshi Sahara        echo '</tr>';
3492db397b2SSatoshi Sahara
350*675f74fbSSatoshi Sahara        echo '</table>';
351*675f74fbSSatoshi Sahara        echo '</div>';
35263ab9afeSSatoshi Sahara    }
35363ab9afeSSatoshi Sahara
35463ab9afeSSatoshi Sahara}
355