Merge pull request 'Passage du jalon 1 au jalon 2' (#136) from dev into main
Reviewed-on: https://10.4.0.131/gitea/DI1-P4-E1/Webzine/pulls/136
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
|
||||
403
.editorconfig
Normal file
403
.editorconfig
Normal file
@@ -0,0 +1,403 @@
|
||||
root = true
|
||||
|
||||
# All files
|
||||
[*]
|
||||
indent_style = space
|
||||
|
||||
# Xml files
|
||||
[*.xml]
|
||||
indent_size = 2
|
||||
|
||||
# Xml project files
|
||||
[*.{csproj,fsproj,vbproj,proj,slnx}]
|
||||
indent_size = 2
|
||||
|
||||
# Xml config files
|
||||
[*.{props,targets,config,nuspec}]
|
||||
indent_size = 2
|
||||
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
|
||||
#### Core EditorConfig Options ####
|
||||
|
||||
# Indentation and spacing
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
|
||||
# New line preferences
|
||||
insert_final_newline = false
|
||||
|
||||
#### .NET Coding Conventions ####
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Organize usings
|
||||
dotnet_separate_import_directive_groups = true
|
||||
dotnet_sort_system_directives_first = true
|
||||
file_header_template = unset
|
||||
|
||||
# this. and Me. preferences
|
||||
dotnet_style_qualification_for_event = false:silent
|
||||
dotnet_style_qualification_for_field = false:silent
|
||||
dotnet_style_qualification_for_method = false:silent
|
||||
dotnet_style_qualification_for_property = false:silent
|
||||
|
||||
# Language keywords vs BCL types preferences
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
|
||||
dotnet_style_predefined_type_for_member_access = true:silent
|
||||
|
||||
# Parentheses preferences
|
||||
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
|
||||
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
|
||||
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
|
||||
|
||||
# Modifier preferences
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_namespace_match_folder = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_operator_placement_when_wrapping = beginning_of_line
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
|
||||
dotnet_style_prefer_compound_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
|
||||
dotnet_style_prefer_conditional_expression_over_return = true:suggestion
|
||||
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
|
||||
dotnet_style_prefer_simplified_interpolation = true:suggestion
|
||||
|
||||
# Field preferences
|
||||
dotnet_style_readonly_field = true:warning
|
||||
|
||||
# Parameter preferences
|
||||
dotnet_code_quality_unused_parameters = all:suggestion
|
||||
|
||||
# Suppression preferences
|
||||
dotnet_remove_unnecessary_suppression_exclusions = none
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
[*.cs]
|
||||
|
||||
# var preferences
|
||||
csharp_style_var_elsewhere = false:silent
|
||||
csharp_style_var_for_built_in_types = false:silent
|
||||
csharp_style_var_when_type_is_apparent = false:silent
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:suggestion
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_prefer_extended_property_pattern = true:suggestion
|
||||
csharp_style_prefer_not_pattern = true:suggestion
|
||||
csharp_style_prefer_pattern_matching = true:silent
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_anonymous_function = true:suggestion
|
||||
csharp_prefer_static_local_function = true:warning
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion
|
||||
csharp_style_prefer_readonly_struct = true:suggestion
|
||||
csharp_style_prefer_readonly_struct_member = true:suggestion
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
csharp_prefer_simple_using_statement = true:suggestion
|
||||
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||
csharp_style_prefer_method_group_conversion = true:silent
|
||||
csharp_style_prefer_primary_constructors = true:suggestion
|
||||
csharp_style_prefer_top_level_statements = true:silent
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_style_prefer_tuple_swap = true:suggestion
|
||||
csharp_style_prefer_utf8_string_literals = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
|
||||
#### Naming styles ####
|
||||
[*.{cs,vb}]
|
||||
|
||||
# Naming rules
|
||||
|
||||
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
|
||||
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
|
||||
dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
|
||||
dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
|
||||
|
||||
dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
|
||||
dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
|
||||
dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
|
||||
|
||||
dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
|
||||
dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
|
||||
dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.events_should_be_pascalcase.symbols = events
|
||||
dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
|
||||
dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
|
||||
|
||||
dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
|
||||
dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
|
||||
|
||||
dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
|
||||
dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
|
||||
|
||||
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
|
||||
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
|
||||
dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
|
||||
dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
|
||||
|
||||
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
|
||||
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
|
||||
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
|
||||
|
||||
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
|
||||
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
|
||||
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
|
||||
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
|
||||
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
|
||||
dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
|
||||
dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
|
||||
|
||||
dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
|
||||
dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
|
||||
dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
|
||||
|
||||
# Symbol specifications
|
||||
|
||||
dotnet_naming_symbols.interfaces.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interfaces.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.enums.applicable_kinds = enum
|
||||
dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.enums.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.events.applicable_kinds = event
|
||||
dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.events.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.methods.applicable_kinds = method
|
||||
dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.methods.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.properties.applicable_kinds = property
|
||||
dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.properties.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.public_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
|
||||
dotnet_naming_symbols.public_fields.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_fields.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_static_fields.required_modifiers = static
|
||||
|
||||
dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
|
||||
dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types_and_namespaces.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
|
||||
dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.type_parameters.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_symbols.local_variables.applicable_kinds = local
|
||||
dotnet_naming_symbols.local_variables.applicable_accessibilities = local
|
||||
dotnet_naming_symbols.local_variables.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.local_constants.applicable_kinds = local
|
||||
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
|
||||
dotnet_naming_symbols.local_constants.required_modifiers = const
|
||||
|
||||
dotnet_naming_symbols.parameters.applicable_kinds = parameter
|
||||
dotnet_naming_symbols.parameters.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.parameters.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
|
||||
dotnet_naming_symbols.public_constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
|
||||
dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
|
||||
|
||||
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
|
||||
|
||||
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
|
||||
dotnet_naming_symbols.local_functions.applicable_accessibilities = *
|
||||
dotnet_naming_symbols.local_functions.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.pascalcase.required_prefix =
|
||||
dotnet_naming_style.pascalcase.required_suffix =
|
||||
dotnet_naming_style.pascalcase.word_separator =
|
||||
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.ipascalcase.required_prefix = I
|
||||
dotnet_naming_style.ipascalcase.required_suffix =
|
||||
dotnet_naming_style.ipascalcase.word_separator =
|
||||
dotnet_naming_style.ipascalcase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.tpascalcase.required_prefix = T
|
||||
dotnet_naming_style.tpascalcase.required_suffix =
|
||||
dotnet_naming_style.tpascalcase.word_separator =
|
||||
dotnet_naming_style.tpascalcase.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style._camelcase.required_prefix = _
|
||||
dotnet_naming_style._camelcase.required_suffix =
|
||||
dotnet_naming_style._camelcase.word_separator =
|
||||
dotnet_naming_style._camelcase.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.camelcase.required_prefix =
|
||||
dotnet_naming_style.camelcase.required_suffix =
|
||||
dotnet_naming_style.camelcase.word_separator =
|
||||
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||
|
||||
dotnet_naming_style.s_camelcase.required_prefix = s_
|
||||
dotnet_naming_style.s_camelcase.required_suffix =
|
||||
dotnet_naming_style.s_camelcase.word_separator =
|
||||
dotnet_naming_style.s_camelcase.capitalization = camel_case
|
||||
|
||||
dotnet_diagnostic.SA1623.severity = none
|
||||
dotnet_diagnostic.SA1624.severity = none
|
||||
|
||||
dotnet_diagnostic.SA1600.severity = suggestion
|
||||
|
||||
[*.cs]
|
||||
# 1. Tell the .NET Formatter to stop injecting a header
|
||||
file_header_template = unset
|
||||
|
||||
# 2. Tell StyleCop to stop requiring a file header (SA1633)
|
||||
dotnet_diagnostic.SA1633.severity = none
|
||||
|
||||
# 3. Tell the IDE to stop requiring a file header (IDE0073)
|
||||
dotnet_diagnostic.IDE0073.severity = none
|
||||
150
.gitea/workflows/pr-endpoint-check.yml
Normal file
150
.gitea/workflows/pr-endpoint-check.yml
Normal file
@@ -0,0 +1,150 @@
|
||||
name: PR Endpoint Performance Check
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
endpoint-performance-check:
|
||||
name: Test All Endpoints (< 1s)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout PR branch
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure appsettings for CI
|
||||
run: |
|
||||
# Find the appsettings.json file
|
||||
APPSETTINGS_PATH="Webzine.WebApplication/appsettings.json"
|
||||
|
||||
# Backup original file
|
||||
cp $APPSETTINGS_PATH $APPSETTINGS_PATH.bak
|
||||
|
||||
# Use jq to modify the JSON
|
||||
jq '.UseDatabase = true | .IsSQLite = true' $APPSETTINGS_PATH > $APPSETTINGS_PATH.tmp
|
||||
mv $APPSETTINGS_PATH.tmp $APPSETTINGS_PATH
|
||||
|
||||
echo "Updated appsettings.json:"
|
||||
cat $APPSETTINGS_PATH
|
||||
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.x"
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore Webzine.sln
|
||||
|
||||
- name: Build solution
|
||||
run: dotnet build Webzine.sln --no-restore --configuration Release
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
dotnet test Webzine.Entity.Tests/Webzine.Entity.Tests.csproj \
|
||||
--no-build \
|
||||
--configuration Release
|
||||
|
||||
- name: Start Webzine application
|
||||
run: |
|
||||
dotnet run \
|
||||
--project Webzine.WebApplication/Webzine.WebApplication.csproj \
|
||||
--configuration Release \
|
||||
--no-build \
|
||||
-- --urls "http://localhost:5038" &
|
||||
|
||||
echo "Attente du démarrage de l'application..."
|
||||
timeout 60 bash -c '
|
||||
until curl -sf http://localhost:5038 > /dev/null 2>&1; do
|
||||
sleep 1
|
||||
done
|
||||
'
|
||||
echo "Application prête!"
|
||||
|
||||
- name: Test endpoint response times
|
||||
id: perf_test
|
||||
run: |
|
||||
chmod +x scripts/test-endpoints.sh
|
||||
bash scripts/test-endpoints.sh http://localhost:5038 1000 2>&1 | tee /tmp/webzine_endpoint_output.txt
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
|
||||
# Count failures
|
||||
FAIL_COUNT=$(grep -cE "^\[ÉCHEC\]" /tmp/webzine_endpoint_output.txt 2>/dev/null || echo 0)
|
||||
SLOW_COUNT=$(grep -cE "^\[LENT\]" /tmp/webzine_endpoint_output.txt 2>/dev/null || echo 0)
|
||||
|
||||
echo "failed=$FAIL_COUNT" >> "$GITHUB_OUTPUT"
|
||||
echo "slow=$SLOW_COUNT" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Échoue s’il y a DES problèmes (échecs OU lents)
|
||||
if [ $FAIL_COUNT -gt 0 ] || [ $SLOW_COUNT -gt 0 ]; then
|
||||
echo "❌ Performance check failed: $FAIL_COUNT endpoint(s) failed, $SLOW_COUNT endpoint(s) exceeded threshold (1000ms)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ All endpoints passed performance check (< 1000ms)"
|
||||
|
||||
- name: Post performance report as PR comment
|
||||
if: always() # Always post comment, even on failure
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||
REPO: ${{ gitea.repository }}
|
||||
PR_NUMBER: ${{ gitea.event.pull_request.number }}
|
||||
run: |
|
||||
RAW_REPORT=$(cat /tmp/webzine_endpoint_output.txt 2>/dev/null || echo "Aucune sortie capturée.")
|
||||
FAILED_COUNT="${{ steps.perf_test.outputs.failed }}"
|
||||
SLOW_COUNT="${{ steps.perf_test.outputs.slow }}"
|
||||
|
||||
# Determine if the check passed or failed
|
||||
if [ "$FAILED_COUNT" -gt 0 ] || [ "$SLOW_COUNT" -gt 0 ]; then
|
||||
STATUS_HEADER="❌ **PERFORMANCE CHECK FAILED**"
|
||||
STATUS_ICON="❌"
|
||||
else
|
||||
STATUS_HEADER="✅ **PERFORMANCE CHECK PASSED**"
|
||||
STATUS_ICON="✅"
|
||||
fi
|
||||
|
||||
# Convert report to Markdown with colors
|
||||
CLEAN_REPORT=$(echo "$RAW_REPORT" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
# Generate formatted report
|
||||
FORMATTED_REPORT=$(echo "$CLEAN_REPORT" | sed \
|
||||
-e 's/^\[OK\] /✅ /' \
|
||||
-e 's/^\[LENT\] /⚠️ /' \
|
||||
-e 's/^\[ÉCHEC\] /❌ /' \
|
||||
-e 's/^→ \[LENT\] / • ⚠️ /' \
|
||||
-e 's/^→ \[ÉCHEC\] / • ❌ /' \
|
||||
-e 's/^→ / • /' \
|
||||
-e 's/^── \(.*\)$/\n### ── \1/' \
|
||||
-e 's/^\(Total.*\)$/\n**\1**/' \
|
||||
-e 's/^\(Réussis.*\)$/**✅ \1**/' \
|
||||
-e 's/^\(Échecs.*\)$/**❌ \1**/' \
|
||||
-e 's/^\(⚠️ ENDPOINTS LENTS.*\)$/\n### \1/' \
|
||||
-e 's/^\(❌ ENDPOINTS EN ÉCHEC.*\)$/\n### \1/' \
|
||||
-e 's/^\(La PR doit.*\)$/\n**❌ \1**/')
|
||||
|
||||
BODY=$(cat <<EOF
|
||||
$STATUS_HEADER
|
||||
|
||||
$FORMATTED_REPORT
|
||||
|
||||
---
|
||||
**Seuil**: 1000ms
|
||||
**Statistiques**:
|
||||
- ✅ Endpoints rapides: \$(grep -c "^\[OK\]" /tmp/webzine_endpoint_output.txt 2>/dev/null || echo 0)
|
||||
- ⚠️ Endpoints lents (>1000ms): $SLOW_COUNT
|
||||
- ❌ Endpoints en échec: $FAILED_COUNT
|
||||
|
||||
**Vérifié par**: Workflow PR Endpoint Performance
|
||||
EOF
|
||||
)
|
||||
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(jq -n --arg body "$BODY" '{body: $body}')" \
|
||||
"$GITEA_SERVER_URL/api/v1/repos/$REPO/issues/$PR_NUMBER/comments"
|
||||
|
||||
- name: Fail job if performance issues detected
|
||||
if: steps.perf_test.outputs.failed > 0 || steps.perf_test.outputs.slow > 0
|
||||
run: |
|
||||
echo "❌ Job failed due to performance issues"
|
||||
exit 1
|
||||
@@ -2,8 +2,14 @@
|
||||
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
|
||||
"settings": {
|
||||
"documentationRules": {
|
||||
"companyName": " Equipe 1 - ",
|
||||
"documentationCulture": "fr-FR"
|
||||
}
|
||||
"documentInterfaces": false,
|
||||
"documentInternalElements": false,
|
||||
"documentExposedElements": false
|
||||
},
|
||||
"maintainabilityRules": {
|
||||
"settings": {
|
||||
"commonWords": [ "Obtient", "définit" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
|
||||
<PackageReference Include="NLog" Version="6.1.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -21,4 +24,8 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
111
Webzine.EntitiesContext/WebzineDbContext.cs
Normal file
111
Webzine.EntitiesContext/WebzineDbContext.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
// <copyright file="WebzineDbContext.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
/// <summary>
|
||||
/// Définit le contexte de la base de données.
|
||||
/// </summary>
|
||||
namespace Webzine.EntitiesContext
|
||||
{
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using Webzine.Entity;
|
||||
|
||||
public class WebzineDbContext : DbContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebzineDbContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">Options.</param>
|
||||
public WebzineDbContext(DbContextOptions<WebzineDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit les artistes de la base.
|
||||
/// </summary>
|
||||
public DbSet<Artiste> Artistes => this.Set<Artiste>();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit les styles de la base.
|
||||
/// </summary>
|
||||
public DbSet<Style> Styles => this.Set<Style>();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit les titres de la base.
|
||||
/// </summary>
|
||||
public DbSet<Titre> Titres => this.Set<Titre>();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit les commentaires de la base.
|
||||
/// </summary>
|
||||
public DbSet<Commentaire> Commentaires => this.Set<Commentaire>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<Artiste>(entity =>
|
||||
{
|
||||
entity.ToTable("Artistes");
|
||||
|
||||
entity.HasKey(a => a.IdArtiste)
|
||||
.HasName("PK_Artiste");
|
||||
|
||||
entity.HasMany(a => a.Titres)
|
||||
.WithOne(t => t.Artiste)
|
||||
.HasForeignKey(t => t.IdArtiste)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Style>(entity =>
|
||||
{
|
||||
entity.ToTable("Styles");
|
||||
|
||||
entity.HasKey(s => s.IdStyle)
|
||||
.HasName("PK_Style");
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Commentaire>(entity =>
|
||||
{
|
||||
entity.ToTable("Commentaires");
|
||||
|
||||
entity.HasKey(c => c.IdCommentaire)
|
||||
.HasName("PK_Commentaire");
|
||||
|
||||
entity.HasOne(c => c.Titre)
|
||||
.WithMany(t => t.Commentaires)
|
||||
.HasForeignKey(c => c.IdTitre)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Titre>(entity =>
|
||||
{
|
||||
entity.ToTable("Titres");
|
||||
|
||||
entity.HasKey(t => t.IdTitre)
|
||||
.HasName("PK_Titre");
|
||||
|
||||
entity.HasMany(t => t.Styles)
|
||||
.WithMany(s => s.Titres)
|
||||
.UsingEntity<Dictionary<string, object>>(
|
||||
"TitreStyle",
|
||||
right => right.HasOne<Style>()
|
||||
.WithMany()
|
||||
.HasForeignKey("IdStyle")
|
||||
.OnDelete(DeleteBehavior.Cascade),
|
||||
left => left.HasOne<Titre>()
|
||||
.WithMany()
|
||||
.HasForeignKey("IdTitre")
|
||||
.OnDelete(DeleteBehavior.Cascade),
|
||||
join =>
|
||||
{
|
||||
join.HasKey("IdTitre", "IdStyle");
|
||||
join.ToTable("TitreStyle");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,4 +106,4 @@ namespace Webzine.Entity.Tests
|
||||
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.Titre));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ namespace Webzine.Entity.Tests
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
/// <summary>
|
||||
@@ -13,8 +14,8 @@ namespace Webzine.Entity.Tests
|
||||
/// <summary>
|
||||
/// Vérifie que l'entité possède bien la propriété passée en paramètre.
|
||||
/// </summary>
|
||||
/// <param name="typeObjet">type de l'entité</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
|
||||
/// <param name="typeObjet">type de l'entité.</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité.</param>
|
||||
public static void HasProperty(Type typeObjet, string nomPropriete)
|
||||
{
|
||||
var property = typeObjet.GetProperty(nomPropriete);
|
||||
@@ -24,9 +25,9 @@ namespace Webzine.Entity.Tests
|
||||
/// <summary>
|
||||
/// Vérifie que l'attribut de l'entité a l'annotation [Display(Name = "xxx")] avec la valeur attendue.
|
||||
/// </summary>
|
||||
/// <param name="typeObjet">type de l'entité</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
|
||||
/// <param name="chaineAttendue">valeur attendue pour l'affichage de cette propriété</param>
|
||||
/// <param name="typeObjet">type de l'entité.</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité.</param>
|
||||
/// <param name="chaineAttendue">valeur attendue pour l'affichage de cette propriété.</param>
|
||||
public static void AttributDisplay(Type typeObjet, string nomPropriete, string chaineAttendue)
|
||||
{
|
||||
var property = typeObjet.GetProperty(nomPropriete);
|
||||
@@ -38,9 +39,9 @@ namespace Webzine.Entity.Tests
|
||||
/// <summary>
|
||||
/// Vérifie que l'attribut de l'entité a l'annotation [MinLength(xx)] avec la longueur attendue.
|
||||
/// </summary>
|
||||
/// <param name="typeObjet">type de l'entité</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
|
||||
/// <param name="max">longueur maximum</param>
|
||||
/// <param name="typeObjet">type de l'entité.</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité.</param>
|
||||
/// <param name="max">longueur maximum.</param>
|
||||
public static void AttributLongueurMax(Type typeObjet, string nomPropriete, int max)
|
||||
{
|
||||
var property = typeObjet.GetProperty(nomPropriete);
|
||||
@@ -52,9 +53,9 @@ namespace Webzine.Entity.Tests
|
||||
/// <summary>
|
||||
/// Vérifie que l'attribut de l'entité a l'annotation [MinLength(xx)] avec la longueur attendue.
|
||||
/// </summary>
|
||||
/// <param name="typeObjet">type de l'entité</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
|
||||
/// <param name="min">longueur minimum</param>
|
||||
/// <param name="typeObjet">type de l'entité.</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité.</param>
|
||||
/// <param name="min">longueur minimum.</param>
|
||||
public static void AttributLongueurMin(Type typeObjet, string nomPropriete, int min)
|
||||
{
|
||||
var property = typeObjet.GetProperty(nomPropriete);
|
||||
@@ -66,8 +67,8 @@ namespace Webzine.Entity.Tests
|
||||
/// <summary>
|
||||
/// Vérifie que l'attribut de l'entité a l'annotation [Required].
|
||||
/// </summary>
|
||||
/// <param name="typeObjet">type de l'entité</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
|
||||
/// <param name="typeObjet">type de l'entité.</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité.</param>
|
||||
public static void AttributRequis(Type typeObjet, string nomPropriete)
|
||||
{
|
||||
var property = typeObjet.GetProperty(nomPropriete);
|
||||
@@ -78,8 +79,8 @@ namespace Webzine.Entity.Tests
|
||||
/// <summary>
|
||||
/// Vérifie que l'attribut de l'entité n'a pas l'annotation [Url].
|
||||
/// </summary>
|
||||
/// <param name="typeObjet">type de l'entité</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
|
||||
/// <param name="typeObjet">type de l'entité.</param>
|
||||
/// <param name="nomPropriete">nom de la propriété de l'entité.</param>
|
||||
public static void AttributHasNotUrlValidation(Type typeObjet, string nomPropriete)
|
||||
{
|
||||
var property = typeObjet.GetProperty(nomPropriete);
|
||||
@@ -87,4 +88,4 @@ namespace Webzine.Entity.Tests
|
||||
Assert.IsNull(annotation, "La propriété '" + nomPropriete + "' ne doit pas être une URL obligatoirement. Retirez l'annotation Url.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,4 +238,4 @@ namespace Webzine.Entity.Tests
|
||||
Common.AttributHasNotUrlValidation(typeof(Titre), nameof(Titre.UrlJaquette));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using System.Timers;
|
||||
|
||||
namespace Webzine.Entity
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// Classe représentant un artiste.
|
||||
/// Lien avec l'entité <see cref="Titre"/> : un artiste peut avoir plusieurs titres, mais un titre n'a qu'un seul artiste.
|
||||
@@ -36,4 +32,4 @@ namespace Webzine.Entity
|
||||
/// </summary>
|
||||
public List<Titre> Titres { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Webzine.Entity
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// Classe représentant un commentaire laissé par un utilisateur sur un titre.
|
||||
/// Lien avec l'entité <see cref="Titre"/> : un titre peut avoir plusieurs commentaires, mais un commentaire n'a qu'un seul titre.
|
||||
@@ -51,4 +48,4 @@ namespace Webzine.Entity
|
||||
/// </summary>
|
||||
public Titre Titre { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using Bogus;
|
||||
|
||||
namespace Webzine.Entity.Fixtures
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory pour générer des artistes avec des titres associés, à l'aide de la bibliothèque Bogus.
|
||||
/// </summary>
|
||||
public class ArtisteFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Récupère un artiste par son nom, en générant des données fictives pour ses titres associés.
|
||||
/// </summary>
|
||||
/// <param name="nom">Le nom de l'artiste à générer.</param>
|
||||
/// <returns>Un objet Artiste avec des titres associés générés de manière aléatoire.</returns>
|
||||
public static Artiste SeedArtisteByName(string nom)
|
||||
{
|
||||
// On définit nos albums "bouchonnés"
|
||||
var albumsData = new[]
|
||||
{
|
||||
new { Nom = "Bohemian Rhapsody", Image = "https://upload.wikimedia.org/wikipedia/en/9/9f/Bohemian_Rhapsody.png" },
|
||||
new { Nom = "Born This Way", Image = "https://static.wikia.nocookie.net/ladygaga/images/2/2d/BornThisWay-DeluxeEdition.jpg/revision/latest/scale-to-width-down/3500?cb=20111120030308" }
|
||||
};
|
||||
|
||||
var faker = new Bogus.Faker("fr");
|
||||
var tousLesTitres = new List<Titre>();
|
||||
|
||||
// Pour chaque album, on génère un paquet de titres
|
||||
foreach (var album in albumsData)
|
||||
{
|
||||
var nombreDeTitres = faker.Random.Number(3, 6);
|
||||
|
||||
var titresDeLalbum = new Faker<Titre>("fr")
|
||||
.RuleFor(t => t.IdTitre, f => f.IndexFaker + 1 + tousLesTitres.Count)
|
||||
.RuleFor(t => t.UrlJaquette, _ => album.Image)
|
||||
.RuleFor(t => t.Album, _ => album.Nom)
|
||||
.RuleFor(t => t.Duree, f => f.Random.Number(90, 180))
|
||||
.RuleFor(t => t.Libelle, f => f.Music.Genre() + " - " + f.Commerce.ProductName())
|
||||
.Generate(nombreDeTitres);
|
||||
|
||||
tousLesTitres.AddRange(titresDeLalbum);
|
||||
}
|
||||
|
||||
// On crée l'artiste final
|
||||
var artisteFaker = new Faker<Artiste>("fr")
|
||||
.RuleFor(a => a.IdArtiste, f => f.IndexFaker + 1)
|
||||
.RuleFor(a => a.Nom, _ => nom)
|
||||
.RuleFor(a => a.Biographie, f => f.Lorem.Paragraphs(2))
|
||||
.RuleFor(a => a.Titres, _ => tousLesTitres);
|
||||
|
||||
return artisteFaker.Generate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
namespace Webzine.Entity.Fixtures;
|
||||
|
||||
using Entity;
|
||||
using Faker;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
public class DataFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionnaire contenant des données de musique réelles pour les titres générés.
|
||||
/// </summary>
|
||||
public Dictionary<string, (string TrackUrl, string ImageUrl)> RealMusicData { get; set; }
|
||||
= new Dictionary<string, (string TrackUrl, string ImageUrl)>
|
||||
{
|
||||
{ "juliana_chahayed_1", ("https://open.spotify.com/intl-fr/track/0qYLUdJQMhrCFA9dNZGcnm?si=b4fd45727a354a31",
|
||||
"https://i.scdn.co/image/ab67616d0000b2738e8e7b8f8f8f8f8f8f8f8f8") },
|
||||
|
||||
{ "mister_v_1", ("https://youtu.be/JeqUw7sGUK8?si=FnmFR2EgkVY6MhqQ",
|
||||
"https://img.youtube.com/vi/JeqUw7sGUK8/maxresdefault.jpg") },
|
||||
|
||||
{ "compagnie_creole_1", ("https://youtu.be/wfxt1SGWAI8",
|
||||
"https://img.youtube.com/vi/wfxt1SGWAI8/maxresdefault.jpg") },
|
||||
|
||||
{ "femto_1", ("https://open.spotify.com/intl-fr/track/0qYLUdJQMhrCFA9dNZGcnm?si=b4fd45727a354a31",
|
||||
"https://i.scdn.co/image/ab67616d0000b2738e8e7b8f8f8f8f8f8f8f8f8") },
|
||||
|
||||
{ "chat_noir_1", ("https://youtu.be/OTi4-q-_Tj0?si=SNnLd-6Y893nL5Au",
|
||||
"https://img.youtube.com/vi/OTi4-q-_Tj0/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_2", ("https://youtu.be/X-rJ01EyiAI?si=gH9m_U8oXI35OgWu",
|
||||
"https://img.youtube.com/vi/X-rJ01EyiAI/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_3", ("https://youtu.be/7lIM0wSx7kQ?si=S-RAsLzd4SiCQhE4",
|
||||
"https://img.youtube.com/vi/7lIM0wSx7kQ/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_4", ("https://youtu.be/dbxyKR1P8vA?si=aRzdYBhwvKptV8Ff",
|
||||
"https://img.youtube.com/vi/dbxyKR1P8vA/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_5", ("https://youtu.be/DDHvKo5NnII?si=NFRkdVQL2mELP0yn",
|
||||
"https://img.youtube.com/vi/DDHvKo5NnII/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_6", ("https://youtu.be/J9LgHNf2Qy0?si=YGO1ggiLkefa9901",
|
||||
"https://img.youtube.com/vi/J9LgHNf2Qy0/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_7", ("https://youtu.be/oadhHk2xs6c?si=mbnJCA6SGsoYXnUK",
|
||||
"https://img.youtube.com/vi/oadhHk2xs6c/maxresdefault.jpg") },
|
||||
|
||||
{ "chat_noir_8", ("https://youtu.be/6K1zCgkBaoE?si=quq9vQLJ-AmzjRJJ",
|
||||
"https://img.youtube.com/vi/6K1zCgkBaoE/maxresdefault.jpg") },
|
||||
|
||||
{ "german_rapper_1", ("https://www.youtube.com/watch?v=DWpg71HJt24",
|
||||
"https://img.youtube.com/vi/DWpg71HJt24/maxresdefault.jpg") },
|
||||
|
||||
{ "nizard_1", ("https://youtu.be/1fjA68k8DAU?si=2PuZSquVQGvfmQkZ",
|
||||
"https://img.youtube.com/vi/1fjA68k8DAU/maxresdefault.jpg") },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste d'artistes de musique en utilisant la bibliothèque Faker pour créer des noms d'artistes et des biographies réalistes.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre d'artistes à générer</param>
|
||||
/// <returns>Liste d'artistes de musique générés</returns>
|
||||
public List<Artiste> GenerateArtists(int count)
|
||||
{
|
||||
var artists = new List<Artiste>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
artists.Add(new Artiste
|
||||
{
|
||||
IdArtiste = i + 1,
|
||||
Nom = Name.FullName(),
|
||||
Biographie = Lorem.Paragraph(),
|
||||
Titres = new List<Titre>()
|
||||
});
|
||||
}
|
||||
|
||||
return artists;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de styles de musique à partir d'une liste prédéfinie de noms de styles.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de styles à générer (maximum 15, car il y a 15 styles prédéfinis)</param>
|
||||
/// <returns>Liste de styles de musique générés</returns>
|
||||
public List<Style> GenerateStyles(int count)
|
||||
{
|
||||
var styleNames = new[] { "Rock", "Pop", "Rap", "Électro", "Jazz", "Classique", "Reggae", "Blues", "Metal", "Folk", "Chanson française", "Hip-Hop", "R&B", "Soul", "Punk" };
|
||||
var styles = new List<Style>();
|
||||
|
||||
for (int i = 0; i < Math.Min(count, styleNames.Length); i++)
|
||||
{
|
||||
styles.Add(new Style
|
||||
{
|
||||
IdStyle = i + 1,
|
||||
Libelle = styleNames[i],
|
||||
Titres = new List<Titre>(),
|
||||
});
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de titres de musique en utilisant des données de musique réelles pour les URL d'écoute et les jaquettes.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de titres à générer</param>
|
||||
/// <param name="artists">Liste des artistes à associer aux titres générés</param>
|
||||
/// <param name="styles">Liste des styles à associer aux titres générés</param>
|
||||
/// <returns>Liste de titres de musique générés</returns>
|
||||
public List<Titre> GenerateTitres(int count, List<Artiste> artists, List<Style> styles)
|
||||
{
|
||||
var titres = new List<Titre>();
|
||||
var random = new Random();
|
||||
var musicItems = RealMusicData.Values.ToList();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var randomMusic = musicItems[random.Next(musicItems.Count)];
|
||||
var randomArtist = artists[random.Next(artists.Count)];
|
||||
|
||||
int numberOfStyles = random.Next(1, 4);
|
||||
var selectedStyles = styles.OrderBy(x => random.Next()).Take(numberOfStyles).ToList();
|
||||
|
||||
var titre = new Titre
|
||||
{
|
||||
IdTitre = i + 1,
|
||||
IdArtiste = randomArtist.IdArtiste,
|
||||
Artiste = randomArtist,
|
||||
Libelle = string.Join(" ", Lorem.Words(3)),
|
||||
Chronique = Lorem.Paragraph(),
|
||||
DateCreation = DateTime.Now.AddDays(-random.Next(1, 365)),
|
||||
Duree = random.Next(120, 360), // 2 à 6 minutes en secondes
|
||||
DateSortie = DateTime.Now.AddYears(-random.Next(0, 5)).AddDays(-random.Next(1, 365)),
|
||||
UrlJaquette = randomMusic.ImageUrl,
|
||||
UrlEcoute = randomMusic.TrackUrl,
|
||||
NbLectures = random.Next(1000, 1000000),
|
||||
NbLikes = random.Next(100, 100000), // Likes entre 100 et 100 000
|
||||
Album = $"Album {random.Next(1, 10)}",
|
||||
Commentaires = new List<Commentaire>(),
|
||||
Styles = new List<Style>(),
|
||||
};
|
||||
|
||||
foreach (var style in selectedStyles)
|
||||
{
|
||||
titre.Styles.Add(style);
|
||||
if (!style.Titres.Contains(titre))
|
||||
{
|
||||
style.Titres.Add(titre);
|
||||
}
|
||||
}
|
||||
|
||||
titres.Add(titre);
|
||||
randomArtist.Titres.Add(titres.Last());
|
||||
}
|
||||
|
||||
return titres;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de commentaires pour les titres de musique générés, en associant chaque commentaire à un titre aléatoire.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de commentaires à générer</param>
|
||||
/// <param name="titres">Liste des titres de musique à associer aux commentaires générés</param>
|
||||
/// <returns>Liste de commentaires générés</returns>
|
||||
public List<Commentaire> GenerateCommentaires(int count, List<Titre> titres)
|
||||
{
|
||||
var commentaires = new List<Commentaire>();
|
||||
var random = new Random();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var randomTitre = titres[random.Next(titres.Count)];
|
||||
|
||||
commentaires.Add(new Commentaire
|
||||
{
|
||||
IdCommentaire = i + 1,
|
||||
Contenu = Lorem.Paragraph(),
|
||||
Auteur = Name.FullName(),
|
||||
DateCreation = DateTime.Now.AddDays(-random.Next(1, 30)), // Commentaires créés dans les 30 derniers jours
|
||||
IdTitre = randomTitre.IdTitre,
|
||||
Titre = randomTitre
|
||||
});
|
||||
|
||||
randomTitre.Commentaires.Add(commentaires.Last());
|
||||
}
|
||||
|
||||
return commentaires;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,169 @@
|
||||
namespace Webzine.Entity.Fixtures;
|
||||
// <copyright file="SeedDataLocal.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.Entity.Fixtures;
|
||||
|
||||
using Bogus;
|
||||
|
||||
/// <summary>
|
||||
/// Classe pour générer des données de test locales, telles que des listes d'artistes, de
|
||||
/// titres, de styles, de commentaires et d'albums, afin de faciliter le peuplement de la base de données
|
||||
/// et les données lcoales.
|
||||
/// </summary>
|
||||
public class SeedDataLocal
|
||||
{
|
||||
/// <summary>
|
||||
/// Génére une liste d'artiste.
|
||||
/// </summary>
|
||||
/// <param name="nombre">Nombre d'artiste.</param>
|
||||
/// <returns>Liste d'artiste.</returns>
|
||||
public static List<Artiste> GenererListeArtiste(int nombre)
|
||||
{
|
||||
int idStart = 1;
|
||||
Faker<Artiste> artistes = new Faker<Artiste>("fr")
|
||||
.RuleFor(a => a.IdArtiste, f => f.IndexFaker + idStart) // Créé les id des artistes de manière incrémentale
|
||||
.RuleFor(a => a.Nom, f => f.Person.FullName)
|
||||
.RuleFor(a => a.Biographie, f => f.Lorem.Paragraph(2));
|
||||
|
||||
return artistes.Generate(nombre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génére une liste de titres.
|
||||
/// </summary>
|
||||
/// <param name="count">Nombre de titres à créer.</param>
|
||||
/// <param name="artistes">Liste d'artistes.</param>
|
||||
/// <param name="styles">Liste de styles.</param>
|
||||
/// <param name="albums">Liste d'albums.</param>
|
||||
/// <returns>Liste de titres.</returns>
|
||||
public static List<Titre> GenererListeTitre(
|
||||
int count,
|
||||
List<Artiste> artistes,
|
||||
List<Style> styles,
|
||||
List<string> albums)
|
||||
{
|
||||
Random random = new Random();
|
||||
int idStart = 1;
|
||||
|
||||
Faker<Titre> faker = new Faker<Titre>("fr")
|
||||
.RuleFor(a => a.IdTitre, f => f.IndexFaker + idStart)
|
||||
.RuleFor(t => t.Libelle, f => f.Lorem.Sentence(3).Replace(".", string.Empty))
|
||||
.RuleFor(t => t.Chronique, f => f.Lorem.Paragraphs(3))
|
||||
.RuleFor(t => t.DateCreation, f => DateTime.SpecifyKind(f.Date.Recent(120), DateTimeKind.Utc))
|
||||
.RuleFor(t => t.DateSortie, (f, t) => DateTime.SpecifyKind(f.Date.Past(10, t.DateCreation), DateTimeKind.Utc))
|
||||
.RuleFor(t => t.Duree, f => f.Random.Int(120, 420))
|
||||
.RuleFor(t => t.UrlJaquette, f => $"https://picsum.photos/seed/{Guid.NewGuid():N}/640/640")
|
||||
.RuleFor(t => t.UrlEcoute, f => $"https://example.com/listen/{Guid.NewGuid():N}")
|
||||
.RuleFor(t => t.NbLectures, f => f.Random.Int(0, 5000))
|
||||
.RuleFor(t => t.NbLikes, f => f.Random.Int(0, 1000))
|
||||
.RuleFor(t => t.Album, f => f.PickRandom(albums))
|
||||
.RuleFor(t => t.Artiste, f => f.PickRandom(artistes))
|
||||
.RuleFor(t => t.Commentaires, f => new List<Commentaire>());
|
||||
List<Titre> titres = faker.Generate(count);
|
||||
|
||||
foreach (Titre titre in titres)
|
||||
{
|
||||
int nbStyles = random.Next(1, 4);
|
||||
|
||||
titre.Styles = styles
|
||||
.OrderBy(_ => Guid.NewGuid())
|
||||
.Take(nbStyles)
|
||||
.ToList();
|
||||
|
||||
titre.IdArtiste = titre.Artiste.IdArtiste;
|
||||
}
|
||||
|
||||
return titres;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génére une liste de styles pour seeder la base
|
||||
/// de données.
|
||||
/// </summary>
|
||||
/// <param name="minCount">Le nombre minimum de styles pouvant être créés.</params>
|
||||
/// <param name="maxCount">Le nombre maximun de styles pouvant être créés.</params>
|
||||
/// <returns>Liste de styles.</returns>
|
||||
public static List<Style> GenererListeStyle(int minCount = 15, int maxCount = 20)
|
||||
{
|
||||
List<string> libelles = new List<string>
|
||||
{
|
||||
"Pop",
|
||||
"Rock",
|
||||
"Jazz",
|
||||
"Blues",
|
||||
"Hip-Hop",
|
||||
"Rap",
|
||||
"Electro",
|
||||
"Techno",
|
||||
"House",
|
||||
"Metal",
|
||||
"Funk",
|
||||
"Soul",
|
||||
"R&B",
|
||||
"Classique",
|
||||
"Reggae",
|
||||
"Punk",
|
||||
"Folk",
|
||||
"Disco",
|
||||
"Ambient",
|
||||
"Indie",
|
||||
};
|
||||
|
||||
Random random = new Random();
|
||||
int count = random.Next(minCount, maxCount + 1);
|
||||
|
||||
return libelles
|
||||
.Take(count)
|
||||
.Select((libelle, index) => new Style
|
||||
{
|
||||
IdStyle = index + 1,
|
||||
Libelle = libelle,
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génére une liste de commentaires pour seeder la base
|
||||
/// de données.
|
||||
/// </summary>
|
||||
/// <param name="titre">Titre.</param>
|
||||
/// <param name="min">Le nombre minimum de commentaires pouvant être créés. La valeur par défaut est 0.</params>
|
||||
/// <param name="max">Le nombre maximun de commentaires pouvant être créés. La valeur par défaut est 5.</params>
|
||||
/// <returns>Liste de commentaire.</returns>
|
||||
public static List<Commentaire> GenererListeCommentaire(Titre titre, int min = 0, int max = 5, int idStart = 1)
|
||||
{
|
||||
Random random = new Random();
|
||||
int count = random.Next(min, max + 1);
|
||||
|
||||
Faker<Commentaire> faker = new Faker<Commentaire>("fr")
|
||||
.RuleFor(a => a.IdCommentaire, f => f.IndexFaker + idStart)
|
||||
.RuleFor(c => c.Auteur, f => f.Internet.UserName())
|
||||
.RuleFor(c => c.Contenu, f => f.Lorem.Sentences(2))
|
||||
.RuleFor(c => c.DateCreation, f => DateTime.SpecifyKind(f.Date.Recent(60), DateTimeKind.Utc))
|
||||
.RuleFor(c => c.Titre, _ => titre)
|
||||
.RuleFor(c => c.IdTitre, _ => titre.IdTitre);
|
||||
|
||||
return faker.Generate(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génére une liste d'albums pour seeder la base
|
||||
/// de données.
|
||||
/// </summary>
|
||||
/// <param name="nombre">Le nombre d'albums à générer.</param>
|
||||
/// <returns>Liste d'albums.</returns>
|
||||
public static List<string> GenererListeAlbums(int nombre)
|
||||
{
|
||||
Faker faker = new Faker("fr");
|
||||
|
||||
HashSet<string> albums = new HashSet<string>();
|
||||
|
||||
while (albums.Count < nombre)
|
||||
{
|
||||
albums.Add(faker.Company.CatchPhrase());
|
||||
}
|
||||
|
||||
return albums.ToList();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace Webzine.EntitiesContext;
|
||||
namespace Webzine.Entity.Fixtures;
|
||||
|
||||
public class SeedDataSpotify
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Faker;
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Fake
|
||||
{
|
||||
/// <summary>
|
||||
/// Classe de fabrique pour générer des données factices (fake data) pour les entités Artiste et Titre.
|
||||
/// </summary>
|
||||
public static class FakeDataFactory
|
||||
{
|
||||
//https://cdn-images.dzcdn.net/images/cover/311bba0fc112d15f72c8b5a65f0456c1/1900x1900-000000-80-0-0.jpg",
|
||||
/// <summary>
|
||||
/// Génère une liste d'artistes avec des données factices, incluant des titres associés à chaque artiste.
|
||||
/// </summary>
|
||||
/// <param name="count">Le nombre d'artistes à générer. Par défaut, 10 artistes seront générés.</param>
|
||||
/// <returns>Une liste d'objets Artiste avec des titres associés, générés de manière aléatoire.</returns>
|
||||
public static List<Artiste> GetArtistes(int count = 10)
|
||||
{
|
||||
var artistes = new List<Artiste>();
|
||||
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
artistes.Add(new Artiste
|
||||
{
|
||||
IdArtiste = i,
|
||||
Nom = Name.FullName(),
|
||||
Biographie = Lorem.Paragraph(),
|
||||
Titres = new List<Titre>()
|
||||
});
|
||||
}
|
||||
|
||||
return artistes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère une liste de titres avec des données factices, en associant chaque titre à un artiste existant.
|
||||
/// </summary>
|
||||
/// <param name="count">Le nombre de titres à générer. Par défaut, 40 titres seront générés.</param>
|
||||
/// <returns>Une liste d'objets Titre avec des données factices, associés à des artistes générés de manière aléatoire.</returns>
|
||||
public static List<Titre> GetTitres(int count = 40)
|
||||
{
|
||||
var artistes = GetArtistes();
|
||||
var titres = new List<Titre>();
|
||||
|
||||
for (int i = 1; i <= count; i++)
|
||||
{
|
||||
var artiste = artistes[RandomNumber.Next(0, artistes.Count - 1)];
|
||||
|
||||
var titre = new Titre
|
||||
{
|
||||
IdTitre = i,
|
||||
IdArtiste = artiste.IdArtiste,
|
||||
Artiste = artiste,
|
||||
Libelle = Lorem.Sentence(3),
|
||||
Chronique = Lorem.Paragraph(),
|
||||
DateCreation = DateTime.Now.AddDays(-RandomNumber.Next(1, 100)),
|
||||
DateSortie = DateTime.Now.AddYears(-RandomNumber.Next(1, 20)),
|
||||
Duree = RandomNumber.Next(120, 420),
|
||||
UrlJaquette = "https://picsum.photos/300",
|
||||
UrlEcoute = Internet.Url(),
|
||||
NbLectures = RandomNumber.Next(0, 500),
|
||||
NbLikes = RandomNumber.Next(0, 200),
|
||||
Album = Lorem.Sentence(2),
|
||||
Commentaires = new List<Commentaire>()
|
||||
};
|
||||
|
||||
titres.Add(titre);
|
||||
artiste.Titres.Add(titre);
|
||||
}
|
||||
|
||||
return titres;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Webzine.Entity
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// Classe représentant un style de musique.
|
||||
/// </summary>
|
||||
@@ -29,4 +26,4 @@ namespace Webzine.Entity
|
||||
/// </summary>
|
||||
public List<Titre> Titres { get; set; } = new List<Titre>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
|
||||
namespace Webzine.Entity
|
||||
namespace Webzine.Entity
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
/// <summary>
|
||||
/// Classe représentant un titre de musique.
|
||||
/// Lien avec l'entité <see cref="Artiste"/> : un artiste peut avoir plusieurs titres, mais un titre n'a qu'un seul artiste.
|
||||
@@ -110,4 +107,4 @@ namespace Webzine.Entity
|
||||
/// </summary>
|
||||
public List<Style> Styles { get; set; } = new List<Style>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,48 @@
|
||||
// using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Contracts
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Défini une interface <see cref="IArtisteRepository"/> pour gérer les opérations de base de données liées aux artistes.
|
||||
/// </summary>
|
||||
public interface IArtisteRepository
|
||||
{
|
||||
// void Add(Artiste artiste);
|
||||
/// <summary>
|
||||
/// Ajoute un nouvel artiste.
|
||||
/// </summary>
|
||||
/// <param name="artiste">L'artiste à ajouter à la collection. Ne peut pas être null.</param>
|
||||
void Add(Artiste artiste);
|
||||
|
||||
// void Delete(Artiste artiste);
|
||||
/// <summary>
|
||||
/// Supprime un artiste.
|
||||
/// </summary>
|
||||
/// <param name="artiste">L'artiste à supprimer.</param>
|
||||
void Delete(Artiste artiste);
|
||||
|
||||
// Artiste Find(int id);
|
||||
/// <summary>
|
||||
/// Récupère un artiste par son identifiant unique. Si aucun artiste n'est trouvé, retourne null.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant de l'artiste.</param>
|
||||
/// <returns></returns>
|
||||
Artiste Find(int id);
|
||||
|
||||
// IEnumerable<Artiste> FindAll();
|
||||
/// <summary>
|
||||
/// Récupère un artiste par son nom. Si aucun artiste n'est trouvé, retourne null.
|
||||
/// </summary>
|
||||
/// <param name="name">Le nom de l'artiste.</param>
|
||||
/// <returns>L'artiste recherché ou null.</returns>
|
||||
Artiste FindByName(string name);
|
||||
|
||||
// void Update(Artiste artiste);
|
||||
/// <summary>
|
||||
/// Récupère tous les artistes disponibles dans la collection. Si aucun artiste n'est trouvé, retourne une collection vide.
|
||||
/// </summary>
|
||||
/// <returns>Retourne une collection d'artistes.</returns>
|
||||
IEnumerable<Artiste> FindAll();
|
||||
|
||||
/// <summary>
|
||||
/// Met à jour les informations d'un artiste existant dans la collection.
|
||||
/// </summary>
|
||||
/// <param name="artiste">L'artiste à mettre à jour.</param>
|
||||
void Update(Artiste artiste);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
// using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Contracts
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
public interface ICommentaireRepository
|
||||
{
|
||||
// void Add(Commentaire commentaire);
|
||||
void Add(Commentaire commentaire);
|
||||
|
||||
// void Delete(Commentaire commentaire);
|
||||
void Delete(Commentaire commentaire);
|
||||
|
||||
// Commentaire Find(int id);
|
||||
Commentaire Find(int id);
|
||||
|
||||
// IEnumerable<Commentaire> FindAll();
|
||||
IEnumerable<Commentaire> FindAll();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,41 @@
|
||||
// using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Contracts
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Interface définissant les opérations de base pour le repository de styles, permettant d'ajouter, supprimer, trouver et mettre à jour des styles dans la source de données.
|
||||
/// </summary>
|
||||
public interface IStyleRepository
|
||||
{
|
||||
// void Add(Style style);
|
||||
/// <summary>
|
||||
/// Ajoute un style à la liste des styles.
|
||||
/// </summary>
|
||||
/// <param name="style">L'objet style à ajouter.</param>
|
||||
void Add(Style style);
|
||||
|
||||
// void Delete(Style style);
|
||||
/// <summary>
|
||||
/// Supprime un style de la liste des styles.
|
||||
/// </summary>
|
||||
/// <param name="style">L'objet style à supprimer.</param>
|
||||
void Delete(Style style);
|
||||
|
||||
// Style Find(int id);
|
||||
/// <summary>
|
||||
/// Trouve un style dans la liste des styles en fonction de son identifiant.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du style à trouver.</param>
|
||||
/// <returns>Le style correspondant à l'identifiant fourni, ou null si aucun style n'est trouvé.</returns>
|
||||
Style Find(int id);
|
||||
|
||||
// IEnumerable<Style> FindAll();
|
||||
/// <summary>
|
||||
/// Trouve tous les styles dans la liste des styles.
|
||||
/// </summary>
|
||||
/// <returns>Une collection de tous les styles présents dans la liste.</returns>
|
||||
IEnumerable<Style> FindAll();
|
||||
|
||||
// void Update(Style style);
|
||||
/// <summary>
|
||||
/// Met à jour un style dans la liste des styles en fonction de son identifiant.
|
||||
/// </summary>
|
||||
/// <param name="style">L'objet style à mettre à jour.</param
|
||||
void Update(Style style);
|
||||
}
|
||||
}
|
||||
81
Webzine.Repository.Contracts/ITitreRepository.cs
Normal file
81
Webzine.Repository.Contracts/ITitreRepository.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
namespace Webzine.Repository.Contracts
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Interface qui définit les opérations de base pour la gestion des titres dans une source de données.
|
||||
/// </summary>
|
||||
public interface ITitreRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Ajoute un titre à la liste des titres.
|
||||
/// </summary>
|
||||
/// <param name="titre">L'objet titre à ajouter.</param>
|
||||
void Add(Titre titre);
|
||||
|
||||
/// <summary>
|
||||
/// Remonte le nombre de titres.
|
||||
/// </summary>
|
||||
/// <returns>Le nombre total de titres présents dans la liste après l'incrémentation du nombre de lectures.</returns>
|
||||
int Count();
|
||||
|
||||
/// <summary>
|
||||
/// Supprime un titre de la liste des titres.
|
||||
/// </summary>
|
||||
/// <param name="titre">L'objet titre à supprimer.</param>
|
||||
void Delete(Titre titre);
|
||||
|
||||
/// <summary>
|
||||
/// Trouve un titre dans la liste des titres en fonction de son identifiant.
|
||||
/// </summary>
|
||||
/// <param name="idTitre">L'identifiant du titre à trouver.</param>
|
||||
/// <returns>Le titre correspondant à l'identifiant fourni, ou null si aucun titre n'est trouvé.</returns>
|
||||
Titre Find(int idTitre);
|
||||
|
||||
/// <summary>
|
||||
/// Recherche les titres dans la liste des titres en fonction de l'offset et de la limite spécifiés, permettant ainsi une pagination des résultats.
|
||||
/// </summary>
|
||||
/// <param name="offset">Le nombre de titres à ignorer avant de commencer à retourner les résultats.</param>
|
||||
/// <param name="limit">Le nombre maximum de titres à retourner.</param>
|
||||
/// <returns>Une collection de titres correspondant au critère de pagination, triée par libellé.</returns>
|
||||
IEnumerable<Titre> FindTitres(int offset, int limit);
|
||||
|
||||
/// <summary>
|
||||
/// Trouve tous les titres dans la liste des titres.
|
||||
/// </summary>
|
||||
/// <returns>Une collection de tous les titres présents dans la liste.</returns>
|
||||
IEnumerable<Titre> FindAll();
|
||||
|
||||
/// <summary>
|
||||
/// Incrémente le nombre de lectures d'un titre donné. Si le titre est null, un message d'avertissement est enregistré dans les logs et aucune action n'est effectuée.
|
||||
/// </summary>
|
||||
/// <param name="titre">L'objet titre dont le nombre de lectures doit être incrémenté.</param>
|
||||
void IncrementNbLectures(Titre titre);
|
||||
|
||||
/// <summary>
|
||||
/// Incrémente le nombre de likes d'un titre donné. Si le titre est null, un message d'avertissement est enregistré dans les logs et aucune action n'est effectuée.
|
||||
/// </summary>
|
||||
/// <param name="titre">L'objet titre dont le nombre de likes doit être incrémenté.</param>
|
||||
void IncrementNbLikes(Titre titre);
|
||||
|
||||
/// <summary>
|
||||
/// Recherche les titres dont le libellé contient le mot spécifié, en ignorant la casse.
|
||||
/// </summary>
|
||||
/// <param name="mot">Le mot à rechercher dans les libellés des titres.</param>
|
||||
/// <returns>Une collection de titres correspondant au critère de recherche, triée par libellé.</returns>
|
||||
IEnumerable<Titre> Search(string mot);
|
||||
|
||||
/// <summary>
|
||||
/// Recherche les titres associés à un style dont le libellé contient la chaîne spécifiée, en ignorant la casse.
|
||||
/// </summary>
|
||||
/// <param name="libelle">Le libellé du style à rechercher dans les titres.</param>
|
||||
/// <returns>Une collection de titres correspondant au critère de recherche, triée par libellé.</returns>
|
||||
IEnumerable<Titre> SearchByStyle(string libelle);
|
||||
|
||||
/// <summary>
|
||||
/// Met à jour un titre dans la liste des titres en fonction de son identifiant. Si aucun titre correspondant à l'identifiant du titre fourni n'est trouvé, un message d'avertissement est enregistré dans les logs et aucune mise à jour n'est effectuée.
|
||||
/// </summary>
|
||||
/// <param name="titre">L'objet titre à mettre à jour.</param>
|
||||
void Update(Titre titre);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.Repository.Contracts
|
||||
{
|
||||
public interface ITitreRepository
|
||||
{
|
||||
// void Add(Titre titre);
|
||||
|
||||
// int Count();
|
||||
|
||||
// void Delete(Titre titre);
|
||||
|
||||
Titre? Find(int idTitre);
|
||||
|
||||
// IEnumerable<Titre> FindTitres(int offset, int limit);
|
||||
|
||||
IEnumerable<Titre> FindAll();
|
||||
|
||||
// void IncrementNbLectures(Titre titre);
|
||||
|
||||
// void IncrementNbLikes(Titre titre);
|
||||
|
||||
IEnumerable<Titre> Search(string mot);
|
||||
|
||||
IEnumerable<Titre> SearchByStyle(string libelle);
|
||||
|
||||
// void Update(Titre titre);
|
||||
}
|
||||
}
|
||||
160
Webzine.Repository/DbArtisteRepository.cs
Normal file
160
Webzine.Repository/DbArtisteRepository.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
// <copyright file="DbArtisteRepository.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.Repository
|
||||
{
|
||||
using System.Data.Common;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Webzine.EntitiesContext;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une classe <see cref="DbArtisteRepository"/> qui implémente l'interface <see cref="IArtisteRepository"/> pour gérer les opérations de base de données liées aux artistes.
|
||||
/// Utilise <see cref="IArtisteRepository"/> en injection de dépendances.
|
||||
/// </summary>
|
||||
public class DbArtisteRepository : IArtisteRepository
|
||||
{
|
||||
private readonly WebzineDbContext context;
|
||||
private readonly ILogger<LocalArtisteRepository> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DbArtisteRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="context">Le contexte de base de données à utiliser pour accéder aux entités et effectuer des opérations de
|
||||
/// persistance. Ne peut pas être null.</param>
|
||||
public DbArtisteRepository(WebzineDbContext context, ILogger<LocalArtisteRepository> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Artiste artiste)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.context.Artistes.Add(artiste);
|
||||
this.context.SaveChanges();
|
||||
}
|
||||
catch (DbUpdateException dbex)
|
||||
{
|
||||
this.logger.LogError(dbex, "Erreur de base de données lors de l'ajout de l'artiste: {id}", artiste.IdArtiste);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Une erreur est survenue lors de l'ajout de l'artiste {Nom}.", artiste?.Nom);
|
||||
throw new Exception("Une erreur est survenue lors de l'ajout de l'artiste.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Artiste artiste)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (artiste == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(artiste), "L'artiste à supprimer ne peut pas être null.");
|
||||
}
|
||||
|
||||
this.context.Artistes.Remove(artiste);
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("L'artiste {IdArtiste} a bien été supprimé", artiste.IdArtiste);
|
||||
}
|
||||
catch (DbUpdateException dbex)
|
||||
{
|
||||
this.logger.LogError(dbex, "Erreur de base de données lors de la suppression de l'artiste: {Id}", artiste.IdArtiste);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Une erreur est survenue lors de la suppression de l'artiste {Nom}.", artiste?.Nom);
|
||||
throw new Exception("Une erreur est survenue lors de la suppression de l'artiste.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Artiste Find(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
Artiste artiste = this.context.Artistes
|
||||
.Include(a => a.Titres)
|
||||
.First(a => a.IdArtiste == id);
|
||||
return artiste;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche de l'artiste: {Id}", id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Artiste FindByName(string nom)
|
||||
{
|
||||
try
|
||||
{
|
||||
var artiste = this.context.Artistes
|
||||
.Include(a => a.Titres)
|
||||
.FirstOrDefault(a => a.Nom == nom);
|
||||
|
||||
return artiste;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche de l'artiste avec le nom: {Nom}", nom);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Artiste> FindAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
// .AsNoTracking() rend la requête beaucoup plus rapide pour de la lecture
|
||||
var artistes = this.context.Artistes.AsNoTracking().ToList();
|
||||
this.logger.LogDebug("{Count} artistes récupérés de la base.", artistes.Count);
|
||||
return artistes;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la récupération de tous les artistes.");
|
||||
return Enumerable.Empty<Artiste>(); // Retourne une liste vide au lieu de faire crash l'UI
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(Artiste artiste)
|
||||
{
|
||||
if (artiste == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(artiste));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.context.Artistes.Update(artiste);
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Artiste {Id} ({Nom}) mis à jour avec succès.", artiste.IdArtiste, artiste.Nom);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de la mise à jour de l'artiste ID: {IdArtiste}", artiste.IdArtiste);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la mise à jour de l'artiste {Id}.", artiste.IdArtiste);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
141
Webzine.Repository/DbCommentaireRepository.cs
Normal file
141
Webzine.Repository/DbCommentaireRepository.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
namespace Webzine.Repository;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Webzine.EntitiesContext;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui implémente le repository pour les commentaires en utilisant une base de données.
|
||||
/// </summary>
|
||||
public class DbCommentaireRepository : ICommentaireRepository
|
||||
{
|
||||
private readonly ILogger<DbCommentaireRepository> logger;
|
||||
private readonly WebzineDbContext context;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DbCommentaireRepository"/> class.
|
||||
/// Initialisation de <see cref="DbCommentaireRepository"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
|
||||
/// <param name="context">Le contexte de base de données injecté.</param>
|
||||
public DbCommentaireRepository(ILogger<DbCommentaireRepository> logger, WebzineDbContext context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.context = context;
|
||||
this.logger.LogDebug("NLog injecté dans DbCommentaireRepository");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Commentaire commentaire)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Ajout d'un nouveau commentaire de l'auteur : {Auteur}", commentaire.Auteur);
|
||||
this.context.Commentaires.Add(commentaire);
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Commentaire ajouté avec l'id : {Id}", commentaire.IdCommentaire);
|
||||
}
|
||||
catch (DbUpdateException dbex)
|
||||
{
|
||||
this.logger.LogError(dbex, "Erreur de base de données lors de l'ajout du commentaire de l'auteur : {Auteur}", commentaire?.Auteur);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Une erreur est survenue lors de l'ajout d'un commentaire.");
|
||||
throw new Exception("Une erreur est survenue lors de l'ajout du commentaire.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Commentaire commentaire)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (commentaire == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(commentaire), "Le commentaire à supprimer ne peut pas être null.");
|
||||
}
|
||||
|
||||
this.context.Commentaires.Remove(commentaire);
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Le commentaire {IdCommentaire} a bien été supprimé", commentaire.IdCommentaire);
|
||||
}
|
||||
catch (DbUpdateException dbex)
|
||||
{
|
||||
this.logger.LogError(dbex, "Erreur de base de données lors de la suppression du commentaire : {Id}", commentaire.IdCommentaire);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Une erreur est survenue lors de la suppression du commentaire {Id}.", commentaire?.IdCommentaire);
|
||||
throw new Exception("Une erreur est survenue lors de la suppression du commentaire.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count()
|
||||
{
|
||||
var count = this.context.Commentaires.Count();
|
||||
this.logger.LogDebug("Compte total des commentaires : {Count}", count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Commentaire Find(int idCommentaire)
|
||||
{
|
||||
this.logger.LogDebug("Recherche du commentaire avec l'id : {Id}", idCommentaire);
|
||||
|
||||
// On inclut le titre car il est souvent affiché avec le commentaire
|
||||
return this.context.Commentaires
|
||||
.Include(c => c.Titre)
|
||||
.FirstOrDefault(c => c.IdCommentaire == idCommentaire);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Commentaire> FindAll()
|
||||
{
|
||||
this.logger.LogDebug("Récupération de tous les commentaires");
|
||||
|
||||
var commentaires = this.context.Commentaires
|
||||
.Include(c => c.Titre)
|
||||
.OrderByDescending(c => c.DateCreation)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("Nombre de commentaires trouvés : {Count}", commentaires.Count);
|
||||
return commentaires;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Commentaire> FindCommentaires(int offset, int limit)
|
||||
{
|
||||
this.logger.LogDebug("Recherche paginée des commentaires (offset : {Offset}, limit : {Limit})", offset, limit);
|
||||
|
||||
var commentaires = this.context.Commentaires
|
||||
.Include(c => c.Titre)
|
||||
.OrderByDescending(c => c.DateCreation)
|
||||
.Skip(offset)
|
||||
.Take(limit)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("{Count} commentaires trouvés pour cette page", commentaires.Count);
|
||||
return commentaires;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Commentaire> FindByIdTitre(int idTitre)
|
||||
{
|
||||
this.logger.LogDebug("Recherche des commentaires pour le titre ID : {IdTitre}", idTitre);
|
||||
|
||||
var commentaires = this.context.Commentaires
|
||||
.Where(c => c.Titre.IdTitre == idTitre)
|
||||
.OrderByDescending(c => c.DateCreation)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug($"{commentaires.Count} commentaires trouvés pour l'ID de titre : {idTitre}");
|
||||
return commentaires;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,63 @@
|
||||
namespace Webzine.Repository;
|
||||
// <copyright file="DbEntityRepository.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
public class DbEntityRepository
|
||||
namespace Webzine.Repository
|
||||
{
|
||||
|
||||
using Webzine.EntitiesContext;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
|
||||
public class DbEntityRepository
|
||||
{
|
||||
private readonly WebzineDbContext context;
|
||||
|
||||
public DbEntityRepository(WebzineDbContext context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seed la base de donnée à l'aide de SeedDataLocal.
|
||||
/// </summary>
|
||||
/// <param name="nbArtistes">Nombre d'artiste.</param>
|
||||
/// <param name="nbTitres">Nombre de titre.</param>
|
||||
/// <param name="minStyles">Nombre min de style.</param>
|
||||
/// <param name="maxStyles">Nombre mac de style.</param>
|
||||
public void SeedBaseDeDonnees(
|
||||
int nbArtistes = 100,
|
||||
int nbTitres = 500,
|
||||
int minStyles = 15,
|
||||
int maxStyles = 20)
|
||||
{
|
||||
if (this.context.Artistes.Any() ||
|
||||
this.context.Titres.Any() ||
|
||||
this.context.Styles.Any() ||
|
||||
this.context.Commentaires.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Artiste> artistes = SeedDataLocal.GenererListeArtiste(nbArtistes);
|
||||
List<Style> styles = SeedDataLocal.GenererListeStyle(minStyles, maxStyles);
|
||||
|
||||
this.context.Artistes.AddRange(artistes);
|
||||
this.context.Styles.AddRange(styles);
|
||||
this.context.SaveChanges();
|
||||
|
||||
List<string> albums = SeedDataLocal.GenererListeAlbums(3);
|
||||
List<Titre> titres = SeedDataLocal.GenererListeTitre(nbTitres, artistes, styles, albums);
|
||||
|
||||
int commentaireIdStart = 1;
|
||||
foreach (var titre in titres)
|
||||
{
|
||||
var commentairesDuTitre = SeedDataLocal.GenererListeCommentaire(titre, 0, 5, commentaireIdStart);
|
||||
titre.Commentaires.AddRange(commentairesDuTitre);
|
||||
commentaireIdStart += commentairesDuTitre.Count;
|
||||
}
|
||||
|
||||
this.context.Titres.AddRange(titres);
|
||||
this.context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
180
Webzine.Repository/DbStyleRepository.cs
Normal file
180
Webzine.Repository/DbStyleRepository.cs
Normal file
@@ -0,0 +1,180 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Webzine.EntitiesContext;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
namespace Webzine.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui implémente le repository pour les styles en utilisant une base de données.
|
||||
/// </summary>
|
||||
public class DbStyleRepository : IStyleRepository
|
||||
{
|
||||
private readonly ILogger<DbStyleRepository> logger;
|
||||
private readonly WebzineDbContext context;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DbStyleRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
|
||||
/// <param name="context">Le contexte de base de données injecté.</param>
|
||||
public DbStyleRepository(ILogger<DbStyleRepository> logger, WebzineDbContext context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.context = context;
|
||||
this.logger.LogDebug(1, "NLog injecté dans DbStyleRepository");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Style style)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Ajout d'un nouveau style: {Libelle}", style.Libelle);
|
||||
this.logger.LogDebug("Début de l'ajout du style en base de données");
|
||||
|
||||
this.context.Styles.Add(style);
|
||||
this.context.SaveChanges();
|
||||
|
||||
this.logger.LogDebug("Style ajouté avec succès avec l'ID: {IdStyle}", style.IdStyle);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de l'ajout du style: {Libelle}", style.Libelle);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de l'ajout du style: {Libelle}", style.Libelle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Style style)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Suppression du style avec l'ID: {IdStyle}", style.IdStyle);
|
||||
this.logger.LogDebug("Vérification de l'existence du style en base de données");
|
||||
|
||||
// Check if style exists
|
||||
var existingStyle = this.context.Styles.Find(style.IdStyle);
|
||||
if (existingStyle == null)
|
||||
{
|
||||
this.logger.LogWarning("Style avec l'ID {IdStyle} non trouvé pour la suppression", style.IdStyle);
|
||||
throw new InvalidOperationException($"Style avec l'ID {style.IdStyle} non trouvé.");
|
||||
}
|
||||
|
||||
this.logger.LogDebug("Style trouvé, suppression en cours");
|
||||
this.context.Styles.Remove(existingStyle);
|
||||
this.context.SaveChanges();
|
||||
|
||||
this.logger.LogDebug("Style supprimé avec succès: {IdStyle}", style.IdStyle);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de la suppression du style ID: {IdStyle}", style.IdStyle);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la suppression du style ID: {IdStyle}", style.IdStyle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Style Find(int id)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Recherche du style avec l'ID: {Id}", id);
|
||||
|
||||
if (id <= 0)
|
||||
{
|
||||
this.logger.LogWarning("Tentative de recherche d'un style avec un Id invalide: {Id}", id);
|
||||
return new Style();
|
||||
}
|
||||
|
||||
this.logger.LogDebug("Préparation de la requête avec inclusion des titres");
|
||||
var style = this.context.Styles
|
||||
.Include(s => s.Titres)
|
||||
.FirstOrDefault(s => s.IdStyle == id);
|
||||
|
||||
if (style == null)
|
||||
{
|
||||
this.logger.LogWarning("Style avec l'ID {Id} non trouvé", id);
|
||||
style = new Style();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logger.LogDebug("Style trouvé: {Libelle}", style.Libelle);
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche du style avec l'ID: {Id}", id);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Style> FindAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Récupération de tous les styles");
|
||||
this.logger.LogDebug("Tri des styles par libellé");
|
||||
|
||||
var styles = this.context.Styles
|
||||
.OrderBy(s => s.Libelle)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("{Count} styles récupérés", styles.Count);
|
||||
return styles;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la récupération de tous les styles");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(Style style)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Mise à jour du style avec l'ID: {IdStyle}", style.IdStyle);
|
||||
this.logger.LogDebug("Recherche du style en base de données");
|
||||
|
||||
var existingStyle = this.context.Styles.Find(style.IdStyle);
|
||||
if (existingStyle == null)
|
||||
{
|
||||
this.logger.LogWarning("Style avec l'ID {IdStyle} non trouvé pour la mise à jour", style.IdStyle);
|
||||
throw new InvalidOperationException($"Style avec l'ID {style.IdStyle} non trouvé.");
|
||||
}
|
||||
|
||||
// Update properties
|
||||
this.logger.LogDebug("Style trouvé, mise à jour des propriétés");
|
||||
existingStyle.Libelle = style.Libelle;
|
||||
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Style mis à jour avec succès: {IdStyle}", style.IdStyle);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de la mise à jour du style ID: {IdStyle}", style.IdStyle);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la mise à jour du style ID: {IdStyle}", style.IdStyle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
335
Webzine.Repository/DbTitreRepository.cs
Normal file
335
Webzine.Repository/DbTitreRepository.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Webzine.EntitiesContext;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
namespace Webzine.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui implémente le repository pour les titres en utilisant une base de données.
|
||||
/// </summary>
|
||||
public class DbTitreRepository : ITitreRepository
|
||||
{
|
||||
private readonly ILogger<DbTitreRepository> logger;
|
||||
private readonly WebzineDbContext context;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DbTitreRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
|
||||
/// <param name="context">Le contexte de base de données injecté.</param>
|
||||
public DbTitreRepository(ILogger<DbTitreRepository> logger, WebzineDbContext context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.context = context;
|
||||
this.logger.LogDebug(1, "NLog injecté dans DbTitreRepository");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Titre titre)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Ajout d'un nouveau titre: {Libelle}", titre.Libelle);
|
||||
this.logger.LogDebug("Début de l'ajout du titre en base de données");
|
||||
|
||||
this.context.Titres.Add(titre);
|
||||
this.context.SaveChanges();
|
||||
|
||||
this.logger.LogDebug("Titre ajouté avec succès avec l'ID: {IdTitre}", titre.IdTitre);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de l'ajout du titre: {Libelle}", titre.Libelle);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de l'ajout du titre: {Libelle}", titre.Libelle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Comptage des titres en base de données");
|
||||
var count = this.context.Titres.Count();
|
||||
this.logger.LogDebug("Nombre total de titres: {Count}", count);
|
||||
return count;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors du comptage des titres");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Titre titre)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Suppression du titre avec l'ID: {IdTitre}", titre.IdTitre);
|
||||
this.logger.LogDebug("Début de la suppression du titre en base de données");
|
||||
|
||||
this.context.Titres.Remove(titre);
|
||||
this.context.SaveChanges();
|
||||
|
||||
this.logger.LogDebug("Titre supprimé avec succès: {IdTitre}", titre.IdTitre);
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de la suppression du titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la suppression du titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> FindTitres(int offset, int limit)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Recherche des titres avec offset: {Offset}, limit: {Limit}", offset, limit);
|
||||
this.logger.LogDebug("Préparation de la requête avec les inclusions Artiste et Styles");
|
||||
|
||||
var titres = this.context.Titres
|
||||
.Include(t => t.Artiste)
|
||||
.Include(t => t.Styles)
|
||||
.OrderBy(t => t.Libelle)
|
||||
.Skip(offset)
|
||||
.Take(limit)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("{Count} titres trouvés", titres.Count);
|
||||
return titres;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche des titres avec offset: {Offset}, limit: {Limit}", offset, limit);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void IncrementNbLectures(Titre titre)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Incrémentation du nombre de lectures pour le titre ID: {IdTitre}", titre.IdTitre);
|
||||
this.logger.LogDebug("Recherche du titre en base de données");
|
||||
|
||||
var existingTitre = this.context.Titres.Find(titre.IdTitre);
|
||||
if (existingTitre != null)
|
||||
{
|
||||
this.logger.LogDebug("Titre trouvé, incrémentation du compteur de lectures");
|
||||
existingTitre.NbLectures++;
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Nouveau nombre de lectures: {NbLectures}", existingTitre.NbLectures);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logger.LogWarning("Titre avec l'ID {IdTitre} non trouvé pour l'incrémentation des lectures", titre.IdTitre);
|
||||
}
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de l'incrémentation des lectures pour le titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de l'incrémentation des lectures pour le titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void IncrementNbLikes(Titre titre)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Incrémentation du nombre de likes pour le titre ID: {IdTitre}", titre.IdTitre);
|
||||
this.logger.LogDebug("Recherche du titre en base de données");
|
||||
|
||||
var existingTitre = this.context.Titres.Find(titre.IdTitre);
|
||||
if (existingTitre != null)
|
||||
{
|
||||
this.logger.LogDebug("Titre trouvé, incrémentation du compteur de likes");
|
||||
existingTitre.NbLikes++;
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Nouveau nombre de likes: {NbLikes}", existingTitre.NbLikes);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logger.LogWarning("Titre avec l'ID {IdTitre} non trouvé pour l'incrémentation des likes", titre.IdTitre);
|
||||
}
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de l'incrémentation des likes pour le titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de l'incrémentation des likes pour le titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(Titre titre)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Mise à jour du titre avec l'ID: {IdTitre}", titre.IdTitre);
|
||||
this.logger.LogDebug("Début de la mise à jour du titre en base de données");
|
||||
|
||||
var existingTitre = this.context.Titres.Find(titre.IdTitre);
|
||||
if (existingTitre != null)
|
||||
{
|
||||
this.logger.LogDebug("Titre trouvé, mise à jour des propriétés");
|
||||
this.context.Entry(existingTitre).CurrentValues.SetValues(titre);
|
||||
|
||||
// Handle many-to-many relationships
|
||||
this.logger.LogDebug("Mise à jour des relations many-to-many (Styles)");
|
||||
this.context.Entry(existingTitre).Collection(t => t.Styles).Load();
|
||||
existingTitre.Styles.Clear();
|
||||
foreach (var style in titre.Styles)
|
||||
{
|
||||
existingTitre.Styles.Add(style);
|
||||
}
|
||||
|
||||
this.context.SaveChanges();
|
||||
this.logger.LogDebug("Titre mis à jour avec succès: {IdTitre}", titre.IdTitre);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.logger.LogWarning("Titre avec l'ID {IdTitre} non trouvé pour la mise à jour", titre.IdTitre);
|
||||
throw new InvalidOperationException($"Titre avec l'ID {titre.IdTitre} non trouvé.");
|
||||
}
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur de base de données lors de la mise à jour du titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la mise à jour du titre ID: {IdTitre}", titre.IdTitre);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> Search(string mot)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Recherche des titres avec le mot-clé: {Mot}", mot);
|
||||
this.logger.LogDebug("Préparation de la requête de recherche avec les inclusions");
|
||||
|
||||
var titres = this.context.Titres
|
||||
.Include(t => t.Artiste)
|
||||
.Include(t => t.Styles)
|
||||
.Where(t => t.Libelle.ToLower().Contains(mot.ToLower()))
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("{Count} titres trouvés correspondant à '{Mot}'", titres.Count, mot);
|
||||
return titres;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche des titres avec le mot-clé: {Mot}", mot);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Titre Find(int idTitre)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Recherche du titre avec l'ID: {IdTitre}", idTitre);
|
||||
this.logger.LogDebug("Préparation de la requête avec les inclusions Artiste, Styles et Commentaires");
|
||||
|
||||
var titre = this.context.Titres
|
||||
.Include(t => t.Artiste)
|
||||
.Include(t => t.Styles)
|
||||
.Include(t => t.Commentaires)
|
||||
.First(t => t.IdTitre == idTitre);
|
||||
|
||||
this.logger.LogDebug("Titre trouvé: {Libelle}", titre.Libelle);
|
||||
return titre;
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
this.logger.LogWarning(ex, "Aucun titre trouvé avec l'ID: {IdTitre}", idTitre);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche du titre avec l'ID: {IdTitre}", idTitre);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> FindAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogDebug("Récupération de tous les titres");
|
||||
this.logger.LogDebug("Préparation de la requête avec les inclusions Artiste et Styles");
|
||||
|
||||
var titres = this.context.Titres
|
||||
.Include(t => t.Artiste)
|
||||
.Include(t => t.Styles)
|
||||
.Include(t => t.Commentaires)
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("{Count} titres récupérés", titres.Count);
|
||||
return titres;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la récupération de tous les titres");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> SearchByStyle(string libelle)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.logger.LogInformation("Recherche des titres par style: {Libelle}", libelle);
|
||||
this.logger.LogDebug("Préparation de la requête de recherche par style");
|
||||
|
||||
var titres = this.context.Titres
|
||||
.Include(t => t.Artiste)
|
||||
.Include(t => t.Styles)
|
||||
.Where(t => t.Styles.Any(s => s.Libelle.ToLower() == libelle.ToLower()))
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
|
||||
this.logger.LogDebug("{Count} titres trouvés pour le style '{Libelle}'", titres.Count, libelle);
|
||||
return titres;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Erreur lors de la recherche des titres par style: {Libelle}", libelle);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Webzine.Repository/InMemoryDataStore.cs
Normal file
32
Webzine.Repository/InMemoryDataStore.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Webzine.Repository
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// Représente un entrepôt de données en mémoire (Mock) pour l'application.
|
||||
/// Cette classe simule une base de données en stockant les entités dans des listes statiques
|
||||
/// durant le cycle de vie de l'application.
|
||||
/// </summary>
|
||||
public class InMemoryDataStore
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des artistes enregistrés.
|
||||
/// </summary>
|
||||
public List<Artiste> Artistes { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des titres (morceaux) musicaux.
|
||||
/// </summary>
|
||||
public List<Titre> Titres { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des styles musicaux disponibles.
|
||||
/// </summary>
|
||||
public List<Style> Styles { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des commentaires rédigés par les utilisateurs.
|
||||
/// </summary>
|
||||
public List<Commentaire> Commentaires { get; set; } = new ();
|
||||
}
|
||||
}
|
||||
89
Webzine.Repository/LocalArtisteRepository.cs
Normal file
89
Webzine.Repository/LocalArtisteRepository.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
// <copyright file="LocalArtisteRepository.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.Repository
|
||||
{
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une classe <see cref="LocalArtisteRepository"/> qui implémente l'interface <see cref="IArtisteRepository"/> pour gérer les opérations de base de données liées aux artistes.
|
||||
/// Utilise <see cref="IArtisteRepository"/> en injection de dépendances.
|
||||
/// </summary>
|
||||
public class LocalArtisteRepository : IArtisteRepository
|
||||
{
|
||||
private readonly ILogger<LocalArtisteRepository> logger;
|
||||
|
||||
// private readonly List<Artiste> artistes;
|
||||
private readonly InMemoryDataStore dataStore;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalArtisteRepository"/> class.
|
||||
/// Est liéee à une liste d'artistes en local et utilise un logger pour enregistrer les opérations effectuées sur les artistes.
|
||||
/// </summary>
|
||||
/// <param name="artistes">La liste des artistes à initialiser. Ne peut pas être null.</param>
|
||||
/// <param name="logger">Le logger à utiliser pour enregistrer les messages de journalisation. Ne peut pas être null.</param>
|
||||
public LocalArtisteRepository(InMemoryDataStore dataStore, ILogger<LocalArtisteRepository> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
|
||||
// this.artistes = artistes;
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Artiste artiste)
|
||||
{
|
||||
throw new NotSupportedException("Mode Local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Artiste artiste)
|
||||
{
|
||||
throw new NotSupportedException("Mode Local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Artiste Find(int id)
|
||||
{
|
||||
var artiste = this.dataStore.Artistes.First(a => a.IdArtiste == id);
|
||||
if (artiste == null)
|
||||
{
|
||||
return new Artiste();
|
||||
}
|
||||
|
||||
return artiste;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Artiste FindByName(string nom)
|
||||
{
|
||||
var artiste = this.dataStore.Artistes.FirstOrDefault(a => a.Nom == nom);
|
||||
|
||||
if (artiste != null)
|
||||
{
|
||||
artiste.Titres = this.dataStore.Titres
|
||||
.Where(t => t.IdArtiste == artiste.IdArtiste)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return artiste;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// La liste retournée est une copie de la liste interne, donc elle ne peut être nulle.
|
||||
public IEnumerable<Artiste> FindAll()
|
||||
{
|
||||
return this.dataStore.Artistes;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(Artiste artiste)
|
||||
{
|
||||
throw new NotSupportedException("Mode Local");
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Webzine.Repository/LocalCommentaireRepository.cs
Normal file
100
Webzine.Repository/LocalCommentaireRepository.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
// <copyright file="LocalCommentaireRepository.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.Repository
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une classe <see cref="LocalCommentaireRepository"/> qui implémente l'interface <see cref="ICommentaireRepository"/> pour gérer les opérations liées aux commentaires.
|
||||
/// Utilise <see cref="ICommentaireRepository"/> en injection de dépendances.
|
||||
/// </summary>
|
||||
public class LocalCommentaireRepository : ICommentaireRepository
|
||||
{
|
||||
private readonly ILogger<LocalCommentaireRepository> logger;
|
||||
private readonly InMemoryDataStore dataStore;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalCommentaireRepository"/> class.
|
||||
/// Initialise une nouvelle instance du <see cref="LocalCommentaireRepository"/> .
|
||||
/// Est liée à un magasin de données en mémoire et utilise un logger pour enregistrer les opérations.
|
||||
/// </summary>
|
||||
/// <param name="dataStore">Le magasin de données en mémoire. Ne peut pas être null.</param>
|
||||
/// <param name="logger">Le logger à utiliser pour enregistrer les messages de journalisation. Ne peut pas être null.</param>
|
||||
public LocalCommentaireRepository(InMemoryDataStore dataStore, ILogger<LocalCommentaireRepository> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.dataStore = dataStore;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Commentaire commentaire)
|
||||
{
|
||||
throw new NotSupportedException("Mode Local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Commentaire commentaire)
|
||||
{
|
||||
throw new NotSupportedException("Mode Local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count()
|
||||
{
|
||||
return this.dataStore.Commentaires.Count;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Commentaire Find(int idCommentaire)
|
||||
{
|
||||
var commentaire = this.dataStore.Commentaires.FirstOrDefault(c => c.IdCommentaire == idCommentaire);
|
||||
if (commentaire == null)
|
||||
{
|
||||
return new Commentaire();
|
||||
}
|
||||
|
||||
return commentaire;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Commentaire> FindAll()
|
||||
{
|
||||
return this.dataStore.Commentaires
|
||||
.OrderByDescending(c => c.DateCreation)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Commentaire> FindCommentaires(int offset, int limit)
|
||||
{
|
||||
if (offset < 0 || limit <= 0)
|
||||
{
|
||||
return Enumerable.Empty<Commentaire>();
|
||||
}
|
||||
|
||||
return this.dataStore.Commentaires
|
||||
.OrderByDescending(c => c.DateCreation)
|
||||
.Skip(offset)
|
||||
.Take(limit)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Commentaire> FindByIdTitre(int idTitre)
|
||||
{
|
||||
return this.dataStore.Commentaires
|
||||
.Where(c => c.Titre != null && c.Titre.IdTitre == idTitre)
|
||||
.OrderByDescending(c => c.DateCreation)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
namespace Webzine.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui permet d'initialiser un jeu de données
|
||||
/// pour tester l'application
|
||||
/// </summary>
|
||||
public class LocalEntityRepository : ITitreRepository
|
||||
{
|
||||
private readonly ILogger<LocalEntityRepository> _logger;
|
||||
private readonly List<Titre> _titres;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="LocalEntityRepository"/> avec un service de journalisation injecte.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecte pour suivre les operations du repository.</param>
|
||||
public LocalEntityRepository(ILogger<LocalEntityRepository> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_logger.LogDebug(1, "NLog injected into LocalEntityRepository");
|
||||
|
||||
var factory = new DataFactory();
|
||||
var artistes = factory.GenerateArtists(10);
|
||||
var styles = factory.GenerateStyles(10);
|
||||
|
||||
_titres = factory.GenerateTitres(30, artistes, styles);
|
||||
factory.GenerateCommentaires(50, _titres);
|
||||
}
|
||||
|
||||
public IEnumerable<Titre> Search(string mot)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mot))
|
||||
{
|
||||
return Enumerable.Empty<Titre>();
|
||||
}
|
||||
|
||||
return _titres
|
||||
.Where(t => !string.IsNullOrWhiteSpace(t.Libelle)
|
||||
&& t.Libelle.Contains(mot, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public Titre? Find(int idTitre)
|
||||
{
|
||||
return _titres.FirstOrDefault(t => t.IdTitre == idTitre);
|
||||
}
|
||||
|
||||
public IEnumerable<Titre> FindAll()
|
||||
{
|
||||
return _titres;
|
||||
}
|
||||
|
||||
public IEnumerable<Titre> SearchByStyle(string libelle)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(libelle))
|
||||
{
|
||||
return Enumerable.Empty<Titre>();
|
||||
}
|
||||
|
||||
return _titres
|
||||
.Where(t => t.Styles.Any(s => !string.IsNullOrWhiteSpace(s.Libelle)
|
||||
&& s.Libelle.Contains(libelle, StringComparison.OrdinalIgnoreCase)))
|
||||
.OrderBy(t => t.Libelle)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
58
Webzine.Repository/LocalStyleRepository.cs
Normal file
58
Webzine.Repository/LocalStyleRepository.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
namespace Webzine.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui implémente le repository pour les styles en utilisant une liste locale comme source de données.
|
||||
/// </summary>
|
||||
public class LocalStyleRepository : IStyleRepository
|
||||
{
|
||||
private readonly ILogger<LocalStyleRepository> logger;
|
||||
|
||||
// private readonly List<Style> styles;
|
||||
private readonly InMemoryDataStore dataStore;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalStyleRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
|
||||
/// <param name="styles">La liste de styles à utiliser comme source de données pour le repository.</param>
|
||||
public LocalStyleRepository(ILogger<LocalStyleRepository> logger, InMemoryDataStore dataStore)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.dataStore = dataStore;
|
||||
this.logger.LogDebug(1, "NLog injecté dans LocalStyleRepository");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Style style)
|
||||
{
|
||||
throw new NotSupportedException("Mode local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Style style)
|
||||
{
|
||||
throw new NotSupportedException("Mode local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Style Find(int id)
|
||||
{
|
||||
return this.dataStore.Styles.Find(s => s.IdStyle == id);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Style> FindAll()
|
||||
{
|
||||
return this.dataStore.Styles.ToList();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(Style style)
|
||||
{
|
||||
throw new NotSupportedException("Mode local");
|
||||
}
|
||||
}
|
||||
99
Webzine.Repository/LocalTitreRepository.cs
Normal file
99
Webzine.Repository/LocalTitreRepository.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
namespace Webzine.Repository;
|
||||
|
||||
/// <summary>
|
||||
/// Classe qui implémente le repository pour les titres en utilisant une liste locale comme source de données.
|
||||
/// </summary>
|
||||
public class LocalTitreRepository : ITitreRepository
|
||||
{
|
||||
private readonly ILogger<LocalTitreRepository> logger;
|
||||
private readonly InMemoryDataStore dataStore;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalTitreRepository"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Le service de journalisation injecté pour suivre les opérations du repository.</param>
|
||||
/// <param name="dataStore">La liste de titres à utiliser comme source de données pour le repository.</param>
|
||||
public LocalTitreRepository(ILogger<LocalTitreRepository> logger, InMemoryDataStore dataStore)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.dataStore = dataStore;
|
||||
this.logger.LogDebug(1, "NLog injecté dans LocalTitreRepository");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Add(Titre titre)
|
||||
{
|
||||
throw new NotSupportedException("Mode local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int Count()
|
||||
{
|
||||
var count = this.dataStore.Titres.Count();
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Delete(Titre titre)
|
||||
{
|
||||
throw new NotSupportedException("Mode Local");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> FindTitres(int offset, int limit)
|
||||
{
|
||||
return this.dataStore.Titres
|
||||
.OrderByDescending(t => t.DateCreation)
|
||||
.Skip(offset)
|
||||
.Take(limit);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void IncrementNbLectures(Titre titre)
|
||||
{
|
||||
titre.NbLectures++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void IncrementNbLikes(Titre titre)
|
||||
{
|
||||
titre.NbLikes++;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> Search(string mot)
|
||||
{
|
||||
return this.dataStore.Titres
|
||||
.Where(t => t.Libelle != null && t.Libelle.Contains(mot));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Titre Find(int idTitre)
|
||||
{
|
||||
return this.dataStore.Titres
|
||||
.First(t => t.IdTitre == idTitre);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> FindAll()
|
||||
{
|
||||
return this.dataStore.Titres;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<Titre> SearchByStyle(string libelle)
|
||||
{
|
||||
return this.dataStore.Titres
|
||||
.Where(t => t.Styles.Any(s => s.Libelle == libelle));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Update(Titre titre)
|
||||
{
|
||||
throw new NotSupportedException("Mode local");
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
|
||||
<PackageReference Include="NLog" Version="6.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -23,6 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.EntitiesContext\Webzine.EntitiesContext.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Repository.Contracts\Webzine.Repository.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Artiste;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Contrôleur pour la gestion des artistes dans l'administration du webzine.
|
||||
/// </summary>
|
||||
[Area("Administration")]
|
||||
public class ArtisteController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<ArtisteController> _logger;
|
||||
private readonly List<Artiste> _artistes;
|
||||
private readonly ILogger<ArtisteController> logger;
|
||||
private readonly IArtisteRepository artisteRepository;
|
||||
|
||||
|
||||
public ArtisteController(ILogger<ArtisteController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
this._logger.LogDebug(1, "initialisation du ArtisteController d'administration");
|
||||
var factory = new DataFactory();
|
||||
|
||||
_artistes = factory.GenerateArtists(10);
|
||||
}
|
||||
/// <summary>
|
||||
/// Affiche la liste des artistes. Pour l'instant, les artistes sont générés à partir de noms prédéfinis via la méthode SeedArtisteByName de la classe ArtisteFactory.
|
||||
/// Chaque artiste est ensuite ajouté à une liste d'artistes qui est passée à la vue via un objet GroupeArtisteViewModel.
|
||||
/// Initializes a new instance of the <see cref="ArtisteController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="ArtisteController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Logger.</param>
|
||||
/// <param name="artisteRepository">Repository pour les artistes.</param>
|
||||
public ArtisteController(
|
||||
ILogger<ArtisteController> logger,
|
||||
IArtisteRepository artisteRepository)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.artisteRepository = artisteRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la liste des artistes. Pour l'instant, les artistes sont générés à partir de noms prédéfinis via la méthode SeedArtisteByName de la classe ArtisteFactory.
|
||||
/// Chaque artiste est ensuite ajouté à une liste d'artistes qui est passée à la vue.
|
||||
/// </summary>
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
IEnumerable<Artiste> artistes = this.artisteRepository.FindAll();
|
||||
|
||||
var _artistes_ordre = _artistes.OrderBy(t => t.Nom).ToList();
|
||||
var artistes_ordre = artistes.OrderBy(t => t.Nom).ToList();
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
|
||||
|
||||
GroupeArtisteViewModel groupeArtisteModel = new GroupeArtisteViewModel
|
||||
{
|
||||
Artistes = _artistes_ordre
|
||||
};
|
||||
|
||||
return View(groupeArtisteModel);
|
||||
return this.View(artistes_ordre);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,10 +53,10 @@ public class ArtisteController : Controller
|
||||
{
|
||||
Id = 0,
|
||||
Nom = string.Empty,
|
||||
Biographie = string.Empty
|
||||
Biographie = string.Empty,
|
||||
};
|
||||
|
||||
return View(model);
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -66,32 +66,51 @@ public class ArtisteController : Controller
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Edit(int id)
|
||||
{
|
||||
var artiste = _artistes.First(t => t.IdArtiste == id);
|
||||
var artiste = this.artisteRepository.Find(id);
|
||||
|
||||
var model = new AdminArtisteForm
|
||||
{
|
||||
Id = artiste.IdArtiste,
|
||||
Nom = artiste.Nom,
|
||||
Biographie = artiste.Biographie
|
||||
Biographie = artiste.Biographie,
|
||||
};
|
||||
|
||||
return View(model);
|
||||
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renvoie à la page supprimer un artiste.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant de l'artiste à supprimer. </param>
|
||||
/// <returns>Redirection.></returns>
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
var artiste = _artistes.First(t => t.IdArtiste == id);
|
||||
var artiste = this.artisteRepository.Find(id);
|
||||
var model = new AdminArtisteForm
|
||||
{
|
||||
Id = id,
|
||||
Nom = artiste.Nom,
|
||||
Biographie = artiste.Biographie
|
||||
Biographie = artiste.Biographie,
|
||||
};
|
||||
return View(model);
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Méthode POST pour supprimer un artiste.
|
||||
/// </summary>
|
||||
/// <param name="model">L'artiste à supprimer.</param>
|
||||
/// <returns>Redirige vers la page d'index d'admin artiste.</returns>
|
||||
[HttpPost]
|
||||
public IActionResult Delete(AdminArtisteForm model)
|
||||
{
|
||||
var artiste = this.artisteRepository.Find(model.Id);
|
||||
|
||||
if (artiste != null)
|
||||
{
|
||||
this.artisteRepository.Delete(artiste);
|
||||
}
|
||||
|
||||
// 3. Redirect back to the list (or wherever you want them to go after)
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
@@ -1,129 +1,86 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers
|
||||
{
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire;
|
||||
|
||||
[Area("Administration")]
|
||||
public class CommentaireController : Controller
|
||||
{
|
||||
private readonly ILogger<CommentaireController> _logger;
|
||||
private readonly List<Commentaire> _commentaires;
|
||||
private readonly ILogger<CommentaireController> logger;
|
||||
private readonly ICommentaireRepository commentaireRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="CommentaireController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// Initializes a new instance of the <see cref="CommentaireController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="CommentaireController"/>.
|
||||
/// Utilise l'injection de dépendances pour récupérer le repository.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public CommentaireController(ILogger<CommentaireController> logger)
|
||||
/// <param name="commentaireRepository">Le repository des commentaires injecté.</param>
|
||||
public CommentaireController(ILogger<CommentaireController> logger, ICommentaireRepository commentaireRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
this.logger = logger;
|
||||
this.commentaireRepository = commentaireRepository;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur CommentaireController.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
var _artistes = factory.GenerateArtists(10);
|
||||
var _styles = factory.GenerateStyles(10);
|
||||
var _titres = factory.GenerateTitres(30, _artistes, _styles);
|
||||
|
||||
_commentaires = factory.GenerateCommentaires(50, _titres);
|
||||
|
||||
_logger.LogInformation("Données fictives générées avec succès.");
|
||||
this.logger.LogInformation("Initialisation du contrôleur CommentaireController.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la liste des commentaires dans la vue Index.
|
||||
/// </summary>
|
||||
/// <returns>>La vue Index avec le ViewModel contenant la liste des commentaires.</returns>
|
||||
public ActionResult Index()
|
||||
/// <returns>La vue Index avec le ViewModel contenant la liste des commentaires.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
// Création de données "bouchon" (mock) pour tester l'affichage
|
||||
var listeCommentaires = new List<Commentaire>
|
||||
{
|
||||
new Commentaire
|
||||
{
|
||||
IdCommentaire = 1, // Correction: Id -> IdCommentaire
|
||||
Auteur = "Michel", // Correction: Nom -> Auteur
|
||||
Contenu = "Nulla sed velit nec tellus gravida molestie",
|
||||
DateCreation = new DateTime(2023, 1, 22, 15, 59, 28),
|
||||
// Important : On initialise l'objet Titre pour accéder à Titre.Libelle
|
||||
Titre = new Titre { Libelle = "St Germain - So Flute" },
|
||||
},
|
||||
new Commentaire
|
||||
{
|
||||
IdCommentaire = 2,
|
||||
Auteur = "Jeff",
|
||||
Contenu = "Lorem ipsum dolor sit.",
|
||||
DateCreation = new DateTime(2023, 1, 22, 14, 27, 8),
|
||||
Titre = new Titre { Libelle = "Queen - Bohemian Rapsody" },
|
||||
},
|
||||
new Commentaire
|
||||
{
|
||||
IdCommentaire = 3,
|
||||
Auteur = "Eva",
|
||||
Contenu = "Aenean vulputate eleifend tellus.",
|
||||
DateCreation = new DateTime(2023, 1, 22, 13, 2, 17),
|
||||
Titre = new Titre { Libelle = "Rammstein - Du hast" },
|
||||
},
|
||||
};
|
||||
// Récupération des commentaires depuis le repository
|
||||
var commentaires = this.commentaireRepository.FindAll();
|
||||
|
||||
// Initialisation du ViewModel
|
||||
var viewModel = new CommentaireViewModel
|
||||
{
|
||||
Commentaires = listeCommentaires
|
||||
Commentaires = commentaires,
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
return this.View(viewModel);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la vue de confirmation de suppression d'un commentaire, en récupérant les détails du commentaire à supprimer à partir de l'identifiant fourni.
|
||||
/// Affiche la vue de confirmation de suppression d'un commentaire, en récupérant les détails à partir de l'identifiant fourni.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du commentaire à supprimer.</param>
|
||||
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du commentaire à supprimer, ou une réponse NotFound si le commentaire n'existe pas.</returns>
|
||||
public ActionResult Delete(int id)
|
||||
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails, ou une redirection vers l'index si introuvable.</returns>
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
var commentaire = _commentaires
|
||||
.FirstOrDefault(c => c.IdCommentaire == id);
|
||||
var commentaire = this.commentaireRepository.Find(id);
|
||||
|
||||
if (commentaire == null)
|
||||
return NotFound();
|
||||
|
||||
var vm = new CommentaireDeleteViewModel
|
||||
var model = new CommentaireDeleteViewModel
|
||||
{
|
||||
IdCommentaire = commentaire.IdCommentaire,
|
||||
Auteur = commentaire.Auteur,
|
||||
Contenu = commentaire.Contenu,
|
||||
DateCreation = commentaire.DateCreation,
|
||||
TitreLibelle = commentaire.Titre?.Libelle
|
||||
TitreLibelle = commentaire.Titre?.Libelle,
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traite la confirmation de suppression d'un commentaire. En cas de succès, redirige vers la liste des commentaires. En cas d'erreur, affiche à nouveau la vue de confirmation avec le message d'erreur.
|
||||
/// Effectue la suppression réelle du commentaire.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du commentaire à supprimer.</param>
|
||||
/// <param name="model">Le ViewModel contenant les détails du commentaire à supprimer, utilisé pour afficher les informations en cas d'erreur.</param>
|
||||
/// <returns>Redirection vers la liste des commentaires en cas de succès, ou la vue de confirmation avec le message d'erreur en cas d'échec.</returns>
|
||||
/// <param name="model">Le CommentaireDeleteViewModel.</param>
|
||||
/// <returns>Redirection vers la vue Index après suppression.</returns>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Delete(int id, CommentaireDeleteViewModel model)
|
||||
public IActionResult Delete(CommentaireDeleteViewModel model)
|
||||
{
|
||||
try
|
||||
var commentaire = this.commentaireRepository.Find(model.IdCommentaire);
|
||||
|
||||
if (commentaire != null)
|
||||
{
|
||||
return RedirectToAction();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Log de l'erreur
|
||||
Console.WriteLine(e);
|
||||
return View(model);
|
||||
this.commentaireRepository.Delete(commentaire);
|
||||
}
|
||||
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
@@ -8,31 +8,25 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
[Area("Administration")]
|
||||
public class DashboardController : Controller
|
||||
{
|
||||
private readonly ILogger<DashboardController> _logger;
|
||||
private readonly List<Titre> _titres;
|
||||
private readonly List<Style> _styles;
|
||||
private readonly List<Artiste> _artistes;
|
||||
private readonly ILogger<DashboardController> logger;
|
||||
private readonly IStyleRepository styleRepository;
|
||||
private readonly IArtisteRepository artisteRepository;
|
||||
private readonly ITitreRepository titreRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="DashboardController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// Initializes a new instance of the <see cref="DashboardController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="DashboardController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public DashboardController(ILogger<DashboardController> logger)
|
||||
/// <param name="styleRepository">Repository des styles injecté.</param>
|
||||
public DashboardController(ILogger<DashboardController> logger, IStyleRepository styleRepository, IArtisteRepository artisteRepository, ITitreRepository titreRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
this.logger = logger;
|
||||
this.styleRepository = styleRepository;
|
||||
this.artisteRepository = artisteRepository;
|
||||
this.titreRepository = titreRepository;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur TitreController.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
_artistes = factory.GenerateArtists(10);
|
||||
_styles = factory.GenerateStyles(10);
|
||||
_titres = factory.GenerateTitres(30, _artistes, _styles);
|
||||
|
||||
factory.GenerateCommentaires(50, _titres);
|
||||
|
||||
_logger.LogInformation("Données fictives générées avec succès.");
|
||||
this.logger.LogInformation("Initialisation du contrôleur TitreController.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -41,42 +35,42 @@ public class DashboardController : Controller
|
||||
/// <returns>La vue Index du tableau de bord.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
var mostChronicledArtist = _titres
|
||||
var artisteLePlusChronique = this.titreRepository.FindAll()
|
||||
.GroupBy(t => t.Artiste)
|
||||
.OrderByDescending(g => g.Count())
|
||||
.FirstOrDefault();
|
||||
.First();
|
||||
|
||||
var topArtistAlbums = _titres
|
||||
var albumLePlusChronique = this.titreRepository.FindAll()
|
||||
.GroupBy(t => t.Artiste)
|
||||
.OrderByDescending(g => g.Select(t => t.Album).Distinct().Count())
|
||||
.FirstOrDefault();
|
||||
.First();
|
||||
|
||||
var mostPlayedTrack = _titres
|
||||
var musiqueLaPlusJouee = this.titreRepository.FindAll()
|
||||
.OrderByDescending(t => t.NbLectures)
|
||||
.FirstOrDefault();
|
||||
.First();
|
||||
|
||||
var model = new DashboardViewModel
|
||||
{
|
||||
ArtistCount = _artistes.Count,
|
||||
NombreArtistes = this.artisteRepository.FindAll().Count(),
|
||||
|
||||
MostChronicledArtistName = mostChronicledArtist?.Key.Nom,
|
||||
ArtisteLePlusChronique = artisteLePlusChronique.Key.Nom,
|
||||
|
||||
TopArtistAlbumsName = topArtistAlbums?.Key.Nom,
|
||||
AlbumLePlusChronique = albumLePlusChronique.Key.Nom,
|
||||
|
||||
BiographyCount = _artistes.Count(a => !string.IsNullOrEmpty(a.Biographie)),
|
||||
NombreBiographies = this.artisteRepository.FindAll().Count(a => !string.IsNullOrEmpty(a.Biographie)),
|
||||
|
||||
MostPlayedTrackId = mostPlayedTrack?.IdTitre ?? 0,
|
||||
MostPlayedTrack = mostPlayedTrack?.Libelle,
|
||||
IdMusiqueLaPlusJouee = musiqueLaPlusJouee.IdTitre,
|
||||
MusiqueLaPlusJouee = musiqueLaPlusJouee.Libelle,
|
||||
|
||||
TrackCount = _titres.Count,
|
||||
NombreTitres = this.titreRepository.Count(),
|
||||
|
||||
GenreCount = _styles.Count,
|
||||
NombreGenres = this.styleRepository.FindAll().Count(),
|
||||
|
||||
TotalPlays = _titres.Sum(t => t.NbLectures),
|
||||
NombreLectures = this.titreRepository.FindAll().Sum(t => t.NbLectures),
|
||||
|
||||
TotalLikes = _titres.Sum(t => t.NbLikes)
|
||||
NombreLikes = this.titreRepository.FindAll().Sum(t => t.NbLikes),
|
||||
};
|
||||
|
||||
return View(model);
|
||||
return this.View(model);
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Style;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers
|
||||
{
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Style;
|
||||
|
||||
/// <summary>
|
||||
/// Contrôleur pour la gestion des styles dans l'administration du webzine.
|
||||
/// </summary>
|
||||
[Area("Administration")]
|
||||
public class StyleController : Controller
|
||||
{
|
||||
private readonly ILogger<StyleController> _logger;
|
||||
private readonly List<Style> _styles;
|
||||
private readonly ILogger<StyleController> logger;
|
||||
private readonly IStyleRepository styleRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="StyleController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// Initializes a new instance of the <see cref="StyleController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="StyleController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public StyleController(ILogger<StyleController> logger)
|
||||
/// <param name="styles">Repository des styles injecté.</param>
|
||||
public StyleController(
|
||||
ILogger<StyleController> logger,
|
||||
IStyleRepository styleRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
this.logger = logger;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur StyleController.");
|
||||
this.logger.LogInformation("Initialisation du contrôleur StyleController.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
_styles = factory.GenerateStyles(10);
|
||||
|
||||
_logger.LogInformation("Données fictives générées avec succès.");
|
||||
this.styleRepository = styleRepository;
|
||||
}
|
||||
|
||||
// GET: Administration/Styles
|
||||
public ActionResult Index()
|
||||
/// <summary>
|
||||
/// Affiche la liste des styles dans la vue Index.
|
||||
/// </summary>
|
||||
/// <returns>La vue Index avec le ViewModel contenant la liste des styles.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
// Création de données "bouchon" (mock) pour tester l'affichage
|
||||
var listeStyles = new List<Style>
|
||||
{
|
||||
new Style
|
||||
{
|
||||
IdStyle = 1,
|
||||
Libelle = "Rock",
|
||||
},
|
||||
new Style
|
||||
{
|
||||
IdStyle = 2,
|
||||
Libelle = "Pop",
|
||||
},
|
||||
new Style
|
||||
{
|
||||
IdStyle = 3,
|
||||
Libelle = "Jazz",
|
||||
},
|
||||
};
|
||||
var listeStyles = this.styleRepository.FindAll().Take(10);
|
||||
|
||||
// Initialisation du ViewModel
|
||||
var viewModel = new StyleViewModel
|
||||
{
|
||||
Styles = listeStyles,
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
return this.View(listeStyles);
|
||||
}
|
||||
|
||||
// GET: Administration/Styles/Create
|
||||
public ActionResult Create()
|
||||
/// <summary>
|
||||
/// Affiche la vue de création d'un nouveau style.
|
||||
/// </summary>
|
||||
/// <returns>La vue Create pour ajouter un nouveau style.</returns>
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
return this.View();
|
||||
}
|
||||
|
||||
// POST: Administration/Styles/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Create(StyleCreateViewModel model)
|
||||
/// <summary>
|
||||
/// Affiche la vue de confirmation de suppression d'un style, en récupérant les détails du style à supprimer à partir de l'identifiant fourni.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du style à supprimer.</param>
|
||||
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du style à supprimer, ou une redirection vers l'index si le style n'existe pas.</returns>
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Nouveau style créé : {Libelle}", model.Libelle);
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Erreur lors de la création du style");
|
||||
ModelState.AddModelError("", "Une erreur est survenue lors de la création.");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// GET: Administration/Styles/Delete/5
|
||||
public ActionResult Delete(int id)
|
||||
{
|
||||
var style = this._styles
|
||||
.FirstOrDefault(c => c.IdStyle == id);
|
||||
|
||||
if (style == null)
|
||||
{
|
||||
return this.NotFound();
|
||||
}
|
||||
var style = this.styleRepository.Find(id);
|
||||
|
||||
var vm = new StyleDeleteViewModel
|
||||
{
|
||||
@@ -112,69 +66,44 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
|
||||
Libelle = style.Libelle,
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
return this.View(vm);
|
||||
}
|
||||
|
||||
// POST: Administration/Styles/Delete/5
|
||||
/// <summary>
|
||||
/// Méthode POST pour supprimer un style.
|
||||
/// </summary>
|
||||
/// <param name="model">Le style à supprimer.</param>
|
||||
/// <returns>Redirige vers la page d'index d'admin style.</returns>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Delete(int id, StyleDeleteViewModel model)
|
||||
public IActionResult Delete(StyleEditViewModel model)
|
||||
{
|
||||
try
|
||||
var style = this.styleRepository.Find(model.IdStyle);
|
||||
|
||||
if (style != null)
|
||||
{
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Log de l'erreur
|
||||
_logger.LogError(e, "Erreur lors de la suppression du style avec l'ID {StyleId}", id);
|
||||
return View(model);
|
||||
this.styleRepository.Delete(style);
|
||||
}
|
||||
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
// GET: Administration/Styles/Edit/5
|
||||
/// <summary>
|
||||
/// Affiche la vue d'édition d'un style existant, en récupérant les détails du style à éditer à partir de l'identifiant fourni.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du style à éditer.</param>
|
||||
/// <returns>La vue d'édition avec le ViewModel contenant les détails du style à éditer, ou une redirection vers l'index si le style n'existe pas.</returns>
|
||||
[HttpGet]
|
||||
public ActionResult Edit(int id)
|
||||
public IActionResult Edit(int id)
|
||||
{
|
||||
// Recherche du style (simulation avec la liste _styles)
|
||||
var style = _styles.FirstOrDefault(s => s.IdStyle == id);
|
||||
var style = this.styleRepository.Find(id);
|
||||
|
||||
if (style == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
// Mapping vers le ViewModel
|
||||
var model = new StyleEditViewModel
|
||||
{
|
||||
IdStyle = style.IdStyle,
|
||||
Libelle = style.Libelle
|
||||
Libelle = style.Libelle,
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: Administration/Styles/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Edit(int id, StyleEditViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Style {Id} mis à jour : {Libelle}", id, model.Libelle);
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Erreur lors de la modification du style {Id}", id);
|
||||
ModelState.AddModelError("", "Une erreur est survenue lors de la modification.");
|
||||
return View(model);
|
||||
}
|
||||
return this.View(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +1,93 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Contrôleur pour la gestion des titres en administration. Ce contrôleur gère les opérations de création, modification, suppression et affichage des titres dans l'interface d'administration du webzine. Chaque action du contrôleur prépare un ViewModel spécifique pour la vue correspondante, permettant ainsi une séparation claire entre la logique métier et la présentation des données.
|
||||
/// </summary>
|
||||
[Area("Administration")]
|
||||
public class TitreController : Controller
|
||||
{
|
||||
private readonly ILogger<TitreController> _logger;
|
||||
private readonly List<Titre> _titres;
|
||||
private readonly List<Style> _styles;
|
||||
private readonly List<Artiste> _artistes;
|
||||
|
||||
private readonly ILogger<TitreController> logger;
|
||||
private readonly ITitreRepository titreRepository;
|
||||
private readonly IArtisteRepository artisteRepository;
|
||||
private readonly IStyleRepository styleRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="TitreController"/>.
|
||||
/// Les données sont générées dynamiquement via <see cref="DataFactory"/>.
|
||||
/// Initializes a new instance of the <see cref="TitreController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="TitreController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public TitreController(ILogger<TitreController> logger)
|
||||
/// <param name="titreRepository">Repository des titres injecté pour accéder aux données des titres.</param>
|
||||
/// <param name="artisteRepository">Repository des artistes injecté pour accéder aux données des artistes, nécessaires pour les associations avec les titres.</param>
|
||||
/// <param name="styleRepository">Repository des styles injecté pour accéder aux données des styles, nécessaires pour les associations avec les titres.</param>
|
||||
public TitreController(ILogger<TitreController> logger, ITitreRepository titreRepository, IArtisteRepository artisteRepository, IStyleRepository styleRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
_logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration.");
|
||||
|
||||
var factory = new DataFactory();
|
||||
|
||||
_artistes = factory.GenerateArtists(10);
|
||||
_styles = factory.GenerateStyles(10);
|
||||
_titres = factory.GenerateTitres(30, _artistes, _styles);
|
||||
|
||||
factory.GenerateCommentaires(50, _titres);
|
||||
|
||||
_logger.LogInformation("Données fictives générées avec succès.");
|
||||
this.logger = logger;
|
||||
this.titreRepository = titreRepository;
|
||||
this.artisteRepository = artisteRepository;
|
||||
this.styleRepository = styleRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la liste des titres dans la vue Index.
|
||||
/// </summary>
|
||||
/// <returns>La vue Index avec le ViewModel contenant la liste des titres.</returns>
|
||||
public ActionResult Index()
|
||||
public IActionResult Index()
|
||||
{
|
||||
var model = _titres.Select(t => new AdminTitreList
|
||||
IEnumerable<Titre> titres = this.titreRepository.FindAll().Take(10);
|
||||
|
||||
var model = titres.Select(t => new AdminTitreList
|
||||
{
|
||||
Id = t.IdTitre,
|
||||
Artiste = t.Artiste?.Nom,
|
||||
Nom = t.Artiste.Nom,
|
||||
Titre = t.Libelle,
|
||||
Duree = TimeSpan.FromSeconds(t.Duree).ToString(@"mm\:ss"),
|
||||
DateSortie = t.DateSortie,
|
||||
NbLectures = t.NbLectures,
|
||||
NbLikes = t.NbLikes,
|
||||
NbCommentaires = t.Commentaires?.Count ?? 0
|
||||
NbCommentaires = t.Commentaires?.Count ?? 0,
|
||||
}).ToList();
|
||||
|
||||
return View(model);
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche le formulaire de création d'un nouveau titre dans la vue Create.
|
||||
/// </summary>
|
||||
/// <returns>La vue Create avec le ViewModel contenant les listes déroulantes pour les artistes et les styles.</returns>
|
||||
public ActionResult Create()
|
||||
public IActionResult Create()
|
||||
{
|
||||
var model = new AdminTitreForm
|
||||
{
|
||||
Artistes = _artistes.Select(a => new SelectListItem
|
||||
Artistes = this.artisteRepository.FindAll().Select(a => new SelectListItem
|
||||
{
|
||||
Value = a.IdArtiste.ToString(),
|
||||
Text = a.Nom
|
||||
Text = a.Nom,
|
||||
}).ToList(),
|
||||
|
||||
AllStyles = _styles.Select(s => new SelectListItem
|
||||
AllStyles = this.styleRepository.FindAll().Select(s => new SelectListItem
|
||||
{
|
||||
Value = s.IdStyle.ToString(),
|
||||
Text = s.Libelle
|
||||
}).ToList()
|
||||
Text = s.Libelle,
|
||||
}).ToList(),
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Traite la soumission du formulaire de création d'un nouveau titre. Actuellement, cette méthode est un stub qui redirige vers l'index sans effectuer de logique de création réelle.
|
||||
/// </summary>
|
||||
/// <param name="collection">Le formulaire soumis contenant les données du nouveau titre. Actuellement, ce paramètre n'est pas utilisé.</param>
|
||||
/// <returns>Redirige vers l'action Index après la soumission du formulaire. En cas d'erreur, retourne la vue Create pour permettre à l'utilisateur de corriger les données.</returns>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Create(IFormCollection collection)
|
||||
{
|
||||
try
|
||||
{
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return View();
|
||||
}
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche le formulaire de modification d'un titre existant dans la vue Edit, en préremplissant les champs avec les données du titre sélectionné. Les listes déroulantes pour les artistes et les styles sont également remplies pour permettre à l'utilisateur de modifier ces associations.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du titre à modifier, utilisé pour récupérer les données du titre à partir de la liste des titres générés.</param>
|
||||
/// <returns>La vue Edit avec le ViewModel contenant les données du titre à modifier, ainsi que les listes déroulantes pour les artistes et les styles. En cas d'erreur, retourne une réponse NotFound si le titre n'existe pas.</returns>
|
||||
public ActionResult Edit(int id)
|
||||
/// <returns>La vue Edit avec le ViewModel contenant les données du titre à modifier, ainsi que les listes déroulantes pour les artistes et les styles. </returns>
|
||||
public IActionResult Edit(int id)
|
||||
{
|
||||
var titre = _titres.First(t => t.IdTitre == id);
|
||||
var titre = this.titreRepository.Find(id);
|
||||
|
||||
var model = new AdminTitreForm
|
||||
{
|
||||
@@ -126,74 +104,55 @@ public class TitreController : Controller
|
||||
NbLikes = titre.NbLikes,
|
||||
Styles = titre.Styles.Select(s => s.IdStyle).ToList(),
|
||||
|
||||
Artistes = _artistes.Select(a => new SelectListItem
|
||||
Artistes = this.artisteRepository.FindAll().Select(a => new SelectListItem
|
||||
{
|
||||
Value = a.IdArtiste.ToString(),
|
||||
Text = a.Nom
|
||||
Text = a.Nom,
|
||||
}).ToList(),
|
||||
|
||||
AllStyles = _styles.Select(s => new SelectListItem
|
||||
AllStyles = this.styleRepository.FindAll().Select(s => new SelectListItem
|
||||
{
|
||||
Value = s.IdStyle.ToString(),
|
||||
Text = s.Libelle
|
||||
}).ToList()
|
||||
Text = s.Libelle,
|
||||
}).ToList(),
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traite la soumission du formulaire de modification d'un titre existant. Actuellement, cette méthode est un stub qui redirige vers l'index sans effectuer de logique de modification réelle.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du titre à modifier, utilisé pour identifier le titre à mettre à jour. Actuellement, ce paramètre n'est pas utilisé dans la logique de traitement.</param>
|
||||
/// <param name="collection">Le formulaire soumis contenant les données modifiées du titre. Actuellement, ce paramètre n'est pas utilisé dans la logique de traitement.</param>
|
||||
/// <returns>Redirige vers l'action Index après la soumission du formulaire. En cas d'erreur, retourne la vue Edit pour permettre à l'utilisateur de corriger les données.</returns>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Edit(int id, IFormCollection collection)
|
||||
{
|
||||
try
|
||||
{
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch
|
||||
{
|
||||
return View();
|
||||
}
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la vue de confirmation de suppression d'un titre, en récupérant les détails du titre à supprimer à partir de l'identifiant fourni. Le ViewModel contient les informations essentielles du titre, telles que le libellé et le nom de l'artiste, pour permettre à l'utilisateur de confirmer la suppression.
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du titre à supprimer, utilisé pour récupérer les données du titre à partir de la liste des titres générés.</param>
|
||||
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du titre à supprimer, ou une réponse NotFound si le titre n'existe pas.</returns>
|
||||
public ActionResult Delete(int id)
|
||||
/// <returns>La vue de confirmation de suppression avec le ViewModel contenant les détails du titre à supprimer.</returns>
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
var titre = _titres.First(t => t.IdTitre == id);
|
||||
var titre = this.titreRepository.Find(id);
|
||||
|
||||
var model = new AdminTitreDelete
|
||||
{
|
||||
Id = titre.IdTitre,
|
||||
Titre = titre.Libelle,
|
||||
Artiste = titre.Artiste?.Nom
|
||||
Artiste = titre.Artiste.Nom,
|
||||
};
|
||||
|
||||
return View(model);
|
||||
return this.View(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Traite la confirmation de suppression d'un titre. En cas de succès, redirige vers la liste des titres après avoir supprimé le titre de la liste. En cas d'erreur, affiche à nouveau la vue de confirmation avec le message d'erreur.
|
||||
/// Méthode POST pour supprimer un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Le ViewModel contenant les détails du titre à supprimer, utilisé pour identifier le titre à supprimer et pour afficher les informations en cas d'erreur.</param>
|
||||
/// <returns>Redirection vers la liste des titres en cas de succès, ou la vue de confirmation avec le message d'erreur en cas d'échec.</returns>
|
||||
/// <param name="model">Le titre à supprimer.</param>
|
||||
/// <returns>Redirige vers la page d'index d'admin titre.</returns>
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Delete(AdminTitreDelete model)
|
||||
public IActionResult Delete(AdminTitreDelete model)
|
||||
{
|
||||
var titre = _titres.First(t => t.IdTitre == model.Id);
|
||||
var titre = this.titreRepository.Find(model.Id);
|
||||
if (titre != null)
|
||||
{
|
||||
this.titreRepository.Delete(titre);
|
||||
}
|
||||
|
||||
_titres.Remove(titre);
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la création et la modification d'un artiste dans l'administration.
|
||||
/// Ne contient pas les titres de l'artiste.
|
||||
/// </summary>
|
||||
public class AdminArtisteForm
|
||||
{
|
||||
@@ -9,13 +10,15 @@
|
||||
/// Définit l'identifiant de l'artiste.
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste.
|
||||
/// </summary>
|
||||
public string Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la biographie de l'artiste.
|
||||
/// </summary>
|
||||
public string Biographie { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Artiste
|
||||
{
|
||||
using Webzine.Entity;
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher un groupe d'artiste.
|
||||
/// </summary>
|
||||
public class GroupeArtisteViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Liste d'artistes.
|
||||
/// </summary>
|
||||
public IEnumerable<Artiste> Artistes { get; set; } = new List<Artiste>();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// <copyright file="CommentaireViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. All rights reserved.
|
||||
// <copyright file="CommentaireViewModel.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire
|
||||
|
||||
@@ -8,49 +8,50 @@ public class DashboardViewModel
|
||||
/// <summary>
|
||||
/// Définit le nombre total d'artistes chroniqués dans le webzine.
|
||||
/// </summary>
|
||||
public int ArtistCount { get; set; }
|
||||
public int NombreArtistes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste le plus chroniqué dans le webzine.
|
||||
/// </summary>
|
||||
public string MostChronicledArtistName { get; set; }
|
||||
public string ArtisteLePlusChronique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album le plus chroniqué dans le webzine.
|
||||
/// </summary>
|
||||
public string TopArtistAlbumsName { get; set; }
|
||||
public string AlbumLePlusChronique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de biographies d'artistes dans le webzine.
|
||||
/// </summary>
|
||||
public int BiographyCount { get; set; }
|
||||
public int NombreBiographies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'identifiant de la biographie d'artiste la plus lue dans le webzine.
|
||||
/// </summary>
|
||||
public int MostPlayedTrackId { get; set; }
|
||||
public int IdMusiqueLaPlusJouee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de la biographie d'artiste la plus lue dans le webzine.
|
||||
/// </summary>
|
||||
public string MostPlayedTrack { get; set; }
|
||||
public string MusiqueLaPlusJouee { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de titres chroniqués dans le webzine.
|
||||
/// </summary>
|
||||
public int TrackCount { get; set; }
|
||||
public int NombreTitres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de genres musicaux chroniqués dans le webzine.
|
||||
/// </summary>
|
||||
public int GenreCount { get; set; }
|
||||
public int NombreGenres { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de chroniques d'albums dans le webzine.
|
||||
/// </summary>
|
||||
public int TotalPlays { get; set; }
|
||||
public int NombreLectures { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nombre total de likes sur les chroniques d'albums dans le webzine.
|
||||
/// </summary>
|
||||
public int TotalLikes { get; set; }
|
||||
public int NombreLikes { get; set; }
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// <copyright file="StyleCreateViewModel.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la création d'un style en administration.
|
||||
/// </summary>
|
||||
|
||||
public class StyleCreateViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,22 @@
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la suppression d'un style en administration.
|
||||
/// </summary>
|
||||
|
||||
public class StyleDeleteViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit l'identifiant du style à supprimer.
|
||||
/// </summary>
|
||||
|
||||
public int IdStyle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,22 @@
|
||||
// <copyright file="StyleDeleteViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// <copyright file="StyleEditViewModel.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la modification d'un style en administration.
|
||||
/// </summary>
|
||||
|
||||
public class StyleEditViewModel
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public int IdStyle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// <copyright file="StyleViewModel.cs" company="Webzine">
|
||||
// Copyright (c) Webzine. Tout droit réservé.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher la liste des commentaires en administration.
|
||||
/// </summary>
|
||||
public class StyleViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit la liste des commentaires.
|
||||
/// </summary>
|
||||
public IEnumerable<Entity.Style> Styles { get; set; } = new List<Entity.Style>();
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public class AdminTitreForm
|
||||
/// <summary>
|
||||
/// Définit la liste des identifiants des styles associés au titre.
|
||||
/// </summary>
|
||||
public List<int> Styles { get; set; } = new();
|
||||
public List<int> Styles { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des artistes disponibles pour la sélection dans le formulaire de création ou de modification d'un titre.
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Webzine.WebApplication.Areas.Administration.ViewModels.Titre
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste associé au titre.
|
||||
/// </summary>
|
||||
public string Artiste { get; set; }
|
||||
public string Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du titre.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Artiste.GroupeArtisteViewModel
|
||||
@model IEnumerable<Webzine.Entity.Artiste>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Artiste";
|
||||
@@ -16,12 +16,12 @@
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th scope="col" class="p-2">Nom</th>
|
||||
<th scope="col" class="text-center p-2" style="width: 100px;">Actions</th>
|
||||
<th scope="col" class="text-center p-2">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
@foreach (var artiste in Model.Artistes)
|
||||
@foreach (var artiste in Model)
|
||||
{
|
||||
<tr class="align-middle">
|
||||
<td class="p-2">
|
||||
@@ -29,13 +29,13 @@
|
||||
</td>
|
||||
<td class="text-center p-2">
|
||||
|
||||
<a asp-action="Edit" asp-route-id="@artiste.IdArtiste"
|
||||
class="text-primary">
|
||||
<a asp-action="Edit" asp-route-id="@artiste.IdArtiste">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
|
||||
<a asp-action="Delete" asp-route-id="@artiste.IdArtiste"
|
||||
class="text-primary">
|
||||
<a asp-action="Delete"
|
||||
asp-controller="Artiste"
|
||||
asp-route-id="@artiste.IdArtiste">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<div class="container">
|
||||
<!-- ARTISTE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="row mb-3">
|
||||
<label class="col-md-3 col-form-label">Nom de l'artiste<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Nom" class="form-control" />
|
||||
@@ -10,14 +10,15 @@
|
||||
</div>
|
||||
|
||||
<!-- BIOGRAPHIE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Biographie<span class="text-danger">*</span></label>
|
||||
<div class="row mb-3">
|
||||
<label class="col-md-3 col-form-label">Biographie</label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Biographie" class="form-control"/>
|
||||
<textarea asp-for="Biographie" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- BOUTONS -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-9 offset-md-3">
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
<div class="mb-4">
|
||||
<h4>@Model.Contenu</h4>
|
||||
|
||||
<div class="text-muted">
|
||||
<blockquote>
|
||||
— <strong>@Model.Auteur</strong>
|
||||
le @Model.DateCreation.ToString("dd/MM/yyyy HH:mm:ss")
|
||||
le @Model.DateCreation.ToString("dd/MM/yyyy HH:mm:ss")
|
||||
sur <em>@Model.TitreLibelle</em>
|
||||
</div>
|
||||
</blockquote>
|
||||
</div>
|
||||
|
||||
<form asp-action="Delete" method="post">
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th scope="col">Titre</th>
|
||||
<th scope="col">Auteur</th>
|
||||
<th scope="col">Nom</th>
|
||||
<th scope="col">Commentaire</th>
|
||||
<th scope="col">Date de création</th>
|
||||
<th scope="col" class="text-center p-2" style="width: 100px" ;>Actions</th>
|
||||
<th scope="col" class="text-center p-2">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -25,7 +25,9 @@
|
||||
{
|
||||
<tr class="align-middle">
|
||||
<td>
|
||||
@commentaire.Titre.Libelle
|
||||
<a asp-action="Details" asp-controller="Titre" asp-route-id="@commentaire.Titre.IdTitre">
|
||||
@commentaire.Titre.Libelle
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@commentaire.Auteur
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<h1 class="mb-4">Tableau de bord</h1>
|
||||
|
||||
<hr />
|
||||
<hr/>
|
||||
|
||||
<div class="container">
|
||||
|
||||
@@ -11,43 +11,39 @@
|
||||
<!-- ARTISTS -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Artiste"
|
||||
class="text-decoration-none">
|
||||
asp-controller="Artiste">
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-users fa-3x text-primary mb-3"></i>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-users fa-5x text-primary mb-3"></i>
|
||||
|
||||
<h3 class="text-primary">
|
||||
@Model.ArtistCount
|
||||
</h3>
|
||||
|
||||
<p class="text-primary">
|
||||
artistes
|
||||
</p>
|
||||
<h2>
|
||||
@Model.NombreArtistes
|
||||
</h2>
|
||||
<p>
|
||||
artistes
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- L'ARTIST LE PLUS CHRONICLED -->
|
||||
<!-- L'ARTIST LE PLUS CHRONIQUE -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area=""
|
||||
asp-controller="Artiste"
|
||||
asp-route-nom="@Model.MostChronicledArtistName"
|
||||
class="text-decoration-none">
|
||||
asp-route-nom="@Model.ArtisteLePlusChronique">
|
||||
<div class="ratio ratio-4x3">
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-user fa-5x text-primary mb-3"></i>
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-user fa-3x text-primary mb-3"></i>
|
||||
<h2>
|
||||
@Model.ArtisteLePlusChronique
|
||||
</h2>
|
||||
|
||||
<h3 class="text-primary">
|
||||
@Model.MostChronicledArtistName
|
||||
</h3>
|
||||
|
||||
<p class="text-primary">
|
||||
artiste le plus chroniqué
|
||||
</p>
|
||||
<p>artiste le plus chroniqué</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -55,42 +51,42 @@
|
||||
<div class="col-md-4">
|
||||
<a asp-area=""
|
||||
asp-controller="Artiste"
|
||||
asp-route-nom="@Model.TopArtistAlbumsName"
|
||||
class="text-decoration-none">
|
||||
asp-route-nom="@Model.AlbumLePlusChronique">
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-trophy fa-3x text-primary mb-3"></i>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-trophy fa-5x text-primary mb-3"></i>
|
||||
|
||||
<h3 class="text-primary">
|
||||
@Model.TopArtistAlbumsName
|
||||
</h3>
|
||||
<h2>
|
||||
@Model.AlbumLePlusChronique
|
||||
</h2>
|
||||
|
||||
<p class="text-primary">
|
||||
<p>
|
||||
artiste avec le plus d'albums distincts
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- BIOGRAPHIES -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Titre"
|
||||
class="text-decoration-none">
|
||||
asp-controller="Titre">
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-book fa-3x text-primary mb-3"></i>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-book fa-5x text-primary mb-3"></i>
|
||||
|
||||
<h3 class="text-primary">
|
||||
@Model.BiographyCount
|
||||
</h3>
|
||||
<h2>
|
||||
@Model.NombreBiographies
|
||||
</h2>
|
||||
|
||||
<p class="text-primary">
|
||||
<p>
|
||||
biographies d'artistes
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -99,94 +95,100 @@
|
||||
<a asp-area=""
|
||||
asp-controller="Titre"
|
||||
asp-action="Details"
|
||||
asp-route-id="@Model.MostPlayedTrackId"
|
||||
class="text-decoration-none">
|
||||
asp-route-id="@Model.IdMusiqueLaPlusJouee">
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-compact-disc fa-3x text-primary mb-3"></i>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-compact-disc fa-5x text-primary mb-3"></i>
|
||||
|
||||
<h4 class="text-primary">
|
||||
@Model.MostPlayedTrack
|
||||
</h4>
|
||||
<h2>
|
||||
@Model.MusiqueLaPlusJouee
|
||||
</h2>
|
||||
|
||||
<p class="text-primary">
|
||||
<p>
|
||||
titre le plus lu
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- TITRE NOMBRE -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Titre"
|
||||
class="text-decoration-none">
|
||||
asp-controller="Titre">
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-music fa-3x text-primary mb-3"></i>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-music fa-5x text-primary mb-3"></i>
|
||||
|
||||
<h3 class="text-primary">
|
||||
@Model.TrackCount
|
||||
</h3>
|
||||
<h2>
|
||||
@Model.NombreTitres
|
||||
</h2>
|
||||
|
||||
<p class="text-primary">
|
||||
<p>
|
||||
titres
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- GENRES -->
|
||||
<div class="col-md-4">
|
||||
<a asp-area="Administration"
|
||||
asp-controller="Styles"
|
||||
class="text-decoration-none">
|
||||
asp-controller="Style">
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<div class="card shadow-sm p-4 bg-light h-100 dashboard-card">
|
||||
<i class="fa fa-tags fa-3x text-primary mb-3"></i>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center text-primary">
|
||||
<i class="fa fa-tags fa-5x text-primary mb-3"></i>
|
||||
|
||||
<h3 class="text-primary">
|
||||
@Model.GenreCount
|
||||
</h3>
|
||||
<h2>
|
||||
@Model.NombreGenres
|
||||
</h2>
|
||||
|
||||
<p class="text-primary">
|
||||
<p>
|
||||
styles de musique
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- NOMBRE DE LECTURES -->
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-4 bg-light h-100">
|
||||
<i class="fa fa-eye fa-3x text-dark mb-3"></i>
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<h3>
|
||||
@Model.TotalPlays
|
||||
</h3>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center">
|
||||
<i class="fa fa-eye fa-5x text-dark mb-3"></i>
|
||||
|
||||
<p>
|
||||
lectures
|
||||
</p>
|
||||
<h2>
|
||||
@Model.NombreLectures
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
lectures
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TOTAL LIKES -->
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm p-4 bg-light h-100">
|
||||
<i class="fa fa-thumbs-up fa-3x text-dark mb-3"></i>
|
||||
<div class="ratio ratio-4x3">
|
||||
|
||||
<h3>
|
||||
@Model.TotalLikes
|
||||
</h3>
|
||||
<div class="py-5 bg-light rounded-3 d-flex flex-column justify-content-center align-items-center">
|
||||
<i class="fa fa-thumbs-up fa-5x text-dark mb-3"></i>
|
||||
|
||||
<h2>
|
||||
@Model.NombreLikes
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
likes
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
@* Input *@
|
||||
<div class="me-3">
|
||||
<input asp-for="Libelle" class="form-control" style="width: 250px;" />
|
||||
<input asp-for="Libelle" class="form-control" />
|
||||
</div>
|
||||
|
||||
@* Bouton *@
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
@* On affiche le Libellé en gros *@
|
||||
<h4>@Model.Libelle</h4>
|
||||
|
||||
@* On affiche l'ID discrètement en dessous *@
|
||||
<div class="text-muted">
|
||||
Identifiant technique : @Model.IdStyle
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form asp-action="Delete" method="post">
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
@* Input *@
|
||||
<div class="me-3">
|
||||
<input asp-for="Libelle" class="form-control" style="width: 250px;" />
|
||||
<input asp-for="Libelle" class="form-control" />
|
||||
</div>
|
||||
|
||||
@* Bouton *@
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleViewModel
|
||||
@model IEnumerable<Webzine.Entity.Style>
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Styles";
|
||||
@@ -20,23 +20,23 @@
|
||||
<thead class="table-active">
|
||||
<tr>
|
||||
<th scope="col" class="p-2">Libellé</th>
|
||||
<th scope="col" class="text-center p-2" style="width: 100px;">Actions</th>
|
||||
<th scope="col" class="text-center p-2">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@if (Model.Styles != null && Model.Styles.Any())
|
||||
@if ( Model.Any())
|
||||
{
|
||||
@foreach (Webzine.Entity.Style style in Model.Styles)
|
||||
@foreach (Webzine.Entity.Style style in Model)
|
||||
{
|
||||
<tr class="align-middle">
|
||||
<td class="p-2">
|
||||
<tr >
|
||||
<td class="p-2 w-75">
|
||||
@style.Libelle
|
||||
</td>
|
||||
<td class="text-center p-2">
|
||||
<td class="text-center w-auto p-2">
|
||||
<a asp-action="Edit" asp-route-id="@style.IdStyle" class="text-primary me-2" title="Éditer">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<a asp-action="Delete" asp-route-id="@style.IdStyle" class="text-primary" title="Supprimer">
|
||||
<a asp-action="Delete" asp-route-id="@style.IdStyle" title="Supprimer">
|
||||
<i class="fas fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Artiste</td>
|
||||
<td>@item.Nom</td>
|
||||
<td>@item.Titre</td>
|
||||
<td>@item.Duree</td>
|
||||
<td>@item.DateSortie.ToString("dd/MM/yyyy")</td>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
<!-- JAQUETTE -->
|
||||
<div class="row mb-3 align-items-center">
|
||||
<label class="col-md-3 col-form-label">Jaquette<span class="text-danger">*</span></label>
|
||||
<label class="col-md-3 col-form-label">Jaquette de l'album<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="UrlJaquette"
|
||||
class="form-control"/>
|
||||
@@ -102,13 +102,13 @@
|
||||
</div>
|
||||
|
||||
<!-- LECTURES / LIKES (AFFICHAGE UNIQUEMENT) -->
|
||||
<div class="row mb-4 align-items-center">
|
||||
<div class="row align-items-center">
|
||||
<label class="col-md-3 col-form-label">Nb de lectures<span class="text-danger">*</span></label>
|
||||
<div class="col-md-3">
|
||||
@Model.NbLectures
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4 align-items-center">
|
||||
<div class="row align-items-center">
|
||||
<label class="col-md-3 col-form-label">Nb de likes<span class="text-danger">*</span></label>
|
||||
<div class="col-md-3">
|
||||
@Model.NbLikes
|
||||
|
||||
@@ -1,53 +1,62 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Repository.Fake;
|
||||
using Webzine.WebApplication.ViewModels.Accueil;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Accueil;
|
||||
|
||||
/// <summary>
|
||||
/// Permet de retourner la page d'accueil avec tous les éléments.
|
||||
/// </summary>
|
||||
public class AccueilController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<AccueilController> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<AccueilController> logger;
|
||||
private readonly IConfiguration configuration;
|
||||
private readonly ITitreRepository titreRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="AccueilController"/> avec un service de journalisation et de configuration injectés.
|
||||
/// Initializes a new instance of the <see cref="AccueilController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="AccueilController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
/// <param name="configuration">Service d'injection de configuration pour accéder aux paramètres de l'application.</param>
|
||||
public AccueilController(ILogger<AccueilController> logger, IConfiguration configuration)
|
||||
public AccueilController(
|
||||
ILogger<AccueilController> logger,
|
||||
IConfiguration configuration,
|
||||
ITitreRepository titreRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
this._logger.LogDebug(1, "initialisation du AccueilController");
|
||||
this.logger = logger;
|
||||
this.configuration = configuration;
|
||||
this.titreRepository = titreRepository;
|
||||
this.logger.LogDebug(1, "initialisation du AccueilController");
|
||||
this.titreRepository = titreRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche la page d'accueil du webzine, présentant les derniers titres et les titres les plus populaires.
|
||||
/// </summary>
|
||||
/// <returns>La vue Index avec le ViewModel contenant les listes de titres à afficher.</returns>
|
||||
public ActionResult Index()
|
||||
public IActionResult Index()
|
||||
{
|
||||
_logger.LogInformation("Arrivée sur la page d'accueil");
|
||||
this.logger.LogInformation("Arrivée sur la page d'accueil");
|
||||
|
||||
var derniereChronique = _configuration.GetValue<int>("Webzine:NombreDerniereChronique");
|
||||
var topTitres = _configuration.GetValue<int>("Webzine:NombreDeTopTitres");
|
||||
var titres = FakeDataFactory.GetTitres();
|
||||
var derniereChronique = this.configuration.GetValue<int>("Webzine:NombreDerniereChronique");
|
||||
var nbTopTitres = this.configuration.GetValue<int>("Webzine:NombreDeTopTitres");
|
||||
|
||||
var titres = this.titreRepository.FindAll();
|
||||
|
||||
var vm = new AccueilIndexViewModel
|
||||
{
|
||||
DerniersTitres = titres
|
||||
.OrderByDescending(t => t.DateCreation)
|
||||
.Take(derniereChronique)
|
||||
.ToList(),
|
||||
DerniersTitres = titres.Take(derniereChronique).ToList(),
|
||||
|
||||
TopTitres = titres
|
||||
.OrderByDescending(t => t.NbLikes)
|
||||
.Take(topTitres)
|
||||
.ToList()
|
||||
.Take(nbTopTitres)
|
||||
.ToList(),
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
return this.View(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,26 +4,17 @@ namespace Webzine.WebApplication.Controllers;
|
||||
|
||||
public class ApiController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<ApiController> _logger;
|
||||
private readonly ILogger<ApiController> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="ApiController"/> avec un service de journalisation injecté.
|
||||
/// Initializes a new instance of the <see cref="ApiController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="ApiController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
public ApiController(ILogger<ApiController> logger)
|
||||
{
|
||||
this._logger = logger;
|
||||
this._logger.LogDebug(1, "initialisation du ApiController");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint de test pour vérifier que l'API fonctionne correctement. Retourne une chaîne de caractères "Hello World !".
|
||||
/// </summary>
|
||||
/// <returns>Une chaîne de caractères "Hello World !".</returns>
|
||||
[HttpGet]
|
||||
public string HelloWorld()
|
||||
{
|
||||
return "Hello World !";
|
||||
this.logger = logger;
|
||||
this.logger.LogDebug(1, "initialisation du ApiController");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -33,12 +24,12 @@ public class ApiController : ControllerBase
|
||||
[HttpGet]
|
||||
public IActionResult Version()
|
||||
{
|
||||
this._logger.LogInformation("Get Version was called");
|
||||
this.logger.LogInformation("Get Version was called");
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
nom = "webzine",
|
||||
version = "1.0",
|
||||
});
|
||||
return this.Ok(new
|
||||
{
|
||||
nom = "webzine",
|
||||
version = "2.0",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.WebApplication.ViewModels;
|
||||
using Webzine.WebApplication.ViewModels.Artiste;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Artiste;
|
||||
|
||||
public class ArtisteController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<ArtisteController> _logger;
|
||||
private readonly ILogger<ArtisteController> logger;
|
||||
private readonly IArtisteRepository artisteRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="ArtisteController"/> avec un service de journalisation injecté.
|
||||
/// Initializes a new instance of the <see cref="ArtisteController"/> class.
|
||||
/// Initialise une nouvelle instance du <see cref="ArtisteController"/>. avec un service de journalisation injecté.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
public ArtisteController(ILogger<ArtisteController> logger)
|
||||
public ArtisteController(
|
||||
ILogger<ArtisteController> logger,
|
||||
IArtisteRepository artisteRepository)
|
||||
{
|
||||
_logger = logger;
|
||||
this._logger.LogDebug(1, "initialisation du ArtisteController");
|
||||
this.logger = logger;
|
||||
this.logger.LogDebug("Initialisation du ArtisteController");
|
||||
this.artisteRepository = artisteRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,33 +33,41 @@ namespace Webzine.WebApplication.Controllers
|
||||
[HttpGet("/artiste/{nom}")]
|
||||
public IActionResult Index(string nom)
|
||||
{
|
||||
_logger.LogInformation("Tentative d'accès à l'artiste avec le nom : {NomArtiste}", nom);
|
||||
this.logger.LogInformation("Tentative d'accès à l'artiste avec le nom : {NomArtiste}", nom);
|
||||
|
||||
if (string.IsNullOrEmpty(nom)) return RedirectToAction("Index", "Accueil");
|
||||
if (string.IsNullOrEmpty(nom))
|
||||
{
|
||||
this.logger.LogWarning("Nom de l'artiste manquant dans la requête.");
|
||||
return this.RedirectToAction("Index", "Accueil");
|
||||
}
|
||||
|
||||
// On transforme "fatal-bazooka" en "Fatal Bazooka" pour la factory
|
||||
string nomPropre = System.Globalization.CultureInfo.CurrentCulture.TextInfo
|
||||
.ToTitleCase(nom.Replace("-", " "));
|
||||
|
||||
// On appelle la factory pour obtenir l'artiste unique
|
||||
var artiste = ArtisteFactory.SeedArtisteByName(nomPropre);
|
||||
var artiste = this.artisteRepository.FindByName(nomPropre);
|
||||
|
||||
// Check if artiste was found
|
||||
if (artiste == null)
|
||||
{
|
||||
_logger.LogWarning("Artiste non trouvé pour le nom : {NomArtiste}", nomPropre);
|
||||
return NotFound();
|
||||
this.logger.LogWarning("Artiste non trouvé avec le nom : {NomArtiste}", nomPropre);
|
||||
return this.RedirectToAction("Index", "Accueil");
|
||||
}
|
||||
|
||||
_logger.LogInformation("Artiste trouvé : {NomArtiste}", nom);
|
||||
|
||||
// On remplit le ViewModel
|
||||
var viewModel = new ArtisteModel
|
||||
var viewModel = new ArtisteDetailsViewModel
|
||||
{
|
||||
Artiste = artiste,
|
||||
Titres = artiste.Titres
|
||||
IdArtiste = artiste.IdArtiste,
|
||||
Nom = artiste.Nom,
|
||||
Biographie = artiste.Biographie,
|
||||
AlbumsGroupes = artiste.Titres
|
||||
.OrderBy(t => t.Libelle)
|
||||
.GroupBy(t => t.Album)
|
||||
.OrderBy(g => g.Key),
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
this.logger.LogInformation("Artiste trouvé : {NomArtiste}", nom);
|
||||
return this.View(viewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,24 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
/// <summary>
|
||||
/// Controller pour la page contact.
|
||||
/// </summary>
|
||||
public class ContactController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
private readonly ILogger<ContactController> _logger;
|
||||
private readonly ILogger<ContactController> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="ContactController"/> avec un service de journalisation injecté.
|
||||
/// Initializes a new instance of the <see cref="ContactController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="ContactController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
public ContactController(ILogger<ContactController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
this._logger.LogDebug(1, "initialisation du ContactController");
|
||||
this.logger = logger;
|
||||
this.logger.LogDebug(1, "initialisation du ContactController");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -26,7 +27,7 @@ namespace Webzine.WebApplication.Controllers
|
||||
/// <returns>La vue Index de la page de contact.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
return this.View();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +1,69 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Recherche;
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
// <copyright file="RechercheController.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Controllers;
|
||||
|
||||
[Route("recherche")]
|
||||
public class RechercheController : Controller
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
private readonly ILogger<RechercheController> _logger;
|
||||
private readonly ITitreRepository _titreRepository;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
public RechercheController(ILogger<RechercheController> logger, ITitreRepository titreRepository)
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Recherche;
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
[Route("recherche")]
|
||||
public class RechercheController : Controller
|
||||
{
|
||||
_logger = logger;
|
||||
_titreRepository = titreRepository;
|
||||
}
|
||||
private readonly ILogger<RechercheController> logger;
|
||||
private readonly ITitreRepository titreRepository;
|
||||
|
||||
[HttpPost("")]
|
||||
public IActionResult Index(string mot)
|
||||
{
|
||||
_logger.LogInformation("Recherche artistes/titres pour le mot : {Mot}.", mot);
|
||||
|
||||
var titres = _titreRepository.Search(mot)
|
||||
.Concat(_titreRepository.SearchByStyle(mot))
|
||||
.DistinctBy(t => t.IdTitre)
|
||||
.OrderBy(t => t.Libelle)
|
||||
.Select(t => new TitreStyleItem
|
||||
{
|
||||
IdTitre = t.IdTitre,
|
||||
Libelle = t.Libelle,
|
||||
ArtisteNom = t.Artiste?.Nom,
|
||||
UrlJaquette = t.UrlJaquette,
|
||||
Duree = t.Duree
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var artistes = _titreRepository.FindAll()
|
||||
.Select(t => t.Artiste)
|
||||
.Where(a => a != null
|
||||
&& !string.IsNullOrWhiteSpace(a.Nom)
|
||||
&& !string.IsNullOrWhiteSpace(mot)
|
||||
&& a.Nom.Contains(mot, StringComparison.OrdinalIgnoreCase))
|
||||
.DistinctBy(a => a!.IdArtiste)
|
||||
.OrderBy(a => a!.Nom)
|
||||
.Select(a => new RechercheArtisteItem
|
||||
{
|
||||
Nom = a!.Nom,
|
||||
NombreDeTitres = a.Titres?.Count ?? 0
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var vm = new RechercheIndexViewModel
|
||||
public RechercheController(ILogger<RechercheController> logger, ITitreRepository titreRepository)
|
||||
{
|
||||
Mot = mot,
|
||||
Artistes = artistes,
|
||||
Titres = titres
|
||||
};
|
||||
this.logger = logger;
|
||||
this.titreRepository = titreRepository;
|
||||
}
|
||||
|
||||
return View(vm);
|
||||
[HttpPost("")]
|
||||
public IActionResult Index(string mot)
|
||||
{
|
||||
this.logger.LogInformation("Recherche artistes/titres pour le mot : {Mot}.", mot);
|
||||
|
||||
var titres = this.titreRepository.Search(mot)
|
||||
.Concat(this.titreRepository.SearchByStyle(mot))
|
||||
.DistinctBy(t => t.IdTitre)
|
||||
.OrderBy(t => t.Libelle)
|
||||
.Select(t => new TitreStyleItem
|
||||
{
|
||||
IdTitre = t.IdTitre,
|
||||
Libelle = t.Libelle,
|
||||
ArtisteNom = t.Artiste?.Nom,
|
||||
UrlJaquette = t.UrlJaquette,
|
||||
Duree = t.Duree,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var artistes = this.titreRepository.FindAll()
|
||||
.Select(t => t.Artiste)
|
||||
.Where(a => a != null
|
||||
&& !string.IsNullOrWhiteSpace(a.Nom)
|
||||
&& !string.IsNullOrWhiteSpace(mot)
|
||||
&& a.Nom.Contains(mot, StringComparison.OrdinalIgnoreCase))
|
||||
.DistinctBy(a => a!.IdArtiste)
|
||||
.OrderBy(a => a!.Nom)
|
||||
.Select(a => new RechercheArtisteItem
|
||||
{
|
||||
Nom = a!.Nom,
|
||||
NombreDeTitres = a.Titres?.Count ?? 0,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var vm = new RechercheIndexViewModel
|
||||
{
|
||||
Mot = mot,
|
||||
Artistes = artistes,
|
||||
Titres = titres,
|
||||
};
|
||||
|
||||
return this.View(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +1,173 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
// <copyright file="TitreController.cs" company="PlaceholderCompany">
|
||||
// Copyright (c) PlaceholderCompany. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// Controleur responsable de la gestion des titres musicaux :
|
||||
/// affichage des details, filtrage par style,
|
||||
/// ajout de likes, commentaires et recherche.
|
||||
/// </summary>
|
||||
[Route("titre")]
|
||||
public class TitreController : Controller
|
||||
namespace Webzine.WebApplication.Controllers
|
||||
{
|
||||
private readonly ILogger<TitreController> _logger;
|
||||
private readonly ITitreRepository _titreRepository;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Titre;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance du <see cref="TitreController"/>.
|
||||
/// Controleur responsable de la gestion des titres musicaux :
|
||||
/// affichage des details, filtrage par style,
|
||||
/// ajout de likes, commentaires et recherche.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecte.</param>
|
||||
/// <param name="titreRepository">Repository des titres injecte.</param>
|
||||
public TitreController(ILogger<TitreController> logger, ITitreRepository titreRepository)
|
||||
[Route("titre")]
|
||||
public class TitreController : Controller
|
||||
{
|
||||
_logger = logger;
|
||||
_titreRepository = titreRepository;
|
||||
private readonly ILogger<TitreController> logger;
|
||||
private readonly ITitreRepository titreRepository;
|
||||
|
||||
_logger.LogInformation("Initialisation du controleur TitreController.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche le detail d'un titre specifique.
|
||||
/// </summary>
|
||||
/// <param name="id">Identifiant du titre.</param>
|
||||
/// <returns>Vue des details ou 404 si introuvable.</returns>
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult Details(int id)
|
||||
{
|
||||
_logger.LogInformation("Demande d'affichage du detail pour le titre ID {Id}.", id);
|
||||
|
||||
var titre = FindById(id);
|
||||
|
||||
if (titre == null)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TitreController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="TitreController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecte.</param>
|
||||
/// <param name="titreRepository">Repository des titres injecte.</param>
|
||||
public TitreController(ILogger<TitreController> logger, ITitreRepository titreRepository)
|
||||
{
|
||||
_logger.LogWarning("Titre avec ID {Id} introuvable.", id);
|
||||
return NotFound();
|
||||
this.logger = logger;
|
||||
this.titreRepository = titreRepository;
|
||||
|
||||
this.logger.LogInformation("Initialisation du controleur TitreController.");
|
||||
}
|
||||
|
||||
var vm = new TitreDetail
|
||||
/// <summary>
|
||||
/// Affiche le detail d'un titre specifique.
|
||||
/// </summary>
|
||||
/// <param name="id">Identifiant du titre.</param>
|
||||
/// <returns>Vue des details ou 404 si introuvable.</returns>
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult Details(int id)
|
||||
{
|
||||
Details = new TitreContent
|
||||
this.logger.LogInformation("Demande d'affichage du detail pour le titre ID {Id}.", id);
|
||||
|
||||
var titre = this.titreRepository.Find(id);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
this.logger.LogWarning("Titre avec ID {Id} introuvable.", id);
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var vm = new TitreDetail
|
||||
{
|
||||
Details = new TitreContent
|
||||
{
|
||||
IdTitre = titre.IdTitre,
|
||||
Libelle = titre.Libelle,
|
||||
Chronique = titre.Chronique,
|
||||
DateSortie = titre.DateSortie,
|
||||
NbLikes = titre.NbLikes,
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
UrlEcoute = titre.UrlEcoute,
|
||||
ArtisteNom = titre.Artiste.Nom,
|
||||
Styles = titre.Styles,
|
||||
Commentaires = titre.Commentaires,
|
||||
},
|
||||
CommentForm = new TitreComment
|
||||
{
|
||||
IdTitre = titre.IdTitre,
|
||||
},
|
||||
};
|
||||
|
||||
return this.View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche les titres correspondant a un style musical donne.
|
||||
/// </summary>
|
||||
/// <param name="style">Nom du style musical.</param>
|
||||
/// <returns>Vue contenant la liste filtree.</returns>
|
||||
[HttpGet("style/{style}")]
|
||||
public IActionResult Style(string style)
|
||||
{
|
||||
this.logger.LogInformation("Recherche des titres pour le style : {Style}.", style);
|
||||
|
||||
var titresFiltres = this.titreRepository.SearchByStyle(style).ToList();
|
||||
|
||||
var vm = new TitreStyle
|
||||
{
|
||||
StyleName = style,
|
||||
Titres = titresFiltres.Select(MapTitreItem).ToList(),
|
||||
};
|
||||
|
||||
return this.View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute un like a un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Modele contenant l'identifiant du titre.</param>
|
||||
/// <returns>Redirection vers la page detail.</returns>
|
||||
[HttpPost("like")]
|
||||
public IActionResult Like(TitreLike model)
|
||||
{
|
||||
this.logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre);
|
||||
|
||||
var titre = this.titreRepository.Find(model.IdTitre);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
this.logger.LogWarning("Impossible d'ajouter un like. Titre ID {Id} introuvable.", model.IdTitre);
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
titre.NbLikes++;
|
||||
|
||||
return this.RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute un commentaire a un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Donnees du commentaire.</param>
|
||||
/// <returns>Redirection vers la page detail.</returns>
|
||||
[HttpPost("comment")]
|
||||
public IActionResult Comment(TitreComment model)
|
||||
{
|
||||
if (!this.ModelState.IsValid)
|
||||
{
|
||||
this.logger.LogWarning("Echec de validation du modele de commentaire pour le titre ID {Id}.", model.IdTitre);
|
||||
return this.RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
var titre = this.titreRepository.Find(model.IdTitre);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
this.logger.LogWarning("Impossible d'ajouter le commentaire. Titre ID {Id} introuvable.", model.IdTitre);
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var commentaire = new Commentaire
|
||||
{
|
||||
Auteur = model.Auteur,
|
||||
Contenu = model.Contenu,
|
||||
DateCreation = DateTime.Now,
|
||||
IdTitre = model.IdTitre,
|
||||
};
|
||||
|
||||
titre.Commentaires.Add(commentaire);
|
||||
|
||||
this.logger.LogInformation("Commentaire ajoute avec succes au titre ID {Id}.", model.IdTitre);
|
||||
|
||||
return this.RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
private static TitreStyleItem MapTitreItem(Titre titre)
|
||||
{
|
||||
return new TitreStyleItem
|
||||
{
|
||||
IdTitre = titre.IdTitre,
|
||||
Libelle = titre.Libelle,
|
||||
Chronique = titre.Chronique,
|
||||
DateSortie = titre.DateSortie,
|
||||
NbLikes = titre.NbLikes,
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
UrlEcoute = titre.UrlEcoute,
|
||||
ArtisteNom = titre.Artiste?.Nom,
|
||||
Styles = titre.Styles,
|
||||
Commentaires = titre.Commentaires
|
||||
},
|
||||
CommentForm = new TitreComment
|
||||
{
|
||||
IdTitre = titre.IdTitre
|
||||
}
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Affiche les titres correspondant a un style musical donne.
|
||||
/// </summary>
|
||||
/// <param name="style">Nom du style musical.</param>
|
||||
/// <returns>Vue contenant la liste filtree.</returns>
|
||||
[HttpGet("style/{style}")]
|
||||
public IActionResult Style(string style)
|
||||
{
|
||||
_logger.LogInformation("Recherche des titres pour le style : {Style}.", style);
|
||||
|
||||
var titresFiltres = _titreRepository.SearchByStyle(style).ToList();
|
||||
|
||||
var vm = new TitreStyle
|
||||
{
|
||||
StyleName = style,
|
||||
Titres = titresFiltres.Select(MapTitreItem).ToList()
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute un like a un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Modele contenant l'identifiant du titre.</param>
|
||||
/// <returns>Redirection vers la page detail.</returns>
|
||||
[HttpPost("like")]
|
||||
public IActionResult Like(TitreLike model)
|
||||
{
|
||||
_logger.LogInformation("Ajout d'un like pour le titre ID {Id}.", model.IdTitre);
|
||||
|
||||
var titre = FindById(model.IdTitre);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
_logger.LogWarning("Impossible d'ajouter un like. Titre ID {Id} introuvable.", model.IdTitre);
|
||||
return NotFound();
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
Duree = titre.Duree,
|
||||
};
|
||||
}
|
||||
|
||||
titre.NbLikes++;
|
||||
|
||||
return RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ajoute un commentaire a un titre.
|
||||
/// </summary>
|
||||
/// <param name="model">Donnees du commentaire.</param>
|
||||
/// <returns>Redirection vers la page detail.</returns>
|
||||
[HttpPost("comment")]
|
||||
public IActionResult Comment(TitreComment model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_logger.LogWarning("Echec de validation du modele de commentaire pour le titre ID {Id}.", model.IdTitre);
|
||||
return RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
var titre = FindById(model.IdTitre);
|
||||
|
||||
if (titre == null)
|
||||
{
|
||||
_logger.LogWarning("Impossible d'ajouter le commentaire. Titre ID {Id} introuvable.", model.IdTitre);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var commentaire = new Commentaire
|
||||
{
|
||||
Auteur = model.Auteur,
|
||||
Contenu = model.Contenu,
|
||||
DateCreation = DateTime.Now,
|
||||
IdTitre = model.IdTitre
|
||||
};
|
||||
|
||||
titre.Commentaires.Add(commentaire);
|
||||
|
||||
_logger.LogInformation("Commentaire ajoute avec succes au titre ID {Id}.", model.IdTitre);
|
||||
|
||||
return RedirectToAction("Details", new { id = model.IdTitre });
|
||||
}
|
||||
|
||||
private Titre? FindById(int id)
|
||||
{
|
||||
return _titreRepository.Find(id);
|
||||
}
|
||||
|
||||
private static TitreStyleItem MapTitreItem(Titre titre)
|
||||
{
|
||||
return new TitreStyleItem
|
||||
{
|
||||
IdTitre = titre.IdTitre,
|
||||
Libelle = titre.Libelle,
|
||||
ArtisteNom = titre.Artiste?.Nom,
|
||||
UrlJaquette = titre.UrlJaquette,
|
||||
Duree = titre.Duree
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Webzine.WebApplication/Data/.gitignore
vendored
Normal file
1
Webzine.WebApplication/Data/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.sqlite*
|
||||
18
Webzine.WebApplication/Extensions/RouteConfiguration.cs
Normal file
18
Webzine.WebApplication/Extensions/RouteConfiguration.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace Webzine.WebApplication.Extensions;
|
||||
|
||||
public static class RouteConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure les routes de l'application.
|
||||
/// </summary>
|
||||
public static void MapCustomRoutes(this IEndpointRouteBuilder endpoints)
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "areas",
|
||||
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Accueil}/{action=Index}/{id?}");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,14 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
using NLog;
|
||||
using NLog.Web;
|
||||
|
||||
using Webzine.EntitiesContext;
|
||||
using Webzine.Entity;
|
||||
using Webzine.Entity.Fixtures;
|
||||
using Webzine.Repository;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Extensions;
|
||||
|
||||
// Initiation du logger NLog pour la classe courante afin de pouvoir l'utiliser pour logger des messages d'information, d'erreur, etc avant la construction de l'application.
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
@@ -14,47 +21,134 @@ try
|
||||
// Ajoute les services necessaires pour permettre l'utilisation des
|
||||
// controllers avec des vues.
|
||||
builder.Services.AddControllersWithViews()
|
||||
|
||||
// Ajoute la compilation des vues lors de l'execution de l'application.
|
||||
// Cela nous evite de recompiler l'application a chaque modification de vue.
|
||||
// Necessite le package Nuget Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.
|
||||
.AddRazorRuntimeCompilation();
|
||||
|
||||
builder.Services.AddSingleton<ITitreRepository, LocalEntityRepository>();
|
||||
|
||||
// NLog: Setup NLog for Dependency injection
|
||||
builder.Logging.ClearProviders();
|
||||
builder.Host.UseNLog();
|
||||
|
||||
// En fonction de la configuration, utilise soit les repositories basés sur une base de données, soit les repositories basés sur des listes locales.
|
||||
bool useDatabase = builder.Configuration.GetValue<bool>("UseDatabase");
|
||||
bool isSQLite = builder.Configuration.GetValue<bool>("IsSQLite");
|
||||
if (useDatabase)
|
||||
{
|
||||
if (isSQLite)
|
||||
{
|
||||
builder.Services.AddDbContext<WebzineDbContext>(options =>
|
||||
options.UseSqlite(builder.Configuration.GetConnectionString("SqliteConnection")));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Services.AddDbContext<WebzineDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("PostGreSQLConnection")));
|
||||
}
|
||||
|
||||
builder.Services.AddScoped<DbEntityRepository>();
|
||||
builder.Services.AddScoped<ITitreRepository, DbTitreRepository>();
|
||||
builder.Services.AddScoped<IStyleRepository, DbStyleRepository>();
|
||||
builder.Services.AddScoped<IArtisteRepository, DbArtisteRepository>();
|
||||
builder.Services.AddScoped<ICommentaireRepository, DbCommentaireRepository>();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Services.AddScoped<ITitreRepository, LocalTitreRepository>();
|
||||
builder.Services.AddScoped<IStyleRepository, LocalStyleRepository>();
|
||||
builder.Services.AddScoped<IArtisteRepository, LocalArtisteRepository>();
|
||||
builder.Services.AddScoped<ICommentaireRepository, LocalCommentaireRepository>();
|
||||
builder.Services.AddSingleton<InMemoryDataStore>();
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/fr-fr/aspnet/core/performance/response-compression?view=aspnetcore-10.0#configuration
|
||||
// Ajoute le service de compression des réponses HTTP pour réduire la taille des données envoyées au client et améliorer les performances de l'application.
|
||||
builder.Services.AddResponseCompression();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Active la possibilite de servir des fichiers statiques presents dans
|
||||
if (useDatabase)
|
||||
{
|
||||
if (isSQLite)
|
||||
{
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<WebzineDbContext>();
|
||||
db.Database.EnsureDeleted();
|
||||
db.Database.EnsureCreated();
|
||||
var repo = scope.ServiceProvider.GetRequiredService<DbEntityRepository>();
|
||||
repo.SeedBaseDeDonnees();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
// TODO : A modifier pour ne pas supprimer la base de donnée en prod
|
||||
var db = scope.ServiceProvider.GetRequiredService<WebzineDbContext>();
|
||||
db.Database.EnsureDeleted();
|
||||
db.Database.EnsureCreated();
|
||||
var repo = scope.ServiceProvider.GetRequiredService<DbEntityRepository>();
|
||||
repo.SeedBaseDeDonnees();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var store = scope.ServiceProvider.GetRequiredService<InMemoryDataStore>();
|
||||
|
||||
var artistes = SeedDataLocal.GenererListeArtiste(100);
|
||||
var styles = SeedDataLocal.GenererListeStyle(15, 20);
|
||||
var albums = SeedDataLocal.GenererListeAlbums(50);
|
||||
var commentaires = new List<Commentaire>();
|
||||
var titres = SeedDataLocal.GenererListeTitre(500, artistes, styles, albums);
|
||||
|
||||
foreach (var titre in titres)
|
||||
{
|
||||
var commentairesForTitre = SeedDataLocal.GenererListeCommentaire(titre, 0, 5);
|
||||
titre.Commentaires.AddRange(commentairesForTitre);
|
||||
commentaires.AddRange(commentairesForTitre);
|
||||
}
|
||||
|
||||
store.Artistes.AddRange(artistes);
|
||||
store.Styles = styles;
|
||||
store.Titres = titres;
|
||||
store.Commentaires.AddRange(commentaires);
|
||||
}
|
||||
}
|
||||
|
||||
app.UseResponseCompression();
|
||||
|
||||
// Active la possibilité de servir des fichiers statiques presents dans
|
||||
// le dossier wwwroot.
|
||||
app.UseStaticFiles();
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
{
|
||||
OnPrepareResponse = ctx =>
|
||||
{
|
||||
// https://learn.microsoft.com/fr-fr/aspnet/core/fundamentals/static-files?view=aspnetcore-10.0#set-http-response-headers
|
||||
ctx.Context.Response.Headers.Append("Cache-Control", "public, max-age=31536000");
|
||||
},
|
||||
});
|
||||
|
||||
// Active le middleware permettant le routage des requetes entrantes.
|
||||
app.UseRouting();
|
||||
|
||||
// Ajoute une route pour les zones (Areas) comme Admin
|
||||
app.MapControllerRoute(
|
||||
name: "areas",
|
||||
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
|
||||
|
||||
// Ajoute un endpoint permettant de router les urls
|
||||
// avec la forme /controller/action/id(optionnel).
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Accueil}/{action=Index}/{id?}");
|
||||
// Appelle les routes définies dans le dossier Extensions.
|
||||
app.MapCustomRoutes();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// NLog: attrape les exceptions non gerees et les logge.
|
||||
// NLog: attrape les exceptions non gerees et les logger.
|
||||
logger.Error(exception, "Stopped program because of exception");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Assure que NLog flush tous les messages de log avant de fermer l'application.
|
||||
NLog.LogManager.Shutdown();
|
||||
}
|
||||
LogManager.Shutdown();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace Webzine.WebApplication.ViewComponents
|
||||
{
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using Webzine.Repository.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// View component pour la sidebar, récupère les styles depuis le repository.
|
||||
/// </summary>
|
||||
public class SidebarViewComponent : ViewComponent
|
||||
{
|
||||
private readonly IStyleRepository styleRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SidebarViewComponent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="styleRepository">Repository des styles injecté.</param>
|
||||
public SidebarViewComponent(IStyleRepository styleRepository)
|
||||
{
|
||||
this.styleRepository = styleRepository;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Récupère tous les styles triés par libellé et les passe à la vue.
|
||||
/// </summary>
|
||||
/// <returns>Une vue contenant la liste des styles.</returns>
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
var styles = this.styleRepository.FindAll()
|
||||
.OrderBy(s => s.Libelle)
|
||||
.ToList();
|
||||
|
||||
return this.View(styles);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,30 @@
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.WebApplication.ViewModels.Accueil
|
||||
namespace Webzine.WebApplication.ViewModels.Accueil
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour la page d'accueil du webzine, affichant les derniers titres et les titres les plus populaires.
|
||||
/// </summary>
|
||||
public class AccueilIndexViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Définit la liste des derniers titres ajoutés au webzine.
|
||||
/// Obtient ou définit la liste des derniers titres ajoutés au webzine.
|
||||
/// </summary>
|
||||
public List<Entity.Titre> DerniersTitres { get; set; } = [];
|
||||
public List<Titre> DerniersTitres { get; set; } = new List<Titre>();
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des titres les plus populaires du webzine.
|
||||
/// Obtient ou définit la liste des titres les plus populaires du webzine.
|
||||
/// </summary>
|
||||
public List<Entity.Titre> TopTitres { get; set; } = [];
|
||||
public List<Titre> TopTitres { get; set; } = new List<Titre>();
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le nombre de titre disponible.
|
||||
/// </summary>
|
||||
// public int NombreDeTitre { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le nombre de titre paginé.
|
||||
/// </summary>
|
||||
// public int Pagination { get; set; } = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
namespace Webzine.WebApplication.ViewModels.Artiste
|
||||
{
|
||||
using Webzine.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher les informations d'un artiste et ses titres groupés par album.
|
||||
/// </summary>
|
||||
public class ArtisteDetailsViewModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Obtient ou définit l'identifiant de l'artiste.
|
||||
/// </summary>
|
||||
public int IdArtiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit le nom de l'artiste.
|
||||
/// </summary>
|
||||
public string Nom { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit la biographie de l'artiste.
|
||||
/// </summary>
|
||||
public string Biographie { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Obtient ou définit défini la liste des titres de l'artiste groupés par nom d'Album.
|
||||
/// </summary>
|
||||
public IEnumerable<IGrouping<string?, Titre>> AlbumsGroupes { get; set; }
|
||||
= Enumerable.Empty<IGrouping<string?, Titre>>();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Webzine.Entity;
|
||||
|
||||
namespace Webzine.WebApplication.ViewModels.Artiste
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour afficher les détails d'un artiste, incluant les informations de l'artiste et la liste de ses titres.
|
||||
/// </summary>
|
||||
public class ArtisteModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Artiste dont on affiche les détails.
|
||||
/// </summary>
|
||||
public Entity.Artiste Artiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste des titres de l'artiste.
|
||||
/// </summary>
|
||||
public List<Entity.Titre> Titres { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -14,4 +14,4 @@ public class RechercheArtisteItem
|
||||
/// Nombre de titres associes a l'artiste.
|
||||
/// </summary>
|
||||
public int NombreDeTitres { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,10 @@ public class RechercheIndexViewModel
|
||||
/// <summary>
|
||||
/// Artistes trouves.
|
||||
/// </summary>
|
||||
public List<RechercheArtisteItem> Artistes { get; set; } = new();
|
||||
public List<RechercheArtisteItem> Artistes { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Titres trouves.
|
||||
/// </summary>
|
||||
public List<TitreStyleItem> Titres { get; set; } = new();
|
||||
}
|
||||
public List<TitreStyleItem> Titres { get; set; } = new ();
|
||||
}
|
||||
@@ -50,10 +50,10 @@ public class TitreContent
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album associé au titre.
|
||||
/// </summary>
|
||||
public List<Style> Styles { get; set; } = new();
|
||||
public List<Style> Styles { get; set; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// Définit la liste des commentaires associés au titre.
|
||||
/// </summary>
|
||||
public List<Commentaire> Commentaires { get; set; } = new();
|
||||
public List<Commentaire> Commentaires { get; set; } = new ();
|
||||
}
|
||||
@@ -13,5 +13,5 @@ public class TitreStyle
|
||||
/// <summary>
|
||||
/// Définit la liste des items de titre associés au style musical.
|
||||
/// </summary>
|
||||
public List<TitreStyleItem> Titres { get; set; } = new();
|
||||
public List<TitreStyleItem> Titres { get; set; } = new ();
|
||||
}
|
||||
@@ -5,48 +5,23 @@
|
||||
|
||||
<h1>Derniers titres chroniqués</h1>
|
||||
|
||||
@* TEMPLATE *@
|
||||
@* <div class="container">
|
||||
<div class="container bg-light row p-3 mt-3">
|
||||
<div class="col-auto">
|
||||
<img class="img-thumbnail"
|
||||
src="" />
|
||||
</div>
|
||||
<div class="col">
|
||||
<a class="text-primary text-decoration-none fw-light h4">Justice - D.A.N.C.E</a>
|
||||
<p class="mt-2 mb-3 text-muted ">
|
||||
Insérer texte
|
||||
</p>
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
<a class="btn btn-primary btn-sm">Lire la suite</a>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small">
|
||||
<i class="fa-solid fa-calendar"></i>
|
||||
Date :
|
||||
17/12/2022 11:08:08
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small">
|
||||
<i class="fa-solid fa-tags"></i>
|
||||
<a class="text-decoration-none m-1">Insérer style</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> *@
|
||||
|
||||
<div class="container">
|
||||
@foreach (var titre in Model.DerniersTitres)
|
||||
{
|
||||
<div class="container bg-light row p-3 mt-3">
|
||||
<div class="col-auto">
|
||||
<img class="img-thumbnail img-fluid"
|
||||
style="max-width:200px;"
|
||||
@* UrlJaquette *@
|
||||
src="@titre.UrlJaquette" />
|
||||
<div class="row bg-light p-3 mt-3 align-items-center">
|
||||
|
||||
<!-- Image -->
|
||||
<div class="col-12 col-md-3 text-center mb-3 mb-md-0">
|
||||
<img class="img-fluid img-thumbnail"
|
||||
src="@titre.UrlJaquette"
|
||||
alt="@titre.Libelle"
|
||||
loading="lazy" />
|
||||
</div>
|
||||
<div class="col">
|
||||
@* Artiste - Titre @titre.Artiste - @titre.Libelle*@
|
||||
|
||||
<!-- Contenu -->
|
||||
<div class="col-12 col-md-9">
|
||||
|
||||
<!-- Artiste - Titre -->
|
||||
<div class="fw-light h4 text-primary">
|
||||
<a asp-action="Index"
|
||||
asp-controller="Artiste"
|
||||
@@ -60,62 +35,72 @@
|
||||
@titre.Libelle
|
||||
</a>
|
||||
</div>
|
||||
@* Chronique *@
|
||||
<p class="mt-2 mb-3 text-muted ">
|
||||
@titre.Chronique
|
||||
|
||||
<!-- Chronique -->
|
||||
<p class="mt-2 mb-3 text-muted">
|
||||
@(titre.Chronique.Length > 200 ? titre.Chronique.Substring(0, 200) + "..." : titre.Chronique)
|
||||
</p>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
<a asp-action="Details" asp-controller="Titre" asp-route-id="@titre.IdTitre" class="btn btn-primary btn-sm">Lire la suite</a>
|
||||
<a asp-action="Details"
|
||||
asp-controller="Titre"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="btn btn-primary btn-sm">
|
||||
Lire la suite
|
||||
</a>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small">
|
||||
<i class="fa-solid fa-calendar me-1"> </i>
|
||||
@* Date de création *@
|
||||
<i class="fa-solid fa-calendar me-1"></i>
|
||||
@titre.DateCreation
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center text-muted small">
|
||||
<i class="fa-solid fa-tags"></i>
|
||||
@* Style *@
|
||||
<a asp-controller="Titre" asp-action="Style" asp-route-id="Pop" class="text-decoration-none m-1">Pop</a>
|
||||
<div class="d-flex align-items-center text-muted small flex-wrap">
|
||||
<i class="fa-solid fa-tags me-2"></i>
|
||||
|
||||
@foreach (var style in titre.Styles)
|
||||
{
|
||||
<a asp-controller="Titre"
|
||||
asp-action="Style"
|
||||
asp-route-id="@style.Libelle"
|
||||
class="text-decoration-none me-1">
|
||||
@style.Libelle@(style != titre.Styles.Last() ? "," : "")
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<!-- Bouton -->
|
||||
<div class="row justify-content-end">
|
||||
<button class="btn btn-secondary col-auto mt-3">Titres plus anciens >></button>
|
||||
<button class="btn btn-secondary col-auto mt-3">
|
||||
Titres plus anciens >>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* TEMPLATE *@
|
||||
@* <div class="container">
|
||||
<div class="row">
|
||||
<div class="card col m-1" style="width: 18rem;">
|
||||
<img class="card-img-top"
|
||||
src="" alt="Alternate Text" />
|
||||
|
||||
<div class="card-body">
|
||||
<a class="card-link" href="#">Album</a><br />
|
||||
par <a class="card-link">Artiste</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> *@
|
||||
<div class="container">
|
||||
<h1 class="mt-5">Titres les plus populaires</h1>
|
||||
<div class="row">
|
||||
@foreach (var titre in Model.TopTitres)
|
||||
{
|
||||
<div class="card col m-1" style="width: auto;">
|
||||
<img class="card-img-top"
|
||||
src="@titre.UrlJaquette" />
|
||||
<div class="row g-3">
|
||||
@foreach (var titre in Model.TopTitres)
|
||||
{
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card h-100">
|
||||
<img class="card-img-top" src="@titre.UrlJaquette" alt="@titre.Album" loading="lazy" />
|
||||
|
||||
<div class="card-body">
|
||||
<a asp-controller="Titre" asp-action="Details" asp-route-id="@titre.IdTitre" class="card-link">@titre.Album</a><br />
|
||||
par <a asp-controller="Artiste" asp-action="Index" asp-route-nom="@titre.Artiste.Nom" class="card-link">@titre.Artiste.Nom</a>
|
||||
<div class="card-body">
|
||||
<a asp-controller="Titre" asp-action="Details" asp-route-id="@titre.IdTitre" class="card-link">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
<br />
|
||||
par
|
||||
<a asp-controller="Artiste" asp-action="Index" asp-route-nom="@titre.Artiste.Nom" class="card-link">
|
||||
@titre.Artiste.Nom
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
@model Webzine.WebApplication.ViewModels.Artiste.ArtisteModel
|
||||
@model Webzine.WebApplication.ViewModels.Artiste.ArtisteDetailsViewModel;
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Artiste";
|
||||
@@ -6,24 +6,22 @@
|
||||
|
||||
|
||||
<div class="container">
|
||||
<h1>@Model.Artiste.Nom</h1>
|
||||
<h1>@Model.Nom</h1>
|
||||
|
||||
<hr class="mb-5" />
|
||||
<hr/>
|
||||
|
||||
<p class="lead">@Model.Artiste.Biographie</p>
|
||||
<p class="lead">@Model.Biographie</p>
|
||||
|
||||
<h2 class="mt-5 mb-4">Albums</h2>
|
||||
<hr class="mb-5" />
|
||||
<hr/>
|
||||
|
||||
@* On groupe les titres par nom d'album *@
|
||||
@{
|
||||
var albumsGroupes = Model.Titres
|
||||
.OrderBy(t => t.Libelle) // Trie les titres par ordre alphabétique au sein de chaque groupe futur
|
||||
.GroupBy(t => t.Album) // Groupe par nom d'album
|
||||
.OrderBy(g => g.Key); // Trie les albums par ordre alphabétique (la clé du groupe)
|
||||
}
|
||||
|
||||
@foreach (var groupe in albumsGroupes)
|
||||
@if (!Model.AlbumsGroupes.Any())
|
||||
{
|
||||
<p>Cet artiste n'a pas encore de titres répertoriés.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
@foreach (var groupe in Model.AlbumsGroupes)
|
||||
{
|
||||
// On récupère le premier titre du groupe pour afficher l'image de l'album
|
||||
var premierTitre = groupe.First();
|
||||
@@ -32,7 +30,8 @@
|
||||
<div class="col-md-3 mb-3">
|
||||
<img src="@premierTitre.UrlJaquette"
|
||||
class="img-fluid shadow-sm rounded border"
|
||||
alt="Pochette de @groupe.Key" />
|
||||
alt="Pochette de @groupe.Key"
|
||||
loading="lazy" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
@@ -59,7 +58,7 @@
|
||||
<a asp-controller="Titre"
|
||||
asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="text-primary fw-bold">
|
||||
class="text-primary">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
</td>
|
||||
@@ -70,4 +69,5 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@@ -4,14 +4,15 @@
|
||||
|
||||
<div class="container">
|
||||
<h1>Contact</h1>
|
||||
<div>
|
||||
<div class="my-2">
|
||||
C.U.C.D.B - DIIAGE <br />
|
||||
69 Avenue Aristide Briand<br />
|
||||
21000 Dijon
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<div class ="my-2">
|
||||
<i class="fa-solid fa-phone"></i> Phone : 03 80 40 50 60<br />
|
||||
<i class="fa-solid fa-envelope"></i> secretariat@cucdb.fr
|
||||
<i class="fa-solid fa-envelope"></i> <span class="text-primary">secretariat@cucdb.fr</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -21,43 +22,43 @@
|
||||
<h2>Suivez-nous</h2>
|
||||
<div class="row g-4 text-center">
|
||||
<div class="col-md-4">
|
||||
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
|
||||
<i class="fa-solid fa-link fa-3x text-primary mb-3"></i>
|
||||
<a href="#" class="card h-100 p-4 border-0 bg-light">
|
||||
<i class="fa-solid fa-link fa-3x text-primary mb-3 align-self-center"></i>
|
||||
<div class="fw-bold text-primary">Site officiel du DIIAGE</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
|
||||
<i class="fa-brands fa-facebook fa-3x text-primary mb-3"></i>
|
||||
<a href="#" class="card h-100 p-4 border-0 bg-light">
|
||||
<i class="fa-brands fa-facebook fa-3x text-primary mb-3 align-self-center"></i>
|
||||
<div class="fw-bold text-primary">Facebook</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
|
||||
<i class="fa-brands fa-instagram fa-3x text-primary mb-3"></i>
|
||||
<a href="#" class="card h-100 p-4 border-0 bg-light">
|
||||
<i class="fa-brands fa-instagram fa-3x text-primary mb-3 align-self-center"></i>
|
||||
<div class="fw-bold text-primary">Instagram</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
|
||||
<i class="fa-brands fa-linkedin fa-3x text-primary mb-3"></i>
|
||||
<a href="#" class="card h-100 p-4 border-0 bg-light">
|
||||
<i class="fa-brands fa-linkedin fa-3x text-primary mb-3 align-self-center"></i>
|
||||
<div class="fw-bold text-primary">LinkedIn</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
|
||||
<i class="fa-solid fa-map fa-3x text-primary mb-3"></i>
|
||||
<a href="#" class="card h-100 p-4 border-0 bg-light">
|
||||
<i class="fa-solid fa-map fa-3x text-primary mb-3 align-self-center"></i>
|
||||
<div class="fw-bold text-primary">Google Maps</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<a href="#" class="card h-100 p-4 shadow-sm border-0 bg-light-subtle text-decoration-none">
|
||||
<i class="fa-brands fa-twitter fa-3x text-primary mb-3"></i>
|
||||
<a href="#" class="card h-100 p-4 border-0 bg-light">
|
||||
<i class="fa-brands fa-twitter fa-3x text-primary mb-3 align-self-center"></i>
|
||||
<div class="fw-bold text-primary">Twitter</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Recherche";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
||||
<div class="container mt-4">
|
||||
@@ -54,7 +53,7 @@
|
||||
asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="me-3 text-black">
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70" height="70" class="object-fit-cover" />
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70" height="70" class="object-fit-cover" loading="lazy"/>
|
||||
</a>
|
||||
|
||||
<div class="justify-content-center d-flex flex-column">
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
@model IEnumerable<Webzine.Entity.Style>
|
||||
|
||||
<aside class="col-lg-3 d-none d-lg-block">
|
||||
<div>
|
||||
<h2>À propos</h2>
|
||||
<p>Retrouvez les dernières pépites sur notre webzine.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Styles</h2>
|
||||
<ul>
|
||||
@foreach (var style in Model)
|
||||
{
|
||||
<li>
|
||||
<a asp-controller="Titre"
|
||||
asp-action="Style"
|
||||
asp-route-style="@style.Libelle">
|
||||
@style.Libelle
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -3,11 +3,6 @@
|
||||
*@
|
||||
@{
|
||||
}
|
||||
<div class="site-footer text-bg-light mt-auto">
|
||||
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3">
|
||||
<div class="col-md-4 d-flex align-items-center">
|
||||
|
||||
<span class="mb-3 mb-md-0 ms-5 text-body-secondary">© ASP .NET Core - DIIAGE 2025 - 2026</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<footer class="py-3 text-bg-light">
|
||||
<p class="ms-5">© ASP .NET Core - DIIAGE 2025 - 2026</p>
|
||||
</footer>
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@* TODO : Modifier, il s'agit d'une liste *@
|
||||
<li class="nav-item">
|
||||
@* <a class="nav-link" href="#">
|
||||
<i class="fa-solid fa-screwdriver-wrench"></i> Administration
|
||||
|
||||
@@ -6,29 +6,25 @@
|
||||
<title>@ViewData["Title"] - Webzine</title>
|
||||
|
||||
@* Ajout de bootstrap *@
|
||||
<script src="~/js/bootstrap.min.js" defer></script>
|
||||
<script src="~/js/bootstrap.bundle.js" defer></script>
|
||||
<link rel="stylesheet" href="~/css/app.css">
|
||||
<link rel="stylesheet" href="~/css/bootstrap.min.css">
|
||||
|
||||
@* Ajout de font-awesome *@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<link rel="stylesheet" href="~/css/all.min.css">
|
||||
<link rel="stylesheet" href="~/css/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="site-shell">
|
||||
<partial name="_Header"/>
|
||||
<div class="container-fluid flex-grow-1 py-4">
|
||||
<div class="row g-0">
|
||||
<main class="col mx-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
@if(ViewContext.RouteData.Values["area"]?.ToString() != "Administration")
|
||||
{
|
||||
<partial name="_Sidebar" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<partial name="_Footer" />
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
<partial name="_Header"/>
|
||||
<div class="container-fluid flex-grow-1 py-4">
|
||||
<div class="row">
|
||||
<main class="col mx-3">
|
||||
@RenderBody()
|
||||
</main>
|
||||
@if(ViewContext.RouteData.Values["area"]?.ToString() != "Administration")
|
||||
{
|
||||
@await Component.InvokeAsync("Sidebar")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<partial name="_Footer" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script src="~/js/bootstrap.bundle.min.js" defer></script>
|
||||
@@ -1,51 +0,0 @@
|
||||
@*
|
||||
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
|
||||
*@
|
||||
@{
|
||||
}
|
||||
<aside class="col-3">
|
||||
<div>
|
||||
<h2>À propos</h2>
|
||||
<p>Retrouvez les dernières pépites sur notre webzine.</p>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Styles</h2>
|
||||
<ul>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Acid house">Acid house</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Ambient">Ambient</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Deep house">Deep house</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Disco">Disco</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Downtempo">Downtempo</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Drum n bass">Drum n bass</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Dub Techno">Dub Techno</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Electro">Electro</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Electronic">Electronic</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Experimental">Experimental</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Funk">Funk</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Garage">Garage</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hardcore">Hardcore</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hardstyle">Hardstyle</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Hip hop">Hip hop</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="House">House</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Indie">Indie</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Industrial">Industrial</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Jazz">Jazz</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Latin">Latin</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Metal">Metal</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Minimal">Minimal</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Pop">Pop</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Progressive">Progressive</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Punk">Punk</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="R&B">R&B</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Rap">Rap</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Reggae">Reggae</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Rock">Rock</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Soul">Soul</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Techno">Techno</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Trance">Trance</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="Trip hop">Trip hop</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="UK garage">UK garage</a></li>
|
||||
<li><a asp-controller="Titre" asp-action="Style" asp-route-id="World">World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
@@ -40,7 +40,7 @@
|
||||
{
|
||||
var style = Model.Details.Styles[i];
|
||||
|
||||
<a class="text-primary text-decoration-none fw-semibold"
|
||||
<a class="text-primary fw-semibold"
|
||||
asp-controller="Titre"
|
||||
asp-action="Style"
|
||||
asp-route-style="@style.Libelle">
|
||||
@@ -86,7 +86,9 @@
|
||||
<div class="col-md-4 text-center">
|
||||
<img src="@Model.Details.UrlJaquette"
|
||||
class="img-fluid rounded shadow"
|
||||
alt="Jaquette" />
|
||||
alt="Jaquette"
|
||||
loading="lazy"
|
||||
fetchpriority="high" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -154,7 +156,7 @@
|
||||
|
||||
<h4 class="mb-4">Commentaires</h4>
|
||||
|
||||
@if (Model.Details.Commentaires.Any())
|
||||
@if (Model.Details.Commentaires != null && Model.Details.Commentaires.Any())
|
||||
{
|
||||
foreach (var comment in Model.Details.Commentaires.OrderByDescending(c => c.DateCreation))
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
@{
|
||||
ViewData["Title"] = $"Titres - {Model.StyleName}";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
<div class="container mt-4">
|
||||
<div class="row">
|
||||
@@ -30,7 +29,7 @@
|
||||
<a asp-action="Details"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="me-3 text-black">
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70px" height="70px" class="object-fit-cover"/>
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70px" height="70px" class="object-fit-cover" loading="lazy"/>
|
||||
</a>
|
||||
|
||||
<!-- Infos -->
|
||||
|
||||
@@ -11,19 +11,20 @@
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
</Content>
|
||||
<Content Include="..\Webzine.Documentation\StyleCop\stylecop.json">
|
||||
<AdditionalFiles Include="..\Webzine.Documentation\StyleCop\stylecop.json">
|
||||
<Link>stylecop.json</Link>
|
||||
</Content>
|
||||
</AdditionalFiles>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\data\" />
|
||||
<Folder Include="wwwroot\lib\" />
|
||||
<Folder Include="Data\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.*" />
|
||||
<PackageReference Include="NLog" Version="6.1.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
|
||||
@@ -33,6 +34,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Webzine.EntitiesContext\Webzine.EntitiesContext.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Repository\Webzine.Repository.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
},
|
||||
"Webzine": {
|
||||
"NombreDerniereChronique": 3,
|
||||
"NombreDeTopTitres" : 3
|
||||
"NombreDeTopTitres": 3
|
||||
},
|
||||
"UseDatabase": true,
|
||||
"IsSQLite": true,
|
||||
"ConnectionStrings": {
|
||||
"SqliteConnection": "Data Source=Data/webzine.sqlite",
|
||||
"PostGreSQLConnection" : "Host=localhost;Port=5432;Username=admin;Password=admin123;Database=webzine_db"
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
||||
9
Webzine.WebApplication/wwwroot/css/all.min.css
vendored
Normal file
9
Webzine.WebApplication/wwwroot/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
Webzine.WebApplication/wwwroot/images/avatar.png
Normal file
BIN
Webzine.WebApplication/wwwroot/images/avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
6312
Webzine.WebApplication/wwwroot/js/bootstrap.bundle.js
vendored
6312
Webzine.WebApplication/wwwroot/js/bootstrap.bundle.js
vendored
File diff suppressed because it is too large
Load Diff
7
Webzine.WebApplication/wwwroot/js/bootstrap.bundle.min.js
vendored
Normal file
7
Webzine.WebApplication/wwwroot/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
Webzine.WebApplication/wwwroot/webfonts/fa-brands-400.woff2
Normal file
BIN
Webzine.WebApplication/wwwroot/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user