| @@ -292,6 +292,7 @@ | ||||
| #define SD_FINISHED_STEPPERRELEASE true  //if sd support and the file is finished: disable steppers? | ||||
| #define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place. | ||||
|  | ||||
| //#define SDCARD_SORT_ALPHA // Sort SD file listings in ASCII order. Find additional options in cardreader.h | ||||
| #define SDCARD_RATHERRECENTFIRST  //reverse file order of sd card menu display. Its sorted practically after the file system block order. | ||||
| // if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that. | ||||
| // using: | ||||
|   | ||||
| @@ -111,10 +111,12 @@ uint8_t const SOFT_SPI_SCK_PIN = 13; | ||||
| /** | ||||
|  * Defines for long (vfat) filenames | ||||
|  */ | ||||
| /** Number of UTF-16 characters per entry */ | ||||
| #define FILENAME_LENGTH 13 | ||||
| /** Number of VFAT entries used. Every entry has 13 UTF-16 characters */ | ||||
| #define MAX_VFAT_ENTRIES (2) | ||||
| /** Total size of the buffer used to store the long filenames */ | ||||
| #define LONG_FILENAME_LENGTH (13*MAX_VFAT_ENTRIES+1) | ||||
| #define LONG_FILENAME_LENGTH (FILENAME_LENGTH*MAX_VFAT_ENTRIES+1) | ||||
| #endif  // SdFatConfig_h | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -11,6 +11,9 @@ | ||||
|  | ||||
| CardReader::CardReader() | ||||
| { | ||||
|   #ifdef SDCARD_SORT_ALPHA | ||||
|    sort_count = 0; | ||||
|   #endif | ||||
|    filesize = 0; | ||||
|    sdpos = 0; | ||||
|    sdprinting = false; | ||||
| @@ -36,13 +39,9 @@ CardReader::CardReader() | ||||
| char *createFilename(char *buffer, const dir_t &p) //buffer>12characters | ||||
| { | ||||
|   char *pos=buffer; | ||||
|   for (uint8_t i = 0; i < 11; i++)  | ||||
|   { | ||||
|   for (uint8_t i = 0; i < 11; i++) { | ||||
|     if (p.name[i] == ' ') continue; | ||||
|     if (i == 8)  | ||||
|     { | ||||
|       *pos++='.'; | ||||
|     } | ||||
|     if (i == 8) *pos++ = '.'; | ||||
|     *pos++ = p.name[i]; | ||||
|   } | ||||
|   *pos++ = 0; | ||||
| @@ -60,8 +59,8 @@ void  CardReader::lsDive(const char *prepend,SdFile parent) | ||||
|     if( DIR_IS_SUBDIR(&p) && lsAction!=LS_Count && lsAction!=LS_GetFilename) // hence LS_SerialPrint | ||||
|     { | ||||
|  | ||||
|       char path[13*2]; | ||||
|       char lfilename[13]; | ||||
|       char path[FILENAME_LENGTH*2]; | ||||
|       char lfilename[FILENAME_LENGTH]; | ||||
|       createFilename(lfilename,p); | ||||
|        | ||||
|       path[0]=0; | ||||
| @@ -87,8 +86,6 @@ void  CardReader::lsDive(const char *prepend,SdFile parent) | ||||
|       } | ||||
|       lsDive(path,dir); | ||||
|       //close done automatically by destructor of SdFile | ||||
|  | ||||
|        | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| @@ -105,7 +102,6 @@ void  CardReader::lsDive(const char *prepend,SdFile parent) | ||||
|       if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue; | ||||
|       filenameIsDir=DIR_IS_SUBDIR(&p); | ||||
|  | ||||
|        | ||||
|       if(!filenameIsDir) | ||||
|       { | ||||
|         if(p.name[8]!='G') continue; | ||||
| @@ -124,10 +120,8 @@ void  CardReader::lsDive(const char *prepend,SdFile parent) | ||||
|       }  | ||||
|       else if(lsAction==LS_GetFilename) | ||||
|       { | ||||
|         if(cnt==nrFiles) | ||||
|           return; | ||||
|         if (cnt == nrFiles) return; | ||||
|         cnt++; | ||||
|          | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -136,9 +130,6 @@ void  CardReader::lsDive(const char *prepend,SdFile parent) | ||||
| void CardReader::ls()  | ||||
| { | ||||
|   lsAction=LS_SerialPrint; | ||||
|   if(lsAction==LS_Count) | ||||
|   nrFiles=0; | ||||
|  | ||||
|   root.rewind(); | ||||
|   lsDive("",root); | ||||
| } | ||||
| @@ -177,6 +168,9 @@ void CardReader::initsd() | ||||
|   } | ||||
|   workDir=root; | ||||
|   curDir=&root; | ||||
|   #ifdef SDCARD_SORT_ALPHA | ||||
|     presort(); | ||||
|   #endif | ||||
|   /* | ||||
|   if(!workDir.openRoot(&volume)) | ||||
|   { | ||||
| @@ -193,8 +187,10 @@ void CardReader::setroot() | ||||
|     SERIAL_ECHOLNPGM(MSG_SD_WORKDIR_FAIL); | ||||
|   }*/ | ||||
|   workDir=root; | ||||
|    | ||||
|   curDir=&workDir; | ||||
|   #ifdef SDCARD_SORT_ALPHA | ||||
|     presort(); | ||||
|   #endif | ||||
| } | ||||
| void CardReader::release() | ||||
| { | ||||
| @@ -207,6 +203,7 @@ void CardReader::startFileprint() | ||||
|   if(cardOK) | ||||
|   { | ||||
|     sdprinting = true; | ||||
|     flush_presort(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -235,7 +232,7 @@ void CardReader::getAbsFilename(char *t) | ||||
|     while(*t!=0 && cnt< MAXPATHNAMELENGTH)  | ||||
|     {t++;cnt++;}  //crawl counter forward. | ||||
|   } | ||||
|   if(cnt<MAXPATHNAMELENGTH-13) | ||||
|   if(cnt<MAXPATHNAMELENGTH-FILENAME_LENGTH) | ||||
|     file.getFilename(t); | ||||
|   else | ||||
|     t[0]=0; | ||||
| @@ -305,7 +302,7 @@ void CardReader::openFile(char* name,bool read, bool replace_current/*=true*/) | ||||
|       //SERIAL_ECHO("end  :");SERIAL_ECHOLN((int)(dirname_end-name)); | ||||
|       if(dirname_end>0 && dirname_end>dirname_start) | ||||
|       { | ||||
|         char subdirname[13]; | ||||
|         char subdirname[FILENAME_LENGTH]; | ||||
|         strncpy(subdirname, dirname_start, dirname_end-dirname_start); | ||||
|         subdirname[dirname_end-dirname_start]=0; | ||||
|         SERIAL_ECHOLN(subdirname); | ||||
| @@ -401,7 +398,7 @@ void CardReader::removeFile(char* name) | ||||
|       //SERIAL_ECHO("end  :");SERIAL_ECHOLN((int)(dirname_end-name)); | ||||
|       if(dirname_end>0 && dirname_end>dirname_start) | ||||
|       { | ||||
|         char subdirname[13]; | ||||
|         char subdirname[FILENAME_LENGTH]; | ||||
|         strncpy(subdirname, dirname_start, dirname_end-dirname_start); | ||||
|         subdirname[dirname_end-dirname_start]=0; | ||||
|         SERIAL_ECHOLN(subdirname); | ||||
| @@ -439,6 +436,9 @@ void CardReader::removeFile(char* name) | ||||
|       SERIAL_PROTOCOLPGM("File deleted:"); | ||||
|       SERIAL_PROTOCOLLN(fname); | ||||
|       sdpos = 0; | ||||
|       #ifdef SDCARD_SORT_ALPHA | ||||
|         presort(); | ||||
|       #endif | ||||
|     } | ||||
|     else | ||||
|     { | ||||
| @@ -552,14 +552,21 @@ void CardReader::closefile(bool store_location) | ||||
|    | ||||
| } | ||||
|  | ||||
| void CardReader::getfilename(const uint8_t nr) | ||||
| void CardReader::getfilename(const uint16_t nr) | ||||
| { | ||||
|   #if defined(SDCARD_SORT_ALPHA) && SORT_USES_RAM && SORT_USES_MORE_RAM | ||||
|     if (nr < sort_count) { | ||||
|       strcpy(filename, sortshort[nr]); | ||||
|       strcpy(longFilename, sortnames[nr]); | ||||
|       filenameIsDir = isDir[nr]; | ||||
|       return; | ||||
|     } | ||||
|   #endif | ||||
|   curDir=&workDir; | ||||
|   lsAction=LS_GetFilename; | ||||
|   nrFiles=nr; | ||||
|   curDir->rewind(); | ||||
|   lsDive("",*curDir); | ||||
|    | ||||
| } | ||||
|  | ||||
| uint16_t CardReader::getnrfilenames() | ||||
| @@ -595,6 +602,9 @@ void CardReader::chdir(const char * relpath) | ||||
|       workDirParents[0]=*parent; | ||||
|     } | ||||
|     workDir=newfile; | ||||
|     #ifdef SDCARD_SORT_ALPHA | ||||
|       presort(); | ||||
|     #endif | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -604,12 +614,152 @@ void CardReader::updir() | ||||
|   { | ||||
|     --workDirDepth; | ||||
|     workDir = workDirParents[0]; | ||||
|     int d; | ||||
|     for (int d = 0; d < workDirDepth; d++) | ||||
|       workDirParents[d] = workDirParents[d+1]; | ||||
|     #ifdef SDCARD_SORT_ALPHA | ||||
|       presort(); | ||||
|     #endif | ||||
|   } | ||||
| } | ||||
|  | ||||
| #ifdef SDCARD_SORT_ALPHA | ||||
|  | ||||
| /** | ||||
|  * Get the name of a file in the current directory by sort-index | ||||
|  */ | ||||
| void CardReader::getfilename_sorted(const uint16_t nr) { | ||||
|   getfilename(nr < sort_count ? sort_order[nr] : nr); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Read all the files and produce a sort key | ||||
|  * | ||||
|  * We can do this in 3 ways... | ||||
|  *  - Minimal RAM: Read two filenames at a time sorting along... | ||||
|  *  - Some RAM: Buffer the directory and return filenames from RAM | ||||
|  *  - Some RAM: Buffer the directory just for this sort | ||||
|  */ | ||||
| void CardReader::presort() | ||||
| { | ||||
|   flush_presort(); | ||||
|  | ||||
|   uint16_t fileCnt = getnrfilenames(); | ||||
|   if (fileCnt > 0) { | ||||
|  | ||||
|     if (fileCnt > SORT_LIMIT) fileCnt = SORT_LIMIT; | ||||
|  | ||||
|     #if SORT_USES_RAM | ||||
|       #if SORT_USES_MORE_RAM | ||||
|         sortshort = (char**)calloc(fileCnt, sizeof(char*)); | ||||
|         sortnames = (char**)calloc(fileCnt, sizeof(char*)); | ||||
|       #else | ||||
|         char *sortnames[fileCnt]; | ||||
|       #endif | ||||
|     #else | ||||
|       char name1[LONG_FILENAME_LENGTH+1]; | ||||
|     #endif | ||||
|  | ||||
|     #if FOLDER_SORTING != 0 | ||||
|       #if SORT_USES_RAM && SORT_USES_MORE_RAM | ||||
|         isDir = (uint8_t*)calloc(fileCnt, sizeof(uint8_t)); | ||||
|       #else | ||||
|         uint8_t isDir[fileCnt]; | ||||
|       #endif | ||||
|     #endif | ||||
|  | ||||
|     sort_order = new uint8_t[fileCnt]; | ||||
|  | ||||
|     if (fileCnt > 1) { | ||||
|  | ||||
|       // Init sort order. If using RAM then read all filenames now. | ||||
|       for (uint16_t i=0; i<fileCnt; i++) { | ||||
|         sort_order[i] = i; | ||||
|         #if SORT_USES_RAM | ||||
|           getfilename(i); | ||||
|           sortnames[i] = strdup(longFilename[0] ? longFilename : filename); | ||||
|           #if SORT_USES_MORE_RAM | ||||
|             sortshort[i] = strdup(filename); | ||||
|           #endif | ||||
|           // char out[30]; | ||||
|           // sprintf_P(out, PSTR("---- %i %s %s"), i, filenameIsDir ? "D" : " ", sortnames[i]); | ||||
|           // SERIAL_ECHOLN(out); | ||||
|           #if FOLDER_SORTING != 0 | ||||
|             isDir[i] = filenameIsDir; | ||||
|           #endif | ||||
|         #endif | ||||
|       } | ||||
|  | ||||
|       // Bubble Sort | ||||
|       for (uint16_t i=fileCnt; --i;) { | ||||
|         bool cmp, didSwap = false; | ||||
|         for (uint16_t j=0; j<i; ++j) { | ||||
|           uint16_t s1 = j, s2 = j+1, o1 = sort_order[s1], o2 = sort_order[s2]; | ||||
|           #if SORT_USES_RAM | ||||
|             #if FOLDER_SORTING != 0 | ||||
|               cmp = (isDir[o1] == isDir[o2]) ? (strcasecmp(sortnames[o1], sortnames[o2]) > 0) : isDir[FOLDER_SORTING > 0 ? o1 : o2]; | ||||
|             #else | ||||
|               cmp = strcasecmp(sortnames[o1], sortnames[o2]) > 0; | ||||
|             #endif | ||||
|           #else | ||||
|             getfilename(o1); | ||||
|             strcpy(name1, longFilename[0] ? longFilename : filename); | ||||
|             #if FOLDER_SORTING != 0 | ||||
|               bool dir1 = filenameIsDir; | ||||
|             #endif | ||||
|             getfilename(o2); | ||||
|             char *name2 = longFilename[0] ? longFilename : filename; | ||||
|             #if FOLDER_SORTING != 0 | ||||
|               cmp = (dir1 == filenameIsDir) ? (strcasecmp(name1, name2) > 0) : (FOLDER_SORTING > 0 ? dir1 : !dir1); | ||||
|             #else | ||||
|               cmp = strcasecmp(name1, name2) > 0; | ||||
|             #endif | ||||
|           #endif | ||||
|           if (cmp) { | ||||
|             sort_order[s1] = o2; | ||||
|             sort_order[s2] = o1; | ||||
|             didSwap = true; | ||||
|           } | ||||
|         } | ||||
|         if (!didSwap) break; | ||||
|       } | ||||
|  | ||||
|       #if SORT_USES_RAM && !SORT_USES_MORE_RAM | ||||
|         for (uint16_t i=0; i<fileCnt; ++i) free(sortnames[i]); | ||||
|       #endif | ||||
|     } | ||||
|     else { | ||||
|       sort_order[0] = 0; | ||||
|       #if SORT_USES_RAM && SORT_USES_MORE_RAM | ||||
|         sortnames = (char**)malloc(sizeof(char*)); | ||||
|         sortshort = (char**)malloc(sizeof(char*)); | ||||
|         isDir = (uint8_t*)malloc(sizeof(uint8_t)); | ||||
|         getfilename(0); | ||||
|         sortnames[0] = strdup(longFilename[0] ? longFilename : filename); | ||||
|         sortshort[0] = strdup(filename); | ||||
|         isDir[0] = filenameIsDir; | ||||
|       #endif | ||||
|     } | ||||
|  | ||||
|     sort_count = fileCnt; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CardReader::flush_presort() { | ||||
|   if (sort_count > 0) { | ||||
|     #if SORT_USES_RAM && SORT_USES_MORE_RAM | ||||
|       for (uint8_t i=0; i<sort_count; ++i) { | ||||
|         free(sortshort[i]); | ||||
|         free(sortnames[i]); | ||||
|       } | ||||
|       free(sortshort); | ||||
|       free(sortnames); | ||||
|     #endif | ||||
|     delete sort_order; | ||||
|     sort_count = 0; | ||||
|   } | ||||
| } | ||||
|  | ||||
| #endif // SDCARD_SORT_ALPHA | ||||
|  | ||||
| void CardReader::printingHasFinished() | ||||
| { | ||||
| @@ -633,6 +783,9 @@ void CardReader::printingHasFinished() | ||||
|           enquecommand_P(PSTR(SD_FINISHED_RELEASECOMMAND)); | ||||
|       } | ||||
|       autotempShutdown(); | ||||
|       #ifdef SDCARD_SORT_ALPHA | ||||
|         presort(); | ||||
|       #endif | ||||
|     } | ||||
| } | ||||
| #endif //SDSUPPORT | ||||
|   | ||||
| @@ -3,7 +3,14 @@ | ||||
|  | ||||
| #ifdef SDSUPPORT | ||||
|  | ||||
| #define MAX_DIR_DEPTH 10 | ||||
| #define MAX_DIR_DEPTH 10          // Maximum folder depth | ||||
|  | ||||
| #ifdef SDCARD_SORT_ALPHA | ||||
|   #define SORT_USES_RAM false      // Buffer while sorting, else re-read from SD | ||||
|   #define SORT_USES_MORE_RAM false // Always keep the directory in RAM | ||||
|   #define SORT_LIMIT 256           // Maximum number of sorted items | ||||
|   #define FOLDER_SORTING -1        // -1=above  0=none  1=below | ||||
| #endif | ||||
|  | ||||
| #include "SdFile.h" | ||||
| enum LsAction {LS_SerialPrint,LS_Count,LS_GetFilename}; | ||||
| @@ -28,7 +35,7 @@ public: | ||||
|   void getStatus(); | ||||
|   void printingHasFinished(); | ||||
|  | ||||
|   void getfilename(const uint8_t nr); | ||||
|   void getfilename(const uint16_t nr); | ||||
|   uint16_t getnrfilenames(); | ||||
|    | ||||
|   void getAbsFilename(char *t); | ||||
| @@ -39,6 +46,12 @@ public: | ||||
|   void updir(); | ||||
|   void setroot(); | ||||
|  | ||||
| #ifdef SDCARD_SORT_ALPHA | ||||
|   void presort(); | ||||
|   void flush_presort(); | ||||
|   void getfilename_sorted(const uint16_t nr); | ||||
| #endif | ||||
|  | ||||
|  | ||||
|   FORCE_INLINE bool isFileOpen() { return file.isOpen(); } | ||||
|   FORCE_INLINE bool eof() { return sdpos>=filesize ;}; | ||||
| @@ -52,18 +65,27 @@ public: | ||||
|   bool logging; | ||||
|   bool sdprinting; | ||||
|   bool cardOK; | ||||
|   char filename[13]; | ||||
|   char filename[FILENAME_LENGTH]; | ||||
|   char longFilename[LONG_FILENAME_LENGTH]; | ||||
|   bool filenameIsDir; | ||||
|   int lastnr; //last number of the autostart; | ||||
| private: | ||||
|   SdFile root,*curDir,workDir,workDirParents[MAX_DIR_DEPTH]; | ||||
|   uint16_t workDirDepth; | ||||
| #ifdef SDCARD_SORT_ALPHA | ||||
|   uint16_t sort_count; | ||||
|   uint8_t *sort_order; | ||||
|   #if SORT_USES_MORE_RAM | ||||
|     char **sortshort; | ||||
|     char **sortnames; | ||||
|     uint8_t *isDir; | ||||
|   #endif | ||||
| #endif | ||||
|   Sd2Card card; | ||||
|   SdVolume volume; | ||||
|   SdFile file; | ||||
|   #define SD_PROCEDURE_DEPTH 1 | ||||
|   #define MAXPATHNAMELENGTH (13*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1) | ||||
|   #define MAXPATHNAMELENGTH (FILENAME_LENGTH*MAX_DIR_DEPTH+MAX_DIR_DEPTH+1) | ||||
|   uint8_t file_subcall_ctr; | ||||
|   uint32_t filespos[SD_PROCEDURE_DEPTH]; | ||||
|   char filenames[SD_PROCEDURE_DEPTH][MAXPATHNAMELENGTH]; | ||||
| @@ -75,7 +97,7 @@ private: | ||||
|   bool autostart_stilltocheck; //the sd start is delayed, because otherwise the serial cannot answer fast enought to make contact with the hostsoftware. | ||||
|    | ||||
|   LsAction lsAction; //stored for recursion. | ||||
|   int16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory. | ||||
|   uint16_t nrFiles; //counter for the files in the current directory and recycled as position counter for getting the nrFiles'th name in the directory. | ||||
|   char* diveDirName; | ||||
|   void lsDive(const char *prepend,SdFile parent); | ||||
| }; | ||||
|   | ||||
| @@ -295,6 +295,7 @@ | ||||
| #define SD_FINISHED_STEPPERRELEASE true  //if sd support and the file is finished: disable steppers? | ||||
| #define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place. | ||||
|  | ||||
| //#define SDCARD_SORT_ALPHA // Sort SD file listings in ASCII order. Find additional options in cardreader.h | ||||
| #define SDCARD_RATHERRECENTFIRST  //reverse file order of sd card menu display. Its sorted practically after the file system block order. | ||||
| // if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that. | ||||
| // using: | ||||
|   | ||||
| @@ -287,6 +287,7 @@ | ||||
| #define SD_FINISHED_STEPPERRELEASE true  //if sd support and the file is finished: disable steppers? | ||||
| #define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place. | ||||
|  | ||||
| //#define SDCARD_SORT_ALPHA // Sort SD file listings in ASCII order. Find additional options in cardreader.h | ||||
| #define SDCARD_RATHERRECENTFIRST  //reverse file order of sd card menu display. Its sorted practically after the filesystem block order.  | ||||
| // if a file is deleted, it frees a block. hence, the order is not purely cronological. To still have auto0.g accessible, there is again the option to do that. | ||||
| // using: | ||||
|   | ||||
| @@ -291,6 +291,7 @@ | ||||
| #define SD_FINISHED_STEPPERRELEASE true  //if sd support and the file is finished: disable steppers? | ||||
| #define SD_FINISHED_RELEASECOMMAND "M84 X Y Z E" // You might want to keep the z enabled so your bed stays in place. | ||||
|  | ||||
| //#define SDCARD_SORT_ALPHA // Sort SD file listings in ASCII order. Find additional options in cardreader.h | ||||
| #define SDCARD_RATHERRECENTFIRST  //reverse file order of sd card menu display. Its sorted practically after the file system block order. | ||||
| // if a file is deleted, it frees a block. hence, the order is not purely chronological. To still have auto0.g accessible, there is again the option to do that. | ||||
| // using: | ||||
|   | ||||
| @@ -1004,15 +1004,22 @@ void lcd_sdcard_menu() | ||||
|     { | ||||
|         if (_menuItemNr == _lineNr) | ||||
|         { | ||||
|             #ifndef SDCARD_RATHERRECENTFIRST | ||||
|               card.getfilename(i); | ||||
|             #if defined(SDCARD_RATHERRECENTFIRST) && !defined(SDCARD_SORT_ALPHA) | ||||
|               int nr = fileCnt-1-i; | ||||
|             #else | ||||
|               card.getfilename(fileCnt-1-i); | ||||
|               int nr = i; | ||||
|             #endif | ||||
|             if (card.filenameIsDir) | ||||
|             { | ||||
|  | ||||
|             #ifdef SDCARD_SORT_ALPHA | ||||
|               card.getfilename_sorted(nr); | ||||
|             #else | ||||
|               card.getfilename(nr); | ||||
|             #endif | ||||
|  | ||||
|             if (card.filenameIsDir) { | ||||
|               MENU_ITEM(sddirectory, MSG_CARD_MENU, card.filename, card.longFilename); | ||||
|             }else{ | ||||
|             } | ||||
|             else { | ||||
|               MENU_ITEM(sdfile, MSG_CARD_MENU, card.filename, card.longFilename); | ||||
|             } | ||||
|         }else{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user