0% found this document useful (0 votes)
31 views114 pages

T3 - Introduction To Information Systems Design

Uploaded by

mohamedabourihh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
31 views114 pages

T3 - Introduction To Information Systems Design

Uploaded by

mohamedabourihh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 114

T3 – Introduction to Information

Systems design

Design & Tests 1


In this lesson

22
Contents

• Previously on DP1…
• React in a Nutshell
– Decomposing our UI into smaller components
– View design patterns
– Component Tree & Lifecycle
– State sharing in React: props and contexts
– React router
– Fetching data and static elements
• Everything you always wanted to know about Spring…
– Inversion of control
– Dependency injection
• The MVC pattern in Spring applied to API creation
– The Dispatcher Servlet
– Request Mapping
– Processing HTTP requests
– Customizing the HTTP responses
• Securing the whole stack

3
Previously on DP1…

4
Our goal is to design applications

• Organized into different modules


• Each module should hide details on how it is implemented to others so that
changes in the module do not affect others
• Modules should have a high cohesion (single responsibility)
• Modules should have a low coupling (should depend on just a few other
modules)

5
To help us on this task we (re-)use

ARCHITECTURAL DESIGN PATTERNS SOFTWARE


STYLES FRAMEWORKS

6
Design is making decisions about our application

7
Decision: We are going to implement a web application

INTERNET SITE:
spring-pet-clinic.com

8
Decision: We are using a SPA lifecycle

9
Decision: We are going to implement a monolithic single-page application
using an external relational database

Backend

1. HTTP
Request
to the page
Spring
2. HTML + boot
JavaScript +
CSS
Frontend
WEB SERVER
Development 5. Data
4. SQL Queries
server
(only used
during development
DEV WEB for hot reloading and
SERVER 10
other useful features) DATABASE
How are we going to organize our web application?

11
Decision: We are going to implement the application using a
layered architecture with three layers

Responsible for presenting information to and


Presentation Layer interacting with the user through the graphical user
interface.

It processes presentation layer requests, performs


calculations and operations (e.g. authentication), and
Business Logic layer
moves data between presentation and resource
layers.

Responsible for managing data: flat files, XML,


Resource layer
databases, web services, etc.

12
This gives us an idea on how to organize our code, but we still
need help on how to organize each layer

13
Let’s focus on the presentation layer

14
Problem: How can we organize the presentation layer?
Solution: Splitting user interface interaction into three distinct roles

Change view Controller


• Select views. Consult and update data
• Process user events.
• Request changes to the model.

User events

View Model
• Show interface. • Retrieve and modify data
• Respond to user events. • Access external resources.

15
What about the other layers?

16
This is the global view.
We will learn about presentation layer in this lesson!. The other layers later.
Presentation Layer Spring
boot
Change view
Controller Update/Query data

RESTful API
User events
Business Logic layer
(HTTP requests) Spring
boot
View Model

Service
Resource layer Entities
Entities
invokes Entities
SQL Queries

Repository returns

DATABASE
17
React in a nutshell

18
This is the global view.
We will learn about presentation layer today!. The other layers later.
Presentation Layer Spring
boot
Change view
Controller Update/Query data

RESTful API
User events
Business Logic layer
(HTTP requests) Spring
boot
View Model

Service
Resource layer Entities
Entities
invokes Entities
SQL Queries

Repository returns

DATABASE
19
Features all these frameworks provide

Decomposition of UI into components

Automatically connect application state and visualization

Routing mechanism

Fetching dynamic and static data

Testing facilities

21
Features all these frameworks provide

Decomposition of UI into components

Automatically connect application state and visualization

Routing mechanism

Fetching dynamic and static data

Testing facilities

22
What is a component in a nutshell

index.html App.js

Hello, Sara
Hello, Cahal
Hello, Edite 23
To create our application we must learn to decompose our UI into
components

24
Decomposition of UI into components

25
Decomposition of UI into components

• FilterableProductTable
• SearchBar
• ProductTable
• ProductCategoryRow
• ProductRow

26
Decomposition of UI into components. Do it yourself

27
Decomposition of UI into components

28
Decomposition of UI into components
• App – Main component of the application
o NavBar – Side navigation bar
▪ [ NavButton ]. Displays a navigation
button with an associated icon.
o UserNotificationArea – Notification area
and identification of the current user
o MetricsBar – This component displays the
main metrics of the game. 4 metrics will be
displayed: games played, points achieved,
total time, and cards played.
▪ [ MetricWell ] –Provides the value and
weekly increment for a particular
metric.
o GamesEvolutionChart – It shows the trend
of evolution in the last 4 months in terms of
game played, won, lost and abandoned.
o PopularCardsChart – It shows the
proportion of the N cards most used.
o FrequentCoPlayersTable – Displays the most
usual co-players. It shows the name, the
date of the last game, the location of the
player, the percentage of games played by
both in which the user has won and
whether the player is a friend or not. 29
Features all these frameworks provide

Decomposition of UI into components

Automatically connect application state and visualization

Routing mechanism

Fetching dynamic and static data

Testing facilities

31
React is about connecting State and View

32
In our previous example, components were like this

HTML + JSX
Javascript components

33
But, it is really like this

State &
Context

HTML + JSX
Javascript
components

Hooks &
Events

34
Components with State: Declarative UI

• In React the UI is not modified from code directly in response to user actions.
• Instead, we describe the UI depending on the state, and then we trigger state
changes that cause the re-rendering of the component.

35
React component tree & life-cycle

• The DOM is a tree structure that browsers use to


represent the page
• Before your components are displayed on screen, they
must be rendered by React.
Trigger: Render: Commit:

2 reasons for triggering a render: React calls your components After rendering (calling) your
to figure out what to display components, React will modify
1 – Initial render on the page on screen. “Rendering” is the DOM. React only changes
2 – Component State update React calling your the DOM nodes if there’s a
components. difference between renders
36
Images taken from the React documentation at: https://react.dev/learn
React component tree & life-cycle (An example)

React passes a snapshot of


You tell React to React updates the state value into the
update the state (using the state value component. Thus, changes
a setX() function in an to state during rendering
event handler) will no be taken into
https://react.dev/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time account for state variables
Images taken from the React documentation at: https://react.dev/learn
during rendering 37
Using complex objects and arrays as State (treat them as read-only)

• Although objects in React state are technically mutable, you should


treat them as if they were immutable—like numbers, booleans, and
strings. Instead of mutating them, you should always replace them with
a new whole object.
https://react.dev/learn/updating-objects-in-state#copying-objects-with-the-spread-syntax
• To ease this you can use the spread syntax (based on the … operator):

• (With arrays is the same, you should treat them as immutable). Thus:
avoid (mutates the array) prefer (returns a new array)
Adding push, unshift concat, [...arr] spread syntax
removing pop, shift, splice filter, slice
replacing splice, arr[i] = ... assignment map
sorting reverse, sort copy the array first
38
https://react.dev/learn/updating-arrays-in-state
The magic behind React
• Browsers use tree structures to model UI. The DOM represents HTML elements.

• React also uses tree structures to manage and model the UI you make.

• React makes UI trees from your JSX. Then React DOM updates the browser
DOM elements to match that UI tree.

• The state of the components is tied to the position in the tree or a key, that
is to identify which state variables correspond to which component.

39
Images taken from the React documentation at: https://react.dev/learn
State is independent for each component

40
Images taken from the React documentation at: https://react.dev/learn
Sharing state between components

• You need to move the state from the individual buttons “upwards” to the closest
component containing all of them

41
Images taken from the React documentation at: https://react.dev/learn
Sharing state between components

42
Images taken from the React documentation at: https://react.dev/learn
Sharing state between components: Lifting state up, passing props down

export default function MyApp() {


const [count, setCount] = useState(0);
function handleClick() { Lifting state up
setCount(count + 1);
}
return(
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}

function MyButton({ count, onClick }) {


return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
44
} from the React documentation at: https://react.dev/learn
Images taken
Prop drilling

• What would happen if we need to share the user name?

Prop drilling

46
Images taken from the React documentation at: https://react.dev/learn
Sharing state with Context
• Context lets a parent component provide data to the entire tree below it.

• Some use cases for context are:


– Theming: If your app lets the user change its appearance (e.g. dark mode), a context provider at the
top of your app, provides such info to components that need to adjust their visual look.
– Current account: Many components might need to know the currently logged in user. Putting it in
context makes it convenient to read it anywhere in the tree.
– Managing state: As your app grows, you might end up with a lot of state closer to the top of your app.
Many distant components below may want to change it. It is common to use a reducer together with
context to manage complex state and pass it down to distant components without too much hassle.
We will cover reducers in later in this course.

47
Images taken from the React documentation at: https://react.dev/learn
Sharing state with Context: an example
MyContext.js
import { createContext } from 'react’
export default MyContext = createContext({})

App.js
import MyContext from '../MyContext’

function App() {
return (
<MyContext.Provider value={{ name: 'Charlie', age: 40 }}>
<MyComponent />
</MyContext.Provider>
);
}

MyComponent.js
import React, { useContext } from 'react’
import MyContext from '../MyContext’

function MyComponent() {
const { name, age } = useContext(MyContext);
return (
<div>
<h1>My name is {name}.</h1>
<h2>I am {age} years old.</h2>
</div> );
}
48
Features all these frameworks provide

Decomposition of UI into components

Automatically connect application state and visualization

Routing mechanism

Fetching dynamic and static data

Testing facilities

49
Routing mechanism
An example: react-router

• React works by manipulating the DOM and rendering components dynamically in a


single page, but users and bots access web pages using deeply nested URLs (e.g.:
http://mystore.com/bills/17/details).

• React router conditionally renders certain components to display depending on


the route used in the URL (e.g.: / for home page, /contact for contact details, etc.).

• In order to setup the app to work with react router everything must be enclosed by
<BrowserRouter>, and alternative routes are defined inside Routes as a Route:
index.js App.js
ReactDOM.render( function App() {
<BrowserRouter> return (
<App /> <div className="App">
</BrowserRouter>, <Routes>
document.getElementById('root’) <Route path="/" element={ <Home/> }/>
) <Route path=”/about" element={ <About/> }/>
Home.js <Route path=”/contact" element={ <Contact/> }/>
function Home() { <Route path=”/cats/:id" element={ <CatPage/> }/>
return ( </Routes>
<Link to=“/about”>About</Link>) } </div> )
51
}
Features all these frameworks provide

Decomposition of UI into components

Automatically connect application state and visualization

Routing mechanism

Fetching dynamic and static data

Testing facilities

53
Fetching data from the backend
Fetching data from the backend (useEffect)
For static resources like images, fonts, JS, CSS…

You can add fonts, images and css at /frontend/src/static/ and link them
statically using %PUBLIC_URL% as the path to the resource.

57
For static resources like images, fonts, JS, CSS…

We recommend to add them to the specific folder of the components you


are working on and import them directly:

WHY?

58
We know how to invoke the Controller from the View but …
How do we connect the controller with the model?

Change view
Controller Update/Query data

User events
(HTTP requests)

View Model

Service
Entities
Entities
invokes Entities
SQL Queries

Repository returns

DATABASE
59
This is the global view.
We will learn about presentation layer today!. The other layers later.
Presentation Layer Spring
boot
Change view
Controller Update/Query data

RESTful API
User events
Business Logic layer
(HTTP requests) Spring
boot
View Model

Service
Resource layer Entities
Entities
invokes Entities
SQL Queries

Repository returns

DATABASE
60
Decision: We are using Spring as a framework to implement the backend of
our application

Let’s see how Spring helps us to implement the MVC pattern, but
before that…

61
Spring involves many different projects

https://spring.io/projects
Spring Framework is also many things…

Integration
Core
Data Access Web with other
Technologies
technologies
IoC Web services
Transactions Web MVC
EJB
Resources
DAO JMS
View
Validation technologies JMX
JDBC
Expressions Integrating with Email
other frameworks Task Execution
ORM
AOP
Cache
OXM Portlet MVC
Testing Dynamic langs

64
It became so complex that a project was necessary to help configure it all

65
Spring boot starters

• Spring boot starters address the problem of managing common dependencies


that often appear clustered in projects.
• Each starter provides, with a single dependency, a whole set of libraries for an
aspect of the project.
• Spring boot ensures that the versions of each library are compatible and work in
accordance together (which is difficult when they evolve independently)
• Spring boot provides more than 30 starters.

66
Spring boot starters: An example
• TEST STARTER:

– To create tests we usually also use a set of libraries such as SpringTest, Junit, Harmcrest, Mockito,
etc. We can easily include them all using the test starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
– It automatically includes all these dependencies with the right versions:

68
Anyway, the core of everything is the Inversion of Control and
Dependency Injection

69
Inversion of Control (or the Hollywood Principle)

Every framework uses Inversion of Control


The idea is that it is the framework the one that controls the application.
The developer “just” needs to implement the pieces of code that
provides the functionality that is specific to the application
70
What does this mean in Spring (Boot)?

71
The Inversion of Control used in Spring is called Dependency Injection

72
This is a dependency

73
Image from: https://www.martinfowler.com/articles/injection.html
This is a dependency injection

MovieLister depends on an
implementation of MovieFinder
There is an
Assembler that
creates the
instance

The Assembler
“injects” the
dependency into
MovieLister

Image from: https://www.martinfowler.com/articles/injection.html 74


Dependency injection

75
In Spring, the Assembler is called Application Context (or IoC Container)

The objects that form the backbone of your application are managed by the
Application Context and are called beans.

A bean is a normal Java object that is instantiated, assembled, and managed


by the Application Context.

The Application Context gets its instructions on what objects to instantiate,


configure, and assemble by reading configuration metadata.

The configuration metadata can be represented in XML, Java annotations, or


Java code and lets you express the objects that compose your application and
the interdependencies between them.

76
It tells Spring this class is a
bean.

77
Annotations that tell Spring that a class is a bean

• @Component
• @Repository
• @Controller and @RestControler
• @Service
• @Configuration

78
It tells Spring we need
instances of PetService and
UserService.

79
Annotation-based
configuration

80
Why is dependency injection useful?

• If PetService were an interface, we would avoid the need to instantiate specific


classes in our code (so we remove that coupling) and we could change the
implementation without changing our code. However, services are not usually
interfaces so we will not use this feature very often.

• We ensure that only one instance of these classes is created and that all
classes that need it can access it. Doing that without an IoC container is
cumbersome.

• We allow the framework to do “magic” on our classes by exploiting the idea of


the proxy pattern and aspect-oriented programming (AOP)

81
The Proxy Pattern

• The Proxy provides a substitute to another object and aggregates some code
(e.g. to control its Access).
<<interface>>
It doesn’t know that it is using a
proxy. It is totally transparent. Bean

PetController

<<@Service>> “PetServiceProxy”
PetService Instantiates //unique instance
petService.findPets() Collection<Pet> findPets() //transaction mnagement

A Proxy of petService.findPets()
PetService
(PetServiceProxy) petService

Acts as a proxy to retrieve the


available pets in DB
It is the object that actually
performs the functionallity
82
Ok, let’s go back to our controllers

83
The MVC pattern in Spring applied to API creation

84
Our architecture

Backend

1. HTTP
Request
to the page
Spring
2. HTML + boot
JavaScript +
CSS
Frontend
WEB SERVER
Development 5. Data
4. SQL Queries
server
(only used
during development
DEV WEB for hot reloading and
SERVER 85
other useful features) DATABASE
Our architecture: We focus now on this

Backend

Spring
boot

01 02 03
WEB SERVER
Decide what Provide the Transform the
method of the method with the result of the
backend should be information from method in an API
called the API request response
1. Decide what method of the backend to call

87
Spring MVC uses the front controller pattern: A class/function that handles
all requests for a website, and then dispatches them to the appropriate
handler
1. HTTP Request to:
POST /api/v1/pets Front Controller

DispatcherServlet
Views
MVC

Controller
Controller

Controller

Model Model
Model

PET REGISTERING PET UPDATE OWNER DELETION 88


How does the Dispatcher Servlet know which method to call?

89
The base path for the methods of this controller is /api/v1/pets


This method handles GET requests to the path /api/v1/pets

This method handles GET requests to the path /api/v1/petstypes

This method handles POST requests to the path /api/v1/pets


2. Provide the method with the information of the API request
Request Mapping annotations

GET /owners/3/pets/23

ownerId = 3
petId = 23

You can learn more at: https://docs.spring.io/spring-framework/docs/current/spring-framework-


reference/web.html#mvc-ann-requestmapping
This idea of Request Mapping (i.e. linking URLs with controllers
methods) is used by most web frameworks nowadays. However,
they differ on the way this mapping is performed.

93
Examples

GET /pets?petId=345

The conversion to the type is


performed automatically by
Spring (it can be customized)
petId = 345
96
Processing requests

• With @RequestBody Spring transforms the JSON body of the request to the
appropriate entity (binding mechanism) {
id: 1,
name: "Leo",
birthDate: "2010/09/07",
type: { id: 1, name: “cat“ },
@PutMapping("/{id}") owner: 1
}
@ResponseStatus(HttpStatus.OK)
public void update(@RequestBody Pet resource, @PathVariable("id") int id) {
if(resource == null) {
throw new BadRequestException();
} else if(petService.findPetById(id) == null) {
throw new ResourceNotFoundException();
} else {
try {
petService.savePet(resource);
} catch (DuplicatedPetNameException ex) {
throw new BadRequestException();
}
}
}
97
3. Transform the result of the method in an API response

98
Automatically generating the response

• A @RestController uses JSON as the codification for objects returned

@RestController
@RequestMapping("/api/pets") 200 OK
public class PetRestController { {
id: 1,
… name: "Leo",
@GetMapping birthDate: "2010/09/07",
public Collection<Pet> findAll() { type: { id: 1,
return petService.findAllPets(); name: “cat“ },
owner: 1
} }
@GetMapping("/{id}")
public Pet findbyId(@PathVariable("id") int id) {
return petService.findPetById(id);
}

99
This does not always work

n s
e sso
g l
w i n
What happens if there is a loop in the object
structure? l l o
e fo
n t h
is i
t h
b o ut
What happens if we don’t want to send all the

rn a
information of the object?

l e a
e’ll
W
100
What if we just want to return a HTTP status code?

• We can use the annotation @ResponseStatus

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable("id") int id) {
petService.deletePet(id);
}

101
How can we send HTTP error codes?

• We can use exceptions to map HTTP error codes when something goes wrong

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
}

@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}

@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable("id") int id) {
if (!petService.deletePet(id))
throw new ResourceNotFoundException();
}

102
Alternatively

• We can use ResponseEntity<T> for specifying additional aspects of the HTTP


response (status codes, headers…)?

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable("id") int id) {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "foo");
petService.deletePet(id);
return new ResponseEntity<>(null, headers, HttpStatus.OK)
}

103
One more thing: How can I implement the functionality of the
controller?

109
We need to inject the dependency of the services in the controller and use
them! We’ll learn how to implement services in T4

@RestController
@RequestMapping("/api/pets")
public class PetRestController {
@Autowired private PetService petService;

Securing the whole stack

111
JWT

• JWT stands for JSON Web Token, It is an open standard (RFC-7519) based on JSON
• JWT allows us to generate an encrypted, verifiable and relatively short text containing any
(JSON) information we want to transmit.
• Its most popular use is in authentication/authorization scenarios with APIs, where tokens
are transmitted as the call meta-information in a specific header (named Authorization).
• The information about the user sending the request, his privileges, etc. are usually
contained in the token, and it can also be used as a session identifier.

• A JWT token has three parts: header . body . signature


• The first two parts are base64-expressed strings, created from two JSON documents, the
signature takes the other two parts and encrypts them using an algorithm (e.g. SHA-256).
An example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEiLCJ1c2VybmFtZSI6InNlcmdpb2R4YSJ9.Qu7rv5wqk6zGjiMU8ZixwvKQGBNW9hhj55DbSP50b1g

112
JWT

The typical interaction when


creating a client-server JWT is:

The header of a JWT token is a JSON


document usually has this form:
{ "alg": "HS256", "typ": "JWT" }

The body of a JWT token can be any valid JSON document. However, the
standard defines a series of properties with a specific meaning:
iss – Issuer, identifies who created the JWT
sub – Identifies the client for which the token was created
exp – Expiration Time, date that serves to verify if the JWT is expired
and force the user to reauthenticate.
iat – Issued at, Indicates when the JWT was created.
113
React router: guarding routes and securing links

• You can define some routes or others on your BrowserRouter depending on the
authentication and role of the user:

We use a jwt token in the local storage to


define the authenticated user and its roles

Depending on the availability of the token


(i.e. if the user is authenticated or not) we
define different sets of routes

114
React router: guarding routes and securing links

We can even define different sets of routes


depending on the roles/privileges of the
authenticated user

• On a different component: We can also show links


depending on the Authentication
{ isAuthenticated ? <Link to="/about/{name}“> Profile</Link> : <Redirect to="/login" /> }
of the user, or even redirect him
to the login page!
115
Fetching data with JWT

• In the same way as we get the current


authentication state from the jwt token in
local browser storage, we must use that
token to invoke the APIs in the backend.

• React provides a hook named useEffect


to load data prior to the initial render of
the component. This hook takes two
parameters, the function or block of code
to execute, an array of state
dependencies whose change should
trigger another execution of the effect.

• It is a good practice to delegate the


implementation of the fetching into a
different method (even in a different file).

116
Specifying security requirements for API endpoints

• In order to protect the resources provided by our API from access or


modifications, we need to specify security policies:
– Can anonymous users create/access/modify/delete the resource?
– Which roles can create/access/modify/delete the resource?

Spring has an specific subproject (and a spring boot starter) for managing this kind of
problems:

Spring Security

117
Specifying security requiements for API endpoints

• The class SecurityConfiguration specifies the access control policy, for instance:

118
Enabling security in the API documentation

You can annotate also specific controller methods (if some operations are public).
119
Wrapping up

121
Wrapping up

• What is Inversion of Control and dependency injection?


• Revisited MVC pattern
• How does Spring implement dependency injection?
• How does Spring implement MVC?
– The Dispatcher Servlet
• How do we implement security in backend API endpoints?
• React as a view technology:
– What is the state of a React component?
– When should we use React context and props?
– What is JWT? Why is it useful for us?
– How could we use react router to impose security constraints?
122
Reminders

123
Reminder: Each project group must play a game and create a video
explaining the rules
Upload a video explaining the rules to the github repository of the project or
youtube.

DEADLINE: 28 September 2023!!!!! 124


References

125
Bibliography

Martin Fowler, David Rice. Patterns of Enterprise


Application Architecture. Addison-Wesley Professional. 3rd
edition. 2003 (Chapters 1, 4 & 14)

Spring Framework Documentation. Pivotal, inc. 2020


Available at: https://docs.spring.io/spring-
framework/docs/current/spring-framework-reference/

126
Bibliography

React Framework Documentation. 2023. Available at:


https://react.dev/learn

React Router Documentation. 2023


Available at: https://reactrouter.com/

127
Disclaimer and Terms of Use

All material displayed on this presentation is for teaching and personal use only.

Many of the images that have been used in the presentation are Royalty Free images
taken from http://www.everystockphoto.com/. Other images have been sourced directly
from the Public domain, from where in most cases it is unclear whether copyright has
been explicitly claimed. Our intention is not to infringe any artist’s copyright, whether
written or visual. We do not claim ownership of any image that has been freely obtained
from the public domain. In the event that we have freely obtained an image or quotation
that has been placed in the public domain and in doing so have inadvertently used a
copyrighted image without the copyright holder’s express permission we ask that the
copyright holder writes to us directly, upon which we will contact the copyright holder to
request full written permission to use the quote or images.

You might also like