This commit is contained in:
lunaticbum 2025-08-05 11:24:23 +09:00
parent cfcbf3c819
commit d6ea6dc154
20 changed files with 306 additions and 89 deletions

View File

@ -42,10 +42,10 @@ class SecurityConfig(
}.authorizeHttpRequests { auth ->
auth
.requestMatchers(
"/", "/home",
"/", "/home.bs",
"/bums/where.bs" ,
"/user/login.bs", "/user/signup.bs","/user/login.bjx",
"/blog/viewer/**" ,
"/blog/viewer/**" , "/blog/posts" , "/blog/rankOfViews.bjx","/blog/recentOfPost.bjx",
"/css/**", "/js/**", "/images/**", "/webjars/**", "/assets/**").permitAll()
.anyRequest().authenticated()
}.formLogin { form ->

View File

@ -32,6 +32,7 @@ import org.springframework.security.core.userdetails.UserDetails
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Mono
import java.io.*
import java.net.URLDecoder
import java.text.SimpleDateFormat
@ -51,7 +52,7 @@ class BlogController() {
private lateinit var locationLogService: LocationLogService
@Autowired
private lateinit var postManageg: PostManageg
private lateinit var postManager: PostManager
@Autowired
lateinit var logService: LogService
@ -99,12 +100,12 @@ class BlogController() {
} else {
logService.log("target.writeTime >>> ${target.writeTime}")
target.modifyTime = System.currentTimeMillis()
postManageg.save(target)
postManager.save(target)
target = Gson().fromJson(fullData.joinToString(""), Post::class.java) ?: Post()
target.originId = target.id
target.id = null
}
var postMono = postManageg.save(target)
var postMono = postManager.save(target)
if (postMono != null) {
lResultMsg = "save post"
lResultCode = 0
@ -128,10 +129,51 @@ class BlogController() {
return responce
}
@GetMapping("rankOfViews.bjx")
fun rankOfViews(httpServletRequest: HttpServletRequest): Mono<ResponseEntity<PostsResult>> {
logService.log(httpServletRequest.requestURI)
val resultCode = 0
val resultMsg = "Success"
return postManager.getTop10Posts()
.collectList() // Flux<Post> -> Mono<List<Post>>
.map { postsList ->
val postsResult = PostsResult().apply {
this.resultCode = resultCode
this.resultMsg = resultMsg
this.posts = postsList // List<Post> 할당 가능
}
ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(postsResult)
}
}
@GetMapping("recentOfPost.bjx")
fun recentOfPost(httpServletRequest: HttpServletRequest): Mono<ResponseEntity<PostsResult>> {
logService.log(httpServletRequest.requestURI)
val resultCode = 0
val resultMsg = "Success"
return postManager.getRecent10Posts()
.collectList() // Flux<Post> -> Mono<List<Post>>
.map { postsList ->
val postsResult = PostsResult().apply {
this.resultCode = resultCode
this.resultMsg = resultMsg
this.posts = postsList // List<Post> 할당 가능
}
ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(postsResult)
}
}
@GetMapping("viewer/{postId}")
fun viewer(@PathVariable postId : String) : ResultMV{
val vm = ResultMV("content/blog/viewer")
postManageg.getPost(postId).block().apply {
postManager.getPost(postId).block().apply {
this?.title = URLDecoder.decode(this?.title)
this?.content = URLDecoder.decode(this?.content)
@ -142,14 +184,14 @@ class BlogController() {
try {
var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.firstPostLat!!,this?.firstPostLon!!)).await()
this.firstAddress = addrs.first().formattedAddress
postManageg.save(this)
postManager.save(this)
} catch (e: Exception) {}
}
if (this?.modifyAddress?.length ?: 0 < 4){
try {
var addrs = GeocodingApi.reverseGeocode(GeoApiContext.Builder().apiKey(it).build(), LatLng(this?.modifyLat!!,this?.modifyLon!!)).await()
this.modifyAddress = addrs.first().formattedAddress
postManageg.save(this)
postManager.save(this)
} catch (e: Exception) {}
}
}
@ -168,7 +210,7 @@ class BlogController() {
if (principal is UserDetails) {
val username = principal.username
// 추가 정보 사용 가능
postManageg.find20()?.apply {
postManager.find20()?.apply {
forEach {
it.title = URLDecoder.decode(it.title)
val content = URLDecoder.decode(it.content)
@ -187,7 +229,7 @@ class BlogController() {
@GetMapping("editor/{postId}")
fun editor(@PathVariable postId : String) : ResultMV{
val vm = ResultMV("content/blog/editor")
postManageg.getPost(postId).block().apply {
postManager.getPost(postId).block().apply {
this?.title = URLDecoder.decode(this?.title)
this?.content = URLDecoder.decode(this?.content)
vm.modelMap.put("srcPost",this)
@ -200,7 +242,7 @@ class BlogController() {
fun posts() : ResultMV{
val vm = ResultMV("content/blog/posts")
try {
vm.modelMap.put("Posts", postManageg.find20().apply {
vm.modelMap.put("Posts", postManager.find20().apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)

View File

@ -35,7 +35,7 @@ class BumsPrivate {
val m = ResultMV("content/private/where")
locationService.find10().apply {
m.modelMap.put("locations",this.reversed())
m.modelMap.put("locations",this)
}
m.setTitle("돼지 여기있다요~!!")
return m

View File

@ -2,7 +2,7 @@ package kr.lunaticbum.back.lun.controllers
import com.google.gson.Gson
import jakarta.servlet.http.HttpServletResponse
import kr.lunaticbum.back.lun.model.PostManageg
import kr.lunaticbum.back.lun.model.PostManager
import kr.lunaticbum.back.lun.model.ResultMV
import kr.lunaticbum.back.lun.utils.LogService
import net.coobird.thumbnailator.Thumbnails
@ -29,13 +29,13 @@ class Home {
lateinit var logService: LogService
@Autowired
private lateinit var postManageg: PostManageg
private lateinit var postManager: PostManager
@GetMapping("/","/home")
@GetMapping("/","/home.bs")
fun home() : ResultMV {
val vm = ResultMV("content/home")
try {
vm.modelMap.put("Posts", postManageg.find4().apply {
vm.modelMap.put("Posts", postManager.find4().apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
@ -105,7 +105,7 @@ class Home {
@GetMapping("/h2")
fun home2() : ResultMV {
val vm = ResultMV("content/index_ex")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply {
vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
@ -118,7 +118,7 @@ class Home {
@GetMapping("/left-sidebar")
fun lside() : ResultMV {
val vm = ResultMV("content/left-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply {
vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
@ -130,7 +130,7 @@ class Home {
@GetMapping("/no-sidebar")
fun nside() : ResultMV {
val vm = ResultMV("content/no-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply {
vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
@ -143,7 +143,7 @@ class Home {
@GetMapping("/right-sidebar")
fun rside() : ResultMV {
val vm = ResultMV("content/right-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply {
vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)
@ -156,7 +156,7 @@ class Home {
@GetMapping("/two-sidebar")
fun bside() : ResultMV {
val vm = ResultMV("content/two-sidebar")
vm.modelMap.put("Posts", postManageg.find20(Pageable.ofSize(20)).apply {
vm.modelMap.put("Posts", postManager.find20(Pageable.ofSize(20)).apply {
this.forEach {
it.title = URLDecoder.decode(it.title)
it.content = URLDecoder.decode(it.content)

View File

@ -10,14 +10,20 @@ import org.bson.codecs.pojo.annotations.BsonIgnore
import org.jsoup.Jsoup
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.annotation.Id
import org.springframework.data.domain.Sort
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.repository.Query
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.data.repository.query.Param
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import org.springframework.web.reactive.function.client.WebClient
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.text.SimpleDateFormat
import java.time.Duration
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
class BumsPrivate {
@ -48,6 +54,8 @@ class LocationLog {
var time : Long = 0L
var userId : String? = null
var bettween : String? = null
override fun toString(): String {
val buffer = StringBuffer()
buffer.append(mFeatureName).append("|").append("\n")
@ -72,7 +80,13 @@ class LocationLog {
@Repository
interface LocationLogRepository : ReactiveMongoRepository<LocationLog, String> {
@Query("{ 'time' : { \$gte: ?0 } }")
fun findRecent(since: Long, sort: Sort): Flux<LocationLog>
// @Query("SELECT l FROM LocationLog l WHERE l.timeString >= :since ORDER BY l.timeString DESC")
// fun findRecent(@Param("since") since: String): Flux<LocationLog>
fun findTop30ByOrderByTimeDesc(): Flux<LocationLog>
fun findAllBy() : Mono<LocationLog>
fun findFirstByOrderByTimeDesc() : Mono<LocationLog>
fun findFirstByUserIdOrderByTimeDesc(userId: String) : Mono<LocationLog>
@ -91,8 +105,16 @@ class LocationLogService : LocationService {
private lateinit var logRepository: LocationLogRepository
fun find10() : List<LocationLog> {
return logRepository.findAll().takeLast(10).buffer(10).blockLast(Duration.ofSeconds(30)) ?: listOf()
val sinceMills = System.currentTimeMillis() - ((24 * 60 * 60 * 1000) * 7)
println("sinceMills >> $sinceMills")
val sort = Sort.by(Sort.Direction.DESC, "time") // 오름차순 정렬
// val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
// val since = LocalDateTime.now().minusHours(24).format(formatter)
// println("since >> $since")
val flux = filterByDistanceReactive(logRepository.findRecent(sinceMills,sort), 10.0)
return flux.collectList().block(Duration.ofSeconds(30)) ?: listOf()
}
fun getLocationLog() : LocationLog? {
return logRepository.findFirstByOrderByTimeDesc().block()
}
@ -100,7 +122,33 @@ class LocationLogService : LocationService {
fun getLocationLogBy(userId : String) : LocationLog? {
return logRepository.findFirstByOrderByTimeDesc().block()
}
fun filterByDistanceReactive(flux: Flux<LocationLog>, minDistanceMeter: Double): Flux<LocationLog> {
return flux
.buffer(2, 1)
.filter { pair ->
if (pair.size < 2) true
else haversine(pair[0].mLatitude, pair[0].mLongitude, pair[1].mLatitude, pair[1].mLongitude) >= minDistanceMeter
}
.map { pair ->
val distance = if (pair.size < 2) 0.0 else haversine(pair[0].mLatitude, pair[0].mLongitude, pair[1].mLatitude, pair[1].mLongitude)
val base = pair[0]
println("base >>> ${base.time} ${base.timeString}")
base.bettween = String.format("%.2f m", distance) // 소수점 두자리까지 거리 표시
base
}
}
// Haversine 거리계산 함수 (단위:m)
fun haversine(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
val R = 6371000.0 // 지구 반지름(m)
val dLat = Math.toRadians(lat2 - lat1)
val dLon = Math.toRadians(lon2 - lon1)
val a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
val c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
return R * c
}
fun save(log: LocationLog) {
println("saved msg before ${log}")

View File

@ -9,13 +9,18 @@ import org.bson.codecs.pojo.annotations.BsonId
import org.bson.codecs.pojo.annotations.BsonRepresentation
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Pageable
import org.springframework.data.mongodb.core.ReactiveMongoTemplate
import org.springframework.data.mongodb.core.mapping.Document
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.data.mongodb.core.query.Query
import org.springframework.data.mongodb.core.query.Update
import org.springframework.data.mongodb.repository.ReactiveMongoRepository
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Repository
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.net.URLDecoder
import java.time.Duration
@Data
@ -49,31 +54,90 @@ class Post {
var modifyTime : Long = 0
var modifyLat : Double = 0.0
var modifyLon : Double = 0.0
var readCount : Long = 0
var voteCount : Long = 0
var unlikeCount : Long = 0
}
@Document(collection = "Comment")
class Comment {
@BsonId
var id: String? = null
var postId: String? = null // 댓글이 달린 포스트의 id
var parentId: String? = null // 대댓글이면 상위 댓글의 id, 최상위 댓글이면 null
var writer: String? = null
var content: String? = null
var writeTime: Long? = null
var mentions: List<String>? = null // 언급된 유저 아이디(선택)
}
@Repository
interface CommentRepository : ReactiveMongoRepository<Comment, String> {
fun findByPostIdAndParentIdIsNullOrderByWriteTimeAsc(postId: String): Flux<Comment> // 최상위 댓글
fun findByParentIdOrderByWriteTimeAsc(parentId: String): Flux<Comment>
}
@Service
class CommentService(private val commentRepository: CommentRepository) {
fun addComment(comment: Comment): Mono<Comment> {
// 예시: 부모 댓글 존재 여부/권한 검증 등 비즈니스 로직 처리
return commentRepository.save(comment)
}
fun getCommentsForPost(postId: String): Flux<Comment> {
return commentRepository.findByPostIdAndParentIdIsNullOrderByWriteTimeAsc(postId)
}
// 기타: 대댓글 불러오기, 신고/삭제, 멘션 알림 등 확장 가능
}
@Repository
interface PostRepository : ReactiveMongoRepository<Post, String> {
fun findAllByModifyTime(time : Long? = 0): Flux<Post>
fun findAllByModifyTime(time : Long? = 0): Flux<Post>
fun findAllByPostingTrue(pageable: Pageable): Flux<Post>
fun findTop10ByOrderByReadCountDesc(): Flux<Post>
fun findTop10ByOrderByModifyTimeDesc(): Flux<Post>
}
@Service
class PostManageg {
class PostManager(
private val postRepository: PostRepository,
private val reactiveMongoTemplate: ReactiveMongoTemplate
) {
@Autowired
private lateinit var logService: LogService
@Autowired
private lateinit var postRepository: PostRepository
@Autowired
private lateinit var bCryptPasswordEncoder: PasswordEncoder
fun getPost(id : String) : Mono<Post> = postRepository.findById(id)
// fun getPost(id : String) : Mono<Post> = postRepository.findById(id)
fun getPost(id: String): Mono<Post> {
val query = Query.query(Criteria.where("id").`is`(id))
val update = Update().inc("readCount", 1)
return reactiveMongoTemplate.findAndModify(query, update, Post::class.java)
.switchIfEmpty(Mono.error(NoSuchElementException("Post not found with id $id")))
}
fun find20(pageable :Pageable) : List<Post> {
return postRepository.findAllByPostingTrue(pageable).takeLast(20).buffer(20).blockLast(Duration.ofSeconds(30)) ?: listOf()
}
fun getTop10Posts(): Flux<Post> {
return postRepository.findTop10ByOrderByReadCountDesc().map { p ->
p.title = URLDecoder.decode(p.title)
p
}
}
fun getRecent10Posts(): Flux<Post> {
return postRepository.findTop10ByOrderByModifyTimeDesc().map { p ->
p.title = URLDecoder.decode(p.title)
p
}
}
fun find4() : List<Post> {

View File

@ -8,6 +8,11 @@ open class ResponceResult : BaseResult() {
}
@Getter
open class PostsResult : BaseResult() {
var posts: List<Post>? = null
}
@Getter
open class LoginResult : ResponceResult() {

View File

@ -91,3 +91,8 @@ resource.location=.
server.forward-headers-strategy=framework
#>>>>>>> ab915d0a416c69708f1df1ad76d7a14c779c1f59
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

View File

@ -39,7 +39,7 @@
$(
'<div id="titleBar">' +
'<a href="#navPanel" class="toggle"></a>' +
'<span class="title">' + $('#logo').html() + '</span>' +
'<span class="title" onclick="javascript:gotoHome()">' + $('#logo').html() + '</span>' +
'</div>'
)
.appendTo($body);

View File

@ -13,13 +13,13 @@ html {
background: black;
}
#where{
table-layout: fixed;
}
/*#where{*/
/* table-layout: fixed;*/
/*}*/
.where_item {
display: table-cell;
}
/*.where_item {*/
/* display: table-cell;*/
/*}*/
body {
user-select: none;
-webkit-user-select: none;

View File

@ -1,18 +1,18 @@
.layer {
height: 100%;
place-content: space-between;
place-items: stretch;
display: grid;
gap: 10px;
grid-auto-rows: minmax(200px, auto);
grid-template-columns: repeat(auto-fill, minmax(200px, auto));
width: 100%;
}
.where_item {
justify-content: space-between;
flex-wrap: wrap;
flex-direction: row;
width: 100%;
border-radius: 10px;
background: #F0F0F524;
}
/*.layer {*/
/* height: 100%;*/
/* place-content: space-between;*/
/* place-items: stretch;*/
/* display: grid;*/
/* gap: 10px;*/
/* grid-auto-rows: minmax(200px, auto);*/
/* grid-template-columns: repeat(auto-fill, minmax(200px, auto));*/
/* width: 100%;*/
/*}*/
/*!*.where_item {*!*/
/*!* justify-content: space-between;*!*/
/*!* flex-wrap: wrap;*!*/
/*!* flex-direction: row;*!*/
/*!* width: 100%;*!*/
/*!* border-radius: 10px;*!*/
/*!* background: #F0F0F524;*!*/
/*!*}*!*/

View File

@ -4,6 +4,68 @@ document.addEventListener('DOMContentLoaded', function() {
e.preventDefault(); // 기본 폼 제출 동작 방지
submitLoginForm();
});
if (document.querySelector(".rank_of_view")) {
fetch('blog/rankOfViews.bjx')
.then(response => response.json())
.then(data => {
const ul = document.querySelector('.rank_of_view');
ul.innerHTML = ''; // 기존 리스트 지움
ul.style.listStyle = 'none'; // 불릿 제거
ul.style.paddingLeft = '0'; // 들여쓰기 제거 (선택사항)
// data가 ['제목1', '제목2', ...] 형식이라고 가정
data.posts.forEach(item => {
const date = new Date(item.writeTime);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const formatted = `${year}/${month}/${day}`;
const li = document.createElement('li');
const a = document.createElement('a');
a.id = item.id;
a.href = `${getMainPath()}/blog/viewer/${item.id}`;
a.textContent = `${item.title}[${year}/${month}/${day}]`
li.appendChild(a);
ul.appendChild(li);
});
})
.catch(error => {
console.error('받아오기 실패:', error);
}
);
}
if (document.querySelector(".recent_posts")) {
fetch('blog/recentOfPost.bjx')
.then(response => response.json())
.then(data => {
const ul = document.querySelector('.recent_posts');
ul.innerHTML = ''; // 기존 리스트 지움
ul.style.listStyle = 'none'; // 불릿 제거
ul.style.paddingLeft = '0'; // 들여쓰기 제거 (선택사항)
// data가 ['제목1', '제목2', ...] 형식이라고 가정
data.posts.forEach(item => {
const date = new Date(item.writeTime);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const formatted = `${year}/${month}/${day}`;
const li = document.createElement('li');
const a = document.createElement('a');
a.id = item.id;
a.href = `${getMainPath()}/blog/viewer/${item.id}`;
a.textContent = `${item.title}[${year}/${month}/${day}]`
li.appendChild(a);
ul.appendChild(li);
});
})
.catch(error => {
console.error('받아오기 실패:', error);
}
);
}
});
onload = function() {
history.replaceState({}, null, location.pathname);
@ -213,6 +275,11 @@ function logout() {
form.submit();
}
function gotoHome() {
console.log(`location.port >> ${location.port}`)
location.href = getMainPath()+"/home.bs"
}
function gotoLogin() {
console.log(`location.port >> ${location.port}`)
location.href = getMainPath()+"/login.bs"
@ -222,6 +289,10 @@ function gotoJoin() {
document.location.replace(getMainPath() + "/user/join.bs")
}
function goToViewer(item) {
location.href = getMainPath() + "/blog/viewer/" + item.id
}
function goToView(path,id) {
location.href = path + id;
}

View File

@ -57,8 +57,8 @@
<article>
<section class="col-6 col-12-narrower imp-narrower" th:each="post : ${Posts}">
<!-- <span th:text="${cell}"></span>-->
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}">
<a href="#" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a>
<div class="box post" onclick="goToViewer(this)" th:id="${post.id}">
<a href="javascript:goToViewer(this)" th:id="${post.id}" th:data="${post.id}" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a>
<div class="inner">
<h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3>
<p th:text="${#strings.abbreviate(post.html, 80)}" class="ellipsis"></p>

View File

@ -11,11 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
// document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script>
</th:block>
<th:block layout:fragment="content" id="content">
@ -51,7 +47,7 @@
<div class="row" th:each="row : ${Posts}">
<section class="col-6 col-12-narrower" th:each="post : ${row}">
<!-- <span th:text="${cell}"></span>-->
<div class="box post" onclick="goToViewer(this)" th:data="${post.id}">
<div class="box post" onclick="goToViewer(this)" th:id="${post.id}">
<a href="#" class="image left"><img th:src="${#strings.length(post.thumb) > 0} ? ${post.thumb} : 'images/pic01.jpg'" alt="" /></a>
<div class="inner">
<h3 th:text="${#strings.length(post.title) > 0} ? ${post.title} : ('untitled[' + ${#temporals.format(T(java.time.Instant).ofEpochMilli(post.writeTime).atZone(T(java.time.ZoneId).systemDefault()).toLocalDateTime(), 'yyyy-MM-dd HH:mm:ss')} + ']')"></h3>

View File

@ -12,10 +12,6 @@
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script>
</th:block>
<th:block layout:fragment="content" id="content">

View File

@ -12,10 +12,7 @@
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script>
</th:block>
<th:block layout:fragment="content" id="content">

View File

@ -11,12 +11,13 @@
<div id="main_layer">
<div class="layer">
<th:block id="where" th:each="location : ${locations}">
<div class="where_item">
<label th:text="${location.timeString}"/><br/>
<label th:text="${location.mAddressLines}"/><br/>
<label th:text="${location.mCountryName}"/><br/>
<label th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"/><br/>
<label th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"/><br/>
<div class="where_item" style="font-family: monospace; white-space: nowrap;">
<span th:text="${location.timeString}"></span> |
<span th:text="${location.mAddressLines}"></span> |
<span th:text="${location.bettween}"></span> |
<span th:text="${#numbers.formatDecimal(location.mLatitude, 1, 3)}"></span> |
<span th:text="${#numbers.formatDecimal(location.mLongitude, 1, 3)}"></span> |
<span th:text="${location.mCountryName}"></span>
</div>
</th:block>
</div>

View File

@ -11,11 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script>
</th:block>
<th:block layout:fragment="content" id="content">-->

View File

@ -11,11 +11,7 @@
<script type="text/javascript" th:src="@{/js/test.js}"></script>
<link th:href="@{/css/toast-ui-dark.css}" rel="stylesheet" />
<script th:inline="javascript">
document.addEventListener("DOMContentLoaded", onLoaded);
function goToViewer(item) {
let uploadUrl = getMainPath() + "/blog/viewer/"+item.attributes['data'].value;
location.href = uploadUrl
}
</script>
</th:block>
<th:block layout:fragment="content" id="content">

View File

@ -5,8 +5,8 @@
<div class="container">
<div class="row">
<section class="col-3 col-6-narrower col-12-mobilep">
<h3>Links to Stuff</h3>
<ul class="links">
<h3>Rank of Views</h3>
<ul class="rank_of_view" >
<li><a href="#">Mattis et quis rutrum</a></li>
<li><a href="#">Suspendisse amet varius</a></li>
<li><a href="#">Sed et dapibus quis</a></li>
@ -17,8 +17,8 @@
</ul>
</section>
<section class="col-3 col-6-narrower col-12-mobilep">
<h3>More Links to Stuff</h3>
<ul class="links">
<h3>Recent of Posts</h3>
<ul class="recent_posts">
<li><a href="#">Duis neque nisi dapibus</a></li>
<li><a href="#">Sed et dapibus quis</a></li>
<li><a href="#">Rutrum accumsan sed</a></li>