일정 페이지

  • 릴리스 버전: Yokohama
  • 업데이트 날짜 2025년 01월 30일
  • 읽기16분
  • 일정 페이지는 달력 또는 타임라인 표시의 사용자 지정 생성을 허용하는 스크립트 모음이 포함된 기록입니다.

    타임라인 일정 페이지를 작성하려면 페이지/이벤트 플로우에 대한 이해와 클라이언트 및 서버 측 JavaScript를 작성할 수 있어야 합니다.

    일정 페이지 양식

    일정 페이지에 액세스하려면 다음으로 이동하십시오. 시스템 스케줄러 > 일정 > 일정 페이지.

    이 양식은 선택한 뷰 유형에 따라 다음 필드를 제공합니다.
    표 1. 일정 페이지 양식
    필드 필드 유형 설명
    이름 문자열 현재 일정 페이지를 식별하는 데 사용되는 일반 이름입니다.
    일정 유형 문자열 일정 유형은 "sysparm_page_schedule_type" URI 매개변수를 통해 일정 페이지를 고유하게 식별하는 데 사용되는 문자열입니다. 예를 들어 다음과 같이 일정 페이지에 액세스할 수 있습니다.

    /show_schedule_page.do?sysparm_type=gantt_chart&sysparm_timeline_task_id=d530bf907f0000015ce594fd929cf6a4

    또는 "sysparm_page_sys_id" URI 매개변수를 일정 페이지의 고유한 32자 16진수 시스템 식별자로 설정하여 일정 페이지에 액세스할 수도 있습니다.

    뷰 유형 선택 각 뷰 유형은 서로 다른 필드 조합을 표시합니다. 다음 두 가지 옵션을 사용할 수 있습니다.
    • 달력
    • 타임라인
    설명 문자열 현재 일정 페이지에 대한 추가 정보를 제공하는 일반 설명입니다. 이 필드는 필요하지 않습니다.
    초기화 함수 이름 문자열
    주:
    이 기능은 달력 유형 일정 페이지에서만 사용됩니다.
    init 함수 이름은 달력 유형 일정 페이지에 대한 클라이언트 스크립트 함수 내에서 호출할 JavaScript 함수의 이름을 지정합니다.
    HTML 문자열
    주:
    이 기능은 달력 유형 일정 페이지에서만 사용됩니다.
    HTML 필드는 Jelly에 의해 구문 분석되고 달력의 나머지 부분보다 먼저 표시 페이지에 삽입되는 스크립트 가능 섹션입니다. 서버에서 변수를 전달하고 필요한 추가 필드를 정의하는 데 사용할 수 있습니다.
    클라이언트 스크립트 문자열 클라이언트 스크립트는 일정 페이지 표시 옵션을 구성할 수 있는 스크립팅 가능한 섹션입니다. API는 일정 페이지 뷰 유형에 따라 다르며 아래에서 설명합니다.
    서버 AJAX 프로세서 문자열
    주:
    이 기능은 달력 유형 일정 페이지에서만 사용됩니다.
    서버 AJAX 프로세서는 표시할 일정 항목 및 범위 세트를 반환하는 데 사용되는 달력 유형 일정 페이지에만 해당됩니다.

    타임라인 일정 페이지

    타임라인 일정 페이지는 시간 기반 포인트와 범위를 "타임라인"과 같은 방식으로 표시하기 위한 구성 정보를 포함하는 특정 기록입니다.

    타임라인 일정 페이지는 AbstractTimelineSchedulePage에서 확장되는 스크립트 포함을 참조하여 다양한 이벤트 및 조건에 따라 타임라인을 동적으로 수정합니다. 일정 페이지와 타임라인 생성을 위한 스크립트 포함은 모두 극단적인 사용자 지정이 가능하며 해당 API(애플리케이션 프로그래밍 인터페이스)는 아래에 문서화되어 있습니다.

    다음 다이어그램은 타임라인 일정 페이지에 액세스할 때 발생하는 일련의 이벤트를 보여줍니다. 타임라인이 로드되면 타임라인 상호작용(예: 타임라인 범위 이동)으로 인한 이벤트와 같은 모든 후속 이벤트는 회색 이벤트 상자에 표시된 것과 동일한 로직 흐름을 따릅니다.
    그림 1. 타임라인 플로우

    일정 페이지를 사용하여 타임라인을 생성하는 애플리케이션

    • 프로젝트 관리
    • 유지관리 일정
    • 그룹 당직 순환
    • 현장 서비스 관리

    타임라인 일정 페이지 예

    다음 예시에서는 위에서 설명한 대부분의 API를 활용하여 해당 스크립트 포함으로 타임라인 일정 페이지를 생성하는 방법을 보여줍니다.

    이 예에서는 프로젝트 지원 관리자가 새로운 인시던트를 모두 시각화할 수 있도록 인시던트 요약 타임라인을 생성하겠습니다. 모든 새 인시던트는 다른 포인트 아이콘으로 인시던트의 우선순위가 구분되는 단일 포인트로 표시되어야 합니다. 또한 종결된 모든 인시던트는 종결되기 전의 인시던트 기간을 보여주는 별도의 그룹으로 타임라인에 표시되어야 합니다. 프로젝트 관리자는 양식 목록을 사용하지 않고 해결된 새 항목을 쉽게 종결할 수 있기를 원하므로 새 인시던트를 종결된 인시던트 그룹 또는 그 안의 항목으로 끌어올 수 있도록 수직 이동 이벤트를 처리하겠습니다.

    일정 페이지

    다음 속성으로 새 일정 페이지를 생성합니다.
    • 이름: 하드웨어 인시던트
    • 일정 유형: incident_timeline
    • 뷰 유형: 타임라인
    • 클라이언트 스크립트:
    // Set our page configuration
    glideTimeline.setReadOnly(false);
    glideTimeline.showLeftPane(true);
    glideTimeline.showLeftPaneAsTree(true);
    glideTimeline.showTimelineText(true);
    glideTimeline.showDependencyLines(false);
    glideTimeline.groupByParent(true);
    glideTimeline.setDefaultPointIconClass('milestone');
     
    // We will define what items to display and provide a custom event handler for moving new items to the closed state
    glideTimeline.registerEvent('getItems', 'IncidentTimelineScriptInclude');
    glideTimeline.registerEvent('elementMoveY', 'IncidentTimelineScriptInclude');

    스크립트 포함

    이제 일정 페이지가 생성되었으므로 등록된 이벤트에 대해 일치하는 스크립트 포함을 생성해야 합니다. 다음 속성을 사용하여 새 스크립트 포함을 만듭니다.
    • 이름: IncidentTimelineScriptInclude
    • 활성: 선택됨
    • 클라이언트 호출 가능: 선택됨
    • 스크립트:
    // Class Imports
     
    var IncidentTimelineScriptInclude = Class.create();
    IncidentTimelineScriptInclude.prototype = Object.extendsObject(AbstractTimelineSchedulePage, {
     
      /////////////////////// // GET_ITEMS ///////////////////////////////////////
      getItems:function() { 
        // Specify the page title 
        this.setPageTitle('My Custom Incident Summary Timeline');
     
        var groupNew = new GlideTimelineItem('new');
        groupNew.setLeftLabelText('New Incidents');
        groupNew.setImage('../images/icons/all.gifx');
        groupNew.setTextBold(true);
        this.add(groupNew);
     
        var groupClosed = new GlideTimelineItem('closed');
        groupClosed.setLeftLabelText('Closed Incidents');
        groupClosed.setImage('../images/icons/all.gifx');
        groupClosed.setTextBold(true);
        groupClosed.setIsDroppable(true);
     
        // This allows us to drag an open incident onto the closed group row. 
        this.add(groupClosed);
     
        // Get all the incidents and let's add only the new/closed ones appropriately 
        var now_GR = new GlideRecord('incident');
        gr.query(); 
        while(gr.next()) { 
           // Only loop through new/closed incidents 
           if(gr.incident_state != '1' && gr.incident_state != '7') continue;
     
           // Ok, we have a new/closed incident. Create the item and the span first. 
           var item = new GlideTimelineItem(gr.getTableName(), gr.sys_id); 
           var span = item.createTimelineSpan(gr.getTableName(), gr.sys_id);
     
           // Specific properties for a new incident 
           if(gr.incident_state == '1') { // New 
             item.setParent(groupNew.getSysId()); 
             item.setImage('../images/icons/open.gifx');
             span.setTimeSpan(gr.getElement('opened_at').getGlideObject().getNumericValue(),
                              gr.getElement('opened_at').getGlideObject().getNumericValue());
     
             // We will show different colors based upon the priorities only for new incidents 
             switch(gr.getElement('priority').toString()) {
               case '1': span.setPointIconClass('red_circle'); break; 
               case '2': span.setPointIconClass('red_square'); break; 
               case '3': span.setPointIconClass('blue_circle'); break; 
               case '4': span.setPointIconClass('blue_square'); break; 
               case '5': span.setPointIconClass('sepia_circle'); break; 
               default: // Otherwise, the default point icon class will be used (Milestone)
              }
             }
            // Specific properties for a closed incident 
            else if(gr.incident_state == '7') { 
              item.setParent(groupClosed.getSysId()); 
              item.setImage('../images/icons/closed.gifx');
              span.setTimeSpan(gr.getElement('opened_at').getGlideObject().getNumericValue(),
                               gr.getElement('closed_at').getGlideObject().getNumericValue()); }
     
            // Common item properties 
            item.setLeftLabelText(gr.short_description);
     
            // Common span properties
            span.setSpanText(gr.short_description);
            span.setTooltip('<strong>' + GlideStringUtil.escapeHTML(gr.short_description) + '</strong><br>' + gr.number);
            span.setAllowXMove(false);
            span.setAllowYMove(gr.canWrite() ? true:false);
            span.setAllowYMovePredecessor(false);
            span.setAllowXDragLeft(false);
            span.setAllowXDragRight(false);
     
            // Now we add the actual item 
            this.add(item); 
            } } ,
     
     
       //////////////////////// // ELEMENT_MOVE_Y /////////////////////////////////////////////////////////////
     
       /**
       * This is one of the AbstractTimelineSchedulePage event handler methods that corresponds to a vertical
       * move. The arguments for this function are defined in the API section of the event handler methods.
       */
      elementMoveY: function(spanId, itemId, newItemId) {
     
        // Get information about the current incident 
        var now_GR = new GlideRecord('incident');
        gr.addQuery('sys_id', spanId);
        gr.query(); 
        if(!gr.next()) 
          return this.setStatusError('Error', 'Unable to lookup the current incident.');
     
        // Only allow the new incidents to have their state adjusted. 
        if(gr.incident_state != '1') 
          return this.setStatusError('Error','Only new incidents can have their state adjusted.');
     
        // Get information about the dropped GlideTimelineItem. If it was dropped in an item on the "New Incidents" 
        // group let's do nothing. If it was dropped in the "Closed Incidents" then let's adjust the state automatically. 
        var grDropped = new GlideRecord('incident');
        grDropped.addQuery('sys_id', newItemId );
        grDropped.query(); 
        if(!grDropped.next() || grDropped.incident_state == '7') { 
           // This means the dropped item was either the 'Closed Incidents' group (which has no record or sys_id) or an 
           // existing incident that is closed. The 'New Incidents' also has no sys_id; however, the default behavior for 
           // items without a sysId is to be non-droppable. This is why we explicitly denoted the 'Closed Incidents' to  
           // be marked as "droppable".
     
           // Return a dialog prompt 
           this.setStatusPrompt('Confirm', 'Are you sure you want to close: ' + 
                  '<div style="margin:10px 0 10px 14px;padding:4px;background-color:#EBEBEB;"><strong>' +
                   GlideStringUtil.escapeHTML(gr.short_description) + 
                   '</strong><br/><div class="font_smaller">' + now_GR. number + '</div></div>', 
                   'this._elementMoveYHandler_DoClose', // This function is for when the OK button is clicked. 
                   'this._elementMoveYHandler_DoNothing', // This function is for when the Cancel button is clicked. 
                   'this._elementMoveYHandler_DoNothing'); // This function is for when the Close button is clicked.
           } } ,
     
      _elementMoveYHandler_DoClose: function(spanId, itemId, newItemId) { 
        // Notice that this function takes the same function arguments as the original function for which it  
        // is a custom event handler for.
     
        // Update the database record from 'New' to 'Closed'. 
        var now_GR = new GlideRecord('incident');
        gr.addQuery('sys_id', spanId);
        gr.query();
        gr.next();
        gr.setValue('incident_state', '7');
        gr.update();
     
        // This will re-render the timeline showing the updated item in the closed group. 
        this.setDoReRenderTimeline(true);
     
        // Let's show a success message 
        this.setStatusSuccess('Success', '<strong>' + gr.short_description + '</strong> was successfully closed.'); } ,
     
      // Since the user clicked cancel or close we simply do nothing.
      _elementMoveYHandler_DoNothing: function(spanId, itemId, newItemId) { }
     
     });

    스크린샷 / 결과

    1. 다음으로 이동합니다.

      http://[인스턴스]:8080/show_schedule_page.do?sysparm_page_schedule_type=incident_timeline

      굵은 텍스트는 일정 페이지 일정 유형 필드의 값입니다.

    2. 이 페이지에는 일정 페이지에서 지정한 타임라인과 생성된 스크립트 포함이 표시됩니다. 필요에 따라 이 페이지에 대한 링크를 모듈 또는 UI 작업으로 만들어 배치할 수 있습니다.
      그림 2. 타임라인 예시 인시던트 미리 보기
    3. 종결된 인시던트를 어디로든 옮기려고 하면 예상되는 오류 메시지가 표시됩니다.
      그림 3. 타임라인 예 이동 오류
    4. 인시던트 이동: 메모리가 더 필요합니다 .에는 다음 확인 상자가 표시됩니다.
      그림 4. 타임라인 예 닫기 확인
    5. 취소 버튼을 클릭하면 오버레이가 닫힙니다. 확인 단추를 클릭하면 실제로 기록의 incident_state 업데이트되고 다음 성공 상자가 표시됩니다.
      그림 5. 타임라인 예 종결 성공
    6. 확인을 클릭하면 인시던트가 종결된 인시던트 그룹에 표시됩니다.
      그림 6. 타임라인 예제 인시던트 업데이트됨
      예제 인시던트의 타임라인