diff --git a/README.md b/README.md index e34511b86..1f865c56f 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ cd spring-petclinic java -jar target/*.jar ``` +(On Windows, or if your shell doesn't expand the glob, you might need to specify the JAR file name explicitly on the command line at the end there.) + You can then access the Petclinic at . petclinic-screenshot @@ -52,13 +54,13 @@ A similar setup is provided for MySQL and PostgreSQL if a persistent database co You can start MySQL or PostgreSQL locally with whatever installer works for your OS or use docker: ```bash -docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.1 +docker run -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:9.2 ``` or ```bash -docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:17.0 +docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES_DB=petclinic -p 5432:5432 postgres:17.5 ``` Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt) diff --git a/build.gradle b/build.gradle index a83b254e9..ddde947b5 100644 --- a/build.gradle +++ b/build.gradle @@ -19,7 +19,7 @@ apply plugin: 'io.spring.javaformat' gradle.startParameter.excludedTaskNames += [ "checkFormatAot", "checkFormatAotTest" ] group = 'org.springframework.samples' -version = '3.4.0' +version = '3.5.0' java { sourceCompatibility = JavaVersion.VERSION_17 @@ -37,16 +37,13 @@ repositories { mavenCentral() } -ext.checkstyleVersion = "10.20.1" -ext.springJavaformatCheckstyleVersion = "0.0.43" -ext.webjarsLocatorLiteVersion = "1.0.1" +ext.checkstyleVersion = "10.25.0" +ext.springJavaformatCheckstyleVersion = "0.0.46" +ext.webjarsLocatorLiteVersion = "1.1.0" ext.webjarsFontawesomeVersion = "4.7.0" -ext.webjarsBootstrapVersion = "5.3.3" +ext.webjarsBootstrapVersion = "5.3.6" dependencies { - // Workaround for AOT issue (https://github.com/spring-projects/spring-framework/pull/33949) --> - implementation 'io.projectreactor:reactor-core' - implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' diff --git a/docker-compose.yml b/docker-compose.yml index 47579bbaf..e066d1237 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: mysql: - image: mysql:9.1 + image: mysql:9.2 ports: - "3306:3306" environment: @@ -12,7 +12,7 @@ services: volumes: - "./conf.d:/etc/mysql/conf.d:ro" postgres: - image: postgres:17.0 + image: postgres:17.5 ports: - "5432:5432" environment: diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b953..1b33c55ba 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72b8..ca025c83a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6..23d15a936 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -115,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -206,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a2183..db3a6ac20 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/k8s/db.yml b/k8s/db.yml index c230ddba2..29d5ea4f8 100644 --- a/k8s/db.yml +++ b/k8s/db.yml @@ -41,7 +41,7 @@ spec: app: demo-db spec: containers: - - image: postgres:17 + - image: postgres:17.5 name: postgresql env: - name: POSTGRES_USER diff --git a/pom.xml b/pom.xml index 27fb9a6bd..8576c22ba 100644 --- a/pom.xml +++ b/pom.xml @@ -5,13 +5,13 @@ org.springframework.boot spring-boot-starter-parent - 3.4.2 + 3.5.0 org.springframework.samples spring-petclinic - 3.4.0-SNAPSHOT + 3.5.0-SNAPSHOT petclinic @@ -21,22 +21,21 @@ 17 UTF-8 UTF-8 - + 2024-11-28T14:37:52Z - 1.0.1 - 5.3.3 + 1.1.0 + 5.3.6 4.7.0 - 10.20.1 - 0.8.12 - 0.2.29 + 10.25.0 + 0.8.13 + 0.3.4 1.0.0 3.6.0 0.0.11 - 0.0.43 + 0.0.46 @@ -71,11 +70,6 @@ spring-boot-starter-test test - - - io.projectreactor - reactor-core - @@ -168,9 +162,7 @@ - This build requires at least Java ${java.version}, - update your JVM, and - run the build again + This build requires at least Java ${java.version}, update your JVM, and run the build again ${java.version} diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java index ac6e15030..fa0630995 100644 --- a/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java +++ b/src/main/java/org/springframework/samples/petclinic/PetClinicApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import org.springframework.context.annotation.ImportRuntimeHints; * PetClinic Spring Boot Application. * * @author Dave Syer - * */ @SpringBootApplication @ImportRuntimeHints(PetClinicRuntimeHints.class) diff --git a/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java b/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java index 2975923d7..4999f4c3c 100644 --- a/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java +++ b/src/main/java/org/springframework/samples/petclinic/PetClinicRuntimeHints.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java index 3038bce3a..6babed56d 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/BaseEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java index 012e8c4be..d4be03e9e 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java +++ b/src/main/java/org/springframework/samples/petclinic/model/NamedEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/model/Person.java b/src/main/java/org/springframework/samples/petclinic/model/Person.java index 7c3d81a84..7ee1f0397 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/Person.java +++ b/src/main/java/org/springframework/samples/petclinic/model/Person.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/model/package-info.java b/src/main/java/org/springframework/samples/petclinic/model/package-info.java index 37d6295e8..e8982c3d4 100644 --- a/src/main/java/org/springframework/samples/petclinic/model/package-info.java +++ b/src/main/java/org/springframework/samples/petclinic/model/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java index 675b2140e..63e3acc7a 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Owner.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Owner.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ public class Owner extends Person { @Column(name = "telephone") @NotBlank - @Pattern(regexp = "\\d{10}", message = "Telephone must be a 10-digit number") + @Pattern(regexp = "\\d{10}", message = "{telephone.invalid}") private String telephone; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java index fa3506456..1348457ee 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java index ab594841c..9384b318e 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; /** - * Repository class for Owner domain objects All method names are compliant + * Repository class for Owner domain objects. All method names are compliant * with Spring Data naming conventions so this interface can easily be extended for Spring * Data. See: * https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation @@ -38,13 +38,6 @@ import org.springframework.data.jpa.repository.Query; */ public interface OwnerRepository extends JpaRepository { - /** - * Retrieve all {@link PetType}s from the data store. - * @return a Collection of {@link PetType}s. - */ - @Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name") - List findPetTypes(); - /** * Retrieve {@link Owner}s from the data store by last name, returning all owners * whose last name starts with the given name. @@ -69,9 +62,4 @@ public interface OwnerRepository extends JpaRepository { */ Optional findById(@Nonnull Integer id); - /** - * Returns all the owners from data store - **/ - Page findAll(Pageable pageable); - } diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java index 1fdc77cec..1945f9b67 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Pet.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Pet.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java index 3234e39d6..56707d99f 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetController.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,13 +48,16 @@ class PetController { private final OwnerRepository owners; - public PetController(OwnerRepository owners) { + private final PetTypeRepository types; + + public PetController(OwnerRepository owners, PetTypeRepository types) { this.owners = owners; + this.types = types; } @ModelAttribute("types") public Collection populatePetTypes() { - return this.owners.findPetTypes(); + return this.types.findPetTypes(); } @ModelAttribute("owner") diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetType.java b/src/main/java/org/springframework/samples/petclinic/owner/PetType.java index eeea6a758..e7d63d1aa 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetType.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetType.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java b/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java index 4fa18da91..7ddc6e923 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetTypeFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ */ package org.springframework.samples.petclinic.owner; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.format.Formatter; import org.springframework.stereotype.Component; @@ -36,11 +35,10 @@ import java.util.Locale; @Component public class PetTypeFormatter implements Formatter { - private final OwnerRepository owners; + private final PetTypeRepository types; - @Autowired - public PetTypeFormatter(OwnerRepository owners) { - this.owners = owners; + public PetTypeFormatter(PetTypeRepository types) { + this.types = types; } @Override @@ -50,7 +48,7 @@ public class PetTypeFormatter implements Formatter { @Override public PetType parse(String text, Locale locale) throws ParseException { - Collection findPetTypes = this.owners.findPetTypes(); + Collection findPetTypes = this.types.findPetTypes(); for (PetType type : findPetTypes) { if (type.getName().equals(text)) { return type; diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java b/src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java new file mode 100644 index 000000000..0412948eb --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetTypeRepository.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.samples.petclinic.owner; + +import java.util.List; +import java.util.Optional; + +import jakarta.annotation.Nonnull; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +/** + * Repository class for PetType domain objects. + * + * @author Patrick Baumgartner + */ + +public interface PetTypeRepository extends JpaRepository { + + /** + * Retrieve all {@link PetType}s from the data store. + * @return a Collection of {@link PetType}s. + */ + @Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name") + List findPetTypes(); + +} diff --git a/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java b/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java index 6422aa8db..dfe5304d5 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/PetValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/owner/Visit.java b/src/main/java/org/springframework/samples/petclinic/owner/Visit.java index 35569bdaa..085cd2849 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/Visit.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/Visit.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java index b546f609a..21c1823a5 100644 --- a/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java +++ b/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java index f90679094..1382f3aea 100644 --- a/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java +++ b/src/main/java/org/springframework/samples/petclinic/system/CacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/system/CrashController.java b/src/main/java/org/springframework/samples/petclinic/system/CrashController.java index 2b28600fd..0081a560b 100644 --- a/src/main/java/org/springframework/samples/petclinic/system/CrashController.java +++ b/src/main/java/org/springframework/samples/petclinic/system/CrashController.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/system/WebConfiguration.java b/src/main/java/org/springframework/samples/petclinic/system/WebConfiguration.java new file mode 100644 index 000000000..1ef32e4dc --- /dev/null +++ b/src/main/java/org/springframework/samples/petclinic/system/WebConfiguration.java @@ -0,0 +1,60 @@ +package org.springframework.samples.petclinic.system; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; + +import java.util.Locale; + +/** + * Configures internationalization (i18n) support for the application. + * + *

+ * Handles loading language-specific messages, tracking the user's language, and allowing + * language changes via the URL parameter (e.g., ?lang=de). + *

+ * + * @author Anuj Ashok Potdar + */ +@Configuration +@SuppressWarnings("unused") +public class WebConfiguration implements WebMvcConfigurer { + + /** + * Uses session storage to remember the user’s language setting across requests. + * Defaults to English if nothing is specified. + * @return session-based {@link LocaleResolver} + */ + @Bean + public LocaleResolver localeResolver() { + SessionLocaleResolver resolver = new SessionLocaleResolver(); + resolver.setDefaultLocale(Locale.ENGLISH); + return resolver; + } + + /** + * Allows the app to switch languages using a URL parameter like + * ?lang=es. + * @return a {@link LocaleChangeInterceptor} that handles the change + */ + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); + interceptor.setParamName("lang"); + return interceptor; + } + + /** + * Registers the locale change interceptor so it can run on each request. + * @param registry where interceptors are added + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(localeChangeInterceptor()); + } + +} diff --git a/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java b/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java index 9224015bc..d77a35ea8 100644 --- a/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java +++ b/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java b/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java index b0b6315fc..184bf5e6b 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Specialty.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java index 00c7ec1c8..fb2bd71ee 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vet.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vet.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java index 29fcecc7a..89ad9bc41 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetController.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetController.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java index 8b9e0823c..dbf68d029 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/java/org/springframework/samples/petclinic/vet/Vets.java b/src/main/java/org/springframework/samples/petclinic/vet/Vets.java index 9b672d25e..634cad773 100644 --- a/src/main/java/org/springframework/samples/petclinic/vet/Vets.java +++ b/src/main/java/org/springframework/samples/petclinic/vet/Vets.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/main/resources/application-postgres.properties b/src/main/resources/application-postgres.properties index 60889b43c..b265d7e5b 100644 --- a/src/main/resources/application-postgres.properties +++ b/src/main/resources/application-postgres.properties @@ -1,3 +1,4 @@ +# database init, supports postgres too database=postgres spring.datasource.url=${POSTGRES_URL:jdbc:postgresql://localhost/petclinic} spring.datasource.username=${POSTGRES_USER:petclinic} diff --git a/src/main/resources/messages/messages.properties b/src/main/resources/messages/messages.properties index 173417a10..7b8b5dfd8 100644 --- a/src/main/resources/messages/messages.properties +++ b/src/main/resources/messages/messages.properties @@ -6,3 +6,43 @@ nonNumeric=must be all numeric duplicateFormSubmission=Duplicate form submission is not allowed typeMismatch.date=invalid date typeMismatch.birthDate=invalid date +owner=Owner +firstName=First Name +lastName=Last Name +address=Address +city=City +telephone=Telephone +owners=Owners +addOwner=Add Owner +findOwner=Find Owner +findOwners=Find Owners +updateOwner=Update Owner +vets=Veterinarians +name=Name +specialties=Specialties +none=none +pages=pages +first=First +next=Next +previous=Previous +last=Last +somethingHappened=Something happened... +pets=Pets +home=Home +error=Error +telephone.invalid=Telephone must be a 10-digit number +layoutTitle=PetClinic :: a Spring Framework demonstration +pet=Pet +birthDate=Birth Date +type=Type +previousVisits=Previous Visits +date=Date +description=Description +new=New +addVisit=Add Visit +editPet=Edit Pet +ownerInformation=Owner Information +visitDate=Visit Date +editOwner=Edit Owner +addNewPet=Add New Pet +petsAndVisits=Pets and Visits diff --git a/src/main/resources/messages/messages_de.properties b/src/main/resources/messages/messages_de.properties index 4828d85fe..8d208410c 100644 --- a/src/main/resources/messages/messages_de.properties +++ b/src/main/resources/messages/messages_de.properties @@ -6,4 +6,43 @@ nonNumeric=darf nur numerisch sein duplicateFormSubmission=Wiederholtes Absenden des Formulars ist nicht erlaubt typeMismatch.date=ung�ltiges Datum typeMismatch.birthDate=ung�ltiges Datum - +owner=Besitzer +firstName=Vorname +lastName=Nachname +address=Adresse +city=Stadt +telephone=Telefon +owners=Besitzer +addOwner=Besitzer hinzufügen +findOwner=Besitzer finden +findOwners=Besitzer suchen +updateOwner=Besitzer aktualisieren +vets=Tierärzte +name=Name +specialties=Fachgebiete +none=keine +pages=Seiten +first=Erste +next=Nächste +previous=Vorherige +last=Letzte +somethingHappened=Etwas ist passiert... +pets=Haustiere +home=Startseite +error=Fehler +telephone.invalid=Telefonnummer muss aus 10 Ziffern bestehen +layoutTitle=PetClinic :: eine Demonstration des Spring Frameworks +pet=Haustier +birthDate=Geburtsdatum +type=Typ +previousVisits=Frühere Besuche +date=Datum +description=Beschreibung +new=Neu +addVisit=Besuch hinzufügen +editPet=Haustier bearbeiten +ownerInformation=Besitzerinformationen +visitDate=Besuchsdatum +editOwner=Besitzer bearbeiten +addNewPet=Neues Haustier hinzufügen +petsAndVisits=Haustiere und Besuche diff --git a/src/main/resources/messages/messages_es.properties b/src/main/resources/messages/messages_es.properties index 116016fa3..91d5c9ebd 100644 --- a/src/main/resources/messages/messages_es.properties +++ b/src/main/resources/messages/messages_es.properties @@ -6,4 +6,43 @@ nonNumeric=Sólo debe contener numeros duplicateFormSubmission=No se permite el envío de formularios duplicados typeMismatch.date=Fecha invalida typeMismatch.birthDate=Fecha invalida - +owner=Propietario +firstName=Nombre +lastName=Apellido +address=Dirección +city=Ciudad +telephone=Teléfono +owners=Propietarios +addOwner=Añadir propietario +findOwner=Buscar propietario +findOwners=Buscar propietarios +updateOwner=Actualizar propietario +vets=Veterinarios +name=Nombre +specialties=Especialidades +none=ninguno +pages=páginas +first=Primero +next=Siguiente +previous=Anterior +last=Último +somethingHappened=Algo pasó... +pets=Mascotas +home=Inicio +error=Error +telephone.invalid=El número de teléfono debe tener 10 dígitos +layoutTitle=PetClinic :: una demostración de Spring Framework +pet=Mascota +birthDate=Fecha de nacimiento +type=Tipo +previousVisits=Visitas anteriores +date=Fecha +description=Descripción +new=Nuevo +addVisit=Agregar visita +editPet=Editar mascota +ownerInformation=Información del propietario +visitDate=Fecha de visita +editOwner=Editar propietario +addNewPet=Agregar nueva mascota +petsAndVisits=Mascotas y visitas diff --git a/src/main/resources/messages/messages_fa.properties b/src/main/resources/messages/messages_fa.properties index a68a21c77..2566b8949 100644 --- a/src/main/resources/messages/messages_fa.properties +++ b/src/main/resources/messages/messages_fa.properties @@ -6,4 +6,43 @@ nonNumeric=باید عددی باشد duplicateFormSubmission=ارسال تکراری فرم مجاز نیست typeMismatch.date=تاریخ نامعتبر typeMismatch.birthDate=تاریخ تولد نامعتبر - +owner=مالک +firstName=نام +lastName=نام خانوادگی +address=آدرس +city=شهر +telephone=تلفن +owners=مالکان +addOwner=افزودن مالک +findOwner=یافتن مالک +findOwners=یافتن مالکان +updateOwner=ویرایش مالک +vets=دامپزشکان +name=نام +specialties=تخصص‌ها +none=هیچ‌کدام +pages=صفحات +first=اول +next=بعدی +previous=قبلی +last=آخر +somethingHappened=مشکلی پیش آمد... +pets=حیوانات خانگی +home=خانه +error=خطا +telephone.invalid=شماره تلفن باید ۱۰ رقمی باشد +layoutTitle=PetClinic :: یک نمایش از Spring Framework +pet=حیوان خانگی +birthDate=تاریخ تولد +type=نوع +previousVisits=ویزیت‌های قبلی +date=تاریخ +description=توضیحات +new=جدید +addVisit=افزودن ویزیت +editPet=ویرایش حیوان خانگی +ownerInformation=اطلاعات مالک +visitDate=تاریخ ویزیت +editOwner=ویرایش مالک +addNewPet=افزودن حیوان خانگی جدید +petsAndVisits=حیوانات و ویزیت‌ها diff --git a/src/main/resources/messages/messages_ko.properties b/src/main/resources/messages/messages_ko.properties index 6cd27bfff..ca3ae5a05 100644 --- a/src/main/resources/messages/messages_ko.properties +++ b/src/main/resources/messages/messages_ko.properties @@ -6,3 +6,43 @@ nonNumeric=모두 숫자로 입력해야 합니다 duplicateFormSubmission=중복 제출은 허용되지 않습니다 typeMismatch.date=잘못된 날짜입니다 typeMismatch.birthDate=잘못된 날짜입니다 +owner=소유자 +firstName=이름 +lastName=성 +address=주소 +city=도시 +telephone=전화번호 +owners=소유자 목록 +addOwner=소유자 추가 +findOwner=소유자 찾기 +findOwners=소유자들 찾기 +updateOwner=소유자 수정 +vets=수의사 +name=이름 +specialties=전문 분야 +none=없음 +pages=페이지 +first=첫 번째 +next=다음 +previous=이전 +last=마지막 +somethingHappened=문제가 발생했습니다... +pets=반려동물 +home=홈 +error=오류 +telephone.invalid=전화번호는 10자리 숫자여야 합니다 +layoutTitle=PetClinic :: Spring Framework 데모 +pet=반려동물 +birthDate=생년월일 +type=종류 +previousVisits=이전 방문 +date=날짜 +description=설명 +new=새로운 +addVisit=방문 추가 +editPet=반려동물 수정 +ownerInformation=소유자 정보 +visitDate=방문 날짜 +editOwner=소유자 수정 +addNewPet=새 반려동물 추가 +petsAndVisits=반려동물 및 방문 diff --git a/src/main/resources/messages/messages_pt.properties b/src/main/resources/messages/messages_pt.properties index e9bc35a39..03f5cd531 100644 --- a/src/main/resources/messages/messages_pt.properties +++ b/src/main/resources/messages/messages_pt.properties @@ -6,3 +6,43 @@ nonNumeric=Deve ser tudo numerico duplicateFormSubmission=O envio duplicado de formulario nao e permitido typeMismatch.date=Data invalida typeMismatch.birthDate=Data de nascimento invalida +owner=Proprietário +firstName=Primeiro Nome +lastName=Sobrenome +address=Endereço +city=Cidade +telephone=Telefone +owners=Proprietários +addOwner=Adicionar proprietário +findOwner=Encontrar proprietário +findOwners=Encontrar proprietários +updateOwner=Atualizar proprietário +vets=Veterinários +name=Nome +specialties=Especialidades +none=nenhum +pages=páginas +first=Primeiro +next=Próximo +previous=Anterior +last=Último +somethingHappened=Algo aconteceu... +pets=Animais de estimação +home=Início +error=Erro +telephone.invalid=O número de telefone deve conter 10 dígitos +layoutTitle=PetClinic :: uma demonstração do Spring Framework +pet=Animal de estimação +birthDate=Data de nascimento +type=Tipo +previousVisits=Visitas anteriores +date=Data +description=Descrição +new=Novo +addVisit=Adicionar visita +editPet=Editar animal +ownerInformation=Informações do proprietário +visitDate=Data da visita +editOwner=Editar proprietário +addNewPet=Adicionar novo animal +petsAndVisits=Animais e visitas diff --git a/src/main/resources/messages/messages_ru.properties b/src/main/resources/messages/messages_ru.properties index 7e8d54d2d..b9ed998be 100644 --- a/src/main/resources/messages/messages_ru.properties +++ b/src/main/resources/messages/messages_ru.properties @@ -6,4 +6,43 @@ nonNumeric=должно быть все числовое значение duplicateFormSubmission=Дублирование формы не допускается typeMismatch.date=неправильная даные typeMismatch.birthDate=неправильная дата - +owner=Владелец +firstName=Имя +lastName=Фамилия +address=Адрес +city=Город +telephone=Телефон +owners=Владельцы +addOwner=Добавить владельца +findOwner=Найти владельца +findOwners=Найти владельцев +updateOwner=Обновить владельца +vets=Ветеринары +name=Имя +specialties=Специальности +none=нет +pages=страницы +first=Первый +next=Следующий +previous=Предыдущий +last=Последний +somethingHappened=Что-то пошло не так... +pets=Питомцы +home=Главная +error=Ошибка +telephone.invalid=Телефон должен содержать 10 цифр +layoutTitle=PetClinic :: демонстрация Spring Framework +pet=Питомец +birthDate=Дата рождения +type=Тип +previousVisits=Предыдущие визиты +date=Дата +description=Описание +new=Новый +addVisit=Добавить визит +editPet=Редактировать питомца +ownerInformation=Информация о владельце +visitDate=Дата визита +editOwner=Редактировать владельца +addNewPet=Добавить нового питомца +petsAndVisits=Питомцы и визиты diff --git a/src/main/resources/messages/messages_tr.properties b/src/main/resources/messages/messages_tr.properties index 1020566aa..ff9ffdabd 100644 --- a/src/main/resources/messages/messages_tr.properties +++ b/src/main/resources/messages/messages_tr.properties @@ -6,4 +6,43 @@ nonNumeric=sadece sayısal olmalıdır duplicateFormSubmission=Formun tekrar gönderilmesine izin verilmez typeMismatch.date=geçersiz tarih typeMismatch.birthDate=geçersiz tarih - +owner=Sahip +firstName=Ad +lastName=Soyad +address=Adres +city=Şehir +telephone=Telefon +owners=Sahipler +addOwner=Sahip Ekle +findOwner=Sahip Bul +findOwners=Sahipleri Bul +updateOwner=Sahip Güncelle +vets=Veterinerler +name=İsim +specialties=Uzmanlıklar +none=yok +pages=sayfalar +first=İlk +next=Sonraki +previous=Önceki +last=Son +somethingHappened=Bir şey oldu... +pets=Evcil Hayvanlar +home=Ana Sayfa +error=Hata +telephone.invalid=Telefon numarası 10 basamaklı olmalıdır +layoutTitle=PetClinic :: bir Spring Framework demosu +pet=Evcil Hayvan +birthDate=Doğum Tarihi +type=Tür +previousVisits=Önceki Ziyaretler +date=Tarih +description=Açıklama +new=Yeni +addVisit=Ziyaret Ekle +editPet=Evcil Hayvanı Düzenle +ownerInformation=Sahip Bilgileri +visitDate=Ziyaret Tarihi +editOwner=Sahibi Düzenle +addNewPet=Yeni Evcil Hayvan Ekle +petsAndVisits=Evcil Hayvanlar ve Ziyaretler diff --git a/src/main/resources/static/resources/css/petclinic.css b/src/main/resources/static/resources/css/petclinic.css index bbf3f0dbb..22e3e50ea 100644 --- a/src/main/resources/static/resources/css/petclinic.css +++ b/src/main/resources/static/resources/css/petclinic.css @@ -12,8 +12,8 @@ * limitations under the License. */ /*! - * Bootstrap v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors + * Bootstrap v5.3.6 (https://getbootstrap.com/) + * Copyright 2011-2025 The Bootstrap Authors * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ :root, @@ -456,8 +456,8 @@ legend { width: 100%; padding: 0; margin-bottom: 0.5rem; - font-size: calc(1.275rem + 0.3vw); - line-height: inherit; } + line-height: inherit; + font-size: calc(1.275rem + 0.3vw); } @media (min-width: 1200px) { legend { font-size: 1.5rem; } } @@ -519,44 +519,44 @@ progress { font-weight: 300; } .display-1 { - font-size: calc(1.625rem + 4.5vw); font-weight: 300; - line-height: 1.2; } + line-height: 1.2; + font-size: calc(1.625rem + 4.5vw); } @media (min-width: 1200px) { .display-1 { font-size: 5rem; } } .display-2 { - font-size: calc(1.575rem + 3.9vw); font-weight: 300; - line-height: 1.2; } + line-height: 1.2; + font-size: calc(1.575rem + 3.9vw); } @media (min-width: 1200px) { .display-2 { font-size: 4.5rem; } } .display-3 { - font-size: calc(1.525rem + 3.3vw); font-weight: 300; - line-height: 1.2; } + line-height: 1.2; + font-size: calc(1.525rem + 3.3vw); } @media (min-width: 1200px) { .display-3 { font-size: 4rem; } } .display-4 { - font-size: calc(1.475rem + 2.7vw); font-weight: 300; - line-height: 1.2; } + line-height: 1.2; + font-size: calc(1.475rem + 2.7vw); } @media (min-width: 1200px) { .display-4 { font-size: 3.5rem; } } .display-5 { - font-size: calc(1.425rem + 2.1vw); font-weight: 300; - line-height: 1.2; } + line-height: 1.2; + font-size: calc(1.425rem + 2.1vw); } @media (min-width: 1200px) { .display-5 { font-size: 3rem; } } .display-6 { - font-size: calc(1.375rem + 1.5vw); font-weight: 300; - line-height: 1.2; } + line-height: 1.2; + font-size: calc(1.375rem + 1.5vw); } @media (min-width: 1200px) { .display-6 { font-size: 2.5rem; } } @@ -674,7 +674,7 @@ progress { margin-top: var(--bs-gutter-y); } .col { - flex: 1 0 0%; } + flex: 1 0 0; } .row-cols-auto > * { flex: 0 0 auto; @@ -839,7 +839,7 @@ progress { @media (min-width: 576px) { .col-sm { - flex: 1 0 0%; } + flex: 1 0 0; } .row-cols-sm-auto > * { flex: 0 0 auto; width: auto; } @@ -963,7 +963,7 @@ progress { @media (min-width: 768px) { .col-md { - flex: 1 0 0%; } + flex: 1 0 0; } .row-cols-md-auto > * { flex: 0 0 auto; width: auto; } @@ -1087,7 +1087,7 @@ progress { @media (min-width: 992px) { .col-lg { - flex: 1 0 0%; } + flex: 1 0 0; } .row-cols-lg-auto > * { flex: 0 0 auto; width: auto; } @@ -1211,7 +1211,7 @@ progress { @media (min-width: 1200px) { .col-xl { - flex: 1 0 0%; } + flex: 1 0 0; } .row-cols-xl-auto > * { flex: 0 0 auto; width: auto; } @@ -1335,7 +1335,7 @@ progress { @media (min-width: 1400px) { .col-xxl { - flex: 1 0 0%; } + flex: 1 0 0; } .row-cols-xxl-auto > * { flex: 0 0 auto; width: auto; } @@ -1899,9 +1899,9 @@ textarea.form-control-lg { .form-check-input:checked { background-color: #0d6efd; border-color: #0d6efd; } - .form-check-input[type="checkbox"]:checked { + .form-check-input:checked[type="checkbox"] { --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } - .form-check-input[type="radio"]:checked { + .form-check-input:checked[type="radio"] { --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-input[type="checkbox"]:indeterminate { background-color: #0d6efd; @@ -2033,9 +2033,11 @@ textarea.form-control-lg { top: 0; left: 0; z-index: 2; + max-width: 100%; height: 100%; padding: 1rem 0.75rem; overflow: hidden; + color: rgba(var(--bs-body-color-rgb), 0.65); text-align: start; text-overflow: ellipsis; white-space: nowrap; @@ -2063,35 +2065,31 @@ textarea.form-control-lg { padding-bottom: 0.625rem; } .form-floating > .form-select { padding-top: 1.625rem; - padding-bottom: 0.625rem; } + padding-bottom: 0.625rem; + padding-left: 0.75rem; } .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-control-plaintext ~ label, .form-floating > .form-select ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } - .form-floating > .form-control:focus ~ label::after, - .form-floating > .form-control:not(:placeholder-shown) ~ label::after, - .form-floating > .form-control-plaintext ~ label::after, - .form-floating > .form-select ~ label::after { - position: absolute; - inset: 1rem 0.375rem; - z-index: -1; - height: 1.5em; - content: ""; - background-color: var(--bs-body-bg); - border-radius: var(--bs-border-radius); } .form-floating > .form-control:-webkit-autofill ~ label { - color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } + .form-floating > textarea:focus ~ label::after, + .form-floating > textarea:not(:placeholder-shown) ~ label::after { + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); } + .form-floating > textarea:disabled ~ label::after { + background-color: var(--bs-secondary-bg); } .form-floating > .form-control-plaintext ~ label { border-width: var(--bs-border-width) 0; } .form-floating > :disabled ~ label, .form-floating > .form-control:disabled ~ label { color: #6c757d; } - .form-floating > :disabled ~ label::after, - .form-floating > .form-control:disabled ~ label::after { - background-color: var(--bs-secondary-bg); } .input-group { position: relative; @@ -2165,7 +2163,7 @@ textarea.form-control-lg { border-bottom-right-radius: 0; } .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { - margin-left: calc(var(--bs-border-width) * -1); + margin-left: calc(-1 * var(--bs-border-width)); border-top-left-radius: 0; border-bottom-left-radius: 0; } @@ -2203,7 +2201,7 @@ textarea.form-control-lg { .was-validated .form-control:valid, .form-control.is-valid { border-color: var(--bs-form-valid-border-color); padding-right: calc(1.5em + 0.75rem); - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right calc(0.375em + 0.1875rem) center; background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } @@ -2217,8 +2215,8 @@ textarea.form-control-lg { .was-validated .form-select:valid, .form-select.is-valid { border-color: var(--bs-form-valid-border-color); } - .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select[size="1"]:valid:not([multiple]), .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid[size="1"]:not([multiple]) { - --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e"); padding-right: 4.125rem; background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } @@ -2241,9 +2239,9 @@ textarea.form-control-lg { .form-check-inline .form-check-input ~ .valid-feedback { margin-left: .5em; } -.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control.is-valid:not(:focus), .was-validated .input-group > .form-select:not(:focus):valid, -.input-group > .form-select.is-valid:not(:focus), .was-validated .input-group > .form-floating:not(:focus-within):valid, -.input-group > .form-floating.is-valid:not(:focus-within) { +.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid, .was-validated .input-group > .form-select:not(:focus):valid, +.input-group > .form-select:not(:focus).is-valid, .was-validated .input-group > .form-floating:not(:focus-within):valid, +.input-group > .form-floating:not(:focus-within).is-valid { z-index: 3; } .invalid-feedback { @@ -2289,7 +2287,7 @@ textarea.form-control-lg { .was-validated .form-select:invalid, .form-select.is-invalid { border-color: var(--bs-form-invalid-border-color); } - .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select[size="1"]:invalid:not([multiple]), .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid[size="1"]:not([multiple]) { + .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); padding-right: 4.125rem; background-position: right 0.75rem center, center right 2.25rem; @@ -2313,9 +2311,9 @@ textarea.form-control-lg { .form-check-inline .form-check-input ~ .invalid-feedback { margin-left: .5em; } -.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control.is-invalid:not(:focus), .was-validated .input-group > .form-select:not(:focus):invalid, -.input-group > .form-select.is-invalid:not(:focus), .was-validated .input-group > .form-floating:not(:focus-within):invalid, -.input-group > .form-floating.is-invalid:not(:focus-within) { +.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid, .was-validated .input-group > .form-select:not(:focus):invalid, +.input-group > .form-select:not(:focus).is-invalid, .was-validated .input-group > .form-floating:not(:focus-within):invalid, +.input-group > .form-floating:not(:focus-within).is-invalid { z-index: 4; } .btn { @@ -3007,7 +3005,7 @@ textarea.form-control-lg { border-radius: var(--bs-border-radius); } .btn-group > :not(.btn-check:first-child) + .btn, .btn-group > .btn-group:not(:first-child) { - margin-left: calc(var(--bs-border-width) * -1); } + margin-left: calc(-1 * var(--bs-border-width)); } .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn.dropdown-toggle-split:first-child, .btn-group > .btn-group:not(:last-child) > .btn { @@ -3044,12 +3042,13 @@ textarea.form-control-lg { width: 100%; } .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { - margin-top: calc(var(--bs-border-width) * -1); } + margin-top: calc(-1 * var(--bs-border-width)); } .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } - .btn-group-vertical > .btn ~ .btn, + .btn-group-vertical > .btn:nth-child(n + 3), + .btn-group-vertical > :not(.btn-check) + .btn, .btn-group-vertical > .btn-group:not(:first-child) > .btn { border-top-left-radius: 0; border-top-right-radius: 0; } @@ -3152,8 +3151,8 @@ textarea.form-control-lg { .nav-justified > .nav-link, .nav-justified .nav-item { - flex-basis: 0; flex-grow: 1; + flex-basis: 0; text-align: center; } .nav-fill .nav-item .nav-link, @@ -3243,8 +3242,8 @@ textarea.form-control-lg { color: var(--bs-navbar-active-color); } .navbar-collapse { - flex-basis: 100%; flex-grow: 1; + flex-basis: 100%; align-items: center; } .navbar-toggler { @@ -3646,7 +3645,7 @@ textarea.form-control-lg { display: flex; flex-flow: row wrap; } .card-group > .card { - flex: 1 0 0%; + flex: 1 0 0; margin-bottom: 0; } .card-group > .card + .card { margin-left: 0; @@ -3654,20 +3653,20 @@ textarea.form-control-lg { .card-group > .card:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-top, - .card-group > .card:not(:last-child) .card-header { + .card-group > .card:not(:last-child) > .card-img-top, + .card-group > .card:not(:last-child) > .card-header { border-top-right-radius: 0; } - .card-group > .card:not(:last-child) .card-img-bottom, - .card-group > .card:not(:last-child) .card-footer { + .card-group > .card:not(:last-child) > .card-img-bottom, + .card-group > .card:not(:last-child) > .card-footer { border-bottom-right-radius: 0; } .card-group > .card:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-top, - .card-group > .card:not(:first-child) .card-header { + .card-group > .card:not(:first-child) > .card-img-top, + .card-group > .card:not(:first-child) > .card-header { border-top-left-radius: 0; } - .card-group > .card:not(:first-child) .card-img-bottom, - .card-group > .card:not(:first-child) .card-footer { + .card-group > .card:not(:first-child) > .card-img-bottom, + .card-group > .card:not(:first-child) > .card-footer { border-bottom-left-radius: 0; } } .accordion { @@ -3682,11 +3681,11 @@ textarea.form-control-lg { --bs-accordion-btn-padding-y: 1rem; --bs-accordion-btn-color: var(--bs-body-color); --bs-accordion-btn-bg: var(--bs-accordion-bg); - --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23212529' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); --bs-accordion-btn-icon-width: 1.25rem; --bs-accordion-btn-icon-transform: rotate(-180deg); --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='M2 5L8 11L14 5'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23052c65' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-accordion-body-padding-x: 1.25rem; --bs-accordion-body-padding-y: 1rem; @@ -3773,14 +3772,14 @@ textarea.form-control-lg { border-top: 0; } .accordion-flush > .accordion-item:last-child { border-bottom: 0; } - .accordion-flush > .accordion-item > .accordion-header .accordion-button, .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed { - border-radius: 0; } - .accordion-flush > .accordion-item > .accordion-collapse { + .accordion-flush > .accordion-item > .accordion-collapse, + .accordion-flush > .accordion-item > .accordion-header .accordion-button, + .accordion-flush > .accordion-item > .accordion-header .accordion-button.collapsed { border-radius: 0; } [data-bs-theme="dark"] .accordion-button::after { - --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); } + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e"); } .breadcrumb { --bs-breadcrumb-padding-x: 0; @@ -3872,7 +3871,7 @@ textarea.form-control-lg { border-color: var(--bs-pagination-disabled-border-color); } .page-item:not(:first-child) .page-link { - margin-left: calc(var(--bs-border-width) * -1); } + margin-left: calc(-1 * var(--bs-border-width)); } .page-item:first-child .page-link { border-top-left-radius: var(--bs-pagination-border-radius); @@ -4002,7 +4001,7 @@ textarea.form-control-lg { @keyframes progress-bar-stripes { 0% { - background-position-x: 1rem; } } + background-position-x: var(--bs-progress-height); } } .progress, .progress-stacked { @@ -4080,19 +4079,6 @@ textarea.form-control-lg { content: counters(section, ".") ". "; counter-increment: section; } -.list-group-item-action { - width: 100%; - color: var(--bs-list-group-action-color); - text-align: inherit; } - .list-group-item-action:hover, .list-group-item-action:focus { - z-index: 1; - color: var(--bs-list-group-action-hover-color); - text-decoration: none; - background-color: var(--bs-list-group-action-hover-bg); } - .list-group-item-action:active { - color: var(--bs-list-group-action-active-color); - background-color: var(--bs-list-group-action-active-bg); } - .list-group-item { position: relative; display: block; @@ -4122,6 +4108,19 @@ textarea.form-control-lg { margin-top: calc(-1 * var(--bs-list-group-border-width)); border-top-width: var(--bs-list-group-border-width); } +.list-group-item-action { + width: 100%; + color: var(--bs-list-group-action-color); + text-align: inherit; } + .list-group-item-action:not(.active):hover, .list-group-item-action:not(.active):focus { + z-index: 1; + color: var(--bs-list-group-action-hover-color); + text-decoration: none; + background-color: var(--bs-list-group-action-hover-bg); } + .list-group-item-action:not(.active):active { + color: var(--bs-list-group-action-active-color); + background-color: var(--bs-list-group-action-active-bg); } + .list-group-horizontal { flex-direction: row; } .list-group-horizontal > .list-group-item:first-child:not(:last-child) { @@ -4334,19 +4333,19 @@ textarea.form-control-lg { .btn-close { --bs-btn-close-color: #000; - --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); + --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e"); --bs-btn-close-opacity: 0.5; --bs-btn-close-hover-opacity: 0.75; --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-btn-close-focus-opacity: 1; --bs-btn-close-disabled-opacity: 0.25; - --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); box-sizing: content-box; width: 1em; height: 1em; padding: 0.25em 0.25em; color: var(--bs-btn-close-color); background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; + filter: var(--bs-btn-close-filter); border: 0; border-radius: 0.375rem; opacity: var(--bs-btn-close-opacity); } @@ -4364,10 +4363,14 @@ textarea.form-control-lg { opacity: var(--bs-btn-close-disabled-opacity); } .btn-close-white { - filter: var(--bs-btn-close-white-filter); } + --bs-btn-close-filter: invert(1) grayscale(100%) brightness(200%); } -[data-bs-theme="dark"] .btn-close { - filter: var(--bs-btn-close-white-filter); } +:root, +[data-bs-theme="light"] { + --bs-btn-close-filter: ; } + +[data-bs-theme="dark"] { + --bs-btn-close-filter: invert(1) grayscale(100%) brightness(200%); } .toast { --bs-toast-zindex: 1090; @@ -4433,7 +4436,7 @@ textarea.form-control-lg { --bs-modal-width: 500px; --bs-modal-padding: 1rem; --bs-modal-margin: 0.5rem; - --bs-modal-color: ; + --bs-modal-color: var(--bs-body-color); --bs-modal-bg: var(--bs-body-bg); --bs-modal-border-color: var(--bs-border-color-translucent); --bs-modal-border-width: var(--bs-border-width); @@ -4467,8 +4470,8 @@ textarea.form-control-lg { margin: var(--bs-modal-margin); pointer-events: none; } .modal.fade .modal-dialog { - transition: transform 0.3s ease-out; - transform: translate(0, -50px); } + transform: translate(0, -50px); + transition: transform 0.3s ease-out; } @media (prefers-reduced-motion: reduce) { .modal.fade .modal-dialog { transition: none; } } @@ -4529,7 +4532,10 @@ textarea.form-control-lg { border-top-right-radius: var(--bs-modal-inner-border-radius); } .modal-header .btn-close { padding: calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5); - margin: calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto; } + margin-top: calc(-.5 * var(--bs-modal-header-padding-y)); + margin-right: calc(-.5 * var(--bs-modal-header-padding-x)); + margin-bottom: calc(-.5 * var(--bs-modal-header-padding-y)); + margin-left: auto; } .modal-title { margin-bottom: 0; @@ -4965,6 +4971,7 @@ textarea.form-control-lg { color: #fff; text-align: center; background: none; + filter: var(--bs-carousel-control-icon-filter); border: 0; opacity: 0.5; transition: opacity 0.15s ease; } @@ -4996,10 +5003,10 @@ textarea.form-control-lg { background-size: 100% 100%; } .carousel-control-prev-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")*/; } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")*/; } .carousel-control-next-icon { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")*/; } + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e") /*rtl:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e")*/; } .carousel-indicators { position: absolute; @@ -5023,7 +5030,7 @@ textarea.form-control-lg { margin-left: 3px; text-indent: -999px; cursor: pointer; - background-color: #fff; + background-color: var(--bs-carousel-indicator-active-bg); background-clip: padding-box; border: 0; border-top: 10px solid transparent; @@ -5043,29 +5050,24 @@ textarea.form-control-lg { left: 15%; padding-top: 1.25rem; padding-bottom: 1.25rem; - color: #fff; + color: var(--bs-carousel-caption-color); text-align: center; } -.carousel-dark .carousel-control-prev-icon, -.carousel-dark .carousel-control-next-icon { - filter: invert(1) grayscale(100); } +.carousel-dark { + --bs-carousel-indicator-active-bg: #000; + --bs-carousel-caption-color: #000; + --bs-carousel-control-icon-filter: invert(1) grayscale(100); } -.carousel-dark .carousel-indicators [data-bs-target] { - background-color: #000; } +:root, +[data-bs-theme="light"] { + --bs-carousel-indicator-active-bg: #fff; + --bs-carousel-caption-color: #fff; + --bs-carousel-control-icon-filter: ; } -.carousel-dark .carousel-caption { - color: #000; } - -[data-bs-theme="dark"] .carousel .carousel-control-prev-icon, -[data-bs-theme="dark"] .carousel .carousel-control-next-icon, .carousel[data-bs-theme="dark"] .carousel-control-prev-icon, -.carousel[data-bs-theme="dark"] .carousel-control-next-icon { - filter: invert(1) grayscale(100); } - -[data-bs-theme="dark"] .carousel .carousel-indicators [data-bs-target], .carousel[data-bs-theme="dark"] .carousel-indicators [data-bs-target] { - background-color: #000; } - -[data-bs-theme="dark"] .carousel .carousel-caption, .carousel[data-bs-theme="dark"] .carousel-caption { - color: #000; } +[data-bs-theme="dark"] { + --bs-carousel-indicator-active-bg: #000; + --bs-carousel-caption-color: #000; + --bs-carousel-control-icon-filter: invert(1) grayscale(100); } .spinner-grow, .spinner-border { @@ -5521,7 +5523,10 @@ textarea.form-control-lg { padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); } .offcanvas-header .btn-close { padding: calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5); - margin: calc(-.5 * var(--bs-offcanvas-padding-y)) calc(-.5 * var(--bs-offcanvas-padding-x)) calc(-.5 * var(--bs-offcanvas-padding-y)) auto; } + margin-top: calc(-.5 * var(--bs-offcanvas-padding-y)); + margin-right: calc(-.5 * var(--bs-offcanvas-padding-x)); + margin-bottom: calc(-.5 * var(--bs-offcanvas-padding-y)); + margin-left: auto; } .offcanvas-title { margin-bottom: 0; @@ -5816,6 +5821,9 @@ textarea.form-control-lg { .visually-hidden:not(caption), .visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { position: absolute !important; } + .visually-hidden *, + .visually-hidden-focusable:not(:focus):not(:focus-within) * { + overflow: hidden !important; } .stretched-link::after { position: absolute; @@ -9316,7 +9324,7 @@ h1, .h1 { margin-left: auto; margin-right: auto; } -.splash[ng-cloak] { +[ng-cloak].splash { display: block !important; } [ng-cloak] { @@ -9456,7 +9464,7 @@ strong { -webkit-transition: opacity 0.12s ease-in-out; -o-transition: opacity 0.12s ease-in-out; } -.navbar a.navbar-brand:hover span { +.navbar a:hover.navbar-brand span { opacity: 1; } .navbar li > a, .navbar-text { diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html index b9026690e..4d76d8d16 100644 --- a/src/main/resources/templates/error.html +++ b/src/main/resources/templates/error.html @@ -2,10 +2,10 @@ - - -

Something happened...

-

Exception message

- + + +

Something happened...

+

Exception message

+ \ No newline at end of file diff --git a/src/main/resources/templates/fragments/inputField.html b/src/main/resources/templates/fragments/inputField.html index 5aeebc9fb..da1de4772 100644 --- a/src/main/resources/templates/fragments/inputField.html +++ b/src/main/resources/templates/fragments/inputField.html @@ -1,28 +1,27 @@ - + + + +
-
- +
-
- - -
- +
+ + +
+ - - Error + + Error
- + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html index eb54d28ae..7c5cd0d86 100644 --- a/src/main/resources/templates/fragments/layout.html +++ b/src/main/resources/templates/fragments/layout.html @@ -1,5 +1,6 @@ - - + + + @@ -7,16 +8,8 @@ - - - PetClinic :: a Spring Framework demonstration - - - + PetClinic :: a Spring Framework demonstration @@ -45,25 +38,25 @@ @@ -80,7 +73,8 @@
- +
@@ -91,4 +85,4 @@ - + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/selectField.html b/src/main/resources/templates/fragments/selectField.html index 25581eb3b..d248864d7 100644 --- a/src/main/resources/templates/fragments/selectField.html +++ b/src/main/resources/templates/fragments/selectField.html @@ -1,29 +1,27 @@ - + + + +
-
- +
- + - - Error + + Error
- + + \ No newline at end of file diff --git a/src/main/resources/templates/owners/createOrUpdateOwnerForm.html b/src/main/resources/templates/owners/createOrUpdateOwnerForm.html index 72c40fbc6..4cd6e115b 100644 --- a/src/main/resources/templates/owners/createOrUpdateOwnerForm.html +++ b/src/main/resources/templates/owners/createOrUpdateOwnerForm.html @@ -1,30 +1,25 @@ - + + + -

Owner

+

Owner

- - - - - + + + + +
- +
- + + \ No newline at end of file diff --git a/src/main/resources/templates/owners/findOwners.html b/src/main/resources/templates/owners/findOwners.html index 0a818fc79..703351c7d 100644 --- a/src/main/resources/templates/owners/findOwners.html +++ b/src/main/resources/templates/owners/findOwners.html @@ -1,34 +1,35 @@ - + + + -

Find Owners

+

Find Owners

-
+
- +
-
+ + +

Error

-
+
+
- +
- Add Owner + Add Owner
- + + \ No newline at end of file diff --git a/src/main/resources/templates/owners/ownerDetails.html b/src/main/resources/templates/owners/ownerDetails.html index 15bca4ad2..cc175cd13 100644 --- a/src/main/resources/templates/owners/ownerDetails.html +++ b/src/main/resources/templates/owners/ownerDetails.html @@ -1,102 +1,96 @@ - + - - - -

Owner Information

+ -
- -
+

Owner Information

-
- -
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
Name
Address
City
Telephone
+ Edit + Owner + Add + New Pet - - - - - - - - - - - - - - - - - -
Name
Address
City
Telephone
- - Edit - Owner - Add - New Pet - -
-
-
-

Pets and Visits

- - - - - -
-
-
Name
-
-
Birth Date
-
-
Type
-
-
-
- - - - - - - - - - - +
+
+
+

Pets and Visits

+ +
Visit DateDescription
+ + + + - - -
+
+
Name
+
+
Birth Date
+
+
Type
+
+
+
+ + - - + + -
Edit PetAdd VisitVisit DateDescription
-
- + - + - + \ No newline at end of file diff --git a/src/main/resources/templates/owners/ownersList.html b/src/main/resources/templates/owners/ownersList.html index 9af325289..01223c1c5 100644 --- a/src/main/resources/templates/owners/ownersList.html +++ b/src/main/resources/templates/owners/ownersList.html @@ -4,59 +4,58 @@ -

Owners

+

Owners

- - - - - - - - - - - - - - - - -
NameAddressCityTelephonePets
- - - - -
-
- Pages: - [ - + + + + + + + + + + + + + + + + +
NameAddressCityTelephonePets
+ + + + +
+
+ Pages: + [ + [[${i}]] [[${i}]] - - - - + + + + - - - + + + - - - + + + - - - + + + -
+
- + \ No newline at end of file diff --git a/src/main/resources/templates/pets/createOrUpdatePetForm.html b/src/main/resources/templates/pets/createOrUpdatePetForm.html index dd8a4dd1c..396963d41 100644 --- a/src/main/resources/templates/pets/createOrUpdatePetForm.html +++ b/src/main/resources/templates/pets/createOrUpdatePetForm.html @@ -1,38 +1,34 @@ - + + +

- New - Pet + New + Pet

- +
- - - + + +
- +
- + \ No newline at end of file diff --git a/src/main/resources/templates/pets/createOrUpdateVisitForm.html b/src/main/resources/templates/pets/createOrUpdateVisitForm.html index 4b0d315f0..4f03e12d5 100644 --- a/src/main/resources/templates/pets/createOrUpdateVisitForm.html +++ b/src/main/resources/templates/pets/createOrUpdateVisitForm.html @@ -1,55 +1,52 @@ - + + +

- New + New Visit

- Pet + Pet - - - - + + + + - + - +
NameBirth DateTypeOwnerNameBirth DateTypeOwner
- - + +
- +

- Previous Visits + Previous Visits - - + + @@ -58,4 +55,5 @@
DateDescriptionDateDescription
- + + \ No newline at end of file diff --git a/src/main/resources/templates/vets/vetList.html b/src/main/resources/templates/vets/vetList.html index e0b8e7050..e40fd654e 100644 --- a/src/main/resources/templates/vets/vetList.html +++ b/src/main/resources/templates/vets/vetList.html @@ -1,57 +1,57 @@ - + -

Veterinarians

+

Veterinarians

- - - - - - - - - - - - - -
NameSpecialties
none
- -
- Pages: - [ - + + + + + + + + + + + + + +
NameSpecialties
+ none +
+
+ Pages: + [ + [[${i}]] [[${i}]] - - - - + + + + - - - + + + - - - + + + - - - + + + -
+
- + + \ No newline at end of file diff --git a/src/main/resources/templates/welcome.html b/src/main/resources/templates/welcome.html index 4fa1cd328..eb795c2f4 100644 --- a/src/main/resources/templates/welcome.html +++ b/src/main/resources/templates/welcome.html @@ -2,15 +2,15 @@ - + -

Welcome

-
-
- -
+

Welcome

+
+
+
+
- + \ No newline at end of file diff --git a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java index d67e449dc..a78e2e6e1 100644 --- a/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/MySqlIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,6 +36,7 @@ import org.springframework.web.client.RestTemplate; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @ActiveProfiles("mysql") @@ -46,7 +47,7 @@ class MySqlIntegrationTests { @ServiceConnection @Container - static MySQLContainer container = new MySQLContainer<>("mysql:9.1"); + static MySQLContainer container = new MySQLContainer<>(DockerImageName.parse("mysql:9.2")); @LocalServerPort int port; diff --git a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java index f98d892d3..2a4ea8e06 100644 --- a/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java +++ b/src/test/java/org/springframework/samples/petclinic/MysqlTestApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,12 +22,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.utility.DockerImageName; /** * PetClinic Spring Boot Application. * * @author Dave Syer - * */ @Configuration public class MysqlTestApplication { @@ -36,7 +36,7 @@ public class MysqlTestApplication { @Profile("mysql") @Bean static MySQLContainer container() { - return new MySQLContainer<>("mysql:9.1"); + return new MySQLContainer<>(DockerImageName.parse("mysql:9.2")); } public static void main(String[] args) { diff --git a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java index 6d9820696..617af9652 100644 --- a/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PetClinicIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java index 0b9e4f93a..709d33e66 100644 --- a/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/PostgresIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java index c75328522..559311ff8 100644 --- a/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java +++ b/src/test/java/org/springframework/samples/petclinic/model/ValidatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java index 426ca5c24..7570ae132 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/OwnerControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.samples.petclinic.owner; -import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledInNativeImage; @@ -31,6 +30,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import java.time.LocalDate; +import java.util.List; import java.util.Optional; import static org.hamcrest.Matchers.empty; @@ -92,9 +92,7 @@ class OwnerControllerTests { Owner george = george(); given(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))) - .willReturn(new PageImpl<>(Lists.newArrayList(george))); - - given(this.owners.findAll(any(Pageable.class))).willReturn(new PageImpl<>(Lists.newArrayList(george))); + .willReturn(new PageImpl<>(List.of(george))); given(this.owners.findById(TEST_OWNER_ID)).willReturn(Optional.of(george)); Visit visit = new Visit(); @@ -143,14 +141,14 @@ class OwnerControllerTests { @Test void testProcessFindFormSuccess() throws Exception { - Page tasks = new PageImpl<>(Lists.newArrayList(george(), new Owner())); + Page tasks = new PageImpl<>(List.of(george(), new Owner())); when(this.owners.findByLastNameStartingWith(anyString(), any(Pageable.class))).thenReturn(tasks); mockMvc.perform(get("/owners?page=1")).andExpect(status().isOk()).andExpect(view().name("owners/ownersList")); } @Test void testProcessFindFormByLastName() throws Exception { - Page tasks = new PageImpl<>(Lists.newArrayList(george())); + Page tasks = new PageImpl<>(List.of(george())); when(this.owners.findByLastNameStartingWith(eq("Franklin"), any(Pageable.class))).thenReturn(tasks); mockMvc.perform(get("/owners?page=1").param("lastName", "Franklin")) .andExpect(status().is3xxRedirection()) @@ -159,7 +157,7 @@ class OwnerControllerTests { @Test void testProcessFindFormNoOwnersFound() throws Exception { - Page tasks = new PageImpl<>(Lists.newArrayList()); + Page tasks = new PageImpl<>(List.of()); when(this.owners.findByLastNameStartingWith(eq("Unknown Surname"), any(Pageable.class))).thenReturn(tasks); mockMvc.perform(get("/owners?page=1").param("lastName", "Unknown Surname")) .andExpect(status().isOk()) diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java index 9a6134cbb..d60636f1f 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.samples.petclinic.owner; -import org.assertj.core.util.Lists; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -25,11 +24,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; +import org.springframework.samples.petclinic.owner.PetTypeRepository; +import org.springframework.samples.petclinic.owner.OwnerRepository; import org.springframework.test.context.aot.DisabledInAotMode; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import java.time.LocalDate; +import java.util.List; import java.util.Optional; import static org.mockito.BDDMockito.given; @@ -61,12 +63,15 @@ class PetControllerTests { @MockitoBean private OwnerRepository owners; + @MockitoBean + private PetTypeRepository types; + @BeforeEach void setup() { PetType cat = new PetType(); cat.setId(3); cat.setName("hamster"); - given(this.owners.findPetTypes()).willReturn(Lists.newArrayList(cat)); + given(this.types.findPetTypes()).willReturn(List.of(cat)); Owner owner = new Owner(); Pet pet = new Pet(); diff --git a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java index 0295b4788..fe4a5f7f7 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/PetTypeFormatterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,13 +43,13 @@ import org.mockito.junit.jupiter.MockitoExtension; class PetTypeFormatterTests { @Mock - private OwnerRepository pets; + private PetTypeRepository types; private PetTypeFormatter petTypeFormatter; @BeforeEach void setup() { - this.petTypeFormatter = new PetTypeFormatter(pets); + this.petTypeFormatter = new PetTypeFormatter(types); } @Test @@ -62,14 +62,14 @@ class PetTypeFormatterTests { @Test void shouldParse() throws ParseException { - given(this.pets.findPetTypes()).willReturn(makePetTypes()); + given(types.findPetTypes()).willReturn(makePetTypes()); PetType petType = petTypeFormatter.parse("Bird", Locale.ENGLISH); assertThat(petType.getName()).isEqualTo("Bird"); } @Test void shouldThrowParseException() { - given(this.pets.findPetTypes()).willReturn(makePetTypes()); + given(types.findPetTypes()).willReturn(makePetTypes()); Assertions.assertThrows(ParseException.class, () -> { petTypeFormatter.parse("Fish", Locale.ENGLISH); }); diff --git a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java index e42e75034..e142a8975 100644 --- a/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/owner/VisitControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java index 17360278f..4c78ab317 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java +++ b/src/test/java/org/springframework/samples/petclinic/service/ClinicServiceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.samples.petclinic.owner.Owner; import org.springframework.samples.petclinic.owner.OwnerRepository; +import org.springframework.samples.petclinic.owner.PetTypeRepository; import org.springframework.samples.petclinic.owner.Pet; import org.springframework.samples.petclinic.owner.PetType; import org.springframework.samples.petclinic.owner.Visit; @@ -75,6 +76,9 @@ class ClinicServiceTests { @Autowired protected OwnerRepository owners; + @Autowired + protected PetTypeRepository types; + @Autowired protected VetRepository vets; @@ -140,7 +144,7 @@ class ClinicServiceTests { @Test void shouldFindAllPetTypes() { - Collection petTypes = this.owners.findPetTypes(); + Collection petTypes = this.types.findPetTypes(); PetType petType1 = EntityUtils.getById(petTypes, PetType.class, 1); assertThat(petType1.getName()).isEqualTo("cat"); @@ -159,7 +163,7 @@ class ClinicServiceTests { Pet pet = new Pet(); pet.setName("bowser"); - Collection types = this.owners.findPetTypes(); + Collection types = this.types.findPetTypes(); pet.setType(EntityUtils.getById(types, PetType.class, 2)); pet.setBirthDate(LocalDate.now()); owner6.addPet(pet); diff --git a/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java b/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java index 7b7a5e64a..180ef07f1 100644 --- a/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java +++ b/src/test/java/org/springframework/samples/petclinic/service/EntityUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java index 3cdca47bf..ed8b0819a 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,12 +50,6 @@ import org.springframework.http.ResponseEntity; properties = { "server.error.include-message=ALWAYS", "management.endpoints.enabled-by-default=false" }) class CrashControllerIntegrationTests { - @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class }) - static class TestConfiguration { - - } - @Value(value = "${local.server.port}") private int port; @@ -96,4 +90,10 @@ class CrashControllerIntegrationTests { "This application has no explicit mapping for"); } + @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, + DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class }) + static class TestConfiguration { + + } + } diff --git a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java index cc2ad6746..b2760ebea 100644 --- a/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/system/CrashControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/system/I18nPropertiesSyncTest.java b/src/test/java/org/springframework/samples/petclinic/system/I18nPropertiesSyncTest.java new file mode 100644 index 000000000..fec159cec --- /dev/null +++ b/src/test/java/org/springframework/samples/petclinic/system/I18nPropertiesSyncTest.java @@ -0,0 +1,136 @@ +package org.springframework.samples.petclinic.system; + +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; + +/** + * This test ensures that there are no hard-coded strings without internationalization in + * any HTML files. Also ensures that a string is translated in every language to avoid + * partial translations. + * + * @author Anuj Ashok Potdar + */ +public class I18nPropertiesSyncTest { + + private static final String I18N_DIR = "src/main/resources"; + + private static final String BASE_NAME = "messages"; + + public static final String PROPERTIES = ".properties"; + + private static final Pattern HTML_TEXT_LITERAL = Pattern.compile(">([^<>{}]+)<"); + + private static final Pattern BRACKET_ONLY = Pattern.compile("<[^>]*>\\s*[\\[\\]](?: )?\\s*]*>"); + + private static final Pattern HAS_TH_TEXT_ATTRIBUTE = Pattern.compile("th:(u)?text\\s*=\\s*\"[^\"]+\""); + + @Test + public void checkNonInternationalizedStrings() throws IOException { + Path root = Paths.get("src/main"); + List files; + + try (Stream stream = Files.walk(root)) { + files = stream.filter(p -> p.toString().endsWith(".java") || p.toString().endsWith(".html")) + .filter(p -> !p.toString().contains("/test/")) + .filter(p -> !p.getFileName().toString().endsWith("Test.java")) + .toList(); + } + + StringBuilder report = new StringBuilder(); + + for (Path file : files) { + List lines = Files.readAllLines(file); + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i).trim(); + + if (line.startsWith("//") || line.startsWith("@") || line.contains("log.") + || line.contains("System.out")) + continue; + + if (file.toString().endsWith(".html")) { + boolean hasLiteralText = HTML_TEXT_LITERAL.matcher(line).find(); + boolean hasThTextAttribute = HAS_TH_TEXT_ATTRIBUTE.matcher(line).find(); + boolean isBracketOnly = BRACKET_ONLY.matcher(line).find(); + + if (hasLiteralText && !line.contains("#{") && !hasThTextAttribute && !isBracketOnly) { + report.append("HTML: ") + .append(file) + .append(" Line ") + .append(i + 1) + .append(": ") + .append(line) + .append("\n"); + } + } + } + } + + if (!report.isEmpty()) { + fail("Hardcoded (non-internationalized) strings found:\n" + report); + } + } + + @Test + public void checkI18nPropertyFilesAreInSync() throws IOException { + List propertyFiles; + try (Stream stream = Files.walk(Paths.get(I18N_DIR))) { + propertyFiles = stream.filter(p -> p.getFileName().toString().startsWith(BASE_NAME)) + .filter(p -> p.getFileName().toString().endsWith(PROPERTIES)) + .toList(); + } + + Map localeToProps = new HashMap<>(); + + for (Path path : propertyFiles) { + Properties props = new Properties(); + try (var reader = Files.newBufferedReader(path)) { + props.load(reader); + localeToProps.put(path.getFileName().toString(), props); + } + } + + String baseFile = BASE_NAME + PROPERTIES; + Properties baseProps = localeToProps.get(baseFile); + if (baseProps == null) { + fail("Base properties file '" + baseFile + "' not found."); + return; + } + + Set baseKeys = baseProps.stringPropertyNames(); + StringBuilder report = new StringBuilder(); + + for (Map.Entry entry : localeToProps.entrySet()) { + String fileName = entry.getKey(); + // We use fallback logic to include english strings, hence messages_en is not + // populated. + if (fileName.equals(baseFile) || fileName.equals("messages_en.properties")) + continue; + + Properties props = entry.getValue(); + Set missingKeys = new TreeSet<>(baseKeys); + missingKeys.removeAll(props.stringPropertyNames()); + + if (!missingKeys.isEmpty()) { + report.append("Missing keys in ").append(fileName).append(":\n"); + missingKeys.forEach(k -> report.append(" ").append(k).append("\n")); + } + } + + if (!report.isEmpty()) { + fail("Translation files are not in sync:\n" + report); + } + } + +} diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java index 5fffeea47..f295cd68a 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetControllerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java b/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java index b29c6d100..24b772dc3 100644 --- a/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java +++ b/src/test/java/org/springframework/samples/petclinic/vet/VetTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.