CREATE OR REPLACE PACKAGE mip_files IS

  -- Author  : PRIESTJ
  -- Created : 02/11/2007 10:59:52
  -- Purpose : Handle files for the webMIP system

  -- Public function and procedure declarations
  FUNCTION quote_file_exists(p_qute_id NUMBER) RETURN BOOLEAN;
  FUNCTION delete_file(p_id         NUMBER
                      ,p_doc_status VARCHAR2
                      ,p_reason     VARCHAR2) RETURN BOOLEAN;
  FUNCTION drawing_file_exists(p_drwg_code VARCHAR2) RETURN BOOLEAN;
  FUNCTION update_file_association(p_uri         IN VARCHAR2
                                  ,p_description IN VARCHAR2
                                  ,p_docu_type   IN VARCHAR2
                                  ,p_qute_id     IN NUMBER
                                  ,p_enqu_id     IN NUMBER
                                  ,p_drwg_code   IN VARCHAR2) RETURN BOOLEAN;
  FUNCTION set_file_association(p_uri         IN VARCHAR2
                               ,p_description IN VARCHAR2
                               ,p_docu_type   IN VARCHAR2
                               ,p_rt_code     IN VARCHAR2
                               ,p_qute_id     IN NUMBER
                               ,p_enqu_id     IN NUMBER
                               ,p_drwg_code   IN VARCHAR2
                               ,p_doro_type   IN VARCHAR2) RETURN BOOLEAN;

  PROCEDURE export_table_to_csv(p_table          IN VARCHAR2
                               ,p_column_headers IN VARCHAR2
                               ,p_delimiter      IN VARCHAR2 DEFAULT ',');

END mip_files;
/
CREATE OR REPLACE PACKAGE BODY mip_files AS

  PROCEDURE pl(p_in   VARCHAR2
              ,p_line NUMBER DEFAULT NULL) IS
  BEGIN
    NULL;
    --  $IF $$DEBUGGING OR mip_debug_constants.debugging OR mip_debug_constants.files 
    --  $THEN
    mip_debug.pl(p_unit => $$PLSQL_UNIT
                ,p_in   => p_in
                ,p_line => p_line);
    --  $END
  END pl;

  /*
   FUNCTION is_file_over_size_limit
   - takes the uri of the file to test for size and returns true if the file
   - is over 3 meg (3145728 bytes) in size
   %param p_uri - the uri of the file
  */
  FUNCTION is_file_over_size_limit(p_uri VARCHAR2) RETURN BOOLEAN IS
    l_file_size wwv_flow_files.doc_size%TYPE;
  
  BEGIN
  
    pl('is_file_over_size_limit:entry:' || p_uri);
    SELECT doc_size
      INTO l_file_size
      FROM wwv_flow_files
     WHERE NAME = p_uri;
    pl('is_file_over_size_limit:exit:' || l_file_size);
    RETURN(l_file_size > 3 * 1024 * 1024);
  
  END is_file_over_size_limit;
  /*
   FUNCTION are_files_over_size_limit
   - takes the enquiry id and an additional uri of the file to test for size
   - and returns true if all the current files and plus additional file pointed
   - by the uri are over 10 meg (10485760 bytes) in size
   %param p_enqu_id - id of the enquiry to check the files for
  */
  FUNCTION are_files_over_size_limit(p_enqu_id NUMBER) RETURN BOOLEAN IS
    l_total_size NUMBER;
  BEGIN
    pl('are_files_over_size_limit:' || p_enqu_id);
    SELECT SUM(doc_size)
      INTO l_total_size
      FROM documents      d
          ,document_roles dr
          ,wwv_flow_files f
     WHERE dr.enqu_id = p_enqu_id
       AND d.id = dr.docu_id
       AND d.uri = f.NAME;
  
    pl('l_total_size:' || l_total_size);
    RETURN(nvl(l_total_size
              ,0) > 10 * 1024 * 1024);
  
  END are_files_over_size_limit;
  /*
   FUNCTION delete_file
   - takes the id of a file held in the documents table and deletes the associated document
   - in the documents, document_roles and wwv_flow_files tables and view
   %param p_id - the id of the file
   %param p_doc_status - the new status of the document one of "REMOVED USER", "REMOVED MALICIOUS", "REMOVED SIZE"
   %param p_reason - a description of what the reason for deleting the file is
  */
  FUNCTION delete_file(p_id         NUMBER
                      ,p_doc_status VARCHAR2
                      ,p_reason     VARCHAR2) RETURN BOOLEAN IS
    l_uri documents.uri%TYPE;
    CURSOR c_get_uri(cp_id NUMBER) IS
      SELECT uri
        FROM documents
       WHERE id = cp_id;
  BEGIN
    IF NOT c_get_uri%ISOPEN THEN
      OPEN c_get_uri(p_id);
    END IF;
    FETCH c_get_uri
      INTO l_uri;
    CLOSE c_get_uri;
    --set up a deletion event for the document
    INSERT INTO document_events
      (docu_id
      ,dost_code
      ,event_date
      ,description
      ,id)
    VALUES
      (p_id
      ,p_doc_status
      ,SYSDATE
      ,p_reason
      ,doev_seq.NEXTVAL);
    --delete the actual file from the database
    DELETE wwv_flow_files
     WHERE wwv_flow_files.NAME = l_uri;
    RETURN TRUE;
  END delete_file;
  /*
   FUNCTION quote_file_exists
   - takes the quote id and searches for associated file in APEX Application
   - Files, returns true if the file exists or false if it does not exist
   %param p_qute_id - the quote id
  */
  FUNCTION quote_file_exists(p_qute_id NUMBER) RETURN BOOLEAN IS
    l_file_exists NUMBER;
    CURSOR c_file_exists(cp_qute_id NUMBER) IS
      SELECT 1
        FROM wwv_flow_files
       WHERE NAME = (SELECT d.uri
                       FROM documents      d
                           ,document_roles dr
                      WHERE dr.qute_id = cp_qute_id
                        AND d.id = dr.docu_id);
  BEGIN
    IF NOT c_file_exists%ISOPEN THEN
      OPEN c_file_exists(p_qute_id);
    END IF;
    FETCH c_file_exists
      INTO l_file_exists;
    CLOSE c_file_exists;
    IF l_file_exists IS NOT NULL
       AND l_file_exists > 0 THEN
      RETURN TRUE;
    ELSE
      RETURN FALSE;
    END IF;
  END quote_file_exists;
  /*
   FUNCTION drawing_file_exists
   - takes the drawing code and searches for associated file in APEX Application
   - Files, returns true if the file exists or false if it does not exist
   %param p_drwg_code - the drawing code
  */
  FUNCTION drawing_file_exists(p_drwg_code VARCHAR2) RETURN BOOLEAN IS
    l_file_exists NUMBER;
    CURSOR c_file_exists(cp_drwg_code VARCHAR2) IS
      SELECT 1
        FROM wwv_flow_files
       WHERE NAME = (SELECT d.uri
                       FROM documents      d
                           ,document_roles dr
                      WHERE dr.drwg_code = cp_drwg_code
                        AND d.id = dr.docu_id);
  BEGIN
    IF NOT c_file_exists%ISOPEN THEN
      OPEN c_file_exists(p_drwg_code);
    END IF;
    FETCH c_file_exists
      INTO l_file_exists;
    CLOSE c_file_exists;
    IF l_file_exists IS NOT NULL
       AND l_file_exists > 0 THEN
      RETURN TRUE;
    ELSE
      RETURN FALSE;
    END IF;
  END drawing_file_exists;
  /*
   FUNCTION update_file_association
   - updates the association between a object within webmip to a file held in the
   - APEX application files view and ophans any existing files.
   %param p_uri - the uri of the file (the contents of the name column in APEX application files)
   %param p_description - description of the file, will be set in documents and document_roles table
   %param p_docu_type - INDO (held within webmip db), EXDO (held outside webmip), FIDO (on file system)
   %param p_rt_code - type of role for docment eg 'ENQUIRY SUPPORTING DOC' see role types table
   %param p_qute_id - the quote id if applicable otherwise supply null
   %param p_enqu_id - the enquiry id if applicable otherwise supply null
   %param p_drwg_code - the drawing code if applicable otherwise supply null
  */
  FUNCTION update_file_association(p_uri         IN VARCHAR2
                                  ,p_description IN VARCHAR2
                                  ,p_docu_type   IN VARCHAR2
                                  ,p_qute_id     IN NUMBER
                                  ,p_enqu_id     IN NUMBER
                                  ,p_drwg_code   IN VARCHAR2) RETURN BOOLEAN IS
    l_doc_id NUMBER;
  
  BEGIN
    --reference it in the documents table
    INSERT INTO documents
      (uri
      ,description
      ,id
      ,docu_type)
    VALUES
      (p_uri
      ,p_description
      ,docu_seq.NEXTVAL
      ,p_docu_type)
    RETURNING id INTO l_doc_id;
    --set up a role for the document
    UPDATE document_roles
       SET docu_id = l_doc_id
     WHERE drwg_code = nvl(p_drwg_code
                          ,'')
        OR qute_id = nvl(p_qute_id
                        ,0)
        OR enqu_id = nvl(p_enqu_id
                        ,0);
    --set up an event for the document
    INSERT INTO document_events
      (docu_id
      ,dost_code
      ,event_date
      ,description
      ,id)
    VALUES
      (l_doc_id
      ,'AWAIT SCAN'
      ,SYSDATE
      ,p_description
      ,doev_seq.NEXTVAL);
    --if it works then return true
    RETURN TRUE;
  END update_file_association;
  /*
   FUNCTION set_file_association
   - sets the association between a object within webmip to a file held in the
   - APEX application files view.
   %param p_uri - the uri of the file (the contents of the name column in APEX application files)
   %param p_description - description of the file, will be set in documents and document_roles table
   %param p_docu_type - INDO (held within webmip db), EXDO (held outside webmip), FIDO (on file system)
   %param p_rt_code - type of role for docment eg 'ENQUIRY SUPPORTING DOC' see role types table
   %param p_qute_id - the quote id if applicable otherwise supply null
   %param p_enqu_id - the enquiry id if applicable otherwise supply null
   %param p_drwg_code - the drawing code if applicable otherwise supply null
   %param p_doro_type - DRRO (for a drawing), ENDO (for an enquiry), QUDO (for a quote)
  */
  FUNCTION set_file_association(p_uri         IN VARCHAR2
                               ,p_description IN VARCHAR2
                               ,p_docu_type   IN VARCHAR2
                               ,p_rt_code     IN VARCHAR2
                               ,p_qute_id     IN NUMBER
                               ,p_enqu_id     IN NUMBER
                               ,p_drwg_code   IN VARCHAR2
                               ,p_doro_type   IN VARCHAR2) RETURN BOOLEAN IS
    l_doc_id  NUMBER;
    l_success BOOLEAN;
  BEGIN
    pl('set_file_association:entry:' || p_uri || ':' || p_docu_type || ':' ||
       p_doro_type);
    --reference it in the documents table
    INSERT INTO documents
      (uri
      ,description
      ,id
      ,docu_type)
    VALUES
      (p_uri
      ,p_description
      ,docu_seq.NEXTVAL
      ,p_docu_type)
    RETURNING id INTO l_doc_id;
    --set up a role for the document
    INSERT INTO document_roles
      (rt_code
      ,start_date
      ,description
      ,qute_id
      ,enqu_id
      ,drwg_code
      ,id
      ,doro_type
      ,docu_id)
    VALUES
      (p_rt_code
      ,SYSDATE
      ,p_description
      ,p_qute_id
      ,p_enqu_id
      ,p_drwg_code
      ,doro_seq.NEXTVAL
      ,p_doro_type
      ,l_doc_id);
    --set up an event for the document
    INSERT INTO document_events
      (docu_id
      ,dost_code
      ,event_date
      ,description
      ,id)
    VALUES
      (l_doc_id
      ,'AWAIT SCAN'
      ,SYSDATE
      ,p_description
      ,doev_seq.NEXTVAL);
  
    IF p_rt_code = 'ENQUIRY SUPPORTING DOC'
       AND NOT p_enqu_id IS NULL THEN
      IF (are_files_over_size_limit(p_enqu_id) OR
         is_file_over_size_limit(p_uri)) THEN
        l_success := delete_file(l_doc_id
                                ,'REMOVED SIZE'
                                ,'webMIP determined the file or files to be too big');
        pl('set_file_association:exit:false');
        RETURN FALSE;
      END IF;
    END IF;
  
    --if it works then return true
    pl('set_file_association:exit:true');
    RETURN TRUE;
  END set_file_association;
  /*
   PROCEDURE export_data_to_csv
   - allows an admin user to create a CSV export of a table in webMIP.
   - This function will be used to extract data from the online webMIP system
   - and pull the data into Metering I&C's backend systems.
   %param p_table - the name of the table we're to export
   %param p_column_headers - tables column names be exported as a header
   %param p_delimiter - what is the delimiter value is for each field (default ',')
  */
  PROCEDURE export_table_to_csv(p_table          IN VARCHAR2
                               ,p_column_headers IN VARCHAR2
                               ,p_delimiter      IN VARCHAR2 DEFAULT ',') IS
    l_cursor   INTEGER DEFAULT dbms_sql.open_cursor; --a handle for the recordset we can then loop through
    l_status      INTEGER;
    l_column_value VARCHAR2(2000);
    l_first_row   BOOLEAN := TRUE;
    l_column_count      NUMBER;
    l_separator   VARCHAR2(10);
    l_headers     BOOLEAN := upper(p_column_headers) = 'YES';
    l_tab_column_descriptions      dbms_sql.desc_tab;
  BEGIN
    pl('export_table_to_csv:entry:' || p_table
      ,$$PLSQL_LINE);
    
    dbms_sql.parse(l_cursor
                  ,'select * from ' || p_table
                  ,dbms_sql.native);
  
    dbms_sql.describe_columns(l_cursor
                             ,l_column_count
                             ,l_tab_column_descriptions);
  
    FOR l_idx IN 1 .. l_column_count LOOP
      BEGIN
        dbms_sql.define_column(l_cursor
                              ,l_idx
                              ,l_column_value
                              ,2000);
      EXCEPTION
        WHEN OTHERS THEN
          IF (SQLCODE = -1007) THEN
            EXIT;
          ELSE
            RAISE;
          END IF;
      END;
    END LOOP;
    
    l_status := dbms_sql.EXECUTE(l_cursor);
  
    LOOP
      EXIT WHEN(dbms_sql.fetch_rows(l_cursor) <= 0);
      
      IF l_first_row THEN
        -- Set the header MIME type
        owa_util.mime_header('application/octet'
                            ,FALSE);
        -- Set the name of the file
        htp.p('Content-Disposition: attachment; filename="' ||
              lower(p_table) || '.csv"');
        -- Close the HTTP Header
        owa_util.http_header_close;
        -- If the user has requested that the tables column names be exported
        -- then display them
        IF l_headers THEN
          l_separator := '';
          FOR l_idx IN 1 .. l_column_count LOOP
            IF l_idx <> l_column_count THEN
              htp.prn(l_separator || '"'||l_tab_column_descriptions(l_idx).col_name||'"');
            ELSE
              htp.p(l_separator || '"'||l_tab_column_descriptions(l_idx).col_name||'"');
            END IF;
            l_separator := p_delimiter;
          END LOOP;
        END IF;
        l_first_row := FALSE;
      END IF;
      
      l_separator := '';
      FOR l_idx IN 1 .. l_column_count LOOP
        dbms_sql.column_value(l_cursor
                             ,l_idx
                             ,l_column_value);
        IF l_idx <> l_column_count THEN
          htp.prn(l_separator || '"'||l_column_value||'"');
        ELSE
          htp.p(l_separator || '"'||l_column_value||'"');
        END IF;
      
        l_separator := p_delimiter;
      END LOOP;
    END LOOP;
    dbms_sql.close_cursor(l_cursor);
  
    pl('export_table_to_csv:exit'
      ,$$PLSQL_LINE);
  EXCEPTION
    WHEN OTHERS THEN
      pl('export_table_to_csv:EXCEPTION:' || SQLERRM
        ,$$PLSQL_LINE);
      RAISE;
  END export_table_to_csv;

END mip_files;
/
