B4J Tutorial [ABMaterial]: Creating a simple UD file manager

Hi there

29-06-2017 Final Version Preview..

Here is the final version of the eLibrary based on this article below.


The article below just touches on the basic stuff and not everything else included in the video above...

Before 29-06-2017

Well, I have been looking for a file manager for a while that I could use in my ABMaterial app. This file manager just provides functionality to upload and download your files, nothing fancy...

FM.png


Some silly asumptions...

1. You know how to create an ABMApplication
2. You know how to create an ABMPage
3. You know how to create an ABMTable on your page
4. You know how to create an ABMUpload on your page.

Step 1: Create my table...

B4X:
Dim FM As ABMTable
    FM.Initialize(page, "FM", True, False, True, "tblTheme")
    FM.IsBordered = True
    FM.IsResponsive = True
    FM.IgnoreFormattingCodes = False
    FM.IsTextSelectable = True
    FM.SetHeaders(Array As String("ID", "File Name", "Path", "Date Modified", "Type", "Size", "Download"))
    FM.SetHeaderThemes(Array As String("bg", "bg", "bg", "bg", "bg", "bgr", "bgc"))
    FM.SetHeaderHeights(Array As Int(0, 0, 0, 0, 0, 0, 48))
    FM.SetColumnVisible(Array As Boolean(False, True, False, True, True, True, True))
    FM.SetColumnSortable(Array As Boolean(False, True, True, True, True, True, False))
    FM.SetColumnDataFields(Array As String("id", "filename", "filepath", "datemodified", "filetype", "filesize", ""))
    page.Cell(5,1).AddComponent(FM)
    RefreshFileManager

Step 2: Refresh the table with contents that have been uploaded. [Press Page Refresh]

B4X:
Sub RefreshFileManager
    DateTime.DateFormat = "yyyy-MM-dd HH:mm"
    Dim nFiles As List
    nFiles.Initialize
    Dim uploads As String
    uploads = "uploads"
    Dim filename As String
    filename = ABMShared.SessionStorageRead(page, "FileManagerfilename")
    Dim www As String = "www"
    Dim fStructure As String = www & "/" & ABMShared.AppName & "/" & uploads
    fStructure = File.Combine(fStructure,"")
    ABMShared.SessionStorageSave(page, "FileManagerfilepath", File.Combine(File.DirApp, fStructure))
    ABMShared.PrepareRecursiveSearch
    ABMShared.ReadDir(fStructure,True)
    For Each strFile As String In ABMShared.ffiles
        strFile = File.Combine(File.DirApp,strFile)
        Dim sfilename As String = File.GetName(strFile)
        Dim sfilepath As String = File.GetFileParent(strFile)
        Dim sdatemodified As String = File.LastModified("",strFile)
        Dim sfileext As String = ABMShared.MvField(strFile,-1,".")
        If sfileext.EqualsIgnoreCase(strFile) Then
            sfileext = "unknown"
        End If
        Dim imgFile As String = File.Combine(File.DirApp,"www/" & ABMShared.appname & "/images/" & sfileext & ".png")
        If File.Exists("",imgFile) = False Then
            'try and save the image for later retrieval
            Dim img As Image = ABMShared.GetFileIcon("",strFile)
            ABMShared.SaveSnapShot(img, imgFile)
        End If
        Dim sfilesize As String = File.Size("",strFile)
        sdatemodified = DateTime.Date(sdatemodified)
        Dim sfiletype As String = sfileext
        Dim nt As Map
        nt.Initialize
        nt.put("filename", sfilename)
        nt.put("filepath", sfilepath)
        nt.Put("fileext", sfileext)
        nt.put("datemodified", sdatemodified)
        nt.put("filetype", sfiletype)
        nt.put("filesize", sfilesize)
        nFiles.Add(nt)
    Next
    'Get connection from current pool if MySQL/MSSQL
    Dim jSQL As SQL = ABMShared.SQLGet
    'clear the file manager, this is a temporal table
    ABMShared.SQLClearTable(jSQL,"FileManager","id",False)
    'add new details of the files
    ABMShared.SQLRecordInsertMaps(jSQL,"FileManager",nFiles)
    'Close the connection to the database
    ABMShared.SQLClose(jSQL)
End Sub

To Do Perhaps...

I intend to show the icon image next to each file name. Currently this saves the icon as an image using the file extension. Perhaps a navbar to Add/Rename/Delete files and folders.

NB: The code to save icon images from files, recursive searches on directories is in the forum.
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
So the next stage was to ensure I also display the icons for each file in the table.

1. For each file I get the extension and save the image based on the extension.

UFM.png


2. I also needed to color code the file uploader to my preferred theme. Here is the updated code.
3. A clean up was also done to ensure proper use of File.Combine.
4. Thanks to the feedback example on ideas about the icon column. (the blank header column for the icon)

Defining the new table...

B4X:
Dim FM As ABMTable
    FM.Initialize(page, "FM", True, False, True, "tblTheme")
    FM.IsBordered = True
    FM.IsResponsive = True
    FM.IgnoreFormattingCodes = False
    FM.IsTextSelectable = True
    FM.SetHeaders(Array As String("ID", "{NBSP}", "File Name", "Path", "Date Modified", "Type", "Size", "Download"))
    FM.SetHeaderThemes(Array As String("bg", "bg", "bg", "bg", "bg", "bg", "bgr", "bgc"))
    FM.SetHeaderHeights(Array As Int(0, 51, 0, 0, 0, 0, 0, 48))
    FM.SetColumnVisible(Array As Boolean(False, True, True, False, True, True, True, True))
    FM.SetColumnSortable(Array As Boolean(False, False, True, True, True, True, True, False))
    FM.SetColumnDataFields(Array As String("id", "fileimg", "filename", "filepath", "datemodified", "filetype", "filesize", ""))
    page.Cell(5,1).AddComponent(FM)
    RefreshFileManager
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Updated RefreshFileManager code.. (decided to use \ instead of /) for file paths and also saved the ABMImage link for the icon image.

B4X:
Sub RefreshFileManager
    'the format of the file data
    DateTime.DateFormat = "yyyy-MM-dd HH:mm"
    'to be used for inserts in the FileManager table
    Dim nFiles As List
    nFiles.Initialize
    'Define the parent folder to file manage
    Dim uploads As String
    uploads = "uploads"
    'Define the parent folder for our app
    Dim www As String = "www"
    Dim fStructure As String = www & "\" & ABMShared.AppName & "\" & uploads
    'process the complete path for the search
    fStructure = File.Combine(File.DirApp,fStructure)
    'save the current path for later retrieval
    ABMShared.SessionStorageSave(page, "FileManagerfilepath", fStructure)
    ABMShared.PrepareRecursiveSearch
    ABMShared.ReadDir(fStructure,True)
    For Each strFile As String In ABMShared.ffiles
        'return the complete file with its path
        Dim sfilename As String = File.GetName(strFile)
        Dim sfilepath As String = File.GetFileParent(strFile)
        Dim sdatemodified As String = File.LastModified("",strFile)
        Dim sfileext As String = ABMShared.MvField(strFile,-1,".")
        If sfileext.EqualsIgnoreCase(strFile) Then
            sfileext = "unknown"
        End If
        'define the complete path for the image
        Dim imgFile As String = "www\" & ABMShared.appname & "\images\" & sfileext & ".png"
        'this will be used to retrieve the image icon for the file
        Dim imgExt As String = "../images/" & sfileext & ".png"
        imgFile = File.Combine(File.DirApp,imgFile)
        'if the image path does not exist, save it for later retrieval
        If File.Exists("",imgFile) = False Then
            'try and save the image for later retrieval
            Dim img As Image = ABMShared.GetFileIcon("",strFile)
            ABMShared.SaveSnapShot(img, imgFile)
        End If
        Dim sfilesize As String = File.Size("",strFile)
        sdatemodified = DateTime.Date(sdatemodified)
        Dim sfiletype As String = sfileext
        Dim nt As Map
        nt.Initialize
        nt.put("filename", sfilename)
        nt.put("filepath", sfilepath)
        nt.Put("fileext", sfileext)
        nt.put("datemodified", sdatemodified)
        nt.put("filetype", sfiletype)
        nt.put("filesize", sfilesize)
        nt.put("fileimg", imgExt)
        'update the list for SQLInserts
        nFiles.Add(nt)
    Next
    'Get connection from current pool if MySQL/MSSQL
    Dim jSQL As SQL = ABMShared.SQLGet
    'clear the file manager, this is a temporal table
    ABMShared.SQLClearTable(jSQL,"FileManager","id",False)
    'add new details of the files
    ABMShared.SQLRecordInsertMaps(jSQL,"FileManager",nFiles)
    'Close the connection to the database
    ABMShared.SQLClose(jSQL)
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
The LoadFM code to list each file record in the ABMTable (the saved FilePath is what is used to filter the files)

B4X:
Private Sub LoadFM(fromPage As Int)
    'How many rows can we add to a single page
    MaxRows = 10
    Dim NowWH As String = ABM.GetBrowserWidthHeight(page)
    If NowWH <> "" And NowWH <> ";" Then
        Dim split() As String = Regex.Split(";", NowWH)
        Dim NewH As Int = split(1) - 350
        NewH = NewH / 50
        NewH = NewH / 5
        NewH = NewH * 5
        If NewH >= 10 Then
            MaxRows = NewH
        End If
    End If
    'Lets get the component from the page.
    Dim FM As ABMTable = page.Component("FM")
    'Define list to store the results of the query
    Dim results As List
    Dim resCnt As Int
    Dim resTot As Int
    Dim resMap As Map
    Dim sqlQry As String
    'Read arguments from LocalStorage (if any)
    Dim FileManagerfilepath As String = ABMShared.SessionStorageRead(page, "filemanagerfilepath")
    'Let's define the qry string
    Filter = Filter.Trim
    If Filter.Length = 0 Then
        Filter = "WHERE filepath = ?"
    Else
        If ABMShared.InStr(Filter, "AND filepath = ?") = -1 Then
            Filter = Filter & " AND filepath = ?"
        End If
    End If
    sqlQry = "SELECT * FROM FileManager " & Filter & " " & LastSort & " LIMIT " & ((fromPage - 1) * MaxRows) & ", " & MaxRows
    'Get connection from current pool if MySQL/MSSQL
    Dim jSQL As SQL = ABMShared.SQLGet
    'Get the number of records
    Dim SQL_str As String
    SQL_str = "Select Count(id) As IDS FROM FileManager " & Filter
    Dim NumRecords As Int = ABMShared.SQLSelectSingleResult(jSQL, SQL_str, Array As String(FileManagerfilepath))
    'Get the records as a list of maps from the db
    results = ABMShared.SQLExecuteMaps(jSQL, sqlQry, Array As String(FileManagerfilepath))
    'Close the connection to the database
    ABMShared.SQLClose(jSQL)
    If results.Size = 0 And fromPage > 1 Then
        'we are on a page without any lines
        fromPage = fromPage - 1
        LoadFM(fromPage)
        Return
    End If
    FM.Clear
    'Loop throught each record read and process it
    resTot = results.size - 1
    For resCnt = 0 To resTot
        'Get the record map
        resMap = results.get(resCnt)
        'Update each table row
        Dim rCellValues As List
        Dim rCellThemes As List
        rCellValues.Initialize
        rCellThemes.Initialize
        Dim primKey As String = resMap.get("id")
        Dim id As String = resMap.GetDefault("id", "")
        If id = "" Then
            rCellValues.Add("{NBSP}")
        Else
            rCellValues.Add(id)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolor")
        Dim fileimg As String = resMap.GetDefault("fileimg", "")
        If fileimg = "" Then
            rCellValues.Add("{NBSP}")
        Else
            Dim imgfileimg As ABMImage
            imgfileimg.Initialize(page, "imgfileimg", fileimg & "?" & DateTime.Now, 1)
            imgfileimg.IsResponsive=False
            imgfileimg.IsMaterialBoxed = False
            imgfileimg.Caption = ""
            imgfileimg.IsCircular = False
            imgfileimg.IsResponsive = False
            imgfileimg.Opacity = 1
            imgfileimg.IsClickable = False
            imgfileimg.SetFixedSize(32,32)
            rCellValues.Add(imgfileimg)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolor")
        Dim filename As String = resMap.GetDefault("filename", "")
        ABMShared.SessionStorageSave(page, "filename", filename)
        If filename = "" Then
            rCellValues.Add("{NBSP}")
        Else
            rCellValues.Add(filename)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolor")
        Dim filepath As String = resMap.GetDefault("filepath", "")
        ABMShared.SessionStorageSave(page, "filepath", filepath)
        If filepath = "" Then
            rCellValues.Add("{NBSP}")
        Else
            rCellValues.Add(filepath)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolor")
        Dim datemodified As String = resMap.GetDefault("datemodified", "DateTime.Now")
        If datemodified = "" Then
            rCellValues.Add("{NBSP}")
        Else
            '*** Start output format
            datemodified = ABMShared.projectdate(datemodified)
            '*** End output format
            rCellValues.Add(datemodified)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolor")
        Dim filetype As String = resMap.GetDefault("filetype", "")
        If filetype = "" Then
            rCellValues.Add("{NBSP}")
        Else
            rCellValues.Add(filetype)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolor")
        Dim filesize As String = resMap.GetDefault("filesize", "0.00")
        If filesize = "" Then
            rCellValues.Add("{NBSP}")
        Else
            '*** Start output format
            filesize = ABMShared.formatfilesize(filesize)
            '*** End output format
            rCellValues.Add(filesize)
        End If
        'Add theme to the cell
        rCellThemes.Add("nocolorr")
        Dim btnDownloadFM As ABMButton
        btnDownloadFM.InitializeFloating(page, "btnDownloadFM", "mdi-action-get-app", "fb")
        btnDownloadFM.Size = ABM.BUTTONSIZE_NORMAL
        btnDownloadFM.Enabled = ABMShared.HasPermission("frmFileManager","canread")
        rCellValues.Add(btnDownloadFM)
        rCellThemes.Add("openedit")
        'Add the row to the table
        FM.AddRow("id" & resCnt, rCellValues)
        FM.SetRowThemes(rCellThemes)
    Next
    FM.Refresh
    'Update the paginating component
    Dim pager As ABMPagination = page.Component("FMPager")
    If (NumRecords Mod MaxRows > 0) Or (NumRecords = 0) Then
        NumRecords = NumRecords/MaxRows + 1
    Else
        NumRecords = NumRecords/MaxRows
    End If
    pager.SetTotalNumberOfPages(NumRecords)
    pager.SetActivePage(fromPage)
    pager.Refresh
End Sub
 

Mashiane

Expert
Licensed User
Longtime User
Clicking the "Download" button, ensures that the complete file path is read and javascript executed to download the file.

B4X:
Sub FM_Clicked(PassedRowsAndColumns As List)
    'Get the details of the cell being selected
    Dim tblCellInfo As ABMTableCell = PassedRowsAndColumns.Get(0)
    'Get the table being processed.
    Dim FM As ABMTable = page.Component(tblCellInfo.TableName)
    'Read the first column in the current row and assign value to ActiveID
    ActiveID = FM.GetString(tblCellInfo.Row, 0)
    Dim sfilename As String = FM.GetString(tblCellInfo.Row, 2)
    ABMShared.SessionStorageSave(page, "FileManagerfilename", sfilename)
    Dim sfilepath As String = FM.GetString(tblCellInfo.Row, 3)
    ABMShared.SessionStorageSave(page, "FileManagerfilepath", sfilepath)
    Select Case tblCellInfo.Column
        Case 7
            If ABMShared.HasPermission("frmFileManager","canread") = False Then Return
            'Download a file
            Dim strFile As String = File.Combine(sfilepath,sfilename)
            Dim lp As Int = ABMShared.InStrRev(strFile,ABMShared.AppName)
            strFile = ABMShared.Mid2(strFile,lp)
            strFile = ".." & strFile.Replace(ABMShared.AppName,"")
            If ws.Open Then
                'force a download of the document
                ws.Eval("window.open(arguments[0],'_blank');", Array As Object(strFile))
                ws.Flush
            End If
    End Select
End Sub
 
Top