728x90

네이버지식인에 질의사항이 있어서 간단하게 테스트해본 예제를 첨부한다.

최신버전 다운로드는 https://github.com/stevenwanderski/bxslider-4 에서 한다.

당연히 jQuery도 시간이 지나면서 버전이 업그레이드 되어 왔고, 이에 맞춰 bxslider 역시 jQuery 버전에 맞춰 개발되었을 확률이 높다.

버전에 맞게 하려면 위 사이트에서 파일을 최신버전으로 받고, jQuery 버전은 https://www.w3schools.com/jquery/default.asp 에서 확인하시라. 


bxslider.zip


http://bxslider.com/ 사이트에 받은 파일중에서 필요한 파일만 js, css 폴더에 복사한다.

아래 코드 중에서 색상을 별도 표시한 부분을 중점적으로 보면 금방 이해할 수 있다.


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex,nofollow"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="pragma" content="no-cache" />
<!-- jQuery library (served from Google) -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<!-- bxSlider Javascript file -->
<script src="./js/jquery.bxslider.min.js"></script>
<!-- bxSlider CSS file -->
<link href="./css/jquery.bxslider.css" rel="stylesheet" />
<script>
$(document).ready(function(){
  $('.bxslider').bxSlider();
});
</script>
</head>
<body>
<ul class="bxslider">
  <li><img src="./images/img_01.jpg" /></li>
  <li><img src="./images/img_02.jpg" /></li>
  <li><img src="./images/img_03.jpg" /></li>
  <li><img src="./images/img_04.jpg" /></li>
  <li><img src="./images/img_05.jpg" /></li>
</ul>   
</body>
</html>


------------------------------------------------------------------------------------------------------------------------

  <li><img src="./images/img_01.jpg" /></li>
  <li><img src="./images/img_02.jpg" /></li>
  <li><img src="./images/img_03.jpg" /></li>
  <li><img src="./images/img_04.jpg" /></li>
  <li><img src="./images/img_05.jpg" /></li>
파일명을 일일이 적기가 귀찮다면 PHP 를 이용해서 해당 폴더 images 를 읽어서 파일 갯수만큼 표시되도록 코드를 만들어도 된다.


<body>
    <?php
    $path = "./images"; // 오픈하고자 하는 폴더
    function getFileNames($directory) {
        $results = array();
        $handler = opendir($directory);
        while ($file = readdir($handler)) {
            if ($file != '.' && $file != '..' && is_dir($file) != '1') {
                $results[] = $file;
            }
        }
        closedir($handler);
        return $results;
    }
?>
<ul class="bxslider">
<?php
    foreach(getFileNames($path) as $value){
        echo '<li><img src="./images/'.$value.'" /></li><\n>';
    }
?>
</ul>
</body>


로 코드를 대체하면 된다.

728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩 기반 WYSIWYG editor 인 summernote 기능을 익혀 보는데 쉽지 않았다.

구글링을 통해 많은 예제를 참고하면서 테스트를 해봤다.

http://summernote.org/ 에서 summernote 파일을 다운로드 받아서 설치한다.


summernote 에디터에 이미지를 첨부하게 되면, 이미지가 지정된 폴더에 업로드되고 그 결과가 editor 화면에 보여지게 된다.

이미지는 하나를 업로드 하고 다시 선택해서 업로드를 해보니 업로드는 잘 되고 두개 모두 에디터에 보여진다.


설명보다 받아서 테스트해보고 내것으로 소화하면 될 거 같다.


==== summernote.php ====

<?php
require_once 'connect.php'; // db접속 성공
require_once 'phpclass/boardClass.php'; // 게시판 클래스
if(isset($_GET['uid']) || empty($_GET['uid'])){
    $_GET['uid'] = '';
}

$uid = $_GET['uid'] ? $_GET['uid'] : '';
$b = new boardClass();
$R = $b->rdata_array($uid); // 배열함수
if($R == 0){
    $R = array();
    $R['uid'] = '';
    $R['subject']= '';
    $R['content'] = "DB에서 값을 가져온다고 가정";
    //$R['content'] = '';
}

?>

<!DOCTYPE html>
<head>
<meta charset=UTF-8" />
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
<meta http-equiv="X-UA Compatible" />
<link rel="stylesheet" href="plugin/bootstrap/css/bootstrap.min.css" />
<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="plugin/bootstrap/js/bootstrap.min.js"></script>
<!-- include summernote css/js-->
<link href="plugin/summernote/summernote.css" rel="stylesheet">
<script src="plugin/summernote/summernote.js"></script>
<script src="plugin/summernote/lang/summernote-ko-KR.js"></script>
<script type="text/javascript">
$(document).ready(function() {
    $('.summernote').summernote({
        height: 200,
        minHeight: null,
        maxHeight: null,
        lang : 'ko-KR',
        callbacks: {
            onImageUpload : function(files, editor, welEditable) {
                console.log('image upload:', files);
                sendFile(files[0], editor, welEditable);
            },
        }
    });

    // summernote 에 값을 기록(set)
    // var markupStr = 'hello world';
    // $('.summernote').summernote('code', markupStr);

    // summernote 에디터에 이미지 업로드
    function sendFile(file,editor,welEditable) {
        data = new FormData();
        data.append("file", file);
        $.ajax({
            url: "saveimage.php", // image 저장 경로
            data: data,
            cache: false,
            contentType: false,
            enctype: 'multipart/form-data',
            processData: false,
            type: 'POST',
            success: function(data){
                var obj = JSON.parse(data);
                if (obj.success) {
                    var image = $('<img>').attr('src', '' + obj.save_url); // 에디터에 img 태그로 저장
                    $('.summernote').summernote("insertNode", image[0]); // summernote 에디터에 img 태그를 보여줌
                } else {
                    switch(parseInt(obj.error)) {
                        case 1: alert('업로드 용량 제한에 걸렸습니다.'); break;
                        case 2: alert('MAX_FILE_SIZE 보다 큰 파일은 업로드할 수 없습니다.'); break;
                        case 3: alert('파일이 일부분만 전송되었습니다.'); break;
                        case 4: alert('파일이 전송되지 않았습니다.'); break;
                        case 6: alert('임시 폴더가 없습니다.'); break;
                        case 7: alert('파일 쓰기 실패'); break;
                        case 8: alert('알수 없는 오류입니다.'); break;
                        case 100: alert('허용된 파일이 아닙니다.'); break;
                        case 101: alert('0 byte 파일은 업로드할 수 없습니다.'); break;
                    }
                }
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.log(textStatus+" "+errorThrown);
            }
        });
    }

    $("#savebtn").click(function(){
        if($("input[name='subject']").val() ==""){
            alert('제목을 입력하세요');
            $("input[name='subject']").focus();
            return false;
        }

        var contents = $('.summernote').summernote('code');
        //alert(contents.length);
        if(contents.length < 12){ // 소스보기로 보면 <p><br></p> 가 기본으로 있음
            window.alert('내용을 입력하세요');
            $('.summernote').summernote('focus');
            return false;
        }

        $("#writeForm").submit();
    });

});
</script>
</head>
<body>
<div class="rdata_write">
    <form name="writeForm" id="writeForm" action="write.php" method="post" enctype="multipart/form-data">
    <input type="hidden" name="uid" value="<?php echo $R['uid'];?>">
    <table class="table" width="1000">
        <tr>
            <td class="td1">No</td>
            <td class="td2"><?php if($R['uid']):?><?php echo $R['uid'];?><?php endif;?></td>
        </tr>

        <tr>
            <td class="td1">제목</td>
            <td class="td2"><input type="text" name="subject" value="<?php echo htmlspecialchars($R['subject']);?>" style="width:98%;" />
            </td>
        </tr>

        <tr>
            <td class="td1">내용</td>
            <td class="td2"><!-- summernote editor -->
                <textarea class="summernote" name="content" rows="10"><?php echo htmlspecialchars($R['content']);?></textarea>
            </td>
        </tr>

        <tr>
        <td colspan="2" align="center"><input type="button" id="savebtn" value="서버전송" class="btn btn-danger" /></td>
        </tr>

    </table>
    </form>
</div>
</body>
</html>


==== saveimage.php ====

<?php
$allowed = array("jpg", "png", "gif", "zip");

if(isset($_POST) and $_SERVER['REQUEST_METHOD'] == "POST"){
    if ($_FILES['file']['name']) {
        $tmpname = $_FILES['file']['tmp_name'];
        $realname = $_FILES['file']['name'];
        if (!$_FILES['file']['error']) { // 오류 없이 파일 업로드 성공
            if(! in_array(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION), $allowed) ){
                @unlink($tmpname);
                echo json_encode(array('success' => false, 'error' => 100));
            } else if (!getimagesize($tmpname)) {
                @unlink($tmpname);
                echo json_encode(array('success'=> false, 'error' => 101));
            } else {
                $fileExt = getExt($realname);
                $filename = date("Ymd").md5(uniqid($tmpname)) .round(microtime(true)).'.'.$fileExt;
                $uploadFile = './upload/' . $filename; //change this directory
                if(move_uploaded_file($tmpname, $uploadFile)){
                    @chmod($uploadFile,0606);
                    echo json_encode(array('success' => true, 'save_url' => $uploadFile ));
                }
            }
        } else {
            echo json_encode(array('success'=> false, 'error' => $error));
        }
    }
}

// 확장자 추출 함수
function getExt($filename){
    $ext = substr(strrchr($filename,"."),1);
    $ext = strtolower($ext);
    return $ext;
}

?>


==== write.php ====

테스트 목적이라서 내용이 없고 파일 내용이 잘 넘어가는지 여부만 테스트를 했다.

write.php 에서 content 필드의 내용을 잘 정리하는 함수 테스트까지는 못한 상태이다.

print_r($_POST) 보다는 var_dump($_POST)가 정보를 더 많이 보여준다.


<?php
extract($_POST);
var_dump($_POST);
//print_r($_POST);
exit;

?>


테스트해보니 $_POST['content'] 의 값은 그대로 잘 저장된다.

<?php
if(!isset($_SESSION)) {
    session_start();
}

@extract($_POST);
//var_dump($_POST);exit;
$tableName = 'table_items';
if(isset($_SESSION['userid']) && !empty($_SESSION['userid'])) { // 세션정보가 있으면(로그인되어있으면)
    include_once $_SERVER['DOCUMENT_ROOT'].'/_core/config/config.php';
    include_once $_site['web_config'].'/dbconnect.php'; // db접속
    include_once $_site['web_class'].'/dbClass.php';
    $c=new MySQLiDbClass;

    $content = trim($content);
    if($uid){ // 수정
        $R = $c->getUidData($tableName,$uid);
        $QVAL = "ItemName='$ItemName',Price='$Price',Quantity='$Quantity',content='$content'";
        $c->getDbUpdate($tableName,$QVAL,'uid='.$R['uid']);
        echo json_encode(array('result'=>'2'));
    } else { // 신규
        $QKEY = "ItemName,Price,Quantity,content";
        $QVAL = "'$ItemName','$Price','$Quantity','$content'";
        $c->getDbInsert($tableName,$QKEY,$QVAL);
        echo json_encode(array('result'=>'1'));
    }
} else {
    echo json_encode(array('result'=>'0'));
}
?>


https://github.com/kimsQ/rb/issues/60 은 나중에 읽어봐야 할 거 같다.


summernote.zip


에디터 테스트한다고 고생해서 다른 분들은 수고를 줄이라고 파일 첨부합니다.

블로그에 정리해주신 분들의 자료가 완벽하게 정리된 것을 찾기가 쉽지 않아서 정말 많은 검색을 통해서 자료에 살을 붙이면서 테스트에 성공했습니다.

write.php 파일과의 처리는 ajax 처리는 아닙니다. ajax 처리로 변경하려면 다른 게시글을 참조하면 됩니다.

728x90

'Web 프로그램 > 테이블, 게시판, 검색' 카테고리의 다른 글

PHP 검색어 함수  (0) 2018.04.08
HTML 특수기호  (0) 2018.01.17
부트스트랩 테이블 만들기(수정)  (0) 2017.02.02
부트스트랩 테이블 만들기  (0) 2017.01.31
부트스트랩 테이블 속성  (0) 2017.01.31
블로그 이미지

Link2Me

,
728x90

상대경로 상에서 게시판(board)에서 게시글을 클릭하면 해당 게시글을 수정할 수 있는 write.php 파일로 이동하는 기능을 작성하다보니 경로 추출이 쉽지 않다.


성공한 결과값부터 적어둔다.

$('.tr1').click(function() {
    var idx=$(this).attr('data-uid');
    var modulepath =$("#ajax_loginpath").attr('data-module');
    var page = urlParam('p') ? urlParam('p') : 1;
    var moveURL = modulepath + 'bbs/board.php?m=write&idx='+idx+'&p='+page;
    $(location).attr('href', moveURL);

}).mouseover(function() {
    $(this).children('.td2').css({'backgroundColor':'#DCDCDC','cursor':'pointer'});
}).mouseout(function() {
    $(this).children('.td2').css({'backgroundColor':'#FFFFFF','cursor':'default'});
});


과정 설명

테이블에서 해당 게시글을 눌렀을때 별도의 팝업창을 띄우거나 해당 게시글을 수정할 수 있는 창을 만들기 위해서는 해당 게시글의 uid 번호를 알아야 한다.

이것은 테이블 작성시에

<tr class="tr1" data-uid="'.$row['uid'].'">

로 해당 uid 를 추출할 수 있게 해준다.


modulepath 는

<div id="ajax_loginpath" data-path="<?php echo $g['path_page'];?>" data-module="<?php echo $g['path_module'];?>"></div>

에서 추출할 수 있도록 작성한다.


해당 페이지 번호도 넘겨줘야 게시글 수정후 list.php 파일로 돌아올 때 화면이 변경되지 않는다.

그러기 위해서는 URL parameter 값을 받아서 넘겨줘야 한다.

이 URL parameter 값을 넘겨주는 함수는

http://handam.tistory.com/41 분이 작성한 함수가 있어서 편하게 해결할 수 있었다. 꾸뻑^^


function urlParam(name){
    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
    if (results==null){
       return null;
    }
    else{
       return results[1] || 0;
    }
}


변수에서 추출을 못할 경우에는 null 을 반환하는데

페이지가 없으면 1을 반환하도록 삼항연산자를 사용했다.

var page = urlParam('p') ? urlParam('p') : 1;


이제 이동하고 싶은 URL을 설정하면 된다.

var moveURL = modulepath + 'bbs/board.php?m=write&idx='+idx+'&p='+page;

$(location).attr('href', moveURL); // 해당 URL 로 이동

728x90
블로그 이미지

Link2Me

,
728x90

PHP에서 MySQL을 연동하여 데이터를 가져오거나(select), 수정(update), 삭제(delete) 등을 할 때 사용하는 간단한 흐름이다.

네이버 지식인에 질문하는 사항을 가지고 간단하게 정리한다.


<?php
// 파일을 직접 실행하면 동작되지 않도록 하기 위해서
if(isset($_POST) && $_SERVER['REQUEST_METHOD'] == "POST"){
    @extract($_POST); // $_POST['id'] 라고 쓰지 않고, $id 라고 써도 인식되게 함
    include "db_info.php"; // db 연결
    $sql ="SELECT id,pass FROM testboarduser";
    $sql.=" where id='".$id."'"; // 조건문에서 얻는 결과가 1개
    if($result=mysql_query($sql,$conn)){
        $row=mysql_fetch_array($result);
        if($id == $row['id'] ){
        echo $row['id'];
        echo $row['pass'];
        } else {
        echo "틀림";
        }
    }
}
?>


The MySQLi Extension (MySQL Improved) is a relational database driver used in the PHP scripting language to provide an interface with MySQL databases.


PHP Connect to MySQL

- MySQL (procedural) : MySQL DB에서만 동작하며, , PHP 7.0 이상에선 동작안됨. PHP 5.5에서 deprecated

- MySQLi (procedural) : MySQL DB에서만 동작한다.

- MySQLi (object-oriented) : MySQL DB에서만 동작한다.
- PDO(PHP Data Objects) :
Oracle, PostgreSQL, SQLite, MySQL 등 12가지의 DB에서 동작한다.


MySQL DB에서 가져온 데이터(배열)을 반환하는 함수에 대해 알아보자. (절차지향 MySQL 방식)

 mysql_query

 MySQL 서버에 하나의 쿼리를 전송한다.

 select, show 와 같이 레코드를 생성하는 쿼리는 그 리소스를 반환한다.

 insert, update, delete 와 같이 결과 레코드가 없는 쿼리는 true, false 를 반환한다.

 mysql_result

 쿼리의 결과 레코드를 반환한다.

 <?php
 $dbconn = mysql_connect("localhost", "root", "mypass");
 $selectdb = mysql_select_db("test",$dbconn);

 $result = mysql_query("select name from test");
 $no_result = mysql_num_rows($result);
 echo "<h2>Here is a list of the test :</h2>";
 for ($i=0;$i<=$no_result;$i++){
     echo "<p>".mysql_result($result,$i)."</p>";
 }
 mysql_close($dbconn);
 ?>

 mysql_fetch_row

 쿼리의 결과 레코드를 숫자 인덱스 배열로 반환한다.
 mysql_query() 함수의 결과 레코드를 한 레코드 단위로 가져오며,

 이 레코드를 배열 형태로 되돌려 준다.

 이 배열에서 필드값을 가져오려면 인덱스로 숫자를 사용한다.

 mysql_fetch_assoc

 쿼리의 결과 레코드를 연관 배열로 반환한다.
 mysql_query() 함수의 결과 레코드를 한 레코드 단위로 가져오며,

 이 레코드를 배열 형태로 되돌려 준다.

 배열의 인덱스로 필드 이름을 사용할 수 있어서 직관적이고 사용하기 편리하다.

 mysql_fetch_array

 쿼리의 결과 레코드를  숫자 인덱스 배열이나 연관 배열로 반환한다.


select 구문으로 조회(mysql_query)하고 그 결과인  result set 에서 레코드를 한개씩 리턴해주는 함수에는 mysql_fetch_row, mysql_fetch_assoc, mysql_fetch_array 가 있다.


MySQL 함수명

MySQLi 함수명

리턴값

 mysql_fetch_row

 mysqli_fetch_row

 일반배열

mysql_fetch_assoc

 mysqli_fetch_assoc

 연관배열

 mysql_fetch_array

 mysqli_fetch_array

 일반배열 + 연관배열


mysql_fetch_row(resource $result)
mysql_fetch_row()는 데이터를 0으로 시작하는 숫자형 인덱스 배열로 반환한다.
가장 빠르다고 알려져 있으나 어느 컬럼을 가져왔는지 가독성이 떨어진다.
컬럼명이 얼마 안될 경우 혹은 문서 정리가 잘 되어있을때 사용 하도록 한다.
$row[0]; //인덱스 번호로만 데이터를 출력한다.

mysql_fetch_assoc(resource $result)
mysql_fetch_assoc()는 데이터를 문자형 연관 배열로 반환한다.
가장 많이 사용되는 함수로써 키값을 가지고 데이터를 출력한다.
데이터베이스에서 SELECT문으로 해당 컬럼을 키값만으로 출력이 가능하다.
$row['id'];
$row['userID']; // 필드의 이름은 대소문자를 구별한다.

mysql_fetch_array(resource $result [, int $result_type ])
$result_type 은 생략 가능하며, 생략하면 MYSQL_BOTH
mysql_fetch_array($result, MYSQL_NUM) = mysql_fetch_row($result)
mysql_fetch_array($result, MYSQL_ASSOC) = mysql_fetch_assoc($result)
mysql_fetch_array($result, MYSQL_BOTH)
인덱스(숫자)번호와 연관 배열의 키값으로 데이터를 가져 올 수 있다.
성능이 크게 차이는 없으나 데이터가 많을 시 key[0] 번째 값과 key['키'] 인덱스 배열과 연관배열값을 모두 가져 오기 때문에 불필요한 메모리가 쌓인다.


성능에 미치는 영향이 별로 없다면 mysql_fetch_array 함수를 사용하는 것이 편하다.


입문자들이 코딩하면서 책에 나온대로 따라 연습하면서 놓치는 부분은 사소한 곳에 있다.

$sql ="SELECT id,pass FROM testboarduser where id='".$id."'";
$result=mysql_query($sql,$conn);
이렇게 두줄로 처리하기도 하지만 더 심하게는 간단하게 한줄로 코딩한다고

$result=mysql_query("SELECT id,pass FROM testboarduser where id='".$id."'",$conn);

로 처리한다.


로직상 틀린 것은 없다.

하지만 문제점을 발견하기 위해서 디버깅을 한다고 치자.

$result=mysql_query("SELECT id,pass FROM testboarduser where id='".$id."'",$conn);

은 문제가 어디에서 생겼는지 발견하기가 쉽지 않다.


$sql ="SELECT id,pass FROM testboarduser where id='".$id."'";
echo $sql; // 데이터가 원하는 결과대로 나오는지 확인 목적
$result=mysql_query($sql,$conn);

이렇게 하면 DB 연결이 잘된 것인지 아닌지 확인이 가능해진다.


$sql ="SELECT id,pass FROM testboarduser";
$sql.=" where id='".$id."'";
$result=mysql_query($sql,$conn);

그러면 왜 이렇게 $sql 을 분리해서 코딩하는 걸까?

검색어를 입력해서 검색어가 포함된 결과를 돌려받고 싶다. 검색어 조건이 2개 이상이다.

이런 경우 등을 처리할 때 유연하게 처리가 가능하기 때문이다.


MySQLi 접속방식과 MySQL 접속방식으로 코딩시 DB 연결 부분을 실수하는 초보자가 많다.

MySQL 

 //SQL Query
 $sql= "SELECT * FROM TableName WHERE search='".$searchName."' LIMIT 5";
 $result =  mysql_query($sql,$conn);
//SQL Query 실행(Excute)

MySQLi

 //SQL Query
 $sql= "SELECT * FROM TableName WHERE search='".$searchName."' LIMIT 5";
 $result =  mysqli_query($conn, $sql);
//SQL Query 실행(Excute)



<?php
@extract($_GET); // $_GET['cat1'] 대신에 $cat1 이라고 써도 되도록 해준다.

include "db_info.php"; // db 연결

$sql ="SELECT id,pass FROM testboarduser";
$sql.=" where display=1";
if (isset($cat1)&& !empty($cat1)) $sql.= ' and category1='.$cat1;
if (isset($cat2)&& !empty($cat2)) $sql.= ' and category2='.$cat2;
if(isset($where) && !empty($where) && isset($keyword) && !empty($keyword)){
    if($where=="name"){
        $sql.= " and name LIKE '%".trim($keyword)."%'";
    } else if( $where=="phone" ){
        $sql.= " and (cellphone LIKE '%".trim($keyword)."%' OR officephone LIKE '%".trim($keyword)."%')";
    } else {
        $sql.= " and ".$where." like '%".trim($keyword)."%'";
    }
}

$sort  = isset($sort) ? $sort : 1;
if($sort ==1){
   $xsort      = "uid";
   $xorderby   = "desc";
}else if($sort ==2){
   $xsort      = "hit";
   $xorderby   = "desc";
}else if($sort ==3){
   $xsort      = "name";
   $xorderby   = "desc";
}else if($sort ==4){
   $xsort      = "cellphone";
   $xorderby   = "desc";
}

$xsort  = isset($xsort) ? $xsort : 'uid';
$xorderby= isset($xorderby) ? $xorderby : 'desc';

$sql.="order by ".$xsort." ".$xorderby."";

$curPage = isset($curPage)? $curPage : 1;  // 출력할 페이지 번호
$rowsPage = isset($rowsPage)? $rowsPage : 10; // 기본 10개씩 출력
$sql.=" LIMIT ".$curPage.",".$rowsPage."";


echo $sql; // 쿼리문이 제대로 된 것인지 확인 목적

$result=mysql_query($sql,$conn);
while($row=mysql_fetch_array($result)){
    // 출력할 내용 테이블 형태로 작성
}


?>


이와같이 복잡한 쿼리문을 유연하게 처리하기 위해서 $sql 변수를 분리해서 사용하는게 좋다.

LIMIT 개념 이해는 https://www.w3schools.com/php/php_mysql_select_limit.asp 사이트를 애용하면 대부분 해결 될 것이다.

찾고자 하는 걸 구글링을 하듯이 https://www.w3schools.com/ 사이트를 찾아보면 정말 유용한 사이트라고 감탄하게 된다.



같이보면 도움되는 게시글

http://link2me.tistory.com/1110


728x90
블로그 이미지

Link2Me

,
728x90

loginChk.php 파일의 경로가 /abc/pages/loginChk.php 로 되어 있다.


로그인 모달창에서 로그인 처리를 하는데 경로설정에 대한 문제로 골치가 아팠다.

유연한 상대경로 설정하는 방법을 몰라서 겪은 증상이다.


아래와 같이 경로에 PHP의 상대경로를 직접 입력하면 문제가 될까? 안될까?

이렇게 입력하면 ajax 에서는 경로를 인식하지 못한다.


자동 상대경로 설정법은 http://link2me.tistory.com/1197 를 참조하면 된다.


// 로그인 처리
$('#login-submit').click(function(e){
    e.preventDefault();
    //alert(window.location.pathname); // 현재 경로 확인
    $.ajax({
        url:
<?php echo $g['path_page'];?>"+'loginChk.php',
        type: 'POST',
        data: {userid:$('#userid').val(), password:$('#password').val()},
        dataType: "json",
        success: function (response) {
           if(response.result == 1){
               //alert('로그인 성공');
               $('#modal-login').modal('hide');
               location.reload(); // 화면 갱신
           } else {
               alert('로그인 실패');
           }
        },
        error: function(jqXHR, textStatus, errorThrown){
           alert("arjax error : " + textStatus + "\n" + errorThrown);
        }
    });
});


해결방법

PHP의 변수를 ajax 등 javascript 로 넘기기 위해서는 HTML 로 일단 저장해야 한다.

<body></body> 사이에 아래 코드를 추가한다.

내용은 전혀 보이지 않게 처리한다.

<div id="ajax_loginpath" data-path="<?php echo $g['path_page'];?>" ></div>


그런 다음에 아래와 같이 코드를 구현하면 해결된다.

// 로그인 처리
$('#login-submit').click(function(e){
    e.preventDefault();
    //alert(window.location.pathname); // 현재 경로 확인
    var loginpath =$("#ajax_loginpath").attr('data-path');
    $.ajax({
        url: loginpath+'loginChk.php',
        type: 'POST',
        data: {userid:$('#userid').val(), password:$('#password').val()},
        dataType: "json",
        success: function (response) {
           if(response.result == 1){
               //alert('로그인 성공');
               $('#modal-login').modal('hide');
               location.reload(); // 화면 갱신
           } else {
               alert('로그인 실패');
           }
        },
        error: function(jqXHR, textStatus, errorThrown){
           alert("arjax error : " + textStatus + "\n" + errorThrown);
        }
    });
});


결론 정리

상대경로의 개념은 현재 실행되는 파일을 기준으로 경로(path)를 상대적으로 설정한다.

따라서 실행되는 파일의 위치가 달라지면 그 위치를 기준으로 경로를 상대적으로 설정하기 때문에

../../loginChk.php 처럼 적어주면 문제가 생긴다.

실행되는 파일의 위치가 어디든지 간에 상대경로 문제가 아무런 문제없이 해결되기 위해서는 코드를 잘 짜야 한다.

이런 문제때문에 계속 문제에 부딪치면서 완벽한 해결책을 구현해보겠다는 일념으로 드디어 해결을 했다.

자동 상대경로 설정법은 http://link2me.tistory.com/1197 과 이 게시글을 활용하면 누구든지 경로설정 문제로 골치아픈 일은 없을 것이다.

728x90
블로그 이미지

Link2Me

,
728x90

현재 경로 설정을 매번 수동으로 일일이 지정하려니까 귀찮아서 구글링으로 찾은 소스를 수정해서 구현했다.

구글에서 찾은 함수 pathUrl 는 http://localhost/ 라는 명칭을 찾아준다.

이런 방법으로 경로를 지정하면 보안 강화로 인해서 url forwarding 문제가 생긴다.


파일을 include 해서 사용하다보니 경로 설정에서 문제가 생기더라.

현재 실행되는 파일의 위치를 기준으로 경로를 인식해서 처리한다.

그러므로 홈페이지의 소스가 설치된 기준 위치를 어디로 정할 것인가를 고려해서 사용하면 된다.


<?php
$cur_root = pathUrl();
$g = array(
    'path_root'   => $cur_root,
    'path_core'   => $cur_root.'_core/',
    'path_config'   => $cur_root.'_core/config/',
    'path_tmp'    => $cur_root.'_tmp/',
    'path_layout' => $cur_root.'layout/',
    'path_module' => $cur_root.'modules/',
    'path_plugin' => $cur_root.'plugin/',
    'path_bootstrap' => $cur_root.'plugin/bootstrap/',
    'path_page'   => $cur_root.'pages/',
    'path_file'   => $cur_root.'files/'
);

function pathUrl($dir = __DIR__){
    $root = "";
    $dir = str_replace('\\', '/', realpath($dir));

    //HTTPS or HTTP
    $root .= !empty($_SERVER['HTTPS']) ? 'https' : 'http';

    //HOST
    $root .= '://' . $_SERVER['HTTP_HOST'];
    $web_root = $root;

    //ALIAS
    if(!empty($_SERVER['CONTEXT_PREFIX'])) {
        $root .= $_SERVER['CONTEXT_PREFIX'];
        $root .= substr($dir, strlen($_SERVER[ 'CONTEXT_DOCUMENT_ROOT' ]));
    } else {
        $root .= substr($dir, strlen($_SERVER[ 'DOCUMENT_ROOT' ]));
    }

    $root = substr($root,strlen($web_root),strlen($root));
    //$root .= '/';

    // 홈페이지 기준점을 http://domain/abc/ 로 설정한 경우에는 -2 를 해준다.
    $cnt = count(explode("/",$root)) -2; // 현재 경로 구하기

    switch($cnt){  // 상대경로 설정
        case 0:
            $cur_root = './';
            break;
        case 1:
            $cur_root = '../';
            break;
        case 2:
            $cur_root = '../../';
            break;
        case 3:
            $cur_root = '../../../';
            break;
        case 4:
            $cur_root = '../../../../';
            break;
        case 5:
            $cur_root = '../../../../../';
            break;
    }

    return $cur_root;
}
?>



사용법 예제

<?php
if(!isset($_SESSION)) {
    session_start();
}
require_once 'path.php';
session_save_path($g['path_tmp'].'session');

require_once $g['path_config'].'dbconnect.php';
require_once $g['path_config'].'config.php';

if(isset($_SESSION['userid'])){
    $userid = $_SESSION['userid'];
}

$m = isset($_GET['m'])? $_GET['m']:'';
?>

728x90
블로그 이미지

Link2Me

,
728x90

ajax 로 form 값을 전송하는데 정상적으로 처리가 안된다.

이것 때문에 몇시간을 삽질했다. 네이버 검색하면 메시지 자체가 검색되지 않는다.

구글링을 하면 검색결과가 많은데 제대로 된 해결책을 찾기가 쉽지 않다.


error

 parsererror,SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

 증상

 error 메시지는 나오는데 DB 테이블에는 정보가 업데이트되고 있음

 원인

 함수에서 warning 메시지 발생으로 인한 문제로 판명됨

 조치

 아래에 자세히 기술


$.ajax({
    url: 'memberInfo.php',
    type: 'POST',
    data: {
        uid:$("#memberuid").val(),
        userNM:$("#memberName").val(),
        mobileNO:$("#memberMobile").val()
    },
    dataType: "json",
    success: function (response) {
        if(response.result == 1){
            alert('수정 완료');
            location.replace('index.php'); // 화면 갱신
        }
        if(response.result == 0){
            alert('수정 실패');
        }
    },
    error: function(jqXHR, textStatus, errorThrown){
        alert("arjax error Type : " + textStatus + "\n" + errorThrown);
    }
});


데이터가 정상적으로 넘어가는지 확인하려고 alert 으로 일일이 찍어봤다.

정상적으로 잘 넘어간다.


그래서 memberInfo.php 파일에서 변수가 넘어왔다고 가정하고 직접 실행을 해봤으나, 정상적이다.

테스트로 값을 넘기는 거라 임의의 값으로 처리하면서 DB 저장되는 건 주석처리를 해버렸다.

<?php
if(!isset($_SESSION)) {
    session_start();
}

//$_POST['userNM'] ='홍길동';
//$_POST['mobileNO'] ='010-123-7777';

if (empty($_POST)) {
    echo json_encode(array('result'=>'0'));
    exit;
} else {
    @extract($_POST);
    include_once $_SERVER['DOCUMENT_ROOT'].'/_core/config/config.php';
    include_once $_SERVER['DOCUMENT_ROOT'].'/_core/config/dbconnect.php'; // db접속
    include_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
    $u=new MySQLiDbClass;

    $mobileNO = preg_replace("/[^0-9]/", "", $mobileNO); //전화번호 숫자이외에 제거
    $QSET ="userNM='".$userNM."',";
    $QSET.="mobileNO='".$mobileNO."'";
    $u->getDbUpdate('member_id',$QSET,"uid=".$uid);
    echo json_encode(array('result'=>'1'));
}

?>


DB에는 제대로 저장되는지 확인해봤다.

DB 테이블에는 정상적으로 update가 되고 있다.

단지 화면상으로 결과를 돌려주는 부분에서 에러를 발생시키고 있다.


문제 해과정

1단계 :

dataType: "json" 을 dataType: "text" 로 변경하고

success : function(response) { alert(response); 를 추가한다.

그러면 아래와 같이 출력되는 메시지를 전부 보여준다.


<br />
<b>Warning</b>:  mysqli_query() expects at least 2 parameters, 1 given in <b>C:\AutoSet9\public_html\workspace\phpclass\dbClass.php</b> on line <b>184</b><br />
<br />
<b>Warning</b>:  mysqli_query() expects at least 2 parameters, 1 given in <b>C:\AutoSet9\public_html\workspace\phpclass\dbClass.php</b> on line <b>185</b><br />
{"result":"1"}


2단계 :

Warning 이 발생한 부분을 찾아서 수정해준다.


//DB업데이트
function getDbUpdate($table,$set,$where){
    global $dbconn;
    mysqli_query($dbconn,'set names utf8');
    mysqli_query($dbconn,'set sql_mode=\'\'');
    $sql ="update ".$table." set ".$set.($where?' where '.$this->getSqlFilter($where):'');
    mysqli_query($dbconn,$sql);
    return 1;
}


3단계 :

dataType: "json" 으로 원복 조치


$rs=$d->getDbUpdate('member_id',$QSET,"uid=".trim($uid));
if($rs == 1){
    echo json_encode(array('result'=>'1'));
}else{
    echo json_encode(array('result'=>'0'));
}   


원인은 mysqli_query 부분을 제대로 입력하지 않아서 경고메시지가 출력되었던 것이었다.

결과값이 1이면 처리하라는 if문의 결과값만 돌려줄 줄 알았는데 그게 아니었다.

화면에 출력되는 경고메시지가 있다보니, echo json_encode(array('result'=>'1')); 메시지 이외의 값도 결과로 돌려주었었나 보다.

함수 하나를 이용하더라도 완벽하게 만들어서 이용해야 한다는 걸 다시 한번 느꼈다.

728x90
블로그 이미지

Link2Me

,
728x90

window.location.href; // URL 포함한 전체 경로


window.location.href = 'index.php';

location.replace('index.php'); // 이전 페이지로 돌아가기 할 수 없음


window.location.pathname; // 현재 경로 확인


window.location.pathname.split('/').pop(); // 현재 실행 파일명


location.reload() and location.reload(true) works like F5 on browser. (페이지를 재실행)

window.location.reload() : 컴퓨터에 이미 받아놓은 캐쉬파일을 뒤지고 없으면 서버에서 파일을 받아온다.
window.location.reload(true) : true 가 들어가게 되면 캐쉬파일은 무시하고 무조건 서버에서 받아온다.


// 회원가입 처리를 위한 화면 이동
$('#member-join').click(function(){
    $('#modal-login').modal('hide'); // 모달 창 닫기
    //alert(window.location.pathname); // 현재 경로 확인
    if(window.location.pathname.split('/').pop() != 'member_join.php'){ // 현재 실행 파일명
        location.replace('member_join.php');
    }
});



// 부모창의 form안에 자동으로 값을 채워넣고 그 값을 submit
window.opener.document.getElementById("parent_hidden_field_something").value = "somethingSpecial";
window.opener.document.getElementById("parent_form_something").submit();




728x90
블로그 이미지

Link2Me

,
728x90

메뉴를 수동으로 일일이 작성하지 않고 PHP/MySQL 과 연동하여 테이블 정보를 자동으로 읽어서 메뉴가 생성되도록 하는 방법이다.


=== index.php ===

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="robots" content="noindex,nofollow"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="pragma" content="no-cache" />
    <meta name="apple-mobile-web-app-capable" content="no" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <title>Multi level dropdown menu in Bootstrap 3</title>
    <link rel="stylesheet" href="css/topmenu.css" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <!--[if lt IE 9]> <!-- 5. 인터넷익스플로러 9버전 이하일 경우 html5가 인식될 수 있게 해주는 스크립트 -->
    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <script src="bootstrap/js/respond.min.js"></script>
    <script src="js/topmenu.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="index.php">Home</a>
        </div>
        <div class="collapse navbar-collapse" id="navbar-collapse-1">
            <ul class="nav navbar-nav">
                <?php require_once 'menu.php';?>
            </ul>
        </div>
    </div>
</nav>
</body>
</html>


=== menu.php ===

<?php
require_once 'dbconnect.php';
$sql = "SELECT * FROM menu_items";
$res = mysqli_query($dbconn, $sql);
while($row = mysqli_fetch_assoc($res)){
    $menu[] = $row;
}

$top_menu = bootstrap_menu($menu);
echo $top_menu;

function bootstrap_menu($array,$parent_id = 0,$parents = array())
{
    if($parent_id==0) {
        foreach ($array as $element) {
            if (($element['parent_id'] != 0) && !in_array($element['parent_id'],$parents)) {
                $parents[] = $element['parent_id'];
            }
        }
    }
    $php_menu = '';
    foreach($array as $element) {
        if($element['parent_id']==$parent_id) {
            if(in_array($element['id'],$parents)) {
                $dropdown_sub=$parent_id > 0 ? 'dropdown-submenu':'';
                $php_menu .= '<li class="dropdown '.$dropdown_sub.'">';
                $php_menu .= '<a href="'.$element['url'].'" class="dropdown-toggle" data-toggle="dropdown">'.$element['name'].' <span class="caret"></span></a>';
            } else {
                $php_menu .= '<li>';
                $php_menu .= '<a href="' . $element['url'] . '">' . $element['name'] . '</a>';
            }
            if(in_array($element['id'],$parents)) {
                $php_menu .= '<ul class="dropdown-menu">';
                $php_menu .= bootstrap_menu($array, $element['id'], $parents);
                $php_menu .= '</ul>';
            }
            $php_menu .= '</li>';
        }
    }
    return $php_menu;
}

?>


=== topmenu.css ===

/* dropdown submenu */
.dropdown-submenu{position:relative;}
.dropdown-submenu>.dropdown-menu{
    top:0;left:100%;
    margin-top:-6px;
    margin-left:-1px;
    -webkit-border-radius:0 6px 6px 6px;
    -moz-border-radius:0 6px 6px 6px;
    border-radius:0 6px 6px 6px;
}
.dropdown-submenu>a:after{
    display:block;
    content:" ";
    float:right;
    width:0;
    height:0;
    border-color:transparent;
    border-style:solid;
    border-width:5px 0 5px 5px;
    border-left-color:#cccccc;
    margin-top:5px;
    margin-right:-10px;
}
.dropdown-submenu:hover>a:after{
    border-left-color:#555;
}
.dropdown-submenu.pull-left{float:none;}
.dropdown-submenu.pull-left>.dropdown-menu{
    left:-100%;
    margin-left:10px;
    -webkit-border-radius:6px 0 6px 6px;
    -moz-border-radius:6px 0 6px 6px;
    border-radius:6px 0 6px 6px;
}

@media only screen and (max-width: 768px) {
   
}

@media only screen and (min-width: 768px) {

    /* 스마트폰에서는 동작하지 않게 */

    .dropdown-submenu:hover>.dropdown-menu {
        display: block;
    }
}


=== topmenu.js ===

$(function(){
    $('ul.dropdown-menu [data-toggle=dropdown]').on('click', function(event) {
        event.preventDefault();
        event.stopPropagation();
        $(this).parent().siblings().removeClass('open');
        $(this).parent().toggleClass('open');
    });
    // 현재 선택한 메뉴 : active
    $("ul li").click(function() {
        // remove classes from all
        $("ul .active").removeClass("active");
        // add class to the one we clicked
        $(this).addClass("active");
    });

});


=== db 스키마 ===

CREATE TABLE IF NOT EXISTS `menu_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` varchar(11) DEFAULT NULL,
  `name` varchar(200) NOT NULL,
  `url` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=12 ;


INSERT INTO `menu_items` (`id`, `parent_id`, `name`, `url`) VALUES
(1, '0', 'Daum', 'http://www.daum.net'),
(2, '0', 'Java', '#'),
(3, '0', 'PHP', '#'),
(4, '3', 'PHP Tutorial', '#'),
(5, '3', '사이트', '#'),
(6, '5', '티스토리', '#'),
(7, '2', 'Java Tutorial', '#'),
(8, '2', 'Java Resources', '#'),
(9, '2', '테이블', 'table.php'),
(10, '5', '네이버', 'http://www.naver.com'),
(11, '3', 'PHP Resources', '#');


메뉴 자동으로 생성하는 것은 구글에서 treemenu 를 검색하면 나온다.

이걸 부트스트랩 기반에 적용하는 것이다.

관리자 화면에서 메뉴를 자동으로 생성하는 것은 아직 완벽하게 만들어놓지 않아서 작성하지 않는다.

다만, 참조할 게시글만 적어둔다.


http://link2me.tistory.com/1146


http://www.phpzag.com/ 사이트에 보면 도움된다.

그리고 구글링으로 jstree 를 검색하면 나온다.

http://phpflow.com/php/dynamic-tree-with-jstree-php-and-mysql/


기존 treemenu를 부트스트랩 기반으로 연동하는 걸 테스트 하는 중이다.

jQuery 기반 동작 소스를 부트스트랩 기반으로 완벽하게 처리할 때까지 시도해볼 생각이다.

728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩 기반으로 멀터레벨 드랍다운 메뉴를 만드는 걸 목표로 구글링해서 찾은 소스를 분석해가면서 만들어보려고 한다.


http://bootsnipp.com/snippets/featured/multi-level-dropdown-menu-bs3

에 나온 예제 소스를 가지고 분석해보자.


사이트 HTML 소스만으로 실행하면 아래 화면과 같은 결과가 나온다.

소스 보기


이 소스에서 bootstrap 관련 CSS와 JS를 연결하면, 아래와 같이 바뀐다.


소스보기


서브메뉴가 제대로 나오지 않는다는 걸 확인할 수 있다.

이제 topmenu.css 라는 이름으로 css 파일을 추가해보자. 

서브메뉴까지 다 나온 걸 확인할 수 있다.

여기까지 시도하려고 작성한 것은 아니다.

분석을 더 해보자.

<div class="container">
    <div class="row">
        <h2>Multi level dropdown menu in Bootstrap 3</h2>
        <hr>
        <div class="dropdown">
            <a id="dLabel" role="button" data-toggle="dropdown" class="btn btn-primary" data-target="#" href="/page.html">
                Dropdown <span class="caret"></span>
            </a>
            <ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
              <li><a href="#">Some action</a></li>
              <li><a href="#">Some other action</a></li>
              <li class="divider"></li>
              <li class="dropdown-submenu">
                <a tabindex="-1" href="#">Hover me for more options</a>
                <ul class="dropdown-menu">
                  <li><a tabindex="-1" href="#">Second level</a></li>
                  <li class="dropdown-submenu">
                    <a href="#">Even More..</a>
                    <ul class="dropdown-menu">
                        <li><a href="#">3rd level</a></li>
                        <li><a href="#">3rd level</a></li>
                    </ul>
                  </li>
                  <li><a href="#">Second level</a></li>
                  <li><a href="#">Second level</a></li>
                </ul>
              </li>
            </ul>
        </div>
    </div>
</div>



<ul class="dropdown-menu"> 로 표기되고 있고,
<li class="dropdown-submenu"> 로 표기되고 있다.


=== topmenu.css ===

.dropdown-submenu { position: relative;}

.dropdown-submenu>.dropdown-menu {
    top: 0;
    left: 100%;
    margin-top: -6px;
    margin-left: -1px;
    -webkit-border-radius: 0 6px 6px 6px;
    -moz-border-radius: 0 6px 6px;
    border-radius: 0 6px 6px 6px;
}

.dropdown-submenu:hover>.dropdown-menu { display: block;}

.dropdown-submenu>a:after {
    display: block;
    content: " ";
    float: right;
    width: 0;
    height: 0;
    border-color: transparent;
    border-style: solid;
    border-width: 5px 0 5px 5px;
    border-left-color: #ccc;
    margin-top: 5px;
    margin-right: -10px;
}

.dropdown-submenu:hover>a:after { border-left-color: #fff;}

.dropdown-submenu.pull-left { float: none;}

.dropdown-submenu.pull-left>.dropdown-menu {
    left: -100%;
    margin-left: 10px;
    -webkit-border-radius: 6px 0 6px 6px;
    -moz-border-radius: 6px 0 6px 6px;
    border-radius: 6px 0 6px 6px;
}


메뉴를 수동으로 직접 작성하는 것은 별로 어려울 것이 없을 거 같다.

목적은 메뉴를 PHP와 연동하여 자동으로 생성하는 걸 구현하고 싶다.

다음에는 PHP 와 연동하여 메뉴를 자동으로 생성하는 걸 해보자.

728x90
블로그 이미지

Link2Me

,
728x90

<?php
if(isMobile()){
    //include_once("m.php");
    echo 'Mobile';
}else{
    //include_once("pc.php");
    echo 'Windows';
}

// 접속 디바이스가 모바일인지 검사
function isMobile() {
        return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $_SERVER["HTTP_USER_AGENT"]);
}
?>


728x90
블로그 이미지

Link2Me

,
728x90

아직 bootstrap 내용을 완벽하게 숙지하지 못한 상태에서 기능 익히느라고 Copy & Paste 하면서 형태를 변경해보면서 하다보니 원하지 않은 결과로 좀 고생하고 있다.


<nav class="navbar navbar-default navbar-fixed-top">

를 설정했는데 모달(modal)창이 계속 비활성화되어 왜 그런지 원인 찾으려고 무진장 고생을 했다.


navbar-fixed-top 때문에 생긴 현상이다.

이거 없앴더니 바로 정상 모드로 돌아온다.


if(tabName == '#modal-login'){
    //alert(tabName);
    $('#modal-login').on('shown.bs.modal', function () {
                $(this).find('input[type=text]:visible:first').focus();
    });
}


그리고 Path 설정을 하면서 고생 좀 하고 있는 중이다.

Web root 에 설정하면 문제가 없는데 실행되는 파일이 서브디렉토리에 있으면 문제가 되는거 같다.

현재 디렉토리 경로를 파악하여 어떤 경로에 있던 완벽하게 할 수 있는 코드를 만들어 볼 생각이다.


<?php
$cur_root = '../';  // 실행경로가 root 가 아니라서 root는 부모 디렉토리
$g = array(
    'path_root'   => $cur_root,
    'path_core'   => $cur_root.'_core/',
    'path_var'    => $cur_root.'_var/',
    'path_tmp'    => $cur_root.'_tmp/',
    'path_layout' => $cur_root.'layout/',
    'path_module' => $cur_root.'modules/',
    'path_plugin' => $cur_root.'plugin/',
    'path_bootstrap' => $cur_root.'plugin/bootstrap/',
    'path_page'   => $cur_root.'pages/',
    'path_file'   => $cur_root.'files/'
);

?>








728x90
블로그 이미지

Link2Me

,
728x90

var email = $('#email').val();
if(email== ''){
    alert('이메일주소를 입력하세요');
    email.focus();
    return false;
} else {
    var emailRegex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
    if (!emailRegex.test(email)) {
        alert('이메일 주소가 유효하지 않습니다. ex)abc@gmail.com');
        email.focus();
        return false;
    }
}



728x90
블로그 이미지

Link2Me

,
728x90

EditPlus 는 인터넷에서 구한 소스 및 텍스트를 정리하는데 최고로 편하다.

이런 작업을 할 때 반복적으로 해야 할 경우와 여러번  실행해야 하는 불편을 키 한번만 누르면 자동으로 실행되게 하는 편리한 기능이 장점 중 하나다.


빈줄지우기 와 줄끝 공백지우기를 한꺼번에 처리하는 걸 매크로 단축키로 지정했다.





여기까지 진행하고 나면 Ctrl + Q 를 눌러주면 매크로 녹음기능이 끝난다.

이후부터는 단축키로 지정한 Alt + 2 만 눌러주면 해당 설정한 매크로가 자동으로 실행된다.

728x90
블로그 이미지

Link2Me

,
728x90

Aptana 에서 프로젝트(PHP Project)를  생성하고, 화면에 작업하는 모습이다.


왼쪽 1번은 생성한 프로젝트가 표시되고 현재 작업중인 프로젝트는 열린 상태로 보인다.

2번은 <div></div> 가 어디에서 어디까지 영역이 미치는지를 마우스로 찍으면 화면상에 표시를 해주어 편하다.


작업창 화면 색상 표시 부분 설정은

Window --> Preference 에서 여러가지 테마를 설정할 수 있다.

내가 선택한 테마는 Pastels on Dark 이다.




파일이 제대로 동작하는지 AutoSet9 상에서 보고자 한다면

환경설정에서 연결한 Firefox 브라우저 설정에 따라 자동으로 Firefox 웹 창이 뜨면서 보여준다.




728x90
블로그 이미지

Link2Me

,
728x90

테이블에 대한 사항은 별도로 table-01.php 파일로 분리했다.

코드가 너무 복잡해 보이는 경향을 줄이기 위함이다.


=== index.php ===

<?php
require_once 'dbconnect.php'; // db접속 성공
require_once 'phpclass/dbClass.php';
require_once 'phpclass/boardiClass.php';

$c = new MySQLiDbClass();

$link_url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$rowsPage = 12;

// 화면에 출력할 칼럼 발췌
$flddata ="uid,ItemName,Price,Quantity";
$where ="";
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$result = $c->getDbArray('items',$where,$flddata,'',$rowsPage,$curPage);
$totalcnt = $c->getDbRows('items',$where);

$b = new boardiClass();
?>
<!DOCTYPE html>
<head>
    <meta charset="UTF-8" />
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
    <meta http-equiv="X-UA Compatible" control="IE=edge,chrome=1" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <link rel="stylesheet" href="css/table.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="js/display.js"></script>
</head>
<body>
<main class="container-fluid">
    <div class="container">
        <div class="col-md-2 sidebar">
            <div class="row">

               <!-- 사이드 메뉴 영역 -->

            </div>
        </div>

        <!-- Main 화면 -->
        <div class="col-md-10 content">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <?php require_once 'table-01.php';?>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>

</body>
</html>


=== table-01.php ===

<table class="table table-hover">
    <thead>
        <tr>
            <th class="header" width="30"><input type="checkbox" id="checkall" /></th>
            <th class="header" width="100">No</th>
            <th class="header" width="250">아이템</th>
            <th class="header" width="250">가격</th>
            <th class="header" width="200">수량</th>
        </tr>
    </thead>
    <tbody>
        <?php
            // 테이블 리스트
            $b->tablelistView_checkbox($result);
        ?>
    </tbody>                                       
    <td colspan="5" style="text-align:left;">
    <button type="button" class="btn btn-success btn-sm" id="optdel">선택삭제</button>
    <button type="button" class="btn btn-success btn-sm" id="optmp3">선택듣기</button>
    </td>
</table>

<a class="btn btn-default pull-right">글쓰기</a>

<?php $b->PageLinkView($link_url,$totalcnt,$rowsPage,$curPage);?>


// column 개수, 게시물 총개수 만큼 자동으로 화면 출력
function tablelistView_checkbox($result){
    global $dbconn;
    while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
        $view='<tr class="tr1">';
        foreach($row as $column => $value) {
            if($column== 'uid'){
                $view.='<td><input type="checkbox" class="chkbox" name="uid[]" value="'.$value.'" /></td>';
            }
            $view.='<td class="td2">'.$value.'</td>';
        }
        $view.='</tr>';
        echo $view;
    }
}

 function PageLinkView($link_url,$totalcnt,$rowsPage,$curPage,$m){
    echo '<div style="position:relative;vertical-align:top;padding-top:0;margin-top:0">';
        echo "<span style='position:absolute;top:10px;'>[전체 글수:".$totalcnt."]</span>";
        echo '<div class="text-center">';
        echo '<ul class="pagination">';
            $Info = $this->PageList($totalcnt,$rowsPage,$curPage,'');
            if($Info['current_block'] > 2){
                echo "<li><a href='".$link_url."?m=$m&p=1'>◀</a></li> ";
            }
            if($Info['current_block'] > 1){
                echo "<li><a href='".$link_url."?m=$m&p=".$Info['prev']."'>◁</a></li> ";
            }
            foreach($Info['current'] as $w) {
                if($curPage == $w){
                    echo "<li><a href='".$link_url."?m=$m&p=".$w."'><span style='color:red;'>".$w."</span></a></li> ";
                } else {
                    echo "<li><a href='".$link_url."?m=$m&p=".$w."'>".$w."</a></li> ";
                }
            }
            if($Info['current_block'] < ($Info['total_block'])){
                echo "<li><a href='".$link_url."?m=$m&p=".$Info['next']."'>▷</a></li> ";
            }
            if($Info['current_block'] < ($Info['total_block']-1)){
                echo "<li><a href='".$link_url."?m=$m&p=".$Info['totalPage']."'>▶</a></li> ";
            }
        echo '</ul>';
        echo '</div>';
    echo '</div>';
}

// $curPage : 현재 페이지, $totalcnt : 총 게시물수
// $block_limit : 한 화면에 뿌려질 게시글 개수
function PageList($totalcnt,$rowsPage,$curPage,$block_limit) {
    $block_limit = $block_limit ? $block_limit : 10;  // 한 화면에 보여줄 개수 기본 10으로 설정

    // 총 페이지수 구하기
    $totalPage = ceil($totalcnt/$rowsPage);
    if($totalPage == 0) {
        ++$totalPage;
    }
    $total_block = ceil($totalPage / $block_limit); //전체 블록 갯수

    $curPage = $curPage ? $curPage : 1; // 현재 페이지

    // 현재 블럭 : 화면에 표시될 페이지 리스트
    $current_block=ceil($curPage/$block_limit);
    // 현재 블럭에서 시작페이지
    $fstPage = (((ceil($curPage/$block_limit)-1)*$block_limit)+1);
    // 현재 블럭에서 마지막 페이지
    $endPage = $fstPage + $block_limit -1;
    if($totalPage < $endPage) {
        $endPage = $totalPage;
    }

    // 시작 바로 전 페이지
    $prev_page = $fstPage - 1;
    // 마지막 다음 페이지
    $next_page = $endPage + 1;

    foreach(range($fstPage, $endPage) as $val) {
        $row[] = $val;
    }
    // 배열로 결과를 돌려준다.
    return array(
        'total_block' => $total_block,
        'current_block' => $current_block,
        'totalPage' => $totalPage,
        'fstPage' => $fstPage,
        'endPage' => $endPage,
        'prev' => $prev_page,
        'next' => $next_page,
        'current' => $row
    );
}


728x90
블로그 이미지

Link2Me

,
728x90

https://startbootstrap.com/template-overviews/sb-admin-2/ 사이트에서 무료 Layout 을 하나 받아서 분석을 해봤다.


전체적인 Layout 구조하에서 테이블 Layout 이 어떻게 배치되는지를 집중적으로 분석했다.

테이블 Layout 구글검색을 해도 실제 테이블 부분만 다루고 있더라.

전체 Layout 에서 배치되는 영역까지는 찾아보기가 쉽지 않다.


본문 영역에 테이블이 배치되는 구조는 아래와 같다.

<div id="page-wrapper">

<div class="row"></div> <!-- 하나의 테이블 처리 div -->
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>

</div>


css 파일에는 PC와 스마트폰(768px 이하)로 구분 처리하고 있다.

css 파일을 본인이 작성하는 css 에 내용을 추가하거나 상황에 맞게 수정해서 사용하면 된다.

#page-wrapper {
  padding: 0 15px;
  min-height: 568px;
  background-color: white;
}
@media (min-width: 768px) {
  #page-wrapper {
    position: inherit;
    margin: 0 0 0 250px;
    padding: 0 30px;
    border-left: 1px solid #e7e7e7;
  }
}


  /* 세로 방향 타블렛부터 가로 방향 타블렛과 데스크탑까지 */
 @media (min-width: 768px) and (max-width: 979px) { ... }
 
 /* 가로 방향 스마트폰부터 세로 방향 타블렛까지 */
 @media (max-width: 767px) { ... }
 
 /* 가로 방향 스마트폰 이하 */
 @media (max-width: 480px) { ... }


The Bootstrap grid system has four classes:
 - xs (for phones)
 - sm (for tablets)
 - md (for desktops)
 - lg (for larger desktops)
 


<div class="col-lg-12"> 를 적용하면 PC화면에 하나의 테이블이 화면 전체를 채운다.


<div class="row">
    <div class="col-lg-12">
        <div class="panel panel-default">
            <div class="panel-heading">
                테이블 제목 1
            </div>
            <div class="panel-body">
                <table width="100%" class="table table-striped table-hover" id="tables-example">
                    <thead>
                        <tr>
                            <th>#</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1</td>
                        </tr>
                    </tbody>
                </table>
            </div><!-- .panel-body -->
        </div><!-- .panel -->
    </div><!-- .col-lg-12 -->
</div><!-- .row -->


만약 공지사항이나 메인화면처럼 화면에 여러개의 테이블을 표시하고 싶은 경우, 즉 왼쪽과 오른쪽에 두개의 테이블을 배치하고 싶은 경우에는 어떻께 할까?

<div class="col-lg-12"> 의 절반인 <div class="col-lg-6"> 를 두개 표시해준다.

그러면 PC화면에서는 2개의 테이블이 나란히 보이고 스마트폰에서는 자동으로 한테이블씩 나열해서 보여준다.


<div class="row">
    <div class="col-lg-6">
        <div class="panel panel-default">
            <div class="panel-heading">
                테이블 제목 2
            </div>
            <div class="panel-body">
                <table width="100%" class="table table-striped table-hover" id="tables-example">
                    <thead>
                        <tr>
                            <th>#</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1</td>
                        </tr>
                    </tbody>
                </table>
            </div><!-- .panel-body -->
        </div><!-- .panel -->
    </div><!-- .col-lg-6 -->
</div><!-- .row -->


<div class="row">
    <div class="col-lg-6">
        <div class="panel panel-default">
            <div class="panel-heading">
                테이블 제목 3
            </div>
            <div class="panel-body">
                <table width="100%" class="table table-striped table-hover" id="tables-example">
                    <thead>
                        <tr>
                            <th>#</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1</td>
                        </tr>
                    </tbody>
                </table>
            </div><!-- .panel-body -->
        </div><!-- .panel -->
    </div><!-- .col-lg-6 -->
</div><!-- .row -->


이제 기존에 어설프게 table layout 처리했던 걸 전체 Layout 상에서 움직이도록 수정해야겠다.

기존에 작성 연습했던 topmenu.php Layout 과 조합해서 구조를 그려보자.

sidebar 는 2, content 는 10 (bootstrap grid system 은 12개의 화면 분할 구조로 처리)


<main class="container-fluid">
    <div class="container main-container">
        <div class="col-md-2 sidebar">
            <div class="row">
            </div>
        </div>

        <!-- Main 화면 -->
        <div class="col-md-10 content">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">

                        <div class="panel-heading">

                           <!-- 제목 적어주기 -->

                        </div>

                        <div class="panel-body">
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>

728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩을 적용하여 상단 메뉴에서 로그인, 로그아웃 처리하는 걸 구현중이다.

메뉴 Layout 이 완벽하게 마음에 들지 않는다. 좀 더 기능을 찾아보고 테스트해야 한다.

아직 미완성 부분도 있지만 현재까지 구현에 성공한 걸 적어둔다.


=== topmenu.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}
session_save_path('./_tmp/session');


// 단말 종류 검사 및 단말 구분에 따라 보여주는 화면 처리
require_once "deviceChk.php";


$titleName = "테스트";
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="robots" content="noindex,nofollow"/>
    <?php if($device_type != 3):?>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="pragma" content="no-cache" />
    <meta name="apple-mobile-web-app-capable" content="no" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <?php endif?>
    <title><?=$titleName;?></title>
    <link rel="stylesheet" href="css/topmenu.css" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <!--[if lt IE 9]> <!-- 5. 인터넷익스플로러 9버전 이하일 경우 html5가 인식될 수 있게 해주는 스크립트 -->
    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <script src="bootstrap/js/respond.min.js"></script>
    <script src="js/login.js"></script>
    <script type="text/javascript">
        var currentName = null;
        $(document).ready(function(){
            tabSetting(); // 탭 초기화 및 설정
        });

        function tabSetting() {
            $('.tabPage').hide();
            $('#navbar-collapse-2 a').on('click', function() {
                var tabName = $(this).attr('href');
                if(currentName != tabName){
                    $('.tabPage').hide();
                    currentName = tabName;
                }
                if($(tabName).is(":visible")){
                    //$(tabName).slideUp();
                    $(tabName).toggle();
                }else{                   
                    $(tabName).slideDown();
                }

                if(tabName != '#nav-loginForm'){ // 로그인 창이 아니면
                    // 반응형으로 메뉴 동작시 메뉴 클릭후 자동 숨김
                    $('#navbar-collapse-2').collapse('hide');
                }
            });
           
            $('.side-menu-container a').on('click', function() {
                var tabName = $(this).attr('href');
                if(currentName != tabName){
                    $('.tabPage').hide();
                    currentName = tabName;
                }
                if($(tabName).is(":visible")){
                    //$(tabName).slideUp();
                    $(tabName).toggle();
                }else{                   
                    $(tabName).slideDown();
                }
            });
        }

    </script>
</head>
<body>

<nav class="navbar navbar-default">
     <div class="container-fluid"><!-- 전체 화면을 사용 / container 클래스 선택자는 최대 넓이가 1170px -->
        <div class="navbar-header">
            <!--모바일 시 메뉴 숨기기 시작 -->
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-2">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <!--모바일 시 메뉴 숨기기 끝  -->
            <a class="navbar-brand" href="#"><?=$titleName;?></a>
        </div>
        <div class="collapse navbar-collapse" id="navbar-collapse-2">
            <ul class="nav navbar-nav navbar-left">
                <li><a href="#home">Home</a></li>
                <li><a href="#tab01">News</a></li>
                <li><a href="#tab02">Contact</a></li>
                <li><a href="#tab03">About</a></li>
                <li><a href="#tab04">Support</a></li>
                <li><a href="#tab05">Blog</a></li>
                <li><a href="#tab06">게시판</a></li>
                <li><a href="#base">Base</a></li>
                <li><a href="#custom">Custom</a></li>
                <li><a href="#more">More</a></li>
                <li><a href="#logo">Logo</a></li>
                <?php if(isset($_SESSION['userid']) && !empty($_SESSION['userid'])):?>
                    <li><a href="#" onclick="document.getElementById('logout-form').submit();">로그아웃</a></li>
                <?php else:?>
                    <li><a href="#nav-loginForm" data-toggle="modal" >로그인</a></li>
                <?php endif;?>
            </ul>

            <div class="modal fade" id="nav-loginForm" role="dialog">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <button type="button" class="close" data-dismiss="modal">&times;</button>
                            <h4 class="modal-title"><span class="glyphicon glyphicon-lock"></span>로그인 폼</h4>
                        </div>
                        <div class="modal-body">
                            <form role="form" id="login-form">
                                <div class="form-group">
                                    <label for="userid"><span class="glyphicon glyphicon-user"></span>userID</label>
                                    <input type="text" class="form-control" id="userid" placeholder="아이디를 입력하세요" autofocus required />
                                </div>
                                <div class="form-group">
                                    <label for="password"><span class="glyphicon glyphicon-eye-open"></span>비밀번호</label>
                                    <input type="password" class="form-control" id="password" placeholder="비밀번호를 입력하세요" required />
                                </div>
                                <div class="checkbox">
                                    <label><input type="checkbox" value="" checked>로그인 유지</label>
                                </div>
                                <button type="submit" id="login-submit" class="btn btn-success btn-block"><span class="glyphicon glyphicon-off"></span>로그인</button>
                            </form>
                        </div>
                        <div class="modal-footer">
                            <button id="login_lost_btn" type="button" class="btn btn-link">비밀번호 찾기</button>
                            <button id="login_register_btn" type="button" class="btn btn-link">회원가입</button>
                            <!--<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>-->
                        </div>
                        <!-- End # Login Form -->

                        <form id="logout-form" style="display:none" action="oauth/logout.php" method="POST"></form>

                        <!-- Begin | Lost Password Form --><!-- 좀 더 테스트 해봐야 할 영역 -->
                        <form id="lost-form" style="display:none;">
                            <div class="modal-body">
                                <div id="div-lost-msg">
                                    <div id="icon-lost-msg" class="glyphicon glyphicon-chevron-right"></div>
                                    <span id="text-lost-msg">Type your e-mail.</span>
                                </div>
                                <input id="lost_email" class="form-control" type="text" placeholder="E-Mail (type ERROR for error effect)" required>
                            </div>
                            <div class="modal-footer">
                                <div>
                                    <button type="submit" class="btn btn-primary btn-lg btn-block">Send</button>
                                </div>
                                <div>
                                    <button id="lost_login_btn" type="button" class="btn btn-link">Log In</button>
                                    <button id="lost_register_btn" type="button" class="btn btn-link">Register</button>
                                </div>
                            </div>
                        </form>
                        <!-- End | Lost Password Form -->

                        <!-- Begin | Register Form -->
                        <form id="register-form" style="display:none;">
                            <div class="modal-body">
                                <div id="div-register-msg">
                                    <div id="icon-register-msg" class="glyphicon glyphicon-chevron-right"></div>
                                    <span id="text-register-msg">Register an account.</span>
                                </div>
                                <input id="register_username" class="form-control" type="text" placeholder="Username (type ERROR for error effect)" required>
                                <input id="register_email" class="form-control" type="text" placeholder="E-Mail" required>
                                <input id="register_password" class="form-control" type="password" placeholder="Password" required>
                            </div>
                            <div class="modal-footer">
                                <div>
                                    <button type="submit" class="btn btn-primary btn-lg btn-block">Register</button>
                                </div>
                                <div>
                                    <button id="register_login_btn" type="button" class="btn btn-link">Log In</button>
                                    <button id="register_lost_btn" type="button" class="btn btn-link">Lost Password?</button>
                                </div>
                            </div>
                        </form>
                        <!-- End | Register Form -->

                    </div>

                </div>
            </div>
        </div><!-- .navbar-collapse -->
     </div><!-- .container-fluid -->
</nav>

<main class="container-fluid">
     <div class="container main-container">
         <div class="col-md-2 sidebar">
             <div class="row">
                 <div class="absolute-wrapper"> </div>
                 <!-- Menu -->
                 <div class="side-menu">
                     <nav class="navbar navbar-default" role="navigation">
                         <!-- Main Menu -->
                         <div class="side-menu-container">
                             <ul class="nav navbar-nav">
                                 <li><a href="#tab01"><span class="glyphicon glyphicon-cloud"></span> Link</a></li>
                             </ul>
                         </div>
                     </nav>
                 </div>
             </div>
            
         </div>
        
         <!-- Main 화면 -->            
         <div class="col-md-10 content">
             <div class="panel panel-default">
                 <div class="panel-body">
                    메인 본문 표시 영역
                    <div id="tab01" class="tabPage">
                    <img class="img-responsive" src="./images/img_01.jpg">
                    </div>
                    <div id="tab02" class="tabPage">
                        <img class="img-responsive" src="./images/img_02.jpg">
                    </div>
                    <div id="tab03" class="tabPage">
                        <img class="img-responsive" src="./images/img_03.jpg">
                    </div>
                    <div id="tab04" class="tabPage">
                        <img class="img-responsive" src="./images/img_04.jpg">
                    </div>
                    <div id="tab05" class="tabPage">
                        <img class="img-responsive" src="./images/img_05.jpg">
                    </div>
                 </div> 
            </div>   
         </div>    
    </div>
</main>

<footer class="pull-left footer">
    <p class="col-md-12">
        <hr class="divider">
        &copy; Linke2ME 2016
    </p>
</footer>

</body>
</html>


=== login.js ===

$(function(){
    $('#login-submit').click(function(){
        $('#login-form').submit(function(e){
            e.preventDefault();
            $.ajax({
                url: 'oauth/loginChk.php',
                type: 'POST',
                data: {userid:$('#userid').val(), password:$('#password').val()},
                dataType: "json",
                success: function (response) {
                if(response.result == 1){
                    //alert('로그인 성공');                       
                    $('#nav-loginForm').modal('hide');
                    location.reload(); // 화면 갱신                      
                } else {
                    alert('로그인 실패');
                }                   
                },
                error: function(){
                    alert('ajax form 전송에 문제가 있습니다');
                }
            });           
        });
    });

});


=== loginChk.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}

@extract($_POST);
if(isset($userid) && !empty($userid) && isset($password) && !empty($password)) {
    require_once 'dbconnect.php'; // db접속 성공
    require_once '../phpclass/loginClass.php';
   
    $salt = "&
c4f96fa549b58356d638%#!s29e5dce852e5d1";
    $password = hash('sha384', $password.$salt);

    $c=new LoginClass();
    $rs = $c->WebUserAuthCheck($userid,$password);
    if($rs == '1') {
        $_SESSION['userid'] = $userid;
        echo '{"result":"1"}';
    } else {
        echo '{"result":"0"}';
    }
}
?>


=== dbconnect.php ===

<?php
include_once 'dbinfo.php';
$dbconn = isConnectDb($db);

function isConnectDb($db)
{
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
        exit;
    } else {
        return $conn;
    }
}
?>


=== logout.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}
unset($_SESSION['userid']);
if(session_destroy()){
    echo '{"result":"1"}';
    header("Location: ../topmenu.php");
}
?>


=== topmenu.css ===

@charset "utf-8";
@import url(http://fonts.googleapis.com/earlyaccess/nanumgothic.css);

body {padding-top: 0px;}

/* 로그인 폼 */
.modal-header {
      color:black !important;
      text-align: center;
      font-size: 30px;
}
.modal-footer {
      background-color: #f9f9f9;
      padding: 5px 5px 5px 5px;
      border-top: 0px;
}
/* 로그인 폼 End */ 

.sidebar { padding-left: 0;}
.main-container { background: #FFF; padding-top: 15px; margin-top: -20px;}
.footer { width: 100%;}   
:focus { outline: none;}
.side-menu {
    position: relative;
    width: 100%;
    height: 100%;
    background-color: #f8f8f8;
    border-right: 1px solid #e7e7e7;
}
.side-menu .navbar {
    border: none;
}
.side-menu .navbar-header {
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
}
.side-menu .navbar-nav .active a {
    background-color: transparent;
    margin-right: -1px;
    border-right: 5px solid #e7e7e7;
}
.side-menu .navbar-nav li {
    display: block;
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
}
.side-menu .navbar-nav li a {
    padding: 15px;
}
.side-menu .navbar-nav li a .glyphicon {
    padding-right: 10px;
}
.side-menu #dropdown {
    border: 0;
    margin-bottom: 0;
    border-radius: 0;
    background-color: transparent;
    box-shadow: none;
}
.side-menu #dropdown .caret {
    float: right;
    margin: 9px 5px 0;
}
.side-menu #dropdown .indicator {
    float: right;
}
.side-menu #dropdown > a {
    border-bottom: 1px solid #e7e7e7;
}
.side-menu #dropdown .panel-body {
    padding: 0;
    background-color: #f3f3f3;
}
.side-menu #dropdown .panel-body .navbar-nav {
    width: 100%;
}
.side-menu #dropdown .panel-body .navbar-nav li {
    padding-left: 15px;
    border-bottom: 1px solid #e7e7e7;
}
.side-menu #dropdown .panel-body .navbar-nav li:last-child {
    border-bottom: none;
}
.side-menu #dropdown .panel-body .panel > a {
    margin-left: -20px;
    padding-left: 35px;
}
.side-menu #dropdown .panel-body .panel-body {
    margin-left: -15px;
}
.side-menu #dropdown .panel-body .panel-body li {
    padding-left: 30px;
}
.side-menu #dropdown .panel-body .panel-body li:last-child {
    border-bottom: 1px solid #e7e7e7;
}
.side-menu #search-trigger {
    background-color: #f3f3f3;
    border: 0;
    border-radius: 0;
    position: absolute;
    top: 0;
    right: 0;
    padding: 15px 18px;
}
.side-menu .brand-name-wrapper {
    min-height: 50px;
}
.side-menu .brand-name-wrapper .navbar-brand {
    display: block;
}
.side-menu #search {
    position: relative;
    z-index: 1000;
}
.side-menu #search .panel-body {
    padding: 0;
}
.side-menu #search .panel-body .navbar-form {
    padding: 0;
    padding-right: 50px;
    width: 100%;
    margin: 0;
    position: relative;
    border-top: 1px solid #e7e7e7;
}
.side-menu #search .panel-body .navbar-form .form-group {
    width: 100%;
    position: relative;
}
.side-menu #search .panel-body .navbar-form input {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    width: 100%;
    height: 50px;
}
.side-menu #search .panel-body .navbar-form .btn {
    position: absolute;
    right: 0;
    top: 0;
    border: 0;
    border-radius: 0;
    background-color: #f3f3f3;
    padding: 15px 18px;
}
/* Main body section */
.side-body {
    margin-left: 310px;
}

.navbar-brand { position: relative; z-index: 2; }

.navbar-nav.navbar-right .btn { position: relative; z-index: 2; padding: 4px 20px; margin: 10px auto; }

.navbar .navbar-collapse { position: relative; }
.navbar .navbar-collapse .navbar-right > li:last-child { padding-left: 22px; }

.navbar .nav-collapse { position: absolute; z-index: 1; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding-right: 120px; padding-left: 80px; width: 100%; }
.navbar.navbar-default .nav-collapse { background-color: #f8f8f8; }
.navbar.navbar-inverse .nav-collapse { background-color: #222; }
.navbar .nav-collapse .navbar-form { border-width: 0; box-shadow: none; }
.nav-collapse>li { float: right; }

.btn.btn-circle { border-radius: 50px; }
.btn.btn-outline { background-color: transparent; }

/* 화면 해상도에 따른 글자 크기 변경 */
@media only screen and (max-width: 768px) {
    .navbar .navbar-collapse .navbar-right > li:last-child { padding-left: 15px; padding-right: 15px; }
   
    .navbar .nav-collapse { margin: 7.5px auto; padding: 0; }
    .navbar .nav-collapse .navbar-form { margin: 0; }
    .nav-collapse>li { float: none; }
       
    .side-menu {
        position: relative;
        width: 100%;
        height: 0;
        border-right: 0;
    }

    .side-menu .navbar {
        z-index: 999;
        position: relative;
        height: 0;
        min-height: 0;
        background-color:none !important;
        border-color: none !important;
    }
    .side-menu .brand-name-wrapper .navbar-brand {
        display: inline-block;
    }
    /* Slide in animation */
    @-moz-keyframes slidein {
        0% {
            left: -300px;
        }
        100% {
            left: 10px;
        }
    }
    @-webkit-keyframes slidein {
        0% {
            left: -300px;
        }
        100% {
            left: 10px;
        }
    }
    @keyframes slidein {
        0% {
            left: -300px;
        }
        100% {
            left: 10px;
        }
    }
    @-moz-keyframes slideout {
        0% {
            left: 0;
        }
        100% {
            left: -300px;
        }
    }
    @-webkit-keyframes slideout {
        0% {
            left: 0;
        }
        100% {
            left: -300px;
        }
    }
    @keyframes slideout {
        0% {
            left: 0;
        }
        100% {
            left: -300px;
        }
    }
    /* Slide side menu*/
    /* Add .absolute-wrapper.slide-in for scrollable menu -> see top comment */
    .side-menu-container > .navbar-nav.slide-in {
        -moz-animation: slidein 300ms forwards;
        -o-animation: slidein 300ms forwards;
        -webkit-animation: slidein 300ms forwards;
        animation: slidein 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    .side-menu-container > .navbar-nav {
        /* Add position:absolute for scrollable menu -> see top comment */
        position: fixed;
        left: -300px;
        width: 300px;
        top: 43px;
        height: 100%;
        border-right: 1px solid #e7e7e7;
        background-color: #f8f8f8;
        overflow: auto;
        -moz-animation: slideout 300ms forwards;
        -o-animation: slideout 300ms forwards;
        -webkit-animation: slideout 300ms forwards;
        animation: slideout 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    @-moz-keyframes bodyslidein {
        0% {
            left: 0;
        }
        100% {
            left: 300px;
        }
    }
    @-webkit-keyframes bodyslidein {
        0% {
            left: 0;
        }
        100% {
            left: 300px;
        }
    }
    @keyframes bodyslidein {
        0% {
            left: 0;
        }
        100% {
            left: 300px;
        }
    }
    @-moz-keyframes bodyslideout {
        0% {
            left: 300px;
        }
        100% {
            left: 0;
        }
    }
    @-webkit-keyframes bodyslideout {
        0% {
            left: 300px;
        }
        100% {
            left: 0;
        }
    }
    @keyframes bodyslideout {
        0% {
            left: 300px;
        }
        100% {
            left: 0;
        }
    }
    /* Slide side body*/
    .side-body {
        margin-left: 5px;
        margin-top: 70px;
        position: relative;
        -moz-animation: bodyslideout 300ms forwards;
        -o-animation: bodyslideout 300ms forwards;
        -webkit-animation: bodyslideout 300ms forwards;
        animation: bodyslideout 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    .body-slide-in {
        -moz-animation: bodyslidein 300ms forwards;
        -o-animation: bodyslidein 300ms forwards;
        -webkit-animation: bodyslidein 300ms forwards;
        animation: bodyslidein 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    /* Hamburger */
    .navbar-toggle-sidebar {
        border: 0;
        float: left;
        padding: 18px;
        margin: 0;
        border-radius: 0;
        background-color: #f3f3f3;
    }
    /* Search */
    #search .panel-body .navbar-form {
        border-bottom: 0;
    }
    #search .panel-body .navbar-form .form-group {
        margin: 0;
    }
    .side-menu .navbar-header {
        /* this is probably redundant */
        position: fixed;
        z-index: 3;
        background-color: #f8f8f8;
    }
    /* Dropdown tweek */
    #dropdown .panel-body .navbar-nav {
        margin: 0;
    }    
}

728x90
블로그 이미지

Link2Me

,
728x90

http://link2me.tistory.com/1128 에서 작성했던 테이블을 부트스트랩 기반으로 변경을 해봤다.


=== index.php ===

<?php
require_once 'dbconnect.php'; // db접속 성공
require_once 'phpclass/dbClass.php';
require_once 'phpclass/boardiClass.php';

$c = new MySQLiDbClass();

$link_url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$rowsPage = 12;

// 화면에 출력할 칼럼 발췌
$flddata ="uid,ItemName,Price,Quantity";
$where ="";
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$result = $c->getDbArray('items',$where,$flddata,'',$rowsPage,$curPage);
$totalcnt = $c->getDbRows('items',$where);

$b = new boardiClass();
?>
<!DOCTYPE html>
<head>
    <meta charset="UTF-8" />
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
    <meta http-equiv="X-UA Compatible" control="IE=edge,chrome=1" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <link rel="stylesheet" type="text/css" href="css/table.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="js/display.js"></script>
</head>
<body>
<div class="container">
    <div class="table-responsive">
        <table class="table table-hover">
            <thead>
                <tr>
                    <th class="header" width="30"><input type="checkbox" id="checkall" /></th>
                    <th class="header" width="100">No</th>
                    <th class="header" width="250">아이템</th>
                    <th class="header" width="250">가격</th>
                    <th class="header" width="200">수량</th>
                </tr>
            </thead>
            <?php
                // 테이블 리스트
                $b->tablelistView_checkbox($result);
            ?>
            <td colspan="5" style="text-align:left;">
            <button type="button" class="btn btn-success btn-sm" id="optdel">선택삭제</button>
            <button type="button" class="btn btn-success btn-sm" id="optmp3">선택듣기</button>
            </td>
        </table>
       
        <a class="btn btn-default pull-right">글쓰기</a>       
        <?php $b->PageLinkView($link_url,$totalcnt,$rowsPage,$curPage);?>
    </div>
</div>

</body>
</html>


=== table.css ===

th.header {
    background:#F4F9FF;
    color:#444444;
    vertical-align:middle;
    border-bottom:1px solid #E6E6E6;
    font-size:1em;
    height:2em;
    }


=== display.js ===

$(function(){
    // class td1 중에서 짝수번째 요소만 선택해서 배경색을 지정색으로 표시
    // $(".tr1:odd").css("background-color","#F4F9FF"); // 부트스트랩 클래스에서 설정가능하므로 불필요

    $('.tr1').click(function() {
        var idx=$(this).attr('id');
    }).mouseover(function() {
        $(this).children('.td2').css({'backgroundColor':'#DCDCDC','cursor':'pointer'});
    }).mouseout(function() {
        $(this).children('.td2').css({'backgroundColor':'#FFFFFF','cursor':'default'});
    });

    // 전체 선택, 전체 해제
    $("#checkall").change(function () {
        $("input:checkbox").prop('checked', $(this).prop("checked"));
    });

    $('#optdel').click(function(){
        var chkdata = new Array();
        // 헤더에 있는 체크박스는 제외
        //$('.chkbox:checked').each(function() {
        $('input:checkbox[name="uid[]"]:checked').each(function() {
            // 체크한 아이템 배열로 저장
            chkdata.push($(this).val());
            //alert($(this).val());
        });
        if(chkdata.length != 0){ // 배열의 길이가 0 이 아니면
            alert(chkdata);
            $.post('ajax.php',{chkdata:chkdata}, function(response) {
                alert(response);
            });
        } else {
            alert('선택한 항목이 없습니다.');
        }
    });

    $('#optmp3').click(function(){
        var chkdata = new Array();
        $('input:checkbox[name="uid[]"]:checked').each(function() {
            // 체크한 아이템 배열로 저장
            chkdata.push($(this).val());
        });
        if(chkdata.length != 0){ // 배열의 길이가 0 이 아니면
            //alert(chkdata);
            $.post('ajax.php',{chkdata:chkdata}, function(response) {
                alert(response);
            });
        } else {
            alert('선택한 항목이 없습니다.');
        }
    });

});


=== boardiClass.php 에서 발췌 ===

페이지네이션 표시를 아래 그림처럼 해주기 위해서는

<div class="text-center">

<ul class="pagination">
  <li><a href="#">1</a></li>
  <li><a href="#">2</a></li>
  <li><a href="#">3</a></li>
  <li><a href="#">4</a></li>
  <li><a href="#">5</a></li>
</ul>

</div>

과 같이 해주면 된다.

http://www.w3schools.com/bootstrap/bootstrap_pagination.asp 를 테스트 해볼 수 있다.


    function PageLinkView($link_url,$totalcnt,$rowsPage,$curPage){
        echo '<div style="position:relative;vertical-align:top;padding-top:0;margin-top:0">';
            echo "<span style='position:absolute;top:10px;'>[전체 글수:".$totalcnt."]</span>";
            echo '<div class="text-center">';
            echo '<ul class="pagination">';
                $Info = $this->PageList($totalcnt,$rowsPage,$curPage,'');
                if($Info['current_block'] > 2){
                    echo "<li><a href='".$link_url."?p=1'>◀</a></li> ";
                }
                if($Info['current_block'] > 1){
                    echo "<li><a href='".$link_url."?p=".$Info['prev']."'>◁</a></li> ";
                }
                foreach($Info['current'] as $w) {
                    if($curPage == $w){
                        echo "<li><a href='".$link_url."?p=".$w."'><span style='color:red;'>".$w."</span></a></li> ";
                    } else {
                        echo "<li><a href='".$link_url."?p=".$w."'>".$w."</a></li> ";
                    }
                }
                if($Info['current_block'] < ($Info['total_block'])){
                    echo "<li><a href='".$link_url."?p=".$Info['next']."'>▷</a></li> ";
                }
                if($Info['current_block'] < ($Info['total_block']-1)){
                    echo "<li><a href='".$link_url."?p=".$Info['totalPage']."'>▶</a></li> ";
                }
            echo '</ul>';
            echo '</div>';
        echo '</div>';
    }


728x90
블로그 이미지

Link2Me

,
728x90

부트스트랩에서 사용하는 테이블은 모두 반응형(responsive) 테이블이다.

따라서 브라우저의 길이에 맞춰 테이블의 크기 및 내용 부분 또한 가변적으로 변하게 된다.

테이블 태그 부분에 class="table" 을 적용해주면 width 가 100%인 테이블이 생성된다.


<table class="table">

각 행의 아래에 테두리(border)가 있는 테이블이 만들어진다.


<table class="table table-bordered">

모든 셀에 테두리가 만들어 진다.


<table class="table table-striped">

한 줄 건너 배경색이 달라지는 스트라이프 형태의 테이블이 된다.


<table class="table table-hover">

테이블에 마우스를 올렸을 때 마우스 커서가 있는 행이 다른 색으로 변한다.


<table class="table table-condensed">

셀 패딩이 반으로 감소돼 작은 크기의 테이블이 된다.


테이블에 적용된 클래스 선택자의 경우 혼합해서 사용 가능하다.

<table class="table table-bordered table-hover">


테이블 행 또는 셀에 색상 지정

클래스 설명
.active 특정 행이나 셀에 마우스 오버 색상 적용
색상은 회색
.success 성공 또는 긍정적인 작업 표시
색상은 연두색 계열 #d0e9c6
.info

중립적인 정보 변경이나 작업 표시

.warning 주의를 필요로 하는 경고를 표시
색상은 겨자색 계열 #faf2cc
.danger 위험 또는 잠재적으로 부정적인 작업 표시
색상은 붉은색 계열 #ebcccc


<div class="table-responsive"></div>

부트스트랩에서는 화면 폭이 좁을 때(768px 이하) 하단에 스크롤바가 나타나며 수평으로 스크롤해서 테이블을 볼 수 있게 했다. <table> 태그 외곽에 <div> 태그로 감싸서 table-responsive 클래스를 추가하면 반응형 테이블로 사용할 수 있다.

728x90
블로그 이미지

Link2Me

,