Category Archives: Wordpress

WordPress Database 외부 접근

<?php
// Get access to WordPress
require( dirname( __FILE__ ) . '/wp-blog-header.php' );
global $user_ID;
// Get all posts
$posts = $wpdb->get_results('SELECT * FROM '. $wpdb->prefix.'posts');
//print_r($posts);
echo $user_ID;
if (!is_user_logged_in()){
    die("You Must Be Logged In to Access This");
}
if( ! current_user_can('edit_files')) {
    die("Sorry you are not authorized to access this file");
}
?>

이전 그누보드에서 _common.php를 불러서 DB에 접속하던 방식이다.

echo $user_ID 이하는 로그인 했을 때만 접근 가능하도록 하는 방식이다.

[WORDPRESS로 환자 DATABASE 만들기] #6. 엑셀(excel) 출력

마지막 포스팅

Excel 출력을 위한 준비물

phpExcel이라는 Library를 사용한다.

https://phpexcel.codeplex.com/

압축파일을 열어보면 설명이 대강 되어있는데, 간단하게 Classes라는 폴더를 wamp/www (혹은 사용하는 서버의 가장 앞쪽 폴더?) 위치에 두면 된다.

혹시 로딩이 안되면 아래 파일의 경로를 바꾸면 된다.

require_once dirname(__FILE__) . ‘/../Classes/PHPExcel.php’;

WordPress 외부 접근

용어가 적절한지 모르겠다. 다만 이전의 그누보드의 경우에서 보면 common.php 를 이용해서 게시판 외적으로 접근이 가능했는데, 워드프레스도 비슷한 방식으로 가능하다.

require_once( dirname( __FILE__ ) . '/wp-load.php' );

global $user_ID;
if (!is_user_logged_in()){
    die("You Must Be Logged In to Access This");
}
if( ! current_user_can('edit_files')) {
    die("Sorry you are not authorized to access this file");
}

내가 만든 코드는 로그인을 안하고 이 페이지를 들어오게되면 안되게 해놨다.

그리고 아래 코드들은 그냥 예제로 만들어진 코드를 간단히 수정해본 소스다.

xls 파일 출력

require_once dirname(__FILE__) . '/../Classes/PHPExcel.php';

// Redirect output to a client’s web browser (Excel2007)
header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
header('Content-Disposition: attachment;filename="export_data.xlsx"');
header('Cache-Control: max-age=0');
// If you're serving to IE 9, then the following may be needed
header('Cache-Control: max-age=1');

// If you're serving to IE over SSL, then the following may be needed
header ('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
header ('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT'); // always modified
header ('Cache-Control: cache, must-revalidate'); // HTTP/1.1
header ('Pragma: public'); // HTTP/1.0

// Create new PHPExcel object
$objPHPExcel = new PHPExcel();

// Set document properties
$objPHPExcel->getProperties()->setCreator("Junn")
							 ->setLastModifiedBy("Junn")
							 ->setTitle("Office 2007 XLSX Test Document")
							 ->setSubject("Office 2007 XLSX Test Document")
							 ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
							 ->setKeywords("office 2007 openxml php")
							 ->setCategory("Test result file");

// Get all posts
$patients = $wpdb->get_results('SELECT * FROM '. $wpdb->prefix.'posts' .' WHERE `post_type` = \'patients\'');

if($patients)
{
	$i =0;
	foreach ($patients as $patient)
	{			
		$i++;
		$pts_id_row = $wpdb->get_row('SELECT * FROM '. $wpdb->prefix.'postmeta' .' WHERE `post_id` = '.$patient->ID.' AND `meta_key` = \'pts_id\'');		
		$objPHPExcel->setActiveSheetIndex(0)
	            ->setCellValue('A'.$i, $pts_id_row->meta_value)
	            ->setCellValue('B'.$i, $patient->post_date);	            
	}
}

$objPHPExcel->getActiveSheet()->setTitle('Simple');
$objPHPExcel->setActiveSheetIndex(0);

$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
$objWriter->save('php://output');

exit;

그리하여 출력된 파일의 결과는 다음과 같다.

Untitled-114


마무리하며

워드프레스로 환자데이터베이스 만들면 다양한 장점이 있다.

  • 첫번째는 백엔드를 거의 신경쓰지 않을 수 있으며,
  • 무엇보다도 일단 기존의 테마를 이용하기 때문에 디자인에 크게 신경을 쓰지 않아도 된다.
  • 소요시간을 획기적으로 줄일 수 있다.
    • 이전에 작업할 때를 생각해보면 발주자가 뭔가 추가하고 싶은 데이터가 생기면, 그에 따른 모든 SQL구문까지 다 수정을 하고, 오류가 없는지, 오타는 없는지 확인해야 했는데 이런 부분에서 쓸데없는 시간 소요가 줄어드는 듯 하다.

 

[WORDPRESS로 환자 DATABASE 만들기] #5. 구조 간소화, 세부 작업들

중대한 문제 – 구조 간소화

작업을 진행하다보니 다음과 같은 문제가 생긴다.

바로 content-xxx.php와 archives-xxx.php 등을 너무 막 써버렸다.

무슨말인고 하니, 저 요소들은 재활용이 가능한데, 예를 들자면

ddd

이런식으로, 각각의 파일은 각각의 역할에 맞춰 써야하나, 나는 content-xxx.php파일을 전체 자료 리스트 출력을 위해 썼다던지 하는 문제가 생겨버렸다.

이러면 나중에 파일이 쓸데없이 많아지는 문제가 발생하는 것이다.

블로그를 게시판처럼 쓰게 되면서 발생해버린 문제인데, 나름 해결책은 여러개가 있을 것 같다.

일단 나는 이렇게 해결하기로 했다.

(1) Archive-xxx에서 list를 출력하는 부분을 다음과 같은 규칙으로 사용.

get_template_part( ‘template-parts/list’, $post->post_type );

content를 list로 바꾸고, 실제로 template-parts 폴더의 이전에 만든 content-patients.php를 list-patients.php로 바꾸었다.

즉 archive-patients.php와 list-patients.php 가 한쌍

(2) 그러면 medicaldata도 역시, archive파일에 위의 php코드를 변경,

그러면 archive-medicaldata.php, list-medicaldata.php가 한쌍

(3) 검색파일(search.php)에서는 고민을 해볼수 있다. 어차피 주민등록번호로 검색하면 단 하나의 자료가 나오는데, 이것도 리스트처럼 보였다가 쓰려면 list-…를 쓰면되고, 바로 그 하나를 들어가볼꺼라면 content…를 써면 되는 것이다.

아까 만들었던, content-search_citizen은 제거, search.php에서 template를 list-patients로 바꾸면 잘 된다.

결국 search.php 와 list-patients.php 도 한쌍

간략해졌다. 이제 환자 번호에 링크를 부여하고 그 링크를 눌렀을 때 나오는 페이지에서 그 환자에 해당하는 자료만 다시 list-medicaldata.php로 넘겨주는 식으로 하면, 아주 간소해지게 된다.

환자 ID 클릭을 통한 자료 나열

Custom UI를 통해 인식되는 single-patient.php 파일을 single.php를 통해 복사한다. 그리고

get_template_part( ‘template-parts/content’, ‘patient’ );

를 유지시킨다. (환자 한명을 보겠다는 이야기)

list-patients가 만들어졌으니, content-patients는 다시 구성되어야 한다. 아예 삭제하고 content-single.php를 복사해서 만드는게 낫다.

이번에는 그 환자의 medical data만 불러오면 되니, 기존 content-single에 있는 다음 내용들을 삭제한다.

the_title( ); , the_content();

그리고 다음 코드를 넣고

$pts_id = get_post_meta($post->ID, 'pts_id')[0];

	$args = array(
		'post_type'  => 'medicaldata',				
		'meta_query' => array(
			array(
				'key'     => 'pts_rec_id',
				'value'   => $pts_id,						
			),
		),
	);
	$query = new WP_Query( $args );

사실 앞에 포스팅한 medicaldata 찾기와 동일하다.

그리고 적절한 위치에 아래 코드들을 삽입하면

<header class="entry-header">
		<?php echo "ID : ".$pts_id; ?>(<?=$query->found_posts?>)
	</header><!-- .entry-header -->
	<table>			
		<?php
		// Start the Loop.
		while ( $query->have_posts() ) : $query->the_post();		
			get_template_part( 'template-parts/list', 'medicaldata');
		// End the loop.
		endwhile;
$query->reset_postdata();
		?>				
	</table>

Untitled-13

출력이 잘 된다.

파일이 불려지는 흐름

archive-patients.php / Search.php –> list-patients.php

: 전체 혹은 검색된 환자 list 출력 (list를 클릭하면 sinlge-patients.php 를 호출)

archive-medicaldata.php –> list-medicaldata.php

: 전체 자료 list 출력

single-patients.php –> list-medicaldata.php

: 그 환자에 해당하는 자료 list 출력 (list를 클릭하면 single-medicaldata.php 를 호출)

이런 흐름인 것이다.


다음 포스팅에서는 & References

#1. pts_id 와 pts_rec_id가 동일한 data만 추출하는 것. 그래야 그 환자의 자료만 볼 수 있을테니까.

#2. 레코드 삭제버튼

#3. 환자 데이터를 삭제할때 하위 레코들도 함께 삭제하기(귀찮을 것 같다)

#4. 환자등록시 주민등록번호를 받아서 암호화 시켜 저장하고, 주민등록번호로 환자 record를 찾을 수 있게 하기.

#5. 환자ID를 통한 환자 데이터 세부조회, 날짜별 정렬, 글쓴이 표시 등등

#6. DB를 엑셀(xml)로 export하기

PHPExcel 이라는 라이브러리를 이용한다 (https://phpexcel.codeplex.com/)

 

 

[WORDPRESS로 환자 DATABASE 만들기] #4. 주민등록번호 암호화, 환자 검색

Intro

결국 개인정보 이슈가 제일 큰 문제이다. 검색을 해보면 알겠지만, 완벽한 보안이란 없다는 점이 특히 의료정보와 관련되서는 많은 문제들을 발생시키게 된다.

아무튼 그 중에 대안으로는, 최소한의 정보만으로, 설령 그 데이터가 유출되더라도 알수 없게 하는게 최선이지 않을까.


주민등록번호 암호화

환자 등록창 다시 보기(WPF)

일단 주민등록번호를 입력받을 수 있게 환자 등록창을 구성한다. (이미 첫번째 포스팅에서 구현을 해긴 했었음)

#1. Frontend에서 pts_citizen_number라는 metakey를 갖는 text field를 구성

0810utf


SHA와 hooking(function.php)을 이용

#2. 1편에서 이용했던 hooking함수에 다음 코드를 넣는다. 당연한 얘기지만 함수 이름을 다른것으로 각각 써도 된다.

function wpufe_update_record_pid( $post_id ) {
    if ( isset( $_POST['pts_rec_id'] ) ) {
        update_post_meta( $post_id, 'pts_rec_id', $_POST['pts_rec_id']); 
    }
    if ( isset( $_POST['pts_citizen_number'] ) ) {
        update_post_meta( $post_id, 'pts_citizen_number', hash("sha256",$_POST['pts_citizen_number']."junn279")); 
    }    
}
 
add_action( 'wpuf_edit_post_after_update', 'wpufe_update_record_pid' );
add_action( 'wpuf_add_post_after_insert', 'wpufe_update_record_pid' );

pts_citizen number를 처리하는 함수로는 sha256을 이용했다. 이부분은 이전 링크들 참조.

Password Hashing and Encryption In PHP; MD5, SHA1, SHA256, BCrypt

http://www.openeg.co.kr/329

그냥 넣기 그래서 “junn279″라고 비밀번호 뒤에 붙여줬다. 이 부분은 추후에 환자별로 바꿔서 넣을 수도 있다. 그 예제는 위의 링크에 있으니 참조할 것. 목적은 조금이라도 자료가 유출되었을 때 분석되는 시간을 늘리게 함에 있는 것이다.

0810good

phpmyadmin으로 보면 암호화된 주민번호가 저장되었다.

결국 핵심은 주민번호 자체를 저장하는 것이 아니라, 암호화된 값을 저장한다는 것, sha256이 단방향 체계이기 때문에 이 값을 뒤집어볼 수 없다. 우리는 input을 확인할 수만 있는 것이다.

이로서 암호화 하기는 끝


주민등록번호를 이용한 검색

검색창 만들기

테마 폴더의 searchform.php 를 열어서 기존의 search_form을 복사해서 나만의 폼을 하나 더 만든다. 이건 환자 검색 전용으로 쓸 목적이다.

<form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
	<label>
		<span class="screen-reader-text"><?php echo _x( 'Search for:', 'label', 'twentysixteen' ); ?></span>
		<input type="search" class="search-field" placeholder="<?php echo esc_attr_x( 'Search &hellip;', 'placeholder', 'twentysixteen' ); ?>" value="<?php echo get_search_query(); ?>" name="s" />
	</label>
	<input type="hidden" name="option" value="citizen_number"/>
	<button type="submit" class="search-submit"><span class="screen-reader-text"><?php echo _x( 'Search', 'submit button', 'twentysixteen' ); ?></span></button>
</form>

거기에 추가한 것은 <input type=”hidden” name=”option” value=”citizen_number”/> 이 코드 하나다.  이를 통해 검색을 바꿔볼 것이다.

이것을 굳이 ‘검색’으로 한 목적은 따로 없다. 두번째 포스팅의 codex를 이용할 수도 있을 것 같다.

검색 관련 쿼리 수정(function.php)

#4. 아래 링크를 보면 custom searching에 대한 모든 것이 되어있다.

이를 적절히 수정한다.

$list_searcheable_acf = array(“pts_citizen_number”);

일단 이 부분은 이렇게 수정하는데, 결국 나는 pts_citizen_number라는 meta_key 값만 추가로 찾을 것이다 라는 이야기

advanced_custom_search 함수의 셋째줄을

if ( empty( $where ) || $_GET[‘option’] != ‘citizen_number’ )
return $where;

이렇게 해주면, 아까 hidden form으로 넘긴 값이 있을때만, 이하의 코드들로 custom searching을 해주겠다는 이야기

그 아랫줄을

$terms = $wp_query->query_vars[ ‘s’ ];
// add encyrpty
$str = $wp_query->query_vars[ ‘s’ ];
$terms = hash(“sha256″,$str.”junn279”);

이렇게 해주면 $terms을 그냥 넘어온 input값이 아닌 암호화시킨 값으로 대조해주겠는 것이다.

그럼 결과는

0810try

검색되는 레코드가 있다. (UI는 건드리지 않아 엉망이다…이부분은 다음 편에 다루기로)

그리고 편집을 누르면

0810confirm

암호화되어있는 주민번호가 등장한다.

잘 찾아졌다.

위 화면 우측에 검색창이 두개있다. 위에것과 아래것은 결과가 완전히 다르다. 당연하겠지만, 위에 것으로 하면

if ( empty( $where ) || $_GET[‘option’] != ‘citizen_number’ )
return $where;

여기에서 걸리기 때문에 원래의 wordpress searching이 되는 것이기 때문이다.

환자 찾기 페이지 UI 수정

search가 이루어진 후에는 테마 폴더의 search.php 에서 그 결과를 출력한다.

그 페이지의 문구를

get_template_part( ‘template-parts/content’, ‘search’ );

에서

get_template_part( ‘template-parts/content’, ‘search_citizen’ );

로 바꾸고,

template-parts 폴더의 content-search.php를 같이 search_citizen.php로 복사를 해서 이전의 content-patients.php 처럼 만들어주면 작업은 완료된다.

Untitled-1

혹은 애초에 contents-patients.php를 부르면, UI가 같을 경우라면 파일을 두개 중복해서 둘 필요가 없게 될 것 같다.


다음 포스팅에서는 & References

#1. pts_id 와 pts_rec_id가 동일한 data만 추출하는 것. 그래야 그 환자의 자료만 볼 수 있을테니까.

#2. 레코드 삭제버튼

#3. 환자 데이터를 삭제할때 하위 레코들도 함께 삭제하기(귀찮을 것 같다)

#4. 환자등록시 주민등록번호를 받아서 암호화 시켜 저장하고, 주민등록번호로 환자 record를 찾을 수 있게 하기.

#5. 환자ID를 통한 환자 데이터 세부조회, 날짜별 정렬, 글쓴이 표시 등등

#6. DB를 엑셀(xml)로 export하기

https://paulund.co.uk/access-database-outside-wordpress

http://wordpress.stackexchange.com/questions/26254/how-to-include-wp-load-php-from-any-location

https://paulund.co.uk/creating-custom-tables-wordpress-plugin-activation

[WORDPRESS로 환자 DATABASE 만들기] #3. 환자별 자료 확인, 자료 삭제

준비작업

Untitled-1

#0. 작업을 쉽게 하기 위해 메뉴를 만들었다.

Untitled-3

먼저 적어보자면, Edit를 해봤더니 63이 뜬다. 알고보니 이건 pid라고 워드프레스 내부적으로 post의 id값이 넘어온건데, 두번째 포스팅에서 내가 사용한 변수인 $pid랑 겹쳤다. 그래서 포스팅 내용은 수정했다..) >> 결론은 $pid라는 변수 이름은 함부로 쓰지 말자

환자 자료의 수정 / 삭제 기능

UI 수정

Edit / Delete 추가하는 것을 시작하면

#1. 자료를 몇개 추가하고, 여기에 편집과 수정기능을 추가

Untitled-2

content-medicaldata.php

	<td><?php
			edit_post_link(
				sprintf(
					/* translators: %s: Name of current post */
					__( 'Edit<span class="screen-reader-text"> "%s"</span>', 'twentysixteen' ),
					get_the_title()
				),
				'<span class="edit-link">',
				'</span>'
			);
		?>
	</td>
	<td>
	<?php 
	$currnet_user = wp_get_current_user();
	<?php if ($post->post_author == get_current_user_id()/*$current_user->ID*/) { ?><p><a onclick="return confirm('Are you SURE you want to delete this Wish?')" href="<?php echo get_delete_post_link( $post->ID ) ?>">삭제</a></p><?php } ?>
     </td>

그러면

ondelete

음..조잡한 UI다.

아무튼 소스를 보면 $current_user->ID가 안먹는다. 이상하다. 레퍼런스에서는 사용할 수 있는 변수값인데.

이부분 나중에 관리자가 아닌 ID로 다시 테스트해봐야한다. 


Custom Query 사용

이번엔 ID별로 등록된 자료를 계산해보자

content-patients.php 상단에 다음 소스를 추가.

이왕이면 지난번 포스팅에서 작성된 $pts_id = get_post_meta($post->ID, ‘pts_id’)[0]; 밑에다 두자. 그래야 $pts_id 라는 변수를 이용할 수 있으니.

$args = array(
				'post_type'  => 'medicaldata',				
				'meta_query' => array(
					array(
						'key'     => 'pts_id',
						'value'   => $pts_id,						
					),
				),
			);
			$query = new WP_Query( $args );

핵심은 meta 값으로 환자 ID를 맞추는 것이다.

그리고 HTML 코드 수정

<td><?=$pts_id?> (<?=$query->found_posts?>)</td>

Untitled-4

완료.


다음 포스팅에서는 & References

  •  pts_id 와 pts_rec_id가 동일한 data만 추출하는 것. 그래야 그 환자의 자료만 볼 수 있을테니까.
  • 레코드 삭제버튼
  • 환자 데이터를 삭제할때 하위 레코들도 함께 삭제하기(귀찮을 것 같다)
  • 환자등록시 주민등록번호를 받아서 암호화 시켜 저장하고, 주민등록번호로 환자 record를 찾을 수 있게 하기.
  • 환자ID를 통한 환자 데이터 세부조회, 날짜별 정렬, 글쓴이 표시 등등.
  • DB를 엑셀(xml)로 export하기
    • https://paulund.co.uk/access-database-outside-wordpress
    • http://wordpress.stackexchange.com/questions/26254/how-to-include-wp-load-php-from-any-location
    • https://paulund.co.uk/creating-custom-tables-wordpress-plugin-activation