見出し画像

スプレッドシートでwikiサイトを強引に作った話。

さいきんnote書いてなくて良くないなって思ったんですが、書くことも特にないので前職のときに書いたコードをここにメモしておくnoteにしました。
たぶん誰の役にも立たないし、私が将来このコードを使うこともないでしょう。墓地です。

ということで、前職で「GASだけでスプレッドシートからデータ引っ張ってくるwikiサイトもどき」を作った話をします。

なんでそんな無茶なことをしたのか。

そもそもwikiサイト作りたいっていう話を上司からもらったときに、「いやそんなんちゃんとツール使ってくださいよ(っていうかbacklogあるんだから使えや)」って言ったんですけど、なぜか使ってもらえず。マジ解せぬ。
(みんなに書いてもらう運用にしてもちゃんと書けないと思うんだよね、的なことを言われた気がします。じゃあやめちまえ)

なんかよくわからないまま、「wikiサイト作りたい。要件?なにそれおいしいの?全部いい感じによろ♡」って感じでwikiサイト作りが始まりました。

とりあえずどんな感じに作りたいかを決める

ぜったい使われなくなるのが目に見えていたので、とりあえず自分が勉強したかったことをやってみることを目的にしました。
本質的に言えば、運用してもらえる機能とルールを考えるべきですが知ったこっちゃありません。

目的
・Googleサイトに動きつけるってどうやるんだ?
・フレームとかって使えないんだっけ…?

専用のツールが使えないので、集約先としてはGoogleサイト一択でした。
ただ、通常の作り方だとファイルをいちいちサイトに追加せねばならず、そんな面倒なことどうせ誰もやりません。
もともとチームではスプレッドシートでそれぞれ情報を管理していたので、それを反映するようにしました。

作り
・定期的に、ドライブの中身を一覧にするスプシ作成
・サイトでボタンを押すと、その一覧を見に行く
・結果をサイト上に表示
集約したい情報
・案件管理(ステータス・担当者など)
・案件のふりかえり・ナレッジ
・営業資料や提案資料など
・外部セミナーの資料やツールのマニュアルなど

3点目についてはスプレッドシートで管理しているとかが特にないので、新しくシートを作ってそこにGASで一覧を作るようにしました。

というわけで目的別に「資料を探す」「案件を探す」「ナレッジを探す」の3種類にページを分割しました。

画像2
グロナビこんなん。

で、単純に並べるだけだと使い勝手が悪いので、ある程度検索できるようにしたいと思いました。

画像1
各ページに検索できる感じのボタンを。

これは案件を探すときのページですが、案件のカテゴリごとに検索できるようにボタンにしています。
ボタンっぽく見えるといいなーと思って影を入れてますが、改めて見るとエグいダサいですね。

ボタンを押すと、該当する内容が下に表示されるようになっています。

画像3
検索ボタンを押すとスプシにあるリストを下に反映します。


GAS書く

で、ここからが個人的にこの作業をやる意味で、「Googleサイトで動的なことやるのってどうやるんだろ?」というのを勉強したかったわけです。

資料埋めるだけならGoogleサイトのきれいなテンプレートがあるんですが、検索するとかをやろうとするとできなそうよくわからん。ということで、GASでサイト作るよー。


①サイトの見た目(HTMLとCSS)

久しぶりにゼロからHTMLを組んで作っていきます。まずは各ページを作ります。

↓たとえばindex.html。素人がコピペで書いてるのでHTMLもCSSもめちゃくちゃです。

<!DOCTYPE html>
<html>
 <head>
 <!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXX-1"></script>
<script>
 window.dataLayer = window.dataLayer || [];
 function gtag(){dataLayer.push(arguments);}
 gtag('js', new Date());

   gtag('config''UA-XXXXXXXX-1',{
   'page_title' : '資料を探す',
   'page_path' : '/knowledge/document'});
</script>

   <base target="_top">
   <title>wiki</title>
   <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
 </head>
 <body>
   <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
   <div id="wrap">
   

 <!-- side menu -->
   <div class="div_a">
     <p align="center">\ メニューだよ /</p>
     <div class="menu">
     <ul class="menu">
       <li><a href="https://script.google.com/a/XXXXXXXX/macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/exec?p=document">資料を探す</a></li>
       <li><a href="https://script.google.com/a/XXXXXXXX/macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/exec?p=project">案件を探す</a></li>
       <li><a href="https://script.google.com/a/XXXXXXXX/macros/s/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/exec?p=review">プロジェクトレビューを探す</a></li>
     </ul>
     </div>
   </div>
   
<!-- contents -->
   <div class="div_b">
   <!-- jump button -->
     <p class="pagetop"><a href="#" onclick="$('body, html').animate({ scrollTop: 0 }, 500);
       return false;">▲検索しなおす</a></p>
   <!-- jump button -->
               
       
     <h1>簡易検索ツール</h1>
     <p>知りたいことを教えてね</p>
     <div class="btn_area" id="btn_area_1">
       <button type="button" class="btn-square-shadow" onClick="getData(this.value)" value="AAAAA">AAAAA</button>
       <button type="button" class="btn-square-shadow" onClick="getData(this.value)" value="BBBBB">BBBBB</button>
       <button type="button" class="btn-square-shadow" onClick="getData(this.value)" value="CCCCC">CCCCC</button>
       <button type="button" class="btn-square-shadow" onClick="getData(this.value)" value="DDDDD">DDDDD</button>
     // 検索したい個数だけここにボタン入れる(valueは日本語でもおっけー)
       
       <p align="center"> 画面が変わるまでちょっと時間がかかることがあります。気長におまちくださいmm </p>
     </div>
     <div class="btn_area" id="btn_area_2">
       <h2 id="category"></h2>
     </div>
     <div class="btn_area" id="btn_area_3">
       <script type="text/javascript">
         //doget.gsのtable関数呼び出し
         function getData(txt){
           google.script.run
           .withSuccessHandler(function(data) {
             // GAS正常終了時の処理
             outPut(data);
           })
         .withFailureHandler(function(data) {
           // GASエラー時の処理
           window.alert("エラーが出たのでこの画面をスクショして管理者に連絡してください:エラーコード004 : " + data);
         })
         .table_document(txt);
         }
         
         //正常終了時に呼び出す書き込み処理 ------------
         function outPut(values){
         var table = ('<h2>' + values[0][0] + '</h2>');
             table += '<table>';
         
         for(var i = 1; i < values.length; i++){
           table += '<tr>';
           table += '<td>' + values[i][0] + '</td>';
           table += '<td>' + '<a href="' + values[i][2] + '" target="_blank">' + values[i][1] + '</a></td>';
           table += '</tr>';
          }
         table += '</table>';
         $('div#btn_area_3').html(table);
         }  
         
       </script>
     </div>
   </div>
   <!-- close wrap -->
 </body>
</html>

ちゃっかりgtag入れちゃったりして。

見て分かるかもしれませんが、一番上のメニューは全部のページに同じものを埋めてあるだけで、iframeとかは特に使ってないです。
複数ページの切り替えどうやるのか知りたかったので今回はこの形。パラメーターをつけるみたいです。

やってないですが、iframeはこのへんが参考になるかもしれません。


それからCSSを作りました。ここも疲れたので必要最低限って感じ。

画像4
ほんとは下にボタンとかあるけどね。



②サイトの挙動(JavaScript)

さて、こっからが面倒で
サイト側でボタンが押される→押されたボタンに該当するデータだけをスプレッドシートからとってくる→サイトに表示
というステップを作ります。

まず踏み台にするスプレッドシートを作ります。

スプレッドシート側でGASを組んで、ドライブの中身を引っこ抜いてあれこれ加工するファイルにしました。
毎週1回、ドライブの特定フォルダの中の資料一覧を作るようにしています。もっと効率良い書き方ある気がしますが、動けばいいのです。エンジニアじゃないもの。

function ShareFolderList() {
 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('共有ファイル一覧');
 var key = 'フォルダのIDをここに';
 var name = ""
 var i = 0 //フォルダを処理する行位置
 var j = 0 //サブフォルダを出力する行位置
 var ary = [];
 
 sheet.getRange("A:L").clear();  //過去のデータは削除しとく

 do {
   //フォルダ一覧を取得
   var folders = DriveApp.searchFolders("'" + key + "' in parents");
   //フォルダ一覧からフォルダを順に取り出す
   while(folders.hasNext()){
     //シートにフォルダ名称とIdを出力
     i++
     var folder = folders.next();

     sheet.getRange(i, 1).setValue(name + folder.getName());
     sheet.getRange(i, 2).setValue(folder.getId());
     sheet.getRange(i, 3).setValue(folder.getUrl());
   }
   //シートからフォルダを取得し次へ
   j++;
   name = sheet.getRange(j, 1).getValue() + " > ";
   key = sheet.getRange(j, 2).getValue();
 } while (key != ""); //処理するフォルダがなくなるまで
}

function ShareFileList() {
 var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('共有ファイル一覧');
 var list = sheet.getRange('A1').getDataRegion().getValues();
 var ary = [];
 for(var k = 0; k < list.length; k++){
   var key = list[k][1];
   var parent = list[k][0];
   var files = DriveApp.getFolderById(key).getFiles();
   var file_name = DriveApp.getFileById(key).getName();
   
   while(files.hasNext()){
     var file = files.next();
     ary.push([parent, file.getName() , file.getUrl()]);      
   }    
 }
 
 sheet.getRange(3,10,ary.length,ary[0].length).setValues(ary)
 
}

こんな感じでシートに吐き出し。

画像5
カテゴリ、のところがサイト側で選べる検索条件です。


で、サイト上でボタンを押したときに、そのボタンのvalueを使ってスプレッドシート内の該当箇所を探して、サイト上にtableタグで表示するようにしています。

javascript.js

var id = 'XXXXXXXXXXXXXXXXXXXXXXXX'//作ったスプレッドシートのファイルID
var ss = SpreadsheetApp.openById(id);

function table_document(txt){
 var key = txt;
 var sheet = ss.getSheetByName('シート名をここに');
 var textFinder = sheet.getRange('A2:BF2').createTextFinder(key).findAll(); //セル範囲は適宜
 
   if(textFinder.length > 1){
     window.alert("エラーが出たのでこの画面をスクショして管理者に連絡してください:エラーコード001")
   } else {
     var target = textFinder[0].getA1Notation();
     var values = sheet.getRange(target).getDataRegion().getValues();
   
     return values;
   }
}

エラーコードとか書いてるけど、画面ごとに01とか02とか振ってただけなのでとくに意味はないです。あと利用者はボタン押すだけなので、エラーも出たことないです。

さっきのindex.htmlの中にあるoutPut()で、むりくりtableタグにして、画面に出しています。

あらためて並べると、変数名とかぜんぜん規則性なくてきもちわるいですね。ドンマイです。

そんなわけで、サイトが無事できあがりました。

おわり

とりあえずGASを理解できたのでよかったです。
私が会社を辞めた今、このコードたちはきっとGoogleドライブの海のどこかを彷徨っているのでしょう。もう二度とやるもんか。

結論、wikiはちゃんとツールを使いましょう