From bcc013bdcf04425e70d3c3f76c5f4b4207349d1c Mon Sep 17 00:00:00 2001 From: chicpro Date: Thu, 20 Mar 2014 14:51:51 +0900 Subject: [PATCH] =?UTF-8?q?sms5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adm/admin.menu900.php | 16 + adm/css/admin.css | 26 +- adm/member_form_update.php | 4 +- adm/member_list.php | 2 +- adm/sms_admin/_common.php | 15 + adm/sms_admin/ajax.hp_chk.php | 48 + adm/sms_admin/ajax.sms_write_form.php | 82 + adm/sms_admin/ajax.sms_write_group.php | 42 + adm/sms_admin/ajax.sms_write_level.php | 56 + adm/sms_admin/ajax.sms_write_person.php | 165 + adm/sms_admin/config.php | 181 + adm/sms_admin/config_update.php | 49 + adm/sms_admin/css/sms5.css | 133 + adm/sms_admin/emoticon_move.php | 114 + adm/sms_admin/emoticon_move_update.php | 60 + adm/sms_admin/form_group.php | 177 + adm/sms_admin/form_group_move.php | 30 + adm/sms_admin/form_group_update.php | 92 + adm/sms_admin/form_list.php | 238 ++ adm/sms_admin/form_multi_update.php | 27 + adm/sms_admin/form_update.php | 78 + adm/sms_admin/form_write.php | 233 ++ adm/sms_admin/history_list.php | 100 + adm/sms_admin/history_member.php | 90 + adm/sms_admin/history_num.php | 108 + adm/sms_admin/history_send.php | 138 + adm/sms_admin/history_view.php | 246 ++ adm/sms_admin/img/ajax-loader.gif | Bin 0 -> 1924 bytes adm/sms_admin/img/ajax-loader2.gif | Bin 0 -> 7825 bytes adm/sms_admin/img/box_ico.gif | Bin 0 -> 60 bytes adm/sms_admin/img/scemo_ico.gif | Bin 0 -> 67 bytes adm/sms_admin/install.php | 87 + adm/sms_admin/member_update.php | 63 + adm/sms_admin/member_update_run.php | 118 + adm/sms_admin/num_book.php | 263 ++ adm/sms_admin/num_book_file.php | 158 + adm/sms_admin/num_book_file_download.php | 67 + adm/sms_admin/num_book_file_upload.php | 166 + adm/sms_admin/num_book_move.php | 128 + adm/sms_admin/num_book_multi_update.php | 63 + adm/sms_admin/num_book_update.php | 137 + adm/sms_admin/num_book_write.php | 216 ++ adm/sms_admin/num_group.php | 186 ++ adm/sms_admin/num_group_move.php | 14 + adm/sms_admin/num_group_update.php | 77 + adm/sms_admin/number_move_update.php | 84 + adm/sms_admin/sms5.sql | 189 ++ adm/sms_admin/sms_ing.php | 22 + adm/sms_admin/sms_write.php | 779 +++++ adm/sms_admin/sms_write_form.php | 132 + adm/sms_admin/sms_write_overlap_check.php | 69 + adm/sms_admin/sms_write_send.php | 231 ++ extend/sms5.extend.php | 57 + js/common.js | 13 + js/jquery.sms_paging.js | 93 + lib/Excel/itemexcel.xls | Bin 0 -> 33792 bytes lib/Excel/oleread.inc | 271 ++ .../class.writeexcel_biffwriter.inc.php | 209 ++ .../class.writeexcel_format.inc.php | 695 ++++ .../class.writeexcel_formula.inc.php | 1610 +++++++++ .../class.writeexcel_olewriter.inc.php | 353 ++ .../class.writeexcel_workbook.inc.php | 1149 +++++++ .../class.writeexcel_workbookbig.inc.php | 55 + .../class.writeexcel_worksheet.inc.php | 2968 +++++++++++++++++ lib/Excel/reader.php | 1084 ++++++ lib/common.lib.php | 6 +- plugin/sms5/JSON.php | 933 ++++++ plugin/sms5/_common.php | 3 + plugin/sms5/ajax.sms_emoticon.php | 70 + plugin/sms5/index.php | 11 + plugin/sms5/skin/basic/img/ajax-loader.gif | Bin 0 -> 1924 bytes plugin/sms5/skin/basic/img/box_ico.gif | Bin 0 -> 60 bytes plugin/sms5/skin/basic/img/scemo_ico.gif | Bin 0 -> 67 bytes plugin/sms5/skin/basic/mobile.css | 48 + plugin/sms5/skin/basic/style.css | 50 + plugin/sms5/skin/basic/write.skin.php | 378 +++ plugin/sms5/skin/basic/write_mobile.skin.php | 394 +++ plugin/sms5/sms5.lib.php | 269 ++ plugin/sms5/write.php | 72 + plugin/sms5/write_update.php | 157 + 80 files changed, 16733 insertions(+), 14 deletions(-) create mode 100644 adm/admin.menu900.php create mode 100644 adm/sms_admin/_common.php create mode 100644 adm/sms_admin/ajax.hp_chk.php create mode 100644 adm/sms_admin/ajax.sms_write_form.php create mode 100644 adm/sms_admin/ajax.sms_write_group.php create mode 100644 adm/sms_admin/ajax.sms_write_level.php create mode 100644 adm/sms_admin/ajax.sms_write_person.php create mode 100644 adm/sms_admin/config.php create mode 100644 adm/sms_admin/config_update.php create mode 100644 adm/sms_admin/css/sms5.css create mode 100644 adm/sms_admin/emoticon_move.php create mode 100644 adm/sms_admin/emoticon_move_update.php create mode 100644 adm/sms_admin/form_group.php create mode 100644 adm/sms_admin/form_group_move.php create mode 100644 adm/sms_admin/form_group_update.php create mode 100644 adm/sms_admin/form_list.php create mode 100644 adm/sms_admin/form_multi_update.php create mode 100644 adm/sms_admin/form_update.php create mode 100644 adm/sms_admin/form_write.php create mode 100644 adm/sms_admin/history_list.php create mode 100644 adm/sms_admin/history_member.php create mode 100644 adm/sms_admin/history_num.php create mode 100644 adm/sms_admin/history_send.php create mode 100644 adm/sms_admin/history_view.php create mode 100644 adm/sms_admin/img/ajax-loader.gif create mode 100644 adm/sms_admin/img/ajax-loader2.gif create mode 100644 adm/sms_admin/img/box_ico.gif create mode 100644 adm/sms_admin/img/scemo_ico.gif create mode 100644 adm/sms_admin/install.php create mode 100644 adm/sms_admin/member_update.php create mode 100644 adm/sms_admin/member_update_run.php create mode 100644 adm/sms_admin/num_book.php create mode 100644 adm/sms_admin/num_book_file.php create mode 100644 adm/sms_admin/num_book_file_download.php create mode 100644 adm/sms_admin/num_book_file_upload.php create mode 100644 adm/sms_admin/num_book_move.php create mode 100644 adm/sms_admin/num_book_multi_update.php create mode 100644 adm/sms_admin/num_book_update.php create mode 100644 adm/sms_admin/num_book_write.php create mode 100644 adm/sms_admin/num_group.php create mode 100644 adm/sms_admin/num_group_move.php create mode 100644 adm/sms_admin/num_group_update.php create mode 100644 adm/sms_admin/number_move_update.php create mode 100644 adm/sms_admin/sms5.sql create mode 100644 adm/sms_admin/sms_ing.php create mode 100644 adm/sms_admin/sms_write.php create mode 100644 adm/sms_admin/sms_write_form.php create mode 100644 adm/sms_admin/sms_write_overlap_check.php create mode 100644 adm/sms_admin/sms_write_send.php create mode 100644 extend/sms5.extend.php create mode 100644 js/jquery.sms_paging.js create mode 100644 lib/Excel/itemexcel.xls create mode 100644 lib/Excel/oleread.inc create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_biffwriter.inc.php create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_format.inc.php create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_formula.inc.php create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_olewriter.inc.php create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_workbook.inc.php create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_workbookbig.inc.php create mode 100644 lib/Excel/php_writeexcel/class.writeexcel_worksheet.inc.php create mode 100644 lib/Excel/reader.php create mode 100644 plugin/sms5/JSON.php create mode 100644 plugin/sms5/_common.php create mode 100644 plugin/sms5/ajax.sms_emoticon.php create mode 100644 plugin/sms5/index.php create mode 100644 plugin/sms5/skin/basic/img/ajax-loader.gif create mode 100644 plugin/sms5/skin/basic/img/box_ico.gif create mode 100644 plugin/sms5/skin/basic/img/scemo_ico.gif create mode 100644 plugin/sms5/skin/basic/mobile.css create mode 100644 plugin/sms5/skin/basic/style.css create mode 100644 plugin/sms5/skin/basic/write.skin.php create mode 100644 plugin/sms5/skin/basic/write_mobile.skin.php create mode 100644 plugin/sms5/sms5.lib.php create mode 100644 plugin/sms5/write.php create mode 100644 plugin/sms5/write_update.php diff --git a/adm/admin.menu900.php b/adm/admin.menu900.php new file mode 100644 index 000000000..3f851232b --- /dev/null +++ b/adm/admin.menu900.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/adm/css/admin.css b/adm/css/admin.css index 45db407c5..b432a1ac4 100644 --- a/adm/css/admin.css +++ b/adm/css/admin.css @@ -42,7 +42,7 @@ h2.h2_frm {padding-top:15px} #tnb #tnb_logout a {color:#ff3061} #tnb span {position:absolute;bottom:10px;left:0;width:100%;text-align:center} -/* gnb js off */ +/* gnb */ #gnb {position:relative;z-index:12;min-width:999px;background:#383a3f} #gnb h2 {margin:0;padding:0;font-size:0;line-height:0;overflow:hidden} #gnb_1dul {position:relative;margin:0 20px;padding:0;zoom:1} @@ -66,7 +66,7 @@ h2.h2_frm {padding-top:15px} #lnb {margin:0;padding:15px;min-width:970px;border-bottom:1px solid #e9e9e9;list-style:none;zoom:1} #lnb:after {display:block;visibility:hidden;clear:both;content:""} #lnb li {float:left} -#lnb a {display:inline-block;margin:0 0 0 10px;padding:0 10px 0 0;border-right:1px solid #ccc;font-size:0.95em;letter-spacing:-0.1em} +#lnb a {display:inline-block;padding:0 10px;border-right:1px solid #ccc;font-size:0.95em;letter-spacing:-0.1em} /* 중간 레이아웃 */ #wrapper {z-index:5;margin:20px 0;min-width:1000px;zoom:1} @@ -125,7 +125,7 @@ h2.h2_frm {padding-top:15px} .btn_confirm02 a {display:inline-block;padding:0 15px;height:30px;background:#617d46;color:#fff;text-decoration:none;line-height:2.5em;vertical-align:middle} .btn_confirm02 a:focus {background:#555} -.btn_submit {background:#ff3061;cursor:pointer} +.btn_submit {margin:0;padding:0;border:0;background:#ff3061;color:#fff;cursor:pointer} .btn_confirm .btn_submit {padding:0 15px;border:0;height:30px;color:#fff} .btn_cancel {display:inline-block;padding:0 15px;height:30px;border:0;background:#617d46;color:#fff;text-decoration:none} a.btn_cancel {line-height:2.5em;vertical-align:middle} @@ -148,7 +148,7 @@ a.btn_cancel {line-height:2.5em;vertical-align:middle} .btn_list {margin:0 20px 10px} /* 목록용 */ .btn_list01 {} -.btn_list01 input, .btn_list01 button, .btn_list01 a {display:inline-block;padding:10px;border:1px solid #ccc;background:#fafafa;text-decoration:none;cursor:pointer} +.btn_list01 input, .btn_list01 button, .btn_list01 a {display:inline-block;margin:0;padding:10px;border:1px solid #ccc;background:#fafafa;text-decoration:none;cursor:pointer} .btn_list01 a:focus {background:#555} .btn_list02 {} @@ -197,7 +197,7 @@ legend {position:absolute;width:0;height:0;font-size:0;line-height:0;text-indent #sort_sodr {width:600px} /* 하단 레이아웃 */ -#ft {margin:20px 0;padding:10px 20px;min-width:960px;border-top:1px solid #d0d6d8} +#ft {clear:both;margin:20px 0;padding:10px 20px;min-width:960px;border-top:1px solid #d0d6d8} /* 페이지 내 검색 */ .local_sch {min-width:960px} @@ -212,7 +212,7 @@ legend {position:absolute;width:0;height:0;font-size:0;line-height:0;text-indent .local_sch02 strong {display:inline-block;width:80px} .local_sch02 strong.sch_long {width:160px} .local_sch02 label {display:inline-block;margin:0 5px 0 0} -.local_sch02 button {margin:0;padding:3px 5px;border:1px solid #ced9de;background:#f6f9fa;font-size:0.95em;cursor:pointer} +.local_sch02 button {margin:0;padding:5px;border:1px solid #ced9de;background:#f6f9fa;font-size:0.95em;vertical-align:middle;cursor:pointer} /* 페이지 내 실행 */ .local_cmd {min-width:960px} @@ -231,7 +231,7 @@ legend {position:absolute;width:0;height:0;font-size:0;line-height:0;text-indent .local_desc01 a {text-decoration:underline} .local_desc02 {margin:0 20px 10px;min-width:960px} /* 주로 온라인 서식 관련 안내 내용에 사용 */ -.local_desc02 p {padding:0} +.local_desc02 p {padding:0;line-height:1.8em} /* 페이지 내 카운트 */ .local_ov {min-width:960px} @@ -397,7 +397,8 @@ td.td_grpset {width:160px;border-left:1px solid #e9ecee;text-align:center} .txt_true {color:#e8180c} .txt_false {color:#ccc} - +.txt_succeed {color:#40b300} +.txt_fail {color:#ce4242} .banner_or_img {margin:10px 0 0} .banner_or_img button {display:block;margin:5px 0 0} @@ -644,13 +645,18 @@ strong.sodr_nonpay {display:block;padding:5px 0;text-align:right} .new_win_ul {margin:-20px 0 20px 0;padding:0 0 0 20px;border-bottom:1px solid #515151;background:#484848;list-style:none;zoom:1} .new_win_ul:after {display:block;visibility:hidden;clear:both;content:""} .new_win_ul li {float:left;margin-left:-1px} - .new_win_ul a {display:block;padding:10px 10px 8px;border-right:1px solid #595959;border-left:1px solid #595959;color:#fff;font-family:dotum;font-weight:bold;text-decoration:none} .new_win_desc {margin:0 20px} .new_win .anchor {margin:0 20px 5px} .new_win .tbl_wrap {margin:0 20px 20px;padding:0} -.new_win .local_sch, .new_win .local_cmd, .new_win .local_desc01, .new_win .local_desc02, .new_win .local_ov {min-width:480px} +.new_win .win_btn {clear:both;padding:20px;text-align:center} /* 새창용 */ +.new_win .win_btn button {display:inline-block;padding:0 10px;height:30px;border:0;background:#4b545e;color:#fff;line-height:2em;cursor:pointer} +.new_win .win_btn input {padding:0 10px;height:30px;line-height:2em} +.new_win .win_btn a {display:inline-block;padding:0 10px;height:30px;background:#4b545e;color:#fff;vertical-align:middle;line-height:2.4em} +.new_win .win_btn a:focus, .new_win .win_btn a:hover {text-decoration:none} + +.new_win .local_sch, .new_win .local_cmd, .new_win .local_desc01, .new_win .local_desc02, .new_win .local_ov {min-width:320px} /* 자바스크립트 alert 대안 */ #validation_check {margin:100px auto;width:500px} diff --git a/adm/member_form_update.php b/adm/member_form_update.php index 50543e0d7..cddbf100d 100644 --- a/adm/member_form_update.php +++ b/adm/member_form_update.php @@ -13,7 +13,7 @@ check_token(); $mb_id = trim($_POST['mb_id']); // 휴대폰번호 체크 -$mb_hp = $_POST['mb_hp']; +$mb_hp = hyphen_hp_number($_POST['mb_hp']); if($mb_hp) { $result = exist_mb_hp($mb_hp, $mb_id); if ($result) @@ -34,7 +34,7 @@ $sql_common = " mb_name = '{$_POST['mb_name']}', mb_email = '{$_POST['mb_email']}', mb_homepage = '{$_POST['mb_homepage']}', mb_tel = '{$_POST['mb_tel']}', - mb_hp = '{$_POST['mb_hp']}', + mb_hp = '{$mb_hp}', mb_certify = '{$mb_certify}', mb_adult = '{$mb_adult}', mb_zip1 = '{$_POST['mb_zip1']}', diff --git a/adm/member_list.php b/adm/member_list.php index 861c3dd42..34b2cf704 100644 --- a/adm/member_list.php +++ b/adm/member_list.php @@ -77,7 +77,7 @@ $colspan = 16;
- diff --git a/adm/sms_admin/_common.php b/adm/sms_admin/_common.php new file mode 100644 index 000000000..289691141 --- /dev/null +++ b/adm/sms_admin/_common.php @@ -0,0 +1,15 @@ +', 0); +?> \ No newline at end of file diff --git a/adm/sms_admin/ajax.hp_chk.php b/adm/sms_admin/ajax.hp_chk.php new file mode 100644 index 000000000..560257309 --- /dev/null +++ b/adm/sms_admin/ajax.hp_chk.php @@ -0,0 +1,48 @@ +encode($data) ); + } +} + +$err = ''; +$arr_ajax_msg = array(); +$exist_hplist = array(); + +if( !$bk_hp ) + $err = '휴대폰번호를 입력해 주십시오.'; + +$bk_hp = get_hp($bk_hp); + +$sql = " select count(*) as cnt from {$g5['sms5_book_table']} where bk_hp = '$bk_hp' "; +if($w == 'u' && $bk_no) + $sql .= " and bk_no <> '$bk_no' "; +$row = sql_fetch($sql); + +if($row['cnt']) + $err = '같은 번호가 존재합니다.'; + +// 수정일 때 회원정보에서 중복체크 +if(!$row['cnt'] && $w == 'u') { + $sql = " select mb_id from {$g5['member_table']} where mb_hp = '{$bk_hp}' and mb_hp <> '' "; + + if( $mb_id ) + $sql .= " and mb_id <> '{$mb_id}' "; + + $result = sql_query($sql); + + while($row = sql_fetch_array($result)){ + $exist_hplist[] = $row['mb_id']; + } +} + +$arr_ajax_msg['error'] = $err; +$arr_ajax_msg['exist'] = $exist_hplist; + +die( json_encode($arr_ajax_msg) ); + +?> \ No newline at end of file diff --git a/adm/sms_admin/ajax.sms_write_form.php b/adm/sms_admin/ajax.sms_write_form.php new file mode 100644 index 000000000..da3745c6b --- /dev/null +++ b/adm/sms_admin/ajax.sms_write_form.php @@ -0,0 +1,82 @@ +encode($data) ); + } +} + +$page_size = 6; + +auth_check($auth[$sub_menu], "r"); + +if (!$page) $page = 1; + +if (is_numeric($fg_no)) + $sql_group = " and fg_no='$fg_no' "; +else + $sql_group = ""; + +if ($st == 'all') { + $sql_search = "and (fo_name like '%{$sv}%' or fo_content like '%{$sv}%')"; +} else if ($st == 'name') { + $sql_search = "and fo_name like '%{$sv}%'"; +} else if ($st == 'content') { + $sql_search = "and fo_content like '%{$sv}%'"; +} else { + $sql_search = ''; +} + +$total_res = sql_fetch("select count(*) as cnt from {$g5['sms5_form_table']} where 1 $sql_group $sql_search"); +$total_count = $total_res['cnt']; + +$total_page = (int)($total_count/$page_size) + ($total_count%$page_size==0 ? 0 : 1); +$page_start = $page_size * ( $page - 1 ); + +$vnum = $total_count - (($page-1) * $page_size); + +$group = array(); +$qry = sql_query("select * from {$g5['sms5_form_group_table']} order by fg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); + +$res = sql_fetch("select count(*) as cnt from {$g5['sms5_form_table']} where fg_no=0"); +$no_count = $res['cnt']; + +$count = 1; +$qry = sql_query("select * from {$g5['sms5_form_table']} where 1 $sql_group $sql_search order by fo_no desc limit $page_start, $page_size"); +$list_text = ""; + +if( !$total_count ){ + $list_text .=" +
  • + 데이터가 없습니다. +
  • + "; +} +while($res = sql_fetch_array($qry)) +{ + $tmp = sql_fetch("select fg_name from {$g5['sms5_form_group_table']} where fg_no='{$res['fg_no']}'"); + if (!$tmp) + $group_name = '미분류'; + else + $group_name = $tmp['fg_name']; + $list_text .=" +
  • + + + + ".cut_str($res['fo_name'],20)." +
  • + "; +} + +$arr_ajax_msg['error'] = ""; +$arr_ajax_msg['list_text'] = $list_text; +$arr_ajax_msg['page'] = $page; +$arr_ajax_msg['total_count'] = $total_count; +$arr_ajax_msg['total_page'] = $total_page; +die( json_encode($arr_ajax_msg) ); +?> \ No newline at end of file diff --git a/adm/sms_admin/ajax.sms_write_group.php b/adm/sms_admin/ajax.sms_write_group.php new file mode 100644 index 000000000..3e64577e4 --- /dev/null +++ b/adm/sms_admin/ajax.sms_write_group.php @@ -0,0 +1,42 @@ +1 order by bg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); +?> +
    + + + + + + + + + + + + + + + + + + + + + + +
    그룹명수신가능추가
    +
    \ No newline at end of file diff --git a/adm/sms_admin/ajax.sms_write_level.php b/adm/sms_admin/ajax.sms_write_level.php new file mode 100644 index 000000000..593236d8e --- /dev/null +++ b/adm/sms_admin/ajax.sms_write_level.php @@ -0,0 +1,56 @@ +encode($data) ); + } +} + +auth_check($auth[$sub_menu], "r"); + +$lev = array(); + +for ($i=1; $i<=10; $i++) +{ + $lev[$i] = 0; +} + +$qry = sql_query("select mb_level, count(*) as cnt from {$g5['member_table']} where mb_sms=1 and not (mb_hp='') group by mb_level"); + +while ($row = sql_fetch_array($qry)) +{ + $lev[$row['mb_level']] = $row['cnt']; +} +$str_json = array(); +$tmp_str = ''; +$tmp_str .= ' +
    + + + + + + + + + '; + for ($i=1; $i<=10; $i++) { + $bg = 'bg'.($line++%2); + $tmp_str .= ' + + + + + '; + } +$tmp_str .= ' + +
    권한수신가능추가
    '.$i.' 레벨'.number_format($lev[$i]).'
    +
    '; + +$str_json['html'] = $tmp_str; +echo json_encode($str_json); +?> \ No newline at end of file diff --git a/adm/sms_admin/ajax.sms_write_person.php b/adm/sms_admin/ajax.sms_write_person.php new file mode 100644 index 000000000..38c8c0b55 --- /dev/null +++ b/adm/sms_admin/ajax.sms_write_person.php @@ -0,0 +1,165 @@ + 0) + $sql_korean = korean_index('bk_name', $ap-1); +else { + $sql_korean = ''; + $ap = 0; +} + +if ($no_hp == 'yes') { + set_cookie('cookie_no_hp', 'yes', 60*60*24*365); + $no_hp_checked = 'checked'; +} else if ($no_hp == 'no') { + set_cookie('cookie_no_hp', '', 0); + $no_hp_checked = ''; +} else { + if (get_cookie('cookie_no_hp') == 'yes') + $no_hp_checked = 'checked'; + else + $no_hp_checked = ''; +} + +//if ($no_hp_checked == 'checked') + $sql_no_hp = "and bk_hp <> '' and bk_receipt=1"; + +$total_res = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where 1 $sql_group $sql_search $sql_korean $sql_no_hp"); +$total_count = $total_res['cnt']; + +$total_page = (int)($total_count/$page_size) + ($total_count%$page_size==0 ? 0 : 1); +$page_start = $page_size * ( $page - 1 ); + +$vnum = $total_count - (($page-1) * $page_size); + +$res = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where bk_receipt=1 $sql_group $sql_search $sql_korean $sql_no_hp"); +$receipt_count = $res['cnt']; +$reject_count = $total_count - $receipt_count; + +$res = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where mb_id='' $sql_group $sql_search $sql_korean $sql_no_hp"); +$no_member_count = $res['cnt']; +$member_count = $total_count - $no_member_count; + +$no_group = sql_fetch("select * from {$g5['sms5_book_group_table']} where bg_no=1"); + +$group = array(); +$qry = sql_query("select * from {$g5['sms5_book_group_table']} where bg_no>1 order by bg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); +?> +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + 이름휴대폰번호등급추가
    데이터가 없습니다.
    + + +
    +
    + +
    + + +
    + + + + + + + + + + + + + + + + + +
    + + \ No newline at end of file diff --git a/adm/sms_admin/config.php b/adm/sms_admin/config.php new file mode 100644 index 000000000..d6a3cb87f --- /dev/null +++ b/adm/sms_admin/config.php @@ -0,0 +1,181 @@ + $res[0], // 결과코드 + 'coin' => $res[1], // 고객 잔액 (충전제만 해당) + 'gpay' => $res[2], // 고객의 건수 별 차감액 표시 (충전제만 해당) + 'payment' => $res[3] // 요금제 표시, A:충전제, C:정액제 + ); +} + +if (!$config['cf_icode_id']) + $config['cf_icode_id'] = 'sir_'; + +if (!$sms5['cf_skin']) + $sms5['cf_skin'] = 'basic'; + +include_once(G5_ADMIN_PATH.'/admin.head.php'); + +?> + +
    +

    + SMS 기능을 사용하시려면 먼저 아이코드에 서비스 신청을 하셔야 합니다.
    + 아이코드 서비스 신청하기 +

    +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + 현재 비밀번호가 입력되어 있지 않습니다. +
    요금제 + '; + } else if ($userinfo['payment'] == 'C') { + echo '정액제'; + echo ''; + } else { + echo '가입해주세요.'; + echo ''; + } + ?> +
    충전 잔액 + 원 + +
    건별 금액
    + 예) 010-123-4567"); ?> + +
    + + > 허용 +
    + + + 레벨 이상 +
    + + +
    + + +
    + +
    +
    + +
    + +
    +
    + + + +
    +

    SMS 문자전송 서비스를 사용할 수 없습니다.

    +
    +

    + SMS 를 사용하지 않고 있기 때문에, 문자 전송을 할 수 없습니다.
    + SMS 사용 설정은 환경설정 > 기본환경설정 > SMS설정 에서 SMS 사용을 아이코드로 변경해 주셔야 사용하실수 있습니다. +

    +
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/config_update.php b/adm/sms_admin/config_update.php new file mode 100644 index 000000000..e224d904b --- /dev/null +++ b/adm/sms_admin/config_update.php @@ -0,0 +1,49 @@ + $res[0], // 결과코드 + 'coin' => $res[1], // 고객 잔액 (충전제만 해당) + 'gpay' => $res[2], // 고객의 건수 별 차감액 표시 (충전제만 해당) + 'payment' => $res[3] // 요금제 표시, A:충전제, C:정액제 +); + +if ($userinfo['code'] == '202') + alert('아이코드 아이디와 패스워드가 맞지 않습니다.'); + +if ($cf_member == 'on') + $cf_member = 1; +else + $cf_member = 0; + +$res = sql_fetch("select * from ".$g5['sms5_config_table']." limit 1"); + +if (!$res) + $sql = "insert into "; +else + $sql = "update "; + +$sql .= $g5['sms5_config_table']." set cf_phone='$cf_phone', cf_register='$cf_register', cf_member='$cf_member', cf_level='$cf_level', cf_point='$cf_point', cf_day_count='$cf_day_count', cf_skin = '$cf_skin' "; + +sql_query($sql); + +// 아이코드 설정 +$sql = " update {$g5['config_table']} + set cf_sms_use = '$cf_sms_use', + cf_icode_id = '$cf_icode_id', + cf_icode_pw = '$cf_icode_pw', + cf_icode_server_ip = '$cf_icode_server_ip', + cf_icode_server_port = '$cf_icode_server_port' "; +sql_query($sql); + +goto_url("./config.php"); +?> \ No newline at end of file diff --git a/adm/sms_admin/css/sms5.css b/adm/sms_admin/css/sms5.css new file mode 100644 index 000000000..3677b3f6a --- /dev/null +++ b/adm/sms_admin/css/sms5.css @@ -0,0 +1,133 @@ +@charset "utf-8"; +/* SIR 지운아빠 */ + +/* 공통 */ +.sms5_txt_success {color:blue} +.sms5_txt_fail {color:red} + +/* 회원정보업데이트 */ +#sms5_mbup dl {zoom:1} +#sms5_mbup dl:after {display:block;visibility:hidden;clear:both;content:''} +#sms5_mbup dt {clear:both;float:left;margin:0 0 5px;width:100px} +#sms5_mbup dd {float:left;margin:0 0 5px} +#sms5_mbup p {clear:both} + +/* SMS BOX */ +.sms5_box {position:relative;padding:10px;border-radius:5px;background:#fbec99} +.sms5_box .box_ico {position:absolute;top:20px;left:-7px;width:7px;height:13px;background:url('../img/box_ico.gif') no-repeat} +.sms5_box .box_txt {border:0;background:transparent;word-break:break-all;resize:none;overflow:hidden} +.sms5_box .box_square {width:100px;height:90px} + +#write_wrap {z-index:9} +#wr_message_lbl {position:absolute;top:50px;left:50px;color:#999;font-size:0.95em;letter-spacing:-0.1em} +.write_scemo_btn {position:absolute;right:10px !important;right:50px;margin:0;padding:0;width:28px;height:28px;border:0;background:transparent;color:#999;font-size:0.95em} +#write_sc_btn {top:15px} +#write_emo_btn {top:50px} +.write_scemo {display:none;z-index:10;position:absolute;left:150px;border:1px solid #e9e9e9;background:#f7f7f7} +.scemo_ico {position:absolute;top:10px;left:-7px;width:7px;height:13px;background:url('../img/scemo_ico.gif') no-repeat} +.write_scemo .scemo_list {z-index:11;margin:0;padding:0;width:190px;height:150px;background:#fff;overflow-y:scroll} +.write_scemo .scemo_add {margin:0;padding:0;height:25px;border:0;background:transparent} +#write_sc {top:10px} +#write_sc .scemo_add {width:25px} +#write_emo {top:45px} +#write_emo .scemo_add {width:50px} +#write_emo .emo_long {width:80px} +.write_scemo .scemo_cls {text-align:right} +.write_scemo .scemo_cls button {margin:5px;padding:0;border:0;background:transparent;color:#666;font-size:0.95em} + +/* 문자보내기 */ +#sms5_send {position:relative;margin:-10px 0 0 20px;padding:0 0 0 490px;height:600px} +#sms5_send h2 {font-size:1em} +#sms5_send ul {margin:0;padding:0;list-style:none;zoom:1} +#sms5_send ul:after {display:block;visibility:hidden;clear:both;content:''} +#sms5_send #send_emo {position:absolute;top:0;left:0;padding:20px 0 0;width:280px} +#sms5_send #send_emo h2 {padding:0 0 10px} +#sms5_send #send_write {position:absolute;top:0;left:290px;padding:20px 20px 0 !important;padding:20px 0 0 20px;width:160px;height:600px;border-left:1px solid #efefef} +#sms5_send #send_write h2 {padding:0 0 10px} +#sms5_send #send_book {position:relative;margin:0 0 0 -1px;padding:20px 0 0;width:100%;height:600px;border-left:1px solid #efefef} +#sms5_send #send_book h2 {padding-bottom:10px} + +#sms5_send .tmp_loading {display:block;padding:180px 0 0;text-align:center} +#sms5_send #send_emo #emo_sel {position:absolute;top:20px;right:10px;margin:0} +#sms5_send #send_emo li {float:left;margin:0 10px 10px 0 !important;margin:0 5px 10px 0;width:110px} +#sms5_send #send_emo .sms5_box {background:#f7f7f7} +#sms5_send #send_emo .box_ico {display:none} +#sms5_send #send_emo .box_txt {cursor:pointer} +#sms5_send #send_emo .emo_tit {display:block;height:20px;line-height:2em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +#sms5_send #send_emo .btn_submit {padding:0 5px;height:24px;border:1px solid #ccc;background:#fafafa;color:#000;font-size:0.95em;vertical-align:middle;cursor:pointer} + +#sms5_send #send_write {z-index:3} +#sms5_send .write_inner {position:relative;left:-20px;padding:15px 20px;width:160px;border-top:1px solid #efefef} +#sms5_send .write_inner h2 {margin:0;padding:0 0 10px !important} +#sms5_send .write_floater {position:absolute;top:15px;right:20px;text-align:right} +#sms5_send .write_floater_btn {margin:0;padding:0;border:0;background:transparent;color:#999;font-size:0.95em;letter-spacing:-0.1em} +#sms5_send .write_inner label, #sms5_send .write_inner input {margin:0 0 3px} +#sms5_send .write_inner label {display:inline-block} +#sms5_send #send_write .sms5_box {margin:0 0 5px} +#sms5_send #sms_byte {position:absolute;top:-37px;right:0;color:#999} +#sms5_send #write_preset {margin:10px 0;color:#999;font-size:0.95em;letter-spacing:-0.1em} +#sms5_send #write_reply {margin:0 0 15px} +#sms5_send #hp_list {width:100%;margin:0 0 5px} +#sms5_send #recv_add {position:relative} +#sms5_send #recv_add button {position:absolute;top:0;right:0 !important;right:20px;margin:0;padding:0;width:45px;height:51px;border:1px solid #ccc;background:#fafafa} +#sms5_send #write_rsv {line-height:1.8em} + +#sms5_send #send_book {z-index:2} +#sms5_send #num_book {margin:0 0 10px;width:auto !important;width:95%;height:500px;overflow-y:auto !important;overflow-y:scroll} +#sms5_send #book_tab {margin:0 35px 10px;text-align:right} +#sms5_send #book_tab a {display:inline-block;width:30px;text-align:center} +#sms5_send #sms_person_form {margin:0 20px} +#sms5_send #send_book .btn_submit {padding:0 5px;height:24px} +#sms5_send #book_desc {margin:0 20px;color:#999;font-size:0.95em;letter-spacing:-0.1em} + +/* 문자전송상세내역 */ +#sms5_sent {} +#sms5_sent #sent_overlap {margin:0 20px 20px;padding:0;border-top:1px solid #e9e9e9;list-style:none} +#sms5_sent #sent_overlap li {padding:10px 0;border-bottom:1px solid #e9e9e9} +#sms5_sent #con_sms {margin:0 20px 20px} + +/* 이모티콘 관리 */ +.sms_preset_sch form {display:inline-block} +#sms5_preset_sel {float:left;padding:8px 20px 0} +.btn_add {float:right} +#sms5_preset {clear:both;margin:0 0 10px;padding:30px 20px 20px 40px;background:#363a3d;list-style:none;zoom:1} +#sms5_preset:after {display:block;visibility:hidden;clear:both;content:''} +#sms5_preset li {float:left;margin:0 20px 20px 0;width:270px;height:125px;background:#fbec99;zoom:1} +#sms5_preset li:after {display:block;visibility:hidden;clear:both;content:''} +#sms5_preset .li_chk {position:absolute;bottom:10px;left:10px;} +#sms5_preset .li_preview {float:left;width:110px} +#sms5_preset .box_square {padding-right:7px;border-right:1px solid #e2d693} +#sms5_preset .li_info {float:left;margin:0 0 0 10px;width:150px;line-height:1.5em} +#sms5_preset .li_date {position:absolute;bottom:10px;left:30px;color:#86857d} +#sms5_preset .li_cmd {position:absolute;bottom:10px;right:10px} +#sms5_preset .li_cmd a {color:#86857d;font-size:0.95em;letter-spacing:-0.1em} + +#sms5_emo_add {position:relative} +#sms5_emo_add .sms5_box {padding:10px !important;padding:10px 5px;width:160px} +#sms5_emo_add .box_txt {margin:0 0 5px} +#sms5_emo_add .write_scemo_btn {right:10px} +#sms5_emo_add .write_scemo {left:170px} + +/* 휴대폰번호 관리 */ +#hp_check_el {width:148px} +#hp_check_el ul {margin:5px 0 0;padding:0;border:1px solid #f7c3cf;background:#f8e8eb;list-style:none} +#hp_check_el li {position:relative;padding:5px;border-bottom:1px solid #f6dae1} +#hp_check_el strong {position:absolute;top:6px;right:5px;color:#ff3061;font-size:0.95em;font-weight:normal;letter-spacing:-0.1em} + +/* 휴대폰번호 파일 */ +#sms5_fileup_frm {margin:0 0 10px;padding:0 20px 5px;border-bottom:1px solid #e9e9e9} +#sms5_fileup_frm div {padding:0 0 5px;margin:0 0 5px;border-bottom:1px solid #f4f4f4} +#sms5_fileup_frm div.sch_last {margin:0;border:0} +#sms5_fileup_frm strong {display:inline-block;width:80px} +#sms5_fileup_frm strong.sch_long {width:160px} +#sms5_fileup_frm label {display:inline-block;margin:0 5px 0 0} +#sms5_fileup_frm .btn_submit {padding:0 5px;height:24px} + +#sms5_fileup_frm #sms5_fileup {border:0} +#sms5_fileup_frm #upload_info {margin:20px 0} +#sms5_fileup_frm #upload_result {margin:0;padding:10px 0;border-top:3px solid #383838;border-bottom:3px solid #383838;list-style:none} +#sms5_fileup_frm #upload_result li {margin:0 0 5px} +#sms5_fileup_frm #upload_result .local_desc01 {margin:5px 0;padding:10px} +#sms5_fileup_frm #btn_fileup {margin:5px 0 0} + +#sms5_fileup_frm .sms_fileup_hide {display:none;border:0} diff --git a/adm/sms_admin/emoticon_move.php b/adm/sms_admin/emoticon_move.php new file mode 100644 index 000000000..592e273ce --- /dev/null +++ b/adm/sms_admin/emoticon_move.php @@ -0,0 +1,114 @@ + + +
    +

    + +
    + + + + +
    + + + + + + + + + + + + + + + + +
    이동할 그룹을 한개 이상 선택하여 주십시오.
    선택그룹
    + + + +
    +
    + +
    + + +
    +
    + +
    + + + + diff --git a/adm/sms_admin/emoticon_move_update.php b/adm/sms_admin/emoticon_move_update.php new file mode 100644 index 000000000..aa1227f6a --- /dev/null +++ b/adm/sms_admin/emoticon_move_update.php @@ -0,0 +1,60 @@ + + + +HEREDOC; +?> \ No newline at end of file diff --git a/adm/sms_admin/form_group.php b/adm/sms_admin/form_group.php new file mode 100644 index 000000000..4b713d3d0 --- /dev/null +++ b/adm/sms_admin/form_group.php @@ -0,0 +1,177 @@ + + + + +
    + +
    + + + +
    +
    + 건수 : +
    +
    + +
    +

    그룹명순으로 정렬됩니다.

    +
    + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    목록
    + + + 그룹명이모티콘수이동보기
    미분류 + + + + 보기 + +
    + + + + + + + > + + + + + + + + 보기 +
    + +
    + +
    + + + +
    + +
    + + \ No newline at end of file diff --git a/adm/sms_admin/form_group_move.php b/adm/sms_admin/form_group_move.php new file mode 100644 index 000000000..27faa14d1 --- /dev/null +++ b/adm/sms_admin/form_group_move.php @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/adm/sms_admin/form_group_update.php b/adm/sms_admin/form_group_update.php new file mode 100644 index 000000000..058980a11 --- /dev/null +++ b/adm/sms_admin/form_group_update.php @@ -0,0 +1,92 @@ +'$fg_no' and fg_name='$fg_name'"); + if ($res) + alert('같은 그룹명이 존재합니다.'); + + sql_query("update {$g5['sms5_form_group_table']} set fg_name='$fg_name', fg_member='$fg_member' where fg_no='$fg_no'"); + sql_query("update {$g5['sms5_form_table']} set fg_member = '$fg_member' where fg_no = '$fg_no'"); + } +} +else if ($w == 'de') // 그룹삭제 +{ + for ($i=0; $i \ No newline at end of file diff --git a/adm/sms_admin/form_list.php b/adm/sms_admin/form_list.php new file mode 100644 index 000000000..4e6cae1f5 --- /dev/null +++ b/adm/sms_admin/form_list.php @@ -0,0 +1,238 @@ + + + + +
    + 건수 : +
    + +
    +
    + + +
    + +
    + + + + + + +
    +
    + +
    + + +
    + + + +
    + + + + +
      + +
    • + +
      + + +
      +
      + +
      +
      + 그룹
      + 제목
      +
      +
      + 등록 +
      + +
    • + +
    + +
    + + +
    + +
    + + + + + \ No newline at end of file diff --git a/adm/sms_admin/form_multi_update.php b/adm/sms_admin/form_multi_update.php new file mode 100644 index 000000000..5c1bd5930 --- /dev/null +++ b/adm/sms_admin/form_multi_update.php @@ -0,0 +1,27 @@ +'$fo_no' and fo_content='$fo_content'"); + if ($res) + alert('같은 이모티콘이 존재합니다.'); +*/ + $res = sql_fetch("select * from {$g5['sms5_form_table']} where fo_no='$fo_no'"); + if (!$res) + alert('존재하지 않는 데이터 입니다.'); + + if ($fg_no != $res['fg_no']) { + if ($res['fg_no']) + sql_query("update {$g5['sms5_form_group_table']} set fg_count = fg_count - 1 where fg_no='{$res[fg_no]}'"); + + sql_query("update {$g5['sms5_form_group_table']} set fg_count = fg_count + 1 where fg_no='$fg_no'"); + } + + $group = sql_fetch("select * from {$g5['sms5_form_group_table']} where fg_no = '$fg_no'"); + + sql_query("update {$g5['sms5_form_table']} set fg_no='$fg_no', fg_member='{$group['fg_member']}', fo_name='$fo_name', fo_content='$fo_content', fo_datetime='".G5_TIME_YMDHIS."' where fo_no='$fo_no'"); +} +else if ($w == 'd') // 삭제 +{ + if (!is_numeric($fo_no)) + alert('고유번호가 없습니다.'); + + $res = sql_fetch("select * from {$g5['sms5_form_table']} where fo_no='$fo_no'"); + if (!$res) + alert('존재하지 않는 데이터 입니다.'); + + sql_query("delete from {$g5['sms5_form_table']} where fo_no='$fo_no'"); + sql_query("update {$g5['sms5_form_group_table']} set fg_count = fg_count - 1 where fg_no = '{$res['fg_no']}'"); + + $get_fg_no = $fg_no; +} +else // 등록 +{ + if (!$fg_no) $fg_no = 0; + + if (!strlen(trim($fo_name))) + alert('이름을 입력해주세요'); + + if (!strlen(trim($fo_content))) + alert('이모티콘을 입력해주세요'); + + $res = sql_fetch("select * from {$g5['sms5_form_table']} where fo_content='$fo_content'"); + if ($res) + alert('같은 이모티콘이 존재합니다.'); + + $group = sql_fetch("select * from {$g5['sms5_form_group_table']} where fg_no = '$fg_no'"); + + sql_query("insert into {$g5['sms5_form_table']} set fg_no='$fg_no', fg_member='{$group['fg_member']}', fo_name='$fo_name', fo_content='$fo_content', fo_datetime='".G5_TIME_YMDHIS."'"); + sql_query("update {$g5['sms5_form_group_table']} set fg_count = fg_count + 1 where fg_no = '$fg_no'"); + + $get_fg_no = $fg_no; +} + +$go_url = './form_list.php?page='.$page.'&fg_no='.$get_fg_no; +goto_url($go_url); +?> \ No newline at end of file diff --git a/adm/sms_admin/form_write.php b/adm/sms_admin/form_write.php new file mode 100644 index 000000000..b5e5a9829 --- /dev/null +++ b/adm/sms_admin/form_write.php @@ -0,0 +1,233 @@ + + +
    + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + +
    목록
    + +
    메세지 +
    + + + + +
    0 / 80 byte
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + +
    + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    업데이트
    +
    +
    + + 목록 +
    +
    + + + + + + \ No newline at end of file diff --git a/adm/sms_admin/history_list.php b/adm/sms_admin/history_list.php new file mode 100644 index 000000000..82b6d8326 --- /dev/null +++ b/adm/sms_admin/history_list.php @@ -0,0 +1,100 @@ + + +
    class="local_sch01 local_sch" method="get"> + + + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    목록
    번호메세지회신번호전송일시예약총건수성공실패중복재전송관리
    + 데이터가 없습니다. +
    예약":'';?> + 수정 + +
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/history_member.php b/adm/sms_admin/history_member.php new file mode 100644 index 000000000..35873dcf8 --- /dev/null +++ b/adm/sms_admin/history_member.php @@ -0,0 +1,90 @@ + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    목록
    번호회원보내는번호받는번호전송일시예약Log
    + 데이터가 없습니다. +
    예약":'';?>
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/history_num.php b/adm/sms_admin/history_num.php new file mode 100644 index 000000000..519f88b99 --- /dev/null +++ b/adm/sms_admin/history_num.php @@ -0,0 +1,108 @@ + + +
    + + + + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + {$res['mb_id']}"; + else + $mb_id = '비회원'; + ?> + + + + + + + + + + + + + + +
    목록
    번호그룹이름회원ID전화번호전송일시예약전송메세지관리
    + 데이터가 없습니다. +
    예약":'';?> + 수정 + +
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/history_send.php b/adm/sms_admin/history_send.php new file mode 100644 index 000000000..fe961494d --- /dev/null +++ b/adm/sms_admin/history_send.php @@ -0,0 +1,138 @@ + + + SMS_con($config['cf_icode_server_ip'], $config['cf_icode_id'], $config['cf_icode_pw'], $config['cf_icode_server_port']); + +$reply = str_replace('-', '', trim($write['wr_reply'])); + +$result = $SMS->Add($list, $reply, '', '', $write['wr_message'], '', $wr_total); + +if ($result) +{ + $result = $SMS->Send(); + + if ($result) //SMS 서버에 접속했습니다. + { + sql_query("insert into {$g5['sms5_write_table']} set wr_no='$wr_no', wr_renum='$new_wr_renum', wr_reply='{$write['wr_reply']}', wr_message='{$write['wr_message']}', wr_total='$wr_total', wr_datetime='".G5_TIME_YMDHIS."'"); + + $wr_success = 0; + $wr_failure = 0; + $count = 0; + + foreach ($SMS->Result as $result) + { + list($phone, $code) = explode(":", $result); + + if (substr($code,0,5) == "Error") + { + $hs_code = substr($code,6,2); + + switch ($hs_code) { + case '02': // "02:형식오류" + $hs_memo = "형식이 잘못되어 전송이 실패하였습니다."; + break; + case '23': // "23:인증실패,데이터오류,전송날짜오류" + $hs_memo = "데이터를 다시 확인해 주시기바랍니다."; + break; + case '97': // "97:잔여코인부족" + $hs_memo = "잔여코인이 부족합니다."; + break; + case '98': // "98:사용기간만료" + $hs_memo = "사용기간이 만료되었습니다."; + break; + case '99': // "99:인증실패" + $hs_memo = "인증 받지 못하였습니다. 계정을 다시 확인해 주세요."; + break; + default: // "미 확인 오류" + $hs_memo = "알 수 없는 오류로 전송이 실패하었습니다."; + break; + } + $wr_failure++; + $hs_flag = 0; + } + else + { + $hs_code = $code; + $hs_memo = get_hp($phone, 1)."로 전송했습니다."; + $wr_success++; + $hs_flag = 1; + } + + $row = array_shift($list); + $row['bk_hp'] = get_hp($row['bk_hp'], 1); + + $log = array_shift($SMS->Log); + sql_query("insert into {$g5['sms5_history_table']} set wr_no='$wr_no', wr_renum='$new_wr_renum', bg_no='{$row['bg_no']}', mb_id='{$row['mb_id']}', bk_no='{$row['bk_no']}', hs_name='{$row['hs_name']}', hs_hp='{$row['hs_hp']}', hs_datetime='".G5_TIME_YMDHIS."', hs_flag='$hs_flag', hs_code='$hs_code', hs_memo='$hs_memo', hs_log='$log'"); + } + $SMS->Init(); // 보관하고 있던 결과값을 지웁니다. + + sql_query("update {$g5['sms5_write_table']} set wr_success='$wr_success', wr_failure='$wr_failure' where wr_no='$wr_no' and wr_renum='$new_wr_renum'"); + sql_query("update {$g5['sms5_write_table']} set wr_re_total=wr_re_total+1 where wr_no='$wr_no' and wr_renum=0"); + } + else alert("에러: SMS 서버와 통신이 불안정합니다."); +} +else alert("에러: SMS 데이터 입력도중 에러가 발생하였습니다."); + +?> + + \ No newline at end of file diff --git a/adm/sms_admin/history_view.php b/adm/sms_admin/history_view.php new file mode 100644 index 000000000..6e29571da --- /dev/null +++ b/adm/sms_admin/history_view.php @@ -0,0 +1,246 @@ +(재전송)"; +} else + $sql_renum = " and wr_renum='0'"; + +$total_res = sql_fetch("select count(*) as cnt from {$g5['sms5_history_table']} where wr_no='$wr_no' $sql_search $sql_renum"); +$total_count = $total_res['cnt']; + +$total_spage = (int)($total_count/$spage_size) + ($total_count%$spage_size==0 ? 0 : 1); +$spage_start = $spage_size * ( $spage - 1 ); + +$vnum = $total_count - (($spage-1) * $spage_size); + +$write = sql_fetch("select * from {$g5['sms5_write_table']} where wr_no='$wr_no' $sql_renum"); +if ($write['wr_booking'] == '0000-00-00 00:00:00') + $write['wr_booking'] = '즉시전송'; + +include_once(G5_ADMIN_PATH.'/admin.head.php'); +?> + + + +
    + + + + + + + + + + +
    + +
    +
    + 전송건수 + 성공건수 + 실패건수 + 전송일시 + 예약일시 + 회신번호 +
    + +

    전송내용

    + +
    + + +
    + + +

    전송실패 문자 재전송 내역

    + + + + + + + + + + + + + + + + 0"); + $re_vnum = $res['cnt']; + + $qry = sql_query("select * from {$g5['sms5_write_table']} where wr_no='$wr_no' and wr_renum>0 order by wr_renum desc"); + while($res = sql_fetch_array($qry)) { + ?> + + + + + + + + + + + + + +
    번호전송일시총건수성공실패관리
    + 수정 + +
    + + + +

    중복번호

    +
      + $v){ + if( empty($v) ) continue; + ?> +
    • 중복
    • + +
    + + +

    문자전송 목록

    + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    번호그룹이름회원ID휴대폰번호전송일시결과비고내역
    + 데이터가 없습니다. +
    + 결과코드 :
    + 로그 :
    + 메모 : +
    + + 내역 + + 내역 + +
    +
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/img/ajax-loader.gif b/adm/sms_admin/img/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..96fefbc5130a6514a03709b2b8f40a88cf82fae2 GIT binary patch literal 1924 zcmciDX;70#00!U)Urwn9NDf8|Nk{?(l!}Qm1QSd^sNw-w96TTt3HoXQ;KEY5&xQ_@`6p)R|7Ze|KlUecpGz-Ms;R zyF6JG&`!`>5GcFv1qcN4J{Gsm76V0CaaFx8^nHnS`e;crfkNLKn`)Z<^T57ad%!m& znBjBCcl%a%%QdDs?C*Arv(c=i=qQKtu}LuwEDq~JJeL)!)orci6g~AYxE|RX;ebAo47rl|h=>fFdqNlMi+s5|5((!#k{zHt zS4${~6YmJ+LP`!vWo6}8<<}Gyn@jVh*X?p$m3wP|x=n>Zs#J3mrIod#8ydYktK+_5 zs4DyV^@f4FgYsU%9mnpoN%&iYhYk}DGH~~D#_=O(M~5nA4bz6n{>8q9mvhgTy*I7~ z9Q!LUrU41@LFa}cvtO@kanB`aV)-0EL8CXuXuN&&E3N+SN8Uvl=TJUl1vbeJ7$&sU<&M?!@% z6R@)CdP1=*nr)Y}wS-;|s5hl$1h!z>TD`TXrsnNkbt+L!x6%w88YcBQk4Bv_j^lKM ziHjYiN&7p*r?`8@o{@^VyR!q+`X%M!z{30+(#H}tjqX+!#}Ldwy>{1Q{pHux+4aoU zxjcK6Rk;hAlV=gYbF}?hcWFmR;}PG3pZ0=`SM|EHf&P0YnNQ`;MtpyG`1qQx-R@pc z79>3*H!q)GP-qQ36T*Nc077IU&smTFM_u%$$+^gyc-MLWRY_CEs9G+R z%M~JVq*P>6QrgwC(&3OznUnDhwTgKk-$h$<)#@`tjaDK^j|h^kuM*rXwuV`xN(5cn)S zOa`=v_z|=+IQFvR9r9ht&@lF@qh2&{zlv~M*2BA1-09zC+tNBs8&?l5j;de%FxmX0 z%c{bW|3XOJEtn5~9QIizhV7q=&ZiY1vl%7mbeb?lELr&x_k$s12sj~#1ZG!ZOYsU* z0Q3_?HATa^*#d-e2o2PxP$kAJMwwz@AX*Ru4Ui!kOa~ZveL8r`cjW1rl*cVoyob6+ zyb<-Nq__VSZdKn07fnpVzi;p#d?545r3fk+A%-a_l^#L3YqbE}gX5r~g!^FI$hs5Q zrrMhcTKgJJeS#WyU8zEu6Y-ZT25iCX2jY85h@@>+BblK-6Pp5%LT4aLEFAo05Ys$S(J2IA~KZ^z>7G@QVNZYl8|LSC;?yS86TEkTBr=dT*}JH%@d?)w5@IJ z@5^lkv%3%P!*yms4qS1N5X-Q`;R>vtI^Z#AZvw^D>4W{#%ZkO(NL@+Y5LW7x}u!bhj)Gf3x+_0a&Nb`z37R$Ctf6$)nzUh%Dz%Yf8 zFFe1E4-WY<^eAh8Zb4yDaY?AT0|62oPqq{@4yM`&Lm<~qp<@|k-qn${=TKEtC5^We z4Xv|;9#+^vt00|{-uF+%mM*7ZTx$hWML$itg`Xf!V#i3+cq4Ikuwbt5ss2g-;>&S^ O2}sxo@>+QV&EpTcIvUad literal 0 HcmV?d00001 diff --git a/adm/sms_admin/img/ajax-loader2.gif b/adm/sms_admin/img/ajax-loader2.gif new file mode 100644 index 0000000000000000000000000000000000000000..fd1a189c21fed1c7ba00c4bb4fad407bd6d1e5f9 GIT binary patch literal 7825 zcmbuES5#ApyTy~7eo}yd8UkVpXkY*_Q9y_qiinOPDmsj9qNsz4jwlKWVnXkP4hk5G z^b(393euZ^(u<-X0-}Q58OQNIb2H9b_uh5y|K)lgvd&t0I1j(IzrDZxoedl9%vT>U zCKwYc2!y}>`s>xJSC1Y&>gwvcefxGnK|xANN=QhEr>EzUBS&`b+-YlTD-Z}c9FCfr z8i7Dy|3!9K@3h0l%5gp4*aRT{{nzgx1}L9~%AfM3#smWRmumaQ@4U>9pSbO+0$LIPtr>H><{{!d^!|HwbAIt1{-+7Ta2>x>}r<$279U7FR%5w|JIQ-7uka zgOwiqlR{u=9DeWlvEEZ6R_mcX_D>?!>0WF)7j`|0NDVG9f412Pc!v4~*d&C+3h)FQ zDA^3X0!Y&NfeCKe#@31Xe6SRh6&ou`vnqJkFWuC?6;s23t|!&A5d{Rda8R%jf~qf2v__<&*>hT?j;)XK+f ztS03*6|qPPs?5Z>TZh$I&;Xj@2w}WgW3jm-&%UQ8qdt6vFQ-g#l zFP9MgVXC(Igxa%BYo*{KhHakWKByrg(VSRs#f^bSS2cz>%S1mlM=i0srSS8cvWrsR zVQ?VjptL(mXJ^iv7-~=@^G`=&?Z~k_rh}cO`KG&P^m@>5mnBP;LPi5$_HRPN9j(Mn zRSmjPkNp9LUEM%Fd(cV`wV7PRgvA{i0*m6PUHK9}Lt~5neh@}b6xp}K^n3aq>r3D- zY?OqWg>w)9JOd!YQx`91fbr?PP%R`AOuJ@@7q}E#q6M;YL4HPAl^F~{AXHQ%sB68^ zY}8uU-ofkq;U2#e?(SpXfgfZK^MW4uJ<`V}5MjJ+XQR_ zk-wpCJ#mr%Na(wvfPh_G6!d|eP1#7I4PuNCW#3qA{4qpHBef0 zHC9{WUT>+Wt!uS}J8pKG!F_!#J%aweLqo&;$b*O1LP7>7^e3NA^TR{Jo*QG6VdPi( zdqPoc>Vp;*_6d7G+ucZ*`iTTl#iY7V5)7Y=|?9n=^KTrBx}O>hwOGVyhkcFV0;y-JHswxK=BXRjL+Xuw<04-vi60w zKUJ|*RhxIJ?>XlQLI{Fk2z`;+ab{N2QCIi`2dHM=88f`vr%Kj+s6k!&2i0A zTrxuQiS%@BE~3cFF++R^pk-+mj0m?2RWSJ8y>h`#bF2I9og?A*LPkb|gL@wd#)JDO z`QzxuXO_V)Ue1}lelhdb<3;GlUrRn=IOUJMhW1li8~h~e-WfR)R1c`A0Sg$1>g7ah zM^!4nXqLrlRi&(7xP5A*cRx#wt#9f&A=kaVnrm0$c9fnIl3=(mUEqH($H8s4W966X zh)^4*UG%R#?v+8=7ag|Vi8xlFwKH8<@>!;~>ML338V{xRws)Bdn@g}584&Z?7Hl<?dh(AhJ@%{ zT!Mw77j%hnkWLhqjO)U2aVNEsGcIwFG-8lE1*hnuIYb@2yu658l2eA0Z(q05yiKMw z-eESjQkyl~s8m|VA}Xb;``-P9{j^b)p#`)7m5GOA>JOewJ|)b&n0@|oj{fTP{JRey zfBo&#@1Otp@>%VhMpb_qVj~stG-!XD;CAhZ9=7|jzXb(ZGtw?#{tbT%DmBQ%=`&RY z9pX>>)$UBz{Bw!wl5KWm)bFe-iUt1+iGc_oUpf!- z^G8q@rRzsr0h6>%*?36YMSbMt6#$OrP=ZSn1!#W6HEjuA9h`22R2B*-eKJLe-R(dcD5Il71~>?fJm?#P2SEY#`HL|XbP$+*PNUN1UJVLppa1xEkn}s_ zle?uV>qo0|a}>~-LC~?*0T?jhH&uibN-Wt~!zOSmBDq#5$=QAd$dhT&A}SMz^t-a6 z{gcV8Gp$!IZ#p^Yv-0O4IaWC<}6I4|E`d1!o7Wj;gu7GO?hlH@Ylnq1am9ZvNH# zs_xcuOnTM|^^>C8y~EsBnsRWNUtk0igTsD-;x*|%%3`o9Y%~l25MK_3Ttt?U1=%?j z7ngG>Hw(!WbEF~%HltQo3!yq7lSA$5qELlcJ1U?Jbt{q9HSmYVl>-U`q#4kBViKL1 z0Y;yU&drl1Un*x)`2N*rA^f4|_oi(n`=nAcRZt#`XG9RmkfGCzW0RD?SfS&zO|7XV zf?<2y{nS(sfgpQi?eXFhp`nA&@kM5ci6reY-~E_r0 z?(RFiLS&$IScpCV#w@Y%C$|Pj6Vrmnr1|GBjN$ot@C_e)PlA3m!hR>^eafg$Qb`d} zkw52mG^jMJBUYM^IC`=~5}zT~mZ=<(q$QeY4Kun_^~VUZMchsC1eN*?#QT57PubN^ z6B54D&+9jf)lE6Q=FHvIvjVJF$AUIAl6UfX*KvnnyYSC($j&A9iFRLI;QyS}`pd(> zgZ7)-*BZel4Z1q%TLUs7=BoI5u{v=LDj8Q3H-RpF3x@*V_kDVwL*&dvNjnCnA5jq#qY=j7Iu^NUBks0Ly~FC<7rs1FXna z+)#llE268+uv;KnD=eynn=FxAP`fz_fIv_8oxY)g{Gq;)G9^`G@w zxlY$o+O4>yRI&QPmpsd=IDPZY%(yF>0cS6sPFjsw$WIx@*g)QEmq3A1e=Q^^FhnaN zI8qbV*cFqg2NJ-Pq(xV*rY*dBB_mUQZISF+L7qBLT3YO_C@Zc6%5M@B(=@AjtMAG|x<5(5tQ+>IL;t9@ANQ#nav%#pF6 zJi7A<0S|7qCB=wxM4d-e3uK%-JDq-R^`M16eoZ{EbK9xI>@v&s+OLAo&0cglHofnd z*LkkGkW1d-4+oqK=3(J58fY5Li9z5i@CD7pq~uf>LNbJxvmq!w*9a>ti!U)ND9ou8 zpk)wTeX9^Gtm7-dMzGl!YwNyigm$;x*GKxG0Up$MXOxWreHD+H5b)$l@hI|a{*m`x zbbcO-s+)Tc-1L@bbdd-T)f|Uy8l(0fpJOlx3U=)#*qwrkVh%Qili&e=hC3_?V^}Vy zkIc^4mPIeL!Dn@kL{NweTuco`u~{7p@vbeDQzZngMQdvs+IM9wDb5Uv;!LX_%l}@- z!JlOGjgA&-8D-~h6@T%Zi0z)?Xt-Pc)2Vi`0o}nVI4OI#`oy!@W~ya`UV- zB-aehhTtLr23=Q_^DA#aH;r%Is5L@wfHe&~qy+@a+PH9MXW>0I*4vppz=Vdus}DMc z``La)CqK_hAj3UUnGsN%P8qzeofN{8$@TL+IcQYxJ4Rn zyY6n$y_5B31yA?8u4k3DMcYn~W43z_XZ_6ZI)CA`uNL_T8j9iufowcX*_opR=VN0d z5{zWYFm}ZlO^(Yj!eZgr9Q{1F6fWXniqdj!Whqov#m1oe+qG=8p|Obxw<7JDb*=H; zDliBQLYLc7aBQqAGIKoh(b$NOvTPG4dC1h`fMUW6A|U6~0k3C#F0v8&Y(7`MDO#5V z&|kbfw>*HUhJ)x=oPmt$RUV&uh6xuIuC+P1*;&kuROf1F5H#O$T7gmXR@a^K!|MaD zUN+xI%ojC=gNv%)xRZm5n15=s)8rmU+qe#;UqUu{@cO>(mecY6>UgV_n>0Z4U{=fv z)~ZcG8fPyYjHf>Luq|5~5_Qd^Ongf3abdF6E?(Y^58Tdk%N`fpOs!QcTWD}vi{gu7 zetcZHd`C(`^vMyCsF#cv7oVgbfuy5XczAkt1~)4k$>HQ(E7Vdp>576gIQjE zU6^QwLCr7}ECc`{(tNLh4-kmNIzD=@c}SZ}8W@-JF>n;>S}o3V`sy-!N6JTu=q~37EM1>=63A*i<<{4_C&tB%kytaHclxM z=k;7(oOlD5eY=V8ZAVMmExi5B;wIE+}z(S+e!Z=`x|q^^1F| zE8edPezO6yVA@aGdfA%Bf4|HHhqQA?3<#ahhLt-?7tQTVUK7wD5%E@Z%>*nH<#uj} z2-yExZ2&-Y$=*JTflIJ1jz*xK+R!CEEHa8s@{MLi5K@=;CX=`*a!s0)k;%exu5t1? zp~YMUpab88OLPn9H<-EAw+xOEpfn8_>TIiSK2}F?JJH%j57qB!>FZbTYBPL5?;jXK zjYpn7qED=Ab8AB`@BXJ3ulu$*{!bCv;M?=|lJp#MjE<#l|7ECf==sgXXh@#RSi%C5 zq@MMK8?9AgCDt)PJ0ibq7O5!C)6P>V6v0JbYyW_Npff&~EW-lG-b9iQVof2Zrn)C4 zom)r2$#|{+N=gE)b4zgnQkaAZ$yjxDJRh$l6$8 z(f&~rt^|N~eGHkJzQ=at;2>%I74%GNaC#aYeejThz4`(`L)D3z40G9j!v*S&b1yf@ z4>XZf+4Pm7SFFYWE?srvS`4EFz7=)GY}n1S zB)ny}fM)LW(O4qft8G^`;5vEec_+sqcvt9M>!AK`_q(ri`Cmv6h2+RHY+0ui1$Kc( zEXkW&8V@}h#w@a{4x1XJ>a_3hmxxsJ9&hmR-n)$^@D6qt(!wKxf=Qv45i}|_DtO`hch+z*6DrXWIJ z+7&Fh8iw%*QCjv2$$EgXz`#~cXbDxrZ1Bz5u5S;vMlSwiRhg$`vL+TXVTzu=MQhL~IyDoU1xuUq3WmjC2Fwj*R)y00x~a3-!`?Y{rF-`U_eTa+1}8NllC z2wKW*VW|?FinaH&*H2$)c;cG!ok%X2+#L4G9PARZVB zNAN;V#Bk%V1WsIF3KQ@HJkyv_R{_~NT0x-z21mSCHdEV*?B8WdBbbZMeevRS z5@SuaWDeYeP zJU#!$?RGZ$Zc?k+#|O=etIbPhFYl*?sC_`2!?uYj+OZlr8pmSz`I|xsdpz9hChm#( zu*$t>CKkmG+y_se&03X4+v425296ZH4GgQ7LHcu_GAZf?EN zakuLZW#vHoP#4x?(e~(W=e>>SBV_p5fmg5JynXk6<+u5q@;fK6{QXfqTq-#+@=~r5 zlpUX98A%6@(#3MqWjwd-ay_rQPU^1urOS{&fpa#ia+GL2xq0((il>|MhRngFBs4&q zEAjLX*T(_RBQZv3{0Z+QeF%^PiD^7A1CV8Na)Gb{Zc%C}yQri>D<`wKiVcBvV1A7X zTo1Z8X`-zcIv8hL_uW%L>LKVxlJEUH!^7o9Gy$DE_!xpmxwJX{ln49_h7p`g{WLde zbcVg*@%ve}y<9A{g2@b%_FNSx(By4Tx24FP)!>IO;(L5L1!{zTu%WRSDH8{+1AjN) zo#8L$tdCiU>DcCGwUIe(nQjutQd` LHfkt@K(PD|Q{O_Y literal 0 HcmV?d00001 diff --git a/adm/sms_admin/img/box_ico.gif b/adm/sms_admin/img/box_ico.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d57d3668813c2a07fd22931284e2baf5136b3c3 GIT binary patch literal 60 zcmZ?wbhEHbWM|-IXkcXc{buI>|Nj+#vM@3*Ff!;c00Bsbfl0WBKYhZ9e@wHBZbiSn NWL*~87{th64FDS>5}*J8 literal 0 HcmV?d00001 diff --git a/adm/sms_admin/img/scemo_ico.gif b/adm/sms_admin/img/scemo_ico.gif new file mode 100644 index 0000000000000000000000000000000000000000..625048e490f176a8b00d8f11260f3cf2f89a6614 GIT binary patch literal 67 zcmZ?wbhEHbWM|-In8?InVPT=6q4EF!e+C8y#h)yU3=B*RIzTo9NS=X7WQu$`OV literal 0 HcmV?d00001 diff --git a/adm/sms_admin/install.php b/adm/sms_admin/install.php new file mode 100644 index 000000000..e420dd7c4 --- /dev/null +++ b/adm/sms_admin/install.php @@ -0,0 +1,87 @@ + +
    + +
    + + var answer = confirm("이미 sms5가 설치되어 있습니다.새로 설치 할 경우 db 자료가 망실됩니다. 새로 설치하시겠습니까?"); + if (answer){ + document.hidden_form.setup.value = "1"; + document.hidden_form.submit(); + } else { + history.back(); + } + + '; + exit; + } +} +?> + +
    +

    SMS5 설치가 시작되었습니다.

    +
      +
    1. 전체 테이블 생성중
    2. + +
    3. DB설정 중
    4. + +
    5. +
    + +

    + +
    +document.getElementById('sms5_job_01').innerHTML='전체 테이블 생성 완료';"; +flush(); usleep(50000); + +$read_point = -1; +$write_point = 5; +$comment_point = 1; +$download_point = -20; + +//------------------------------------------------------------------------------------------------- +// config 테이블 설정 +$sql = " insert into {$g5['sms5_book_group_table']} set bg_name='미분류'"; +mysql_query($sql) or die(mysql_error() . "

    " . $sql); + +echo ""; +flush(); usleep(50000); +//------------------------------------------------------------------------------------------------- + +echo ""; +flush(); usleep(50000); +?> + + + + + \ No newline at end of file diff --git a/adm/sms_admin/member_update.php b/adm/sms_admin/member_update.php new file mode 100644 index 000000000..b0b20bd3f --- /dev/null +++ b/adm/sms_admin/member_update.php @@ -0,0 +1,63 @@ + + +

    +
    +
    +

    + 새로운 회원정보로 업데이트 합니다.
    + 실행 후 '완료' 메세지가 나오기 전에 프로그램의 실행을 중지하지 마십시오. +

    +
    +
    +

    + 마지막 업데이트 일시 :
    +

    +
    + +
    +
    + +
    + +
    +
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/member_update_run.php b/adm/sms_admin/member_update_run.php new file mode 100644 index 000000000..1dba4cbf7 --- /dev/null +++ b/adm/sms_admin/member_update_run.php @@ -0,0 +1,118 @@ +encode($data) ); + } +} + +auth_check($auth[$sub_menu], "w"); + +$count = 0; +$hp_yes = 0; +$hp_no = 0; +$hp_empty = 0; +$leave = 0; +$receipt = 0; + +// 회원 데이터 마이그레이션 +$qry = sql_query("select mb_id, mb_name, mb_hp, mb_sms, mb_leave_date from ".$g5['member_table']." order by mb_datetime"); +while ($res = sql_fetch_array($qry)) +{ + if ($res['mb_leave_date'] != '') + $leave++; + else if ($res['mb_hp'] == '') + $hp_empty++; + else if (is_hp($res['mb_hp'])) + $hp_yes++ ; + else + $hp_no++; + + $hp = get_hp($res['mb_hp']); + + if ($hp == '') $bk_receipt = 0; else $bk_receipt = $res['mb_sms']; + + $field = "mb_id='{$res['mb_id']}', bk_name='{$res['mb_name']}', bk_hp='{$hp}', bk_receipt='{$bk_receipt}', bk_datetime='".G5_TIME_YMDHIS."'"; + + $res2 = sql_fetch("select * from {$g5['sms5_book_table']} where mb_id='{$res['mb_id']}'"); + if ($res2) // 기존에 등록되어 있을 경우 업데이트 + { + $res3 = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where mb_id='{$res2['mb_id']}'"); + $mb_count = $res3['cnt']; + + // 회원이 삭제되었다면 휴대폰번호 DB 에서도 삭제한다. + if ($res['mb_leave_date']) + { + sql_query("delete from {$g5['sms5_book_table']} where mb_id='{$res2['mb_id']}'"); + + $sql = "update {$g5['sms5_book_group_table']} set bg_count = bg_count - $mb_count, bg_member = bg_member - $mb_count"; + if ($res2['bk_receipt'] == 1) + $sql .= ", bg_receipt = bg_receipt - $mb_count"; + else + $sql .= ", bg_reject = bg_reject - $mb_count"; + $sql .= " where bg_no='{$res2['bg_no']}'"; + + sql_query($sql); + } + else + { + if ($bk_receipt != $res2['bk_receipt']) { + if ($bk_receipt == 1) + $sql_sms = "bg_receipt = bg_receipt + $mb_count, bg_reject = bg_reject - $mb_count"; + else + $sql_sms = "bg_receipt = bg_receipt - $mb_count, bg_reject = bg_reject + $mb_count"; + + sql_query("update {$g5['sms5_book_group_table']} set $sql_sms where bg_no='{$res2['bg_no']}'"); + } + + if ($bk_receipt) $receipt++; + + sql_query("update {$g5['sms5_book_table']} set $field where mb_id='$res[mb_id]'"); + } + } + else if ($res['mb_leave_date'] == '') // 기존에 등록되어 있지 않을 경우 추가 (삭제된 회원이 아닐 경우) + { + if ($bk_receipt == 1) { + $sql_sms = "bg_receipt = bg_receipt + 1"; + $receipt++; + } else { + $sql_sms = "bg_reject = bg_reject + 1"; + } + + sql_query("insert into {$g5['sms5_book_table']} set $field, bg_no=1"); + sql_query("update {$g5['sms5_book_group_table']} set bg_count = bg_count + 1, bg_member = bg_member + 1, $sql_sms where bg_no=1"); + } + + $count++; +} + +sql_query("update {$g5['sms5_config_table']} set cf_datetime='".G5_TIME_YMDHIS."'"); + +$msg = ''; + +$msg .= '

    회원정보를 휴대폰번호 DB로 업데이트 하였습니다.

    '; +$msg .= '
    '; +$msg .= '
    총 회원 수
    '.number_format($count).'명
    '; +$msg .= '
    삭제된 회원
    '.number_format($leave).'명
    '; +$msg .= '
    휴대폰번호 없음
    '.number_format($hp_empty).' 명
    '; +$msg .= '
    휴대폰번호 정상
    '.number_format($hp_yes).' 명 '; +$msg .= '(수신'.number_format($receipt).' 명'; +$msg .= ' / '; +$msg .= '거부'.number_format($hp_yes-$receipt).' 명)
    '; +$msg .= '
    휴대폰번호 오류
    '.number_format($hp_no).' 명
    '; +$msg .= '
    '; +$msg .= '

    프로그램의 실행을 끝마치셔도 좋습니다.

    '; + +if( $mtype == "json" ){ + $json_msg = array(); + $json_msg['datetime'] = G5_TIME_YMDHIS; + $json_msg['res_msg'] = $msg; + die( json_encode($json_msg) ); +} else { + die( $msg ); +} +?> \ No newline at end of file diff --git a/adm/sms_admin/num_book.php b/adm/sms_admin/num_book.php new file mode 100644 index 000000000..8c6e2ee4e --- /dev/null +++ b/adm/sms_admin/num_book.php @@ -0,0 +1,263 @@ + 0) + $sql_korean = korean_index('bk_name', $ap-1); +else { + $sql_korean = ''; + $ap = 0; +} + +if ($no_hp == 'yes') { + set_cookie('cookie_no_hp', 'yes', 60*60*24*365); + $no_hp_checked = 'checked'; +} else if ($no_hp == 'no') { + set_cookie('cookie_no_hp', '', 0); + $no_hp_checked = ''; +} else { + if (get_cookie('cookie_no_hp') == 'yes') + $no_hp_checked = 'checked'; + else + $no_hp_checked = ''; +} + +if ($no_hp_checked == 'checked') + $sql_no_hp = "and bk_hp <> ''"; + +$total_res = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where 1 $sql_group $sql_search $sql_korean $sql_no_hp"); +$total_count = $total_res[cnt]; + +$total_page = (int)($total_count/$page_size) + ($total_count%$page_size==0 ? 0 : 1); +$page_start = $page_size * ( $page - 1 ); + +$vnum = $total_count - (($page-1) * $page_size); + +$res = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where bk_receipt=1 $sql_group $sql_search $sql_korean $sql_no_hp"); +$receipt_count = $res['cnt']; +$reject_count = $total_count - $receipt_count; + +$res = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where mb_id='' $sql_group $sql_search $sql_korean $sql_no_hp"); +$no_member_count = $res['cnt']; +$member_count = $total_count - $no_member_count; + +$no_group = sql_fetch("select * from {$g5['sms5_book_group_table']} where bg_no = 1"); + +$group = array(); +$qry = sql_query("select * from {$g5['sms5_book_group_table']} where bg_no>1 order by bg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); + +include_once(G5_ADMIN_PATH.'/admin.head.php'); +?> + + + +
    + 회원정보 최근 업데이트 + 총 건수 + 회원 + 비회원 + 수신 + 거부 +
    + +
    + + + + + + +
    + +
    + + + onclick="no_hp_click(this.checked)"> + +
    + + + +
    + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    목록
    + + + 번호그룹이름휴대폰수신아이디업데이트관리
    데이터가 없습니다.
    + + + 수신' : '거부'?> + 수정 + 보내기 + 내역 +
    +
    + +
    + + + + + +
    +
    + + + + + \ No newline at end of file diff --git a/adm/sms_admin/num_book_file.php b/adm/sms_admin/num_book_file.php new file mode 100644 index 000000000..602a3b4f2 --- /dev/null +++ b/adm/sms_admin/num_book_file.php @@ -0,0 +1,158 @@ + 1 order by bg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); + +include_once(G5_ADMIN_PATH.'/admin.head.php'); +?> + +

    파일 업로드

    +
    +

    + 엑셀에 저장된 휴대폰번호 목록을 데이터베이스에 저장할 수 있습니다. +

    + +

    + 엑셀에는 이름과 휴대폰번호 두개만 저장해주세요. 첫번째 라인부터 저장됩니다.
    + ※ 휴대폰번호에 하이픈(-)은 포함되어도 되고 포함되지 않아도 됩니다. +

    + +

    + 엑셀파일은 XLS( Excel 97 - 2003 통합 문서 ) 또는 CSV형식만 업로드 할수 있습니다. (xlsx 불가)
    + CSV 저장방법 : 파일 > 다른 이름으로 저장 > 파일형식 : CSV (쉼표로 분리) (*.CSV) +

    + +

    + 이 작업을 실행하기 전에 회원정보업데이트를 먼저 실행해주세요. +

    +
    + +
    +
    + + +
    + +
    + + + + + + + 파일을 업로드 중입니다. 잠시만 기다려주세요. + + +
    +
    + 휴대폰번호를 DB에 저장중 입니다. 잠시만 기다려주세요. +
    +
    +
    + +

    파일 다운로드

    +
    +

    + 저장된 휴대폰번호 목록을 엑셀(xls) 파일로 다운로드 할 수 있습니다.
    + 다운로드 할 휴대폰번호 그룹을 선택해주세요. +

    +
    + +
    +

    + +
    + + +

    + + + + +
    + + + \ No newline at end of file diff --git a/adm/sms_admin/num_book_file_download.php b/adm/sms_admin/num_book_file_download.php new file mode 100644 index 000000000..65c1bdaa1 --- /dev/null +++ b/adm/sms_admin/num_book_file_download.php @@ -0,0 +1,67 @@ +''"; + +$sql = "select count(*) as cnt from {$g5['sms5_book_table']} where 1 $sql_bg $sql_hp order by bk_name"; +$total = sql_fetch($sql); + +if (!$total['cnt']) alert_just('데이터가 없습니다.'); + +$qry = sql_query("select * from {$g5['sms5_book_table']} where 1 $sql_bg $sql_hp order by bk_name"); + +/*================================================================================ +php_writeexcel http://www.bettina-attack.de/jonny/view.php/projects/php_writeexcel/ +=================================================================================*/ + +include_once(G5_LIB_PATH.'/Excel/php_writeexcel/class.writeexcel_workbook.inc.php'); +include_once(G5_LIB_PATH.'/Excel/php_writeexcel/class.writeexcel_worksheet.inc.php'); + +$fname = tempnam(G5_DATA_PATH, "tmp.xls"); +$workbook = new writeexcel_workbook($fname); +$worksheet = $workbook->addworksheet(); + +$num2_format =& $workbook->addformat(array(num_format => '\0#')); + +// Put Excel data +$data = array('이름', '전화번호'); +$data = array_map('iconv_euckr', $data); + +$col = 0; +foreach($data as $cell) { + $worksheet->write(0, $col++, $cell); +} + +for($i=1; $res=sql_fetch_array($qry); $i++) +{ + $res = array_map('iconv_euckr', $res); + + $hp = get_hp($res['bk_hp'], $hyphen); + + if ($no_hp && $res['bk_hp'] != '' && !$hp) continue; + + $worksheet->write($i, 0, $res['bk_name']); + $worksheet->write($i, 1, $hp, $num2_format); +} + +$workbook->close(); + +$filename = "휴대폰번호목록-".date("ymd", time()).".xls"; +if( is_ie() ) $filename = utf2euc($filename); + +header("Content-Type: application/x-msexcel; name=".$filename); +header("Content-Disposition: inline; filename=".$filename); +$fh=fopen($fname, "rb"); +fpassthru($fh); +unlink($fname); + +exit; +?> \ No newline at end of file diff --git a/adm/sms_admin/num_book_file_upload.php b/adm/sms_admin/num_book_file_upload.php new file mode 100644 index 000000000..f4596be41 --- /dev/null +++ b/adm/sms_admin/num_book_file_upload.php @@ -0,0 +1,166 @@ +setOutputEncoding('UTF-8'); + $data->read($file); + $num_rows = $data->sheets[0]['numRows']; + break; + default : + alert_after('xls파일과 csv파일만 허용합니다.'); +} + +$counter = 0; +$success = 0; +$failure = 0; +$inner_overlap = 0; +$overlap = 0; +$arr_hp = array(); +$encode = array('ASCII','UTF-8','EUC-KR'); + +for ($i = 1; $i <= $num_rows; $i++) { + $counter++; + $j = 1; + + switch ($ext) { + case '.csv' : + $name = $csv[$i][0]; + $str_encode = @mb_detect_encoding($name, $encode); + if( $str_encode == "EUC-KR" ){ + $name = iconv_utf8( $name ); + } + $name = addslashes($name); + $hp = addslashes($csv[$i][1]); + break; + case '.xls' : + $name = addslashes($data->sheets[0]['cells'][$i][$j++]); + $str_encode = @mb_detect_encoding($name, $encode); + if( $str_encode == "EUC-KR" ){ + $name = iconv_utf8( $name ); + } + $hp = addslashes(get_hp($data->sheets[0]['cells'][$i][$j++])); + break; + } + if (!(strlen($name)&&$hp)) + { + $failure++; + + } else { + if (in_array($hp, $arr_hp)) + { + $inner_overlap++; + } else { + + array_push($arr_hp, $hp); + + $res = sql_fetch("select * from {$g5['sms5_book_table']} where bk_hp='$hp'"); + if ($res) + { + $overlap++; + } + else if (!$confirm && $hp) + { + sql_query("insert into {$g5['sms5_book_table']} set bg_no='$bg_no', bk_name='$name', bk_hp='$hp', bk_receipt=1, bk_datetime='".G5_TIME_YMDHIS."'"); + sql_query("update {$g5['sms5_book_group_table']} set bg_count = bg_count + 1, bg_nomember = bg_nomember + 1, bg_receipt = bg_receipt + 1 where bg_no='$bg_no'"); + $success++; + } + } + } + if ($inner_overlap > 0) $overlap += $inner_overlap; +} + +unlink($_FILES['csv']['tmp_name']); + +if ($success){ + $sql = "select count(*) as cnt from {$g5['sms5_book_table']} where bg_no='$bg_no'"; + $total = sql_fetch($sql); + sql_query("update {$g5['sms5_book_group_table']} set bg_count = ".$total['cnt']." where bg_no='$bg_no'"); +} + +$result = $counter - $failure - $overlap; + +echo ""; + + +function alert_after($str) { + echo ""; + alert_just($str); +} +?> \ No newline at end of file diff --git a/adm/sms_admin/num_book_move.php b/adm/sms_admin/num_book_move.php new file mode 100644 index 000000000..1a68adece --- /dev/null +++ b/adm/sms_admin/num_book_move.php @@ -0,0 +1,128 @@ + + +
    +

    + +
    + + + + + +
    + + + + + + + + + + + + + + + + +
    할 그룹을 한개 이상 선택하여 주십시오.
    + + + + + 그룹
    + + + + +
    +
    + +
    + + +
    +
    + +
    + + + + diff --git a/adm/sms_admin/num_book_multi_update.php b/adm/sms_admin/num_book_multi_update.php new file mode 100644 index 000000000..172b6c014 --- /dev/null +++ b/adm/sms_admin/num_book_multi_update.php @@ -0,0 +1,63 @@ + \ No newline at end of file diff --git a/adm/sms_admin/num_book_update.php b/adm/sms_admin/num_book_update.php new file mode 100644 index 000000000..edb9dd373 --- /dev/null +++ b/adm/sms_admin/num_book_update.php @@ -0,0 +1,137 @@ +'$bk_no' and bk_hp='$bk_hp'"); + if ($res) + alert('같은 번호가 존재합니다.'); +*/ + $res = sql_fetch("select * from {$g5['sms5_book_table']} where bk_no='$bk_no'"); + if (!$res) + alert('존재하지 않는 데이터 입니다.'); + + if ($bg_no != $res['bg_no']) { + if ($res['mb_id']) $mem = "bg_member"; else $mem = "bg_nomember"; + if ($res['bk_receipt'] == 1) $sms = "bg_receipt"; else $sms = "bg_reject"; + sql_query("update {$g5['sms5_book_group_table']} set bg_count = bg_count - 1, $mem = $mem - 1, $sms = $sms - 1 where bg_no='{$res['bg_no']}'"); + sql_query("update {$g5['sms5_book_group_table']} set bg_count = bg_count + 1, $mem = $mem + 1, $sms = $sms + 1 where bg_no='$bg_no'"); + } + + if ($bk_receipt != $res['bk_receipt']) { + if ($bk_receipt == 1) + sql_query("update {$g5['sms5_book_group_table']} set bg_receipt = bg_receipt + 1, bg_reject = bg_reject - 1 where bg_no='$bg_no'"); + else + sql_query("update {$g5['sms5_book_group_table']} set bg_receipt = bg_receipt - 1, bg_reject = bg_reject + 1 where bg_no='$bg_no'"); + } + + sql_query("update {$g5['sms5_book_table']} set bg_no='$bg_no', bk_name='$bk_name', bk_hp='$bk_hp', bk_receipt='$bk_receipt', bk_datetime='".G5_TIME_YMDHIS."', bk_memo='$bk_memo' where bk_no='$bk_no'"); + if ($res['mb_id']){ //만약에 mb_id가 있다면... + // 휴대폰번호 중복체크 + $sql = " select mb_id from {$g5['member_table']} where mb_id <> '{$res['mb_id']}' and mb_hp = '{$bk_hp}' "; + $mb_hp_exist = sql_fetch($sql); + if ($mb_hp_exist['mb_id']) { //중복된 회원 휴대폰번호가 있다면 + $is_hp_exist = true; + } else { + sql_query("update {$g5['member_table']} set mb_name='$bk_name', mb_hp='$bk_hp', mb_sms='$bk_receipt' where mb_id='{$res['mb_id']}'", false); + } + } + $get_bg_no = $bg_no; + + $go_url = './num_book_write.php?bk_no='.$bk_no.'&w='.$w.'&page='.$page; + if( $is_hp_exist ){ //중복된 회원 휴대폰번호가 있다면 + //alert( "중복된 회원 휴대폰번호가 있어서 회원정보에는 반영되지 않았습니다.", $go_url ); + goto_url($go_url); + } else { + goto_url($go_url); + } + exit; +} +else if ($w=='d') // 삭제 +{ + if (!is_numeric($bk_no)) + alert('고유번호가 없습니다.'); + + $res = sql_fetch("select * from {$g5['sms5_book_table']} where bk_no='$bk_no'"); + if (!$res) + alert('존재하지 않는 데이터 입니다.'); + + if ($res['bk_receipt'] == 1) $bg_sms = 'bg_receipt'; else $bg_sms = 'bg_reject'; + if ($res['mb_id']) $bg_mb = 'bg_member'; else $bg_mb = 'bg_nomember'; + + sql_query("delete from {$g5['sms5_book_table']} where bk_no='$bk_no'"); + sql_query("update {$g5['sms5_book_group_table']} set bg_count = bg_count - 1, $bg_mb = $bg_mb - 1, $bg_sms = $bg_sms - 1 where bg_no = '{$res['bg_no']}'"); + +/* + if (!is_numeric($bk_no)) + alert('고유번호가 없습니다.'); + + $res = sql_fetch("select * from $g5[sms5_book_table] where bk_no='$bk_no'"); + if (!$res) + alert('존재하지 않는 데이터 입니다.'); + + if (!$res[mb_id]) + { + if ($res[receipt] == 1) + $sql_sms = "bg_receipt = bg_receipt - 1"; + else + $sql_sms = "bg_reject = bg_reject - 1"; + + sql_query("delete from $g5[sms5_book_table] where bk_no='$bk_no'"); + sql_query("update $g5[sms5_book_group_table] set bg_count = bg_count - 1, bg_nomember = bg_nomember - 1, $sql_sms where bg_no = '$res[bg_no]'"); + } + else + alert("회원은 삭제할 수 없습니다.\\n\\n회원관리 메뉴에서 삭제한 후\\n\\n회원정보업데이트 메뉴를 실행 해주세요."); +*/ +} +else // 등록 +{ + if (!$bg_no) $bg_no = 1; + + if (!$bk_receipt) $bk_receipt = 0; else $bk_receipt = 1; + + if (!strlen(trim($bk_name))) + alert('이름을 입력해주세요'); + + if ($bk_hp == '') + alert('휴대폰번호만 입력 가능합니다.'); + + $res = sql_fetch("select * from {$g5['sms5_book_table']} where bk_hp='$bk_hp'"); + if ($res) + alert('같은 번호가 존재합니다.'); + + if ($bk_receipt == 1) + $sql_sms = "bg_receipt = bg_receipt + 1"; + else + $sql_sms = "bg_reject = bg_reject + 1"; + + sql_query("insert into {$g5['sms5_book_table']} set bg_no='$bg_no', bk_name='$bk_name', bk_hp='$bk_hp', bk_receipt='$bk_receipt', bk_datetime='".G5_TIME_YMDHIS."', bk_memo='$bk_memo'"); + sql_query("update {$g5['sms5_book_group_table']} set bg_count = bg_count + 1, bg_nomember = bg_nomember + 1, $sql_sms where bg_no = '$bg_no'"); + + $get_bg_no = $bg_no; +} + +$go_url = './num_book.php?page='.$page.'&bg_no='.$get_bg_no.'&ap='.$ap; +goto_url($go_url); +?> \ No newline at end of file diff --git a/adm/sms_admin/num_book_write.php b/adm/sms_admin/num_book_write.php new file mode 100644 index 000000000..9a8777908 --- /dev/null +++ b/adm/sms_admin/num_book_write.php @@ -0,0 +1,216 @@ + '{$write['mb_id']}' and mb_hp <> '' "; + $result = sql_query($sql); + while($tmp = sql_fetch_array($result)){ + $exist_hplist[] = $tmp; + } + $exist_msg_1 = '(수정시 회원정보에 반영되지 않습니다.)'; + $exist_msg_2 = '(수정시 회원정보에 반영됩니다.)'; + $exist_msg = count($exist_hplist) ? $exist_msg_1 : $exist_msg_2; + } + $g5['title'] .= '수정'; +} +else { + $write['bg_no'] = $bg_no; + $g5['title'] .= '추가'; +} + +if (!is_numeric($write['bk_receipt'])) + $write['bk_receipt'] = 1; + +$no_group = sql_fetch("select * from {$g5['sms5_book_group_table']} where bg_no = 1"); + +include_once(G5_ADMIN_PATH."/admin.head.php"); +?> + +
    + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + +
    +
      + +
    • 중복됨
    • + +
    +
    + +
    수신여부 + > + + > + +
    회원아이디 '.$write['mb_id'].'' : '비회원'?>
    업데이트
    + +
    +
    + +
    + + 목록 +
    + +
    + + \ No newline at end of file diff --git a/adm/sms_admin/num_group.php b/adm/sms_admin/num_group.php new file mode 100644 index 000000000..07e5cf79f --- /dev/null +++ b/adm/sms_admin/num_group.php @@ -0,0 +1,186 @@ + 1 order by bg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); + +include_once(G5_ADMIN_PATH.'/admin.head.php'); +?> + + + + +
    + +
    + + + +
    +
    + 건수 : +
    +
    + +
    +

    그룹명순으로 정렬됩니다.

    +
    + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    목록
    + + + 그룹명회원비회원수신거부이동보기
    + + + + 보기 +
    + + + + + + + + + + + 보기 +
    +
    + +
    + + + +
    + +
    + \ No newline at end of file diff --git a/adm/sms_admin/num_group_move.php b/adm/sms_admin/num_group_move.php new file mode 100644 index 000000000..733585c16 --- /dev/null +++ b/adm/sms_admin/num_group_move.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/adm/sms_admin/num_group_update.php b/adm/sms_admin/num_group_update.php new file mode 100644 index 000000000..3f7cc9990 --- /dev/null +++ b/adm/sms_admin/num_group_update.php @@ -0,0 +1,77 @@ +'$bg_no' and bg_name='$bg_name'"); + if ($res) + alert('같은 그룹명이 존재합니다.'); + + sql_query("update {$g5['sms5_book_group_table']} set bg_name='$bg_name' where bg_no='$bg_no'"); + } +} +else if ($w == 'de') // 그룹삭제 +{ + for ($i=0; $i \ No newline at end of file diff --git a/adm/sms_admin/number_move_update.php b/adm/sms_admin/number_move_update.php new file mode 100644 index 000000000..ad24a7010 --- /dev/null +++ b/adm/sms_admin/number_move_update.php @@ -0,0 +1,84 @@ += 1 "); + $bg_reject = (int)$bg_count['cnt'] - (int)$bg_receipt['cnt']; + $bg_member = sql_fetch("select count(*) as cnt from {$g5['sms5_book_table']} where bg_no='$v' and mb_id <> '' "); + $bg_nomember = (int)$bg_count['cnt'] - (int)$bg_member['cnt']; + $sql = "update {$g5['sms5_book_group_table']} set bg_count = {$bg_count['cnt']}, bg_receipt = {$bg_receipt['cnt']}, bg_reject = {$bg_reject}, bg_member = {$bg_member['cnt']}, bg_nomember = {$bg_nomember} where bg_no='$v' "; + sql_query($sql); + } +} + +$msg = '해당 번호를 선택한 그룹으로 '.$act.' 하였습니다.'; +$opener_href = './num_book.php?page='.$page; + +echo << + + +HEREDOC; +?> \ No newline at end of file diff --git a/adm/sms_admin/sms5.sql b/adm/sms_admin/sms5.sql new file mode 100644 index 000000000..ae8bcb2cf --- /dev/null +++ b/adm/sms_admin/sms5.sql @@ -0,0 +1,189 @@ +## 마이에스큐엘 dump 10.11 +## +## +## ###################################################### +## Server version 5.0.92-log + + + + + + + +## +## Table structure for table `{$g5['sms5_book_table']}` +## + +DROP TABLE IF EXISTS `{$g5['sms5_book_table']}`; + + +CREATE TABLE `{$g5['sms5_book_table']}` ( + `bk_no` int(11) NOT NULL auto_increment, + `bg_no` int(11) NOT NULL default '0', + `mb_no` int(11) NOT NULL default '0', + `mb_id` varchar(20) NOT NULL default '', + `bk_name` varchar(255) NOT NULL default '', + `bk_hp` varchar(255) NOT NULL default '', + `bk_receipt` tinyint(4) NOT NULL default '0', + `bk_datetime` datetime NOT NULL default '0000-00-00 00:00:00', + `bk_memo` text NOT NULL, + PRIMARY KEY (`bk_no`), + KEY `bk_name` (`bk_name`), + KEY `bk_hp` (`bk_hp`), + KEY `mb_no` (`mb_no`), + KEY `bg_no` (`bg_no`,`bk_no`), + KEY `mb_id` (`mb_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_book_table']}_group` +## + +DROP TABLE IF EXISTS `{$g5['sms5_book_table']}_group`; + + +CREATE TABLE `{$g5['sms5_book_table']}_group` ( + `bg_no` int(11) NOT NULL auto_increment, + `bg_name` varchar(255) NOT NULL default '', + `bg_count` int(11) NOT NULL default '0', + `bg_member` int(11) NOT NULL default '0', + `bg_nomember` int(11) NOT NULL default '0', + `bg_receipt` int(11) NOT NULL default '0', + `bg_reject` int(11) NOT NULL default '0', + PRIMARY KEY (`bg_no`), + KEY `bg_name` (`bg_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_config_table']}` +## + +DROP TABLE IF EXISTS `{$g5['sms5_config_table']}`; + + +CREATE TABLE `{$g5['sms5_config_table']}` ( + `cf_phone` varchar(255) NOT NULL default '', + `cf_register` varchar(255) NOT NULL default '', + `cf_datetime` datetime NOT NULL default '0000-00-00 00:00:00', + `cf_member` tinyint(4) NOT NULL default '1', + `cf_level` tinyint(4) NOT NULL default '2', + `cf_point` int(11) NOT NULL default '0', + `cf_day_count` int(11) NOT NULL default '0', + `cf_skin` varchar(100) NOT NULL DEFAULT '' +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_form_table']}` +## + +DROP TABLE IF EXISTS `{$g5['sms5_form_table']}`; + + +CREATE TABLE `{$g5['sms5_form_table']}` ( + `fo_no` int(11) NOT NULL auto_increment, + `fg_no` tinyint(4) NOT NULL default '0', + `fg_member` char(1) NOT NULL default '0', + `fo_name` varchar(255) NOT NULL default '', + `fo_content` text NOT NULL, + `fo_datetime` datetime NOT NULL default '0000-00-00 00:00:00', + PRIMARY KEY (`fo_no`), + KEY `fg_no` (`fg_no`,`fo_no`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_form_table']}_group` +## + +DROP TABLE IF EXISTS `{$g5['sms5_form_table']}_group`; + + +CREATE TABLE `{$g5['sms5_form_table']}_group` ( + `fg_no` int(11) NOT NULL auto_increment, + `fg_name` varchar(255) NOT NULL default '', + `fg_count` int(11) NOT NULL default '0', + `fg_member` tinyint(4) NOT NULL, + PRIMARY KEY (`fg_no`), + KEY `fg_name` (`fg_name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_history_table']}` +## + +DROP TABLE IF EXISTS `{$g5['sms5_history_table']}`; + + +CREATE TABLE `{$g5['sms5_history_table']}` ( + `hs_no` int(11) NOT NULL auto_increment, + `wr_no` int(11) NOT NULL default '0', + `wr_renum` int(11) NOT NULL default '0', + `bg_no` int(11) NOT NULL default '0', + `mb_no` int(11) NOT NULL default '0', + `mb_id` varchar(20) NOT NULL default '', + `bk_no` int(11) NOT NULL default '0', + `hs_name` varchar(30) NOT NULL default '', + `hs_hp` varchar(255) NOT NULL default '', + `hs_datetime` datetime NOT NULL default '0000-00-00 00:00:00', + `hs_flag` tinyint(4) NOT NULL default '0', + `hs_code` varchar(255) NOT NULL default '', + `hs_memo` varchar(255) NOT NULL default '', + `hs_log` varchar(255) NOT NULL default '', + PRIMARY KEY (`hs_no`), + KEY `wr_no` (`wr_no`), + KEY `mb_no` (`mb_no`), + KEY `bk_no` (`bk_no`), + KEY `hs_hp` (`hs_hp`), + KEY `hs_code` (`hs_code`), + KEY `bg_no` (`bg_no`), + KEY `mb_id` (`mb_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_write_table']}` +## + +DROP TABLE IF EXISTS `{$g5['sms5_write_table']}`; + + +CREATE TABLE `{$g5['sms5_write_table']}` ( + `wr_no` int(11) NOT NULL default '1', + `wr_renum` int(11) NOT NULL default '0', + `wr_reply` varchar(255) NOT NULL default '', + `wr_message` varchar(255) NOT NULL default '', + `wr_booking` datetime NOT NULL default '0000-00-00 00:00:00', + `wr_total` int(11) NOT NULL default '0', + `wr_re_total` int(11) NOT NULL default '0', + `wr_success` int(11) NOT NULL default '0', + `wr_failure` int(11) NOT NULL default '0', + `wr_datetime` datetime NOT NULL default '0000-00-00 00:00:00', + `wr_memo` text NOT NULL, + KEY `wr_no` (`wr_no`,`wr_renum`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +## +## Table structure for table `{$g5['sms5_member_history_table']}` +## + +DROP TABLE IF EXISTS `{$g5['sms5_member_history_table']}`; + + +CREATE TABLE `{$g5['sms5_member_history_table']}` ( + `mh_no` int(11) NOT NULL auto_increment, + `mb_id` varchar(30) NOT NULL, + `mh_reply` varchar(30) NOT NULL, + `mh_hp` varchar(30) NOT NULL, + `mh_datetime` datetime NOT NULL, + `mh_booking` datetime NOT NULL, + `mh_log` varchar(255) NOT NULL, + `mh_ip` varchar(15) NOT NULL, + PRIMARY KEY (`mh_no`), + KEY `mb_id` (`mb_id`,`mh_datetime`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + diff --git a/adm/sms_admin/sms_ing.php b/adm/sms_admin/sms_ing.php new file mode 100644 index 000000000..3c8fdbabb --- /dev/null +++ b/adm/sms_admin/sms_ing.php @@ -0,0 +1,22 @@ + + + + + + +SMS 전송 중 + + + + +

    SMS를 전송 중입니다.
    잠시만 기다려주십시오.
    + + + diff --git a/adm/sms_admin/sms_write.php b/adm/sms_admin/sms_write.php new file mode 100644 index 000000000..871402560 --- /dev/null +++ b/adm/sms_admin/sms_write.php @@ -0,0 +1,779 @@ + + +
    + 회원정보 최근 업데이트 : +
    + + +
    + +
    +

    이모티콘 목록

    + +
    + +
    +
    + + +

    보낼내용

    +
    + + + + +
    0 / 80 byte
    + + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + +
    + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + +
    + {이름} : 받는사람 이름 +
    + +
    + + +
    + +
    +

    받는사람

    + + + + + +
    + +
    + + +
    +
    +
    + +
    +

    예약전송

    + +
    + + +
    + + + +
    + + + +
    + + + + +
    + +
    + + +
    +
    +
    + +
    +

    휴대폰번호 목록

    +
    + 그룹 + 개인 + 권한 +
    + +
    + +
    SMS 수신을 허용한 회원님만 출력됩니다.
    +
    +
    + + + +\n"; + echo "var hp_list = document.getElementById('hp_list');\n"; + //echo "add(\"$row[wr_message]\");\n"; + $wr_message = str_replace('"', '\"', $row['wr_message']); + $wr_message = str_replace("\r\n", "\\n", $wr_message); + echo "add(\"$wr_message\");\n"; + echo "document.getElementById('wr_reply').value = '{$row['wr_reply']}';\n"; + + // 회원목록 + $sql = " select * from {$g5['sms5_history_table']} where wr_no = '$wr_no' and bk_no > 0 "; + $qry = sql_query($sql); + $tot = mysql_num_rows($qry); + + if ($tot > 0) { + + $str = '재전송그룹 ('.number_format($tot).'명)'; + $val = 'p,'; + + while ($row = sql_fetch_array($qry)) + { + $val .= $row['bk_no'].','; + } + + echo "hp_list.options[hp_list.length] = new Option('$str', '$val');\n"; + } + + // 비회원 목록 + $sql = " select * from {$g5['sms5_history_table']} where wr_no = '$wr_no' and bk_no = 0 "; + $qry = sql_query($sql); + $tot = mysql_num_rows($qry); + + if ($tot > 0) + { + while ($row = sql_fetch_array($qry)) + { + $str = "{$row['hs_name']} ({$row['hs_hp']})"; + $val = "h,{$row['hs_name']}:{$row['hs_hp']}"; + echo "hp_list.options[hp_list.length] = new Option('$str', '$val');\n"; + } + } + echo "\n"; +} +?> + + + + +
    +

    SMS 문자전송 서비스를 사용할 수 없습니다.

    +
    +

    + SMS 를 사용하지 않고 있기 때문에, 문자 전송을 할 수 없습니다.
    + SMS 사용 설정은 환경설정 > 기본환경설정 > SMS설정 에서 SMS 사용을 아이코드로 변경해 주셔야 사용하실수 있습니다. +

    +
    +
    + + + + \ No newline at end of file diff --git a/adm/sms_admin/sms_write_form.php b/adm/sms_admin/sms_write_form.php new file mode 100644 index 000000000..781b3fbb8 --- /dev/null +++ b/adm/sms_admin/sms_write_form.php @@ -0,0 +1,132 @@ + + +
    + + +
    + +
      +
    + +
    + + + + + + + + +
    + + + + + \ No newline at end of file diff --git a/adm/sms_admin/sms_write_overlap_check.php b/adm/sms_admin/sms_write_overlap_check.php new file mode 100644 index 000000000..100ed6ba3 --- /dev/null +++ b/adm/sms_admin/sms_write_overlap_check.php @@ -0,0 +1,69 @@ + $hp, 'bk_name' => $name)); + array_push($hps, $hp); + break; + + case 'p': // 개인 선택 + $res = sql_fetch("select * from {$g5['sms5_book_table']} where bk_no='$item[$i]'"); + $res['bk_hp'] = get_hp($res['bk_hp'], 0); + if (array_overlap($hps, $res['bk_hp'])) { + $overlap ++; + continue; + } + array_push($list, $res); + array_push($hps, $res['bk_hp']); + break; + } + } +} + +if ($overlap) + die("중복되는 휴대폰번호가 $overlap 건 있습니다. "); +else + die("중복되는 휴대폰번호가 없습니다. "); +?> \ No newline at end of file diff --git a/adm/sms_admin/sms_write_send.php b/adm/sms_admin/sms_write_send.php new file mode 100644 index 000000000..348e44141 --- /dev/null +++ b/adm/sms_admin/sms_write_send.php @@ -0,0 +1,231 @@ + $hp, 'bk_name' => $name, 'mb_id' => $mb_id, 'bg_no' => $bg_no, 'bk_no' => $bk_no)); + array_push($hps, $hp); + } + break; + + case 'h': // 권한(mb_leve) 선택 + + $item[$i] = explode(':', $item[$i]); + $hp = get_hp($item[$i][1], 0); + $name = $item[$i][0]; + + if ($wr_overlap && array_overlap($hps, $hp)) { + $overlap++; + array_push( $duplicate_data['hp'], $row['bk_hp'] ); + continue; + } + + array_push($list, array('bk_hp' => $hp, 'bk_name' => $name)); + array_push($hps, $hp); + break; + + case 'p': // 개인 선택 + + $row = sql_fetch("select * from {$g5['sms5_book_table']} where bk_no='$item[$i]'"); + $row['bk_hp'] = get_hp($row['bk_hp'], 0); + + if ($wr_overlap && array_overlap($hps, $row['bk_hp'])) { + $overlap++; + array_push( $duplicate_data['hp'], $row['bk_hp'] ); + continue; + } + array_push($list, $row); + array_push($hps, $row['bk_hp']); + break; + } + } +} + +if( count($duplicate_data['hp']) ){ //중복된 번호가 있다면 + $duplicate_data['total'] = $overlap; + $str_serialize = serialize($duplicate_data); +} + +$wr_total = count($list); + +// 예약전송 +if ($wr_by && $wr_bm && $wr_bd && $wr_bh && $wr_bi) { + $wr_booking = "$wr_by-$wr_bm-$wr_bd $wr_bh:$wr_bi"; + $booking = $wr_by.$wr_bm.$wr_bd.$wr_bh.$wr_bi; +} else { + $wr_booking = ''; + $booking = ''; +} + +if ($config['cf_sms_use'] != 'icode') { + alert('기본환경설정에서 icode sms 사용이 비활성화 되어 있습니다.'); +} + +include_once(G5_ADMIN_PATH.'/admin.head.php'); + +$SMS = new SMS5; +$SMS->SMS_con($config['cf_icode_server_ip'], $config['cf_icode_id'], $config['cf_icode_pw'], $config['cf_icode_server_port']); + +$reply = str_replace('-', '', trim($wr_reply)); + +$result = $SMS->Add($list, $reply, '', '', $wr_message, $booking, $wr_total); + +if ($result) +{ + $result = $SMS->Send(); + + if ($result) //SMS 서버에 접속했습니다. + { + $row = sql_fetch("select max(wr_no) as wr_no from {$g5['sms5_write_table']}"); + if ($row) + $wr_no = $row['wr_no'] + 1; + else + $wr_no = 1; + + sql_query("insert into {$g5['sms5_write_table']} set wr_no='$wr_no', wr_renum=0, wr_reply='$wr_reply', wr_message='$wr_message', wr_booking='$wr_booking', wr_total='$wr_total', wr_datetime='".G5_TIME_YMDHIS."'"); + + $wr_success = 0; + $wr_failure = 0; + $count = 0; + + foreach ($SMS->Result as $result) + { + list($phone, $code) = explode(":", $result); + + if (substr($code,0,5) == "Error") + { + $hs_code = substr($code,6,2); + + switch ($hs_code) { + case '02': // "02:형식오류" + $hs_memo = "형식이 잘못되어 전송이 실패하였습니다."; + break; + case '23': // "23:인증실패,데이터오류,전송날짜오류" + $hs_memo = "데이터를 다시 확인해 주시기바랍니다."; + break; + case '97': // "97:잔여코인부족" + $hs_memo = "잔여코인이 부족합니다."; + break; + case '98': // "98:사용기간만료" + $hs_memo = "사용기간이 만료되었습니다."; + break; + case '99': // "99:인증실패" + $hs_memo = "인증 받지 못하였습니다. 계정을 다시 확인해 주세요."; + break; + default: // "미 확인 오류" + $hs_memo = "알 수 없는 오류로 전송이 실패하었습니다."; + break; + } + $wr_failure++; + $hs_flag = 0; + } + else + { + $hs_code = $code; + $hs_memo = get_hp($phone, 1)."로 전송했습니다."; + $wr_success++; + $hs_flag = 1; + } + + $row = array_shift($list); + $row['bk_hp'] = get_hp($row['bk_hp'], 1); + + $log = array_shift($SMS->Log); + sql_query("insert into {$g5['sms5_history_table']} set wr_no='$wr_no', wr_renum=0, bg_no='{$row['bg_no']}', mb_id='{$row['mb_id']}', bk_no='{$row['bk_no']}', hs_name='{$row['bk_name']}', hs_hp='{$row['bk_hp']}', hs_datetime='".G5_TIME_YMDHIS."', hs_flag='$hs_flag', hs_code='$hs_code', hs_memo='$hs_memo', hs_log='".stripslashes($log)."'"); + } + $SMS->Init(); // 보관하고 있던 결과값을 지웁니다. + + sql_query("update {$g5['sms5_write_table']} set wr_success='$wr_success', wr_failure='$wr_failure', wr_memo='$str_serialize' where wr_no='$wr_no' and wr_renum=0"); + } + else win_close_alert("에러: SMS 서버와 통신이 불안정합니다."); +} +else win_close_alert("에러: SMS 데이터 입력도중 에러가 발생하였습니다."); + +function win_close_alert($msg) { + + $html = ""; + + echo $html; + exit; +} + +?> + + \ No newline at end of file diff --git a/extend/sms5.extend.php b/extend/sms5.extend.php new file mode 100644 index 000000000..8b47fc273 --- /dev/null +++ b/extend/sms5.extend.php @@ -0,0 +1,57 @@ + \ No newline at end of file diff --git a/js/common.js b/js/common.js index 5848854a0..00cfccb59 100644 --- a/js/common.js +++ b/js/common.js @@ -368,6 +368,14 @@ var win_zip = function(href) { new_win.focus(); } +/** + * sms5 창 + **/ +var win_sms5 = function(href) { + var new_win = window.open(href, 'win_zip', 'width=474, height=560, scrollbars=1'); + new_win.focus(); +} + /** * 새로운 비밀번호 분실 창 : 101123 **/ @@ -452,6 +460,11 @@ $(function(){ return false; }); + $(".win_sms5").click(function() { + win_sms5(this.href); + return false; + }); + /* $(".win_poll").click(function() { win_poll(this.href); diff --git a/js/jquery.sms_paging.js b/js/jquery.sms_paging.js new file mode 100644 index 000000000..a35f3b623 --- /dev/null +++ b/js/jquery.sms_paging.js @@ -0,0 +1,93 @@ +/************************************************************************************** + * jQuery Paging 0.1.7 + * by composite (ukjinplant@msn.com) + * http://composite.tistory.com + * This project licensed under a MIT License. + **************************************************************************************/; +(function($){ + //default properties. + var a=/a/i,defs={ + liitem:'li', item:'a',next:'[>{5}]',prev:'[{4}<]',format:'[{0}]', + itemClass:'',appendhtml:'',sideClass:'paging-side',prevClass:'paging-side', + itemCurrent:'active',length:10,max:1,current:1,append:false + ,href:'#{0}',event:true,first:'[1<<]',last:'[>>{6}]' + },InStr=function(strSearch, charSearchFor) { + return strSearch.indexOf(charSearchFor); + },format=function(str){ + var arg=arguments; + return str.replace(/\{(\d+)\}/g,function(m,d){ + if(+d<0) return m; + else return arg[+d+1]||""; + }); + },item,make=function(op,page,cls,str){ + var is_current = false; + if( InStr( cls , op.itemCurrent) > -1 ){ + item=document.createElement("strong"); + is_current = true; + } else { + item=document.createElement(op.item); + } + item.className=cls; + item.innerHTML=format(str,page,op.length,op.start,op.end,op.start-1,op.end+1,op.max); + if(a.test(op.item)) item.href=format(op.href,page); + if(op.event){ + $(item).bind('click',function(e){ + var fired=true; + if($.isFunction(op.onclick)) fired=op.onclick.call(item,e,page,op); + if(fired==undefined||fired) + op.origin.paging($.extend({},op,{current:page})); + return fired; + }); + /* + $liitem= $(document.createElement(op.liitem)); + $liitem.addClass(cls); + $liitem.append($(item)); + */ + if(op.appendhtml){ + $(item).append(op.appendhtml); + } + $(item).appendTo(op.origin); + if( is_current ){ + $(item).prepend('열린'); + } else { + (op.origin).append('\n'); + } + //bind event for each elements. + var ev='on'; + switch(str){ + case op.prev:ev+='prev';break; + case op.next:ev+='next';break; + case op.first:ev+='first';break; + case op.last:ev+='last';break; + default:ev+='item';break; + } + if($.isFunction(op[ev])) op[ev].call(item,page,op); + } + return item; + }; + + $.fn.paging=function(op){ + op=$.extend({origin:this},defs,op||{});this.html(''); + if(op.max<1) op.max=1; if(op.current<1) op.current=1; + op.start=Math.floor((op.current-1)/op.length)*op.length+1; + op.end=op.start-1+op.length; + if(op.end>op.max) op.end=op.max; + if(!op.append) this.empty(); + //prev button + if(op.current>op.length){ + //if(op.first!==false) make(op,1,op.sideClass,op.first); + make(op,op.start-1,op.prevClass,op.prev); + } + //pages button + for(var i=op.start;i<=op.end;i++){ + make(op,i,op.itemClass+(i==op.current?' '+op.itemCurrent:''),op.format); + } + //next button + if(op.current<=Math.floor(op.max/op.length)*op.length){ + if(op.max > op.length && op.max > op.end ){ make(op,op.end+1,op.sideClass,op.next); } + //if(op.last!==false) make(op,op.max,op.sideClass,op.last); + } + + //last button + }; +})(jQuery); \ No newline at end of file diff --git a/lib/Excel/itemexcel.xls b/lib/Excel/itemexcel.xls new file mode 100644 index 0000000000000000000000000000000000000000..4da8cc5fb510321b80255db92bc06e407e154234 GIT binary patch literal 33792 zcmeG^30M@z(!I+93y2(Yi?9L;C^x7aDg^PyBZ|Kl2CoH)5`9Y4B!ZaWi;2dk7>}S) ziTX$5Mp5y=pd1mwCn_F@*HbiVG#WLEq6Xc+YG!A5c6Mf6^1t_g@6X$Yo|&qyuIjF? zu0E!BYERiU6zp=lPB@__F(I#&7Q~E&7r}EJ1KScvS1J)??y>L;C}a44qyY;lG^Cj+ zNeeq+ek?~s%%H)q6EP{2l*5&f8xWJp41AsyF8jX=@l+Wmh_DQ0V0b-5W<&%y6S5Aj zcElA+MC_?+8|u1~qAj7WJs_n)G=}^>~`kp_k1-hXZCMi8i+|HtK8=qUQuASI|!f%-b= z$xBFgJ*i^itS41OEcD8a(3@APH*ZhDyi-IX5-FHBKyRu!o0D9 zr7D8F{5I(!=}b&X8zS-|+hktEn*>1gCLtsSI&L80Vm%~Xz^S|Nk&h+Ah@4C&;{-Bx zBj(WWbmRnqEFo!hnup{qD7=SoR{fG0CUVjV1c_4AwdzDj?Yv$TCpsTq z;|N|oBmu;q_!5#5*h%Ic;O!lf5*U-rdPclgpci0M0^?EwJtXgg&Lf2jMwg!!qiGWD z;<0Ivl5RGnp$i!cwn1~iD^t)2(wPLa3K|rmRaP2Q6*o3kQ(!mfm5$)pedy}>$0Yly zo|FAOB%WX%ZtT>t!{n34X_3gl5ZbCqP!5X+fXJo;Fp+U+v^i0P4a5O#n~U%O zOon)zeNgyDCc%iOHSx#vfO$oPrDrjR54sr)k`3DWSL44Iz<(!z-ywisD}diAfL93M zzZSqR7QhRYM~MF(ijO&{gt9}m9@Y!s>GJ5()8)~{Zx-OQO8{RafIlpN|6BlHEP!um zdG=C#q>O$z{nUcNe`#m8+G@2Gk*uQVEg8I8pB%edXJ_YR6caQGC>TpzYj_!E?%K&*0CIq422Y(?%N))u-h;Xz((4gko^;9RA?@IeMe} zuM>lZa?<*t+sh0FI6gg?!HCR0sTgcGN6NKQ3muJEy{o{r9~OgceE%2Iu*Sc-s1EJ^fo5K z{Xs%?6nQF|9J5x)l5&}#YchZdnxQZg^kW7vLDLjwf=CTOn0OwdCbzywWLmHVNCS5j+Y@! zVD$oI!X41tFedt2$Hy=x`dg=iVNCS5PDjI-=x-fg!S zmU9Lwe;_lb1}e<(GK-S|I~yiEe*9R;G^e==H-G+oC9_W=W#O285(^d%SWv-|2d6vm zbpl%2E28Xm#7w&uOrexjC)I_@l5%U-8M4wf1^{J?CK7>=fu#z$K>H}~ty9-RS89-2D(Vy4?G07|8(J*g0if%%kkghe zThvk+-NnYn>gWz}T-_lK8Dn5{$HwW_A_OS)>WszYbOy%Al{q4H zwX>unI6W#Hm`H@DeyArjNVbDS#te?>QMv{l0l73?H8ys(b}On4y5=i&>6k|O=XF)C zj=MmiG>w9lV4Idmw{Ld^63vW@XLwdk<*D+4*EZp9CLZ<&1?7hB1u?h*u7}G?g5wJoVCp zC;W08`Q>!wkrT!%hxUY2`&h_RU){aPFUJYxL>Odmo*;*hQe7WOP2z!?oBVQ|`Q`B0 zn>Vix0=KYH%s{Cxbn;4v$xK?a@h7(lUP%BiC>Nz zzZ^b$3+9!>wzrzZ{a5es%W2CmhtJ-^dF8O}ttPR#u83cbJId*5kiE%34j-k)-onCH z?y0BcpmT$u_HXU@zYD~D}wVc|#W*YeBp$7;FEN4>%lzf zJKB82mcR#72xx$*hZ6QBm}29ef~MFs#1xx`m}1k=3bBL)XGq;bxtB zs9|)7>mD<8)6)>6LmFaqNJFT@kQ@`xp_{gNQxj0jyo%&=SS_Rq%`pQiFAkL%NJATW!va0pmu58P$s6*ot2s z&8pN{gqXNOaWMtA94PIu7aFJ?b66&$H>E9%foQ%F$ai5?hXcBu&jI*i3cCfAu=l`} z7B?vV*PX8m%6G5c2A!s}ww_FEKt850R%8x)37-A4D4C)aVr!V0fE6KyjNsy|2PeNR zSP_%TXqnAJj9O;2VG3HdCfzg~acJRkv{Q4WhZaiPtWv2sDr3Jb#WfOgjMQ+%-ooW* zujWV(E&?3sS(cdM7^UHe!%Hxy)@{@rad@F!u2!wlCq$#x7gr}j-{ZPGH_$q!uzdpV zRWPlN8yaet7lVA(o#3+`N8p1gZ0ljWZtR3w)`JL*52PV>|HX5xv|FdsuxED9k?{0i z35ec%I_W&!BPFCqjyX^NHHV&sRNDO)x=IcKYK}3A2=D}ta7?XIb|+gZ9YvP1JmE5s zYzKcypsa~x8XWi>PBO^{)YBm9_-8tKpJafm7)sK~Y%(2EH57?}OCwVN7YZgmUWD67 z3!&;(xG4rsc=!~#JTnwA#ba2QFvRpvW?{nfr7k3&RW7>B^w#7iIF!DLm4^A2fN2eb zzjBcjA|Z8=V$w_wnxk#v7Bl!4eD7Uew&+~asVha)NCtg|3E;>uIrq@0;xKxc<4+71 zN<_vB4+^k#ubxq@C=*DmJz7{+c1$3#)!}vfbxR9f153O0;M_gje5RUu%BmdChw7A< zL!et*4uNisIRp`J646D6K-Cx_JD!)|n9HpLtF8Ew&6(Oel+3F_z4H;NcwDKlJ4O`S z6ape_&)zKNOIDe~{j+nc4xHiUHKXNY-epypRY?cuRdGpF(iK$=3YUsw`vsCE)k)_g ze;6f{+p#*}fO~~ISToEXS>~u>o>}$$hq7b34Xl-{+q_!Ix{a%qtlP9&$$||_7p<#M zyVJ?t^e4lCX?^vM(n(gCC8HKN3C_uXnQk{cG|jky0C+|##D0&@bT7uAb0pNcRNxH4 z66V+%`Y(6Oh~2TLB`!r}$7EzcWkTh=N*S3_M^iDJYAcbhx%}U_tX)fqXGr;@O^fWb5Y}u_T7hPeRdNO$JOo+kY zhcm%jXF$G$$~F5@-XTTu0j!aXNRQ4dzFAZRCD)}7ru_aSNYU^JBJaXaeA0l|Jc>^t z)CETeA*vcxt>`jlLLTg)%?i3K8f~(*FC)JdVJUHbe)^1zd|&2XomuBzj+*RO>8Inc zRffo@oZHr_KDzZF<0EvvjLKf{sOn+x`AJ<_;4M93+@*$eo=H z;WtP^tL;bGB_j6YExWKQ{!Oq(lWbh#>Fjv*z-VDQF6ce~)VZzJ4` zLkMXW5wZtfpgx1?#des^K7e_w9Ci@4nh`Qd>I4x8Rl!Mh7n4;mYgolfiN7TxYXP^G zg#*T93E-BnaL51;7Scb3v(@@3oR!!Clc|7n9g~rSM8V9)8Yh=fZZY&D8|v8(t_0rC zNJ;ay-_8u0I5ake!s7~_Q0s3(KBh1^K*NL%Fi|9AfM@}vluJ;SBg9XrJh{}hBT%PO z;dZPq>)B7uv<5l3MYs9!R$>&hLBYFONV|ehaN5mk0FCC`YvEHXf%D{DHNX4 zU>Tml6i9U!Nr)NzT*;%CGRGVzxA|5P(#cR`6VWI!)||e+1>F950ow)x>I)oQ03u+k_KSSZAtZ? zMgjNT2+-7l--za)WDxk1M}d7XeLCD z9|@Uz>DRAM3~_(orE0^)k)O*Pwu`sKO;wz8`*C+kK$pEig$p|luR?aW6TKQkY4HL-i+!`ix|M~*B`3*PtHuD>oj(DBj~pBqixp42?JAJW$8LdNDN zucocfO3i-qdhJ)InrElECTxD%d*J7dT^1EhZR)!yd{IQjl37EK``X29K38g2)BMw# zdfCBE)q_HpzkPgks>AZdn_3lnhC1%-?z!i^7`u_7gFPQRFZ$b0Ctv2@wed;Ft9Csy zwBn-K+0vW`xhIwO(L21~Da+Y6J>m6>UuXa2^+FrP2><4ucSqkmzHn;bCD(CB9Im~i z+&n>fePx}c`|^*LRx~+1d;4(To41#*EPZ=?V1BQkQ%;*yG}d@~MQ3k%kTmdkz>tXl z@@wkrBm3^Flb0IyjqbAknsVWQr)e28)^>}za;c%`qG>DUdXJAev1@7gweOxi|9tLA z#~*6WylD6}=}6SKULOt~9X)b*_5k0PCi3~|FI*IJS}mVCQ*z;fYrXZ(^+!h6*QI^f zJoViTXLm38;%>yx>B`?nCI)Q!XmI_kqcc~%^1ZS2R=`K@p$iu6a5!`K?C9m5yN`$O zx_|xXx3z&oR#)8^dG)}mFPne)HTP!dc5!lZ!Si1#KRVfV;+Veoev8_7%Ja$kClx;g zPaJdp8)r+4$n1ZlzbuN~koik>a>Dw!*J0NigVUGZee_^+c!pWA)eGC15vjpjNA8SG z-Fr2okK;G{vJ;k0i26rW@53LBoqWwAZTY~I#gqSX>;6hn|Lc~s9Y09wGg%b8e8tSS zu2$R|6y@t2da{3Uz|MwOJwE6gR4`2>AH8H&dGhF6xh0+UxI};aB6+#5Jbr!U@pdy? z)juE5Yv$CyO0RzV+u#2_%jLuK*&BLa{LAk-cUPXB>2dFUk9#xv^t-SoFVx-o=R)Pr zPpjAU>RRPvVe(UCQ}CqH($t4p_FZdy51s1nob-FeSErnx^l{s?_KQ^+S8{y!`hL1^ zMS9@4BO~r8YGg}y4~y@YJ0ec}b;x3uNn3K4`EAch%8XyN(msBoLTrC0*ZphTVK=C! zsOSZQWtktJvS{u}J=9zLq;QB{P;24g`{*5?ujB4WcDCs_+CNiV(5Cy~H50egTI3FH z$oh2Iit&!i;(SXId=c>|YQo3BJCEY{^TokZe{1T|_39j1uC3SYsJ9=4JCxW=3_H-| zV}JORt9A4jzt4zlDwy7s{@$4@iWk}bJ$9A{Y#Q-o@QI9k&)SlKPd>@_Jkwlwd-jQT z`7vD{p1-o?gTF=2Z@T#X<#U@Z7uAn6Z$kUQ{U@)&$}D|)m(DFn zSyXW8_Y1M+FXf9I`(4Ox@3`&R`sX_rWV^~u?bm+gcR6@+V9a;!51-ziS=#Di_OU|o zXIY8&f7zH)Jti8^eb$z~$}10z@cVSK+x5n$Q?E(yukfF3vwEe?)H&&$vX35gMgdQP zUM?J5kkaqKg>iFA3s1zCE-O4UBCWL6ztPGf_@j64J7-^uzvF!4%(%WZu3L{TO;(o! za+IE%!ks&^3x?luwomog_DRZx)Pj^d&J*Vr^~t`T z=Jj%6cKhsS^;d#3Enmf#+J0K~?z0z*GMo^7t1;tPa4-3sO~d6`CJW|lA9OAGbjXkQ z+!jr!wZV;6q21ry6vp@SR_uI?I=l>X2y7EsqpcpG;)PPX~MhzG>VAOz714a!PHDJ_$Q3FN| z7&TzjfKdZR4Hz{*y@M)%Uu{^xdv_7_Cx&-hqr~A0FW~c=Vmxj^;CVRpS~*T+L*ek}q>HJx!C@o1(I3GY%kd+qHN(-{qf?8`q4qA{CKv-tFEG_`yZ~1Ac z9YCE_5EdH0FGQ!IMr}a53?v2!DKHW1)dS-}23U=lqQS2@H3> z6x;2~rKGd(o{MlYkF0@+4ja?sAflV^2N64J5=3;8;~}Cuo(mBjaV|u3kjo&Vq`lxa z(7j;ZSuhN9pRp4UN}n`y`mE`xnX=wy${63r<3TMv$%E@`T<7Bw8joG! zx*XT;xQ@r;RCtIA*ZO#13g7wTdY}1x4X*idU%&+-9!A0=NO zd?g4zOh~6k!aWLNG{hK)-68gX*b^e=e+3cm|H)w(eBcp3@hBs`$$N0037>*&NpK>j z)UhnoA=;5lidRC)s4MA1-?Xo&a|SaKq|YQ%;bUW1j;RdV;JZB$tL0-E49$2Ejv`?& zg^yd}=PonggJ)Rx{-A|as!WKTk;AHH-&cOJNzk_3=2i7JDf RYJrxOC-M)}zZ*;b{{W355;Xt- literal 0 HcmV?d00001 diff --git a/lib/Excel/oleread.inc b/lib/Excel/oleread.inc new file mode 100644 index 000000000..c7241aa86 --- /dev/null +++ b/lib/Excel/oleread.inc @@ -0,0 +1,271 @@ +=4294967294) + { + $value=-2; + } + return $value; +} + + +class OLERead { + var $data = ''; + + + function OLERead(){ + + + } + + function read($sFileName){ + + // check if file exist and is readable (Darko Miljanovic) + if(!is_readable($sFileName)) { + $this->error = 1; + return false; + } + + $this->data = @file_get_contents($sFileName); + if (!$this->data) { + $this->error = 1; + return false; + } + //echo IDENTIFIER_OLE; + //echo 'start'; + if (substr($this->data, 0, 8) != IDENTIFIER_OLE) { + $this->error = 1; + return false; + } + $this->numBigBlockDepotBlocks = GetInt4d($this->data, NUM_BIG_BLOCK_DEPOT_BLOCKS_POS); + $this->sbdStartBlock = GetInt4d($this->data, SMALL_BLOCK_DEPOT_BLOCK_POS); + $this->rootStartBlock = GetInt4d($this->data, ROOT_START_BLOCK_POS); + $this->extensionBlock = GetInt4d($this->data, EXTENSION_BLOCK_POS); + $this->numExtensionBlocks = GetInt4d($this->data, NUM_EXTENSION_BLOCK_POS); + + /* + echo $this->numBigBlockDepotBlocks." "; + echo $this->sbdStartBlock." "; + echo $this->rootStartBlock." "; + echo $this->extensionBlock." "; + echo $this->numExtensionBlocks." "; + */ + //echo "sbdStartBlock = $this->sbdStartBlock\n"; + $bigBlockDepotBlocks = array(); + $pos = BIG_BLOCK_DEPOT_BLOCKS_POS; + // echo "pos = $pos"; + $bbdBlocks = $this->numBigBlockDepotBlocks; + + if ($this->numExtensionBlocks != 0) { + $bbdBlocks = (BIG_BLOCK_SIZE - BIG_BLOCK_DEPOT_BLOCKS_POS)/4; + } + + for ($i = 0; $i < $bbdBlocks; $i++) { + $bigBlockDepotBlocks[$i] = GetInt4d($this->data, $pos); + $pos += 4; + } + + + for ($j = 0; $j < $this->numExtensionBlocks; $j++) { + $pos = ($this->extensionBlock + 1) * BIG_BLOCK_SIZE; + $blocksToRead = min($this->numBigBlockDepotBlocks - $bbdBlocks, BIG_BLOCK_SIZE / 4 - 1); + + for ($i = $bbdBlocks; $i < $bbdBlocks + $blocksToRead; $i++) { + $bigBlockDepotBlocks[$i] = GetInt4d($this->data, $pos); + $pos += 4; + } + + $bbdBlocks += $blocksToRead; + if ($bbdBlocks < $this->numBigBlockDepotBlocks) { + $this->extensionBlock = GetInt4d($this->data, $pos); + } + } + + // var_dump($bigBlockDepotBlocks); + + // readBigBlockDepot + $pos = 0; + $index = 0; + $this->bigBlockChain = array(); + + for ($i = 0; $i < $this->numBigBlockDepotBlocks; $i++) { + $pos = ($bigBlockDepotBlocks[$i] + 1) * BIG_BLOCK_SIZE; + //echo "pos = $pos"; + for ($j = 0 ; $j < BIG_BLOCK_SIZE / 4; $j++) { + $this->bigBlockChain[$index] = GetInt4d($this->data, $pos); + $pos += 4 ; + $index++; + } + } + + //var_dump($this->bigBlockChain); + //echo '=====2'; + // readSmallBlockDepot(); + $pos = 0; + $index = 0; + $sbdBlock = $this->sbdStartBlock; + $this->smallBlockChain = array(); + + while ($sbdBlock != -2) { + + $pos = ($sbdBlock + 1) * BIG_BLOCK_SIZE; + + for ($j = 0; $j < BIG_BLOCK_SIZE / 4; $j++) { + $this->smallBlockChain[$index] = GetInt4d($this->data, $pos); + $pos += 4; + $index++; + } + + $sbdBlock = $this->bigBlockChain[$sbdBlock]; + } + + + // readData(rootStartBlock) + $block = $this->rootStartBlock; + $pos = 0; + $this->entry = $this->__readData($block); + + /* + while ($block != -2) { + $pos = ($block + 1) * BIG_BLOCK_SIZE; + $this->entry = $this->entry.substr($this->data, $pos, BIG_BLOCK_SIZE); + $block = $this->bigBlockChain[$block]; + } + */ + //echo '==='.$this->entry."==="; + $this->__readPropertySets(); + + } + + function __readData($bl) { + $block = $bl; + $pos = 0; + $data = ''; + + while ($block != -2) { + $pos = ($block + 1) * BIG_BLOCK_SIZE; + $data = $data.substr($this->data, $pos, BIG_BLOCK_SIZE); + //echo "pos = $pos data=$data\n"; + $block = $this->bigBlockChain[$block]; + } + return $data; + } + + function __readPropertySets(){ + $offset = 0; + //var_dump($this->entry); + while ($offset < strlen($this->entry)) { + $d = substr($this->entry, $offset, PROPERTY_STORAGE_BLOCK_SIZE); + + $nameSize = ord($d[SIZE_OF_NAME_POS]) | (ord($d[SIZE_OF_NAME_POS+1]) << 8); + + $type = ord($d[TYPE_POS]); + //$maxBlock = strlen($d) / BIG_BLOCK_SIZE - 1; + + $startBlock = GetInt4d($d, START_BLOCK_POS); + $size = GetInt4d($d, SIZE_POS); + + $name = ''; + for ($i = 0; $i < $nameSize ; $i++) { + $name .= $d[$i]; + } + + $name = str_replace("\x00", "", $name); + + $this->props[] = array ( + 'name' => $name, + 'type' => $type, + 'startBlock' => $startBlock, + 'size' => $size); + + if (($name == "Workbook") || ($name == "Book")) { + $this->wrkbook = count($this->props) - 1; + } + + if ($name == "Root Entry") { + $this->rootentry = count($this->props) - 1; + } + + //echo "name ==$name=\n"; + + + $offset += PROPERTY_STORAGE_BLOCK_SIZE; + } + + } + + + function getWorkBook(){ + if ($this->props[$this->wrkbook]['size'] < SMALL_BLOCK_THRESHOLD){ +// getSmallBlockStream(PropertyStorage ps) + + $rootdata = $this->__readData($this->props[$this->rootentry]['startBlock']); + + $streamData = ''; + $block = $this->props[$this->wrkbook]['startBlock']; + //$count = 0; + $pos = 0; + while ($block != -2) { + $pos = $block * SMALL_BLOCK_SIZE; + $streamData .= substr($rootdata, $pos, SMALL_BLOCK_SIZE); + + $block = $this->smallBlockChain[$block]; + } + + return $streamData; + + + }else{ + + $numBlocks = $this->props[$this->wrkbook]['size'] / BIG_BLOCK_SIZE; + if ($this->props[$this->wrkbook]['size'] % BIG_BLOCK_SIZE != 0) { + $numBlocks++; + } + + if ($numBlocks == 0) return ''; + + //echo "numBlocks = $numBlocks\n"; + //byte[] streamData = new byte[numBlocks * BIG_BLOCK_SIZE]; + //print_r($this->wrkbook); + $streamData = ''; + $block = $this->props[$this->wrkbook]['startBlock']; + //$count = 0; + $pos = 0; + //echo "block = $block"; + while ($block != -2) { + $pos = ($block + 1) * BIG_BLOCK_SIZE; + $streamData .= substr($this->data, $pos, BIG_BLOCK_SIZE); + $block = $this->bigBlockChain[$block]; + } + //echo 'stream'.$streamData; + return $streamData; + } + } + +} +?> \ No newline at end of file diff --git a/lib/Excel/php_writeexcel/class.writeexcel_biffwriter.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_biffwriter.inc.php new file mode 100644 index 000000000..04f90fbbc --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_biffwriter.inc.php @@ -0,0 +1,209 @@ +byte_order = ''; + $this->BIFF_version = 0x0500; + $this->_byte_order = ''; + $this->_data = false; + $this->_datasize = 0; + $this->_limit = 2080; + + $this->_set_byte_order(); + } + + /* + * Determine the byte order and store it as class data to avoid + * recalculating it for each call to new(). + */ + function _set_byte_order() { + $this->byteorder=0; + // Check if "pack" gives the required IEEE 64bit float + $teststr = pack("d", 1.2345); + $number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F); + + if ($number == $teststr) { + $this->byte_order = 0; // Little Endian + } elseif ($number == strrev($teststr)) { + $this->byte_order = 1; // Big Endian + } else { + // Give up + trigger_error("Required floating point format not supported ". + "on this platform. See the portability section ". + "of the documentation.", E_USER_ERROR); + } + + $this->_byte_order = $this->byte_order; + } + + /* + * General storage function + */ + function _prepend($data) { + + if (func_num_args()>1) { + trigger_error("writeexcel_biffwriter::_prepend() ". + "called with more than one argument", E_USER_ERROR); + } + + if ($this->_debug) { + print "*** writeexcel_biffwriter::_prepend() called:"; + for ($c=0;$c $this->_limit) { + $data = $this->_add_continue($data); + } + + $this->_data = $data . $this->_data; + $this->_datasize += strlen($data); + } + + /* + * General storage function + */ + function _append($data) { + + if (func_num_args()>1) { + trigger_error("writeexcel_biffwriter::_append() ". + "called with more than one argument", E_USER_ERROR); + } + + if ($this->_debug) { + print "*** writeexcel_biffwriter::_append() called:"; + for ($c=0;$c $this->_limit) { + $data = $this->_add_continue($data); + } + + $this->_data = $this->_data . $data; + $this->_datasize += strlen($data); + } + + /* + * Writes Excel BOF record to indicate the beginning of a stream or + * sub-stream in the BIFF file. + * + * $type = 0x0005, Workbook + * $type = 0x0010, Worksheet + */ + function _store_bof($type) { + + $record = 0x0809; // Record identifier + $length = 0x0008; // Number of bytes to follow + + $version = $this->BIFF_version; + + // According to the SDK $build and $year should be set to zero. + // However, this throws a warning in Excel 5. So, use these + // magic numbers. + $build = 0x096C; + $year = 0x07C9; + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $version, $type, $build, $year); + + $this->_prepend($header . $data); + } + + /* + * Writes Excel EOF record to indicate the end of a BIFF stream. + */ + function _store_eof() { + + $record = 0x000A; // Record identifier + $length = 0x0000; // Number of bytes to follow + + $header = pack("vv", $record, $length); + + $this->_append($header); + } + + /* + * Excel limits the size of BIFF records. In Excel 5 the limit is 2084 + * bytes. In Excel 97 the limit is 8228 bytes. Records that are longer + * than these limits must be split up into CONTINUE blocks. + * + * This function take a long BIFF record and inserts CONTINUE records as + * necessary. + */ + function _add_continue($data) { + + $limit = $this->_limit; + $record = 0x003C; // Record identifier + + // The first 2080/8224 bytes remain intact. However, we have to change + // the length field of the record. + $tmp = substr($data, 0, $limit); + $data = substr($data, $limit); + $tmp = substr($tmp, 0, 2) . pack ("v", $limit-4) . substr($tmp, 4); + + // Strip out chunks of 2080/8224 bytes +4 for the header. + while (strlen($data) > $limit) { + $header = pack("vv", $record, $limit); + $tmp .= $header; + $tmp .= substr($data, 0, $limit); + $data = substr($data, $limit); + } + + // Mop up the last of the data + $header = pack("vv", $record, strlen($data)); + $tmp .= $header; + $tmp .= $data; + + return $tmp; + } + +} + +?> diff --git a/lib/Excel/php_writeexcel/class.writeexcel_format.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_format.inc.php new file mode 100644 index 000000000..15435c049 --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_format.inc.php @@ -0,0 +1,695 @@ +_xf_index = (sizeof($_)>0) ? array_shift($_) : 0; + + $this->_font_index = 0; + $this->_font = 'Arial'; + $this->_size = 10; + $this->_bold = 0x0190; + $this->_italic = 0; + $this->_color = 0x7FFF; + $this->_underline = 0; + $this->_font_strikeout = 0; + $this->_font_outline = 0; + $this->_font_shadow = 0; + $this->_font_script = 0; + $this->_font_family = 0; + $this->_font_charset = 0; + + $this->_num_format = 0; + + $this->_hidden = 0; + $this->_locked = 1; + + $this->_text_h_align = 0; + $this->_text_wrap = 0; + $this->_text_v_align = 2; + $this->_text_justlast = 0; + $this->_rotation = 0; + + $this->_fg_color = 0x40; + $this->_bg_color = 0x41; + + $this->_pattern = 0; + + $this->_bottom = 0; + $this->_top = 0; + $this->_left = 0; + $this->_right = 0; + + $this->_bottom_color = 0x40; + $this->_top_color = 0x40; + $this->_left_color = 0x40; + $this->_right_color = 0x40; + + // Set properties passed to writeexcel_workbook::addformat() + if (sizeof($_)>0) { + call_user_func_array(array(&$this, 'set_properties'), $_); + } + } + + /* + * Copy the attributes of another writeexcel_format object. + */ + function copy($other) { + $xf = $this->_xf_index; // Backup XF index + foreach ($other as $key->$value) { + $this->{$key} = $value; + } + $this->_xf_index = $xf; // Restore XF index + } + + /* + * Generate an Excel BIFF XF record. + */ + function get_xf() { + + $_=func_get_args(); + + // $record Record identifier + // $length Number of bytes to follow + + // $ifnt Index to FONT record + // $ifmt Index to FORMAT record + // $style Style and other options + // $align Alignment + // $icv fg and bg pattern colors + // $fill Fill and border line style + // $border1 Border line style and color + // $border2 Border color + + // Set the type of the XF record and some of the attributes. + if ($_[0] == "style") { + $style = 0xFFF5; + } else { + $style = $this->_locked; + $style |= $this->_hidden << 1; + } + + // Flags to indicate if attributes have been set. + $atr_num = ($this->_num_format != 0) ? 1 : 0; + $atr_fnt = ($this->_font_index != 0) ? 1 : 0; + $atr_alc = $this->_text_wrap ? 1 : 0; + $atr_bdr = ($this->_bottom || + $this->_top || + $this->_left || + $this->_right) ? 1 : 0; + $atr_pat = ($this->_fg_color != 0x41 || + $this->_bg_color != 0x41 || + $this->_pattern != 0x00) ? 1 : 0; + $atr_prot = 0; + + // Reset the default colors for the non-font properties + if ($this->_fg_color == 0x7FFF) $this->_fg_color = 0x40; + if ($this->_bg_color == 0x7FFF) $this->_bg_color = 0x41; + if ($this->_bottom_color == 0x7FFF) $this->_bottom_color = 0x41; + if ($this->_top_color == 0x7FFF) $this->_top_color = 0x41; + if ($this->_left_color == 0x7FFF) $this->_left_color = 0x41; + if ($this->_right_color == 0x7FFF) $this->_right_color = 0x41; + + // Zero the default border colour if the border has not been set. + if ($this->_bottom == 0) { + $this->_bottom_color = 0; + } + if ($this->_top == 0) { + $this->_top_color = 0; + } + if ($this->_right == 0) { + $this->_right_color = 0; + } + if ($this->_left == 0) { + $this->_left_color = 0; + } + + // The following 2 logical statements take care of special cases in + // relation to cell colors and patterns: + // 1. For a solid fill (_pattern == 1) Excel reverses the role of + // foreground and background colors + // 2. If the user specifies a foreground or background color + // without a pattern they probably wanted a solid fill, so we + // fill in the defaults. + if ($this->_pattern <= 0x01 && + $this->_bg_color != 0x41 && + $this->_fg_color == 0x40 ) + { + $this->_fg_color = $this->_bg_color; + $this->_bg_color = 0x40; + $this->_pattern = 1; + } + + if ($this->_pattern <= 0x01 && + $this->_bg_color == 0x41 && + $this->_fg_color != 0x40 ) + { + $this->_bg_color = 0x40; + $this->_pattern = 1; + } + + $record = 0x00E0; + $length = 0x0010; + + $ifnt = $this->_font_index; + $ifmt = $this->_num_format; + + $align = $this->_text_h_align; + $align |= $this->_text_wrap << 3; + $align |= $this->_text_v_align << 4; + $align |= $this->_text_justlast << 7; + $align |= $this->_rotation << 8; + $align |= $atr_num << 10; + $align |= $atr_fnt << 11; + $align |= $atr_alc << 12; + $align |= $atr_bdr << 13; + $align |= $atr_pat << 14; + $align |= $atr_prot << 15; + + $icv = $this->_fg_color; + $icv |= $this->_bg_color << 7; + + $fill = $this->_pattern; + $fill |= $this->_bottom << 6; + $fill |= $this->_bottom_color << 9; + + $border1 = $this->_top; + $border1 |= $this->_left << 3; + $border1 |= $this->_right << 6; + $border1 |= $this->_top_color << 9; + + $border2 = $this->_left_color; + $border2 |= $this->_right_color << 7; + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align, + $icv, $fill, + $border1, $border2); + + return($header . $data); + } + + /* + * Generate an Excel BIFF FONT record. + */ + function get_font() { + + // $record Record identifier + // $length Record length + + // $dyHeight Height of font (1/20 of a point) + // $grbit Font attributes + // $icv Index to color palette + // $bls Bold style + // $sss Superscript/subscript + // $uls Underline + // $bFamily Font family + // $bCharSet Character set + // $reserved Reserved + // $cch Length of font name + // $rgch Font name + + $dyHeight = $this->_size * 20; + $icv = $this->_color; + $bls = $this->_bold; + $sss = $this->_font_script; + $uls = $this->_underline; + $bFamily = $this->_font_family; + $bCharSet = $this->_font_charset; + $rgch = $this->_font; + + $cch = strlen($rgch); + $record = 0x31; + $length = 0x0F + $cch; + $reserved = 0x00; + + $grbit = 0x00; + + if ($this->_italic) { + $grbit |= 0x02; + } + + if ($this->_font_strikeout) { + $grbit |= 0x08; + } + + if ($this->_font_outline) { + $grbit |= 0x10; + } + + if ($this->_font_shadow) { + $grbit |= 0x20; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls, + $sss, $uls, $bFamily, + $bCharSet, $reserved, $cch); + + return($header . $data . $this->_font); + } + + /* + * Returns a unique hash key for a font. + * Used by writeexcel_workbook::_store_all_fonts() + */ + function get_font_key() { + + # The following elements are arranged to increase the probability of + # generating a unique key. Elements that hold a large range of numbers + # eg. _color are placed between two binary elements such as _italic + # + $key = $this->_font.$this->_size. + $this->_font_script.$this->_underline. + $this->_font_strikeout.$this->_bold.$this->_font_outline. + $this->_font_family.$this->_font_charset. + $this->_font_shadow.$this->_color.$this->_italic; + + $key = preg_replace('/ /', '_', $key); # Convert the key to a single word + + return $key; + } + + /* + * Returns the used by Worksheet->_XF() + */ + function get_xf_index() { + return $this->_xf_index; + } + + /* + * Used in conjunction with the set_xxx_color methods to convert a color + * string into a number. Color range is 0..63 but we will restrict it + * to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15. + */ + function _get_color($color=false) { + + $colors = array( + 'aqua' => 0x0F, + 'cyan' => 0x0F, + 'black' => 0x08, + 'blue' => 0x0C, + 'brown' => 0x10, + 'magenta' => 0x0E, + 'fuchsia' => 0x0E, + 'gray' => 0x17, + 'grey' => 0x17, + 'green' => 0x11, + 'lime' => 0x0B, + 'navy' => 0x12, + 'orange' => 0x35, + 'purple' => 0x14, + 'red' => 0x0A, + 'silver' => 0x16, + 'white' => 0x09, + 'yellow' => 0x0D + ); + + // Return the default color, 0x7FFF, if undef, + if ($color===false) { + return 0x7FFF; + } + + // or the color string converted to an integer, + if (isset($colors[strtolower($color)])) { + return $colors[strtolower($color)]; + } + + // or the default color if string is unrecognised, + if (preg_match('/\D/', $color)) { + return 0x7FFF; + } + + // or an index < 8 mapped into the correct range, + if ($color<8) { + return $color + 8; + } + + // or the default color if arg is outside range, + if ($color>63) { + return 0x7FFF; + } + + // or an integer in the valid range + return $color; + } + + /* + * Set cell alignment. + */ + function set_align($location) { + + // Ignore numbers + if (preg_match('/\d/', $location)) { + return; + } + + $location = strtolower($location); + + switch ($location) { + + case 'left': + $this->set_text_h_align(1); + break; + + case 'centre': + case 'center': + $this->set_text_h_align(2); + break; + + case 'right': + $this->set_text_h_align(3); + break; + + case 'fill': + $this->set_text_h_align(4); + break; + + case 'justify': + $this->set_text_h_align(5); + break; + + case 'merge': + $this->set_text_h_align(6); + break; + + case 'equal_space': + $this->set_text_h_align(7); + break; + + case 'top': + $this->set_text_v_align(0); + break; + + case 'vcentre': + case 'vcenter': + $this->set_text_v_align(1); + break; + break; + + case 'bottom': + $this->set_text_v_align(2); + break; + + case 'vjustify': + $this->set_text_v_align(3); + break; + + case 'vequal_space': + $this->set_text_v_align(4); + break; + } + } + + /* + * Set vertical cell alignment. This is required by the set_properties() + * method to differentiate between the vertical and horizontal properties. + */ + function set_valign($location) { + $this->set_align($location); + } + + /* + * This is an alias for the unintuitive set_align('merge') + */ + function set_merge() { + $this->set_text_h_align(6); + } + + /* + * Bold has a range 0x64..0x3E8. + * 0x190 is normal. 0x2BC is bold. + */ + function set_bold($weight=1) { + + if ($weight == 1) { + // Bold text + $weight = 0x2BC; + } + + if ($weight == 0) { + // Normal text + $weight = 0x190; + } + + if ($weight < 0x064) { + // Lower bound + $weight = 0x190; + } + + if ($weight > 0x3E8) { + // Upper bound + $weight = 0x190; + } + + $this->_bold = $weight; + } + + /* + * Set all cell borders (bottom, top, left, right) to the same style + */ + function set_border($style) { + $this->set_bottom($style); + $this->set_top($style); + $this->set_left($style); + $this->set_right($style); + } + + /* + * Set all cell borders (bottom, top, left, right) to the same color + */ + function set_border_color($color) { + $this->set_bottom_color($color); + $this->set_top_color($color); + $this->set_left_color($color); + $this->set_right_color($color); + } + + /* + * Convert hashes of properties to method calls. + */ + function set_properties() { + + $_=func_get_args(); + + $properties=array(); + foreach($_ as $props) { + if (is_array($props)) { + $properties=array_merge($properties, $props); + } else { + $properties[]=$props; + } + } + + foreach ($properties as $key=>$value) { + + // Strip leading "-" from Tk style properties eg. -color => 'red'. + $key = preg_replace('/^-/', '', $key); + + /* Make sure method names are alphanumeric characters only, in + case tainted data is passed to the eval(). */ + if (preg_match('/\W/', $key)) { + trigger_error("Unknown property: $key.", + E_USER_ERROR); + } + + /* Evaling all $values as a strings gets around the problem of + some numerical format strings being evaluated as numbers, for + example "00000" for a zip code. */ + if (is_int($key)) { + eval("\$this->set_$value();"); + } else { + eval("\$this->set_$key('$value');"); + } + + } + } + + function set_font($font) { + $this->_font=$font; + } + + function set_size($size) { + $this->_size=$size; + } + + function set_italic($italic=1) { + $this->_italic=$italic; + } + + function set_color($color) { + $this->_color=$this->_get_color($color); + } + + function set_underline($underline=1) { + $this->_underline=$underline; + } + + function set_font_strikeout($font_strikeout=1) { + $this->_font_strikeout=$font_strikeout; + } + + function set_font_outline($font_outline=1) { + $this->_font_outline=$font_outline; + } + + function set_font_shadow($font_shadow=1) { + $this->_font_shadow=$font_shadow; + } + + function set_font_script($font_script=1) { + $this->_font_script=$font_script; + } + + /* Undocumented */ + function set_font_family($font_family=1) { + $this->_font_family=$font_family; + } + + /* Undocumented */ + function set_font_charset($font_charset=1) { + $this->_font_charset=$font_charset; + } + + function set_num_format($num_format=1) { + $this->_num_format=$num_format; + } + + function set_hidden($hidden=1) { + $this->_hidden=$hidden; + } + + function set_locked($locked=1) { + $this->_locked=$locked; + } + + function set_text_h_align($align) { + $this->_text_h_align=$align; + } + + function set_text_wrap($wrap=1) { + $this->_text_wrap=$wrap; + } + + function set_text_v_align($align) { + $this->_text_v_align=$align; + } + + function set_text_justlast($text_justlast=1) { + $this->_text_justlast=$text_justlast; + } + + function set_rotation($rotation=1) { + $this->_rotation=$rotation; + } + + function set_fg_color($color) { + $this->_fg_color=$this->_get_color($color); + } + + function set_bg_color($color) { + $this->_bg_color=$this->_get_color($color); + } + + function set_pattern($pattern=1) { + $this->_pattern=$pattern; + } + + function set_bottom($bottom=1) { + $this->_bottom=$bottom; + } + + function set_top($top=1) { + $this->_top=$top; + } + + function set_left($left=1) { + $this->_left=$left; + } + + function set_right($right=1) { + $this->_right=$right; + } + + function set_bottom_color($color) { + $this->_bottom_color=$this->_get_color($color); + } + + function set_top_color($color) { + $this->_top_color=$this->_get_color($color); + } + + function set_left_color($color) { + $this->_left_color=$this->_get_color($color); + } + + function set_right_color($color) { + $this->_right_color=$this->_get_color($color); + } + +} + +?> diff --git a/lib/Excel/php_writeexcel/class.writeexcel_formula.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_formula.inc.php new file mode 100644 index 000000000..7a04fd63a --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_formula.inc.php @@ -0,0 +1,1610 @@ +"); + // @const SPREADSHEET_EXCEL_WRITER_GT token identifier for character ">" +define('SPREADSHEET_EXCEL_WRITER_LT',"<"); + // @const SPREADSHEET_EXCEL_WRITER_LT token identifier for character "<" +define('SPREADSHEET_EXCEL_WRITER_LE',"<="); + // @const SPREADSHEET_EXCEL_WRITER_LE token identifier for character "<=" +define('SPREADSHEET_EXCEL_WRITER_GE',">="); + // @const SPREADSHEET_EXCEL_WRITER_GE token identifier for character ">=" +define('SPREADSHEET_EXCEL_WRITER_EQ',"="); + // @const SPREADSHEET_EXCEL_WRITER_EQ token identifier for character "=" +define('SPREADSHEET_EXCEL_WRITER_NE',"<>"); + // @const SPREADSHEET_EXCEL_WRITER_NE token identifier for character "<>" + + +class writeexcel_formula { + +############################################################################### +# +# Class data. +# +var $parser; +var $ptg; +var $_functions; +var $_current_char; +var $_current_token; +var $_lookahead; +var $_debug; +var $_byte_order; +var $_volatile; +var $_workbook; +var $_ext_sheets; +var $_formula; + +############################################################################### +# +# new() +# +# Constructor +# +function writeexcel_formula($byte_order) { + + $this->parser = false; + $this->ptg = array(); + $this->_functions = array(); + $this->_debug = 0; + $this->_byte_order = $byte_order; + $this->_volatile = 0; + $this->_workbook = ""; + $this->_ext_sheets = array(); + $this->_current_token = ''; + $this->_lookahead = ''; + $this->_current_char = 0; + $this->_formula = ''; +} + +############################################################################### +# +# _init_parser() +# +# There is a small overhead involved in generating the parser. Therefore, the +# initialisation is delayed until a formula is required. TODO: use a pre- +# compiled header. +# +function _init_parser() { + + $this->_initializeHashes(); + + + if ($this->_debug) { + print "Init_parser.\n\n"; + } +} + +############################################################################### +# +# parse_formula() +# +# This is the only public method. It takes a textual description of a formula +# and returns a RPN encoded byte string. +# +function parse_formula() { + + $_=func_get_args(); + + # Initialise the parser if this is the first call + if ($this->parser===false) { + $this->_init_parser(); + } + + $formula = array_shift($_); + //$str; + //$tokens; + + if ($this->_debug) { + print "$formula\n"; + } + + # Build the parse tree for the formula + + $this->_formula = $formula; + $this->_current_char = 0; + $this->_lookahead = $this->_formula{1}; + $this->_advance($formula); + $parsetree = $this->_condition(); + + $str = $this->toReversePolish($parsetree); + + return $str; +} + +function isError($data) { + return (bool)(is_object($data) && + (get_class($data) == 'pear_error' || + is_subclass_of($data, 'pear_error'))); +} + +/** +* Class for parsing Excel formulas +* +* @author Xavier Noguer +* @category FileFormats +* @package Spreadsheet_Excel_Writer +*/ + + +/** +* Initialize the ptg and function hashes. +* +* @access private +*/ +function _initializeHashes() + { + // The Excel ptg indices + $this->ptg = array( + 'ptgExp' => 0x01, + 'ptgTbl' => 0x02, + 'ptgAdd' => 0x03, + 'ptgSub' => 0x04, + 'ptgMul' => 0x05, + 'ptgDiv' => 0x06, + 'ptgPower' => 0x07, 'ptgConcat' => 0x08, + 'ptgLT' => 0x09, + 'ptgLE' => 0x0A, + 'ptgEQ' => 0x0B, + 'ptgGE' => 0x0C, + 'ptgGT' => 0x0D, + 'ptgNE' => 0x0E, + 'ptgIsect' => 0x0F, + 'ptgUnion' => 0x10, + 'ptgRange' => 0x11, + 'ptgUplus' => 0x12, + 'ptgUminus' => 0x13, + 'ptgPercent' => 0x14, + 'ptgParen' => 0x15, + 'ptgMissArg' => 0x16, + 'ptgStr' => 0x17, + 'ptgAttr' => 0x19, + 'ptgSheet' => 0x1A, + 'ptgEndSheet' => 0x1B, + 'ptgErr' => 0x1C, + 'ptgBool' => 0x1D, + 'ptgInt' => 0x1E, + 'ptgNum' => 0x1F, + 'ptgArray' => 0x20, + 'ptgFunc' => 0x21, + 'ptgFuncVar' => 0x22, + 'ptgName' => 0x23, + 'ptgRef' => 0x24, + 'ptgArea' => 0x25, + 'ptgMemArea' => 0x26, + 'ptgMemErr' => 0x27, + 'ptgMemNoMem' => 0x28, + 'ptgMemFunc' => 0x29, + 'ptgRefErr' => 0x2A, + 'ptgAreaErr' => 0x2B, + 'ptgRefN' => 0x2C, + 'ptgAreaN' => 0x2D, + 'ptgMemAreaN' => 0x2E, + 'ptgMemNoMemN' => 0x2F, + 'ptgNameX' => 0x39, + 'ptgRef3d' => 0x3A, + + 'ptgArea3d' => 0x3B, + 'ptgRefErr3d' => 0x3C, + 'ptgAreaErr3d' => 0x3D, + 'ptgArrayV' => 0x40, + 'ptgFuncV' => 0x41, + 'ptgFuncVarV' => 0x42, + 'ptgNameV' => 0x43, + 'ptgRefV' => 0x44, + 'ptgAreaV' => 0x45, + 'ptgMemAreaV' => 0x46, + 'ptgMemErrV' => 0x47, + 'ptgMemNoMemV' => 0x48, + 'ptgMemFuncV' => 0x49, + 'ptgRefErrV' => 0x4A, + 'ptgAreaErrV' => 0x4B, + 'ptgRefNV' => 0x4C, + 'ptgAreaNV' => 0x4D, + 'ptgMemAreaNV' => 0x4E, + 'ptgMemNoMemN' => 0x4F, + 'ptgFuncCEV' => 0x58, + 'ptgNameXV' => 0x59, + 'ptgRef3dV' => 0x5A, + 'ptgArea3dV' => 0x5B, 'ptgRefErr3dV' => 0x5C, + 'ptgAreaErr3d' => 0x5D, + 'ptgArrayA' => 0x60, + 'ptgFuncA' => 0x61, + 'ptgFuncVarA' => 0x62, + 'ptgNameA' => 0x63, 'ptgRefA' => 0x64, + 'ptgAreaA' => 0x65, + 'ptgMemAreaA' => 0x66, + 'ptgMemErrA' => 0x67, + 'ptgMemNoMemA' => 0x68, + 'ptgMemFuncA' => 0x69, + 'ptgRefErrA' => 0x6A, + 'ptgAreaErrA' => 0x6B, + 'ptgRefNA' => 0x6C, + 'ptgAreaNA' => 0x6D, + 'ptgMemAreaNA' => 0x6E, + 'ptgMemNoMemN' => 0x6F, + 'ptgFuncCEA' => 0x78, + 'ptgNameXA' => 0x79, + 'ptgRef3dA' => 0x7A, + 'ptgArea3dA' => 0x7B, + 'ptgRefErr3dA' => 0x7C, + 'ptgAreaErr3d' => 0x7D + ); + + // Thanks to Michael Meeks and Gnumeric for the initial arg values. + // + // The following hash was generated by "function_locale.pl" in the distro. + // Refer to function_locale.pl for non-English function names. + // + // The array elements are as follow: + // ptg: The Excel function ptg code. + // args: The number of arguments that the function takes: + // >=0 is a fixed number of arguments. + // -1 is a variable number of arguments. + // class: The reference, value or array class of the function args. + // vol: The function is volatile. + // + $this->_functions = array( + // function ptg args class vol + 'COUNT' => array( 0, -1, 0, 0 ), + 'IF' => array( 1, -1, 1, 0 ), + 'ISNA' => array( 2, 1, 1, 0 ), + 'ISERROR' => array( 3, 1, 1, 0 ), + 'SUM' => array( 4, -1, 0, 0 ), + 'AVERAGE' => array( 5, -1, 0, 0 ), + 'MIN' => array( 6, -1, 0, 0 ), + 'MAX' => array( 7, -1, 0, 0 ), + 'ROW' => array( 8, -1, 0, 0 ), + 'COLUMN' => array( 9, -1, 0, 0 ), + 'NA' => array( 10, 0, 0, 0 ), + 'NPV' => array( 11, -1, 1, 0 ), + 'STDEV' => array( 12, -1, 0, 0 ), + 'DOLLAR' => array( 13, -1, 1, 0 ), + 'FIXED' => array( 14, -1, 1, 0 ), + 'SIN' => array( 15, 1, 1, 0 ), + 'COS' => array( 16, 1, 1, 0 ), + 'TAN' => array( 17, 1, 1, 0 ), + 'ATAN' => array( 18, 1, 1, 0 ), + 'PI' => array( 19, 0, 1, 0 ), + 'SQRT' => array( 20, 1, 1, 0 ), + 'EXP' => array( 21, 1, 1, 0 ), + 'LN' => array( 22, 1, 1, 0 ), + 'LOG10' => array( 23, 1, 1, 0 ), + 'ABS' => array( 24, 1, 1, 0 ), + 'INT' => array( 25, 1, 1, 0 ), + 'SIGN' => array( 26, 1, 1, 0 ), + 'ROUND' => array( 27, 2, 1, 0 ), + 'LOOKUP' => array( 28, -1, 0, 0 ), + 'INDEX' => array( 29, -1, 0, 1 ), + 'REPT' => array( 30, 2, 1, 0 ), + 'MID' => array( 31, 3, 1, 0 ), + 'LEN' => array( 32, 1, 1, 0 ), + 'VALUE' => array( 33, 1, 1, 0 ), + 'TRUE' => array( 34, 0, 1, 0 ), + 'FALSE' => array( 35, 0, 1, 0 ), + 'AND' => array( 36, -1, 0, 0 ), + 'OR' => array( 37, -1, 0, 0 ), + 'NOT' => array( 38, 1, 1, 0 ), + 'MOD' => array( 39, 2, 1, 0 ), + 'DCOUNT' => array( 40, 3, 0, 0 ), + 'DSUM' => array( 41, 3, 0, 0 ), + 'DAVERAGE' => array( 42, 3, 0, 0 ), + 'DMIN' => array( 43, 3, 0, 0 ), + 'DMAX' => array( 44, 3, 0, 0 ), + 'DSTDEV' => array( 45, 3, 0, 0 ), + 'VAR' => array( 46, -1, 0, 0 ), + 'DVAR' => array( 47, 3, 0, 0 ), + 'TEXT' => array( 48, 2, 1, 0 ), + 'LINEST' => array( 49, -1, 0, 0 ), + 'TREND' => array( 50, -1, 0, 0 ), + 'LOGEST' => array( 51, -1, 0, 0 ), + 'GROWTH' => array( 52, -1, 0, 0 ), + 'PV' => array( 56, -1, 1, 0 ), + 'FV' => array( 57, -1, 1, 0 ), + 'NPER' => array( 58, -1, 1, 0 ), + 'PMT' => array( 59, -1, 1, 0 ), + 'RATE' => array( 60, -1, 1, 0 ), + 'MIRR' => array( 61, 3, 0, 0 ), + 'IRR' => array( 62, -1, 0, 0 ), + 'RAND' => array( 63, 0, 1, 1 ), + 'MATCH' => array( 64, -1, 0, 0 ), + 'DATE' => array( 65, 3, 1, 0 ), + 'TIME' => array( 66, 3, 1, 0 ), + 'DAY' => array( 67, 1, 1, 0 ), + 'MONTH' => array( 68, 1, 1, 0 ), + 'YEAR' => array( 69, 1, 1, 0 ), + 'WEEKDAY' => array( 70, -1, 1, 0 ), + 'HOUR' => array( 71, 1, 1, 0 ), + 'MINUTE' => array( 72, 1, 1, 0 ), + 'SECOND' => array( 73, 1, 1, 0 ), + 'NOW' => array( 74, 0, 1, 1 ), + 'AREAS' => array( 75, 1, 0, 1 ), + 'ROWS' => array( 76, 1, 0, 1 ), + 'COLUMNS' => array( 77, 1, 0, 1 ), + 'OFFSET' => array( 78, -1, 0, 1 ), + 'SEARCH' => array( 82, -1, 1, 0 ), + 'TRANSPOSE' => array( 83, 1, 1, 0 ), + 'TYPE' => array( 86, 1, 1, 0 ), + 'ATAN2' => array( 97, 2, 1, 0 ), + 'ASIN' => array( 98, 1, 1, 0 ), + 'ACOS' => array( 99, 1, 1, 0 ), + 'CHOOSE' => array( 100, -1, 1, 0 ), + 'HLOOKUP' => array( 101, -1, 0, 0 ), + 'VLOOKUP' => array( 102, -1, 0, 0 ), + 'ISREF' => array( 105, 1, 0, 0 ), + 'LOG' => array( 109, -1, 1, 0 ), + 'CHAR' => array( 111, 1, 1, 0 ), + 'LOWER' => array( 112, 1, 1, 0 ), + 'UPPER' => array( 113, 1, 1, 0 ), + 'PROPER' => array( 114, 1, 1, 0 ), + 'LEFT' => array( 115, -1, 1, 0 ), + 'RIGHT' => array( 116, -1, 1, 0 ), + 'EXACT' => array( 117, 2, 1, 0 ), + 'TRIM' => array( 118, 1, 1, 0 ), + 'REPLACE' => array( 119, 4, 1, 0 ), + 'SUBSTITUTE' => array( 120, -1, 1, 0 ), + 'CODE' => array( 121, 1, 1, 0 ), + 'FIND' => array( 124, -1, 1, 0 ), + 'CELL' => array( 125, -1, 0, 1 ), + 'ISERR' => array( 126, 1, 1, 0 ), + 'ISTEXT' => array( 127, 1, 1, 0 ), + 'ISNUMBER' => array( 128, 1, 1, 0 ), + 'ISBLANK' => array( 129, 1, 1, 0 ), + 'T' => array( 130, 1, 0, 0 ), + 'N' => array( 131, 1, 0, 0 ), + 'DATEVALUE' => array( 140, 1, 1, 0 ), + 'TIMEVALUE' => array( 141, 1, 1, 0 ), + 'SLN' => array( 142, 3, 1, 0 ), + 'SYD' => array( 143, 4, 1, 0 ), + 'DDB' => array( 144, -1, 1, 0 ), + 'INDIRECT' => array( 148, -1, 1, 1 ), + 'CALL' => array( 150, -1, 1, 0 ), + 'CLEAN' => array( 162, 1, 1, 0 ), + 'MDETERM' => array( 163, 1, 2, 0 ), + 'MINVERSE' => array( 164, 1, 2, 0 ), + 'MMULT' => array( 165, 2, 2, 0 ), + 'IPMT' => array( 167, -1, 1, 0 ), + 'PPMT' => array( 168, -1, 1, 0 ), + 'COUNTA' => array( 169, -1, 0, 0 ), + 'PRODUCT' => array( 183, -1, 0, 0 ), + 'FACT' => array( 184, 1, 1, 0 ), + 'DPRODUCT' => array( 189, 3, 0, 0 ), + 'ISNONTEXT' => array( 190, 1, 1, 0 ), + 'STDEVP' => array( 193, -1, 0, 0 ), + 'VARP' => array( 194, -1, 0, 0 ), + 'DSTDEVP' => array( 195, 3, 0, 0 ), + 'DVARP' => array( 196, 3, 0, 0 ), + 'TRUNC' => array( 197, -1, 1, 0 ), + 'ISLOGICAL' => array( 198, 1, 1, 0 ), + 'DCOUNTA' => array( 199, 3, 0, 0 ), + 'ROUNDUP' => array( 212, 2, 1, 0 ), + 'ROUNDDOWN' => array( 213, 2, 1, 0 ), + 'RANK' => array( 216, -1, 0, 0 ), + 'ADDRESS' => array( 219, -1, 1, 0 ), + 'DAYS360' => array( 220, -1, 1, 0 ), + 'TODAY' => array( 221, 0, 1, 1 ), + 'VDB' => array( 222, -1, 1, 0 ), + 'MEDIAN' => array( 227, -1, 0, 0 ), + 'SUMPRODUCT' => array( 228, -1, 2, 0 ), + 'SINH' => array( 229, 1, 1, 0 ), + 'COSH' => array( 230, 1, 1, 0 ), + 'TANH' => array( 231, 1, 1, 0 ), + 'ASINH' => array( 232, 1, 1, 0 ), + 'ACOSH' => array( 233, 1, 1, 0 ), + 'ATANH' => array( 234, 1, 1, 0 ), + 'DGET' => array( 235, 3, 0, 0 ), + 'INFO' => array( 244, 1, 1, 1 ), + 'DB' => array( 247, -1, 1, 0 ), + 'FREQUENCY' => array( 252, 2, 0, 0 ), + 'ERROR.TYPE' => array( 261, 1, 1, 0 ), + 'REGISTER.ID' => array( 267, -1, 1, 0 ), + 'AVEDEV' => array( 269, -1, 0, 0 ), + 'BETADIST' => array( 270, -1, 1, 0 ), + 'GAMMALN' => array( 271, 1, 1, 0 ), + 'BETAINV' => array( 272, -1, 1, 0 ), + 'BINOMDIST' => array( 273, 4, 1, 0 ), + 'CHIDIST' => array( 274, 2, 1, 0 ), + 'CHIINV' => array( 275, 2, 1, 0 ), + 'COMBIN' => array( 276, 2, 1, 0 ), + 'CONFIDENCE' => array( 277, 3, 1, 0 ), + 'CRITBINOM' => array( 278, 3, 1, 0 ), + 'EVEN' => array( 279, 1, 1, 0 ), + 'EXPONDIST' => array( 280, 3, 1, 0 ), + 'FDIST' => array( 281, 3, 1, 0 ), + 'FINV' => array( 282, 3, 1, 0 ), + 'FISHER' => array( 283, 1, 1, 0 ), + 'FISHERINV' => array( 284, 1, 1, 0 ), + 'FLOOR' => array( 285, 2, 1, 0 ), + 'GAMMADIST' => array( 286, 4, 1, 0 ), + 'GAMMAINV' => array( 287, 3, 1, 0 ), + 'CEILING' => array( 288, 2, 1, 0 ), + 'HYPGEOMDIST' => array( 289, 4, 1, 0 ), + 'LOGNORMDIST' => array( 290, 3, 1, 0 ), + 'LOGINV' => array( 291, 3, 1, 0 ), + 'NEGBINOMDIST' => array( 292, 3, 1, 0 ), + 'NORMDIST' => array( 293, 4, 1, 0 ), + 'NORMSDIST' => array( 294, 1, 1, 0 ), + 'NORMINV' => array( 295, 3, 1, 0 ), + 'NORMSINV' => array( 296, 1, 1, 0 ), + 'STANDARDIZE' => array( 297, 3, 1, 0 ), + 'ODD' => array( 298, 1, 1, 0 ), + 'PERMUT' => array( 299, 2, 1, 0 ), + 'POISSON' => array( 300, 3, 1, 0 ), + 'TDIST' => array( 301, 3, 1, 0 ), + 'WEIBULL' => array( 302, 4, 1, 0 ), + 'SUMXMY2' => array( 303, 2, 2, 0 ), + 'SUMX2MY2' => array( 304, 2, 2, 0 ), + 'SUMX2PY2' => array( 305, 2, 2, 0 ), + 'CHITEST' => array( 306, 2, 2, 0 ), + 'CORREL' => array( 307, 2, 2, 0 ), + 'COVAR' => array( 308, 2, 2, 0 ), + 'FORECAST' => array( 309, 3, 2, 0 ), + 'FTEST' => array( 310, 2, 2, 0 ), + 'INTERCEPT' => array( 311, 2, 2, 0 ), + 'PEARSON' => array( 312, 2, 2, 0 ), + 'RSQ' => array( 313, 2, 2, 0 ), + 'STEYX' => array( 314, 2, 2, 0 ), + 'SLOPE' => array( 315, 2, 2, 0 ), + 'TTEST' => array( 316, 4, 2, 0 ), + 'PROB' => array( 317, -1, 2, 0 ), + 'DEVSQ' => array( 318, -1, 0, 0 ), + 'GEOMEAN' => array( 319, -1, 0, 0 ), + 'HARMEAN' => array( 320, -1, 0, 0 ), + 'SUMSQ' => array( 321, -1, 0, 0 ), + 'KURT' => array( 322, -1, 0, 0 ), + 'SKEW' => array( 323, -1, 0, 0 ), + 'ZTEST' => array( 324, -1, 0, 0 ), + 'LARGE' => array( 325, 2, 0, 0 ), + 'SMALL' => array( 326, 2, 0, 0 ), + 'QUARTILE' => array( 327, 2, 0, 0 ), + 'PERCENTILE' => array( 328, 2, 0, 0 ), + 'PERCENTRANK' => array( 329, -1, 0, 0 ), + 'MODE' => array( 330, -1, 2, 0 ), + 'TRIMMEAN' => array( 331, 2, 0, 0 ), + 'TINV' => array( 332, 2, 1, 0 ), + 'CONCATENATE' => array( 336, -1, 1, 0 ), + 'POWER' => array( 337, 2, 1, 0 ), + 'RADIANS' => array( 342, 1, 1, 0 ), + 'DEGREES' => array( 343, 1, 1, 0 ), + 'SUBTOTAL' => array( 344, -1, 0, 0 ), + 'SUMIF' => array( 345, -1, 0, 0 ), + 'COUNTIF' => array( 346, 2, 0, 0 ), + 'COUNTBLANK' => array( 347, 1, 0, 0 ), + 'ROMAN' => array( 354, -1, 1, 0 ) + ); +} + +/** +* Convert a token to the proper ptg value. +* +* @access private +* @param mixed $token The token to convert. +* @return mixed the converted token on success. PEAR_Error if the token +* is not recognized +*/ +function _convert($token) + { + if (preg_match('/^"[^"]{0,255}"$/', $token)) + { + return $this->_convertString($token); + } + elseif (is_numeric($token)) + { + return $this->_convertNumber($token); + } + // match references like A1 or $A$1 + +elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$token)) + { + return $this->_convertRef2d($token); + } + // match external references like Sheet1:Sheet2!A1 + elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-Ia-i]?[A-Za-z](\d+)$/",$token)) + { + + return $this->_convertRef3d($token); + } + // match ranges like A1:B2 + elseif (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+\:\$?[A-Ia-i]?[A-Za-z]\$?\d+$/',$token)) + { + return $this->_convertRange2d($token); + } + // match ranges like A1..B2 + elseif (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+\.\.\$?[A-Ia-i]?[A-Za-z]\$?\d+$/',$token)) + { + return $this->_convertRange2d($token); + } + // match external ranges like Sheet1:Sheet2!A1:B2 + elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/",$token)) + { + return $this->_convertRange3d($token); + } + // match external ranges like 'Sheet1:Sheet2'!A1:B2 + elseif (preg_match("/^'[A-Za-z0-9_ ]+(\:[A-Za-z0-9_ ]+)?'\!([A-Ia-i]?[A-Za-z])?(\d+)\:([A-Ia-i]?[A-Za-z])?(\d+)$/",$token)) + { + return $this->_convertRange3d($token); + } + elseif (isset($this->ptg[$token])) // operators (including parentheses) + { + return pack("C", $this->ptg[$token]); + } + // commented so argument number can be processed correctly. See toReversePolish(). + /*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token)) + { + return($this->_convertFunction($token,$this->_func_args)); + }*/ + // if it's an argument, ignore the token (the argument remains) + elseif ($token == 'arg') + { + return ''; + } + // TODO: use real error codes + trigger_error("Unknown token $token", E_USER_ERROR); +} + +/** +* Convert a number token to ptgInt or ptgNum +* +* @access private +* @param mixed $num an integer or double for conversion to its ptg value +*/ +function _convertNumber($num) + { + + // Integer in the range 0..2**16-1 + + if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) { + return(pack("Cv", $this->ptg['ptgInt'], $num)); + } + else { // A float + if ($this->_byte_order) { // if it's Big Endian + $num = strrev($num); + } + return pack("Cd", $this->ptg['ptgNum'], $num); + } +} + +/** +* Convert a string token to ptgStr +* +* @access private +* @param string $string A string for conversion to its ptg value +*/ +function _convertString($string) + { + // chop away beggining and ending quotes + $string = substr($string, 1, strlen($string) - 2); + return pack("CC", $this->ptg['ptgStr'], strlen($string)).$string; +} + +/** +* Convert a function to a ptgFunc or ptgFuncVarV depending on the number of +* args that it takes. +* +* @access private +* @param string $token The name of the function for convertion to ptg value. +* @param integer $num_args The number of arguments the function receives. +* @return string The packed ptg for the function +*/ +function _convertFunction($token, $num_args) + { + $args = $this->_functions[$token][1]; + $volatile = $this->_functions[$token][3]; + + // Fixed number of args eg. TIME($i,$j,$k). + if ($args >= 0) { + return pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]); + } + // Variable number of args eg. SUM($i,$j,$k, ..). + if ($args == -1) { + return pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]); + } +} + +/** +* Convert an Excel range such as A1:D4 to a ptgRefV. +* +* @access private +* @param string $range An Excel range in the A1:A2 or A1..A2 format. +*/ +function _convertRange2d($range) + { + $class = 2; // as far as I know, this is magick. + + // Split the range into 2 cell refs + if (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)\:\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$range)) { + list($cell1, $cell2) = split(':', $range); + } + elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)\.\.\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/',$range)) { + list($cell1, $cell2) = split('\.\.', $range); + } + else { + // TODO: use real error codes + trigger_error("Unknown range separator", E_USER_ERROR); + } + + // Convert the cell references + $cell_array1 = $this->_cellToPackedRowcol($cell1); + if ($this->isError($cell_array1)) { + return $cell_array1; + } + list($row1, $col1) = $cell_array1; + $cell_array2 = $this->_cellToPackedRowcol($cell2); + if ($this->isError($cell_array2)) { + return $cell_array2; + } + list($row2, $col2) = $cell_array2; + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea']); + } + elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgAreaV']); + } + elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgAreaA']); + } + else { + // TODO: use real error codes + trigger_error("Unknown class $class", E_USER_ERROR); + } + return $ptgArea . $row1 . $row2 . $col1. $col2; +} + +/** +* Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to +* a ptgArea3dV. +* +* @access private +* @param string $token An Excel range in the Sheet1!A1:A2 format. +*/ +function _convertRange3d($token) + { + $class = 2; // as far as I know, this is magick. + + // Split the ref at the ! symbol + list($ext_ref, $range) = split('!', $token); + + // Convert the external reference part + $ext_ref = $this->_packExtRef($ext_ref); + if ($this->isError($ext_ref)) { + return $ext_ref; + } + + // Split the range into 2 cell refs + list($cell1, $cell2) = split(':', $range); + + // Convert the cell references + if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $cell1)) + { + $cell_array1 = $this->_cellToPackedRowcol($cell1); + if ($this->isError($cell_array1)) { + return $cell_array1; + } + list($row1, $col1) = $cell_array1; + $cell_array2 = $this->_cellToPackedRowcol($cell2); + if ($this->isError($cell_array2)) { + return $cell_array2; + } + list($row2, $col2) = $cell_array2; + } + else { // It's a columns range (like 26:27) + $cells_array = $this->_rangeToPackedRange($cell1.':'.$cell2); + if ($this->isError($cells_array)) { + return $cells_array; + } + list($row1, $col1, $row2, $col2) = $cells_array; + } + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgArea = pack("C", $this->ptg['ptgArea3d']); + } + elseif ($class == 1) { + $ptgArea = pack("C", $this->ptg['ptgArea3dV']); + } + elseif ($class == 2) { + $ptgArea = pack("C", $this->ptg['ptgArea3dA']); + } + else { + trigger_error("Unknown class $class", E_USER_ERROR); + } + + return $ptgArea . $ext_ref . $row1 . $row2 . $col1. $col2; +} + +/** +* Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV. +* +* @access private +* @param string $cell An Excel cell reference +* @return string The cell in packed() format with the corresponding ptg +*/ +function _convertRef2d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Convert the cell reference + $cell_array = $this->_cellToPackedRowcol($cell); + if ($this->isError($cell_array)) { + return $cell_array; + } + list($row, $col) = $cell_array; + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef']); + } + elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRefV']); + } + elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRefA']); + } + else { + // TODO: use real error codes + trigger_error("Unknown class $class",E_USER_ERROR); + } + return $ptgRef.$row.$col; +} + +/** +* Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a +* ptgRef3dV. +* +* @access private +* @param string $cell An Excel cell reference +* @return string The cell in packed() format with the corresponding ptg +*/ +function _convertRef3d($cell) + { + $class = 2; // as far as I know, this is magick. + + // Split the ref at the ! symbol + list($ext_ref, $cell) = split('!', $cell); + + // Convert the external reference part + $ext_ref = $this->_packExtRef($ext_ref); + if ($this->isError($ext_ref)) { + return $ext_ref; + } + + // Convert the cell reference part + list($row, $col) = $this->_cellToPackedRowcol($cell); + + // The ptg value depends on the class of the ptg. + if ($class == 0) { + $ptgRef = pack("C", $this->ptg['ptgRef3d']); + } elseif ($class == 1) { + $ptgRef = pack("C", $this->ptg['ptgRef3dV']); + } elseif ($class == 2) { + $ptgRef = pack("C", $this->ptg['ptgRef3dA']); + } + else { + trigger_error("Unknown class $class", E_USER_ERROR); + } + + return $ptgRef . $ext_ref. $row . $col; +} + +/** +* Convert the sheet name part of an external reference, for example "Sheet1" or +* "Sheet1:Sheet2", to a packed structure. +* +* @access private +* @param string $ext_ref The name of the external reference +* @return string The reference index in packed() format +*/ +function _packExtRef($ext_ref) { + $ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any. + $ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any. + + // Check if there is a sheet range eg., Sheet1:Sheet2. + if (preg_match("/:/", $ext_ref)) + { + list($sheet_name1, $sheet_name2) = split(':', $ext_ref); + + $sheet1 = $this->_getSheetIndex($sheet_name1); + if ($sheet1 == -1) { + trigger_error("Unknown sheet name $sheet_name1 in formula",E_USER_ERROR); + } + $sheet2 = $this->_getSheetIndex($sheet_name2); + if ($sheet2 == -1) { + trigger_error("Unknown sheet name $sheet_name2 in formula",E_USER_ERROR); + } + + // Reverse max and min sheet numbers if necessary + if ($sheet1 > $sheet2) { + list($sheet1, $sheet2) = array($sheet2, $sheet1); + } + } + else { // Single sheet name only. + $sheet1 = $this->_getSheetIndex($ext_ref); + if ($sheet1 == -1) { + trigger_error("Unknown sheet name $ext_ref in formula",E_USER_ERROR); + } + $sheet2 = $sheet1; + } + + // References are stored relative to 0xFFFF. + $offset = -1 - $sheet1; + + return pack('vdvv', $offset, 0x00, $sheet1, $sheet2); +} + +/** +* Look up the index that corresponds to an external sheet name. The hash of +* sheet names is updated by the addworksheet() method of the +* Spreadsheet_Excel_Writer_Workbook class. +* +* @access private +* @return integer +*/ +function _getSheetIndex($sheet_name) + { + if (!isset($this->_ext_sheets[$sheet_name])) { + return -1; + } + else { + return $this->_ext_sheets[$sheet_name]; + } +} + +/** +* This method is used to update the array of sheet names. It is +* called by the addWorksheet() method of the Spreadsheet_Excel_Writer_Workbook class. +* +* @access private +* @param string $name The name of the worksheet being added +* @param integer $index The index of the worksheet being added +*/ +function set_ext_sheet($name, $index) + { + $this->_ext_sheets[$name] = $index; +} + +/** +* pack() row and column into the required 3 byte format. +* +* @access private +* @param string $cell The Excel cell reference to be packed +* @return array Array containing the row and column in packed() format +*/ +function _cellToPackedRowcol($cell) + { + $cell = strtoupper($cell); + list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell); + if ($col >= 256) { + trigger_error("Column in: $cell greater than 255", E_USER_ERROR); + } + if ($row >= 16384) { + trigger_error("Row in: $cell greater than 16384 ", E_USER_ERROR); + } + + // Set the high bits to indicate if row or col are relative. + $row |= $col_rel << 14; + $row |= $row_rel << 15; + + $row = pack('v', $row); + $col = pack('C', $col); + + return array($row, $col); +} + +/** +* pack() row range into the required 3 byte format. +* Just using maximun col/rows, which is probably not the correct solution +* +* @access private +* @param string $range The Excel range to be packed +* @return array Array containing (row1,col1,row2,col2) in packed() format +*/ +function _rangeToPackedRange($range) + { + preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match); + // return absolute rows if there is a $ in the ref + $row1_rel = empty($match[1]) ? 1 : 0; + $row1 = $match[2]; + $row2_rel = empty($match[3]) ? 1 : 0; + $row2 = $match[4]; + // Convert 1-index to zero-index + $row1--; + $row2--; + // Trick poor inocent Excel + $col1 = 0; + $col2 = 16383; // maximum possible value for Excel 5 (change this!!!) + + //list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell); + if (($row1 >= 16384) or ($row2 >= 16384)) { + trigger_error("Row in: $range greater than 16384 ",E_USER_ERROR); + } + + // Set the high bits to indicate if rows are relative. + $row1 |= $row1_rel << 14; + $row2 |= $row2_rel << 15; + + $row1 = pack('v', $row1); + $row2 = pack('v', $row2); + $col1 = pack('C', $col1); + $col2 = pack('C', $col2); + + return array($row1, $col1, $row2, $col2); +} + +/** +* Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero +* indexed row and column number. Also returns two (0,1) values to indicate +* whether the row or column are relative references. +* +* @access private +* @param string $cell The Excel cell reference in A1 format. +* @return array +*/ +function _cellToRowcol($cell) + { + preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match); + // return absolute column if there is a $ in the ref + $col_rel = empty($match[1]) ? 1 : 0; + $col_ref = $match[2]; + $row_rel = empty($match[3]) ? 1 : 0; + $row = $match[4]; + + // Convert base26 column string to a number. + $expn = strlen($col_ref) - 1; + $col = 0; + for ($i=0; $i < strlen($col_ref); $i++) + { + $col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn); + $expn--; + } + + // Convert 1-index to zero-index + $row--; + $col--; + + return array($row, $col, $row_rel, $col_rel); +} + +/** +* Advance to the next valid token. +* +* @access private +*/ +function _advance() + { + $i = $this->_current_char; + // eat up white spaces + if ($i < strlen($this->_formula)) + { + while ($this->_formula{$i} == " ") { + $i++; + } + if ($i < strlen($this->_formula) - 1) { + $this->_lookahead = $this->_formula{$i+1}; + } + $token = ""; + } + while ($i < strlen($this->_formula)) + { + $token .= $this->_formula{$i}; + if ($i < strlen($this->_formula) - 1) { + $this->_lookahead = $this->_formula{$i+1}; + } + else { + $this->_lookahead = ''; + } + if ($this->_match($token) != '') + { + //if ($i < strlen($this->_formula) - 1) { + // $this->_lookahead = $this->_formula{$i+1}; + //} + $this->_current_char = $i + 1; + $this->_current_token = $token; + return 1; + } + if ($i < strlen($this->_formula) - 2) { + $this->_lookahead = $this->_formula{$i+2}; + } + else { + // if we run out of characters _lookahead becomes empty + $this->_lookahead = ''; + } + $i++; + } + //die("Lexical error ".$this->_current_char); +} + +/** +* Checks if it's a valid token. +* +* @access private +* @param mixed $token The token to check. +* @return mixed The checked token or false on failure +*/ +function _match($token) + { + switch($token) + { + case SPREADSHEET_EXCEL_WRITER_ADD: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_SUB: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_MUL: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_DIV: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_OPEN: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_CLOSE: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_SCOLON: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_COMA: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_GT: + if ($this->_lookahead == '=') { // it's a GE token + break; + } + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_LT: + // it's a LE or a NE token + if (($this->_lookahead == '=') or ($this->_lookahead == '>')) { + break; + } + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_GE: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_LE: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_EQ: + return($token); + break; + case SPREADSHEET_EXCEL_WRITER_NE: + return($token); + break; + default: + // if it's a reference + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.') and + ($this->_lookahead != '!')) + { + return $token; + } + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1) + elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-Ia-i]?[A-Za-z][0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead) and + ($this->_lookahead != ':') and ($this->_lookahead != '.')) + { + return $token; + } + // if it's a range (A1:A2) + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // if it's a range (A1..A2) + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's an external range like Sheet1:Sheet2!A1:B2 + elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's an external range like 'Sheet1:Sheet2'!A1:B2 + elseif (preg_match("/^'[A-Za-z0-9_ ]+(\:[A-Za-z0-9_ ]+)?'\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/",$token) and + !preg_match("/[0-9]/",$this->_lookahead)) + { + return $token; + } + // If it's a number (check that it's not a sheet name or range) + elseif (is_numeric($token) and + (!is_numeric($token.$this->_lookahead) or ($this->_lookahead == '')) and + ($this->_lookahead != '!') and ($this->_lookahead != ':')) + { + return $token; + } + // If it's a string (of maximum 255 characters) + elseif (preg_match("/^\"[^\"]{0,255}\"$/",$token)) + { + return $token; + } + // if it's a function call + elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$token) and ($this->_lookahead == "(")) { + return $token; + } + return ''; + } +} + +/** +* The parsing method. It parses a formula. +* +* @access public +* @param string $formula The formula to parse, without the initial equal sign (=). +*/ +function parse($formula) + { + $this->_current_char = 0; + $this->_formula = $formula; + $this->_lookahead = $formula{1}; + $this->_advance(); + $this->_parse_tree = $this->_condition(); + if ($this->isError($this->_parse_tree)) { + return $this->_parse_tree; + } +} + +/** +* It parses a condition. It assumes the following rule: +* Cond -> Expr [(">" | "<") Expr] +* +* @access private +* @return mixed The parsed ptg'd tree +*/ +function _condition() + { + $result = $this->_expression(); + if ($this->isError($result)) { + return $result; + } + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LT) + { + $this->_advance(); + $result2 = $this->_expression(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgLT', $result, $result2); + } + elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GT) +{ + $this->_advance(); + $result2 = $this->_expression(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgGT', $result, $result2); + } + elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LE) +{ + $this->_advance(); + $result2 = $this->_expression(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgLE', $result, $result2); + } + elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GE) +{ + $this->_advance(); + $result2 = $this->_expression(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgGE', $result, $result2); + } + elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_EQ) +{ + $this->_advance(); + $result2 = $this->_expression(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgEQ', $result, $result2); + } + elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_NE) +{ + $this->_advance(); + $result2 = $this->_expression(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgNE', $result, $result2); + } + return $result; +} + +/** +* It parses a expression. It assumes the following rule: +* Expr -> Term [("+" | "-") Term] +* +* @access private +* @return mixed The parsed ptg'd tree +*/ +function _expression() + { + // If it's a string return a string node + if (preg_match("/^\"[^\"]{0,255}\"$/", $this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + $result = $this->_term(); + if ($this->isError($result)) { + return $result; + } + while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) or + ($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB)) + { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) + +{ + $this->_advance(); + $result2 = $this->_term(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgAdd', $result, $result2); + } + else +{ + $this->_advance(); + $result2 = $this->_term(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgSub', $result, $result2); + } + } + return $result; +} + +/** +* This function just introduces a ptgParen element in the tree, so that Excel +* doesn't get confused when working with a parenthesized formula afterwards. +* +* @access private +* @see _fact() +* @return mixed The parsed ptg'd tree +*/ +function _parenthesizedExpression() + { + $result = $this->_createTree('ptgParen', $this->_expression(), ''); + return $result; +} + +/** +* It parses a term. It assumes the following rule: +* Term -> Fact [("*" | "/") Fact] +* +* @access private +* @return mixed The parsed ptg'd tree +*/ +function _term() + { + $result = $this->_fact(); + if ($this->isError($result)) { + return $result; + } + while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) or + ($this->_current_token == SPREADSHEET_EXCEL_WRITER_DIV)) { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) + +{ + $this->_advance(); + $result2 = $this->_fact(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgMul', $result, $result2); + } + else +{ + $this->_advance(); + $result2 = $this->_fact(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('ptgDiv', $result, $result2); + } + } + return $result; +} + +/** +* It parses a factor. It assumes the following rule: +* Fact -> ( Expr ) +* | CellRef +* | CellRange +* | Number +* | Function +* +* @access private +* @return mixed The parsed ptg'd tree +*/ +function _fact() + { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_OPEN) + { + $this->_advance(); // eat the "(" + $result = $this->_parenthesizedExpression(); + if ($this->_current_token != SPREADSHEET_EXCEL_WRITER_CLOSE) { + trigger_error("')' token expected.",E_USER_ERROR); + } + $this->_advance(); // eat the ")" + return $result; + } + if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?[0-9]+$/',$this->_current_token)) + { + // if it's a reference + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-Ia-i]?[A-Za-z][0-9]+$/",$this->_current_token)) + { + // If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1) + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + elseif (preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+:(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$this->_current_token) or + preg_match("/^(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?[0-9]+$/",$this->_current_token)) + { + // if it's a range + $result = $this->_current_token; + $this->_advance(); + return $result; + } + elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/",$this->_current_token)) + { + // If it's an external range (Sheet1!A1:B2) + $result = $this->_current_token; + $this->_advance(); + return $result; + } + elseif (preg_match("/^'[A-Za-z0-9_ ]+(\:[A-Za-z0-9_ ]+)?'\!([A-Ia-i]?[A-Za-z])?[0-9]+:([A-Ia-i]?[A-Za-z])?[0-9]+$/",$this->_current_token)) + { + // If it's an external range ('Sheet1'!A1:B2) + $result = $this->_current_token; + $this->_advance(); + return $result; + } + elseif (is_numeric($this->_current_token)) + { + $result = $this->_createTree($this->_current_token, '', ''); + $this->_advance(); + return $result; + } + elseif (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/i",$this->_current_token)) + { + // if it's a function call + $result = $this->_func(); + return $result; + } + trigger_error("Sintactic error: ".$this->_current_token.", lookahead: ". + $this->_lookahead.", current char: ".$this->_current_char, E_USER_ERROR); +} + +/** +* It parses a function call. It assumes the following rule: +* Func -> ( Expr [,Expr]* ) +* +* @access private +*/ +function _func() + { + $num_args = 0; // number of arguments received + $function = $this->_current_token; + $this->_advance(); + $this->_advance(); // eat the "(" + while ($this->_current_token != ')') + { + if ($num_args > 0) + { + if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_COMA || + $this->_current_token == SPREADSHEET_EXCEL_WRITER_SCOLON) { + $this->_advance(); // eat the "," + } + else { + trigger_error("Sintactic error: coma expected in ". + "function $function, {$num_args}?arg", E_USER_ERROR); + } + $result2 = $this->_condition(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('arg', $result, $result2); + } + else { // first argument + $result2 = $this->_condition(); + if ($this->isError($result2)) { + return $result2; + } + $result = $this->_createTree('arg', '', $result2); + } + $num_args++; + } + $args = $this->_functions[$function][1]; + // If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid. + if (($args >= 0) and ($args != $num_args)) { + trigger_error("Incorrect number of arguments in function $function() ",E_USER_ERROR); + } + + $result = $this->_createTree($function, $result, $num_args); + $this->_advance(); // eat the ")" + return $result; +} + +/** +* Creates a tree. In fact an array which may have one or two arrays (sub-trees) +* as elements. +* +* @access private +* @param mixed $value The value of this node. +* @param mixed $left The left array (sub-tree) or a final node. +* @param mixed $right The right array (sub-tree) or a final node. +*/ +function _createTree($value, $left, $right) + { + return(array('value' => $value, 'left' => $left, 'right' => $right)); +} + +/** +* Builds a string containing the tree in reverse polish notation (What you +* would use in a HP calculator stack). +* The following tree: +* +* + +* / \ +* 2 3 +* +* produces: "23+" +* +* The following tree: +* +* + +* / \ +* 3 * +* / \ +* 6 A1 +* +* produces: "36A1*+" +* +* In fact all operands, functions, references, etc... are written as ptg's +* +* @access public +* @param array $tree The optional tree to convert. +* @return string The tree in reverse polish notation +*/ +function toReversePolish($tree = array()) + { + $polish = ""; // the string we are going to return + if (empty($tree)) { // If it's the first call use _parse_tree + $tree = $this->_parse_tree; + } + if (is_array($tree['left'])) + { + $converted_tree = $this->toReversePolish($tree['left']); + if ($this->isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + elseif ($tree['left'] != '') { // It's a final node + $converted_tree = $this->_convert($tree['left']); + if ($this->isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + if (is_array($tree['right'])) + { + $converted_tree = $this->toReversePolish($tree['right']); + if ($this->isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + elseif ($tree['right'] != '') { // It's a final node + $converted_tree = $this->_convert($tree['right']); + if ($this->isError($converted_tree)) { + return $converted_tree; + } + $polish .= $converted_tree; + } + // if it's a function convert it here (so we can set it's arguments) + if (preg_match("/^[A-Z0-9\xc0-\xdc\.]+$/",$tree['value']) and + !preg_match('/^([A-Ia-i]?[A-Za-z])(\d+)$/',$tree['value']) and + !preg_match("/^[A-Ia-i]?[A-Za-z](\d+)\.\.[A-Ia-i]?[A-Za-z](\d+)$/",$tree['value']) and + !is_numeric($tree['value']) and + !isset($this->ptg[$tree['value']])) + { + // left subtree for a function is always an array. + if ($tree['left'] != '') { + $left_tree = $this->toReversePolish($tree['left']); + } + else { + $left_tree = ''; + } + if ($this->isError($left_tree)) { + return $left_tree; + } + // add it's left subtree and return. + return $left_tree.$this->_convertFunction($tree['value'], $tree['right']); + } + else + { + $converted_tree = $this->_convert($tree['value']); + if ($this->isError($converted_tree)) { + return $converted_tree; + } + } + $polish .= $converted_tree; + return $polish; +} + +} + + +?> diff --git a/lib/Excel/php_writeexcel/class.writeexcel_olewriter.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_olewriter.inc.php new file mode 100644 index 000000000..133a1b501 --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_olewriter.inc.php @@ -0,0 +1,353 @@ +_OLEfilename = $filename; + $this->_filehandle = false; + $this->_fileclosed = 0; + $this->_internal_fh = 0; + $this->_biff_only = 0; + $this->_size_allowed = 0; + $this->_biffsize = 0; + $this->_booksize = 0; + $this->_big_blocks = 0; + $this->_list_blocks = 0; + $this->_root_start = 0; + $this->_block_count = 4; + + $this->_initialize(); + } + + /* + * Check for a valid filename and store the filehandle. + */ + function _initialize() { + $OLEfile = $this->_OLEfilename; + + /* Check for a filename. Workbook.pm will catch this first. */ + if ($OLEfile == '') { + trigger_error("Filename required", E_USER_ERROR); + } + + /* + * If the filename is a resource it is assumed that it is a valid + * filehandle, if not we create a filehandle. + */ + if (is_resource($OLEfile)) { + $fh = $OLEfile; + } else { + // Create a new file, open for writing + $fh = fopen($OLEfile, "wb"); + // The workbook class also checks this but something may have + // happened since then. + if (!$fh) { + trigger_error("Can't open $OLEfile. It may be in use or ". + "protected", E_USER_ERROR); + } + + $this->_internal_fh = 1; + } + + // Store filehandle + $this->_filehandle = $fh; + } + + /* + * Set the size of the data to be written to the OLE stream + * + * $big_blocks = (109 depot block x (128 -1 marker word) + * - (1 x end words)) = 13842 + * $maxsize = $big_blocks * 512 bytes = 7087104 + */ + function set_size($size) { + $maxsize = 7087104; + + if ($size > $maxsize) { + trigger_error("Maximum file size, $maxsize, exceeded. To create ". + "files bigger than this limit please use the ". + "workbookbig class.", E_USER_ERROR); + return ($this->_size_allowed = 0); + } + + $this->_biffsize = $size; + + // Set the min file size to 4k to avoid having to use small blocks + if ($size > 4096) { + $this->_booksize = $size; + } else { + $this->_booksize = 4096; + } + + return ($this->_size_allowed = 1); + } + + /* + * Calculate various sizes needed for the OLE stream + */ + function _calculate_sizes() { + $datasize = $this->_booksize; + + if ($datasize % 512 == 0) { + $this->_big_blocks = $datasize/512; + } else { + $this->_big_blocks = floor($datasize/512)+1; + } + // There are 127 list blocks and 1 marker blocks for each big block + // depot + 1 end of chain block + $this->_list_blocks = floor(($this->_big_blocks)/127)+1; + $this->_root_start = $this->_big_blocks; + + //print $this->_biffsize. "\n"; + //print $this->_big_blocks. "\n"; + //print $this->_list_blocks. "\n"; + } + + /* + * Write root entry, big block list and close the filehandle. + * This method must be called so that the file contents are + * actually written. + */ + function close() { + + if (!$this->_size_allowed) { + return; + } + + if (!$this->_biff_only) { + $this->_write_padding(); + $this->_write_property_storage(); + $this->_write_big_block_depot(); + } + + // Close the filehandle if it was created internally. + if ($this->_internal_fh) { + fclose($this->_filehandle); + } +/* ABR */ + if ($this->_OLEtmpfilename != '') { + $fh = fopen($this->_OLEtmpfilename, "rb"); + if ($fh == false) { + trigger_error("Can't read temporary file.", E_USER_ERROR); + } + fpassthru($fh); + fclose($fh); + unlink($this->_OLEtmpfilename); + }; + + $this->_fileclosed = 1; + } + + /* + * Write BIFF data to OLE file. + */ + function write($data) { + fputs($this->_filehandle, $data); + } + + /* + * Write OLE header block. + */ + function write_header() { + if ($this->_biff_only) { + return; + } + + $this->_calculate_sizes(); + + $root_start = $this->_root_start; + $num_lists = $this->_list_blocks; + + $id = pack("C8", 0xD0, 0xCF, 0x11, 0xE0, + 0xA1, 0xB1, 0x1A, 0xE1); + $unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00); + $unknown2 = pack("vv", 0x3E, 0x03); + $unknown3 = pack("v", -2); + $unknown4 = pack("v", 0x09); + $unknown5 = pack("VVV", 0x06, 0x00, 0x00); + $num_bbd_blocks = pack("V", $num_lists); + $root_startblock = pack("V", $root_start); + $unknown6 = pack("VV", 0x00, 0x1000); + $sbd_startblock = pack("V", -2); + $unknown7 = pack("VVV", 0x00, -2 ,0x00); + $unused = pack("V", -1); + + fputs($this->_filehandle, $id); + fputs($this->_filehandle, $unknown1); + fputs($this->_filehandle, $unknown2); + fputs($this->_filehandle, $unknown3); + fputs($this->_filehandle, $unknown4); + fputs($this->_filehandle, $unknown5); + fputs($this->_filehandle, $num_bbd_blocks); + fputs($this->_filehandle, $root_startblock); + fputs($this->_filehandle, $unknown6); + fputs($this->_filehandle, $sbd_startblock); + fputs($this->_filehandle, $unknown7); + + for ($c=1;$c<=$num_lists;$c++) { + $root_start++; + fputs($this->_filehandle, pack("V", $root_start)); + } + + for ($c=$num_lists;$c<=108;$c++) { + fputs($this->_filehandle, $unused); + } + } + + /* + * Write big block depot. + */ + function _write_big_block_depot() { + $num_blocks = $this->_big_blocks; + $num_lists = $this->_list_blocks; + $total_blocks = $num_lists * 128; + $used_blocks = $num_blocks + $num_lists + 2; + + $marker = pack("V", -3); + $end_of_chain = pack("V", -2); + $unused = pack("V", -1); + + for ($i=1;$i<=($num_blocks-1);$i++) { + fputs($this->_filehandle, pack("V", $i)); + } + + fputs($this->_filehandle, $end_of_chain); + fputs($this->_filehandle, $end_of_chain); + + for ($c=1;$c<=$num_lists;$c++) { + fputs($this->_filehandle, $marker); + } + + for ($c=$used_blocks;$c<=$total_blocks;$c++) { + fputs($this->_filehandle, $unused); + } + } + + /* + * Write property storage. TODO: add summary sheets + */ + function _write_property_storage() { + $rootsize = -2; + $booksize = $this->_booksize; + + // name type dir start size + $this->_write_pps('Root Entry', 0x05, 1, -2, 0x00); + $this->_write_pps('Book', 0x02, -1, 0x00, $booksize); + $this->_write_pps('', 0x00, -1, 0x00, 0x0000); + $this->_write_pps('', 0x00, -1, 0x00, 0x0000); + } + + /* + * Write property sheet in property storage + */ + function _write_pps($name, $type, $dir, $start, $size) { + $names = array(); + $length = 0; + + if ($name != '') { + $name = $name . "\0"; + // Simulate a Unicode string + $chars=preg_split("''", $name, -1, PREG_SPLIT_NO_EMPTY); + foreach ($chars as $char) { + array_push($names, ord($char)); + } + $length = strlen($name) * 2; + } + + $rawname = call_user_func_array('pack', array_merge(array("v*"), $names)); + $zero = pack("C", 0); + + $pps_sizeofname = pack("v", $length); //0x40 + $pps_type = pack("v", $type); //0x42 + $pps_prev = pack("V", -1); //0x44 + $pps_next = pack("V", -1); //0x48 + $pps_dir = pack("V", $dir); //0x4c + + $unknown1 = pack("V", 0); + + $pps_ts1s = pack("V", 0); //0x64 + $pps_ts1d = pack("V", 0); //0x68 + $pps_ts2s = pack("V", 0); //0x6c + $pps_ts2d = pack("V", 0); //0x70 + $pps_sb = pack("V", $start); //0x74 + $pps_size = pack("V", $size); //0x78 + + fputs($this->_filehandle, $rawname); + fputs($this->_filehandle, str_repeat($zero, (64-$length))); + fputs($this->_filehandle, $pps_sizeofname); + fputs($this->_filehandle, $pps_type); + fputs($this->_filehandle, $pps_prev); + fputs($this->_filehandle, $pps_next); + fputs($this->_filehandle, $pps_dir); + fputs($this->_filehandle, str_repeat($unknown1, 5)); + fputs($this->_filehandle, $pps_ts1s); + fputs($this->_filehandle, $pps_ts1d); + fputs($this->_filehandle, $pps_ts2d); + fputs($this->_filehandle, $pps_ts2d); + fputs($this->_filehandle, $pps_sb); + fputs($this->_filehandle, $pps_size); + fputs($this->_filehandle, $unknown1); + } + + /* + * Pad the end of the file + */ + function _write_padding() { + $biffsize = $this->_biffsize; + + if ($biffsize < 4096) { + $min_size = 4096; + } else { + $min_size = 512; + } + + if ($biffsize % $min_size != 0) { + $padding = $min_size - ($biffsize % $min_size); + fputs($this->_filehandle, str_repeat("\0", $padding)); + } + } + +} + +?> diff --git a/lib/Excel/php_writeexcel/class.writeexcel_workbook.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_workbook.inc.php new file mode 100644 index 000000000..11c7c0e2b --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_workbook.inc.php @@ -0,0 +1,1149 @@ +writeexcel_biffwriter(); + + $tmp_format = new writeexcel_format(); + $byte_order = $this->_byte_order; + $parser = new writeexcel_formula($byte_order); + + $this->_filename = $filename; + $this->_parser = $parser; +//? $this->_tempdir = undef; + $this->_1904 = 0; + $this->_activesheet = 0; + $this->_firstsheet = 0; + $this->_selected = 0; + $this->_xf_index = 16; # 15 style XF's and 1 cell XF. + $this->_fileclosed = 0; + $this->_biffsize = 0; + $this->_sheetname = "Sheet"; + $this->_tmp_format = $tmp_format; + $this->_url_format = false; + $this->_codepage = 0x04E4; + $this->_worksheets = array(); + $this->_sheetnames = array(); + $this->_formats = array(); + $this->_palette = array(); + + # Add the default format for hyperlinks + $this->_url_format =& $this->addformat(array('color' => 'blue', 'underline' => 1)); + + # Check for a filename + if ($this->_filename == '') { +//todo: print error + return; + } + + # Try to open the named file and see if it throws any errors. + # If the filename is a reference it is assumed that it is a valid + # filehandle and ignore + # +//todo + + # Set colour palette. + $this->set_palette_xl97(); +} + +############################################################################### +# +# close() +# +# Calls finalization methods and explicitly close the OLEwriter file +# handle. +# +function close() { + # Prevent close() from being called twice. + if ($this->_fileclosed) { + return; + } + + $this->_store_workbook(); + $this->_fileclosed = 1; +} + +//PHPport: method DESTROY deleted + +############################################################################### +# +# sheets() +# +# An accessor for the _worksheets[] array +# +# Returns: a list of the worksheet objects in a workbook +# +function &sheets() { + return $this->_worksheets; +} + +//PHPport: method worksheets deleted: +# This method is now deprecated. Use the sheets() method instead. + +############################################################################### +# +# addworksheet($name) +# +# Add a new worksheet to the Excel workbook. +# TODO: Add accessor for $self->{_sheetname} for international Excel versions. +# +# Returns: reference to a worksheet object +# +function &addworksheet($name="") { + + # Check that sheetname is <= 31 chars (Excel limit). + if (strlen($name) > 31) { + trigger_error("Sheetname $name must be <= 31 chars", E_USER_ERROR); + } + + $index = sizeof($this->_worksheets); + $sheetname = $this->_sheetname; + + if ($name == "") { + $name = $sheetname . ($index+1); + } + + # Check that the worksheet name doesn't already exist: a fatal Excel error. + foreach ($this->_worksheets as $tmp) { + if ($name == $tmp->get_name()) { + trigger_error("Worksheet '$name' already exists", E_USER_ERROR); + } + } + + $worksheet = new writeexcel_worksheet($name, $index, $this->_activesheet, + $this->_firstsheet, + $this->_url_format, $this->_parser, + $this->_tempdir); + + $this->_worksheets[$index] = &$worksheet; # Store ref for iterator + $this->_sheetnames[$index] = $name; # Store EXTERNSHEET names + $this->_parser->set_ext_sheet($name, $index); # Store names in Formula.pm + return $worksheet; +} + +############################################################################### +# +# addformat(%properties) +# +# Add a new format to the Excel workbook. This adds an XF record and +# a FONT record. Also, pass any properties to the Format::new(). +# +function &addformat($para=false) { + if($para===false) { + $format = new writeexcel_format($this->_xf_index); + } else { + $format = new writeexcel_format($this->_xf_index, $para); + } + + $this->_xf_index += 1; + # Store format reference + $this->_formats[]=&$format; + + return $format; +} + +############################################################################### +# +# set_1904() +# +# Set the date system: 0 = 1900 (the default), 1 = 1904 +# +function set_1904($_1904) { + $this->_1904 = $_1904; +} + +############################################################################### +# +# get_1904() +# +# Return the date system: 0 = 1900, 1 = 1904 +# +function get_1904() { + return $this->_1904; +} + +############################################################################### +# +# set_custom_color() +# +# Change the RGB components of the elements in the colour palette. +# +function set_custom_color($index, $red, $green, $blue) { +// todo +/* + # Match a HTML #xxyyzz style parameter + if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) { + @_ = ($_[0], hex $1, hex $2, hex $3); + } +*/ + + $aref = &$this->_palette; + + # Check that the colour index is the right range + if ($index < 8 or $index > 64) { +//todo carp "Color index $index outside range: 8 <= index <= 64"; + return; + } + + # Check that the colour components are in the right range + if ( ($red < 0 || $red > 255) || + ($green < 0 || $green > 255) || + ($blue < 0 || $blue > 255) ) + { +//todo carp "Color component outside range: 0 <= color <= 255"; + return; + } + + $index -=8; # Adjust colour index (wingless dragonfly) + + # Set the RGB value + $aref[$index] = array($red, $green, $blue, 0); + + return $index +8; +} + +############################################################################### +# +# set_palette_xl97() +# +# Sets the colour palette to the Excel 97+ default. +# +function set_palette_xl97() { + $this->_palette = array( + array(0x00, 0x00, 0x00, 0x00), # 8 + array(0xff, 0xff, 0xff, 0x00), # 9 + array(0xff, 0x00, 0x00, 0x00), # 10 + array(0x00, 0xff, 0x00, 0x00), # 11 + array(0x00, 0x00, 0xff, 0x00), # 12 + array(0xff, 0xff, 0x00, 0x00), # 13 + array(0xff, 0x00, 0xff, 0x00), # 14 + array(0x00, 0xff, 0xff, 0x00), # 15 + array(0x80, 0x00, 0x00, 0x00), # 16 + array(0x00, 0x80, 0x00, 0x00), # 17 + array(0x00, 0x00, 0x80, 0x00), # 18 + array(0x80, 0x80, 0x00, 0x00), # 19 + array(0x80, 0x00, 0x80, 0x00), # 20 + array(0x00, 0x80, 0x80, 0x00), # 21 + array(0xc0, 0xc0, 0xc0, 0x00), # 22 + array(0x80, 0x80, 0x80, 0x00), # 23 + array(0x99, 0x99, 0xff, 0x00), # 24 + array(0x99, 0x33, 0x66, 0x00), # 25 + array(0xff, 0xff, 0xcc, 0x00), # 26 + array(0xcc, 0xff, 0xff, 0x00), # 27 + array(0x66, 0x00, 0x66, 0x00), # 28 + array(0xff, 0x80, 0x80, 0x00), # 29 + array(0x00, 0x66, 0xcc, 0x00), # 30 + array(0xcc, 0xcc, 0xff, 0x00), # 31 + array(0x00, 0x00, 0x80, 0x00), # 32 + array(0xff, 0x00, 0xff, 0x00), # 33 + array(0xff, 0xff, 0x00, 0x00), # 34 + array(0x00, 0xff, 0xff, 0x00), # 35 + array(0x80, 0x00, 0x80, 0x00), # 36 + array(0x80, 0x00, 0x00, 0x00), # 37 + array(0x00, 0x80, 0x80, 0x00), # 38 + array(0x00, 0x00, 0xff, 0x00), # 39 + array(0x00, 0xcc, 0xff, 0x00), # 40 + array(0xcc, 0xff, 0xff, 0x00), # 41 + array(0xcc, 0xff, 0xcc, 0x00), # 42 + array(0xff, 0xff, 0x99, 0x00), # 43 + array(0x99, 0xcc, 0xff, 0x00), # 44 + array(0xff, 0x99, 0xcc, 0x00), # 45 + array(0xcc, 0x99, 0xff, 0x00), # 46 + array(0xff, 0xcc, 0x99, 0x00), # 47 + array(0x33, 0x66, 0xff, 0x00), # 48 + array(0x33, 0xcc, 0xcc, 0x00), # 49 + array(0x99, 0xcc, 0x00, 0x00), # 50 + array(0xff, 0xcc, 0x00, 0x00), # 51 + array(0xff, 0x99, 0x00, 0x00), # 52 + array(0xff, 0x66, 0x00, 0x00), # 53 + array(0x66, 0x66, 0x99, 0x00), # 54 + array(0x96, 0x96, 0x96, 0x00), # 55 + array(0x00, 0x33, 0x66, 0x00), # 56 + array(0x33, 0x99, 0x66, 0x00), # 57 + array(0x00, 0x33, 0x00, 0x00), # 58 + array(0x33, 0x33, 0x00, 0x00), # 59 + array(0x99, 0x33, 0x00, 0x00), # 60 + array(0x99, 0x33, 0x66, 0x00), # 61 + array(0x33, 0x33, 0x99, 0x00), # 62 + array(0x33, 0x33, 0x33, 0x00), # 63 + ); + + return 0; +} + +############################################################################### +# +# set_palette_xl5() +# +# Sets the colour palette to the Excel 5 default. +# +function set_palette_xl5() { + $this->_palette = array( + array(0x00, 0x00, 0x00, 0x00), # 8 + array(0xff, 0xff, 0xff, 0x00), # 9 + array(0xff, 0x00, 0x00, 0x00), # 10 + array(0x00, 0xff, 0x00, 0x00), # 11 + array(0x00, 0x00, 0xff, 0x00), # 12 + array(0xff, 0xff, 0x00, 0x00), # 13 + array(0xff, 0x00, 0xff, 0x00), # 14 + array(0x00, 0xff, 0xff, 0x00), # 15 + array(0x80, 0x00, 0x00, 0x00), # 16 + array(0x00, 0x80, 0x00, 0x00), # 17 + array(0x00, 0x00, 0x80, 0x00), # 18 + array(0x80, 0x80, 0x00, 0x00), # 19 + array(0x80, 0x00, 0x80, 0x00), # 20 + array(0x00, 0x80, 0x80, 0x00), # 21 + array(0xc0, 0xc0, 0xc0, 0x00), # 22 + array(0x80, 0x80, 0x80, 0x00), # 23 + array(0x80, 0x80, 0xff, 0x00), # 24 + array(0x80, 0x20, 0x60, 0x00), # 25 + array(0xff, 0xff, 0xc0, 0x00), # 26 + array(0xa0, 0xe0, 0xe0, 0x00), # 27 + array(0x60, 0x00, 0x80, 0x00), # 28 + array(0xff, 0x80, 0x80, 0x00), # 29 + array(0x00, 0x80, 0xc0, 0x00), # 30 + array(0xc0, 0xc0, 0xff, 0x00), # 31 + array(0x00, 0x00, 0x80, 0x00), # 32 + array(0xff, 0x00, 0xff, 0x00), # 33 + array(0xff, 0xff, 0x00, 0x00), # 34 + array(0x00, 0xff, 0xff, 0x00), # 35 + array(0x80, 0x00, 0x80, 0x00), # 36 + array(0x80, 0x00, 0x00, 0x00), # 37 + array(0x00, 0x80, 0x80, 0x00), # 38 + array(0x00, 0x00, 0xff, 0x00), # 39 + array(0x00, 0xcf, 0xff, 0x00), # 40 + array(0x69, 0xff, 0xff, 0x00), # 41 + array(0xe0, 0xff, 0xe0, 0x00), # 42 + array(0xff, 0xff, 0x80, 0x00), # 43 + array(0xa6, 0xca, 0xf0, 0x00), # 44 + array(0xdd, 0x9c, 0xb3, 0x00), # 45 + array(0xb3, 0x8f, 0xee, 0x00), # 46 + array(0xe3, 0xe3, 0xe3, 0x00), # 47 + array(0x2a, 0x6f, 0xf9, 0x00), # 48 + array(0x3f, 0xb8, 0xcd, 0x00), # 49 + array(0x48, 0x84, 0x36, 0x00), # 50 + array(0x95, 0x8c, 0x41, 0x00), # 51 + array(0x8e, 0x5e, 0x42, 0x00), # 52 + array(0xa0, 0x62, 0x7a, 0x00), # 53 + array(0x62, 0x4f, 0xac, 0x00), # 54 + array(0x96, 0x96, 0x96, 0x00), # 55 + array(0x1d, 0x2f, 0xbe, 0x00), # 56 + array(0x28, 0x66, 0x76, 0x00), # 57 + array(0x00, 0x45, 0x00, 0x00), # 58 + array(0x45, 0x3e, 0x01, 0x00), # 59 + array(0x6a, 0x28, 0x13, 0x00), # 60 + array(0x85, 0x39, 0x6a, 0x00), # 61 + array(0x4a, 0x32, 0x85, 0x00), # 62 + array(0x42, 0x42, 0x42, 0x00), # 63 + ); + + return 0; +} + +############################################################################### +# +# set_tempdir() +# +# Change the default temp directory used by _initialize() in Worksheet.pm. +# +function set_tempdir($tempdir) { +//todo +/* + croak "$_[0] is not a valid directory" unless -d $_[0]; + croak "set_tempdir must be called before addworksheet" if $self->sheets(); +*/ + + $this->_tempdir = $tempdir; +} + +############################################################################### +# +# set_codepage() +# +# See also the _store_codepage method. This is used to store the code page, i.e. +# the character set used in the workbook. +# +function set_codepage($cp) { + + if($cp==1) + $codepage = 0x04E4; + else if($cp==2) + $codepage = 0x8000; + if($codepage) + $this->_codepage = $codepage; +} + + +############################################################################### +# +# _store_workbook() +# +# Assemble worksheets into a workbook and send the BIFF data to an OLE +# storage. +# +function _store_workbook() { + + # Ensure that at least one worksheet has been selected. + if ($this->_activesheet == 0) { + $this->_worksheets[0]->_selected = 1; + } + + # Calculate the number of selected worksheet tabs and call the finalization + # methods for each worksheet + for ($c=0;$c_worksheets);$c++) { + $sheet=&$this->_worksheets[$c]; + if ($sheet->_selected) { + $this->_selected++; + } + $sheet->_close($this->_sheetnames); + } + + # Add Workbook globals + $this->_store_bof(0x0005); + + $this->_store_externs(); # For print area and repeat rows + + $this->_store_names(); # For print area and repeat rows + + $this->_store_codepage(); + + $this->_store_window1(); + + $this->_store_1904(); + + $this->_store_all_fonts(); + + $this->_store_all_num_formats(); + + $this->_store_all_xfs(); + + $this->_store_all_styles(); + + $this->_store_palette(); + + $this->_calc_sheet_offsets(); + + # Add BOUNDSHEET records + for ($c=0;$c_worksheets);$c++) { + $sheet=&$this->_worksheets[$c]; + $this->_store_boundsheet($sheet->_name, $sheet->_offset); + } + + # End Workbook globals + $this->_store_eof(); + + # Store the workbook in an OLE container + $this->_store_OLE_file(); +} + +############################################################################### +# +# _store_OLE_file() +# +# Store the workbook in an OLE container if the total size of the workbook data +# is less than ~ 7MB. +# +function _store_OLE_file() { +## ABR + if ($this->_tmpfilename != '') { + $OLE = new writeexcel_olewriter('/tmp/'.$this->_tmpfilename); + $OLE->_OLEtmpfilename = '/tmp/'.$this->_tmpfilename; + } else { + $OLE = new writeexcel_olewriter($this->_filename); + $OLE->_OLEtmpfilename = ''; + }; +## END ABR + + # Write Worksheet data if data <~ 7MB + if ($OLE->set_size($this->_biffsize)) { + $OLE->write_header(); + $OLE->write($this->_data); + + for ($c=0;$c_worksheets);$c++) { + $sheet=&$this->_worksheets[$c]; + while ($tmp = $sheet->get_data()) { + $OLE->write($tmp); + } + $sheet->cleanup(); + } + } + + $OLE->close(); +} + +############################################################################### +# +# _calc_sheet_offsets() +# +# Calculate offsets for Worksheet BOF records. +# +function _calc_sheet_offsets() { + + $BOF = 11; + $EOF = 4; + $offset = $this->_datasize; + + foreach ($this->_worksheets as $sheet) { + $offset += $BOF + strlen($sheet->_name); + } + + $offset += $EOF; + + for ($c=0;$c_worksheets);$c++) { + $sheet=&$this->_worksheets[$c]; + $sheet->_offset = $offset; + $offset += $sheet->_datasize; + } + + $this->_biffsize = $offset; +} + +############################################################################### +# +# _store_all_fonts() +# +# Store the Excel FONT records. +# +function _store_all_fonts() { + # _tmp_format is added by new(). We use this to write the default XF's + $format = $this->_tmp_format; + $font = $format->get_font(); + + # Note: Fonts are 0-indexed. According to the SDK there is no index 4, + # so the following fonts are 0, 1, 2, 3, 5 + # + for ($c=0;$c<5;$c++) { + $this->_append($font); + } + + # Iterate through the XF objects and write a FONT record if it isn't the + # same as the default FONT and if it hasn't already been used. + # + $index = 6; # The first user defined FONT + + $key = $format->get_font_key(); # The default font from _tmp_format + $fonts[$key] = 0; # Index of the default font + + for ($c=0;$c_formats);$c++) { + $format=&$this->_formats[$c]; + + $key = $format->get_font_key(); + + if (isset($fonts[$key])) { + # FONT has already been used + $format->_font_index = $fonts[$key]; + } else { + # Add a new FONT record + $fonts[$key] = $index; + $format->_font_index = $index; + $index++; + $font = $format->get_font(); + $this->_append($font); + } + } +} + +############################################################################### +# +# _store_all_num_formats() +# +# Store user defined numerical formats i.e. FORMAT records +# +function _store_all_num_formats() { + + # Leaning num_format syndrome + $num_formats_list=array(); + $index = 164; + + # Iterate through the XF objects and write a FORMAT record if it isn't a + # built-in format type and if the FORMAT string hasn't already been used. + # + + for ($c=0;$c_formats);$c++) { + $format=&$this->_formats[$c]; + + $num_format = $format->_num_format; + + # Check if $num_format is an index to a built-in format. + # Also check for a string of zeros, which is a valid format string + # but would evaluate to zero. + # + if (!preg_match('/^0+\d/', $num_format)) { + if (preg_match('/^\d+$/', $num_format)) { + # built-in + continue; + } + } + + if (isset($num_formats[$num_format])) { + # FORMAT has already been used + $format->_num_format = $num_formats[$num_format]; + } else { + # Add a new FORMAT + $num_formats[$num_format] = $index; + $format->_num_format = $index; + array_push($num_formats_list, $num_format); + $index++; + } + } + + # Write the new FORMAT records starting from 0xA4 + $index = 164; + foreach ($num_formats_list as $num_format) { + $this->_store_num_format($num_format, $index); + $index++; + } +} + +############################################################################### +# +# _store_all_xfs() +# +# Write all XF records. +# +function _store_all_xfs() { + # _tmp_format is added by new(). We use this to write the default XF's + # The default font index is 0 + # + $format = $this->_tmp_format; + $xf; + + for ($c=0;$c<15;$c++) { + $xf = $format->get_xf('style'); # Style XF + $this->_append($xf); + } + + $xf = $format->get_xf('cell'); # Cell XF + $this->_append($xf); + + # User defined XFs + foreach ($this->_formats as $format) { + $xf = $format->get_xf('cell'); + $this->_append($xf); + } +} + +############################################################################### +# +# _store_all_styles() +# +# Write all STYLE records. +# +function _store_all_styles() { + $this->_store_style(); +} + +############################################################################### +# +# _store_externs() +# +# Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for +# the NAME records. +# +function _store_externs() { + + # Create EXTERNCOUNT with number of worksheets + $this->_store_externcount(sizeof($this->_worksheets)); + + # Create EXTERNSHEET for each worksheet + foreach ($this->_sheetnames as $sheetname) { + $this->_store_externsheet($sheetname); + } +} + +############################################################################### +# +# _store_names() +# +# Write the NAME record to define the print area and the repeat rows and cols. +# +function _store_names() { + + # Create the print area NAME records + foreach ($this->_worksheets as $worksheet) { + # Write a Name record if the print area has been defined + if ($worksheet->_print_rowmin!==false) { + $this->_store_name_short( + $worksheet->_index, + 0x06, # NAME type + $worksheet->_print_rowmin, + $worksheet->_print_rowmax, + $worksheet->_print_colmin, + $worksheet->_print_colmax + ); + } + } + + # Create the print title NAME records + foreach ($this->_worksheets as $worksheet) { + + $rowmin = $worksheet->_title_rowmin; + $rowmax = $worksheet->_title_rowmax; + $colmin = $worksheet->_title_colmin; + $colmax = $worksheet->_title_colmax; + + # Determine if row + col, row, col or nothing has been defined + # and write the appropriate record + # + if ($rowmin!==false && $colmin!==false) { + # Row and column titles have been defined. + # Row title has been defined. + $this->_store_name_long( + $worksheet->_index, + 0x07, # NAME type + $rowmin, + $rowmax, + $colmin, + $colmax + ); + } elseif ($rowmin!==false) { + # Row title has been defined. + $this->_store_name_short( + $worksheet->_index, + 0x07, # NAME type + $rowmin, + $rowmax, + 0x00, + 0xff + ); + } elseif ($colmin!==false) { + # Column title has been defined. + $this->_store_name_short( + $worksheet->_index, + 0x07, # NAME type + 0x0000, + 0x3fff, + $colmin, + $colmax + ); + } else { + # Print title hasn't been defined. + } + } +} + +############################################################################### +############################################################################### +# +# BIFF RECORDS +# + +############################################################################### +# +# _store_window1() +# +# Write Excel BIFF WINDOW1 record. +# +function _store_window1() { + + $record = 0x003D; # Record identifier + $length = 0x0012; # Number of bytes to follow + + $xWn = 0x0000; # Horizontal position of window + $yWn = 0x0000; # Vertical position of window + $dxWn = 0x25BC; # Width of window + $dyWn = 0x1572; # Height of window + + $grbit = 0x0038; # Option flags + $ctabsel = $this->_selected; # Number of workbook tabs selected + $wTabRatio = 0x0258; # Tab to scrollbar ratio + + $itabFirst = $this->_firstsheet; # 1st displayed worksheet + $itabCur = $this->_activesheet; # Active worksheet + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn, + $grbit, + $itabCur, $itabFirst, + $ctabsel, $wTabRatio); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_boundsheet() +# +# Writes Excel BIFF BOUNDSHEET record. +# +function _store_boundsheet($sheetname, $offset) { + $record = 0x0085; # Record identifier + $length = 0x07 + strlen($sheetname); # Number of bytes to follow + + //$sheetname = $_[0]; # Worksheet name + //$offset = $_[1]; # Location of worksheet BOF + $grbit = 0x0000; # Sheet identifier + $cch = strlen($sheetname); # Length of sheet name + + $header = pack("vv", $record, $length); + $data = pack("VvC", $offset, $grbit, $cch); + + $this->_append($header . $data . $sheetname); +} + +############################################################################### +# +# _store_style() +# +# Write Excel BIFF STYLE records. +# +function _store_style() { + $record = 0x0293; # Record identifier + $length = 0x0004; # Bytes to follow + + $ixfe = 0x8000; # Index to style XF + $BuiltIn = 0x00; # Built-in style + $iLevel = 0xff; # Outline style level + + $header = pack("vv", $record, $length); + $data = pack("vCC", $ixfe, $BuiltIn, $iLevel); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_num_format() +# +# Writes Excel FORMAT record for non "built-in" numerical formats. +# +function _store_num_format($num_format, $index) { + $record = 0x041E; # Record identifier + $length = 0x03 + strlen($num_format); # Number of bytes to follow + + $format = $num_format; # Custom format string + $ifmt = $index; # Format index code + $cch = strlen($format); # Length of format string + + $header = pack("vv", $record, $length); + $data = pack("vC", $ifmt, $cch); + + $this->_append($header . $data . $format); +} + +############################################################################### +# +# _store_1904() +# +# Write Excel 1904 record to indicate the date system in use. +# +function _store_1904() { + $record = 0x0022; # Record identifier + $length = 0x0002; # Bytes to follow + + $f1904 = $this->_1904; # Flag for 1904 date system + + $header = pack("vv", $record, $length); + $data = pack("v", $f1904); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_externcount($count) +# +# Write BIFF record EXTERNCOUNT to indicate the number of external sheet +# references in the workbook. +# +# Excel only stores references to external sheets that are used in NAME. +# The workbook NAME record is required to define the print area and the repeat +# rows and columns. +# +# A similar method is used in Worksheet.pm for a slightly different purpose. +# +function _store_externcount($par0) { + $record = 0x0016; # Record identifier + $length = 0x0002; # Number of bytes to follow + + $cxals = $par0; # Number of external references + + $header = pack("vv", $record, $length); + $data = pack("v", $cxals); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_externsheet($sheetname) +# +# +# Writes the Excel BIFF EXTERNSHEET record. These references are used by +# formulas. NAME record is required to define the print area and the repeat +# rows and columns. +# +# A similar method is used in Worksheet.pm for a slightly different purpose. +# +function _store_externsheet($par0) { + $record = 0x0017; # Record identifier + $length = 0x02 + strlen($par0); # Number of bytes to follow + + $sheetname = $par0; # Worksheet name + $cch = strlen($sheetname); # Length of sheet name + $rgch = 0x03; # Filename encoding + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + + $this->_append($header . $data . $sheetname); +} + +############################################################################### +# +# _store_name_short() +# +# +# Store the NAME record in the short format that is used for storing the print +# area, repeat rows only and repeat columns only. +# +function _store_name_short($par0, $par1, $par2, $par3, $par4, $par5) { + $record = 0x0018; # Record identifier + $length = 0x0024; # Number of bytes to follow + + $index = $par0; # Sheet index + $type = $par1; + + $grbit = 0x0020; # Option flags + $chKey = 0x00; # Keyboard shortcut + $cch = 0x01; # Length of text name + $cce = 0x0015; # Length of text definition + $ixals = $index +1; # Sheet index + $itab = $ixals; # Equal to ixals + $cchCustMenu = 0x00; # Length of cust menu text + $cchDescription = 0x00; # Length of description text + $cchHelptopic = 0x00; # Length of help topic text + $cchStatustext = 0x00; # Length of status bar text + $rgch = $type; # Built-in name type + + $unknown03 = 0x3b; + $unknown04 = 0xffff-$index; + $unknown05 = 0x0000; + $unknown06 = 0x0000; + $unknown07 = 0x1087; + $unknown08 = 0x8005; + + $rowmin = $par2; # Start row + $rowmax = $par3; # End row + $colmin = $par4; # Start column + $colmax = $par5; # end column + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $data .= pack("C", $chKey); + $data .= pack("C", $cch); + $data .= pack("v", $cce); + $data .= pack("v", $ixals); + $data .= pack("v", $itab); + $data .= pack("C", $cchCustMenu); + $data .= pack("C", $cchDescription); + $data .= pack("C", $cchHelptopic); + $data .= pack("C", $cchStatustext); + $data .= pack("C", $rgch); + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", $rowmin); + $data .= pack("v", $rowmax); + $data .= pack("C", $colmin); + $data .= pack("C", $colmax); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_name_long() +# +# +# Store the NAME record in the long format that is used for storing the repeat +# rows and columns when both are specified. This share a lot of code with +# _store_name_short() but we use a separate method to keep the code clean. +# Code abstraction for reuse can be carried too far, and I should know. ;-) +# +function _store_name_long($par0, $par1, $par2, $par3, $par4, $par5) { + $record = 0x0018; # Record identifier + $length = 0x003d; # Number of bytes to follow + + $index = $par0; # Sheet index + $type = $par1; + + $grbit = 0x0020; # Option flags + $chKey = 0x00; # Keyboard shortcut + $cch = 0x01; # Length of text name + $cce = 0x002e; # Length of text definition + $ixals = $index +1; # Sheet index + $itab = $ixals; # Equal to ixals + $cchCustMenu = 0x00; # Length of cust menu text + $cchDescription = 0x00; # Length of description text + $cchHelptopic = 0x00; # Length of help topic text + $cchStatustext = 0x00; # Length of status bar text + $rgch = $type; # Built-in name type + + $unknown01 = 0x29; + $unknown02 = 0x002b; + $unknown03 = 0x3b; + $unknown04 = 0xffff-$index; + $unknown05 = 0x0000; + $unknown06 = 0x0000; + $unknown07 = 0x1087; + $unknown08 = 0x8008; + + $rowmin = $par2; # Start row + $rowmax = $par3; # End row + $colmin = $par4; # Start column + $colmax = $par5; # end column + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + $data .= pack("C", $chKey); + $data .= pack("C", $cch); + $data .= pack("v", $cce); + $data .= pack("v", $ixals); + $data .= pack("v", $itab); + $data .= pack("C", $cchCustMenu); + $data .= pack("C", $cchDescription); + $data .= pack("C", $cchHelptopic); + $data .= pack("C", $cchStatustext); + $data .= pack("C", $rgch); + $data .= pack("C", $unknown01); + $data .= pack("v", $unknown02); + # Column definition + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", 0x0000); + $data .= pack("v", 0x3fff); + $data .= pack("C", $colmin); + $data .= pack("C", $colmax); + # Row definition + $data .= pack("C", $unknown03); + $data .= pack("v", $unknown04); + $data .= pack("v", $unknown05); + $data .= pack("v", $unknown06); + $data .= pack("v", $unknown07); + $data .= pack("v", $unknown08); + $data .= pack("v", $index); + $data .= pack("v", $index); + $data .= pack("v", $rowmin); + $data .= pack("v", $rowmax); + $data .= pack("C", 0x00); + $data .= pack("C", 0xff); + # End of data + $data .= pack("C", 0x10); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_palette() +# +# Stores the PALETTE biff record. +# +function _store_palette() { + $aref = &$this->_palette; + + $record = 0x0092; # Record identifier + $length = 2 + 4 * sizeof($aref); # Number of bytes to follow + $ccv = sizeof($aref); # Number of RGB values to follow + //$data; # The RGB data + + # Pack the RGB data + foreach($aref as $dat) { + $data .= call_user_func_array('pack', array_merge(array("CCCC"), $dat)); + } + + $header = pack("vvv", $record, $length, $ccv); + + $this->_append($header . $data); +} + +############################################################################### +# +# _store_codepage() +# +# Stores the CODEPAGE biff record. +# +function _store_codepage() { + + $record = 0x0042; # Record identifier + $length = 0x0002; # Number of bytes to follow + $cv = $this->_codepage; # The code page + + $header = pack("vv", $record, $length); + $data = pack("v", $cv); + + $this->_append($header.$data); +} + +} + +?> diff --git a/lib/Excel/php_writeexcel/class.writeexcel_workbookbig.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_workbookbig.inc.php new file mode 100644 index 000000000..3fb2f61d1 --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_workbookbig.inc.php @@ -0,0 +1,55 @@ +writeexcel_workbook($filename); + } + + function _store_OLE_file() { + $file=new ole_pps_file(asc2ucs("Book")); + $file->append($this->_data); + + for ($c=0;$c_worksheets);$c++) { + $worksheet=&$this->_worksheets[$c]; + while ($data=$worksheet->get_data()) { + $file->append($data); + } + $worksheet->cleanup(); + } + + $ole=new ole_pps_root(false, false, array($file)); + $ole->save($this->_filename); + } + +} + +?> diff --git a/lib/Excel/php_writeexcel/class.writeexcel_worksheet.inc.php b/lib/Excel/php_writeexcel/class.writeexcel_worksheet.inc.php new file mode 100644 index 000000000..9860cbf7e --- /dev/null +++ b/lib/Excel/php_writeexcel/class.writeexcel_worksheet.inc.php @@ -0,0 +1,2968 @@ +writeexcel_biffwriter(); + + $rowmax = 65536; // 16384 in Excel 5 + $colmax = 256; + $strmax = 255; + + $this->_name = $name; + $this->_index = $index; + $this->_activesheet = &$activesheet; + $this->_firstsheet = &$firstsheet; + $this->_url_format = &$url_format; + $this->_parser = &$parser; + $this->_tempdir = $tempdir; + + $this->_ext_sheets = array(); + $this->_using_tmpfile = 1; + $this->_tmpfilename = false; + $this->_filehandle = false; + $this->_fileclosed = 0; + $this->_offset = 0; + $this->_xls_rowmax = $rowmax; + $this->_xls_colmax = $colmax; + $this->_xls_strmax = $strmax; + $this->_dim_rowmin = $rowmax +1; + $this->_dim_rowmax = 0; + $this->_dim_colmin = $colmax +1; + $this->_dim_colmax = 0; + $this->_colinfo = array(); + $this->_selection = array(0, 0); + $this->_panes = array(); + $this->_active_pane = 3; + $this->_frozen = 0; + $this->_selected = 0; + + $this->_paper_size = 0x0; + $this->_orientation = 0x1; + $this->_header = ''; + $this->_footer = ''; + $this->_hcenter = 0; + $this->_vcenter = 0; + $this->_margin_head = 0.50; + $this->_margin_foot = 0.50; + $this->_margin_left = 0.75; + $this->_margin_right = 0.75; + $this->_margin_top = 1.00; + $this->_margin_bottom = 1.00; + + $this->_title_rowmin = false; + $this->_title_rowmax = false; + $this->_title_colmin = false; + $this->_title_colmax = false; + $this->_print_rowmin = false; + $this->_print_rowmax = false; + $this->_print_colmin = false; + $this->_print_colmax = false; + + $this->_print_gridlines = 1; + $this->_screen_gridlines = 1; + $this->_print_headers = 0; + + $this->_fit_page = 0; + $this->_fit_width = 0; + $this->_fit_height = 0; + + $this->_hbreaks = array(); + $this->_vbreaks = array(); + + $this->_protect = 0; + $this->_password = false; + + $this->_col_sizes = array(); + $this->_row_sizes = array(); + + $this->_col_formats = array(); + $this->_row_formats = array(); + + $this->_zoom = 100; + $this->_print_scale = 100; + + $this->_initialize(); + } + +############################################################################### +# +# _initialize() +# +# Open a tmp file to store the majority of the Worksheet data. If this fails, +# for example due to write permissions, store the data in memory. This can be +# slow for large files. +# +function _initialize() { + + # Open tmp file for storing Worksheet data. + $this->_tmpfilename=tempnam($this->_tempdir, "php_writeexcel"); + $fh=fopen($this->_tmpfilename, "w+b"); + + if ($fh) { + # Store filehandle + $this->_filehandle = $fh; + } else { + # If tempfile() failed store data in memory + $this->_using_tmpfile = 0; + $this->_tmpfilename=false; + + if ($this->_index == 0) { + $dir = $this->_tempdir; + +//todo warn "Unable to create temp files in $dir. Refer to set_tempdir()". +// " in the Spreadsheet::WriteExcel documentation.\n" ; + } + } +} + + /* + * Add data to the beginning of the workbook (note the reverse order) + * and to the end of the workbook. + */ + function _close($sheetnames) { + + /////////////////////////////// + // Prepend in reverse order!! + // + + $this->_store_dimensions(); // Prepend the sheet dimensions + $this->_store_password(); // Prepend the sheet password + $this->_store_protect(); // Prepend the sheet protection + $this->_store_setup(); // Prepend the page setup + $this->_store_margin_bottom(); // Prepend the bottom margin + $this->_store_margin_top(); // Prepend the top margin + $this->_store_margin_right(); // Prepend the right margin + $this->_store_margin_left(); // Prepend the left margin + $this->_store_vcenter(); // Prepend the page vertical + // centering + $this->_store_hcenter(); // Prepend the page horizontal + // centering + $this->_store_footer(); // Prepend the page footer + $this->_store_header(); // Prepend the page header + $this->_store_vbreak(); // Prepend the vertical page breaks + $this->_store_hbreak(); // Prepend the horizontal + // page breaks + $this->_store_wsbool(); // Prepend WSBOOL + $this->_store_gridset(); // Prepend GRIDSET + $this->_store_print_gridlines(); // Prepend PRINTGRIDLINES + $this->_store_print_headers(); // Prepend PRINTHEADERS + + // Prepend EXTERNSHEET references + $num_sheets = sizeof($sheetnames); + for ($i = $num_sheets; $i > 0; $i--) { + $sheetname = $sheetnames[$i-1]; + $this->_store_externsheet($sheetname); + } + + $this->_store_externcount($num_sheets); // Prepend the EXTERNCOUNT + // of external references. + + // Prepend the COLINFO records if they exist + if (sizeof($this->_colinfo)>0){ + while (sizeof($this->_colinfo)>0) { + $arrayref = array_pop ($this->_colinfo); + $this->_store_colinfo($arrayref); + } + $this->_store_defcol(); + } + + $this->_store_bof(0x0010); // Prepend the BOF record + + // + // End of prepend. Read upwards from here. + //////////////////////////////////////////// + + // Append + $this->_store_window2(); + $this->_store_zoom(); + + if (sizeof($this->_panes)>0) { + $this->_store_panes($this->_panes); + } + + $this->_store_selection($this->_selection); + $this->_store_eof(); + } + + /* + * Retrieve the worksheet name. + */ + function get_name() { + return $this->_name; + } + +############################################################################### +# +# get_data(). +# +# Retrieves data from memory in one chunk, or from disk in $buffer +# sized chunks. +# +function get_data() { + + $buffer = 4096; + + # Return data stored in memory + if ($this->_data!==false) { + $tmp=$this->_data; + $this->_data=false; + + // The next data comes from the temporary file, so prepare + // it by putting the file pointer to the beginning + if ($this->_using_tmpfile) { + fseek($this->_filehandle, 0, SEEK_SET); + } + + if ($this->_debug) { + print "*** worksheet::get_data() called (1):"; + for ($c=0;$c_using_tmpfile) { + if ($tmp=fread($this->_filehandle, $buffer)) { + + if ($this->_debug) { + print "*** worksheet::get_data() called (2):"; + for ($c=0;$c_using_tmpfile) { + fclose($this->_filehandle); + unlink($this->_tmpfilename); + $this->_tmpfilename=false; + $this->_using_tmpfile=false; + } + } + + /* + * Set this worksheet as a selected worksheet, i.e. the worksheet has + * its tab highlighted. + */ + function select() { + $this->_selected = 1; + } + + /* + * Set this worksheet as the active worksheet, i.e. the worksheet + * that is displayed when the workbook is opened. Also set it as + * selected. + */ + function activate() { + $this->_selected = 1; + $this->_activesheet = $this->_index; + } + + /* + * Set this worksheet as the first visible sheet. This is necessary + * when there are a large number of worksheets and the activated + * worksheet is not visible on the screen. + */ + function set_first_sheet() { + $this->_firstsheet = $this->_index; + } + + /* + * Set the worksheet protection flag to prevent accidental modification + * and to hide formulas if the locked and hidden format properties have + * been set. + */ + function protect($password) { + $this->_protect = 1; + $this->_password = $this->_encode_password($password); + } + +############################################################################### +# +# set_column($firstcol, $lastcol, $width, $format, $hidden) +# +# Set the width of a single column or a range of column. +# See also: _store_colinfo +# +function set_column() { + + $_=func_get_args(); + + $cell = $_[0]; + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $cell)) { + $_ = $this->_substitute_cellref($_); + } + + array_push($this->_colinfo, $_); + + # Store the col sizes for use when calculating image vertices taking + # hidden columns into account. Also store the column formats. + # + if (sizeof($_)<3) { + # Ensure at least $firstcol, $lastcol and $width + return; + } + + $width = $_[4] ? 0 : $_[2]; # Set width to zero if column is hidden + $format = $_[3]; + + list($firstcol, $lastcol) = $_; + + for ($col=$firstcol;$col<=$lastcol;$col++) { + $this->_col_sizes[$col] = $width; + if ($format) { + $this->_col_formats[$col] = $format; + } + } +} + +############################################################################### +# +# set_selection() +# +# Set which cell or cells are selected in a worksheet: see also the +# function _store_selection +# +function set_selection() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + $this->_selection = $_; +} + +############################################################################### +# +# freeze_panes() +# +# Set panes and mark them as frozen. See also _store_panes(). +# +function freeze_panes() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + $this->_frozen = 1; + $this->_panes = $_; +} + +############################################################################### +# +# thaw_panes() +# +# Set panes and mark them as unfrozen. See also _store_panes(). +# +function thaw_panes() { + + $_=func_get_args(); + + $this->_frozen = 0; + $this->_panes = $_; +} + + /* + * Set the page orientation as portrait. + */ + function set_portrait() { + $this->_orientation = 1; + } + + /* + * Set the page orientation as landscape. + */ + function set_landscape() { + $this->_orientation = 0; + } + + /* + * Set the paper type. Ex. 1 = US Letter, 9 = A4 + */ + function set_paper($type) { + $this->_paper_size = $type; + } + + /* + * Set the page header caption and optional margin. + */ + function set_header($string, $margin) { + + if (strlen($string) >= 255) { + trigger_error("Header string must be less than 255 characters", + E_USER_WARNING); + return; + } + + $this->_header = $string; + $this->_margin_head = $margin; + } + + /* + * Set the page footer caption and optional margin. + */ + function set_footer($string, $margin) { + if (strlen($string) >= 255) { + trigger_error("Footer string must be less than 255 characters", + E_USER_WARNING); + return; + } + + $this->_footer = $string; + $this->_margin_foot = $margin; + } + + /* + * Center the page horizontally. + */ + function center_horizontally($hcenter=1) { + $this->_hcenter = $hcenter; + } + + /* + * Center the page horizontally. + */ + function center_vertically($vcenter=1) { + $this->_vcenter = $vcenter; + } + + /* + * Set all the page margins to the same value in inches. + */ + function set_margins($margin) { + $this->set_margin_left($margin); + $this->set_margin_right($margin); + $this->set_margin_top($margin); + $this->set_margin_bottom($margin); + } + + /* + * Set the left and right margins to the same value in inches. + */ + function set_margins_LR($margin) { + $this->set_margin_left($margin); + $this->set_margin_right($margin); + } + + /* + * Set the top and bottom margins to the same value in inches. + */ + function set_margins_TB($margin) { + $this->set_margin_top($margin); + $this->set_margin_bottom($margin); + } + + /* + * Set the left margin in inches. + */ + function set_margin_left($margin=0.75) { + $this->_margin_left = $margin; + } + + /* + * Set the right margin in inches. + */ + function set_margin_right($margin=0.75) { + $this->_margin_right = $margin; + } + + /* + * Set the top margin in inches. + */ + function set_margin_top($margin=1.00) { + $this->_margin_top = $margin; + } + + /* + * Set the bottom margin in inches. + */ + function set_margin_bottom($margin=1.00) { + $this->_margin_bottom = $margin; + } + +############################################################################### +# +# repeat_rows($first_row, $last_row) +# +# Set the rows to repeat at the top of each printed page. See also the +# _store_name_xxxx() methods in Workbook.pm. +# +function repeat_rows() { + + $_=func_get_args(); + + $this->_title_rowmin = $_[0]; + $this->_title_rowmax = isset($_[1]) ? $_[1] : $_[0]; # Second row is optional +} + +############################################################################### +# +# repeat_columns($first_col, $last_col) +# +# Set the columns to repeat at the left hand side of each printed page. +# See also the _store_names() methods in Workbook.pm. +# +function repeat_columns() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + $this->_title_colmin = $_[0]; + $this->_title_colmax = isset($_[1]) ? $_[1] : $_[0]; # Second col is optional +} + +############################################################################### +# +# print_area($first_row, $first_col, $last_row, $last_col) +# +# Set the area of each worksheet that will be printed. See also the +# _store_names() methods in Workbook.pm. +# +function print_area() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + if (sizeof($_) != 4) { + # Require 4 parameters + return; + } + + $this->_print_rowmin = $_[0]; + $this->_print_colmin = $_[1]; + $this->_print_rowmax = $_[2]; + $this->_print_colmax = $_[3]; +} + + /* + * Set the option to hide gridlines on the screen and the printed page. + * There are two ways of doing this in the Excel BIFF format: The first + * is by setting the DspGrid field of the WINDOW2 record, this turns off + * the screen and subsequently the print gridline. The second method is + * to via the PRINTGRIDLINES and GRIDSET records, this turns off the + * printed gridlines only. The first method is probably sufficient for + * most cases. The second method is supported for backwards compatibility. + */ + function hide_gridlines($option=1) { + if ($option == 0) { + $this->_print_gridlines = 1; # 1 = display, 0 = hide + $this->_screen_gridlines = 1; + } elseif ($option == 1) { + $this->_print_gridlines = 0; + $this->_screen_gridlines = 1; + } else { + $this->_print_gridlines = 0; + $this->_screen_gridlines = 0; + } + } + + /* + * Set the option to print the row and column headers on the printed page. + * See also the _store_print_headers() method below. + */ + function print_row_col_headers($headers=1) { + $this->_print_headers = $headers; + } + + /* + * Store the vertical and horizontal number of pages that will define + * the maximum area printed. See also _store_setup() and _store_wsbool() + * below. + */ + function fit_to_pages($width, $height) { + $this->_fit_page = 1; + $this->_fit_width = $width; + $this->_fit_height = $height; + } + + /* + * Store the horizontal page breaks on a worksheet. + */ + function set_h_pagebreaks($breaks) { + $this->_hbreaks=array_merge($this->_hbreaks, $breaks); + } + + /* + * Store the vertical page breaks on a worksheet. + */ + function set_v_pagebreaks($breaks) { + $this->_vbreaks=array_merge($this->_vbreaks, $breaks); + } + + /* + * Set the worksheet zoom factor. + */ + function set_zoom($scale=100) { + // Confine the scale to Excel's range + if ($scale < 10 || $scale > 400) { + trigger_error("Zoom factor $scale outside range: ". + "10 <= zoom <= 400", E_USER_WARNING); + $scale = 100; + } + + $this->_zoom = $scale; + } + + /* + * Set the scale factor for the printed page. + */ + function set_print_scale($scale=100) { + // Confine the scale to Excel's range + if ($scale < 10 || $scale > 400) { + trigger_error("Print scale $scale outside range: ". + "10 <= zoom <= 400", E_USER_WARNING); + $scale = 100; + } + + // Turn off "fit to page" option + $this->_fit_page = 0; + + $this->_print_scale = $scale; + } + +############################################################################### +# +# write($row, $col, $token, $format) +# +# Parse $token call appropriate write method. $row and $column are zero +# indexed. $format is optional. +# +# Returns: return value of called subroutine +# +function write() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + $token = $_[2]; + + # Match an array ref. + if (is_array($token)) { + return call_user_func_array(array(&$this, 'write_row'), $_); + } + + # Match number + if (preg_match('/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/', $token)) { + return call_user_func_array(array(&$this, 'write_number'), $_); + } + # Match http, https or ftp URL + elseif (preg_match('|^[fh]tt?ps?://|', $token)) { + return call_user_func_array(array(&$this, 'write_url'), $_); + } + # Match mailto: + elseif (preg_match('/^mailto:/', $token)) { + return call_user_func_array(array(&$this, 'write_url'), $_); + } + # Match internal or external sheet link + elseif (preg_match('[^(?:in|ex)ternal:]', $token)) { + return call_user_func_array(array(&$this, 'write_url'), $_); + } + # Match formula + elseif (preg_match('/^=/', $token)) { + return call_user_func_array(array(&$this, 'write_formula'), $_); + } + # Match blank + elseif ($token == '') { + array_splice($_, 2, 1); # remove the empty string from the parameter list + return call_user_func_array(array(&$this, 'write_blank'), $_); + } + # Default: match string + else { + return call_user_func_array(array(&$this, 'write_string'), $_); + } +} + +############################################################################### +# +# write_row($row, $col, $array_ref, $format) +# +# Write a row of data starting from ($row, $col). Call write_col() if any of +# the elements of the array ref are in turn array refs. This allows the writing +# of 1D or 2D arrays of data in one go. +# +# Returns: the first encountered error value or zero for no errors +# +function write_row() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Catch non array refs passed by user. + if (!is_array($_[2])) { + trigger_error("Not an array ref in call to write_row()!", E_USER_ERROR); + } + + list($row, $col, $tokens)=array_splice($_, 0, 3); + $options = $_[0]; + $error = 0; + + foreach ($tokens as $token) { + + # Check for nested arrays + if (is_array($token)) { + $ret = $this->write_col($row, $col, $token, $options); + } else { + $ret = $this->write ($row, $col, $token, $options); + } + + # Return only the first error encountered, if any. + $error = $error || $ret; + $col++; + } + + return $error; +} + +############################################################################### +# +# _XF() +# +# Returns an index to the XF record in the workbook. +# TODO +# +# Note: this is a function, not a method. +# +function _XF($row=false, $col=false, $format=false) { + + if ($format) { + return $format->get_xf_index(); + } elseif (isset($this->_row_formats[$row])) { + return $this->_row_formats[$row]->get_xf_index(); + } elseif (isset($this->_col_formats[$col])) { + return $this->_col_formats[$col]->get_xf_index(); + } else { + return 0x0F; + } +} + +############################################################################### +# +# write_col($row, $col, $array_ref, $format) +# +# Write a column of data starting from ($row, $col). Call write_row() if any of +# the elements of the array ref are in turn array refs. This allows the writing +# of 1D or 2D arrays of data in one go. +# +# Returns: the first encountered error value or zero for no errors +# +function write_col() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Catch non array refs passed by user. + if (!is_array($_[2])) { + trigger_error("Not an array ref in call to write_row()!", E_USER_ERROR); + } + + $row = array_shift($_); + $col = array_shift($_); + $tokens = array_shift($_); + $options = $_; + + $error = 0; + + foreach ($tokens as $token) { + + # write() will deal with any nested arrays + $ret = $this->write($row, $col, $token, $options); + + # Return only the first error encountered, if any. + $error = $error || $ret; + $row++; + } + + return $error; +} + +############################################################################### +############################################################################### +# +# Internal methods +# + +############################################################################### +# +# _append(), overloaded. +# +# Store Worksheet data in memory using the base class _append() or to a +# temporary file, the default. +# +function _append($data) { + + if (func_num_args()>1) { + trigger_error("writeexcel_worksheet::_append() ". + "called with more than one argument", E_USER_ERROR); + } + + if ($this->_using_tmpfile) { + + if ($this->_debug) { + print "worksheet::_append() called:"; + for ($c=0;$c $this->_limit) { + $data = $this->_add_continue($data); + } + + fputs($this->_filehandle, $data); + $this->_datasize += strlen($data); + } else { + parent::_append($data); + } +} + +############################################################################### +# +# _substitute_cellref() +# +# Substitute an Excel cell reference in A1 notation for zero based row and +# column values in an argument list. +# +# Ex: ("A4", "Hello") is converted to (3, 0, "Hello"). +# +// Exactly one array must be passed! +function _substitute_cellref($_) { + $cell = strtoupper(array_shift($_)); + + # Convert a column range: 'A:A' or 'B:G' + if (preg_match('/([A-I]?[A-Z]):([A-I]?[A-Z])/', $cell, $reg)) { + list($dummy, $col1) = $this->_cell_to_rowcol($reg[1] .'1'); # Add a dummy row + list($dummy, $col2) = $this->_cell_to_rowcol($reg[2] .'1'); # Add a dummy row + return array_merge(array($col1, $col2), $_); + } + + # Convert a cell range: 'A1:B7' + if (preg_match('/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/', $cell, $reg)) { + list($row1, $col1) = $this->_cell_to_rowcol($reg[1]); + list($row2, $col2) = $this->_cell_to_rowcol($reg[2]); + return array_merge(array($row1, $col1, $row2, $col2), $_); + } + + # Convert a cell reference: 'A1' or 'AD2000' + if (preg_match('/\$?([A-I]?[A-Z]\$?\d+)/', $cell, $reg)) { + list($row1, $col1) = $this->_cell_to_rowcol($reg[1]); + return array_merge(array($row1, $col1), $_); + + } + + trigger_error("Unknown cell reference $cell", E_USER_ERROR); +} + +############################################################################### +# +# _cell_to_rowcol($cell_ref) +# +# Convert an Excel cell reference in A1 notation to a zero based row and column +# reference; converts C1 to (0, 2). +# +# Returns: row, column +# +# TODO use functions in Utility.pm +# +function _cell_to_rowcol($cell) { + + preg_match('/\$?([A-I]?[A-Z])\$?(\d+)/', $cell, $reg); + + $col = $reg[1]; + $row = $reg[2]; + + # Convert base26 column string to number + # All your Base are belong to us. + $chars = preg_split('//', $col, -1, PREG_SPLIT_NO_EMPTY); + $expn = 0; + $col = 0; + + while (sizeof($chars)) { + $char = array_pop($chars); # LS char first + $col += (ord($char) -ord('A') +1) * pow(26, $expn); + $expn++; + } + + # Convert 1-index to zero-index + $row--; + $col--; + + return array($row, $col); +} + + /* + * This is an internal method that is used to filter elements of the + * array of pagebreaks used in the _store_hbreak() and _store_vbreak() + * methods. It: + * 1. Removes duplicate entries from the list. + * 2. Sorts the list. + * 3. Removes 0 from the list if present. + */ + function _sort_pagebreaks($breaks) { + // Hash slice to remove duplicates + foreach ($breaks as $break) { + $hash["$break"]=1; + } + + // Numerical sort + $breaks=array_keys($hash); + sort($breaks, SORT_NUMERIC); + + // Remove zero + if ($breaks[0] == 0) { + array_shift($breaks); + } + + // 1000 vertical pagebreaks appears to be an internal Excel 5 limit. + // It is slightly higher in Excel 97/200, approx. 1026 + if (sizeof($breaks) > 1000) { + array_splice($breaks, 1000); + } + + return $breaks; + } + + /* + * Based on the algorithm provided by Daniel Rentz of OpenOffice. + */ + function _encode_password($plaintext) { + $chars=preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY); + $count=sizeof($chars); + $i=0; + + for ($c=0;$c> 15; + $char = $low_15 | $high_15; + } + + $password = 0x0000; + + foreach ($chars as $char) { + $password ^= $char; + } + + $password ^= $count; + $password ^= 0xCE4B; + + return $password; + } + +############################################################################### +############################################################################### +# +# BIFF RECORDS +# + +############################################################################### +# +# write_number($row, $col, $num, $format) +# +# Write a double to the specified row and column (zero indexed). +# An integer can be written as a double. Excel will display an +# integer. $format is optional. +# +# Returns 0 : normal termination +# -1 : insufficient number of arguments +# -2 : row or column out of range +# +function write_number() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Check the number of args + if (sizeof($_) < 3) { + return -1; + } + + $record = 0x0203; # Record identifier + $length = 0x000E; # Number of bytes to follow + + $row = $_[0]; # Zero indexed row + $col = $_[1]; # Zero indexed column + $num = $_[2]; +//!!! + $xf = $this->_XF($row, $col, $_[3]); # The cell format + + # Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { return -2; } + if ($col >= $this->_xls_colmax) { return -2; } + if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; } + if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; } + if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; } + if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + $xl_double = pack("d", $num); + + if ($this->_byte_order) { +//TODO + $xl_double = strrev($xl_double); + } + + $this->_append($header . $data . $xl_double); + + return 0; +} + +############################################################################### +# +# write_string ($row, $col, $string, $format) +# +# Write a string to the specified row and column (zero indexed). +# NOTE: there is an Excel 5 defined limit of 255 characters. +# $format is optional. +# Returns 0 : normal termination +# -1 : insufficient number of arguments +# -2 : row or column out of range +# -3 : long string truncated to 255 chars +# +function write_string() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Check the number of args + if (sizeof($_) < 3) { + return -1; + } + + $record = 0x0204; # Record identifier + $length = 0x0008 + strlen($_[2]); # Bytes to follow + + $row = $_[0]; # Zero indexed row + $col = $_[1]; # Zero indexed column + $strlen = strlen($_[2]); + $str = $_[2]; + $xf = $this->_XF($row, $col, $_[3]); # The cell format + + $str_error = 0; + + # Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { return -2; } + if ($col >= $this->_xls_colmax) { return -2; } + if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; } + if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; } + if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; } + if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; } + + if ($strlen > $this->_xls_strmax) { # LABEL must be < 255 chars + $str = substr($str, 0, $this->_xls_strmax); + $length = 0x0008 + $this->_xls_strmax; + $strlen = $this->_xls_strmax; + $str_error = -3; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row, $col, $xf, $strlen); + + $this->_append($header . $data . $str); + + return $str_error; +} + +############################################################################### +# +# write_blank($row, $col, $format) +# +# Write a blank cell to the specified row and column (zero indexed). +# A blank cell is used to specify formatting without adding a string +# or a number. +# +# A blank cell without a format serves no purpose. Therefore, we don't write +# a BLANK record unless a format is specified. This is mainly an optimisation +# for the write_row() and write_col() methods. +# +# Returns 0 : normal termination (including no format) +# -1 : insufficient number of arguments +# -2 : row or column out of range +# +function write_blank() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Check the number of args + if (sizeof($_) < 2) { + return -1; + } + + # Don't write a blank cell unless it has a format + if (!isset($_[2])) { + return 0; + } + + $record = 0x0201; # Record identifier + $length = 0x0006; # Number of bytes to follow + + $row = $_[0]; # Zero indexed row + $col = $_[1]; # Zero indexed column + $xf = $this->_XF($row, $col, $_[2]); # The cell format + + # Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { return -2; } + if ($col >= $this->_xls_colmax) { return -2; } + if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; } + if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; } + if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; } + if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; } + + $header = pack("vv", $record, $length); + $data = pack("vvv", $row, $col, $xf); + + $this->_append($header . $data); + + return 0; +} + +############################################################################### +# +# write_formula($row, $col, $formula, $format) +# +# Write a formula to the specified row and column (zero indexed). +# The textual representation of the formula is passed to the parser in +# Formula.pm which returns a packed binary string. +# +# $format is optional. +# +# Returns 0 : normal termination +# -1 : insufficient number of arguments +# -2 : row or column out of range +# +function write_formula() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Check the number of args + if (sizeof($_) < 3) { + return -1; + } + + $record = 0x0006; # Record identifier + $length=0; # Bytes to follow + + $row = $_[0]; # Zero indexed row + $col = $_[1]; # Zero indexed column + $formula = $_[2]; # The formula text string + + # Excel normally stores the last calculated value of the formula in $num. + # Clearly we are not in a position to calculate this a priori. Instead + # we set $num to zero and set the option flags in $grbit to ensure + # automatic calculation of the formula when the file is opened. + # + $xf = $this->_XF($row, $col, $_[3]); # The cell format + $num = 0x00; # Current value of formula + $grbit = 0x03; # Option flags + $chn = 0x0000; # Must be zero + + # Check that row and col are valid and store max and min values + if ($row >= $this->_xls_rowmax) { return -2; } + if ($col >= $this->_xls_colmax) { return -2; } + if ($row < $this->_dim_rowmin) { $this->_dim_rowmin = $row; } + if ($row > $this->_dim_rowmax) { $this->_dim_rowmax = $row; } + if ($col < $this->_dim_colmin) { $this->_dim_colmin = $col; } + if ($col > $this->_dim_colmax) { $this->_dim_colmax = $col; } + + # Strip the = sign at the beginning of the formula string + $formula = preg_replace('/^=/', "", $formula); + + # Parse the formula using the parser in Formula.pm + $parser =& $this->_parser; + $formula = $parser->parse_formula($formula); + + $formlen = strlen($formula); # Length of the binary string + $length = 0x16 + $formlen; # Length of the record data + + $header = pack("vv", $record, $length); + $data = pack("vvvdvVv", $row, $col, $xf, $num, + $grbit, $chn, $formlen); + + $this->_append($header . $data . $formula); + + return 0; +} + +############################################################################### +# +# write_url($row, $col, $url, $string, $format) +# +# Write a hyperlink. This is comprised of two elements: the visible label and +# the invisible link. The visible label is the same as the link unless an +# alternative string is specified. The label is written using the +# write_string() method. Therefore the 255 characters string limit applies. +# $string and $format are optional and their order is interchangeable. +# +# The hyperlink can be to a http, ftp, mail, internal sheet, or external +# directory url. +# +# Returns 0 : normal termination +# -1 : insufficient number of arguments +# -2 : row or column out of range +# -3 : long string truncated to 255 chars +# +function write_url() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Check the number of args + if (sizeof($_) < 3) { + return -1; + } + + # Add start row and col to arg list + return call_user_func_array(array(&$this, 'write_url_range'), + array_merge(array($_[0], $_[1]), $_)); +} + +############################################################################### +# +# write_url_range($row1, $col1, $row2, $col2, $url, $string, $format) +# +# This is the more general form of write_url(). It allows a hyperlink to be +# written to a range of cells. This function also decides the type of hyperlink +# to be written. These are either, Web (http, ftp, mailto), Internal +# (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1'). +# +# See also write_url() above for a general description and return values. +# +function write_url_range() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + # Check the number of args + if (sizeof($_) < 5) { + return -1; + } + + # Reverse the order of $string and $format if necessary. +//TODO ($_[5], $_[6]) = ($_[6], $_[5]) if (ref $_[5]); + + $url = $_[4]; + + # Check for internal/external sheet links or default to web link + if (preg_match('[^internal:]', $url)) { + return call_user_func_array(array(&$this, '_write_url_internal'), $_); + } + + if (preg_match('[^external:]', $url)) { + return call_user_func_array(array(&$this, '_write_url_external'), $_); + } + + return call_user_func_array(array(&$this, '_write_url_web'), $_); +} + +############################################################################### +# +# _write_url_web($row1, $col1, $row2, $col2, $url, $string, $format) +# +# Used to write http, ftp and mailto hyperlinks. +# The link type ($options) is 0x03 is the same as absolute dir ref without +# sheet. However it is differentiated by the $unknown2 data stream. +# +# See also write_url() above for a general description and return values. +# +function _write_url_web() { + + $_=func_get_args(); + + $record = 0x01B8; # Record identifier + $length = 0x00000; # Bytes to follow + + $row1 = $_[0]; # Start row + $col1 = $_[1]; # Start column + $row2 = $_[2]; # End row + $col2 = $_[3]; # End column + $url = $_[4]; # URL string + if (isset($_[5])) { + $str = $_[5]; # Alternative label + } + $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format + + # Write the visible label using the write_string() method. + if(!isset($str)) { + $str = $url; + } + + $str_error = $this->write_string($row1, $col1, $str, $xf); + + if ($str_error == -2) { + return $str_error; + } + + # Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + $unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B"); + + # Pack the option flags + $options = pack("V", 0x03); + + # Convert URL to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + # Pack the length of the URL + $url_len = pack("V", strlen($url)); + + # Calculate the data length + $length = 0x34 + strlen($url); + + # Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + # Write the packed data + $this->_append($header. + $data. + $unknown1. + $options. + $unknown2. + $url_len. + $url); + + return $str_error; +} + +############################################################################### +# +# _write_url_internal($row1, $col1, $row2, $col2, $url, $string, $format) +# +# Used to write internal reference hyperlinks such as "Sheet1!A1". +# +# See also write_url() above for a general description and return values. +# +function _write_url_internal() { + + $_=func_get_args(); + + $record = 0x01B8; # Record identifier + $length = 0x00000; # Bytes to follow + + $row1 = $_[0]; # Start row + $col1 = $_[1]; # Start column + $row2 = $_[2]; # End row + $col2 = $_[3]; # End column + $url = $_[4]; # URL string + if (isset($_[5])) { + $str = $_[5]; # Alternative label + } + $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format + + # Strip URL type + $url = preg_replace('s[^internal:]', '', $url); + + # Write the visible label + if (!isset($str)) { + $str = $url; + } + $str_error = $this->write_string($row1, $col1, $str, $xf); + + if ($str_error == -2) { + return $str_error; + } + + # Pack the undocumented parts of the hyperlink stream + $unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000"); + + # Pack the option flags + $options = pack("V", 0x08); + + # Convert the URL type and to a null terminated wchar string + $url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY)); + $url = $url . "\0\0\0"; + + # Pack the length of the URL as chars (not wchars) + $url_len = pack("V", int(strlen($url)/2)); + + # Calculate the data length + $length = 0x24 + strlen($url); + + # Pack the header data + $header = pack("vv", $record, $length); + $data = pack("vvvv", $row1, $row2, $col1, $col2); + + # Write the packed data + $this->_append($header. + $data. + $unknown1. + $options. + $url_len. + $url); + + return $str_error; +} + +############################################################################### +# +# _write_url_external($row1, $col1, $row2, $col2, $url, $string, $format) +# +# Write links to external directory names such as 'c:\foo.xls', +# c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'. +# +# Note: Excel writes some relative links with the $dir_long string. We ignore +# these cases for the sake of simpler code. +# +# See also write_url() above for a general description and return values. +# +function _write_url_external() { + + $_=func_get_args(); + + # Network drives are different. We will handle them separately + # MS/Novell network drives and shares start with \\ + if (preg_match('[^external:\\\\]', $_[4])) { + return call_user_func_array(array(&$this, '_write_url_external_net'), $_); + } + + $record = 0x01B8; # Record identifier + $length = 0x00000; # Bytes to follow + + $row1 = $_[0]; # Start row + $col1 = $_[1]; # Start column + $row2 = $_[2]; # End row + $col2 = $_[3]; # End column + $url = $_[4]; # URL string + if (isset($_[5])) { + $str = $_[5]; # Alternative label + } + $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format + + # Strip URL type and change Unix dir separator to Dos style (if needed) + # + $url = preg_replace('[^external:]', '', $url); + $url = preg_replace('[/]', "\\", $url); + + # Write the visible label + if (!isset($str)) { + $str = preg_replace('[\#]', ' - ', $url); + } + $str_error = $this->write_string($row1, $col1, $str, $xf); + if ($str_error == -2) { + return $str_error; + } + + # Determine if the link is relative or absolute: + # relative if link contains no dir separator, "somefile.xls" + # relative if link starts with up-dir, "..\..\somefile.xls" + # otherwise, absolute + # + $absolute = 0x02; # Bit mask + + if (!preg_match('[\\]', $url)) { + $absolute = 0x00; + } + + if (preg_match('[^\.\.\\]', $url)) { + $absolute = 0x00; + } + + # Determine if the link contains a sheet reference and change some of the + # parameters accordingly. + # Split the dir name and sheet name (if it exists) + # + list($dir_long, $sheet) = preg_split('/\#/', $url); + $link_type = 0x01 | $absolute; + +//!!! + if (isset($sheet)) { + $link_type |= 0x08; + $sheet_len = pack("V", length($sheet) + 0x01); + $sheet = join("\0", split('', $sheet)); + $sheet .= "\0\0\0"; + } else { + $sheet_len = ''; + $sheet = ''; + } + + # Pack the link type + $link_type = pack("V", $link_type); + + + # Calculate the up-level dir count e.g.. (..\..\..\ == 3) +/* TODO + $up_count = 0; + $up_count++ while $dir_long =~ s[^\.\.\\][]; + $up_count = pack("v", $up_count); +*/ + + # Store the short dos dir name (null terminated) + $dir_short = $dir_long . "\0"; + + # Store the long dir name as a wchar string (non-null terminated) + $dir_long = join("\0", preg_split('', $dir_long, -1, PREG_SPLIT_NO_EMPTY)); + $dir_long = $dir_long . "\0"; + + # Pack the lengths of the dir strings + $dir_short_len = pack("V", strlen($dir_short) ); + $dir_long_len = pack("V", strlen($dir_long) ); + $stream_len = pack("V", strlen($dir_long) + 0x06); + + # Pack the undocumented parts of the hyperlink stream + $unknown1 =pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' ); + $unknown2 =pack("H*",'0303000000000000C000000000000046' ); + $unknown3 =pack("H*",'FFFFADDE000000000000000000000000000000000000000'); + $unknown4 =pack("v", 0x03 ); + + # Pack the main data stream + $data = pack("vvvv", $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $unknown2 . + $up_count . + $dir_short_len. + $dir_short . + $unknown3 . + $stream_len . + $dir_long_len . + $unknown4 . + $dir_long . + $sheet_len . + $sheet ; + + # Pack the header data + $length = strlen($data); + $header = pack("vv", $record, $length); + + # Write the packed data + $this->_append($header . $data); + + return $str_error; +} + +############################################################################### +# +# write_url_xxx($row1, $col1, $row2, $col2, $url, $string, $format) +# +# Write links to external MS/Novell network drives and shares such as +# '//NETWORK/share/foo.xls' and '//NETWORK/share/foo.xls#Sheet1!A1'. +# +# See also write_url() above for a general description and return values. +# +function _write_url_external_net() { + + $_=func_get_args(); + + $record = 0x01B8; # Record identifier + $length = 0x00000; # Bytes to follow + + $row1 = $_[0]; # Start row + $col1 = $_[1]; # Start column + $row2 = $_[2]; # End row + $col2 = $_[3]; # End column + $url = $_[4]; # URL string + if(isset($_[5])) { + $str = $_[5]; # Alternative label + } + $xf = $_[6] ? $_[6] : $this->_url_format; # The cell format + + # Strip URL type and change Unix dir separator to Dos style (if needed) + # + $url = preg_replace('[^external:]', "", $url); + $url = preg_replace('[/]', "\\"); + + # Write the visible label + if (!isset($str)) { + $str = preg_replace('[\#]', " - ", $url); + } + + $str_error = $this->write_string($row1, $col1, $str, $xf); + if ($str_error == -2) { + return $str_error; + } + + # Determine if the link contains a sheet reference and change some of the + # parameters accordingly. + # Split the dir name and sheet name (if it exists) + # + list($dir_long , $sheet) = preg_split('\#', $url); + $link_type = 0x0103; # Always absolute + +//!!! + if (isset($sheet)) { + $link_type |= 0x08; + $sheet_len = pack("V", strlen($sheet) + 0x01); + $sheet = join("\0", preg_split("''", $sheet, -1, PREG_SPLIT_NO_EMPTY)); + $sheet .= "\0\0\0"; + } else { + $sheet_len = ''; + $sheet = ''; + } + + # Pack the link type + $link_type = pack("V", $link_type); + + # Make the string null terminated + $dir_long = $dir_long . "\0"; + + # Pack the lengths of the dir string + $dir_long_len = pack("V", strlen($dir_long)); + + # Store the long dir name as a wchar string (non-null terminated) + $dir_long = join("\0", preg_split("''", $dir_long, -1, PREG_SPLIT_NO_EMPTY)); + $dir_long = $dir_long . "\0"; + + # Pack the undocumented part of the hyperlink stream + $unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000'); + + # Pack the main data stream + $data = pack("vvvv", $row1, $row2, $col1, $col2) . + $unknown1 . + $link_type . + $dir_long_len . + $dir_long . + $sheet_len . + $sheet ; + + # Pack the header data + $length = strlen($data); + $header = pack("vv", $record, $length); + + # Write the packed data + $this->_append($header . $data); + + return $str_error; +} + +############################################################################### +# +# set_row($row, $height, $XF) +# +# This method is used to set the height and XF format for a row. +# Writes the BIFF record ROW. +# +function set_row() { + + $_=func_get_args(); + + $record = 0x0208; # Record identifier + $length = 0x0010; # Number of bytes to follow + + $rw = $_[0]; # Row Number + $colMic = 0x0000; # First defined column + $colMac = 0x0000; # Last defined column + //$miyRw; # Row height + $irwMac = 0x0000; # Used by Excel to optimise loading + $reserved = 0x0000; # Reserved + $grbit = 0x01C0; # Option flags. (monkey) see $1 do + //$ixfe; # XF index + if (isset($_[2])) { + $format = $_[2]; # Format object + } + + # Check for a format object + if (isset($_[2])) { + $ixfe = $format->get_xf_index(); + } else { + $ixfe = 0x0F; + } + + # Use set_row($row, undef, $XF) to set XF without setting height + if (isset($_[1])) { + $miyRw = $_[1] *20; + } else { + $miyRw = 0xff; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvvvvv", $rw, $colMic, $colMac, $miyRw, + $irwMac,$reserved, $grbit, $ixfe); + + $this->_append($header . $data); + + # Store the row sizes for use when calculating image vertices. + # Also store the column formats. + # + # Ensure at least $row and $height + if (sizeof($_) < 2) { + return; + } + + $this->_row_sizes[$_[0]] = $_[1]; + if (isset($_[2])) { + $this->_row_formats[$_[0]] = $_[2]; + } +} + + /* + * Writes Excel DIMENSIONS to define the area in which there is data. + */ + function _store_dimensions() { + $record = 0x0000; // Record identifier + $length = 0x000A; // Number of bytes to follow + $row_min = $this->_dim_rowmin; // First row + $row_max = $this->_dim_rowmax; // Last row plus 1 + $col_min = $this->_dim_colmin; // First column + $col_max = $this->_dim_colmax; // Last column plus 1 + $reserved = 0x0000; // Reserved by Excel + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $row_min, $row_max, + $col_min, $col_max, $reserved); + $this->_prepend($header . $data); + } + + /* + * Write BIFF record Window2. + */ + function _store_window2() { + $record = 0x023E; // Record identifier + $length = 0x000A; // Number of bytes to follow + + $grbit = 0x00B6; // Option flags + $rwTop = 0x0000; // Top row visible in window + $colLeft = 0x0000; // Leftmost column visible in window + $rgbHdr = 0x00000000; // Row/column heading and gridline + // color + + // The options flags that comprise $grbit + $fDspFmla = 0; // 0 - bit + $fDspGrid = $this->_screen_gridlines; // 1 + $fDspRwCol = 1; // 2 + $fFrozen = $this->_frozen; // 3 + $fDspZeros = 1; // 4 + $fDefaultHdr = 1; // 5 + $fArabic = 0; // 6 + $fDspGuts = 1; // 7 + $fFrozenNoSplit = 0; // 0 - bit + $fSelected = $this->_selected; // 1 + $fPaged = 1; // 2 + + $grbit = $fDspFmla; + $grbit |= $fDspGrid << 1; + $grbit |= $fDspRwCol << 2; + $grbit |= $fFrozen << 3; + $grbit |= $fDspZeros << 4; + $grbit |= $fDefaultHdr << 5; + $grbit |= $fArabic << 6; + $grbit |= $fDspGuts << 7; + $grbit |= $fFrozenNoSplit << 8; + $grbit |= $fSelected << 9; + $grbit |= $fPaged << 10; + + $header = pack("vv", $record, $length); + $data = pack("vvvV", $grbit, $rwTop, $colLeft, $rgbHdr); + + $this->_append($header . $data); + } + + /* + * Write BIFF record DEFCOLWIDTH if COLINFO records are in use. + */ + function _store_defcol() { + $record = 0x0055; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $colwidth = 0x0008; // Default column width + + $header = pack("vv", $record, $length); + $data = pack("v", $colwidth); + + $this->_prepend($header . $data); + } + +############################################################################### +# +# _store_colinfo($firstcol, $lastcol, $width, $format, $hidden) +# +# Write BIFF record COLINFO to define column widths +# +# Note: The SDK says the record length is 0x0B but Excel writes a 0x0C +# length record. +# +function _store_colinfo($_) { + + $record = 0x007D; # Record identifier + $length = 0x000B; # Number of bytes to follow + + $colFirst = $_[0] ? $_[0] : 0; # First formatted column + $colLast = $_[1] ? $_[1] : 0; # Last formatted column + $coldx = $_[2] ? $_[2] : 8.43; # Col width, 8.43 is Excel default + + $coldx += 0.72; # Fudge. Excel subtracts 0.72 !? + $coldx *= 256; # Convert to units of 1/256 of a char + + //$ixfe; # XF index + $grbit = $_[4] || 0; # Option flags + $reserved = 0x00; # Reserved + $format = $_[3]; # Format object + + # Check for a format object + if (isset($_[3])) { + $ixfe = $format->get_xf_index(); + } else { + $ixfe = 0x0F; + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvvC", $colFirst, $colLast, $coldx, + $ixfe, $grbit, $reserved); + $this->_prepend($header . $data); +} + +############################################################################### +# +# _store_selection($first_row, $first_col, $last_row, $last_col) +# +# Write BIFF record SELECTION. +# +function _store_selection($_) { + + $record = 0x001D; # Record identifier + $length = 0x000F; # Number of bytes to follow + + $pnn = $this->_active_pane; # Pane position + $rwAct = $_[0]; # Active row + $colAct = $_[1]; # Active column + $irefAct = 0; # Active cell ref + $cref = 1; # Number of refs + + $rwFirst = $_[0]; # First row in reference + $colFirst = $_[1]; # First col in reference + $rwLast = $_[2] ? $_[2] : $rwFirst; # Last row in reference + $colLast = $_[3] ? $_[3] : $colFirst; # Last col in reference + + # Swap last row/col for first row/col as necessary + if ($rwFirst > $rwLast) { + list($rwFirst, $rwLast) = array($rwLast, $rwFirst); + } + + if ($colFirst > $colLast) { + list($colFirst, $colLast) = array($colLast, $colFirst); + } + + $header = pack("vv", $record, $length); + $data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct, + $irefAct, $cref, + $rwFirst, $rwLast, + $colFirst, $colLast); + + $this->_append($header . $data); +} + + /* + * Write BIFF record EXTERNCOUNT to indicate the number of external + * sheet references in a worksheet. + * + * Excel only stores references to external sheets that are used in + * formulas. For simplicity we store references to all the sheets in + * the workbook regardless of whether they are used or not. This reduces + * the overall complexity and eliminates the need for a two way dialogue + * between the formula parser the worksheet objects. + */ + function _store_externcount($cxals) { + // $cxals Number of external references + + $record = 0x0016; // Record identifier + $length = 0x0002; // Number of bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cxals); + + $this->_prepend($header . $data); + } + + /* + * Writes the Excel BIFF EXTERNSHEET record. These references are used + * by formulas. A formula references a sheet name via an index. Since we + * store a reference to all of the external worksheets the EXTERNSHEET + * index is the same as the worksheet index. + */ + function _store_externsheet($sheetname) { + $record = 0x0017; # Record identifier + // $length Number of bytes to follow + + // $cch Length of sheet name + // $rgch Filename encoding + + // References to the current sheet are encoded differently to + // references to external sheets. + if ($this->_name == $sheetname) { + $sheetname = ''; + $length = 0x02; // The following 2 bytes + $cch = 1; // The following byte + $rgch = 0x02; // Self reference + } else { + $length = 0x02 + strlen($sheetname); + $cch = strlen($sheetname); + $rgch = 0x03; // Reference to a sheet in the current + // workbook + } + + $header = pack("vv", $record, $length); + $data = pack("CC", $cch, $rgch); + + $this->_prepend($header . $data . $sheetname); + } + +############################################################################### +# +# _store_panes() +# +# +# Writes the Excel BIFF PANE record. +# The panes can either be frozen or thawed (unfrozen). +# Frozen panes are specified in terms of a integer number of rows and columns. +# Thawed panes are specified in terms of Excel's units for rows and columns. +# +function _store_panes($_) { + + $record = 0x0041; # Record identifier + $length = 0x000A; # Number of bytes to follow + + $y = $_[0] ? $_[0] : 0; # Vertical split position + $x = $_[1] ? $_[1] : 0; # Horizontal split position + if (isset($_[2])) { + $rwTop = $_[2]; # Top row visible + } + if (isset($_[3])) { + $colLeft = $_[3]; # Leftmost column visible + } + if (isset($_[4])) { + $pnnAct = $_[4]; # Active pane + } + + # Code specific to frozen or thawed panes. + if ($this->_frozen) { + # Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = $y; + } + if (!isset($colLeft)) { + $colLeft = $x; + } + } else { + # Set default values for $rwTop and $colLeft + if (!isset($rwTop)) { + $rwTop = 0; + } + if (!isset($colLeft)) { + $colLeft = 0; + } + + # Convert Excel's row and column units to the internal units. + # The default row height is 12.75 + # The default column width is 8.43 + # The following slope and intersection values were interpolated. + # + $y = 20*$y + 255; + $x = 113.879*$x + 390; + } + + # Determine which pane should be active. There is also the undocumented + # option to override this should it be necessary: may be removed later. + # + if (!isset($pnnAct)) { + # Bottom right + if ($x != 0 && $y != 0) { + $pnnAct = 0; + } + # Top right + if ($x != 0 && $y == 0) { + $pnnAct = 1; + } + # Bottom left + if ($x == 0 && $y != 0) { + $pnnAct = 2; + } + # Top left + if ($x == 0 && $y == 0) { + $pnnAct = 3; + } + } + + $this->_active_pane = $pnnAct; # Used in _store_selection + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct); + + $this->_append($header . $data); +} + + /* + * Store the page setup SETUP BIFF record. + */ + function _store_setup() { + $record = 0x00A1; // Record identifier + $length = 0x0022; // Number of bytes to follow + + $iPaperSize = $this->_paper_size; // Paper size + $iScale = $this->_print_scale; // Print scaling factor + $iPageStart = 0x01; // Starting page number + $iFitWidth = $this->_fit_width; // Fit to number of pages wide + $iFitHeight = $this->_fit_height; // Fit to number of pages high + $grbit = 0x00; // Option flags + $iRes = 0x0258; // Print resolution + $iVRes = 0x0258; // Vertical print resolution + $numHdr = $this->_margin_head; // Header Margin + $numFtr = $this->_margin_foot; // Footer Margin + $iCopies = 0x01; // Number of copies + + $fLeftToRight = 0x0; // Print over then down + $fLandscape = $this->_orientation; // Page orientation + $fNoPls = 0x0; // Setup not read from printer + $fNoColor = 0x0; // Print black and white + $fDraft = 0x0; // Print draft quality + $fNotes = 0x0; // Print notes + $fNoOrient = 0x0; // Orientation not set + $fUsePage = 0x0; // Use custom starting page + + $grbit = $fLeftToRight; + $grbit |= $fLandscape << 1; + $grbit |= $fNoPls << 2; + $grbit |= $fNoColor << 3; + $grbit |= $fDraft << 4; + $grbit |= $fNotes << 5; + $grbit |= $fNoOrient << 6; + $grbit |= $fUsePage << 7; + + $numHdr = pack("d", $numHdr); + $numFtr = pack("d", $numFtr); + + if ($this->_byte_order) { + $numHdr = strrev($numHdr); + $numFtr = strrev($numFtr); + } + + $header = pack("vv", $record, $length); + $data1 = pack("vvvvvvvv", $iPaperSize, + $iScale, + $iPageStart, + $iFitWidth, + $iFitHeight, + $grbit, + $iRes, + $iVRes); + $data2 = $numHdr . $numFtr; + $data3 = pack("v", $iCopies); + + $this->_prepend($header . $data1 . $data2 . $data3); + } + + /* + * Store the header caption BIFF record. + */ + function _store_header() { + $record = 0x0014; // Record identifier + + $str = $this->_header; // header string + $cch = strlen($str); // Length of header string + $length = 1 + $cch; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("C", $cch); + + $this->_append($header . $data . $str); + } + + /* + * Store the footer caption BIFF record. + */ + function _store_footer() { + $record = 0x0015; // Record identifier + + $str = $this->_footer; // Footer string + $cch = strlen($str); // Length of footer string + $length = 1 + $cch; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("C", $cch); + + $this->_append($header . $data . $str); + } + + /* + * Store the horizontal centering HCENTER BIFF record. + */ + function _store_hcenter() { + $record = 0x0083; // Record identifier + $length = 0x0002; // Bytes to follow + + $fHCenter = $this->_hcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fHCenter); + + $this->_append($header . $data); + } + + /* + * Store the vertical centering VCENTER BIFF record. + */ + function _store_vcenter() { + $record = 0x0084; // Record identifier + $length = 0x0002; // Bytes to follow + + $fVCenter = $this->_vcenter; // Horizontal centering + + $header = pack("vv", $record, $length); + $data = pack("v", $fVCenter); + + $this->_append($header . $data); + } + + /* + * Store the LEFTMARGIN BIFF record. + */ + function _store_margin_left() { + $record = 0x0026; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_left; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + + if ($this->_byte_order) { + $data = strrev($data); + } + + $this->_append($header . $data); + } + + /* + * Store the RIGHTMARGIN BIFF record. + */ + function _store_margin_right() { + $record = 0x0027; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_right; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + + if ($this->_byte_order) { + $data = strrev($data); + } + + $this->_append($header . $data); + } + + /* + * Store the TOPMARGIN BIFF record. + */ + function _store_margin_top() { + $record = 0x0028; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_top; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + + if ($this->_byte_order) { + $data = strrev($data); + } + + $this->_append($header . $data); + } + + /* + * Store the BOTTOMMARGIN BIFF record. + */ + function _store_margin_bottom() { + $record = 0x0029; // Record identifier + $length = 0x0008; // Bytes to follow + + $margin = $this->_margin_bottom; // Margin in inches + + $header = pack("vv", $record, $length); + $data = pack("d", $margin); + + if ($this->_byte_order) { + $data = strrev($data); + } + + $this->_append($header . $data); + } + +############################################################################### +# +# merge_cells($first_row, $first_col, $last_row, $last_col) +# +# This is an Excel97/2000 method. It is required to perform more complicated +# merging than the normal align merge in Format.pm +# +function merge_cells() { + + $_=func_get_args(); + + // Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + $record = 0x00E5; # Record identifier + $length = 0x000A; # Bytes to follow + + $cref = 1; # Number of refs + $rwFirst = $_[0]; # First row in reference + $colFirst = $_[1]; # First col in reference + $rwLast = $_[2] ? $_[2] : $rwFirst; # Last row in reference + $colLast = $_[3] ? $_[3] : $colFirst; # Last col in reference + + // Swap last row/col for first row/col as necessary + if ($rwFirst > $rwLast) { + list($rwFirst, $rwLast) = array($rwLast, $rwFirst); + } + + if ($colFirst > $colLast) { + list($colFirst, $colLast) = array($colLast, $colFirst); + } + + $header = pack("vv", $record, $length); + $data = pack("vvvvv", $cref, + $rwFirst, $rwLast, + $colFirst, $colLast); + + $this->_append($header . $data); +} + + /* + * Write the PRINTHEADERS BIFF record. + */ + function _store_print_headers() { + $record = 0x002a; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintRwCol = $this->_print_headers; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintRwCol); + + $this->_prepend($header . $data); + } + + /* + * Write the PRINTGRIDLINES BIFF record. Must be used in conjunction + * with the GRIDSET record. + */ + function _store_print_gridlines() { + $record = 0x002b; // Record identifier + $length = 0x0002; // Bytes to follow + + $fPrintGrid = $this->_print_gridlines; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fPrintGrid); + + $this->_prepend($header . $data); + } + + /* + * Write the GRIDSET BIFF record. Must be used in conjunction with the + * PRINTGRIDLINES record. + */ + function _store_gridset() { + $record = 0x0082; // Record identifier + $length = 0x0002; // Bytes to follow + + $fGridSet = !$this->_print_gridlines; // Boolean flag + + $header = pack("vv", $record, $length); + $data = pack("v", $fGridSet); + + $this->_prepend($header . $data); + } + + /* + * Write the WSBOOL BIFF record, mainly for fit-to-page. Used in + * conjunction with the SETUP record. + */ + function _store_wsbool() { + $record = 0x0081; # Record identifier + $length = 0x0002; # Bytes to follow + + // $grbit Option flags + + // The only option that is of interest is the flag for fit to page. + // So we set all the options in one go. + if ($this->_fit_page) { + $grbit = 0x05c1; + } else { + $grbit = 0x04c1; + } + + $header = pack("vv", $record, $length); + $data = pack("v", $grbit); + + $this->_prepend($header . $data); + } + + /* + * Write the HORIZONTALPAGEBREAKS BIFF record. + */ + function _store_hbreak() { + // Return if the user hasn't specified pagebreaks + if(sizeof($this->_hbreaks)==0) { + return; + } + + # Sort and filter array of page breaks + $breaks = $this->_sort_pagebreaks($this->_hbreaks); + + $record = 0x001b; // Record identifier + $cbrk = sizeof($breaks); // Number of page breaks + $length = ($cbrk + 1) * 2; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + $data .= pack("v", $break); + } + + $this->_prepend($header . $data); + } + + /* + * Write the VERTICALPAGEBREAKS BIFF record. + */ + function _store_vbreak() { + // Return if the user hasn't specified pagebreaks + if(sizeof($this->_vbreaks)==0) { + return; + } + + // Sort and filter array of page breaks + $breaks = $this->_sort_pagebreaks($this->_vbreaks); + + $record = 0x001a; // Record identifier + $cbrk = sizeof($breaks); // Number of page breaks + $length = ($cbrk + 1) * 2; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("v", $cbrk); + + // Append each page break + foreach ($breaks as $break) { + $data .= pack("v", $break); + } + + $this->_prepend($header . $data); + } + + /* + * Set the Biff PROTECT record to indicate that the worksheet is + * protected. + */ + function _store_protect() { + // Exit unless sheet protection has been specified + if (!$this->_protect) { + return; + } + + $record = 0x0012; // Record identifier + $length = 0x0002; // Bytes to follow + + $fLock = $this->_protect; // Worksheet is protected + + $header = pack("vv", $record, $length); + $data = pack("v", $fLock); + + $this->_prepend($header . $data); + } + + /* + * Write the worksheet PASSWORD record. + */ + function _store_password() { + // Exit unless sheet protection and password have been specified + if (!$this->_protect || !$this->_password) { + return; + } + + $record = 0x0013; // Record identifier + $length = 0x0002; // Bytes to follow + + $wPassword = $this->_password; // Encoded password + + $header = pack("vv", $record, $length); + $data = pack("v", $wPassword); + + $this->_prepend($header . $data); + } + +############################################################################### +# +# insert_bitmap($row, $col, $filename, $x, $y, $scale_x, $scale_y) +# +# Insert a 24bit bitmap image in a worksheet. The main record required is +# IMDATA but it must be proceeded by a OBJ record to define its position. +# +function insert_bitmap() { + + $_=func_get_args(); + + # Check for a cell reference in A1 notation and substitute row and column + if (preg_match('/^\D/', $_[0])) { + $_ = $this->_substitute_cellref($_); + } + + $row = $_[0]; + $col = $_[1]; + $bitmap = $_[2]; + $x = $_[3] ? $_[3] : 0; + $y = $_[4] ? $_[4] : 0; + $scale_x = $_[5] ? $_[5] : 1; + $scale_y = $_[6] ? $_[6] : 1; + + list($width, $height, $size, $data) = $this->_process_bitmap($bitmap); + + # Scale the frame of the image. + $width *= $scale_x; + $height *= $scale_y; + + # Calculate the vertices of the image and write the OBJ record + $this->_position_image($col, $row, $x, $y, $width, $height); + + # Write the IMDATA record to store the bitmap data + $record = 0x007f; + $length = 8 + $size; + $cf = 0x09; + $env = 0x01; + $lcb = $size; + + $header = pack("vvvvV", $record, $length, $cf, $env, $lcb); + + $this->_append($header . $data); +} + + /* + * Calculate the vertices that define the position of the image as + * required by the OBJ record. + * + * +------------+------------+ + * | A | B | + * +-----+------------+------------+ + * | |(x1,y1) | | + * | 1 |(A1)._______|______ | + * | | | | | + * | | | | | + * +-----+----| BITMAP |-----+ + * | | | | | + * | 2 | |______________. | + * | | | (B2)| + * | | | (x2,y2)| + * +---- +------------+------------+ + * + * Example of a bitmap that covers some of the area from cell A1 to + * cell B2. + * + * Based on the width and height of the bitmap we need to calculate 8 + *vars: + * $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2. + * The width and height of the cells are also variable and have to be + * taken into account. + * The values of $col_start and $row_start are passed in from the calling + * function. The values of $col_end and $row_end are calculated by + * subtracting the width and height of the bitmap from the width and + * height of the underlying cells. + * The vertices are expressed as a percentage of the underlying cell + * width as follows (rhs values are in pixels): + * + * x1 = X / W *1024 + * y1 = Y / H *256 + * x2 = (X-1) / W *1024 + * y2 = (Y-1) / H *256 + * + * Where: X is distance from the left side of the underlying cell + * Y is distance from the top of the underlying cell + * W is the width of the cell + * H is the height of the cell + * + * Note: the SDK incorrectly states that the height should be expressed + * as a percentage of 1024. + */ + function _position_image($col_start, $row_start, $x1, $y1, + $width, $height) { + // $col_start Col containing upper left corner of object + // $x1 Distance to left side of object + + // $row_start Row containing top left corner of object + // $y1 Distance to top of object + + // $col_end Col containing lower right corner of object + // $x2 Distance to right side of object + + // $row_end Row containing bottom right corner of object + // $y2 Distance to bottom of object + + // $width Width of image frame + // $height Height of image frame + + // Initialise end cell to the same as the start cell + $col_end = $col_start; + $row_end = $row_start; + + // Zero the specified offset if greater than the cell dimensions + if ($x1 >= $this->_size_col($col_start)) { + $x1 = 0; + } + if ($y1 >= $this->_size_row($row_start)) { + $y1 = 0; + } + + $width = $width + $x1 -1; + $height = $height + $y1 -1; + + // Subtract the underlying cell widths to find the end cell of the + // image + while ($width >= $this->_size_col($col_end)) { + $width -= $this->_size_col($col_end); + $col_end++; + } + + // Subtract the underlying cell heights to find the end cell of the + // image + while ($height >= $this->_size_row($row_end)) { + $height -= $this->_size_row($row_end); + $row_end++; + } + + // Bitmap isn't allowed to start or finish in a hidden cell, i.e. a + // cell with zero height or width. + if ($this->_size_col($col_start) == 0) { return; } + if ($this->_size_col($col_end) == 0) { return; } + if ($this->_size_row($row_start) == 0) { return; } + if ($this->_size_row($row_end) == 0) { return; } + + // Convert the pixel values to the percentage value expected by Excel + $x1 = $x1 / $this->_size_col($col_start) * 1024; + $y1 = $y1 / $this->_size_row($row_start) * 256; + $x2 = $width / $this->_size_col($col_end) * 1024; + $y2 = $height / $this->_size_row($row_end) * 256; + + $this->_store_obj_picture($col_start, $x1, $row_start, $y1, + $col_end, $x2, $row_end, $y2); + } + + /* + * Convert the width of a cell from user's units to pixels. By + * interpolation the relationship is: y = 7x +5. If the width + * hasn't been set by the user we use the default value. If the + * col is hidden we use a value of zero. + */ + function _size_col($col) { + // Look up the cell value to see if it has been changed + if (isset($this->_col_sizes[$col])) { + if ($this->_col_sizes[$col] == 0) { + return 0; + } else { + return floor(7 * $this->_col_sizes[$col] + 5); + } + } else { + return 64; + } + } + + /* + * Convert the height of a cell from user's units to pixels. By + * interpolation # the relationship is: y = 4/3x. If the height + * hasn't been set by the user we use the default value. If the + * row is hidden we use a value of zero. (Not possible to hide row + * yet). + */ + function _size_row($row) { + // Look up the cell value to see if it has been changed + if (isset($this->_row_sizes[$row])) { + if ($this->_row_sizes[$row] == 0) { + return 0; + } else { + return floor(4/3 * $this->_row_sizes[$row]); + } + } else { + return 17; + } + } + + /* + * Store the OBJ record that precedes an IMDATA record. This could + * be generalized to support other Excel objects. + */ + function _store_obj_picture($col_start, $x1, $row_start, $y1, + $col_end, $x2, $row_end, $y2) { + $record = 0x005d; // Record identifier + $length = 0x003c; // Bytes to follow + + $cObj = 0x0001; // Count of objects in file (set to 1) + $OT = 0x0008; // Object type. 8 = Picture + $id = 0x0001; // Object ID + $grbit = 0x0614; // Option flags + + $colL = $col_start; // Col containing upper left corner of + // object + $dxL = $x1; // Distance from left side of cell + + $rwT = $row_start; // Row containing top left corner of + // object + $dyT = $y1; // Distance from top of cell + + $colR = $col_end; // Col containing lower right corner of + // object + $dxR = $x2; // Distance from right of cell + + $rwB = $row_end; // Row containing bottom right corner of + // object + $dyB = $y2; // Distance from bottom of cell + + $cbMacro = 0x0000; // Length of FMLA structure + $Reserved1 = 0x0000; // Reserved + $Reserved2 = 0x0000; // Reserved + + $icvBack = 0x09; // Background colour + $icvFore = 0x09; // Foreground colour + $fls = 0x00; // Fill pattern + $fAuto = 0x00; // Automatic fill + $icv = 0x08; // Line colour + $lns = 0xff; // Line style + $lnw = 0x01; // Line weight + $fAutoB = 0x00; // Automatic border + $frs = 0x0000; // Frame style + $cf = 0x0009; // Image format, 9 = bitmap + $Reserved3 = 0x0000; // Reserved + $cbPictFmla = 0x0000; // Length of FMLA structure + $Reserved4 = 0x0000; // Reserved + $grbit2 = 0x0001; // Option flags + $Reserved5 = 0x0000; // Reserved + + $header = pack("vv", $record, $length); + $data = pack("V", $cObj); + $data .= pack("v", $OT); + $data .= pack("v", $id); + $data .= pack("v", $grbit); + $data .= pack("v", $colL); + $data .= pack("v", $dxL); + $data .= pack("v", $rwT); + $data .= pack("v", $dyT); + $data .= pack("v", $colR); + $data .= pack("v", $dxR); + $data .= pack("v", $rwB); + $data .= pack("v", $dyB); + $data .= pack("v", $cbMacro); + $data .= pack("V", $Reserved1); + $data .= pack("v", $Reserved2); + $data .= pack("C", $icvBack); + $data .= pack("C", $icvFore); + $data .= pack("C", $fls); + $data .= pack("C", $fAuto); + $data .= pack("C", $icv); + $data .= pack("C", $lns); + $data .= pack("C", $lnw); + $data .= pack("C", $fAutoB); + $data .= pack("v", $frs); + $data .= pack("V", $cf); + $data .= pack("v", $Reserved3); + $data .= pack("v", $cbPictFmla); + $data .= pack("v", $Reserved4); + $data .= pack("v", $grbit2); + $data .= pack("V", $Reserved5); + + $this->_append($header . $data); + } + + /* + * Convert a 24 bit bitmap into the modified internal format used by + * Windows. This is described in BITMAPCOREHEADER and BITMAPCOREINFO + * structures in the MSDN library. + */ + function _process_bitmap($bitmap) { + // Open file and binmode the data in case the platform needs it. + $bmp=fopen($bitmap, "rb"); + if (!$bmp) { + trigger_error("Could not open file '$bitmap'.", E_USER_ERROR); + } + + $data=fread($bmp, filesize($bitmap)); + + // Check that the file is big enough to be a bitmap. + if (strlen($data) <= 0x36) { + trigger_error("$bitmap doesn't contain enough data.", + E_USER_ERROR); + } + + // The first 2 bytes are used to identify the bitmap. + if (substr($data, 0, 2) != "BM") { + trigger_error("$bitmap doesn't appear to to be a ". + "valid bitmap image.", E_USER_ERROR); + } + + // Remove bitmap data: ID. + $data = substr($data, 2); + + // Read and remove the bitmap size. This is more reliable than reading + // the data size at offset 0x22. + $array = unpack("Vsize", $data); + $data = substr($data, 4); + $size = $array["size"]; + $size -= 0x36; # Subtract size of bitmap header. + $size += 0x0C; # Add size of BIFF header. + + // Remove bitmap data: reserved, offset, header length. + $data = substr($data, 12); + + // Read and remove the bitmap width and height. Verify the sizes. + $array = unpack("Vwidth/Vheight", $data); + $data = substr($data, 8); + $width = $array["width"]; + $height = $array["height"]; + + if ($width > 0xFFFF) { + trigger_error("$bitmap: largest image width supported is 64k.", + E_USER_ERROR); + } + + if ($height > 0xFFFF) { + trigger_error("$bitmap: largest image height supported is 64k.", + E_USER_ERROR); + } + + // Read and remove the bitmap planes and bpp data. Verify them. + $array = unpack("vplanes/vbitcount", $data); + $data = substr($data, 4); + $planes = $array["planes"]; + $bitcount = $array["bitcount"]; + + if ($bitcount != 24) { + trigger_error("$bitmap isn't a 24bit true color bitmap.", + E_USER_ERROR); + } + + if ($planes != 1) { + trigger_error("$bitmap: only 1 plane supported in bitmap image.", + E_USER_ERROR); + } + + // Read and remove the bitmap compression. Verify compression. + $array = unpack("Vcompression", $data); + $data = substr($data, 4); + $compression = $array["compression"]; + + if ($compression != 0) { + trigger_error("$bitmap: compression not supported in bitmap image.", + E_USER_ERROR); + } + + // Remove bitmap data: data size, hres, vres, colours, imp. colours. + $data = substr($data, 20); + + // Add the BITMAPCOREHEADER data + $header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18); + $data = $header . $data; + + return array($width, $height, $size, $data); + } + + /* + * Store the window zoom factor. This should be a reduced fraction but for + * simplicity we will store all fractions with a numerator of 100. + */ + function _store_zoom() { + // If scale is 100% we don't need to write a record + if ($this->_zoom == 100) { + return; + } + + $record = 0x00A0; // Record identifier + $length = 0x0004; // Bytes to follow + + $header = pack("vv", $record, $length); + $data = pack("vv", $this->_zoom, 100); + + $this->_append($header . $data); + } + +} + +?> diff --git a/lib/Excel/reader.php b/lib/Excel/reader.php new file mode 100644 index 000000000..97c5becc8 --- /dev/null +++ b/lib/Excel/reader.php @@ -0,0 +1,1084 @@ + +* @license http://www.php.net/license/3_0.txt PHP License 3.0 +* @version CVS: $Id: reader.php 19 2007-03-13 12:42:41Z shangxiao $ +* @link http://pear.php.net/package/Spreadsheet_Excel_Reader +* @see OLE, Spreadsheet_Excel_Writer +*/ + + +//require_once 'PEAR.php'; +require_once 'oleread.inc'; +//require_once 'OLE.php'; + +define('SPREADSHEET_EXCEL_READER_BIFF8', 0x600); +define('SPREADSHEET_EXCEL_READER_BIFF7', 0x500); +define('SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5); +define('SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10); + +define('SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809); +define('SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a); +define('SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85); +define('SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200); +define('SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208); +define('SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7); +define('SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f); +define('SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c); +define('SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6); +define('SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e); +define('SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e); +define('SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd); +define('SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe); +define('SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b); +define('SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc); +define('SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff); +define('SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c); +define('SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204); +define('SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd); +define('SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203); +define('SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18); +define('SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221); +define('SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207); +define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406); +define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6); +define('SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e); +define('SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0); +define('SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205); +define('SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff); +define('SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22); +define('SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5); + +define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS' , 25569); +define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107); +define('SPREADSHEET_EXCEL_READER_MSINADAY', 86400); +//define('SPREADSHEET_EXCEL_READER_MSINADAY', 24 * 60 * 60); + +//define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%.2f"); +define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%s"); + + +/* +* Place includes, constant defines and $_GLOBAL settings here. +* Make sure they have appropriate docblocks to avoid phpDocumentor +* construing they are documented by the page-level docblock. +*/ + +/** +* A class for reading Microsoft Excel Spreadsheets. +* +* Originally developed by Vadim Tkachenko under the name PHPExcelReader. +* (http://sourceforge.net/projects/phpexcelreader) +* Based on the Java version by Andy Khan (http://www.andykhan.com). Now +* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats. +* +* @category Spreadsheet +* @package Spreadsheet_Excel_Reader +* @author Vadim Tkachenko +* @copyright 1997-2005 The PHP Group +* @license http://www.php.net/license/3_0.txt PHP License 3.0 +* @version Release: @package_version@ +* @link http://pear.php.net/package/PackageName +* @see OLE, Spreadsheet_Excel_Writer +*/ +class Spreadsheet_Excel_Reader +{ + /** + * Array of worksheets found + * + * @var array + * @access public + */ + var $boundsheets = array(); + + /** + * Array of format records found + * + * @var array + * @access public + */ + var $formatRecords = array(); + + /** + * todo + * + * @var array + * @access public + */ + var $sst = array(); + + /** + * Array of worksheets + * + * The data is stored in 'cells' and the meta-data is stored in an array + * called 'cellsInfo' + * + * Example: + * + * $sheets --> 'cells' --> row --> column --> Interpreted value + * --> 'cellsInfo' --> row --> column --> 'type' - Can be 'date', 'number', or 'unknown' + * --> 'raw' - The raw data that Excel stores for that data cell + * + * @var array + * @access public + */ + var $sheets = array(); + + /** + * The data returned by OLE + * + * @var string + * @access public + */ + var $data; + + /** + * OLE object for reading the file + * + * @var OLE object + * @access private + */ + var $_ole; + + /** + * Default encoding + * + * @var string + * @access private + */ + var $_defaultEncoding; + + /** + * Default number format + * + * @var integer + * @access private + */ + var $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT; + + /** + * todo + * List of formats to use for each column + * + * @var array + * @access private + */ + var $_columnsFormat = array(); + + /** + * todo + * + * @var integer + * @access private + */ + var $_rowoffset = 1; + + /** + * todo + * + * @var integer + * @access private + */ + var $_coloffset = 1; + + /** + * List of default date formats used by Excel + * + * @var array + * @access public + */ + var $dateFormats = array ( + 0xe => "d/m/Y", + 0xf => "d-M-Y", + 0x10 => "d-M", + 0x11 => "M-Y", + 0x12 => "h:i a", + 0x13 => "h:i:s a", + 0x14 => "H:i", + 0x15 => "H:i:s", + 0x16 => "d/m/Y H:i", + 0x2d => "i:s", + 0x2e => "H:i:s", + 0x2f => "i:s.S"); + + /** + * Default number formats used by Excel + * + * @var array + * @access public + */ + var $numberFormats = array( + 0x1 => "%1.0f", // "0" + 0x2 => "%1.2f", // "0.00", + 0x3 => "%1.0f", //"#,##0", + 0x4 => "%1.2f", //"#,##0.00", + 0x5 => "%1.0f", /*"$#,##0;($#,##0)",*/ + 0x6 => '$%1.0f', /*"$#,##0;($#,##0)",*/ + 0x7 => '$%1.2f', //"$#,##0.00;($#,##0.00)", + 0x8 => '$%1.2f', //"$#,##0.00;($#,##0.00)", + 0x9 => '%1.0f%%', // "0%" + 0xa => '%1.2f%%', // "0.00%" + 0xb => '%1.2f', // 0.00E00", + 0x25 => '%1.0f', // "#,##0;(#,##0)", + 0x26 => '%1.0f', //"#,##0;(#,##0)", + 0x27 => '%1.2f', //"#,##0.00;(#,##0.00)", + 0x28 => '%1.2f', //"#,##0.00;(#,##0.00)", + 0x29 => '%1.0f', //"#,##0;(#,##0)", + 0x2a => '$%1.0f', //"$#,##0;($#,##0)", + 0x2b => '%1.2f', //"#,##0.00;(#,##0.00)", + 0x2c => '$%1.2f', //"$#,##0.00;($#,##0.00)", + 0x30 => '%1.0f'); //"##0.0E0"; + + // }}} + // {{{ Spreadsheet_Excel_Reader() + + /** + * Constructor + * + * Some basic initialisation + */ + function Spreadsheet_Excel_Reader() + { + $this->_ole =& new OLERead(); + $this->setUTFEncoder('iconv'); + } + + // }}} + // {{{ setOutputEncoding() + + /** + * Set the encoding method + * + * @param string Encoding to use + * @access public + */ + function setOutputEncoding($encoding) + { + $this->_defaultEncoding = $encoding; + } + + // }}} + // {{{ setUTFEncoder() + + /** + * $encoder = 'iconv' or 'mb' + * set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding + * set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding + * + * @access public + * @param string Encoding type to use. Either 'iconv' or 'mb' + */ + function setUTFEncoder($encoder = 'iconv') + { + $this->_encoderFunction = ''; + + if ($encoder == 'iconv') { + $this->_encoderFunction = function_exists('iconv') ? 'iconv' : ''; + } elseif ($encoder == 'mb') { + $this->_encoderFunction = function_exists('mb_convert_encoding') ? + 'mb_convert_encoding' : + ''; + } + } + + // }}} + // {{{ setRowColOffset() + + /** + * todo + * + * @access public + * @param offset + */ + function setRowColOffset($iOffset) + { + $this->_rowoffset = $iOffset; + $this->_coloffset = $iOffset; + } + + // }}} + // {{{ setDefaultFormat() + + /** + * Set the default number format + * + * @access public + * @param Default format + */ + function setDefaultFormat($sFormat) + { + $this->_defaultFormat = $sFormat; + } + + // }}} + // {{{ setColumnFormat() + + /** + * Force a column to use a certain format + * + * @access public + * @param integer Column number + * @param string Format + */ + function setColumnFormat($column, $sFormat) + { + $this->_columnsFormat[$column] = $sFormat; + } + + + // }}} + // {{{ read() + + /** + * Read the spreadsheet file using OLE, then parse + * + * @access public + * @param filename + * @todo return a valid value + */ + function read($sFileName) + { + /* + require_once 'OLE.php'; + $ole = new OLE(); + $ole->read($sFileName); + + foreach ($ole->_list as $i => $pps) { + if (($pps->Name == 'Workbook' || $pps->Name == 'Book') && + $pps->Size >= SMALL_BLOCK_THRESHOLD) { + + $this->data = $ole->getData($i, 0, $ole->getDataLength($i)); + } elseif ($pps->Name == 'Root Entry') { + $this->data = $ole->getData($i, 0, $ole->getDataLength($i)); + } + //var_dump(strlen($ole->getData($i, 0, $ole->getDataLength($i))), $pps->Name, md5($this->data), $ole->getDataLength($i)); + } +//exit; + $this->_parse(); + + return sizeof($this->sheets) > 0; + */ + + $res = $this->_ole->read($sFileName); + + // oops, something goes wrong (Darko Miljanovic) + if($res === false) { + // check error code + if($this->_ole->error == 1) { + // bad file + die('The filename ' . $sFileName . ' is not readable'); + } + // check other error codes here (eg bad fileformat, etc...) + } + + $this->data = $this->_ole->getWorkBook(); + + + /* + $res = $this->_ole->read($sFileName); + + if ($this->isError($res)) { +// var_dump($res); + return $this->raiseError($res); + } + + $total = $this->_ole->ppsTotal(); + for ($i = 0; $i < $total; $i++) { + if ($this->_ole->isFile($i)) { + $type = unpack("v", $this->_ole->getData($i, 0, 2)); + if ($type[''] == 0x0809) { // check if it's a BIFF stream + $this->_index = $i; + $this->data = $this->_ole->getData($i, 0, $this->_ole->getDataLength($i)); + break; + } + } + } + + if ($this->_index === null) { + return $this->raiseError("$file doesn't seem to be an Excel file"); + } + + */ + + //echo "data =".$this->data; + //$this->readRecords(); + $this->_parse(); + } + + + // }}} + // {{{ _parse() + + /** + * Parse a workbook + * + * @access private + * @return bool + */ + function _parse() + { + $pos = 0; + + $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; + $length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; + + $version = ord($this->data[$pos + 4]) | ord($this->data[$pos + 5])<<8; + $substreamType = ord($this->data[$pos + 6]) | ord($this->data[$pos + 7])<<8; + //echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n"; + + if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && + ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { + return false; + } + + if ($substreamType != SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS){ + return false; + } + + //print_r($rec); + $pos += $length + 4; + + $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; + $length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; + + while ($code != SPREADSHEET_EXCEL_READER_TYPE_EOF) { + switch ($code) { + case SPREADSHEET_EXCEL_READER_TYPE_SST: + //echo "Type_SST\n"; + $spos = $pos + 4; + $limitpos = $spos + $length; + $uniqueStrings = $this->_GetInt4d($this->data, $spos+4); + $spos += 8; + for ($i = 0; $i < $uniqueStrings; $i++) { + // Read in the number of characters + if ($spos == $limitpos) { + $opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + if ($opcode != 0x3c) { + return -1; + } + $spos += 4; + $limitpos = $spos + $conlength; + } + $numChars = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8); + //echo "i = $i pos = $pos numChars = $numChars "; + $spos += 2; + $optionFlags = ord($this->data[$spos]); + $spos++; + $asciiEncoding = (($optionFlags & 0x01) == 0) ; + $extendedString = ( ($optionFlags & 0x04) != 0); + + // See if string contains formatting information + $richString = ( ($optionFlags & 0x08) != 0); + + if ($richString) { + // Read in the crun + $formattingRuns = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8); + $spos += 2; + } + + if ($extendedString) { + // Read in cchExtRst + $extendedRunLength = $this->_GetInt4d($this->data, $spos); + $spos += 4; + } + + $len = ($asciiEncoding)? $numChars : $numChars*2; + if ($spos + $len < $limitpos) { + $retstr = substr($this->data, $spos, $len); + $spos += $len; + }else{ + // found countinue + $retstr = substr($this->data, $spos, $limitpos - $spos); + $bytesRead = $limitpos - $spos; + $charsLeft = $numChars - (($asciiEncoding) ? $bytesRead : ($bytesRead / 2)); + $spos = $limitpos; + + while ($charsLeft > 0){ + $opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + if ($opcode != 0x3c) { + return -1; + } + $spos += 4; + $limitpos = $spos + $conlength; + $option = ord($this->data[$spos]); + $spos += 1; + if ($asciiEncoding && ($option == 0)) { + $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); + $retstr .= substr($this->data, $spos, $len); + $charsLeft -= $len; + $asciiEncoding = true; + }elseif (!$asciiEncoding && ($option != 0)){ + $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); + $retstr .= substr($this->data, $spos, $len); + $charsLeft -= $len/2; + $asciiEncoding = false; + }elseif (!$asciiEncoding && ($option == 0)) { + // Bummer - the string starts off as Unicode, but after the + // continuation it is in straightforward ASCII encoding + $len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength); + for ($j = 0; $j < $len; $j++) { + $retstr .= $this->data[$spos + $j].chr(0); + } + $charsLeft -= $len; + $asciiEncoding = false; + }else{ + $newstr = ''; + for ($j = 0; $j < strlen($retstr); $j++) { + $newstr = $retstr[$j].chr(0); + } + $retstr = $newstr; + $len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength); + $retstr .= substr($this->data, $spos, $len); + $charsLeft -= $len/2; + $asciiEncoding = false; + //echo "Izavrat\n"; + } + $spos += $len; + + } + } + $retstr = ($asciiEncoding) ? $retstr : $this->_encodeUTF16($retstr); +// echo "Str $i = $retstr\n"; + if ($richString){ + $spos += 4 * $formattingRuns; + } + + // For extended strings, skip over the extended string data + if ($extendedString) { + $spos += $extendedRunLength; + } + //if ($retstr == 'Derby'){ + // echo "bb\n"; + //} + $this->sst[]=$retstr; + } + /*$continueRecords = array(); + while ($this->getNextCode() == Type_CONTINUE) { + $continueRecords[] = &$this->nextRecord(); + } + //echo " 1 Type_SST\n"; + $this->shareStrings = new SSTRecord($r, $continueRecords); + //print_r($this->shareStrings->strings); + */ + // echo 'SST read: '.($time_end-$time_start)."\n"; + break; + + case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS: + return false; + break; + case SPREADSHEET_EXCEL_READER_TYPE_NAME: + //echo "Type_NAME\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_FORMAT: + $indexCode = ord($this->data[$pos+4]) | ord($this->data[$pos+5]) << 8; + + if ($version == SPREADSHEET_EXCEL_READER_BIFF8) { + $numchars = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8; + if (ord($this->data[$pos+8]) == 0){ + $formatString = substr($this->data, $pos+9, $numchars); + } else { + $formatString = substr($this->data, $pos+9, $numchars*2); + } + } else { + $numchars = ord($this->data[$pos+6]); + $formatString = substr($this->data, $pos+7, $numchars*2); + } + + $this->formatRecords[$indexCode] = $formatString; + // echo "Type.FORMAT\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_XF: + //global $dateFormats, $numberFormats; + $indexCode = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8; + //echo "\nType.XF ".count($this->formatRecords['xfrecords'])." $indexCode "; + if (array_key_exists($indexCode, $this->dateFormats)) { + //echo "isdate ".$dateFormats[$indexCode]; + $this->formatRecords['xfrecords'][] = array( + 'type' => 'date', + 'format' => $this->dateFormats[$indexCode] + ); + }elseif (array_key_exists($indexCode, $this->numberFormats)) { + //echo "isnumber ".$this->numberFormats[$indexCode]; + $this->formatRecords['xfrecords'][] = array( + 'type' => 'number', + 'format' => $this->numberFormats[$indexCode] + ); + }else{ + $isdate = FALSE; + if ($indexCode > 0){ + if (isset($this->formatRecords[$indexCode])) + $formatstr = $this->formatRecords[$indexCode]; + //echo '.other.'; + //echo "\ndate-time=$formatstr=\n"; + if ($formatstr) + if (preg_match("/[^hmsday\/\-:\s]/i", $formatstr) == 0) { // found day and time format + $isdate = TRUE; + $formatstr = str_replace('mm', 'i', $formatstr); + $formatstr = str_replace('h', 'H', $formatstr); + //echo "\ndate-time $formatstr \n"; + } + } + + if ($isdate){ + $this->formatRecords['xfrecords'][] = array( + 'type' => 'date', + 'format' => $formatstr, + ); + }else{ + $this->formatRecords['xfrecords'][] = array( + 'type' => 'other', + 'format' => '', + 'code' => $indexCode + ); + } + } + //echo "\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR: + //echo "Type.NINETEENFOUR\n"; + $this->nineteenFour = (ord($this->data[$pos+4]) == 1); + break; + case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET: + //echo "Type.BOUNDSHEET\n"; + $rec_offset = $this->_GetInt4d($this->data, $pos+4); + $rec_typeFlag = ord($this->data[$pos+8]); + $rec_visibilityFlag = ord($this->data[$pos+9]); + $rec_length = ord($this->data[$pos+10]); + + if ($version == SPREADSHEET_EXCEL_READER_BIFF8){ + $chartype = ord($this->data[$pos+11]); + if ($chartype == 0){ + $rec_name = substr($this->data, $pos+12, $rec_length); + } else { + $rec_name = $this->_encodeUTF16(substr($this->data, $pos+12, $rec_length*2)); + } + }elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){ + $rec_name = substr($this->data, $pos+11, $rec_length); + } + $this->boundsheets[] = array('name'=>$rec_name, + 'offset'=>$rec_offset); + + break; + + } + + //echo "Code = ".base_convert($r['code'],10,16)."\n"; + $pos += $length + 4; + $code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8; + $length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8; + + //$r = &$this->nextRecord(); + //echo "1 Code = ".base_convert($r['code'],10,16)."\n"; + } + + foreach ($this->boundsheets as $key=>$val){ + $this->sn = $key; + $this->_parsesheet($val['offset']); + } + return true; + + } + + /** + * Parse a worksheet + * + * @access private + * @param todo + * @todo fix return codes + */ + function _parsesheet($spos) + { + $cont = true; + // read BOF + $code = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + + $version = ord($this->data[$spos + 4]) | ord($this->data[$spos + 5])<<8; + $substreamType = ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8; + + if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && ($version != SPREADSHEET_EXCEL_READER_BIFF7)) { + return -1; + } + + if ($substreamType != SPREADSHEET_EXCEL_READER_WORKSHEET){ + return -2; + } + //echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n"; + $spos += $length + 4; + //var_dump($this->formatRecords); + //echo "code $code $length"; + while($cont) { + //echo "mem= ".memory_get_usage()."\n"; +// $r = &$this->file->nextRecord(); + $lowcode = ord($this->data[$spos]); + if ($lowcode == SPREADSHEET_EXCEL_READER_TYPE_EOF) break; + $code = $lowcode | ord($this->data[$spos+1])<<8; + $length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $spos += 4; + $this->sheets[$this->sn]['maxrow'] = $this->_rowoffset - 1; + $this->sheets[$this->sn]['maxcol'] = $this->_coloffset - 1; + //echo "Code=".base_convert($code,10,16)." $code\n"; + unset($this->rectype); + $this->multiplier = 1; // need for format with % + switch ($code) { + case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION: + //echo 'Type_DIMENSION '; + if (!isset($this->numRows)) { + if (($length == 10) || ($version == SPREADSHEET_EXCEL_READER_BIFF7)){ + $this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+2]) | ord($this->data[$spos+3]) << 8; + $this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+6]) | ord($this->data[$spos+7]) << 8; + } else { + $this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; + $this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+10]) | ord($this->data[$spos+11]) << 8; + } + } + //echo 'numRows '.$this->numRows.' '.$this->numCols."\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS: + $cellRanges = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + for ($i = 0; $i < $cellRanges; $i++) { + $fr = ord($this->data[$spos + 8*$i + 2]) | ord($this->data[$spos + 8*$i + 3])<<8; + $lr = ord($this->data[$spos + 8*$i + 4]) | ord($this->data[$spos + 8*$i + 5])<<8; + $fc = ord($this->data[$spos + 8*$i + 6]) | ord($this->data[$spos + 8*$i + 7])<<8; + $lc = ord($this->data[$spos + 8*$i + 8]) | ord($this->data[$spos + 8*$i + 9])<<8; + //$this->sheets[$this->sn]['mergedCells'][] = array($fr + 1, $fc + 1, $lr + 1, $lc + 1); + if ($lr - $fr > 0) { + $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['rowspan'] = $lr - $fr + 1; + } + if ($lc - $fc > 0) { + $this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['colspan'] = $lc - $fc + 1; + } + } + //echo "Merged Cells $cellRanges $lr $fr $lc $fc\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_RK: + case SPREADSHEET_EXCEL_READER_TYPE_RK2: + //echo 'SPREADSHEET_EXCEL_READER_TYPE_RK'."\n"; + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $rknum = $this->_GetInt4d($this->data, $spos + 6); + $numValue = $this->_GetIEEE754($rknum); + //echo $numValue." "; + if ($this->isDate($spos)) { + list($string, $raw) = $this->createDate($numValue); + }else{ + $raw = $numValue; + if (isset($this->_columnsFormat[$column + 1])){ + $this->curformat = $this->_columnsFormat[$column + 1]; + } + $string = sprintf($this->curformat, $numValue * $this->multiplier); + //$this->addcell(RKRecord($r)); + } + $this->addcell($row, $column, $string, $raw); + //echo "Type_RK $row $column $string $raw {$this->curformat}\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_LABELSST: + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5])<<8; + $index = $this->_GetInt4d($this->data, $spos + 6); + //var_dump($this->sst); + $this->addcell($row, $column, $this->sst[$index]); + //echo "LabelSST $row $column $string\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_MULRK: + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $colFirst = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $colLast = ord($this->data[$spos + $length - 2]) | ord($this->data[$spos + $length - 1])<<8; + $columns = $colLast - $colFirst + 1; + $tmppos = $spos+4; + for ($i = 0; $i < $columns; $i++) { + $numValue = $this->_GetIEEE754($this->_GetInt4d($this->data, $tmppos + 2)); + if ($this->isDate($tmppos-4)) { + list($string, $raw) = $this->createDate($numValue); + }else{ + $raw = $numValue; + if (isset($this->_columnsFormat[$colFirst + $i + 1])){ + $this->curformat = $this->_columnsFormat[$colFirst + $i + 1]; + } + $string = sprintf($this->curformat, $numValue * $this->multiplier); + } + //$rec['rknumbers'][$i]['xfindex'] = ord($rec['data'][$pos]) | ord($rec['data'][$pos+1]) << 8; + $tmppos += 6; + $this->addcell($row, $colFirst + $i, $string, $raw); + //echo "MULRK $row ".($colFirst + $i)." $string\n"; + } + //MulRKRecord($r); + // Get the individual cell records from the multiple record + //$num = ; + + break; + case SPREADSHEET_EXCEL_READER_TYPE_NUMBER: + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent + if ($this->isDate($spos)) { + list($string, $raw) = $this->createDate($tmp['double']); + // $this->addcell(DateRecord($r, 1)); + }else{ + //$raw = $tmp['']; + if (isset($this->_columnsFormat[$column + 1])){ + $this->curformat = $this->_columnsFormat[$column + 1]; + } + $raw = $this->createNumber($spos); + $string = sprintf($this->curformat, $raw * $this->multiplier); + + // $this->addcell(NumberRecord($r)); + } + $this->addcell($row, $column, $string, $raw); + //echo "Number $row $column $string\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_FORMULA: + case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2: + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + if ((ord($this->data[$spos+6])==0) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { + //String formula. Result follows in a STRING record + //echo "FORMULA $row $column Formula with a string
    \n"; + } elseif ((ord($this->data[$spos+6])==1) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { + //Boolean formula. Result is in +2; 0=false,1=true + } elseif ((ord($this->data[$spos+6])==2) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { + //Error formula. Error code is in +2; + } elseif ((ord($this->data[$spos+6])==3) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) { + //Formula result is a null string. + } else { + // result is a number, so first 14 bytes are just like a _NUMBER record + $tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent + if ($this->isDate($spos)) { + list($string, $raw) = $this->createDate($tmp['double']); + // $this->addcell(DateRecord($r, 1)); + }else{ + //$raw = $tmp['']; + if (isset($this->_columnsFormat[$column + 1])){ + $this->curformat = $this->_columnsFormat[$column + 1]; + } + $raw = $this->createNumber($spos); + $string = sprintf($this->curformat, $raw * $this->multiplier); + + // $this->addcell(NumberRecord($r)); + } + $this->addcell($row, $column, $string, $raw); + //echo "Number $row $column $string\n"; + } + break; + case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR: + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $string = ord($this->data[$spos+6]); + $this->addcell($row, $column, $string); + //echo 'Type_BOOLERR '."\n"; + break; + case SPREADSHEET_EXCEL_READER_TYPE_ROW: + case SPREADSHEET_EXCEL_READER_TYPE_DBCELL: + case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK: + break; + case SPREADSHEET_EXCEL_READER_TYPE_LABEL: + $row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8; + $column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8; + $this->addcell($row, $column, substr($this->data, $spos + 8, ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8)); + + // $this->addcell(LabelRecord($r)); + break; + + case SPREADSHEET_EXCEL_READER_TYPE_EOF: + $cont = false; + break; + default: + //echo ' unknown :'.base_convert($r['code'],10,16)."\n"; + break; + + } + $spos += $length; + } + + if (!isset($this->sheets[$this->sn]['numRows'])) + $this->sheets[$this->sn]['numRows'] = $this->sheets[$this->sn]['maxrow']; + if (!isset($this->sheets[$this->sn]['numCols'])) + $this->sheets[$this->sn]['numCols'] = $this->sheets[$this->sn]['maxcol']; + + } + + /** + * Check whether the current record read is a date + * + * @param todo + * @return boolean True if date, false otherwise + */ + function isDate($spos) + { + //$xfindex = GetInt2d(, 4); + $xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8; + //echo 'check is date '.$xfindex.' '.$this->formatRecords['xfrecords'][$xfindex]['type']."\n"; + //var_dump($this->formatRecords['xfrecords'][$xfindex]); + if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'date') { + $this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format']; + $this->rectype = 'date'; + return true; + } else { + if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'number') { + $this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format']; + $this->rectype = 'number'; + if (($xfindex == 0x9) || ($xfindex == 0xa)){ + $this->multiplier = 100; + } + }else{ + $this->curformat = $this->_defaultFormat; + $this->rectype = 'unknown'; + } + return false; + } + } + + //}}} + //{{{ createDate() + + /** + * Convert the raw Excel date into a human readable format + * + * Dates in Excel are stored as number of seconds from an epoch. On + * Windows, the epoch is 30/12/1899 and on Mac it's 01/01/1904 + * + * @access private + * @param integer The raw Excel value to convert + * @return array First element is the converted date, the second element is number a unix timestamp + */ + function createDate($numValue) + { + if ($numValue > 1) { + $utcDays = $numValue - ($this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS); + $utcValue = round(($utcDays+1) * SPREADSHEET_EXCEL_READER_MSINADAY); + $string = date ($this->curformat, $utcValue); + $raw = $utcValue; + } else { + $raw = $numValue; + $hours = floor($numValue * 24); + $mins = floor($numValue * 24 * 60) - $hours * 60; + $secs = floor($numValue * SPREADSHEET_EXCEL_READER_MSINADAY) - $hours * 60 * 60 - $mins * 60; + $string = date ($this->curformat, mktime($hours, $mins, $secs)); + } + + return array($string, $raw); + } + + function createNumber($spos) + { + $rknumhigh = $this->_GetInt4d($this->data, $spos + 10); + $rknumlow = $this->_GetInt4d($this->data, $spos + 6); + //for ($i=0; $i<8; $i++) { echo ord($this->data[$i+$spos+6]) . " "; } echo "
    "; + $sign = ($rknumhigh & 0x80000000) >> 31; + $exp = ($rknumhigh & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknumhigh & 0x000fffff)); + $mantissalow1 = ($rknumlow & 0x80000000) >> 31; + $mantissalow2 = ($rknumlow & 0x7fffffff); + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); + if ($mantissalow1 != 0) $value += 1 / pow (2 , (21 - ($exp - 1023))); + $value += $mantissalow2 / pow (2 , (52 - ($exp - 1023))); + //echo "Sign = $sign, Exp = $exp, mantissahighx = $mantissa, mantissalow1 = $mantissalow1, mantissalow2 = $mantissalow2
    \n"; + if ($sign) {$value = -1 * $value;} + return $value; + } + + function addcell($row, $col, $string, $raw = '') + { + //echo "ADD cel $row-$col $string\n"; + $this->sheets[$this->sn]['maxrow'] = max($this->sheets[$this->sn]['maxrow'], $row + $this->_rowoffset); + $this->sheets[$this->sn]['maxcol'] = max($this->sheets[$this->sn]['maxcol'], $col + $this->_coloffset); + $this->sheets[$this->sn]['cells'][$row + $this->_rowoffset][$col + $this->_coloffset] = $string; + if ($raw) + $this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['raw'] = $raw; + if (isset($this->rectype)) + $this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['type'] = $this->rectype; + + } + + + function _GetIEEE754($rknum) + { + if (($rknum & 0x02) != 0) { + $value = $rknum >> 2; + } else { +//mmp +// first comment out the previously existing 7 lines of code here +// $tmp = unpack("d", pack("VV", 0, ($rknum & 0xfffffffc))); +// //$value = $tmp['']; +// if (array_key_exists(1, $tmp)) { +// $value = $tmp[1]; +// } else { +// $value = $tmp['']; +// } +// I got my info on IEEE754 encoding from +// http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html +// The RK format calls for using only the most significant 30 bits of the +// 64 bit floating point value. The other 34 bits are assumed to be 0 +// So, we use the upper 30 bits of $rknum as follows... + $sign = ($rknum & 0x80000000) >> 31; + $exp = ($rknum & 0x7ff00000) >> 20; + $mantissa = (0x100000 | ($rknum & 0x000ffffc)); + $value = $mantissa / pow( 2 , (20- ($exp - 1023))); + if ($sign) {$value = -1 * $value;} +//end of changes by mmp + + } + + if (($rknum & 0x01) != 0) { + $value /= 100; + } + return $value; + } + + function _encodeUTF16($string) + { + $result = $string; + if ($this->_defaultEncoding){ + switch ($this->_encoderFunction){ + case 'iconv' : $result = iconv('UTF-16LE', $this->_defaultEncoding, $string); + break; + case 'mb_convert_encoding' : $result = mb_convert_encoding($string, $this->_defaultEncoding, 'UTF-16LE' ); + break; + } + } + return $result; + } + + function _GetInt4d($data, $pos) + { + $value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24); + if ($value>=4294967294) + { + $value=-2; + } + return $value; + } + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> diff --git a/lib/common.lib.php b/lib/common.lib.php index 7902c8baa..b884704cd 100644 --- a/lib/common.lib.php +++ b/lib/common.lib.php @@ -1205,7 +1205,7 @@ function get_sideview($mb_id, $name='', $email='', $homepage='') { global $config; global $g5; - global $bo_table, $sca, $is_admin; + global $bo_table, $sca, $is_admin, $member; $email = base64_encode($email); $homepage = set_http($homepage); @@ -1273,6 +1273,10 @@ function get_sideview($mb_id, $name='', $email='', $homepage='') } if($mb_id) $str2 .= "
    전체게시물\n"; + if($g5['sms5_use_sideview']){ + $mb = get_member($mb_id, " mb_open, mb_sms , mb_hp "); + if( $mb['mb_open'] && $mb['mb_sms'] && $mb['mb_hp'] ) $str2 .= "문자보내기\n"; + } if($is_admin == "super" && $mb_id) { $str2 .= "회원정보변경\n"; $str2 .= "포인트내역\n"; diff --git a/plugin/sms5/JSON.php b/plugin/sms5/JSON.php new file mode 100644 index 000000000..8dc8a6f01 --- /dev/null +++ b/plugin/sms5/JSON.php @@ -0,0 +1,933 @@ + + * @author Matt Knapp + * @author Brett Stimmerman + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_USE_TO_JSON', 64); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects + * It serializes the return value from the toJSON call rather + * than the object it'self, toJSON can return associative arrays, + * strings or numbers, if you return an object, make sure it does + * not have a toJSON method, otherwise an error will occur. + */ + function Services_JSON($use = 0) + { + $this->use = $use; + $this->_mb_strlen = function_exists('mb_strlen'); + $this->_mb_convert_encoding = function_exists('mb_convert_encoding'); + $this->_mb_substr = function_exists('mb_substr'); + } + // private - cache the mbstring lookup results.. + var $_mb_strlen = false; + var $_mb_substr = false; + var $_mb_convert_encoding = false; + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if($this->_mb_convert_encoding) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if($this->_mb_convert_encoding) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch($this->strlen8($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format (and sends JSON Header) + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + header('Content-type: application/json'); + return $this->encodeUnsafe($var); + } + /** + * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!) + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encodeUnsafe($var) + { + // see bug #16908 - regarding numeric locale printing + $lc = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, 'C'); + $ret = $this->_encode($var); + setlocale(LC_NUMERIC, $lc); + return $ret; + + } + /** + * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function _encode($var) + { + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = $this->strlen8($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + if ($c+1 >= $strlen_var) { + $c += 1; + $ascii .= '?'; + break; + } + + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + if ($c+2 >= $strlen_var) { + $c += 2; + $ascii .= '?'; + break; + } + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + if ($c+3 >= $strlen_var) { + $c += 3; + $ascii .= '?'; + break; + } + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + if ($c+4 >= $strlen_var) { + $c += 4; + $ascii .= '?'; + break; + } + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + if ($c+5 >= $strlen_var) { + $c += 5; + $ascii .= '?'; + break; + } + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, '_encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + + // support toJSON methods. + if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) { + // this may end up allowing unlimited recursion + // so we check the return value to make sure it's not got the same method. + $recode = $var->toJSON(); + + if (method_exists($recode, 'toJSON')) { + + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(class_name($var). + " toJSON returned an object with a toJSON method."); + + } + + return $this->_encode( $recode ); + } + + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->_encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->_encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = $this->substr8($str, 0, 1); + $chrs = $this->substr8($str, 1, -1); + $utf8 = ''; + $strlen_chrs = $this->strlen8($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2))) + . chr(hexdec($this->substr8($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = $this->substr8($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = $this->strlen8($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = $this->substr8($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } + + /** + * Calculates length of string in bytes + * @param string + * @return integer length + */ + function strlen8( $str ) + { + if ( $this->_mb_strlen ) { + return mb_strlen( $str, "8bit" ); + } + return strlen( $str ); + } + + /** + * Returns part of a string, interpreting $start and $length as number of bytes. + * @param string + * @param integer start + * @param integer length + * @return integer length + */ + function substr8( $string, $start, $length=false ) + { + if ( $length === false ) { + $length = $this->strlen8( $string ) - $start; + } + if ( $this->_mb_substr ) { + return mb_substr( $string, $start, $length, "8bit" ); + } + return substr( $string, $start, $length ); + } + +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} diff --git a/plugin/sms5/_common.php b/plugin/sms5/_common.php new file mode 100644 index 000000000..14553420b --- /dev/null +++ b/plugin/sms5/_common.php @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/plugin/sms5/ajax.sms_emoticon.php b/plugin/sms5/ajax.sms_emoticon.php new file mode 100644 index 000000000..7299bc157 --- /dev/null +++ b/plugin/sms5/ajax.sms_emoticon.php @@ -0,0 +1,70 @@ +encode($data) ); + } +} + +$page_size = 9; + +if (!$page) $page = 1; + +if (is_numeric($fg_no)) + $sql_group = " and fg_no='$fg_no' "; +else + $sql_group = ""; + +if ($st == 'all') { + $sql_search = "and (fo_name like '%{$sv}%' or fo_content like '%{$sv}%')"; +} else if ($st == 'name') { + $sql_search = "and fo_name like '%{$sv}%'"; +} else if ($st == 'content') { + $sql_search = "and fo_content like '%{$sv}%'"; +} else { + $sql_search = ''; +} + +$total_res = sql_fetch("select count(*) as cnt from {$g5['sms5_form_table']} where fg_member = 1 $sql_group $sql_search"); +$total_count = $total_res['cnt']; + +$total_page = (int)($total_count/$page_size) + ($total_count%$page_size==0 ? 0 : 1); +$page_start = $page_size * ( $page - 1 ); + +$vnum = $total_count - (($page-1) * $page_size); + +$group = array(); +$qry = sql_query("select * from {$g5['sms5_form_group_table']} where fg_member = 1 order by fg_name"); +while ($res = sql_fetch_array($qry)) array_push($group, $res); + +$res = sql_fetch("select count(*) as cnt from {$g5['sms5_form_table']} where fg_no=0"); +$no_count = $res['cnt']; + +$count = 1; +$qry = sql_query("select * from {$g5['sms5_form_table']} where fg_member = 1 $sql_group $sql_search order by fo_no desc limit $page_start, $page_size"); +$list_text = array(); + +for($k=0;$res = sql_fetch_array($qry);$k++) +{ + $tmp = sql_fetch("select fg_name from {$g5['sms5_form_group_table']} where fg_no='{$res['fg_no']}'"); + if (!$tmp) + $group_name = '미분류'; + else + $group_name = $tmp['fg_name']; + + $list_text[$k]['fo_no'] = $res['fo_no']; + $list_text[$k]['fo_content'] = $res['fo_content']; + $list_text[$k]['fo_content'] = $res['fo_content']; + $list_text[$k]['fo_name'] = cut_str($res['fo_name'],20); +} + +$arr_ajax_msg['error'] = ""; +$arr_ajax_msg['list_text'] = $list_text; +$arr_ajax_msg['page'] = $page; +$arr_ajax_msg['total_count'] = $total_count; +$arr_ajax_msg['total_page'] = $total_page; +die( json_encode($arr_ajax_msg) ); +?> \ No newline at end of file diff --git a/plugin/sms5/index.php b/plugin/sms5/index.php new file mode 100644 index 000000000..087803eb5 --- /dev/null +++ b/plugin/sms5/index.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/plugin/sms5/skin/basic/img/ajax-loader.gif b/plugin/sms5/skin/basic/img/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..96fefbc5130a6514a03709b2b8f40a88cf82fae2 GIT binary patch literal 1924 zcmciDX;70#00!U)Urwn9NDf8|Nk{?(l!}Qm1QSd^sNw-w96TTt3HoXQ;KEY5&xQ_@`6p)R|7Ze|KlUecpGz-Ms;R zyF6JG&`!`>5GcFv1qcN4J{Gsm76V0CaaFx8^nHnS`e;crfkNLKn`)Z<^T57ad%!m& znBjBCcl%a%%QdDs?C*Arv(c=i=qQKtu}LuwEDq~JJeL)!)orci6g~AYxE|RX;ebAo47rl|h=>fFdqNlMi+s5|5((!#k{zHt zS4${~6YmJ+LP`!vWo6}8<<}Gyn@jVh*X?p$m3wP|x=n>Zs#J3mrIod#8ydYktK+_5 zs4DyV^@f4FgYsU%9mnpoN%&iYhYk}DGH~~D#_=O(M~5nA4bz6n{>8q9mvhgTy*I7~ z9Q!LUrU41@LFa}cvtO@kanB`aV)-0EL8CXuXuN&&E3N+SN8Uvl=TJUl1vbeJ7$&sU<&M?!@% z6R@)CdP1=*nr)Y}wS-;|s5hl$1h!z>TD`TXrsnNkbt+L!x6%w88YcBQk4Bv_j^lKM ziHjYiN&7p*r?`8@o{@^VyR!q+`X%M!z{30+(#H}tjqX+!#}Ldwy>{1Q{pHux+4aoU zxjcK6Rk;hAlV=gYbF}?hcWFmR;}PG3pZ0=`SM|EHf&P0YnNQ`;MtpyG`1qQx-R@pc z79>3*H!q)GP-qQ36T*Nc077IU&smTFM_u%$$+^gyc-MLWRY_CEs9G+R z%M~JVq*P>6QrgwC(&3OznUnDhwTgKk-$h$<)#@`tjaDK^j|h^kuM*rXwuV`xN(5cn)S zOa`=v_z|=+IQFvR9r9ht&@lF@qh2&{zlv~M*2BA1-09zC+tNBs8&?l5j;de%FxmX0 z%c{bW|3XOJEtn5~9QIizhV7q=&ZiY1vl%7mbeb?lELr&x_k$s12sj~#1ZG!ZOYsU* z0Q3_?HATa^*#d-e2o2PxP$kAJMwwz@AX*Ru4Ui!kOa~ZveL8r`cjW1rl*cVoyob6+ zyb<-Nq__VSZdKn07fnpVzi;p#d?545r3fk+A%-a_l^#L3YqbE}gX5r~g!^FI$hs5Q zrrMhcTKgJJeS#WyU8zEu6Y-ZT25iCX2jY85h@@>+BblK-6Pp5%LT4aLEFAo05Ys$S(J2IA~KZ^z>7G@QVNZYl8|LSC;?yS86TEkTBr=dT*}JH%@d?)w5@IJ z@5^lkv%3%P!*yms4qS1N5X-Q`;R>vtI^Z#AZvw^D>4W{#%ZkO(NL@+Y5LW7x}u!bhj)Gf3x+_0a&Nb`z37R$Ctf6$)nzUh%Dz%Yf8 zFFe1E4-WY<^eAh8Zb4yDaY?AT0|62oPqq{@4yM`&Lm<~qp<@|k-qn${=TKEtC5^We z4Xv|;9#+^vt00|{-uF+%mM*7ZTx$hWML$itg`Xf!V#i3+cq4Ikuwbt5ss2g-;>&S^ O2}sxo@>+QV&EpTcIvUad literal 0 HcmV?d00001 diff --git a/plugin/sms5/skin/basic/img/box_ico.gif b/plugin/sms5/skin/basic/img/box_ico.gif new file mode 100644 index 0000000000000000000000000000000000000000..9d57d3668813c2a07fd22931284e2baf5136b3c3 GIT binary patch literal 60 zcmZ?wbhEHbWM|-IXkcXc{buI>|Nj+#vM@3*Ff!;c00Bsbfl0WBKYhZ9e@wHBZbiSn NWL*~87{th64FDS>5}*J8 literal 0 HcmV?d00001 diff --git a/plugin/sms5/skin/basic/img/scemo_ico.gif b/plugin/sms5/skin/basic/img/scemo_ico.gif new file mode 100644 index 0000000000000000000000000000000000000000..625048e490f176a8b00d8f11260f3cf2f89a6614 GIT binary patch literal 67 zcmZ?wbhEHbWM|-In8?InVPT=6q4EF!e+C8y#h)yU3=B*RIzTo9NS=X7WQu$`OV literal 0 HcmV?d00001 diff --git a/plugin/sms5/skin/basic/mobile.css b/plugin/sms5/skin/basic/mobile.css new file mode 100644 index 000000000..24956c39b --- /dev/null +++ b/plugin/sms5/skin/basic/mobile.css @@ -0,0 +1,48 @@ +#sms5_send {position:relative} + +.sms5_box {position:relative;padding:10px;border-radius:5px;background:#fbec99} +.sms5_box .box_ico {position:absolute;top:20px;left:-7px;width:7px;height:13px;background:url('img/box_ico.gif') no-repeat} +.sms5_box .box_txt {border:0;background:transparent;word-break:break-all;resize:none;overflow:hidden} +.sms5_box .box_square {width:100px;height:90px} + +#send_write {padding:0 20px !important} +#send_write h2 {padding:0 0 10px} +#send_write .sms5_box {margin:0 0 5px;text-align:center} +#send_write .box_txt {width:90%;height:80px} +#wr_message_lbl {position:absolute;top:45px;left:48%;color:#999;font-size:0.95em;letter-spacing:-0.1em} + +.write_inner {position:relative;padding:10px 0;border-bottom:1px solid #efefef;zoom:1} +.write_inner:after {display:block;visibility:hidden;clear:both;content:''} +.write_inner h2 {margin:0;padding:0 0 20px !important} +.write_floater {position:absolute;top:10px;right:0;text-align:right} +.write_floater_btn {margin:0;padding:0;border:0;background:transparent;color:#999;font-size:0.95em;letter-spacing:-0.1em} + +#write_rcv {margin:0 0 10px} +#write_rcv strong {display:inline-block;margin:0 10px 0 0} +#write_reply label {display:inline-block;margin:0 10px 0 0;font-weight:bold} +#write_reply #mh_reply {padding:0 5px;width:90px;height:20px;border:1px solid #e9e9e9;text-align:center;line-height:1.8em} + +#write_rsv .rsv_line {display:block;height:10px} + +.write_scemo strong {display:block;margin:0 0 10px} +.write_scemo .scemo_btn {margin:0 0 1px;padding:10px 0;width:100%;border:0;background:#686868;color:#fff;text-align:center} +.write_scemo .scemo_list {display:none;letter-spacing:-4px} +.write_scemo .list_closer {margin:5px 0;text-align:right} +.write_scemo .list_closer_btn {margin:0;padding:10px 0;width:100%;border:0;background:#383838;color:#fff;letter-spacing:0} +.write_scemo .scemo_add {margin:0;padding:0;width:25%;height:40px;border:1px solid #e9e9e9;background:transparent;letter-spacing:0} +#write_sc .scemo_list {margin:0 0 20px} + +#sms_byte {position:absolute;top:-27px;right:0;color:#999} + +#send_emo {position:relative;padding:20px;border-top:1px solid #e9e9e9;background:#f7f7f7} +#send_emo h2 {margin:0 0 20px} +#send_emo .tmp_loading {display:block;padding:180px 0 0;text-align:center} +#send_emo #emo_sel {position:absolute;top:20px;right:20px;margin:0} +#send_emo .emo_list {margin:0;padding:0;list-style:none} +#send_emo li {float:left;margin:0 2% 10px 0;width:49%} +#send_emo li:nth-of-type(even) {margin:0 0 10px} +#send_emo .sms5_box {background:#fbec99} +#send_emo .box_ico {display:none} +#send_emo .box_txt {cursor:pointer} +#send_emo .emo_tit {display:block;height:20px;line-height:2em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +#send_emo .btn_submit {padding:0 5px;height:24px;border:1px solid #ccc;background:#fafafa;color:#000;font-size:0.95em;vertical-align:middle;cursor:pointer} \ No newline at end of file diff --git a/plugin/sms5/skin/basic/style.css b/plugin/sms5/skin/basic/style.css new file mode 100644 index 000000000..36bb9c94f --- /dev/null +++ b/plugin/sms5/skin/basic/style.css @@ -0,0 +1,50 @@ +#sms5_send {position:relative} + +.sms5_box {position:relative;padding:10px;border-radius:5px;background:#fbec99} +.sms5_box .box_ico {position:absolute;top:20px;left:-7px;width:7px;height:13px;background:url('img/box_ico.gif') no-repeat} +.sms5_box .box_txt {border:0;background:transparent;word-break:break-all;resize:none;overflow:hidden} +.sms5_box .box_square {width:100px;height:90px} + +#send_write {padding:0 20px !important} +#send_write h2 {padding:0 0 10px} +#send_write .sms5_box {margin:0 0 5px;text-align:center} +#send_write .box_txt {width:390px;height:80px} +#wr_message_lbl {position:absolute;top:45px;left:200px;color:#999;font-size:0.95em;letter-spacing:-0.1em} + +.write_inner {position:relative;padding:10px 0;border-bottom:1px solid #efefef;zoom:1} +.write_inner:after {display:block;visibility:hidden;clear:both;content:''} +.write_inner h2 {margin:0;padding:0 0 10px !important} +.write_floater {position:absolute;top:15px;right:0;text-align:right} +.write_floater_btn {margin:0;padding:0;border:0;background:transparent;color:#999;font-size:0.95em;letter-spacing:-0.1em} + +#write_rcv {float:left;height:22px;line-height:1.8em} +#write_rcv strong {display:inline-block;margin:0 10px 0 0} +#write_reply {float:right} +#write_reply label {display:inline-block;margin:0 10px 0 0;font-weight:bold} +#write_reply #mh_reply {padding:0 5px;width:90px;height:20px;border:1px solid #e9e9e9;text-align:center;line-height:1.8em} + +.write_scemo {width:48%} +.write_scemo strong {display:block;margin:0 0 10px} +.write_scemo .scemo_list {letter-spacing:-4px} +.write_scemo .scemo_add {margin:0;padding:0;height:25px;border:1px solid #e9e9e9;background:transparent;letter-spacing:0} +#write_sc {float:left} +#write_sc .scemo_add {width:25px} +#write_emo {float:right} +#write_emo .scemo_list {text-align:right} +#write_emo .scemo_add {width:66px} +#write_emo .emo_long {} + +#sms_byte {position:absolute;top:-27px;right:0;color:#999} + +#send_emo {position:relative;padding:20px;border-top:1px solid #e9e9e9;background:#f7f7f7} +#send_emo h2 {margin:0 0 20px} +#send_emo .tmp_loading {display:block;padding:180px 0 0;text-align:center} +#send_emo #emo_sel {position:absolute;top:20px;right:20px;margin:0} +#send_emo .emo_list {margin:0;padding:0;list-style:none} +#send_emo li {float:left;margin:0 10px 10px 0 !important;margin:0 5px 10px 0;width:113px !important;width:110px} +#send_emo li:nth-of-type(3n) {margin:0 0 10px !important} +#send_emo .sms5_box {background:#fbec99} +#send_emo .box_ico {display:none} +#send_emo .box_txt {cursor:pointer} +#send_emo .emo_tit {display:block;height:20px;line-height:2em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +#send_emo .btn_submit {padding:0 5px;height:24px;border:1px solid #ccc;background:#fafafa;color:#000;font-size:0.95em;vertical-align:middle;cursor:pointer} \ No newline at end of file diff --git a/plugin/sms5/skin/basic/write.skin.php b/plugin/sms5/skin/basic/write.skin.php new file mode 100644 index 000000000..632bdf272 --- /dev/null +++ b/plugin/sms5/skin/basic/write.skin.php @@ -0,0 +1,378 @@ +', 0); +?> + +
    +

    SMS 보내기

    + +
    +
    + + + +

    보낼내용

    +
    + + + +
    0 / 80 byte
    +
    + +
    + +
    + 수신회원 +
    + +
    + + readonly> +
    +
    + +
    +

    예약전송

    + +
    + + +
    + + 년 + 월 + 일 + 시 + 분 +
    + +
    +
    + 특수기호 +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + 이모티콘 +
    + + + + + + + + + + + + + + + +
    +
    +
    + +
    + + +
    +
    + +
    + + +
    +

    이모티콘 목록

    +
    + + +
    + +
      +
    + + + + +
    + + +
    + + + + \ No newline at end of file diff --git a/plugin/sms5/skin/basic/write_mobile.skin.php b/plugin/sms5/skin/basic/write_mobile.skin.php new file mode 100644 index 000000000..f8bdca77d --- /dev/null +++ b/plugin/sms5/skin/basic/write_mobile.skin.php @@ -0,0 +1,394 @@ +', 0); +?> + +
    +

    SMS 보내기

    + +
    +
    + + + +

    보낼내용

    +
    + + + +
    0 / 80 byte
    +
    + +
    + +
    + 수신회원 +
    + +
    + + readonly> +
    +
    + +
    +

    예약전송

    + +
    + + +
    + + 년 + 월 + + 일 + 시 + 분 +
    + +
    +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    +
    + + + + + + + + + + + + + + + +
    +
    +
    +
    + +
    + + +
    +
    + +
    + + +
    +

    이모티콘 목록

    +
    + + +
    + +
      +
    + + + + +
    + + +
    + + + + \ No newline at end of file diff --git a/plugin/sms5/sms5.lib.php b/plugin/sms5/sms5.lib.php new file mode 100644 index 000000000..7ff5fc61d --- /dev/null +++ b/plugin/sms5/sms5.lib.php @@ -0,0 +1,269 @@ +\n"; + for ($i=0; $i선택"; + $str .= option_selected($skins[$i], $selected); + } + $str .= ""; + return $str; +} + +if ( ! function_exists('array_overlap')) { + function array_overlap($arr, $val) { + for ($i=0, $m=count($arr); $i<$m; $i++) { + if ($arr[$i] == $val) + return true; + } + return false; + } +} +if ( ! function_exists('get_hp')) { + function get_hp($hp, $hyphen=1) + { + global $g5; + + if (!is_hp($hp)) return ''; + + if ($hyphen) $preg = "$1-$2-$3"; else $preg = "$1$2$3"; + + $hp = str_replace('-', '', trim($hp)); + $hp = preg_replace("/^(01[016789])([0-9]{3,4})([0-9]{4})$/", $preg, $hp); + + if ($g5['sms5_demo']) + $hp = '0100000000'; + + return $hp; + } +} +if ( ! function_exists('is_hp')) { + function is_hp($hp) + { + $hp = str_replace('-', '', trim($hp)); + if (preg_match("/^(01[016789])([0-9]{3,4})([0-9]{4})$/", $hp)) + return true; + else + return false; + } +} +if ( ! function_exists('alert_just')) { + // 경고메세지를 경고창으로 + function alert_just($msg='', $url='') + { + global $g5; + + if (!$msg) $msg = '올바른 방법으로 이용해 주십시오.'; + + //header("Content-Type: text/html; charset=$g5[charset]"); + echo ""; + echo ""; + exit; + } +} + +if ( ! function_exists('utf2euc')) { + function utf2euc($str) { + return iconv("UTF-8","cp949//IGNORE", $str); + } +} +if ( ! function_exists('is_ie')) { + function is_ie() { + return isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'Trident') !== false || strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false); + } +} + +/** + * SMS 발송을 관장하는 메인 클래스이다. + * + * 접속, 발송, URL발송, 결과등의 실질적으로 쓰이는 모든 부분이 포함되어 있다. + */ + +class SMS5 extends SMS { + var $Log = array(); + + function SMS_con($sms_server,$sms_id,$sms_pw,$port) { + $this->ID=$sms_id; // 계약 후 지정 + $this->PWD=$sms_pw; // 계약 후 지정 + $this->SMS_Server=$sms_server; + $this->SMS_Port=$port; + $this->ID = spacing($this->ID,10); + $this->PWD = spacing($this->PWD,10); + } + + /** + * 발송번호의 값이 정확한 값인지 확인합니다. + * + * @param strDest 발송번호 배열입니다. + * nCount 배열의 크기입니다. + * @return 처리결과입니다. + */ + function CheckCommonTypeDest($strDest, $nCount) { + for ($i=0; $i<$nCount; $i++) { + $hp_number = preg_replace("/[^0-9]/","",$strDest[$i]['bk_hp']); + if (strlen($hp_number)<10 || strlen($hp_number)>11) return "휴대폰 번호가 틀렸습니다"; + + $CID=substr($hp_number,0,3); + if ( preg_match("/[^0-9]/",$CID) || ($CID!='010' && $CID!='011' && $CID!='016' && $CID!='017' && $CID!='018' && $CID!='019') ) return "휴대폰 앞자리 번호가 잘못되었습니다"; + } + } + + /** + * 회신번호의 값이 정확한 값인지 확인합니다. + * + * @param strDest 회신번호입니다. + * @return 처리결과입니다. + */ + function CheckCommonTypeCallBack($strCallBack) { + if (preg_match("/[^0-9]/", $strCallBack)) return "회신 전화번호가 잘못되었습니다"; + } + + + /** + * 예약날짜의 값이 정확한 값인지 확인합니다. + * + * @param text 원하는 문자열입니다. + * size 원하는 길이입니다. + * @return 처리결과입니다. + */ + function CheckCommonTypeDate($strDate) { + $strDate=preg_replace("/[^0-9]/","",$strDate); + if ($strDate) { + if (!checkdate(substr($strDate,4,2),substr($strDate,6,2),substr($rsvTime,0,4))) return "예약날짜가 잘못되었습니다"; + if (substr($strDate,8,2)>23 || substr($strDate,10,2)>59) return "예약시간이 잘못되었습니다"; + } + } + + + /** + * URL콜백용으로 메세지 크기를 수정합니다. + * + * @param url URL 내용입니다. + * msg 결과메시지입니다. + * desk 문자내용입니다. + */ + function CheckCallCenter($url, $dest, $data) { + switch (substr($dest,0,3)) { + case '010': //20바이트 + return cut_char($data,20); + break; + case '011': //80바이트 + return cut_char($data,80); + break; + case '016': // 80바이트 + return cut_char($data,80); + break; + case '017': // URL 포함 80바이트 + return cut_char($data,80 - strlen($url)); + break; + case '018': // 20바이트 + return cut_char($data,20); + break; + case '019': // 20바이트 + return cut_char($data,20); + break; + default: + return cut_char($data,80); + break; + } + } + function Add($strDest, $strCallBack, $strCaller, $strURL, $strMessage, $strDate="", $nCount) { + global $g5; + + $Error = $this->CheckCommonTypeDest($strDest, $nCount); + $Error = $this->CheckCommonTypeCallBack($strCallBack); + $Error = $this->CheckCommonTypeDate($strDate); + + $strCallBack = spacing($strCallBack,11); + $strCaller = spacing($strCaller,10); + $strDate = spacing($strDate,12); + + + for ($i=0; $i<$nCount; $i++) { + $hp_number = spacing($strDest[$i]['bk_hp'],11); + $strData = $strMessage; + if( !empty($strDest[$i]['bk_name']) ){ + $strData = str_replace("{이름}", $strDest[$i]['bk_name'], $strData); + } + // 아이코드에서는 문자에 utf-8 인코딩 형식을 아직 지원하지 않는다. + $strData = iconv('utf-8', "euc-kr", stripslashes($strData)); + + if (!$strURL) { + $strData = spacing(cut_char($strData,80),80); + + $this->Data[$i] = '01144 '.$this->ID.$this->PWD.$hp_number.$strCallBack.$strCaller.$strDate.$strData; + } else { + $strURL = spacing($strURL,50); + $strData = spacing($this->CheckCallCenter($strURL, $hp_number, $strData),80); + + $this->Data[$i] = '05173 '.$this->ID.$this->PWD.$hp_number.$strCallBack.$strURL.$strDate.$strData; + } + } + return true; // 수정대기 + } + + function Send() { + global $g5; + + $count = 1; + + if ($g5['sms5_demo_send']) { + foreach($this->Data as $puts) { + if (rand(0,10)) { + $phone = substr($puts,26,11); + $code = '47022497 '; + } else { + $phone = substr($puts,26,11); + $code = 'Error(02)'; + } + $this->Result[] = "$phone:$code"; + $this->Log[] = $puts; + } + $this->Data = ""; + return true; + exit; + } + + $fsocket=fsockopen($this->SMS_Server,$this->SMS_Port); + if (!$fsocket) return false; + set_time_limit(300); + + ## php4.3.10일경우 + ## zend 최신버전으로 업해주세요.. + ## 또는 69번째 줄을 $this->Data as $tmp => $puts 로 변경해 주세요. + + foreach($this->Data as $puts) { + $dest = substr($puts,26,11); + fputs($fsocket, $puts); + while(!$gets) { + $gets = fgets($fsocket,30); + } + if (substr($gets,0,19) == "0223 00".$dest) { + $this->Result[] = $dest.":".substr($gets,19,10); + $this->Log[] = $puts; + } else { + $this->Result[$dest] = $dest.":Error(".substr($gets,6,2).")"; + $this->Log[] = $puts; + } + $gets = ""; + + // 1천건씩 전송 후 5초 쉼 + if ($count++%1000 == 0) sleep(5); + } + fclose($fsocket); + $this->Data = ""; + return true; + } +} +?> \ No newline at end of file diff --git a/plugin/sms5/write.php b/plugin/sms5/write.php new file mode 100644 index 000000000..dc250dcb9 --- /dev/null +++ b/plugin/sms5/write.php @@ -0,0 +1,72 @@ + 0 && $is_admin != 'super') { + if ($total >= $sms5['cf_day_count']) { + $err = "하루에 보낼수 있는 문자갯수(".number_format($sms5['cf_day_count'])." 건)를 초과하였습니다."; + alert_close($err); + } +} + +// 포인트 검사 +if (!$err and $sms5['cf_point'] > 0 && $is_admin != 'super') { + if ($sms5['cf_point'] > $member['mb_point']) { + $err = "보유하신 포인트(".number_format($member['mb_point'])." 포인트)가 없거나 모자라서\\n\\n문자전송(".number_format($sms5['cf_point'])." 포인트)이 불가합니다.\\n\\n포인트를 적립하신 후 다시 시도 해 주십시오."; + alert_close($err); + } +} + +// 특정회원에게 문자 전송 +if ($mb_id) { + $mb = get_member($mb_id); + if (!$mb['mb_hp']) alert_close("회원 휴대폰번호가 없습니다."); + if (!$mb['mb_open']) alert_close("정보를 공개하지 않았습니다."); + if (!$mb['mb_sms']) alert_close("SMS 수신여부가 비활성화 되어 있습니다."); + //$hp = $mb['mb_hp']; +} + +$g5['title'] = "문자전송"; + +$token = get_token(); + +$emoticon_group = array(); +$qry = sql_query("select * from {$g5['sms5_form_group_table']} where fg_member = 1 order by fg_name"); +while ($res = sql_fetch_array($qry)) array_push($emoticon_group, $res); + +$action_url = "./write_update.php"; + +if( G5_IS_MOBILE ){ + $write_skin_page = "/write_mobile.skin.php"; +} else { + $write_skin_page = "/write.skin.php"; +} +include_once ($sms5_skin_path.$write_skin_page); +echo PHP_EOL.''.PHP_EOL; +?> \ No newline at end of file diff --git a/plugin/sms5/write_update.php b/plugin/sms5/write_update.php new file mode 100644 index 000000000..735004fc7 --- /dev/null +++ b/plugin/sms5/write_update.php @@ -0,0 +1,157 @@ + 0 && $is_admin != 'super') { + $row = sql_fetch(" select count(*) as cnt from {$g5['sms5_member_history_table']} where mb_id='{$member['mb_id']}' and date_format(mh_datetime, '%Y-%m-%d') = '".G5_TIME_YMD."' "); + if ($row['cnt'] + $total > $sms5['cf_day_count']) { + alert("하루에 보낼수 있는 문자갯수(".number_format($sms5['cf_day_count']).")를 초과하였습니다."); + } +} + +// 포인트 검사 +if ($sms5['cf_point'] > 0 && $is_admin != 'super') { + $minus_point = $sms5['cf_point'] * $total; + if ($minus_point > $member['mb_point']) + alert("보유하신 포인트(".number_format($member['mb_point']).")가 없거나 모자라서 문자전송(".number_format($minus_point).")이 불가합니다.\\n\\n포인트를 적립하신 후 다시 시도 해 주십시오."); +} else + $minus_point = 0; + +// 예약전송 +if ($mh_by && $mh_bm && $mh_bd && $mh_bh && $mh_bi) { + $mh_booking = "$mh_by-$mh_bm-$mh_bd $mh_bh:$mh_bi:00"; + $booking = $mh_by.$mh_bm.$mh_bd.$mh_bh.$mh_bi; +} else { + $mh_booking = ''; + $booking = ''; +} + +$SMS = new SMS5; +$SMS->SMS_con($config['cf_icode_server_ip'], $config['cf_icode_id'], $config['cf_icode_pw'], $config['cf_icode_server_port']); + +$result = $SMS->Add($mh_hp, $mh_reply, '', '', $mh_message, $booking, $total); + +$is_success = null; + +if ($result) +{ + $result = $SMS->Send(); + + if ($result) //SMS 서버에 접속했습니다. + { + foreach ($SMS->Result as $result) + { + list($hp, $code) = explode(":", $result); + + if (substr($code,0,5) == "Error") + { + $is_success = false; + + switch (substr($code,6,2)) { + case '02': // "02:형식오류" + $mh_log = "형식이 잘못되어 전송이 실패하였습니다."; + break; + case '23': // "23:인증실패,데이터오류,전송날짜오류" + $mh_log = "데이터를 다시 확인해 주시기바랍니다."; + break; + case '97': // "97:잔여코인부족" + $mh_log = "잔여코인이 부족합니다."; + break; + case '98': // "98:사용기간만료" + $mh_log = "사용기간이 만료되었습니다."; + break; + case '99': // "99:인증실패" + $mh_log = "인증 받지 못하였습니다. 계정을 다시 확인해 주세요."; + break; + default: // "미 확인 오류" + $mh_log = "알 수 없는 오류로 전송이 실패하었습니다."; + break; + } + } + else + { + $is_success = true; + $mh_log = "문자전송:".get_hp($hp, 1); + } + + $hp = get_hp($hp, 1); + $log = array_shift($SMS->Log); + sql_query("insert into {$g5['sms5_member_history_table']} set mb_id='{$member['mb_id']}', mh_reply='$mh_reply', mh_hp='$hp', mh_datetime='".G5_TIME_YMDHIS."', mh_booking='$mh_booking', mh_log='$mh_log', mh_ip='".$_SERVER['REMOTE_ADDR']."'"); + + if ($is_admin == 'super') + $sms5['cf_point'] = 0; + + if ($is_success) + insert_point($member['mb_id'], (-1) * $sms5['cf_point'], "$mh_log"); + + } + $SMS->Init(); // 보관하고 있던 결과값을 지웁니다. + } + else alert("에러: SMS 서버와 통신이 불안정합니다."); +} +else alert("에러: SMS 데이터 입력도중 에러가 발생하였습니다."); + +alert_close("$total 건의 문자메세지 전송을 완료하였습니다."); +?> \ No newline at end of file