📘

WixStudioでVeloを使う Step 19 「コレクションを使う - ユーザ・権限」

2023/09/20に公開

はじめに

Step18までの内容で、HolidaysコレクションとCommentsコレクションを連携させて「祝日毎の掲示板」のようなものが出来上がった。いったいはコレは何..。まあ、気にしない。この掲示板のようなものは不特定多数の利用者が各祝日に対してコメントを残す(投稿)ことが出来る。でも、投稿したコメントを消すことは出来ない。やっぱり消せなきゃダメでしょ。

目標

投稿したコメントを削除できるようにする。ただし、削除は投稿した人だけに許されるべき権利。そんな機能の実現を目指す。

準備

前回の続きです。
HolidaysコレクションとCommentsコレクションを連携させた「掲示板のようなも」に機能を拡張します。

掲示板のようなもの
掲示板のようなもの

Commentsコレクションを設定する

先にVeloを有効にしておく。
Commentsコレクションの設定を変更する。具体的には「権限」の設定。

Commentsコレクションの権限設定を開く
Commentsコレクションの見解設定を開く

コンテンツの削除に関する権限を変更しておかなければならない。「『サイト会員執筆者』はコンテンツ削除が可能」に設定しておく。

『サイト会員執筆者』はコンテンツ削除が可能
『サイト会員執筆者』はコンテンツ削除が可能

これでコメントを登録した人が、そのコメントを削除する権限を持つことになる。この時点では「権限」だけの話なので「削除が許される」というだけで、削除する方法はこの後に作る。

ここで1点確認する。「投稿した本人には削除を許す」という設定にしたが、「投稿した本人」って誰という疑問。これを確認するには、Commentsコレクションの隠された列を確認すれば良い。

CommentsコレクションのCMS画面から「フィールドを管理」を選択する。「所有者」にチェックをすれば「所有者」列が表示される。

「所有者」列を表示
「所有者」列を表示

所有者列の内容を確認すると半角英数で構成された値が格納されている。

「所有者」列の値は謎の半角英数
「所有者」列の値は謎の半角英数

この値は会員(Member)の識別子(ID)になる。Commentsを登録する際にログインしている会員によって「所有者」列の値は異なる。この制御自体はシステムが行っているので気にする必要はない。Commentsコレクションのアイテムには会員を識別するための情報がちゃんと含まれていることだけを僕らは認識していれば良い。

ここまででCommentsコレクションの設定は完了。

Holidays(Code)画面を改造する

改造ポイントは1つ。リピータのコメントに削除ボタンを付ける。

削除ボタンをリピータに配置
削除ボタンをリピータに配置

ボタンの種類とレイアウトにこだわり無し。

以上でHolidays(Code)画面の改造は完了。

スクリプトを書く

スクリプトで処理しなければならないポイントは2つ。

  1. ログイン状態の確認
    • 現在ログインしている会員情報を取得
  2. コメントの削除処理
    • 削除できるコメントの削除ボタンを有効にする
    • 削除ボタンを押された削除する

今回もHolidays (Code).###.jsを開いてをスクリプトを書く。

Holidays (Code).###.js -
import wixData from 'wix-data'
import { currentMember } from 'wix-members-frontend'

$w.onReady(async function () {
  const holiday = $w("#dynamicDataset").getCurrentItem()
  const repeater = $w('#repeater1')
  const member = await currentMember.getMember()

  const initRepeater = () =>{
    wixData.query('comments')
      .eq('holiday',holiday._id)
      .descending('_createdDate')
      .find()
      .then( results => {
        repeater.data = results.items
      })
    }

  const deleteComment = (commentId) => {
    wixData.remove('comments',commentId)
      .then( result => {
        initRepeater()
      })
  }

  initRepeater()

  repeater.onItemReady( ($item,itemData,index) => {

    $item('#button4').disable()
    $item('#text7').text = itemData.message
    $item('#text6').text = itemData._createdDate.toLocaleString()

    if(member && member._id === itemData._owner){
      $item('#button4').enable()
      $item('#button4').onClick( event => {
        deleteComment(itemData._id)
      })
    }
  })

  $w('#button3').onClick( event => {
    const comment = {}
    let input = $w('#textBox1').value
    comment.title = input.split(/\r\n|\n|\.|/)[0]
    comment.message = input
    comment.holiday = holiday
    wixData.save('comments',comment)
    .then( item => {
      $w('#textBox1').value = null
      initRepeater()
    })
    .catch( error => {})
  })
})

ログイン状態の確認

利用者の会員情報を取得するにはwix-members-frontendを利用するのでimport { currentMember } from 'wix-members-frontend' を先頭に記述しておく。
実際に会員情報を取得しているのはconst member = await currentMember.getMember()の部分。currentMember.getMember()で会員情報を取得出来る。getMember()は非同期で処理されPromiseが返される。でも、結果を取得してから先へ進みたいのでawaitを使っている。$w.onReady(async function () {..}) にもasyncを付けておく。

https://www.wix.com/velo/reference/wix-members-frontend/currentmember
https://www.wix.com/velo/reference/wix-members-frontend/currentmember/getmember

コメントの削除処理

削除処理を関数(deleteComment())として定義しておく。

Holidays (Code).###.js 抜粋
const deleteComment = (commentId) => {
  wixData.remove('comments',commentId)
    .then( result => {
      initRepeater()
    })
}

引数には削除したいコメントのIDを指定する。
所有者列と同様にID列は自動的にシステムによって制御されている。

IDはシステムが管理
IDはシステムが管理

ID列の値は._idで取得出来る。
ID列のフィールドキー
ID列のフィールドキー

コレクションのデータを削除するときはwixData.remove()を使う。引数は2つ。1つ目には削除元のコレクションIDを指定する。今回はCommentsコレクションが対象。二つ目の引数は削除したいアイテムのIDを渡す。結果はPromise<Object>.then()で処理する。正常に削除出来た際の処理として、リピータを再設定する(initRepeater())。

https://www.wix.com/velo/reference/wix-data/remove

deleteComment()が呼び出されるタイミングは、リピータに配置したボタンを押されたとき。記述はrepeater.onItemReady()内。

Holidays (Code).###.js 抜粋
repeater.onItemReady( ($item,itemData,index) => {
  $item('#button4').disable()
  $item('#text7').text = itemData.message
  $item('#text6').text = itemData._createdDate.toLocaleString()

  if(member && member._id === itemData._owner){
    $item('#button4').enable()
    $item('#button4').onClick( event => {
      deleteComment(itemData._id)
    })
  }
})

削除ボタンのIDは#button4$item('#button4').disable()で削除ボタンを使えないようにしておく(無効化)。削除ボタンが利用出来るのは限定的。

  • ページを会員として閲覧している。
  • 削除しようとしているコメントが自身による投稿である。

だから、基本は無効状態とした。

if(member && member._id === itemData._owner){...}は閲覧者の状態を判定。判定するポイントは2点。

  • memberには現在の会員情報が入っている。
  • さらに、会員のID(member._id)と該当コメントの所有者(itemData._owner)が一致している。要はコメントの所有者かどうか。

条件が満たされた場合に、そのコメントの削除ボタンは有効化($item('#button4').enable())される。削除ボタンがクリックされた際にはdeleteComment(itemData._id)が呼び出され削除が実施される。itemData._idでコレクションのアイテムのIDが取得出来る。

基本的にはこれで完成。

ログイン

基本的には完成しているが、ログインを求められないため検証が出来ない。このページにアクセスしたタイミングで未ログイン状態の利用者に対してログインを促す。

ログインを促す
ログインを促す

スクリプトで実現すると、こんな感じの記述が入ればログインを促してくれる。ちょっと中途半端な感じが漂う…。

let member = null
let isLogin = false

const initRepeater = () =>{/*省略*/}
const deleteComment = (commentId) =>{/*省略*/}

authentication.onLogin( async login => {
  member = await login.getMember()
  initRepeater()
})

isLogin = authentication.loggedIn()
if (isLogin) {
  member = await currentMember.getMember()
  initRepeater()
}else{
  authentication.promptLogin({ mode: 'login', modal: true })
      .then(() => {})
      .catch(error => { console.log(error)})
} 

実際に組み込んでみたコードも後ほど掲載する。このスクリプトのポイントは3箇所。

  • authentication.onLogin()はログインしたときに実施したい処理を指定する。
  • authentication.loggedIn()でログイン状態を取得する。
  • authentication.promptLogin()でログインフォームを提示する。

何れもwix-members-frontendのautheticationを利用する事になるので事前にimportが必要(import { authentication, currentMember } from 'wix-members-frontend')。

https://www.wix.com/velo/reference/wix-members-frontend/authentication
https://www.wix.com/velo/reference/wix-members-frontend/authentication/onlogin
https://www.wix.com/velo/reference/wix-members-frontend/authentication/loggedin
https://www.wix.com/velo/reference/wix-members-frontend/authentication/promptlogin

これらの記述をわざわざ書かなくても、画面のアクセス許可を設定するのも良いと思う。ページにアクセスしたタイミングでログインが促される。ただし、ページ自体が会員限定ページになってしまうので未ログインではアクセス出来なくなる。

ページのアクセス許可
ページのアクセス許可

参考

Holidays (Code).###.js
import wixData from 'wix-data'
import { authentication, currentMember } from 'wix-members-frontend'

$w.onReady(async function () {
    const holiday = $w("#dynamicDataset").getCurrentItem()
    const repeater = $w('#repeater1')
    
    let member = null
    let isLogin = false

    const initRepeater = () => {
        wixData.query('comments')
            .eq('holiday', holiday._id)
            .descending('_createdDate')
            .find()
            .then(results => {
                repeater.data = results.items
            })
    }

    const deleteComment = (commentId) => {
        wixData.remove('comments', commentId)
            .then(result => {
                initRepeater()
            })
    }

    authentication.onLogin( async login => {
      member = await login.getMember()
      initRepeater()
    })

    isLogin = authentication.loggedIn()
    if (isLogin) {
      member = await currentMember.getMember()
      initRepeater()
    }else{
      authentication.promptLogin({ mode: 'login', modal: true })
          .then(() => {})
          .catch(error => {
            console.log(error)
          })
    } 
    

    repeater.onItemReady(($item, itemData, index) => {

        $item('#button4').disable()
        $item('#text7').text = itemData.message
        $item('#text6').text = itemData._createdDate.toLocaleString()

        if (member && member._id === itemData._owner) {
            $item('#button4').enable()
            $item('#button4').onClick(event => {
                deleteComment(itemData._id)
            })
        }
    })

    $w('#button3').onClick(event => {
        const comment = {}
        let input = $w('#textBox1').value
        comment.title = input.split(/\r\n|\n|\.|/)[0]
        comment.message = input
        comment.holiday = holiday
        wixData.save('comments', comment)
            .then(item => {
                $w('#textBox1').value = null
                initRepeater()
            })
            .catch(error => {})
    })
})

動作確認

サイトを公開して、祝日のアイテムページを開いてみる。既に投稿済のコメントがある。

1955-01-01_元日
1955-01-01_元日

削除ボタンは無効状態なので押せない。

削除ボタンは無効状態
削除ボタンは無効状態

ログインしてからコメントを投稿する。

ログイン後、新しいコメントを登録する
ログイン後、新しいコメントを登録する

表示されたコメントの削除ボタンは有効になっている。

自身が投稿したコメントの削除ボタンは有効状態
自身が投稿したコメントの削除ボタンは有効状態

作成したデモ

まとめ

まず、コレクションのアイテムに対する操作はスクリプトだけでは実現しない。操作に対する許可を適切に設定しておく必要があるので注意。今回はアイテムの削除を行う処理が目的で、これ自体はそれほど難しくなかった(wixData.remove())。使い方も単純だった。また、アイテムの所有者を確認するのも簡単。コレクションのアイテムには、システムが管理するフィールドが幾つか用意されている。例えばID(_id)とか所有者(_owner)とか。これらの値を確認しながら画面を制御すれば良い。
それらよりも苦労したのはログインを促す処理。正解がわからなかった。ちょっと力ずくになった気がするので、そのうちリベンジする。

つづき

WixStudioでVeloを使う Step 20 「ジョブ・スケジュールを使う」

Discussion