ไฟล์แนบประเภทเนื้อหา

นี่คือคำแนะนำทีละขั้นตอนที่ 4 ในชุดคำแนะนำทีละขั้นตอนเกี่ยวกับส่วนเสริมของ Classroom

ในคำแนะนำทีละขั้นตอนนี้ คุณจะได้โต้ตอบกับ Google Classroom API เพื่อสร้างไฟล์แนบ โดยคุณจะระบุเส้นทางให้ผู้ใช้ดูเนื้อหาของไฟล์แนบ ซึ่งมุมมองจะแตกต่างกันไปตามบทบาทของผู้ใช้ในชั้นเรียน คำแนะนำทีละขั้นตอนนี้ครอบคลุม ไฟล์แนบประเภทเนื้อหา ซึ่งไม่จำเป็นต้องให้นักเรียนส่งงาน

ในคำแนะนำทีละขั้นตอนนี้ คุณจะได้ทำสิ่งต่อไปนี้

  • ดึงข้อมูลและใช้พารามิเตอร์การค้นหาส่วนเสริมต่อไปนี้
    • addOnToken: โทเค็นการให้สิทธิ์ที่ส่งไปยังมุมมองการค้นพบไฟล์แนบ
    • itemId: ตัวระบุที่ไม่ซ้ำกันสำหรับการบ้านและรายงาน สื่อการเรียนการสอนของชั้นเรียน หรือประกาศที่ได้รับไฟล์แนบของส่วนเสริม
    • itemType: "courseWork", "courseWorkMaterials" หรือ "announcement"
    • courseId: ตัวระบุที่ไม่ซ้ำกันสำหรับหลักสูตร Google Classroom ที่มีการสร้างงาน
    • attachmentId: ตัวระบุที่ไม่ซ้ำกันที่ Google Classroom กำหนดให้กับไฟล์แนบของส่วนเสริมหลังจากสร้าง
  • ใช้พื้นที่เก็บข้อมูลถาวรสำหรับไฟล์แนบประเภทเนื้อหา
  • ระบุเส้นทางเพื่อสร้างไฟล์แนบและแสดง iframe ของมุมมองของครูและมุมมองของนักเรียน
  • ส่งคำขอต่อไปนี้ไปยัง Google Classroom API สำหรับส่วนเสริม
    • สร้างไฟล์แนบใหม่
    • รับบริบทของส่วนเสริม ซึ่งจะระบุว่าผู้ใช้ที่เข้าสู่ระบบเป็นนักเรียนหรือครู

เมื่อเสร็จแล้ว คุณจะสร้างไฟล์แนบประเภทเนื้อหาในงานผ่าน UI ของ Google Classroom ได้เมื่อเข้าสู่ระบบในฐานะครู นอกจากนี้ ครูและนักเรียนในชั้นเรียนยังดูเนื้อหาได้ด้วย

เปิดใช้ Classroom API

เริ่มโทรหา Classroom API ด้วยขั้นตอนนี้ คุณต้องเปิดใช้ API สำหรับโปรเจ็กต์ Google Cloud ก่อนจึงจะโทรหา API ได้ ไปที่รายการคลัง Google Classroom API และเลือกเปิดใช้

จัดการพารามิเตอร์การค้นหาของมุมมองการค้นพบไฟล์แนบ

ดังที่ได้กล่าวไว้ก่อนหน้านี้ Google Classroom จะส่งพารามิเตอร์การค้นหาเมื่อ โหลดมุมมองการค้นพบไฟล์แนบใน iframe ดังนี้

  • courseId: รหัสของหลักสูตร Classroom ปัจจุบัน
  • itemId: ตัวระบุที่ไม่ซ้ำกันสำหรับการบ้านและรายงาน สื่อการเรียนการสอนของชั้นเรียน หรือประกาศที่ได้รับไฟล์แนบของส่วนเสริม
  • itemType: "courseWork", "courseWorkMaterials" หรือ "announcement"
  • addOnToken: โทเค็นที่ใช้ให้สิทธิ์การดำเนินการบางอย่างของส่วนเสริมใน Classroom
  • login_hint: รหัส Google ของผู้ใช้ปัจจุบัน

คำแนะนำทีละขั้นตอนนี้จะกล่าวถึง courseId, itemId, itemType และ addOnToken โดยให้เก็บรักษาและส่งพารามิเตอร์เหล่านี้เมื่อโทรหา Classroom API

เช่นเดียวกับขั้นตอนก่อนหน้าในคำแนะนำทีละขั้นตอน ให้เก็บค่าพารามิเตอร์การค้นหาที่ส่งมาไว้ในเซสชัน เราต้องทำเช่นนี้เมื่อเปิดมุมมองการค้นพบไฟล์แนบเป็นครั้งแรก เนื่องจากเป็นโอกาสเดียวที่ Classroom จะส่งพารามิเตอร์การค้นหาเหล่านี้

Python

ไปที่ไฟล์เซิร์ฟเวอร์ Flask ที่ระบุเส้นทางสำหรับมุมมองการค้นพบไฟล์แนบ (attachment-discovery-routes.py หากคุณทำตามตัวอย่างที่เราให้ไว้) ที่ด้านบนของเส้นทางหน้า Landing Page ของส่วนเสริม (/classroom-addon ในตัวอย่างที่เราให้ไว้) ให้ดึงข้อมูลและเก็บพารามิเตอร์การค้นหา courseId, itemId, itemType และ addOnToken

# Retrieve the itemId, courseId, and addOnToken query parameters.
if flask.request.args.get("itemId"):
    flask.session["itemId"] = flask.request.args.get("itemId")
if flask.request.args.get("itemType"):
    flask.session["itemType"] = flask.request.args.get("itemType")
if flask.request.args.get("courseId"):
    flask.session["courseId"] = flask.request.args.get("courseId")
if flask.request.args.get("addOnToken"):
    flask.session["addOnToken"] = flask.request.args.get("addOnToken")

เขียนค่าเหล่านี้ลงในเซสชันเฉพาะในกรณีที่มีค่าอยู่เท่านั้น ระบบจะไม่ส่งค่าเหล่านี้อีกครั้งหากผู้ใช้กลับไปที่มุมมองการค้นพบไฟล์แนบในภายหลังโดยไม่ปิด iframe

เพิ่มพื้นที่เก็บข้อมูลแบบถาวรสำหรับไฟล์แนบประเภทเนื้อหา

คุณต้องมีบันทึกในเครื่องของไฟล์แนบที่สร้างขึ้น ซึ่งจะช่วยให้คุณค้นหาเนื้อหาที่ครูเลือกได้โดยใช้ตัวระบุที่ Classroom ให้ไว้

ตั้งค่าสคีมาฐานข้อมูลสำหรับ Attachment ตัวอย่างที่เราให้ไว้จะแสดงไฟล์แนบที่มีรูปภาพและคำบรรยาย Attachment มีแอตทริบิวต์ต่อไปนี้

  • attachment_id: ตัวระบุที่ไม่ซ้ำกันสำหรับไฟล์แนบ กำหนดโดย Classroom และแสดงผลในการตอบกลับเมื่อสร้างไฟล์แนบ
  • image_filename: ชื่อไฟล์ในเครื่องของรูปภาพที่จะแสดง
  • image_caption: คำบรรยายที่จะแสดงพร้อมกับรูปภาพ

Python

ขยายการใช้งาน SQLite และ flask_sqlalchemy จากขั้นตอนก่อนหน้า

ไปที่ไฟล์ที่คุณกำหนดตารางผู้ใช้ (models.py หากคุณทำตามตัวอย่างที่เราให้ไว้) เพิ่มโค้ดต่อไปนี้ที่ด้านล่างของไฟล์ใต้คลาส User

class Attachment(db.Model):
    # The attachmentId is the unique identifier for the attachment.
    attachment_id = db.Column(db.String(120), primary_key=True)

    # The image filename to store.
    image_filename = db.Column(db.String(120))

    # The image caption to store.
    image_caption = db.Column(db.String(120))

นำเข้าคลาส Attachment ใหม่ลงในไฟล์เซิร์ฟเวอร์ที่มีเส้นทางการจัดการไฟล์แนบ

ตั้งค่าเส้นทางใหม่

เริ่มขั้นตอนในคำแนะนำทีละขั้นตอนนี้ด้วยการตั้งค่าหน้าเว็บใหม่บางหน้าในแอปพลิเคชัน ซึ่งจะช่วยให้ผู้ใช้สร้างและดูเนื้อหาผ่านส่วนเสริมของเราได้

เพิ่มเส้นทางการสร้างไฟล์แนบ

คุณต้องมีหน้าเว็บเพื่อให้ครูเลือกเนื้อหาและส่งคำขอสร้างไฟล์แนบ ใช้เส้นทาง /attachment-options เพื่อแสดงตัวเลือกเนื้อหาให้ครูเลือก นอกจากนี้ คุณยังต้องมีเทมเพลตสำหรับหน้าการเลือกเนื้อหาและหน้ายืนยันการสร้าง ตัวอย่างที่เราให้ไว้มีเทมเพลตสำหรับหน้าเหล่านี้ และยังแสดงคำขอและการตอบกลับจาก Classroom API ได้ด้วย

โปรดทราบว่าคุณสามารถแก้ไขหน้า Landing Page ของมุมมองการค้นพบไฟล์แนบที่มีอยู่เพื่อแสดงตัวเลือกเนื้อหาแทนการสร้างหน้า /attachment-options ใหม่ได้ เราขอแนะนำให้สร้างหน้าใหม่เพื่อวัตถุประสงค์ของ แบบฝึกหัดนี้ เพื่อให้คุณคงลักษณะการทำงานของ SSO ที่ใช้ในขั้นตอนที่ สอง ของคำแนะนำทีละขั้นตอนไว้ เช่น การเพิกถอนสิทธิ์ของแอป ซึ่งควรมีประโยชน์เมื่อคุณสร้างและทดสอบส่วนเสริม

ครูสามารถเลือกจากรูปภาพที่มีคำบรรยายจำนวนเล็กน้อยในตัวอย่างที่เราให้ไว้ เราได้จัดเตรียมรูปภาพสถานที่สำคัญที่มีชื่อเสียง 4 รูปที่มีคำบรรยายที่ได้มาจากชื่อไฟล์

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้จะอยู่ในไฟล์ webapp/attachment_routes.py

@app.route("/attachment-options", methods=["GET", "POST"])
def attachment_options():
    """
    Render the attachment options page from the "attachment-options.html"
    template.

    This page displays a grid of images that the user can select using
    checkboxes.
    """

    # A list of the filenames in the static/images directory.
    image_filenames = os.listdir(os.path.join(app.static_folder, "images"))

    # The image_list_form_builder method creates a form that displays a grid
    # of images, checkboxes, and captions with a Submit button. All images
    # passed in image_filenames will be shown, and the captions will be the
    # title-cased filenames.

    # The form must be built dynamically due to limitations in WTForms. The
    # image_list_form_builder method therefore also returns a list of
    # attribute names in the form, which will be used by the HTML template
    # to properly render the form.
    form, var_names = image_list_form_builder(image_filenames)

    # If the form was submitted, validate the input and create the attachments.
    if form.validate_on_submit():

        # Build a dictionary that maps image filenames to captions.
        # There will be one dictionary entry per selected item in the form.
        filename_caption_pairs = construct_filename_caption_dictionary_list(
            form)

        # Check that the user selected at least one image, then proceed to
        # make requests to the Classroom API.
        if len(filename_caption_pairs) > 0:
            return create_attachments(filename_caption_pairs)
        else:
            return flask.render_template(
                "create-attachment.html",
                message="You didn't select any images.",
                form=form,
                var_names=var_names)

    return flask.render_template(
        "attachment-options.html",
        message=("You've reached the attachment options page. "
                "Select one or more images and click 'Create Attachment'."),
        form=form,
        var_names=var_names,
    )

ซึ่งจะสร้างหน้า "สร้างไฟล์แนบ" ที่มีลักษณะดังนี้

มุมมองการเลือกเนื้อหาตัวอย่าง Python

ครูสามารถเลือกรูปภาพหลายรูปได้ สร้างไฟล์แนบ 1 ไฟล์สำหรับรูปภาพแต่ละรูปที่ครูเลือกในเมธอด create_attachments

ส่งคำขอสร้างไฟล์แนบ

ตอนนี้คุณทราบแล้วว่าครูต้องการแนบเนื้อหาส่วนใด ให้ส่งคำขอไปยัง Classroom API เพื่อสร้างไฟล์แนบในงานของเรา เก็บรายละเอียดไฟล์แนบไว้ในฐานข้อมูลหลังจากได้รับการตอบกลับจาก Classroom API

เริ่มต้นด้วยการรับอินสแตนซ์ของบริการ Classroom ดังนี้

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้จะอยู่ในไฟล์ webapp/attachment_routes.py

def create_attachments(filename_caption_pairs):
    """
    Create attachments and show an acknowledgement page.

    Args:
        filename_caption_pairs: A dictionary that maps image filenames to
            captions.
    """
    # Get the Google Classroom service.
    classroom_service = googleapiclient.discovery.build(
        serviceName="classroom",
        version="v1",
        credentials=credentials)

ส่งคำขอ CREATE ไปยังcourses.courseWork.addOnAttachments ปลายทาง สำหรับรูปภาพแต่ละรูปที่ครูเลือก ให้สร้างออบเจ็กต์ AddOnAttachment ก่อน ดังนี้

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้เป็นส่วนต่อจากเมธอด create_attachments

# Create a new attachment for each image that was selected.
attachment_count = 0
for key, value in filename_caption_pairs.items():
    attachment_count += 1

    # Create a dictionary with values for the AddOnAttachment object fields.
    attachment = {
        # Specifies the route for a teacher user.
        "teacherViewUri": {
            "uri":
                flask.url_for(
                    "load_content_attachment", _scheme='https', _external=True),
        },
        # Specifies the route for a student user.
        "studentViewUri": {
            "uri":
                flask.url_for(
                    "load_content_attachment", _scheme='https', _external=True)
        },
        # The title of the attachment.
        "title": f"Attachment {attachment_count}",
    }

คุณต้องระบุฟิลด์ teacherViewUri, studentViewUri และ title อย่างน้อย 1 ฟิลด์สำหรับไฟล์แนบแต่ละไฟล์ teacherViewUri และ studentViewUri แสดง URL ที่โหลดเมื่อผู้ใช้ประเภทนั้นๆ เปิดไฟล์แนบ

ส่งออบเจ็กต์ AddOnAttachment ในเนื้อหาของคำขอไปยังปลายทาง addOnAttachments ที่เหมาะสม ระบุตัวระบุ courseId, itemId, itemType และ addOnToken พร้อมกับคำขอแต่ละรายการ

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้เป็นส่วนต่อจากเมธอด create_attachments

# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
    case "announcements":
        parent = classroom_service.courses().announcements()
    case "courseWorkMaterials":
        parent = classroom_service.courses().courseWorkMaterials()
    case _:
        parent = classroom_service.courses().courseWork()

# Issue a request to create the attachment.
resp = parent.addOnAttachments().create(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"],
    addOnToken=flask.session["addOnToken"],
    body=attachment).execute()

สร้างรายการสำหรับไฟล์แนบนี้ในฐานข้อมูลในเครื่องเพื่อให้คุณโหลดเนื้อหาที่ถูกต้องได้ในภายหลัง Classroom จะแสดงค่า id ที่ไม่ซ้ำกันในการตอบกลับคำขอสร้าง ดังนั้นให้ใช้ค่านี้เป็นคีย์หลักในฐานข้อมูล โปรดทราบด้วยว่า Classroom จะส่งพารามิเตอร์การค้นหา attachmentId เมื่อเปิดมุมมองของครูและมุมมองของนักเรียน ดังนี้

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้เป็นส่วนต่อจากเมธอด create_attachments

# Store the value by id.
new_attachment = Attachment(
    # The new attachment's unique ID, returned in the CREATE response.
    attachment_id=resp.get("id"),
    image_filename=key,
    image_caption=value)
db.session.add(new_attachment)
db.session.commit()

พิจารณาเปลี่ยนเส้นทางผู้ใช้ไปยังหน้ายืนยันในตอนนี้ เพื่อแจ้งให้ทราบว่าผู้ใช้สร้างไฟล์แนบเรียบร้อยแล้ว

อนุญาตไฟล์แนบจากส่วนเสริม

ตอนนี้เป็นเวลาที่เหมาะสมในการเพิ่มที่อยู่ที่เหมาะสมลงในช่องคำนำหน้า URI ของไฟล์แนบที่อนุญาตในหน้าการกำหนดค่าแอปของ Google Workspace Marketplace SDK ส่วนเสริมจะสร้างไฟล์แนบได้จากคำนำหน้า URI รายการใดรายการหนึ่งที่แสดงอยู่ในหน้านี้เท่านั้น ซึ่งเป็นมาตรการรักษาความปลอดภัยเพื่อช่วยลดโอกาสในการโจมตีแบบแทรกกลางของบุคคลภายนอก (MITM)

วิธีที่ง่ายที่สุดคือการระบุโดเมนระดับบนสุดในช่องนี้ เช่น https://example.com https://localhost:<your port number>/ จะ ใช้ได้หากคุณใช้เครื่องในเครื่องเป็นเว็บเซิร์ฟเวอร์

เพิ่มเส้นทางสำหรับมุมมองของครูและมุมมองของนักเรียน

มี iframe 4 รายการที่อาจโหลดส่วนเสริมของ Google Classroom จนถึงตอนนี้คุณได้สร้างเส้นทางที่แสดง iframe ของมุมมองการค้นพบไฟล์แนบเท่านั้น จากนั้นให้เพิ่มเส้นทางเพื่อแสดง iframe ของมุมมองของครูและมุมมองของนักเรียนด้วย

iframe ของมุมมองของครู จำเป็นต้องแสดงตัวอย่างประสบการณ์การใช้งานของนักเรียน แต่สามารถรวมข้อมูลเพิ่มเติมหรือฟีเจอร์การแก้ไขได้โดยไม่บังคับ

มุมมองของนักเรียน คือหน้าที่แสดงต่อนักเรียนแต่ละคนเมื่อเปิดไฟล์แนบของส่วนเสริม

สำหรับวัตถุประสงค์ในการทำแบบฝึกหัดนี้ ให้สร้างเส้นทาง /load-content-attachment เส้นทางเดียวที่แสดงทั้งมุมมองของครูและมุมมองของนักเรียน ใช้วิธีการของ Classroom API เพื่อพิจารณาว่าผู้ใช้เป็นครูหรือนักเรียนเมื่อโหลดหน้าเว็บ

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้จะอยู่ในไฟล์ webapp/attachment_routes.py

@app.route("/load-content-attachment")
def load_content_attachment():
    """
    Load the attachment for the user's role."""

    # Since this is a landing page for the Teacher and Student View iframes, we
    # need to preserve the incoming query parameters.
    if flask.request.args.get("itemId"):
        flask.session["itemId"] = flask.request.args.get("itemId")
    if flask.request.args.get("itemType"):
        flask.session["itemType"] = flask.request.args.get("itemType")
    if flask.request.args.get("courseId"):
        flask.session["courseId"] = flask.request.args.get("courseId")
    if flask.request.args.get("attachmentId"):
        flask.session["attachmentId"] = flask.request.args.get("attachmentId")

โปรดทราบว่าคุณควรตรวจสอบสิทธิ์ผู้ใช้ในตอนนี้ด้วย นอกจากนี้ คุณควรจัดการพารามิเตอร์การค้นหา login_hint ที่นี่ และเปลี่ยนเส้นทางผู้ใช้ไปยังโฟลว์การให้สิทธิ์หากจำเป็น ดูรายละเอียดคำแนะนำในการเข้าสู่ระบบที่กล่าวถึง ในคำแนะนำทีละขั้นตอนก่อนหน้าเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับโฟลว์นี้

จากนั้นส่งคำขอไปยังปลายทาง getAddOnContext ที่ตรงกับประเภทรายการ

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้เป็นส่วนต่อจากเมธอด load_content_attachment

# Create an instance of the Classroom service.
classroom_service = googleapiclient.discovery.build(
    serviceName="classroom"
    version="v1",
    credentials=credentials)

# Use the itemType to determine which stream item type the teacher created
match flask.session["itemType"]:
    case "announcements":
        parent = classroom_service.courses().announcements()
    case "courseWorkMaterials":
        parent = classroom_service.courses().courseWorkMaterials()
    case _:
        parent = classroom_service.courses().courseWork()

addon_context_response = parent.getAddOnContext(
    courseId=flask.session["courseId"],
    itemId=flask.session["itemId"]).execute()

เมธอดนี้จะแสดงข้อมูลเกี่ยวกับบทบาทของผู้ใช้ปัจจุบันในชั้นเรียน เปลี่ยนมุมมองที่แสดงต่อผู้ใช้ตามบทบาทของผู้ใช้ ระบบจะป้อนข้อมูลในฟิลด์ studentContext หรือ teacherContext เพียงฟิลด์เดียวในออบเจ็กต์การตอบกลับ ตรวจสอบฟิลด์เหล่านี้เพื่อพิจารณาว่าจะกล่าวถึงผู้ใช้อย่างไร

ไม่ว่าในกรณีใด ให้ใช้ค่าพารามิเตอร์การค้นหา attachmentId เพื่อทราบว่าควรดึงข้อมูลไฟล์แนบใดจากฐานข้อมูล ระบบจะระบุพารามิเตอร์การค้นหานี้เมื่อเปิด URI ของมุมมองของครูหรือมุมมองของนักเรียน

Python

ในตัวอย่างที่เราให้ไว้ โค้ดนี้เป็นส่วนต่อจากเมธอด load_content_attachment

# Determine which view we are in by testing the returned context type.
user_context = "student" if addon_context_response.get(
    "studentContext") else "teacher"

# Look up the attachment in the database.
attachment = Attachment.query.get(flask.session["attachmentId"])

# Set the text for the next page depending on the user's role.
message_str = f"I see that you're a {user_context}! "
message_str += (
    f"I've loaded the attachment with ID {attachment.attachment_id}. "
    if user_context == "teacher" else
    "Please enjoy this image of a famous landmark!")

# Show the content with the customized message text.
return flask.render_template(
    "show-content-attachment.html",
    message=message_str,
    image_filename=attachment.image_filename,
    image_caption=attachment.image_caption,
    responses=response_strings)

ทดสอบส่วนเสริม

ทำตามขั้นตอนต่อไปนี้เพื่อทดสอบการสร้างไฟล์แนบ

  • ลงชื่อเข้าใช้ [Google Classroom] ในฐานะผู้ใช้ทดสอบ ครู รายใดรายหนึ่ง
  • ไปที่แท็บงานของชั้นเรียน แล้วสร้างงาน ใหม่
  • คลิกปุ่มส่วนเสริม ใต้พื้นที่ข้อความ แล้วเลือกส่วนเสริม iframe จะเปิดขึ้นและส่วนเสริมจะโหลด URI การตั้งค่าไฟล์แนบ ที่คุณ ระบุไว้ในหน้า การกำหนดค่าแอป ของ Google Workspace Marketplace SDK
  • เลือกเนื้อหาที่จะแนบกับงาน
  • ปิด iframe หลังจากโฟลว์การสร้างไฟล์แนบเสร็จสมบูรณ์

คุณควรเห็นการ์ดไฟล์แนบปรากฏขึ้นใน UI การสร้างงานใน Google Google Classroom คลิกการ์ดเพื่อเปิด iframe ของมุมมองของครูและยืนยันว่าไฟล์แนบที่ถูกต้องปรากฏขึ้น คลิกปุ่มมอบหมาย

ทำตามขั้นตอนต่อไปนี้เพื่อทดสอบประสบการณ์การใช้งานของนักเรียน

  • จากนั้นลงชื่อเข้าใช้ Classroom ในฐานะผู้ใช้ทดสอบนักเรียนในชั้นเรียนเดียวกับผู้ใช้ทดสอบครู
  • ค้นหางานทดสอบในแท็บงานของชั้นเรียน
  • ขยายงานแล้วคลิกการ์ดไฟล์แนบเพื่อเปิด iframe ของมุมมองของนักเรียน

ยืนยันว่าไฟล์แนบที่ถูกต้องปรากฏขึ้นสำหรับนักเรียน

ยินดีด้วย คุณพร้อมที่จะไปยังขั้นตอนถัดไปแล้ว นั่นคือการสร้างไฟล์แนบประเภทกิจกรรม