mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-10-25 11:17:28 +02:00 
			
		
		
		
	Update sonarqube.yml and removal of gradle keys (#2866)
# Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details.
This commit is contained in:
		
							parent
							
								
									5e3612a9b0
								
							
						
					
					
						commit
						69d4b52b06
					
				
							
								
								
									
										6
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @ -37,12 +37,6 @@ jobs: | ||||
|           java-version: ${{ matrix.jdk-version }} | ||||
|           distribution: "temurin" | ||||
| 
 | ||||
|       - name: PR | Generate verification metadata with signatures and checksums for dependabot[bot] | ||||
|         if: github.event.pull_request.user.login == 'dependabot[bot]' | ||||
|         run: | | ||||
|           ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 --refresh-dependencies help | ||||
|           ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help | ||||
| 
 | ||||
|       - name: Build with Gradle and no spring security | ||||
|         run: ./gradlew clean build | ||||
|         env: | ||||
|  | ||||
							
								
								
									
										81
									
								
								.github/workflows/sonarqube.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										81
									
								
								.github/workflows/sonarqube.yml
									
									
									
									
										vendored
									
									
								
							| @ -8,30 +8,71 @@ on: | ||||
| 
 | ||||
| permissions: | ||||
|   pull-requests: read | ||||
|   actions: read | ||||
| name: Run Sonarqube | ||||
| jobs: | ||||
|   sonarqube: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Analyze with SonarCloud | ||||
| 
 | ||||
|         # You can pin the exact commit or the version. | ||||
|         # uses: SonarSource/sonarcloud-github-action@v2.2.0 | ||||
|         uses: SonarSource/sonarcloud-github-action@4006f663ecaf1f8093e8e4abb9227f6041f52216 #v2.2.0 | ||||
|         env: | ||||
|           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}   # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) | ||||
| 
 | ||||
|       - name: Harden Runner | ||||
|         uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 | ||||
|         with: | ||||
|           # Additional arguments for the SonarScanner CLI | ||||
|           args: | ||||
|             # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) | ||||
|             # mandatory | ||||
|             -Dsonar.projectKey=Stirling-Tools_Stirling-PDF | ||||
|             -Dsonar.organization=stirling-tools | ||||
|             # Comma-separated paths to directories containing main source files. | ||||
|             #-Dsonar.sources= # optional, default is project base directory | ||||
|             # Comma-separated paths to directories containing test source files. | ||||
|             #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ | ||||
|             # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. | ||||
|             #-Dsonar.verbose= # optional, default is false | ||||
|           # When you need the analysis to take place in a directory other than the one from which it was launched, default is . | ||||
|           projectBaseDir: . | ||||
|           egress-policy: audit | ||||
| 
 | ||||
|       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||||
|         with: | ||||
|           fetch-depth: 0  | ||||
| 
 | ||||
|       - name: Set up JDK | ||||
|         uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 | ||||
|         with: | ||||
|           java-version: '17' | ||||
|           distribution: 'temurin' | ||||
| 
 | ||||
|       - name: Cache SonarCloud packages | ||||
|         uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 | ||||
|         with: | ||||
|           path: ~/.sonar/cache | ||||
|           key: ${{ runner.os }}-sonar | ||||
|           restore-keys: ${{ runner.os }}-sonar | ||||
| 
 | ||||
|       - name: Cache Gradle packages | ||||
|         uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 | ||||
|         with: | ||||
|           path: ~/.gradle/caches | ||||
|           key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} | ||||
|           restore-keys: ${{ runner.os }}-gradle | ||||
| 
 | ||||
|       - name: Build and analyze with Gradle | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | ||||
|           DOCKER_ENABLE_SECURITY: true | ||||
|           STIRLING_PDF_DESKTOP_UI: true | ||||
|         run: | | ||||
|           ./gradlew clean build sonar \ | ||||
|             -Dsonar.projectKey=Stirling-Tools_Stirling-PDF \ | ||||
|             -Dsonar.organization=stirling-tools \ | ||||
|             -Dsonar.host.url=https://sonarcloud.io \ | ||||
|             -Dsonar.log.level=DEBUG \ | ||||
|             --info | ||||
| 
 | ||||
|       - name: Upload Problems Report on Failure | ||||
|         if: failure() | ||||
|         uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 | ||||
|         with: | ||||
|           name: gradle-problems-report | ||||
|           path: build/reports/problems/problems-report.html | ||||
|           retention-days: 7 | ||||
| 
 | ||||
|       - name: Upload Sonar Logs on Failure | ||||
|         if: failure() | ||||
|         uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 | ||||
|         with: | ||||
|           name: sonar-logs | ||||
|           path: | | ||||
|             .scannerwork/report-task.txt | ||||
|             build/sonar/ | ||||
|           retention-days: 7 | ||||
|  | ||||
							
								
								
									
										10
									
								
								.github/workflows/sync_files.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/sync_files.yml
									
									
									
									
										vendored
									
									
								
							| @ -104,16 +104,6 @@ jobs: | ||||
|           git add README.md | ||||
|           git diff --staged --quiet || git commit -m ":memo: Sync README.md" || echo "no changes" | ||||
| 
 | ||||
|       - name: Generate verification metadata with signatures and checksums | ||||
|         run: | | ||||
|           set -e | ||||
|           if [ -f ./gradle/verification-metadata.xml ]; then | ||||
|             rm ./gradle/verification-metadata.xml | ||||
|           fi | ||||
|           ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 help | ||||
|           ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help | ||||
|           ./gradlew clean build | ||||
| 
 | ||||
|       - name: Run git add | ||||
|         run: | | ||||
|           git add gradle/verification-keyring.keys | ||||
|  | ||||
| @ -585,41 +585,3 @@ In your Thymeleaf templates, use the `#{key}` syntax to reference the new transl | ||||
| ``` | ||||
| 
 | ||||
| Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization. | ||||
| 
 | ||||
| 
 | ||||
| ## Managing Dependencies | ||||
| 
 | ||||
| When adding new dependencies or updating existing ones in Stirling-PDF, follow these steps to ensure proper verification and security: | ||||
| 
 | ||||
| 1. Update the dependency in `build.gradle`: | ||||
| ```groovy | ||||
| dependencies { | ||||
|     // Add or update your dependency | ||||
|     implementation "com.example:new-library:1.2.3" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 2. Generate new verification metadata and keys: | ||||
| ```bash | ||||
| # Generate verification metadata with signatures and checksums | ||||
| ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp | ||||
| 
 | ||||
| # Export the .keys file | ||||
| ./gradlew --export-keys | ||||
| ``` | ||||
| 
 | ||||
| 3. Files to commit: | ||||
|    - `build.gradle` - Your dependency changes | ||||
|    - `gradle/verification-metadata.xml` - Contains verification rules and checksums | ||||
|    - `gradle/verification-keyring.keys` - Contains PGP keys in text format | ||||
| 
 | ||||
| 4. Verify the build works with the new verification: | ||||
| ```bash | ||||
| ./gradlew build | ||||
| ``` | ||||
| 
 | ||||
| 5. Before committing, check: | ||||
|    - Verify any new BOM files are properly handled in verification metadata | ||||
|    - Review the changes in `verification-metadata.xml` to ensure they match your dependency updates | ||||
| 
 | ||||
| This ensures dependencies are properly verified and secure while maintaining transparency in the repository. | ||||
|  | ||||
							
								
								
									
										13
									
								
								build.gradle
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								build.gradle
									
									
									
									
									
								
							| @ -9,6 +9,7 @@ plugins { | ||||
|     id "com.github.jk1.dependency-license-report" version "2.9" | ||||
|     //id "nebula.lint" version "19.0.3" | ||||
|     id("org.panteleyev.jpackageplugin") version "1.6.0" | ||||
|     id "org.sonarqube" version "6.0.1.5171" | ||||
| } | ||||
| 
 | ||||
| import com.github.jk1.license.render.* | ||||
| @ -34,7 +35,6 @@ java { | ||||
| 
 | ||||
| repositories { | ||||
|     mavenCentral() | ||||
|     maven { url = "https://jitpack.io" } | ||||
|     maven { url = "https://build.shibboleth.net/maven/releases" } | ||||
|     maven { url = "https://maven.pkg.github.com/jcefmaven/jcefmaven" } | ||||
| } | ||||
| @ -269,6 +269,17 @@ spotless { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sonar { | ||||
|     properties { | ||||
|         property "sonar.projectKey", "Stirling-Tools_Stirling-PDF" | ||||
|         property "sonar.organization", "stirling-tools" | ||||
|          | ||||
|         property "sonar.exclusions", "**/build-wrapper-dump.json, src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" | ||||
|         property "sonar.coverage.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" | ||||
|         property "sonar.cpd.exclusions", "src/main/java/org/apache/**, src/main/resources/static/pdfjs/**, src/main/resources/static/pdfjs-legacy/**, src/main/resources/static/js/thirdParty/**" | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| //gradleLint { | ||||
| //        rules=['unused-dependency'] | ||||
| //    } | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -32,7 +32,10 @@ public class AdditionalLanguageJsController { | ||||
|         response.setContentType("application/javascript"); | ||||
|         PrintWriter writer = response.getWriter(); | ||||
|         // Erstelle das JavaScript dynamisch | ||||
|         writer.println("const supportedLanguages = " + toJsonArray(new ArrayList<>(supportedLanguages)) + ";"); | ||||
|         writer.println( | ||||
|                 "const supportedLanguages = " | ||||
|                         + toJsonArray(new ArrayList<>(supportedLanguages)) | ||||
|                         + ";"); | ||||
|         // Generiere die `getDetailedLanguageCode`-Funktion | ||||
|         writer.println( | ||||
|                 """ | ||||
|  | ||||
| @ -13,8 +13,8 @@ import io.github.pixee.security.Filenames; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
| 
 | ||||
| import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; | ||||
| import stirling.software.SPDF.model.ApplicationProperties; | ||||
| import stirling.software.SPDF.model.api.converters.HTMLToPdfRequest; | ||||
| import stirling.software.SPDF.service.CustomPDDocumentFactory; | ||||
| import stirling.software.SPDF.utils.FileToPdf; | ||||
| import stirling.software.SPDF.utils.WebResponseUtils; | ||||
| @ -28,16 +28,16 @@ public class ConvertHtmlToPDF { | ||||
| 
 | ||||
|     private final CustomPDDocumentFactory pdfDocumentFactory; | ||||
| 
 | ||||
| 	private final ApplicationProperties applicationProperties; | ||||
|     private final ApplicationProperties applicationProperties; | ||||
| 
 | ||||
|     @Autowired | ||||
|     public ConvertHtmlToPDF( | ||||
|             CustomPDDocumentFactory pdfDocumentFactory, | ||||
|             @Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled, | ||||
| 			ApplicationProperties applicationProperties) { | ||||
|             ApplicationProperties applicationProperties) { | ||||
|         this.pdfDocumentFactory = pdfDocumentFactory; | ||||
|         this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled; | ||||
| 		this.applicationProperties = applicationProperties; | ||||
|         this.applicationProperties = applicationProperties; | ||||
|     } | ||||
| 
 | ||||
|     @PostMapping(consumes = "multipart/form-data", value = "/html/pdf") | ||||
| @ -60,7 +60,8 @@ public class ConvertHtmlToPDF { | ||||
|             throw new IllegalArgumentException("File must be either .html or .zip format."); | ||||
|         } | ||||
| 
 | ||||
| 		boolean disableSanitize = Boolean.TRUE.equals(applicationProperties.getSystem().getDisableSanitize()); | ||||
|         boolean disableSanitize = | ||||
|                 Boolean.TRUE.equals(applicationProperties.getSystem().getDisableSanitize()); | ||||
| 
 | ||||
|         byte[] pdfBytes = | ||||
|                 FileToPdf.convertHtmlToPdf( | ||||
| @ -68,7 +69,7 @@ public class ConvertHtmlToPDF { | ||||
|                         fileInput.getBytes(), | ||||
|                         originalFilename, | ||||
|                         bookAndHtmlFormatsInstalled, | ||||
| 						disableSanitize); | ||||
|                         disableSanitize); | ||||
| 
 | ||||
|         pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes); | ||||
| 
 | ||||
|  | ||||
| @ -23,8 +23,8 @@ import io.github.pixee.security.Filenames; | ||||
| import io.swagger.v3.oas.annotations.Operation; | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
| 
 | ||||
| import stirling.software.SPDF.model.api.GeneralFile; | ||||
| import stirling.software.SPDF.model.ApplicationProperties; | ||||
| import stirling.software.SPDF.model.api.GeneralFile; | ||||
| import stirling.software.SPDF.service.CustomPDDocumentFactory; | ||||
| import stirling.software.SPDF.utils.FileToPdf; | ||||
| import stirling.software.SPDF.utils.WebResponseUtils; | ||||
| @ -38,16 +38,16 @@ public class ConvertMarkdownToPdf { | ||||
| 
 | ||||
|     private final CustomPDDocumentFactory pdfDocumentFactory; | ||||
| 
 | ||||
| 	private final ApplicationProperties applicationProperties; | ||||
|     private final ApplicationProperties applicationProperties; | ||||
| 
 | ||||
|     @Autowired | ||||
|     public ConvertMarkdownToPdf( | ||||
|             CustomPDDocumentFactory pdfDocumentFactory, | ||||
|             @Qualifier("bookAndHtmlFormatsInstalled") boolean bookAndHtmlFormatsInstalled, | ||||
| 			ApplicationProperties applicationProperties) { | ||||
|             ApplicationProperties applicationProperties) { | ||||
|         this.pdfDocumentFactory = pdfDocumentFactory; | ||||
|         this.bookAndHtmlFormatsInstalled = bookAndHtmlFormatsInstalled; | ||||
| 		this.applicationProperties = applicationProperties; | ||||
|         this.applicationProperties = applicationProperties; | ||||
|     } | ||||
| 
 | ||||
|     @PostMapping(consumes = "multipart/form-data", value = "/markdown/pdf") | ||||
| @ -81,7 +81,8 @@ public class ConvertMarkdownToPdf { | ||||
| 
 | ||||
|         String htmlContent = renderer.render(document); | ||||
| 
 | ||||
| 		boolean disableSanitize = Boolean.TRUE.equals(applicationProperties.getSystem().getDisableSanitize()); | ||||
|         boolean disableSanitize = | ||||
|                 Boolean.TRUE.equals(applicationProperties.getSystem().getDisableSanitize()); | ||||
| 
 | ||||
|         byte[] pdfBytes = | ||||
|                 FileToPdf.convertHtmlToPdf( | ||||
| @ -89,7 +90,7 @@ public class ConvertMarkdownToPdf { | ||||
|                         htmlContent.getBytes(), | ||||
|                         "converted.html", | ||||
|                         bookAndHtmlFormatsInstalled, | ||||
| 						disableSanitize); | ||||
|                         disableSanitize); | ||||
|         pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes); | ||||
|         String outputFilename = | ||||
|                 originalFilename.replaceFirst("[.][^.]+$", "") | ||||
|  | ||||
| @ -1,10 +1,10 @@ | ||||
| package stirling.software.SPDF.service; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.util.Arrays; | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
| import java.util.stream.Collectors; | ||||
| import java.util.Arrays; | ||||
| 
 | ||||
| import org.springframework.core.io.Resource; | ||||
| import org.springframework.core.io.support.PathMatchingResourcePatternResolver; | ||||
| @ -21,8 +21,7 @@ public class LanguageService { | ||||
|     private final PathMatchingResourcePatternResolver resourcePatternResolver = | ||||
|             new PathMatchingResourcePatternResolver(); | ||||
| 
 | ||||
|     public LanguageService( | ||||
|             ApplicationProperties applicationProperties) { | ||||
|     public LanguageService(ApplicationProperties applicationProperties) { | ||||
|         this.applicationProperties = applicationProperties; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -27,7 +27,7 @@ public class FileToPdf { | ||||
|             byte[] fileBytes, | ||||
|             String fileName, | ||||
|             boolean htmlFormatsInstalled, | ||||
| 			boolean disableSanitize) | ||||
|             boolean disableSanitize) | ||||
|             throws IOException, InterruptedException { | ||||
| 
 | ||||
|         Path tempOutputFile = Files.createTempFile("output_", ".pdf"); | ||||
| @ -36,7 +36,9 @@ public class FileToPdf { | ||||
|         try { | ||||
|             if (fileName.endsWith(".html")) { | ||||
|                 tempInputFile = Files.createTempFile("input_", ".html"); | ||||
|                 String sanitizedHtml = sanitizeHtmlContent(new String(fileBytes, StandardCharsets.UTF_8), disableSanitize); | ||||
|                 String sanitizedHtml = | ||||
|                         sanitizeHtmlContent( | ||||
|                                 new String(fileBytes, StandardCharsets.UTF_8), disableSanitize); | ||||
|                 Files.write(tempInputFile, sanitizedHtml.getBytes(StandardCharsets.UTF_8)); | ||||
|             } else if (fileName.endsWith(".zip")) { | ||||
|                 tempInputFile = Files.createTempFile("input_", ".zip"); | ||||
| @ -93,7 +95,8 @@ public class FileToPdf { | ||||
|         return (!disableSanitize) ? CustomHtmlSanitizer.sanitize(htmlContent) : htmlContent; | ||||
|     } | ||||
| 
 | ||||
|     private static void sanitizeHtmlFilesInZip(Path zipFilePath, boolean disableSanitize) throws IOException { | ||||
|     private static void sanitizeHtmlFilesInZip(Path zipFilePath, boolean disableSanitize) | ||||
|             throws IOException { | ||||
|         Path tempUnzippedDir = Files.createTempDirectory("unzipped_"); | ||||
|         try (ZipInputStream zipIn = | ||||
|                 ZipSecurity.createHardenedInputStream( | ||||
|  | ||||
| @ -587,9 +587,7 @@ public class GeneralUtils { | ||||
|             for (byte b : hash) { | ||||
|                 fingerprint.append(String.format("%02x", b)); | ||||
|             } | ||||
| 
 | ||||
|             return fingerprint.toString(); | ||||
| 
 | ||||
|         } catch (Exception e) { | ||||
|             return "GenericID"; | ||||
|         } | ||||
|  | ||||
| @ -84,12 +84,6 @@ main() { | ||||
| 	SECONDS=0 | ||||
| 
 | ||||
|     cd "$PROJECT_ROOT" | ||||
|      | ||||
|     # Run the gradlew build command and check if it fails | ||||
|     if [[ "$VERIFICATION" == "true" ]]; then | ||||
|         ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256 --refresh-dependencies help | ||||
|         ./gradlew clean dependencies buildEnvironment spotlessApply --write-verification-metadata sha256,pgp --refresh-keys --export-keys --refresh-dependencies help | ||||
|     fi | ||||
|     | ||||
|     export DOCKER_ENABLE_SECURITY=false | ||||
|     # Run the gradlew build command and check if it fails | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user